Beispiel #1
0
class P2PHeader(object):
    HeaderLength = funcs.iproperty('_get_HeaderLength')
    Identifier = 0
    MessageSize = 0
    TotalSize = 0
    SessionId = 0
    AckIdentifier = funcs.iproperty('_get_AckIdentifier', '_set_AckIdentifier')
    IsNak = funcs.iproperty('_get_IsNak')
    IsAck = funcs.iproperty('_get_IsAck')
    RequireAck = funcs.iproperty('_get_RequireAck')

    def CreateAck(self):
        raise NotImplementedError

    def CreateNak(self):
        raise NotImplementedError

    def ParseHeader(self, data):
        raise NotImplementedError

    def GetBytes(self):
        raise NotImplementedError

    def __repr__(self):
        varstr = ', '.join('%s=%r' % i for i in vars(self).items())
        return '%s(%s)' % (type(self).__name__, varstr)

    def Copy(self):
        new = type(self)()
        new.ParseHeader(self.GetBytes())
        return new
Beispiel #2
0
class P2PDCHandshakeMessage(P2PDCMessage):
    Guid = funcs.iproperty('_get_Guid', '_set_Guid')
    guid = None

    def _get_Guid(self):
        return self.guid

    def _set_Guid(self, value):
        self.guid = value
        if self.Version == P2P.Version.V1:

            self.Header.AckSessionId = self.guid.bytes_le[0:4]
            self.Header.AckIdentifier = self.guid.bytes_le[4:8]
            self.Header.AckTotalSize = self.guid.bytes_le[8:16]

    @classmethod
    def Copy(cls, message):
        copy = super(P2PDCHandshakeMessage, cls).Copy(message)
        copy.Guid = message.Guid
        return copy

    def __init__(self, version):
        super(P2PDCHandshakeMessage, self).__init__(version)
        if self.Version == P2P.Version.V1:
            self.Header.Flags = P2P.Flags.DirectHandshake

        self.InnerBody = ''

    def CreateAck(self):
        ackMessage = self.Copy(self)
        ackMessage.Header.Identifier = 0
        return ack

    def ParseBytes(self, data):
        if self.Version == P2P.Version.V1:
            super(P2PDCHandshakeMessage, self).ParseBytes(data)
            self.Guid = uuid.UUID(bytes_le=self.Header.GetBytes()[-16:])

        else:
            # Don't call super()
            self.Guid = MSNU.CreateGuidFromData(self.Version, data)

        self.InnerBody = ''

    def GetBytes(self):
        self.InnerBody = ''
        guidData = self.Guid.bytes_le
        if self.Version == P2P.Version.V1:
            handshakeMessage = super(P2PDCHandshakeMessage, self).GetBytes()
            return handshakeMessage[:-16] + guidData
        else:
            # Doesn't call super.
            packetSize = struct.pack("<I", 16)
            return packetSize + guidData
Beispiel #3
0
class MultiResource(Resource):
    priority = None
    def __init__(self,
                 **parts):

        for part in parts:
            setattr(self, part, Resource(*parts[part]))

        if self.priority is None:
            self.priority = parts.keys()

        self._load_callback = None

    @property
    def resource(self):
        first = None
        for pri in self.priority:
            res = getattr(self, pri, None)
            if res is None:
                continue
            else:
                if first is None:
                    first = res

            if res.has_loaded_content:
                return res

        return first

    @property
    def content_location(self):
        return self.resource.content_location

    @property
    def web(self):
        return self.resource.web

    def _set_content(self, value):
        self.resource.content = value

    def _get_content(self):
        return self.resource.content

    content = funcs.iproperty('_get_content', '_set_content')

    @property
    def has_loaded_content(self):
        return self.resource.has_loaded_content

    @callbacks.callsback
    def load(self, callback = None):
        self.set_load_callback(callback)
        self._load_pos = 0
        self._try_next_load()

    def _try_next_load(self, *a):
        if self._load_pos < len(self.priority):
            resource = getattr(self, self.priority[self._load_pos])
            self._load_pos += 1
            resource.load(success = self._content_success,
                          error   = self._try_next_load)
        else:
            self._content_error()

    def process_data(self, resource):
        return super(MultiResource, self).process_data(resource.content)

    @callbacks.callsback
    def get_metadata(self, callback = None):
        self._metadata_callback = callback
        self._meta_pos = 0
        self._try_next_meta()

    def _try_next_meta(self, *a):
        if self._meta_pos < len(self.priority):
            resource = getattr(self, self.priority[self._meta_pos])
            self._meta_pos += 1
            resource.get_metadata(success = self._metadata_success,
                                  error   = self._try_next_meta)
        else:
            self._metadata_error()

    def _metadata_success(self, meta):
        log.info("Got metadata for %r: %r", self, meta)
        self.meta = meta
        mcb, self._metadata_callback = self._metadata_callback, None
        if mcb is not None:
            mcb.success(meta)

    def _metadata_error(self, err = None):
        log.info("Error getting metadata for %r: %r", self, err)
        self.meta = None
        mcb, self._metadata_callback = self._metadata_callback, None
        if mcb is not None:
            mcb.error(err)
