Example #1
0
    def __init__(self, jid, password, resource, avatars=None):
        super(TlenStream, self).__init__()

        self.session_id = None
        self.uplink = None

        # Set by adapt.incoming_avatar
        self.avatar_token = None

        self.jid = jid
        self.resource = adapt.tlen_encode(resource)
        self._password = password
        self._transport = GeventTransport()
        self._stream_reader = StreamReader(self)
        self._avatars = avatars

        self._closed = False
        self._authenticated = gevent.event.Event()

        self.blocklist = BlockList(self.jid.as_string())
Example #2
0
class TlenStream(XMLStreamHandler):
    def __init__(self, jid, password, resource, avatars=None):
        super(TlenStream, self).__init__()

        self.session_id = None
        self.uplink = None

        # Set by adapt.incoming_avatar
        self.avatar_token = None

        self.jid = jid
        self.resource = adapt.tlen_encode(resource)
        self._password = password
        self._transport = GeventTransport()
        self._stream_reader = StreamReader(self)
        self._avatars = avatars

        self._closed = False
        self._authenticated = gevent.event.Event()

        self.blocklist = BlockList(self.jid.as_string())

    def wait_for_auth(self):
        """
		Wait until self._authenticated event is signalled, or
		a timeout occurs.

		Return True if auth succeeded, False otherwise.
		"""

        try:
            self._authenticated.wait(30)
            if self._closed:
                return False
            else:
                gevent.spawn(self._pinger)
                return True
        except gevent.Timeout:
            return False

    @property
    def authenticated(self):
        return self._authenticated.is_set()

    def start(self):
        gevent.spawn(self.connect)

    def connect(self):
        if not self._transport.connect(self._connected):
            self._closed = True
            self._transport.close()

    def close(self):
        self._transport.send("</s>")
        self._closed = True
        self._transport.close()
        self.uplink.close()
        self.uplink = None

    def send(self, data):
        if isinstance(data, Stanza):
            data = adapt.outgoing_stanza(data)
            data = data.serialize()

        logger.debug("client send %s", data)
        self._transport.send(data)

    def _pinger(self):
        # XXX: Race condition? Dunno how gevent handles this internally
        while True:
            gevent.sleep(30)

            if self._closed:
                break
            try:
                self._transport.send(" ")
            except Exception as e:
                logger.error("Tlen transport error: %s", e)
                self.close()
                break

    def _loop(self):
        logger.debug("loop")
        while not self._closed:
            data = self._transport.recv()
            logger.debug("data=%s", data)
            if not data:
                break
            try:
                self._stream_reader.feed(data)
            except Exception as e:
                logger.error("Tlen error: %s", exc_info=sys.exc_info())
                break
        logger.debug("tlen loop done")

        if self.uplink:
            # Close the other side (XMPP client)
            self.uplink.close()
            # Lose the ref
            self.uplink = None

    def _connected(self):
        logger.debug("connected")
        self._transport.send('<s v="9" t="7">')
        self._loop()

    def _auth_digest(self):
        magic1 = 0x50305735
        magic2 = 0x12345671
        s = 7

        for c in self._password:
            if c in (" ", "\t"):
                continue
            magic1 ^= (((magic1 & 0x3F) + s) * ord(c)) + (magic1 << 8)
            magic2 += (magic2 << 8) ^ magic1
            s += ord(c)

        magic1 &= 0x7FFFFFFF
        magic2 &= 0x7FFFFFFF

        code = "%08x%08x" % (magic1, magic2)

        sha1 = sha.new(self.session_id + code)
        return sha1.hexdigest()

    def _uplink_element(self, element):
        # logger.debug('uplink %s', element)
        # The element processing might be deferred
        if element is None:
            return

        element = self._add_namespaces(element)
        self.uplink.send(stanza_factory(element))

    def _add_namespaces(self, element):
        if element.tag[0] != "{":
            element.tag = constants.STANZA_CLIENT_QNP + element.tag

        for sub in element:
            logging.debug("fixing %s", sub)
            if not sub.tag:
                continue
            if sub.tag[0] != "{":
                sub.tag = constants.STANZA_CLIENT_QNP + sub.tag
        return element

        #
        # XMLStreamHandler API
        #

    def stream_start(self, element):
        self.session_id = element.get("i")

        # Authorize with the Tlen server; this is tlen-specific,
        # and can't be handled the usual XMPP way.
        xml = '<iq type="set" id="%s"><query xmlns="jabber:iq:auth">'
        xml += "<username>%s</username><digest>%s</digest><resource>%s</resource>"
        xml += "<host>tlen.pl</host></query></iq>"

        xml = xml % (self.session_id, self.jid.local, self._auth_digest(), self.resource)
        self.send(xml)

    def stream_element(self, element):
        if self.authenticated:
            element = adapt.incoming_element(self, element)
            self._uplink_element(element)
        else:
            if element.tag != "iq":
                raise TlenAuthError("Unexpected server response: " + stanza.serialize())
            typ = element.get("type")
            if typ == "result":
                self._authenticated.set()
            else:
                logger.warning("Tlen server denied authentication (account: %s)", self.jid.as_string())
                self.close()

    def stream_end(self):
        self._closed = True
        self._transport.close()