import sys, os, cPickle, struct, traceback
from twisted.internet import protocol, reactor, defer, task
from twisted.protocols import basic
try:
    import sqllite3                     # For Python 2.5
except ImportError:
    from pysqlite2 import dbapi2 as sqlite3

SIO_PATH = "./mutella_sio"
SOCKET_PATH = "/u/drkp/.mutella/socket"

class BrowseProtocol(basic.LineReceiver):
    delimiter="\n"
    def connectionMade(self):
        self.dataBuf = ""
        self.sentRequest = False
        self.responseCode = None
        print "Connection made from", self.transport.getPeer()
        
    def lineReceived(self, line):
        print "Got line:", line
        if self.sentRequest == False:
            self.sendRequest()
            self.sentRequest = True
        else:
            if line.lower().startswith("http"):
                try:
                    self.responseCode = int(line.split()[1])
                except:
                    print "Parse error on http response code"
                    self.responseCode = -1
            elif line == "" or line == "\r":
                if self.responseCode != None:
                    self.setRawMode()

    def rawDataReceived(self, line):
        self.dataBuf += line

    def connectionLost(self, reason):
        print "Connection lost"
        packets = self.splitIntoPackets(self.dataBuf)
        hits = [self.processQueryHit(x) for x in packets]
        cPickle.dump((self.dataBuf, packets, hits),
                     file("connection.data", "w"))

    def sendRequest(self):
        self.transport.write("GET / HTTP/1.1\n")
        self.transport.write("Host: %s\n" % self.transport.getPeer()[1])
        self.transport.write("User-Agent: drkp\n")
        self.transport.write("Accept: application/x-gnutella-packets\n")
        self.transport.write("Content-Length: 0\n")
        self.transport.write("Connection: close\n\n")

    def splitIntoPackets(self, data):
        packets = []
        while len(data) > 0:
            try:
                packetType = struct.unpack("B", data[16])[0]
                packetLen = struct.unpack("I", data[19:23])[0]
                packet = data[:23+packetLen]
                packets.append(packet)
                data = data[23+packetLen:]
                print "Got packet len=%ld type=%x" % (packetLen, packetType)
            except Exception, e:
                traceback.print_exc(file=sys.stdout)
                print "Parse error, remaining:", data[:150]
                return packets
        return packets

    def processQueryHit(self, data):
        if data[16] != '\x81':
            print "Not a query hit packet, type=%x" % (struct.unpack("B", data[16])[0])
            return []
        data = data[23:]
        numHits = struct.unpack("B", data[0])[0]
        port = struct.unpack("H", data[1:3])[0]
        ip = struct.unpack("L", data[3:7])[0]
        data = data[11:]

        hits = []
        try:
            for i in range(numHits):
                data = data[8:]
                name = data.split("\0")[0]
                print name
                name = name.decode("utf8")
                data = "\0".join(data.split("\0")[2:])
                hits.append(name)
        except Exception, e:
            traceback.print_exc(file=sys.stdout)
            print "Parse error, remaining:", data[:150]
            return hits

        return hits


class MutellaInterface:
    def __init__(self):
        pass

    def startSIO(self, pp):
        reactor.spawnProcess(pp, SIO_PATH, [SIO_PATH, SOCKET_PATH])

    class FindProcessProtocol(protocol.ProcessProtocol):
        def __init__(self, q):
            self.q = q

        def connectionMade(self):
            print "FindProcessProtocol connectionMade; sending query", self.q
            self.transport.write("find " + self.q + "\n")
            #self.transport.closeStdin()

        def outReceived(self, d):
            print d

        def errReceived(self, d):
            print d

        def processEnded (self, status):
            print "FindProcessProtocol processEnded"

    class GetResultsProtocl(protocol.ProcessProtocol):
        def __init__(self):
            pass
            
    def find(self, q):
        self.startSIO(self.FindProcessProtocol(q))

                
class BrowseFactory(protocol.ServerFactory):
    protocol = BrowseProtocol

reactor.listenTCP(50000, BrowseFactory())

mi = MutellaInterface()
reactor.callLater(5, lambda : mi.find("arpeggio"))
reactor.run()