Beispiel #4
0
class P2PBridge(Events.EventMixin):
    events = Events.EventMixin.events | set((
        'BridgeOpened',
        'BridgeSynced',
        'BridgeClosed',
        'BridgeSent',
    ))

    bridgeCount = 0
    syncIdentifier = 0
    bridgeID = 0
    queueSize = 0
    packageNumber = 0

    IsOpen = funcs.iproperty('_get_IsOpen')
    MaxDataSize = funcs.iproperty('_get_MaxDataSize')
    Remote = funcs.iproperty('_get_Remote')

    def _get_Synced(self):
        return self.SyncId != 0

    Synced = funcs.iproperty('_get_Synced')

    def _get_SyncId(self):
        return self.syncIdentifier

    def _set_SyncId(self, value):
        if value != self.syncIdentifier:
            self.syncIdentifier = value
            if value != 0:
                self.OnBridgeSynced()

    SyncId = funcs.iproperty('_get_SyncId', '_set_SyncId')

    def _get_SyncQueues(self):
        return self.sendQueues

    SendQueues = funcs.iproperty('_get_SyncQueues')

    def __init__(self, queueSize):
        self.localTrackerId = randid()
        Events.EventMixin.__init__(self)
        type(self).bridgeCount += 1
        self.bridgeID = self.bridgeCount

        self.queueSize = queueSize

        self.sendQueues = {}
        self.sendingQueues = {}
        self.stoppedSessions = []

    def IncrementPackageNumber(self):
        self.packageNumber += 1
        return self.packageNumber

    def Dispose(self):
        self.sendQueues.clear()
        self.sendingQueues.clear()
        self.stoppedSessions.Clear()

    def SuitableFor(self, session):
        remote = self.Remote
        return session is not None and remote is not None and session.Remote.account == remote.account

    def Ready(self, session):
        if not self.IsOpen:
            return False
        if session in self.stoppedSessions:
            return False

        if self.queueSize == 0:
            return True

        if session not in self.sendingQueues:
            return self.SuitableFor(session)

        ready = len(self.sendingQueues[session]) < self.queueSize
        #log.debug("Queue status %r/%r", len(self.sendingQueues[session]), self.queueSize)
        if not ready:
            log.info("Queue is full!")

        return ready

    def Send(self, session, remote, remoteGuid, msg, callback=None, **kw):
        if remote is None:
            raise Exception()

        msg1 = self.SetSequenceNumberAndRegisterAck(session, remote, msg,
                                                    callback)
        if msg1 is None:
            log.info("Message was finished already, not sending: %r", msg)
            callback.after_send()
            self.ProcessSendQueues()
            return

        if session is None:
            if not self.IsOpen:
                log.error("Send called with no session on a closed bridge: %r",
                          self)
                return

            if msg1 is not None:
                self.SendOnePacket(None,
                                   remote,
                                   remoteGuid,
                                   msg1,
                                   after_send=callback.after_send)

            return

        if not self.SuitableFor(session):
            log.error(
                "Send called with a session this bridge is not suitable for: %r",
                self)
            return

        if session not in self.sendQueues:
            self.sendQueues[session] = P2PSendQueue.P2PSendQueue()

        if msg1 is not None:
            self.sendQueues[session].Enqueue(remote, remoteGuid, msg1,
                                             callback)

#        self.sendQueues[session].Enqueue(remote, remoteGuid, msg)
        self.ProcessSendQueues()

    Send = callbacks.callsback(Send,
                               ('success', 'error', 'after_send', 'progress'))

    def SetSequenceNumberAndRegisterAck(self, session, remote, message,
                                        callback):
        version1 = message.Version == P2P.Version.V1
        version2 = message.Version == P2P.Version.V2
        if message.Header.Identifier == 0:
            if session is None:
                message.Header.Identifier = self.localTrackerId
                if version1:
                    self.localTrackerId += 1
                    message.Header.Identifier = self.localTrackerId
                elif version2:
                    message.Header.Identifier = self.localTrackerId
            else:
                self.localTrackerId = message.Header.Identifier = session.NextLocalIdentifier(
                    message.Header.MessageSize)

        if version1 and message.Header.AckSessionId == 0:
            message.Header.AckSessionId = randid()
        elif version2 and message.Header.PackageNumber == 0:
            message.Header.PackageNumber = self.packageNumber

        if message.IsFinished():
            return None

        firstMessage, firstCallback = message.GetNextMessage(self.MaxDataSize)

        if firstMessage is None:
            assert message.IsFinished()
            firstMessage = message
            firstCallback = callback
        else:
            for key in firstCallback:
                cb_type = getattr(callback, key, None)
                if cb_type is not None:
                    cb_type += firstCallback[key]

#        if version2:
#self.localTrackerId = message.Header.Identifier + message.Header.TotalSize
#self.localTrackerId = session.NextLocalIdentifier(message.Header.TotalSize)
        self.localTrackerId = message.Header.Identifier

        if remote.client.self_buddy.protocol.P2PHandler is not None:
            remote.client.self_buddy.protocol.P2PHandler.RegisterAckHandler(
                firstMessage, callback)
        else:
            callback.error("Connection shutting down!")
            self.Dispose()

        return firstMessage

    def ProcessSendQueues(self):
        to_send = []
        #_log.debug("ProcessSendQueues:")
        for session, queue in self.sendQueues.items():
            #_log.debug("\tSession = %r", session)
            #_log.debug("\tQueue = %r", queue)
            #_log.debug('\tlen(queue) = %r', len(queue))

            spin_check = 10
            while self.Ready(session) and len(queue) and spin_check:
                item = queue.Dequeue()
                #_log.debug("\titem = %r", item)
                if item is None:
                    spin_check -= 1
                    continue
                spin_check = 10

                if session not in self.sendingQueues:
                    self.sendingQueues[session] = P2PSendQueue.P2PSendList()

                self.sendingQueues[session].append(item)
                to_send.append((session, item.Remote, item.RemoteGuid,
                                item.p2pMessage, item.callback))

        for args in to_send:

            self.SendOnePacket(*args[:-1], callback=args[-1])

        moreQueued = False

        for session, queue in self.sendQueues.items():
            if len(queue):
                moreQueued = True
                break

        if not moreQueued:
            #log.debug("All queues empty")
            pass

    def SendOnePacket(self, session, remote, remoteGuid, msg, callback=None):
        raise NotImplementedError

    def StopSending(self, session):
        if session not in self.stoppedSessions:
            self.stoppedSessions.append(session)

        else:
            log.debug("Session already stopped: %r", session)

    def ResumeSending(self, session):
        if session in self.stoppedSessions:
            self.stoppedSessions.remove(session)
            self.ProcessSendQueues()

        else:
            log.debug("Session is not stopped: %r", session)

    def MigrateQueue(self, session, newBridge):
        newQueue = P2PSendQueue.P2PSendQueue()

        if session in self.sendingQueues:
            if newBridge is not None:
                for item in self.sendingQueues[session]:
                    newQueue.Enqueue(item)

            self.sendingQueues.pop(session, None)

        if session in self.sendQueues:
            if newBridge is not None:
                while len(self.sendQueues[session]):
                    newQueue.Enqueue(self.sendQueues[session].Dequeue())

            self.sendQueues.pop(session, None)

        if session in self.stoppedSessions:
            self.stoppedSessions.remove(session)

        if newBridge is not None:
            newBridge.AddQueue(session, newQueue)

    def AddQueue(self, session, queue):
        if session in self.sendQueues:
            while len(queue):
                self.sendQueues[session].Enqueue(queue.Dequeue())

        else:
            self.sendQueues[session] = queue

        self.ProcessSendQueues()

    def OnBridgeOpened(self):
        self.event('BridgeOpened')
        self.ProcessSendQueues()

    def OnBridgeSynced(self):
        self.event('BridgeSynced')

    def OnBridgeClosed(self):
        self.event('BridgeClosed')

    def OnBridgeSent(self, session, message):
        version1 = message.Version == P2P.Version.V1
        version2 = message.Version == P2P.Version.V2

        if message.Header.Identifier != 0:
            self.localTrackerId = (message.Header.Identifier +
                                   1) if version1 else (
                                       message.Header.Identifier +
                                       message.Header.MessageSize)

        #_log.info("OnBridgeSent: %r, %r", session, message)
        if session is not None and session in self.sendingQueues:
            if message in self.sendingQueues[session]:
                #_log.info("\tSession in queue and message in session queue")
                try:
                    self.sendingQueues[session].remove(message)
                except ValueError as e:
                    #_log.info("\terror removing! %r", e)
                    pass

        self.event('BridgeSent', session, message)
        self.ProcessSendQueues()
Beispiel #5
0
class SLPMessage(object):
    Encoding = 'utf8'

    def __repr__(self):
        varstr = ', '.join('%s=%r' % i for i in vars(self).items())
        return '%s(%s)' % (type(self).__name__, varstr)

    def GetEndPointIDFromMailEPIDString(self, mailEPID):
        if ';' in mailEPID:
            return uuid.UUID(filter(None, mailEPID.split(';'))[-1])

        return uuid.UUID(int=0)

    def GetEmailAccount(self, mailEPID):
        if ';' in mailEPID:
            return filter(None, mailEPID.split(';'))[0]
        return mailEPID

    def __init__(self, data=None):
        if data is not None:
            self.ParseBytes(data)
        else:
            self.mimeHeaders = MSNC.MSNMime()
            self.mimeBodies = MSNC.MSNMime()

            self.Via = 'MSNSLP/1.0/TLP '
            self.Branch = uuid.uuid4()
            self.CSeq = 0
            self.CallId = uuid.uuid4()
            self.MaxForwards = 0
            self.ContentType = 'text/unknown'

    StartLine = funcs.iproperty('_get_StartLine', '_set_StartLine')

    def MaxForwards():
        def fget(self):
            return int(self.mimeHeaders.get('Max-Forwards', 0))

        def fset(self, value):
            del self.mimeHeaders['Max-Forwards']
            self.mimeHeaders['Max-Forwards'] = str(value)

        return locals()

    MaxForwards = property(**MaxForwards())

    def To():
        def fget(self):
            return self.mimeHeaders['To']

        def fset(self, value):
            del self.mimeHeaders['To']
            self.mimeHeaders['To'] = value

        return locals()

    To = property(**To())

    def From():
        def fget(self):
            return self.mimeHeaders['From']

        def fset(self, value):
            self.mimeHeaders['From'] = value

        return locals()

    From = property(**From())

    @property
    def FromEmailAccount(self):
        return self.GetEmailAccount(
            self.From.replace('<msnmsgr:', '').replace('>', ''))

    @property
    def ToEmailAccount(self):
        return self.GetEmailAccount(
            self.To.replace('<msnmsgr:', '').replace('>', ''))

    @property
    def FromEndPoint(self):
        return self.GetEndPointIDFromMailEPIDString(
            self.From.replace('<msnmsgr:', '').replace('>', ''))

    @property
    def ToEndPoint(self):
        return self.GetEndPointIDFromMailEPIDString(
            self.To.replace('<msnmsgr:', '').replace('>', ''))

    def Source():
        def fget(self):
            return self.From.replace('<msnmsgr:', '').replace('>', '')

        def fset(self, value):
            self.From = '<msnmsgr:%s>' % value

        return locals()

    Source = property(**Source())

    def Target():
        def fget(self):
            return self.To.replace('<msnmsgr:', '').replace('>', '')

        def fset(self, value):
            self.To = '<msnmsgr:%s>' % value

        return locals()

    Target = property(**Target())

    def Via():
        def fget(self):
            return self.mimeHeaders['Via']

        def fset(self, value):
            del self.mimeHeaders['Via']
            self.mimeHeaders['Via'] = value

        return locals()

    Via = property(**Via())

    def Branch():
        def fget(self):
            via_parts = self.mimeHeaders.get('Via', '').split('branch=', 1)
            if len(via_parts) == 2:
                return uuid.UUID(via_parts[1])
            else:
                return uuid.UUID(int=0)

        def fset(self, value):
            old_via = self.mimeHeaders.get('Via')
            via = old_via.split(';')[0]
            new_via = (via + ';branch={%s}') % (str(value).upper())
            del self.mimeHeaders['Via']
            self.mimeHeaders['Via'] = new_via

        return locals()

    Branch = property(**Branch())

    def CSeq():
        def fget(self):
            return int(self.mimeHeaders.get('CSeq', 0))

        def fset(self, value):
            del self.mimeHeaders['CSeq']
            self.mimeHeaders['CSeq'] = str(value)

        return locals()

    CSeq = property(**CSeq())

    def CallId():
        def fget(self):
            return uuid.UUID(
                self.mimeHeaders.get('Call-ID', str(uuid.UUID(int=0))))

        def fset(self, value):
            del self.mimeHeaders['Call-ID']
            self.mimeHeaders['Call-ID'] = '{' + str(value).upper() + '}'

        return locals()

    CallId = property(**CallId())

    def ContentType():
        def fget(self):
            return self.mimeHeaders.get('Content-Type')

        def fset(self, value):
            del self.mimeHeaders['Content-Type']
            self.mimeHeaders['Content-Type'] = value

        return locals()

    ContentType = property(**ContentType())

    @property
    def BodyValues(self):
        return self.mimeBodies

    def GetBytes(self, appendNull=True):
        body = self.mimeBodies.as_string().replace(
            '\n', '\r\n') + ('\0' * appendNull)
        del self.mimeHeaders['Content-Length']
        self.mimeHeaders['Content-Length'] = str(len(body))

        return ''.join((self.StartLine.strip(), '\r\n',
                        self.mimeHeaders.as_string().replace('\n',
                                                             '\r\n'), body))

    def ParseBytes(self, data):
        self.StartLine, data = data.split('\r\n', 1)
        self.mimeHeaders = email.message_from_string(data, _class=MSNC.MSNMime)
        self.mimeBodies = email.message_from_string(
            self.mimeHeaders.get_payload(), _class=MSNC.MSNMime)
        self.mimeHeaders.set_payload(None)

    @classmethod
    def Parse(cls, data):
        if '\r\n' not in data:
            return None

        firstline = data.split('\r\n', 1)[0]
        if "MSNSLP" not in firstline:
            return None

        if firstline.startswith("MSNSLP/1.0"):
            return SLPStatusMessage(data)
        else:
            return SLPRequestMessage(data)
Beispiel #6
0
class V2Header(P2PHeader):
    OperationCode = 0
    ackIdentifier = 0
    nakIdentifier = 0
    TFCombination = 0
    PackageNumber = 0
    dataRemaining = 0

    def __init__(self):
        # these are big endian
        self.HeaderTLVs = {}
        self.DataPacketTLVs = self.dataPacketTLVs = {}

    def _get_HeaderLength(self):
        length = 8
        if self.HeaderTLVs:
            for val in self.HeaderTLVs.values():
                length += (1 + 1 + len(val))

            if (length % 4) != 0:
                length += (4 - (length % 4))

        return length

    @property
    def DataPacketHeaderLength(self):
        if self.MessageSize == 0:
            return 0

        length = 8
        if self.dataPacketTLVs:
            for val in self.dataPacketTLVs.values():
                length += (1 + 1 + len(val))

            if (length % 4) != 0:
                length += (4 - (length % 4))
        return length

    def _get_AckIdentifier(self):
        if self.ackIdentifier == 0 and (2 in self.HeaderTLVs):
            self.ackIdentifier = struct.unpack('>I', self.HeaderTLVs[2])[0]

        return self.ackIdentifier

    def _set_AckIdentifier(self, value):
        self.HeaderTLVs[2] = struct.pack('>I', value)

    def _get_NakIdentifier(self):
        if self.nakIdentifier == 0 and (3 in self.HeaderTLVs):
            self.nakIdentifier = struct.unpack('>I', self.HeaderTLVs[3])[0]

        return self.nakIdentifier

    def _set_NakIdentifier(self, value):
        self.HeaderTLVs[3] = struct.pack('>I', value)

    NakIdentifier = funcs.iproperty('_get_NakIdentifier', '_set_NakIdentifier')

    def _get_IsAck(self):
        return 2 in self.HeaderTLVs

    def _get_IsNak(self):
        return 3 in self.HeaderTLVs

    def _get_RequireAck(self):
        rak = (self.OperationCode & P2P.OperationCode.RAK) != 0

        #        if rak and self.MessageSize == 0 and (len(self.HeaderTLVs) - (self.IsAck + self.IsNak)) == 0 and len(self.DataPacketTLVs) == 0:
        #            log.error("Ignoring RAK flag because packet has no data")
        #            return False

        return rak

    def _get_DataRemaining(self):
        if self.dataRemaining == 0 and (1 in self.DataPacketTLVs):
            self.dataRemaining = struct.unpack('>Q', self.DataPacketTLVs[1])[0]

        return self.dataRemaining

    def _set_DataRemaining(self, value):
        self.dataRemaining = value
        if value == 0:
            self.DataPacketTLVs.pop(1, None)
        else:
            self.DataPacketTLVs[1] = struct.pack('>Q', value)

    DataRemaining = funcs.iproperty('_get_DataRemaining', '_set_DataRemaining')

    def AppendPeerInfoTLV(self):
        self.OperationCode |= P2P.OperationCode.SYN
        self.HeaderTLVs[1] = self.CreatePeerInfoValue()

    def CreateAck(self):
        ack = V2Header()
        if self.RequireAck:
            ack.AckIdentifier = self.Identifier + self.MessageSize
            ack.OperationCode = P2P.OperationCode.NONE

            if self.MessageSize > 0:
                if not self.IsAck:
                    if (self.OperationCode & P2P.OperationCode.SYN) != 0:
                        ack.OperationCode |= P2P.OperationCode.RAK

                        if 1 in self.HeaderTLVs:
                            ack.HeaderTLVs[1] = self.HeaderTLVs[1]
                            ack.OperationCode |= P2P.OperationCode.SYN
        else:
            raise Exception("Can't ack a non-RAK header")

        return ack

    def CreateNak(self):
        nak = V2Header()
        nak.NakIdentifier = self.Identifier + self.MessageSize
        return nak

    def ParseHeader(self, data):
        (headerLen, self.OperationCode, self.MessageSize,
         self.Identifier), data = struct.unpack('>BBHI', data[:8]), data[8:]

        if headerLen > 8:
            TLVs_data, data = data[:headerLen - 8], data[headerLen - 8:]
            tlvs = unpack_tlvs(TLVs_data)
            for tlv in tlvs:
                self.ProcessHeaderTLVData(*tlv)

        dataHeaderLen = 0
        if self.MessageSize > 0:
            (dataHeaderLen, self.TFCombination, self.PackageNumber,
             self.SessionId), data = struct.unpack('>BBHI', data[:8]), data[8:]
            if dataHeaderLen > 8:
                TLVs_data, data = data[:dataHeaderLen -
                                       8], data[dataHeaderLen - 8:]
                tlvs = unpack_tlvs(TLVs_data)
                for tlv in tlvs:
                    self.ProcessDataPacketTLVData(*tlv)

        return headerLen + dataHeaderLen

    def ProcessHeaderTLVData(self, t, l, v):
        self.HeaderTLVs[t] = v

        if t == 1:
            pass
        elif t == 2:
            return
            if L == 4:
                pass
        elif t == 3:
            pass

    def ProcessDataPacketTLVData(self, t, l, v):
        self.DataPacketTLVs[t] = v

    def CreatePeerInfoValue(self):
        return struct.pack('<HHHHI', 512, 0, 3584, 0, 271)

    def GetBytes(self):
        headerLen = self.HeaderLength
        dataHeaderLen = self.DataPacketHeaderLength

        data = ''
        data += struct.pack('>BBHI', headerLen, self.OperationCode,
                            self.MessageSize, self.Identifier)
        for key, val in self.HeaderTLVs.items():
            data += struct.pack('BB', key, len(val)) + val

        missing_bytes = 4 - len(data) % 4
        if missing_bytes != 4:
            data += '\0' * missing_bytes

        data += struct.pack('>BBHI', dataHeaderLen, self.TFCombination,
                            self.PackageNumber, self.SessionId)
        for key, val in self.DataPacketTLVs.items():
            data += struct.pack('BB', key, len(val)) + val

        missing_bytes = 4 - len(data) % 4
        if missing_bytes != 4:
            data += '\0' * missing_bytes

        return data
Beispiel #7
0
class P2PApplication(Events.EventMixin):
    events = Events.EventMixin.events | set((
        'TransferStarted',
        'TransferFinished',
        'TransferAborted',
        'TransferError',
    ))

    AppId = 0
    EufGuid = uuid.UUID(int=0)

    localEP = uuid.UUID(int=0)
    remoteEP = uuid.UUID(int=0)

    status = AppStatus.Waiting
    version = P2P.Version.V1

    client = None
    session = None
    remote = None
    local = None

    def __init__(self, session=None, ver=None, remote=None, remoteEP=None):
        Events.EventMixin.__init__(self)
        if session is not None:
            self.client = session.protocol
            self.P2PSession = session
            self.version = session.Version
            self.remote = self.session.Remote
        else:
            self.client = remote.client
            self.version = ver
            self.remote = getattr(remote, 'contact', remote)
            self.remoteEP = remoteEP

        self.local = self.client.self_buddy.contact
        self.localEP = self.client.get_machine_guid()

    def _get_P2PVersion(self):
        return self.version

    def _set_P2PVersion(self, val):
        raise AttributeError("Not settable")

    P2PVersion = funcs.iproperty("_get_P2PVersion", "_set_P2PVersion")

    InvitationContext = funcs.iproperty("_get_InvitationContext",
                                        "_set_InvitationContext")

    def _get_AutoAccept(self):
        return False

    def _set_AutoAccept(self, val):
        raise AttributeError("Not settable")

    AutoAccept = funcs.iproperty("_get_AutoAccept", "_set_AutoAccept")

    def _get_ApplicationId(self):
        if self.AppId == 0:
            self.AppId = type(self).FindApplicationId(self)
        return self.AppId

    def _set_ApplicationId(self, val):
        raise AttributeError("Not settable")

    ApplicationId = funcs.iproperty("_get_ApplicationId", "_set_ApplicationId")

    def _get_ApplicationEufGuid(self):
        if self.EufGuid == uuid.UUID(int=0):
            self.EufGuid = type(self).FindApplicationEufGuid(self)
        return self.EufGuid

    def _set_ApplicationEufGuid(self, val):
        raise AttributeError("Not settable")

    ApplicationEufGuid = funcs.iproperty("_get_ApplicationEufGuid",
                                         "_set_ApplicationEufGuid")

    def _get_ApplicationStatus(self):
        return self.status

    ApplicationStatus = funcs.iproperty("_get_ApplicationStatus",
                                        "_set_ApplicationStatus")

    @property
    def Local(self):
        return self.local

    @property
    def Remote(self):
        return self.remote

    @property
    def Version(self):
        return self.version

    def _get_P2PSession(self):
        return self.session

    def _set_P2PSession(self, val):
        if self.session is not None:
            self.session.unbind_event('Closing', self.P2PSessionClosing)
            self.session.unbind_event('Closed', self.P2PSessionClosed)
            self.session.unbind_event('Error', self.P2PSessionError)

        self.session = val
        if self.session is not None:
            self.session.bind_event('Closing', self.P2PSessionClosing)
            self.session.bind_event('Closed', self.P2PSessionClosed)
            self.session.bind_event('Error', self.P2PSessionError)

    P2PSession = funcs.iproperty("_get_P2PSession", "_set_P2PSession")

    def SetupInviteMessage(self, slp):
        slp.BodyValues['EUF-GUID'] = ('{%s}' % self.ApplicationEufGuid).upper()
        slp.BodyValues['AppID'] = str(self.ApplicationId)
        slp.BodyValues['Context'] = self.InvitationContext

    def ValidateInvitation(self, invitation):
        ret = invitation.ToEmailAccount.lower() == self.local.account.lower()

        if ret and self.version == P2P.Version.V2:
            ret = invitation.ToEndPoint == self.localEP

        return ret

    def ProcessData(self, bridge, data, reset):
        return NotImplemented

    def SendMessage(self, message, callback=None):
        assert message.Version == self.version

        if self.P2PSession is None:
            callback.error()
            log.error("No session to send message with! message = %r", message)
            return

        message.Header.SessionId = self.P2PSession.SessionId
        if (message.Version == P2P.Version.V1) and (
            (message.Header.Flags & P2P.Flags.Ack) != P2P.Flags.Ack):
            message.Footer = self.ApplicationId

        self.P2PSession.Send(message, callback=callback)

    SendMessage = callbacks.callsback(
        SendMessage, ('success', 'error', 'after_send', 'progress'))

    def BridgeIsReady(self):
        return

    def Start(self):
        if self.status in (AppStatus.Finished, AppStatus.Aborted,
                           AppStatus.Error):
            return False

        if self.status == AppStatus.Active:
            log.error("Can't start app again! %r", self)
            return False
        self.OnTransferStarted()
        return True

    def Accept(self):
        if self.P2PSession is None:
            return
        self.P2PSession.Accept()

    def Decline(self):
        if self.P2PSession is None:
            return
        self.P2PSession.Decline()

    def Abort(self):
        self.OnTransferAborted(self.local)
        if self.P2PSession is None:
            return
        self.P2PSession.Close()

    def Dispose(self):
        if self.P2PSession is not None:
            self.P2PSession.KillTimeoutTimer()

        if self.status not in (AppStatus.Aborted, AppStatus.Finished,
                               AppStatus.Error):
            self.OnTransferError()

        self.P2PSession = None

    def OnTransferStarted(self):
        log.info("TransferStarted: %r", self)
        self.status = AppStatus.Active
        self.TransferStarted(self)

    def OnTransferFinished(self, *a):
        if self.status not in (AppStatus.Aborted, AppStatus.Finished,
                               AppStatus.Error):
            log.info("TransferFinished: %r", self)
            self.status = AppStatus.Finished
            if self.P2PSession is not None:
                self.P2PSession.KillTimeoutTimer()
                self.P2PSession = None
            self.TransferFinished(self, *a)

    def OnTransferAborted(self, who):
        if self.status not in (AppStatus.Aborted, AppStatus.Finished,
                               AppStatus.Error):
            log.info("TransferAborted: %r, %r", self, who)
            self.status = AppStatus.Aborted
            if self.P2PSession is not None:
                self.P2PSession.KillTimeoutTimer()
                self.P2PSession = None
            self.TransferAborted(self, who)

    def OnTransferError(self):
        if self.status not in (AppStatus.Aborted, AppStatus.Finished,
                               AppStatus.Error):
            log.info("TransferError: %r, session = %r", self, self.P2PSession)
            self.status = AppStatus.Error
            if self.P2PSession is not None:
                self.P2PSession.KillTimeoutTimer()
                self.P2PSession.Close()
                self.P2PSession = None
            self.TransferError(self)

    def P2PSessionClosing(self, session, contact):
        if self.status in (AppStatus.Waiting, AppStatus.Active):
            self.OnTransferAborted(contact)

    def P2PSessionClosed(self, session, contact):
        if self.status in (AppStatus.Waiting, AppStatus.Active):
            self.OnTransferAborted(contact)

    def P2PSessionError(self, session):
        if self.status != AppStatus.Error:
            self.OnTransferError()

    # Class methods
    _known_apps = {}

    @classmethod
    def IsRegistered(cls, euf, appid):
        if euf.int != 0 and euf in cls._known_apps:
            if appid != 0:
                for app in cls._known_apps[euf]:
                    if appid == app.AppId:
                        return True
            return True
        return False

    @classmethod
    def CreateInstance(cls, euf, appid, session):
        if session is not None and euf.int != 0 and euf in cls._known_apps:
            if appid != 0:
                for app in cls._known_apps[euf]:
                    if appid == app.AppId:
                        return app.Type(session)

            return cls._known_apps[euf][0].Type(session)

        return None

    @classmethod
    def RegisterApplication(cls, Type):
        if Type is None:
            raise Exception("no app type provided")

        if not issubclass(Type, P2PApplication):
            raise Exception("Must be subclass of P2PApplication")

        added = False

        if Type.EufGuid not in cls._known_apps:
            cls._known_apps[Type.EufGuid] = []

        app = P2PApp(Type.EufGuid, Type.AppId, Type)

        if app not in cls._known_apps[app.EufGuid]:
            cls._known_apps[app.EufGuid].append(app)
            added = True

        return Type

    @classmethod
    def FindApplicationId(cls, p2papp):
        for app_list in cls._known_apps.values():
            for app in app_list:
                if app.Type is type(p2papp):
                    return app.AppId

        return 0

    @classmethod
    def FindApplicationEufGuid(cls, p2papp):
        for app_list in cls._known_apps.values():
            for app in app_list:
                if app.Type is type(p2papp):
                    return app.EufGuid

        return uuid.UUID(int=0)