Example #1
0
    def complete_xml_element(self, xmlnode, _unused):
        """Complete the XML node with `self` content.

        Should be overriden in classes derived from `StanzaPayloadObject`.

        :Parameters:
            - `xmlnode`: XML node with the element being built. It has already
              right name and namespace, but no attributes or content.
            - `_unused`: document to which the element belongs.
        :Types:
            - `xmlnode`: `libxml2.xmlNode`
            - `_unused`: `libxml2.xmlDoc`"""
        if self.node:
            xmlnode.setProp("node", to_utf8(self.node))
        if self.sid:
            xmlnode.setProp("sid", to_utf8(self.sid))
        if self.mode and self.mode != 'tcp':
            xmlnode.setProp("mode", to_utf8(self.mode))
        for host in self.hosts:
            try:
                host.as_xml(xmlnode, _unused)
            except:
                pprint(host)
                raise
        if self.activate:
            xmlnode.newChild(None, "activate", JID(self.activate).as_utf8())
        if self.host_used:
            h = xmlnode.newChild(None, "streamhost-used", None)
            h.setProp("jid", JID(self.host_used).as_utf8())
Example #2
0
 def handle_message(self, stanza):
     '''
     <message
         from='[email protected]/desktop'
         to='*****@*****.**'>
       <x xmlns='jabber:x:conference'
          jid='*****@*****.**'
          password='******'
          reason='Hey Hecate, this is the place for all good witches!'/>
     </message>
     '''
     try:
         fromjid = stanza.get_from()
         x = stanza.xpath_eval('c:x', {'c': CONFERENCE_NS})[0]
         roomjid = JID(from_utf8(x.prop('jid')))
         roomname = JID(roomjid).node
         password = x.prop('password')
         password = from_utf8(password) if password else None
         reason = x.prop('reason')
         reason = from_utf8(reason) if reason else None
     except Exception:
         traceback.print_exc()
         return False
     else:
         if not all((roomname, fromjid)):
             return False
         self.protocol.hub.on_invite(
             protocol=self.protocol,
             buddy=fromjid,
             room_name=roomname,
             message=reason,
             on_yes=lambda: self.protocol.join_chat_jid(
                 roomjid, self.protocol.self_buddy.jid.node))
         return True  # don't let other message handlers do it
Example #3
0
 def check_presence_subscribe(self, p):
     self.failUnlessEqual(p.get_from(), JID("[email protected]/res"))
     self.failUnlessEqual(p.get_to(), JID("*****@*****.**"))
     self.failUnlessEqual(p.get_type(), "subscribe")
     self.failUnlessEqual(p.get_id(), None)
     self.failUnlessEqual(p.get_show(), None)
     self.failUnlessEqual(p.get_status(), None)
Example #4
0
    def from_xml(self, xmlnode):
        """Initialize Delay object from an XML node.

        :Parameters:
            - `xmlnode`: the jabber:x:delay XML element.
        :Types:
            - `xmlnode`: `libxml2.xmlNode`"""
        if xmlnode.type != "element":
            raise ValueError, "XML node is not a jabber:x:delay element (not an element)"
        ns = get_node_ns_uri(xmlnode)
        if ns and (ns != self.xml_element_namespace
                   or xmlnode.name != self.xml_element_name):
            raise ValueError, "XML node is not a " + self.xml_element_namespace + " element"
        stamp = xmlnode.prop("stamp")
        tm = _parse_ts(stamp)
        tm = tm[0:8] + (0, )
        self.timestamp = datetime.datetime.fromtimestamp(time.mktime(tm))
        delay_from = from_utf8(xmlnode.prop("from"))
        if delay_from:
            try:
                self.delay_from = JID(delay_from)
            except JIDError:
                raise JIDMalformedProtocolError, "Bad JID in the " + self.xml_element_namespace + " 'from' attribute"
        else:
            self.delay_from = None
        self.reason = from_utf8(xmlnode.getContent())
Example #5
0
    def from_xml(self, xmlnode):
        """Initialize Delay object from an XML node.

        :Parameters:
            - `xmlnode`: the jabber:x:delay XML element.
        :Types:
            - `xmlnode`: `libxml2.xmlNode`"""
        if xmlnode.type != "element":
            raise ValueError, "XML node is not a jabber:x:delay element (not an element)"
        ns = get_node_ns_uri(xmlnode)
        if ns and ns != DELAY_NS or xmlnode.name != "x":
            raise ValueError, "XML node is not a jabber:x:delay element"
        stamp = xmlnode.prop("stamp")
        if stamp.endswith("Z"):
            stamp = stamp[:-1]
        if "-" in stamp:
            stamp = stamp.split("-", 1)[0]
        try:
            tm = time.strptime(stamp, "%Y%m%dT%H:%M:%S")
        except ValueError:
            raise BadRequestProtocolError, "Bad timestamp"
        tm = tm[0:8] + (0, )
        self.timestamp = datetime.datetime.fromtimestamp(time.mktime(tm))
        delay_from = from_utf8(xmlnode.prop("from"))
        if delay_from:
            try:
                self.delay_from = JID(delay_from)
            except JIDError:
                raise JIDMalformedProtocolError, "Bad JID in the jabber:x:delay 'from' attribute"
        else:
            self.delay_from = None
        self.reason = from_utf8(xmlnode.getContent())
Example #6
0
    def __init__(self,
                 node_or_datetime,
                 delay_from=None,
                 reason=None,
                 utc=True):
        """
        Initialize the Delay object.

        :Parameters:
            - `node_or_datetime`: an XML node to parse or the timestamp.
            - `delay_from`: JID of the entity which adds the delay mark
              (when `node_or_datetime` is a timestamp).
            - `reason`: reason of the delay (when `node_or_datetime` is a
              timestamp).
            - `utc`: if `True` then the timestamp is assumed to be UTC,
              otherwise it is assumed to be local time.
        :Types:
            - `node_or_datetime`: `libxml2.xmlNode` or `datetime.datetime`
            - `delay_from`: `pyxmpp.JID`
            - `reason`: `unicode`
            - `utc`: `bool`"""
        if isinstance(node_or_datetime, libxml2.xmlNode):
            self.from_xml(node_or_datetime)
        else:
            if utc:
                self.timestamp = node_or_datetime
            else:
                self.timestamp = datetime_local_to_utc(node_or_datetime)
            self.delay_from = JID(delay_from)
            self.reason = unicode(reason)
Example #7
0
    def tls_is_certificate_valid(self, cert):
        """Default certificate verification callback for TLS connections.

        :Parameters:
            - `cert`: certificate information, as returned by `ssl.SSLSocket.getpeercert`

        :return: computed verification result."""
        try:
            self.__logger.debug("tls_is_certificate_valid(cert = %r)" %
                                (cert, ))
            if not cert:
                self.__logger.warning(
                    "No TLS certificate information received.")
                return False
            valid_hostname_found = False
            if 'subject' in cert:
                for rdns in cert['subject']:
                    for key, value in rdns:
                        if key == 'commonName' and JID(value) == self.peer:
                            self.__logger.debug(
                                " good commonName: {0}".format(value))
                            valid_hostname_found = True
            if 'subjectAltName' in cert:
                for key, value in cert['subjectAltName']:
                    if key == 'DNS' and JID(value) == self.peer:
                        self.__logger.debug(
                            " good subjectAltName({0}): {1}".format(
                                key, value))
                        valid_hostname_found = True
            return valid_hostname_found
        except:
            self.__logger.exception("Exception caught")
            raise
Example #8
0
 def service_discovery_init(self):
     self.disco_init = jabber.disco.DiscoNode(self.cache,
                                              JID(self.jid.domain))
     self.disco_init.fetch(self.disco_finished, depth=1, timeout_duration=1)
     self.disco_init2 = jabber.disco.DiscoNode(self.cache,
                                               JID("google.com"))
     self.disco_init2.fetch(super(GoogleTalk, self).disco_finished,
                            depth=1,
                            timeout_duration=1)
Example #9
0
 def test_field_jid_single(self):
     field = Field(field_type="jid-single", value=JID(u"*****@*****.**"))
     self.failUnlessEqual(field.value, JID(u"*****@*****.**"))
     self.failUnlessEqual(field.values, [u"*****@*****.**"])
     field = Field(field_type="jid-single", values=[u"*****@*****.**"])
     self.failUnlessEqual(field.value, JID(u"*****@*****.**"))
     self.failUnlessEqual(field.values, [u"*****@*****.**"])
     field.value = JID(u"example.com")
     self.failUnlessEqual(field.value, JID(u"example.com"))
     self.failUnlessEqual(field.values, [u"example.com"])
Example #10
0
    def join(self,
             room,
             nick,
             handler,
             password=None,
             history_maxchars=None,
             history_maxstanzas=None,
             history_seconds=None,
             history_since=None):
        """
        Create and return a new room state object and request joining
        to a MUC room.

        :Parameters:
            - `room`: the name of a room to be joined
            - `nick`: the nickname to be used in the room
            - `handler`: is an object to handle room events.
            - `password`: password for the room, if any
            - `history_maxchars`: limit of the total number of characters in
              history.
            - `history_maxstanzas`: limit of the total number of messages in
              history.
            - `history_seconds`: send only messages received in the last
              `history_seconds` seconds.
            - `history_since`: Send only the messages received since the
              dateTime specified (UTC).

        :Types:
            - `room`: `JID`
            - `nick`: `unicode`
            - `handler`: `MucRoomHandler`
            - `password`: `unicode`
            - `history_maxchars`: `int`
            - `history_maxstanzas`: `int`
            - `history_seconds`: `int`
            - `history_since`: `datetime.datetime`

        :return: the room state object created.
        :returntype: `MucRoomState`
        """

        if not room.node or room.resource:
            raise ValueError, "Invalid room JID"

        room_jid = JID(room.node, room.domain, nick)

        cur_rs = self.rooms.get(room_jid.bare().as_unicode())
        if cur_rs and cur_rs.joined:
            raise RuntimeError, "Room already joined"

        rs = MucRoomState(self, self.stream.me, room_jid, handler)
        self.rooms[room_jid.bare().as_unicode()] = rs
        rs.join(password, history_maxchars, history_maxstanzas,
                history_seconds, history_since)
        return rs
Example #11
0
    def _process_sasl_auth(self, mechanism, content):
        """Process incoming <sasl:auth/> element.

        [receiving entity only]

        :Parameters:
            - `mechanism`: mechanism choosen by the peer.
            - `content`: optional "initial response" included in the element.
        """
        if self.authenticator:
            self.__logger.debug("Authentication already started")
            return False

        self.auth_method_used = "sasl:" + mechanism
        self.authenticator = sasl.server_authenticator_factory(mechanism, self)

        r = self.authenticator.start(base64.decodestring(content))

        if isinstance(r, sasl.Success):
            el_name = "success"
            content = r.base64()
        elif isinstance(r, sasl.Challenge):
            el_name = "challenge"
            content = r.base64()
        else:
            el_name = "failure"
            content = None

        root = self.doc_out.getRootElement()
        xmlnode = root.newChild(None, el_name, None)
        ns = xmlnode.newNs(SASL_NS, None)
        xmlnode.setNs(ns)
        if content:
            xmlnode.setContent(content)
        if isinstance(r, sasl.Failure):
            xmlnode.newChild(None, r.reason, None)

        self._write_raw(xmlnode.serialize(encoding="UTF-8"))
        xmlnode.unlinkNode()
        xmlnode.freeNode()

        if isinstance(r, sasl.Success):
            if r.authzid:
                self.peer = JID(r.authzid)
            else:
                self.peer = JID(r.username, self.me.domain)
            self.peer_authenticated = 1
            self.state_change("authenticated", self.peer)
            self._post_auth()

        if isinstance(r, sasl.Failure):
            raise SASLAuthenticationFailed, "SASL authentication failed"

        return True
Example #12
0
 def send(self, message, jabber_id, jabber_password, jabber_recpt):
     try:
         self.lock.acquire()#acquire lock
         subject = "Deluge Torrent completed"
         jid=JID(jabber_id)
         if not jid.resource:
             jid=JID(jid.node,jid.domain,"send_message")
             recpt=JID(jabber_recpt)
             send_message(jid, jabber_password, recpt, message, subject)
     finally:
         self.lock.release();#release lock
Example #13
0
    def send_msg(self, rid, msg):
        self.rid = JID(rid)
        if not self.sid.resource:
            self.sid = JID(self.sid.node, self.sid.domain, 'send_message')

        msg = Message(to_jid=self.rid, body=msg)

        def send(stream):
            stream.send(msg)

        self.xmpp_do(send)
Example #14
0
    def handle_message(self, stanza):
        '''
        <message
            from='*****@*****.**'
            to='*****@*****.**'>
          <x xmlns='http://jabber.org/protocol/muc#user'>
            <invite from='[email protected]/desktop'>
              <reason>
                Hey Hecate, this is the place for all good witches!
              </reason>
            </invite>
            <password>cauldronburn</password>
          </x>
        </message>
        '''
        self.stanza = stanza
        try:
            roomjid = JID(stanza.get_from())
            roomname = roomjid.node
        except Exception:
            traceback.print_exc()
            return False
        else:
            if not roomname:
                return False
        for invite in stanza.xpath_eval('user:x/user:invite',
                                        {'user': MUC_USER_NS}):
            frm = invite.prop('from')
            if not frm:
                continue
            try:
                frm = JID(from_utf8(frm))
            except Exception:
                continue
            else:
                break
        else:
            return False

        reason = None
        for rsn in xpath_eval(invite, 'user:reason/text()',
                              {'user': MUC_USER_NS}):
            if rsn:
                reason = reason
        reason = reason or ''
        self.protocol.hub.on_invite(
            protocol=self.protocol,
            buddy=frm,
            room_name=roomname,
            message=reason,
            on_yes=lambda: self.protocol.join_chat_jid(
                roomjid, self.protocol.self_buddy.jid.node))
        return True
Example #15
0
    def stream_start(self, doc):
        """Process <stream:stream> (stream start) tag received from peer.

        :Parameters:
            - `doc`: document created by the parser"""
        self.doc_in = doc
        log.debug("input document: %r" % (self.doc_in.serialize(), ))

        try:
            r = self.doc_in.getRootElement()
            if r.ns().getContent() != STREAM_NS:
                self._send_stream_error("invalid-namespace")
                raise FatalStreamError, "Invalid namespace."
        except libxml2.treeError:
            self._send_stream_error("invalid-namespace")
            raise FatalStreamError, "Couldn't get the namespace."

        self.version = r.prop("version")
        if self.version and self.version != "1.0":
            self._send_stream_error("unsupported-version")
            raise FatalStreamError, "Unsupported protocol version."

#        to_from_mismatch=0
        assert self.initiator
        if self.initiator:
            self.stream_id = r.prop("id")
            peer = r.prop("from")
            if peer:
                peer = JID(peer)
                #            if self.peer:
                #                if peer and peer!=self.peer:
                #                    self.__logger.debug("peer hostname mismatch:"
                #                        " %r != %r" % (peer,self.peer))
                #                    to_from_mismatch=1
                #            else:
                self.peer = peer
        else:
            to = r.prop("to")
            if to:
                to = self.check_to(to)
                if not to:
                    self._send_stream_error("host-unknown")
                    raise FatalStreamError, 'Bad "to"'
                self.me = JID(to)
            self._send_stream_start(self.generate_id())
            self._send_stream_features()
            self.state_change("fully connected", self.peer)
            self._post_connect()

        if not self.version:
            self.state_change("fully connected", self.peer)
            self._post_connect()
Example #16
0
 def prefix_to_jid(self,prefix):
     if channel_re.match(prefix):
         node=channel_to_node(prefix,self.default_encoding)
         return JID(node,self.network.jid.domain,None)
     else:
         if "!" in prefix:
             nick,user=prefix.split("!",1)
         else:
             nick=prefix
             user=""
         node=nick_to_node(nick,self.default_encoding)
         resource=unicode(user,self.default_encoding,"replace")
         return JID(node,self.network.jid.domain,resource)
Example #17
0
    def _process_sasl_response(self, content):
        """Process incoming <sasl:response/> element.

        [receiving entity only]

        :Parameters:
            - `content`: the response data received (Base64-encoded).
        """
        if not self.authenticator:
            self.__logger.debug("Unexpected SASL response")
            return 0

        r = self.authenticator.response(base64.decodestring(content))
        if isinstance(r, sasl.Success):
            el_name = "success"
            content = r.base64()
        elif isinstance(r, sasl.Challenge):
            el_name = "challenge"
            content = r.base64()
        else:
            el_name = "failure"
            content = None

        root = self.doc_out.getRootElement()
        xmlnode = root.newChild(None, el_name, None)
        ns = xmlnode.newNs(SASL_NS, None)
        xmlnode.setNs(ns)
        if content:
            xmlnode.setContent(content)
        if isinstance(r, sasl.Failure):
            xmlnode.newChild(None, r.reason, None)

        self._write_raw(xmlnode.serialize(encoding="UTF-8"))
        xmlnode.unlinkNode()
        xmlnode.freeNode()

        if isinstance(r, sasl.Success):
            authzid = r.authzid
            if authzid:
                self.peer = JID(r.authzid)
            else:
                self.peer = JID(r.username, self.me.domain)
            self.peer_authenticated = 1
            self._restart_stream()
            self.state_change("authenticated", self.peer)
            self._post_auth()

        if isinstance(r, sasl.Failure):
            raise SASLAuthenticationFailed, "SASL authentication failed"

        return 1
Example #18
0
 def check_presence_full(self, p):
     self.failUnlessEqual(p.get_from(), JID("[email protected]/res"))
     self.failUnlessEqual(p.get_to(), JID("*****@*****.**"))
     self.failUnlessEqual(p.get_type(), None)
     self.failUnlessEqual(p.get_id(), "1")
     self.failUnlessEqual(p.get_show(), "away")
     self.failUnlessEqual(p.get_status(), "The Status")
     self.failUnlessEqual(p.get_priority(), 10)
     nodes = p.xpath_eval("t:payload",
                          {"t": "http://pyxmpp.jajcus.net/xmlns/test"})
     self.failUnless(nodes)
     self.failUnlessEqual(nodes[0].name, "payload")
     self.failUnless(nodes[0].children)
     self.failUnlessEqual(nodes[0].children.name, "abc")
Example #19
0
 def check_message_full(self, m):
     self.failUnlessEqual(m.get_from(), JID("[email protected]/res"))
     self.failUnlessEqual(m.get_to(), JID("*****@*****.**"))
     self.failUnlessEqual(m.get_type(), "normal")
     self.failUnlessEqual(m.get_id(), "1")
     self.failUnlessEqual(m.get_subject(), u"Subject")
     self.failUnlessEqual(m.get_body(), u"The body")
     self.failUnlessEqual(m.get_thread(), u"thread-id")
     nodes = m.xpath_eval("t:payload",
                          {"t": "http://pyxmpp.jajcus.net/xmlns/test"})
     self.failUnless(nodes)
     self.failUnlessEqual(nodes[0].name, "payload")
     self.failUnless(nodes[0].children)
     self.failUnlessEqual(nodes[0].children.name, "abc")
Example #20
0
    def join(self, room, nick, handler, password = None, history_maxchars = None,
            history_maxstanzas = None, history_seconds = None, history_since = None):
        """
        Create and return a new room state object and request joining
        to a MUC room.

        :Parameters:
            - `room`: the name of a room to be joined
            - `nick`: the nickname to be used in the room
            - `handler`: is an object to handle room events.
            - `password`: password for the room, if any
            - `history_maxchars`: limit of the total number of characters in
              history.
            - `history_maxstanzas`: limit of the total number of messages in
              history.
            - `history_seconds`: send only messages received in the last
              `history_seconds` seconds.
            - `history_since`: Send only the messages received since the
              dateTime specified (UTC).

        :Types:
            - `room`: `JID`
            - `nick`: `unicode`
            - `handler`: `MucRoomHandler`
            - `password`: `unicode`
            - `history_maxchars`: `int`
            - `history_maxstanzas`: `int`
            - `history_seconds`: `int`
            - `history_since`: `datetime.datetime`

        :return: the room state object created.
        :returntype: `MucRoomState`
        """

        if not room.node or room.resource:
            raise ValueError,"Invalid room JID"

        room_jid = JID(room.node, room.domain, nick)

        cur_rs = self.rooms.get(room_jid.bare().as_unicode())
        if cur_rs and cur_rs.joined:
            raise RuntimeError,"Room already joined"

        rs=MucRoomState(self, self.stream.me, room_jid, handler)
        self.rooms[room_jid.bare().as_unicode()]=rs
        rs.join(password, history_maxchars, history_maxstanzas,
            history_seconds, history_since)
        return rs
Example #21
0
 def _sender_is_jid(sender, jid):
     cjc = Application.instance
     if not sender:
         return False
     if "@" in sender:
         try:
             sender_jid = JID(sender)
             if sender_jid.bare() == jid.bare():
                 return True
         except ValueError:
             pass
     if sender == cjc.get_user_info(jid, "nick"):
         return True
     if sender == cjc.get_user_info(jid, "rostername"):
         return True
     return sender == jid.node
Example #22
0
    def __init__(self,node_or_datetime,delay_from=None,reason=None,utc=True):
        """
        Initialize the Delay object.

        :Parameters:
            - `node_or_datetime`: an XML node to parse or the timestamp.
            - `delay_from`: JID of the entity which adds the delay mark
              (when `node_or_datetime` is a timestamp).
            - `reason`: reason of the delay (when `node_or_datetime` is a
              timestamp).
            - `utc`: if `True` then the timestamp is assumed to be UTC,
              otherwise it is assumed to be local time.
        :Types:
            - `node_or_datetime`: `libxml2.xmlNode` or `datetime.datetime`
            - `delay_from`: `pyxmpp.JID`
            - `reason`: `unicode`
            - `utc`: `bool`"""
        if isinstance(node_or_datetime,libxml2.xmlNode):
            self.from_xml(node_or_datetime)
        else:
            if utc:
                self.timestamp=node_or_datetime
            else:
                self.timestamp=datetime_local_to_utc(node_or_datetime)
            self.delay_from=JID(delay_from)
            self.reason=unicode(reason)
Example #23
0
    def from_xml(self, node):
        #need jid, host, port, zeroconf
        """Initialize StreamHost from XML node."""
        if node.type != "element":
            raise ValueError, "XML node is not a streamhost (not en element)"
        ns = get_node_ns_uri(node)
        if ns and ns != self.xml_element_namespace or node.name != self.xml_element_name:
            raise ValueError, "XML node is not a %s descriptor" % self.xml_element_name
        jid = JID(node.prop("jid").decode("utf-8"))
        self.jid = jid

        host = node.prop("host").decode("utf-8")
        self.host = host

        port = node.prop("port")
        #py2.5:
        self.port = int(port.decode("utf-8")) if port else None
        #py2.4:
        #        if port:
        #            self.port = int(port.decode("utf-8"))
        #        else:
        #            self.port = None
        zeroconf = node.prop("zeroconf")
        #py2.5:
        self.zeroconf = zeroconf.decode("utf-8") if zeroconf else None
Example #24
0
    def __init__(self, xmlnode_or_jid, host=None, port=None, zeroconf=None):
        """Initialize an `StreamHost` object.

        Wrap an existing streamhost XML element or create a new one.

        :Parameters:
            - `xmlnode_or_jid`: XML element describing the StreamHost or the JID of
              the StreamHost.
            - `host`: the hostname or IP address of the StreamHost.
            - `port`: the port of the StreamHost
            - `zeroconf`: the zeroconf identifier of the StreamHost.
        :Types:
            - `xmlnode_or_node`: `libxml2.xmlNode` or `unicode`
            - `host`: `unicode`
            - `port`: `int`
            - `zeroconf`: `unicode`
            """
        if isinstance(xmlnode_or_jid, libxml2.xmlNode):
            self.from_xml(xmlnode_or_jid)
        else:
            self.jid = JID(xmlnode_or_jid)
            self.host = host
            self.port = port
            self.zeroconf = zeroconf
        if not (bool(self.port) ^ bool(self.zeroconf)):
            raise ValueError, 'StreamHost element requires one of [port, zeroconf]'
Example #25
0
    def from_xml(self,xmlnode):
        """Initialize Delay object from an XML node.

        :Parameters:
            - `xmlnode`: the jabber:x:delay XML element.
        :Types:
            - `xmlnode`: `libxml2.xmlNode`"""
        if xmlnode.type!="element":
            raise ValueError,"XML node is not a jabber:x:delay element (not an element)"
        ns=get_node_ns_uri(xmlnode)
        if ns and (ns != self.xml_element_namespace
                or xmlnode.name != self.xml_element_name):
            raise ValueError,"XML node is not a " + self.xml_element_namespace + " element"
        stamp=xmlnode.prop("stamp")
        tm = _parse_ts(stamp)
        tm=tm[0:8]+(0,)
        self.timestamp=datetime.datetime.fromtimestamp(time.mktime(tm))
        delay_from=from_utf8(xmlnode.prop("from"))
        if delay_from:
            try:
                self.delay_from = JID(delay_from)
            except JIDError:
                raise JIDMalformedProtocolError, "Bad JID in the " + self.xml_element_namespace + " 'from' attribute"
        else:
            self.delay_from = None
        self.reason = from_utf8(xmlnode.getContent())
Example #26
0
    def _process_sasl_success(self, content):
        """Process incoming <sasl:success/> element.

        [initiating entity only]

        :Parameters:
            - `content`: the "additional data with success" received (Base64-encoded).
        """
        if not self.authenticator:
            self.__logger.debug("Unexpected SASL response")
            return False

        r = self.authenticator.finish(base64.decodestring(content))
        if isinstance(r, sasl.Success):
            self.__logger.debug("SASL authentication succeeded")
            if r.authzid:
                self.me = JID(r.authzid)
            else:
                self.me = self.me
            self.authenticated = 1
            self._restart_stream()
            self.state_change("authenticated", self.me)
            self._post_auth()
        else:
            self.__logger.debug("SASL authentication failed")
            raise SASLAuthenticationFailed, "Additional success data procesing failed"
        return True
Example #27
0
 def _import(self, msg_type, directory, patterns):
     in_pattern, out_pattern = patterns
     same_pattern = (in_pattern == out_pattern)
     if same_pattern:
         pattern = in_pattern.replace("@@", "")
     else:
         in_pattern = in_pattern.replace("@@", "_in")
         out_pattern = out_pattern.replace("@@", "_out")
         pattern = "(?:{0}|{1})".format(in_pattern, out_pattern)
     pattern = re.compile(pattern)
     matches, unparseable, imported = 0, 0, 0
     for filename in sorted(os.listdir(directory)):
         path = os.path.join(directory, filename)
         if not os.path.isfile(path) \
                 or not LOG_FILENAME_RE.match(filename):
             self.info(" skipping {0}".format(filename))
             continue
         try:
             peer = JID(filename)
         except ValueError:
             self.info(" skipping {0} (bad jid)".format(filename))
             continue
         self.info(" {0}...".format(filename))
         with codecs.open(path, "r", "utf-8", "replace") as log_file:
             data = log_file.read()
         new_m, new_up, new_i = self._import_log(msg_type, peer, pattern,
                                                 data)
         matches += new_m
         unparseable += new_up
         imported += new_i
     return matches, unparseable, imported
Example #28
0
    def check_authzid(self, authzid, extra_info=None):
        """Check authorization id provided by the client.

        [server only]

        :Parameters:
            - `authzid`: authorization id provided.
            - `extra_info`: additional information about the user
              from the authentication backend. This mapping will
              usually contain at least 'username' item.
        :Types:
            - `authzid`: unicode
            - `extra_info`: mapping

        :return: `True` if user is authorized to use that `authzid`.
        :returntype: `bool`"""
        if not extra_info:
            extra_info = {}
        if not authzid:
            return 1
        if not self.initiator:
            jid = JID(authzid)
            if not extra_info.has_key("username"):
                ret = 0
            elif jid.node != extra_info["username"]:
                ret = 0
            elif jid.domain != self.my_jid.domain:
                ret = 0
            elif not jid.resource:
                ret = 0
            else:
                ret = 1
        else:
            ret = 0
        return ret
Example #29
0
 def pass_message_to_raw_channel(self, msg):
     fr = JID('#', self.network.jid.domain, None)
     m = Message(to_jid=self.jid,
                 from_jid=fr,
                 body=msg,
                 stanza_type="groupchat")
     self.component.send(m)
Example #30
0
    def from_xml(self, xmlnode):
        """Initialize Delay object from an XML node.

        :Parameters:
            - `xmlnode`: the jabber:x:delay XML element.
        :Types:
            - `xmlnode`: `libxml2.xmlNode`"""
        if xmlnode.type != "element":
            raise ValueError, "XML node is not a jabber:x:delay element (not an element)"
        ns = get_node_ns_uri(xmlnode)
        if ns and ns != DELAY_NS or xmlnode.name != "x":
            raise ValueError, "XML node is not a jabber:x:delay element"
        stamp = xmlnode.prop("stamp")
        if stamp.endswith("Z"):
            stamp = stamp[:-1]
        if "-" in stamp:
            stamp = stamp.split("-", 1)[0]
        try:
            tm = time.strptime(stamp, "%Y%m%dT%H:%M:%S")
        except ValueError:
            raise BadRequestProtocolError, "Bad timestamp"
        tm = tm[0:8] + (0,)
        self.timestamp = datetime.datetime.fromtimestamp(time.mktime(tm))
        delay_from = from_utf8(xmlnode.prop("from"))
        if delay_from:
            try:
                self.delay_from = JID(delay_from)
            except JIDError:
                raise JIDMalformedProtocolError, "Bad JID in the jabber:x:delay 'from' attribute"
        else:
            self.delay_from = None
        self.reason = from_utf8(xmlnode.getContent())
Example #31
0
 def _sender_is_jid(sender, jid):
     cjc = Application.instance
     if not sender:
         return False
     if "@" in sender:
         try:
             sender_jid = JID(sender)
             if sender_jid.bare() == jid.bare():
                 return True
         except ValueError:
             pass
     if sender == cjc.get_user_info(jid, "nick"):
         return True
     if sender == cjc.get_user_info(jid, "rostername"):
         return True
     return sender == jid.node
Example #32
0
 def from_xml(self, node):
     """Initialize RosterItem from XML node."""
     if node.type != "element":
         raise ValueError, "XML node is not a roster item (not en element)"
     ns = get_node_ns_uri(node)
     if ns and ns != ROSTER_NS or node.name != "item":
         raise ValueError, "XML node is not a roster item"
     jid = JID(node.prop("jid").decode("utf-8"))
     subscription = node.prop("subscription")
     if subscription not in ("none", "from", "to", "both", "remove"):
         subscription = "none"
     ask = node.prop("ask")
     if ask not in ("subscribe", None):
         ask = None
     name = from_utf8(node.prop("name"))
     groups = []
     n = node.children
     while n:
         if n.type != "element":
             n = n.next
             continue
         ns = get_node_ns_uri(n)
         if ns and ns != ROSTER_NS or n.name != "group":
             n = n.next
             continue
         group = n.getContent()
         if group:
             groups.append(from_utf8(group))
         n = n.next
     self.jid = jid
     self.name = name
     self.groups = groups
     self.subscription = subscription
     self.ask = ask
Example #33
0
 def __init__(self, config_dir, data_dir):
     self.doc = None
     self.config_dir = config_dir
     self.data_dir = data_dir
     os.chdir(data_dir)
     libxml2.initializeCatalog()
     libxml2.loadCatalog(os.path.join(data_dir, "catalog.xml"))
     parser = libxml2.createFileParserCtxt(
         os.path.join(config_dir, "jjigw.xml"))
     parser.validate(1)
     parser.parseDocument()
     if not parser.isValid():
         raise JJIGWFatalError, "Invalid configuration"
     self.doc = parser.doc()
     self.connect = ConnectConfig(self.doc.xpathEval("jjigw/connect")[0])
     self.jid = None
     self.networks = {}
     for n in self.doc.xpathEval("jjigw/network"):
         network = NetworkConfig(n)
         if not self.jid:
             self.jid = network.jid
         self.networks[network.jid.domain] = network
     spidentd = self.doc.xpathEval("jjigw/spidentd")
     if spidentd:
         self.spidentd = SPIdentDConfig(spidentd[0])
     else:
         self.spidentd = None
     self.admins = []
     for n in self.doc.xpathEval("jjigw/admin"):
         self.admins.append(JID(n.getContent()))
Example #34
0
    def __init__(self,
                 node_or_jid,
                 subscription="none",
                 name=None,
                 groups=(),
                 ask=None):
        """
        Initialize a roster item from XML node or jid and optional attributes.

        :Parameters:
            - `node_or_jid`: XML node or JID
            - `subscription`: subscription type ("none", "to", "from" or "both"
            - `name`: item visible name
            - `groups`: sequence of groups the item is member of
            - `ask`: True if there was unreplied subsription or unsubscription
              request sent."""
        if isinstance(node_or_jid, libxml2.xmlNode):
            self.from_xml(node_or_jid)
        else:
            node_or_jid = JID(node_or_jid)
            if subscription not in ("none", "from", "to", "both", "remove"):
                raise ValueError, "Bad subscription type: %r" % (
                    subscription, )
            if ask not in ("subscribe", None):
                raise ValueError, "Bad ask type: %r" % (ask, )
            self.jid = node_or_jid
            self.ask = ask
            self.subscription = subscription
            self.name = name
            self.groups = list(groups)
Example #35
0
    def get_jid(self):
        """Get the JID of the item.

        :return: the JID of the item.
        :returntype: `JID`"""
        jid = self.xmlnode.prop("jid")
        return JID(jid.decode("utf-8"))
Example #36
0
    def __init__(self, **args):
        self.isconnected = False

        # Create a unique jabber resource
        resource = args.get('resource') or 'python_client'
        resource += '_' + gethostname() + ':' + str(os.getpid()) + '_' + \
            threading.currentThread().getName().lower()
        self.jid = JID(args['username'], args['host'], resource)

        osrf.log.log_debug("initializing network with JID %s and host=%s, "
            "port=%s, username=%s" % (self.jid.as_utf8(), args['host'], \
            args['port'], args['username']))

        #initialize the superclass
        JabberClient.__init__(self, self.jid, args['password'], args['host'])
        self.queue = []

        self.receive_callback = None
        self.transport_error_msg = None
Example #37
0
    def __init(self,affiliation,role,jid=None,nick=None,actor=None,reason=None):
        """Initialize a `MucItem` object from a set of attributes.

        :Parameters:
            - `affiliation`: affiliation of the user.
            - `role`: role of the user.
            - `jid`: JID of the user.
            - `nick`: nickname of the user.
            - `actor`: actor modyfying the user data.
            - `reason`: reason of change of the user data.
        :Types:
            - `affiliation`: `str`
            - `role`: `str`
            - `jid`: `JID`
            - `nick`: `unicode`
            - `actor`: `JID`
            - `reason`: `unicode`
        """
        if not affiliation:
            affiliation=None
        elif affiliation not in affiliations:
            raise ValueError,"Bad affiliation"
        self.affiliation=affiliation
        if not role:
            role=None
        elif role not in roles:
            raise ValueError,"Bad role"
        self.role=role
        if jid:
            self.jid=JID(jid)
        else:
            self.jid=None
        if actor:
            self.actor=JID(actor)
        else:
            self.actor=None
        self.nick=nick
        self.reason=reason
Example #38
0
class MucItem(MucItemBase):
    """
    MUC <item/> element -- describes a room occupant.

    :Ivariables:
        - `affiliation`: affiliation of the user.
        - `role`: role of the user.
        - `jid`: JID of the user.
        - `nick`: nickname of the user.
        - `actor`: actor modyfying the user data.
        - `reason`: reason of change of the user data.
    :Types:
        - `affiliation`: `str`
        - `role`: `str`
        - `jid`: `JID`
        - `nick`: `unicode`
        - `actor`: `JID`
        - `reason`: `unicode`
    """
    def __init__(self,xmlnode_or_affiliation,role=None,jid=None,nick=None,actor=None,reason=None):
        """
        Initialize a `MucItem` object.

        :Parameters:
            - `xmlnode_or_affiliation`: XML node to be pased or the affiliation of
              the user being described.
            - `role`: role of the user.
            - `jid`: JID of the user.
            - `nick`: nickname of the user.
            - `actor`: actor modyfying the user data.
            - `reason`: reason of change of the user data.
        :Types:
            - `xmlnode_or_affiliation`: `libxml2.xmlNode` or `str`
            - `role`: `str`
            - `jid`: `JID`
            - `nick`: `unicode`
            - `actor`: `JID`
            - `reason`: `unicode`
        """
        self.jid,self.nick,self.actor,self.affiliation,self.reason,self.role=(None,)*6
        MucItemBase.__init__(self)
        if isinstance(xmlnode_or_affiliation,libxml2.xmlNode):
            self.__from_xmlnode(xmlnode_or_affiliation)
        else:
            self.__init(xmlnode_or_affiliation,role,jid,nick,actor,reason)

    def __init(self,affiliation,role,jid=None,nick=None,actor=None,reason=None):
        """Initialize a `MucItem` object from a set of attributes.

        :Parameters:
            - `affiliation`: affiliation of the user.
            - `role`: role of the user.
            - `jid`: JID of the user.
            - `nick`: nickname of the user.
            - `actor`: actor modyfying the user data.
            - `reason`: reason of change of the user data.
        :Types:
            - `affiliation`: `str`
            - `role`: `str`
            - `jid`: `JID`
            - `nick`: `unicode`
            - `actor`: `JID`
            - `reason`: `unicode`
        """
        if not affiliation:
            affiliation=None
        elif affiliation not in affiliations:
            raise ValueError,"Bad affiliation"
        self.affiliation=affiliation
        if not role:
            role=None
        elif role not in roles:
            raise ValueError,"Bad role"
        self.role=role
        if jid:
            self.jid=JID(jid)
        else:
            self.jid=None
        if actor:
            self.actor=JID(actor)
        else:
            self.actor=None
        self.nick=nick
        self.reason=reason

    def __from_xmlnode(self, xmlnode):
        """Initialize a `MucItem` object from an XML node.

        :Parameters:
            - `xmlnode`: the XML node.
        :Types:
            - `xmlnode`: `libxml2.xmlNode`
        """
        actor=None
        reason=None
        n=xmlnode.children
        while n:
            ns=n.ns()
            if ns and ns.getContent()!=MUC_USER_NS:
                continue
            if n.name=="actor":
                actor=n.getContent()
            if n.name=="reason":
                reason=n.getContent()
            n=n.next
        self.__init(
            from_utf8(xmlnode.prop("affiliation")),
            from_utf8(xmlnode.prop("role")),
            from_utf8(xmlnode.prop("jid")),
            from_utf8(xmlnode.prop("nick")),
            from_utf8(actor),
            from_utf8(reason),
            );

    def as_xml(self,parent):
        """
        Create XML representation of `self`.

        :Parameters:
            - `parent`: the element to which the created node should be linked to.
        :Types:
            - `parent`: `libxml2.xmlNode`

        :return: an XML node.
        :returntype: `libxml2.xmlNode`
        """
        n=parent.newChild(None,"item",None)
        if self.actor:
            n.newTextChild(None,"actor",to_utf8(self.actor))
        if self.reason:
            n.newTextChild(None,"reason",to_utf8(self.reason))
        n.setProp("affiliation",to_utf8(self.affiliation))
        if self.role:
            n.setProp("role",to_utf8(self.role))
        if self.jid:
            n.setProp("jid",to_utf8(self.jid.as_unicode()))
        if self.nick:
            n.setProp("nick",to_utf8(self.nick))
        return n
Example #39
0
    def __init__(self, name_or_xmlnode, from_jid=None, to_jid=None,
            stanza_type=None, stanza_id=None, error=None, error_cond=None,
            stream = None):
        """Initialize a Stanza object.

        :Parameters:
            - `name_or_xmlnode`: XML node to be wrapped into the Stanza object
              or other Presence object to be copied. If not given then new
              presence stanza is created using following parameters.
            - `from_jid`: sender JID.
            - `to_jid`: recipient JID.
            - `stanza_type`: staza type: one of: "get", "set", "result" or "error".
            - `stanza_id`: stanza id -- value of stanza's "id" attribute. If
              not given, then unique for the session value is generated.
            - `error`: error object. Ignored if `stanza_type` is not "error".
            - `error_cond`: error condition name. Ignored if `stanza_type` is not
              "error" or `error` is not None.
        :Types:
            - `name_or_xmlnode`: `unicode` or `libxml2.xmlNode` or `Stanza`
            - `from_jid`: `JID`
            - `to_jid`: `JID`
            - `stanza_type`: `unicode`
            - `stanza_id`: `unicode`
            - `error`: `pyxmpp.error.StanzaErrorNode`
            - `error_cond`: `unicode`"""
        self._error=None
        self.xmlnode=None
        if isinstance(name_or_xmlnode,Stanza):
            self.xmlnode=name_or_xmlnode.xmlnode.copyNode(1)
            common_doc.addChild(self.xmlnode)
        elif isinstance(name_or_xmlnode,libxml2.xmlNode):
            self.xmlnode=name_or_xmlnode.docCopyNode(common_doc,1)
            common_doc.addChild(self.xmlnode)
            try:
                ns = self.xmlnode.ns()
            except libxml2.treeError:
                ns = None
            if not ns or not ns.name:
                xmlextra.replace_ns(self.xmlnode, ns, common_ns)
        else:
            self.xmlnode=common_doc.newChild(common_ns,name_or_xmlnode,None)

        if from_jid is not None:
            if not isinstance(from_jid,JID):
                from_jid=JID(from_jid)
            self.xmlnode.setProp("from",from_jid.as_utf8())

        if to_jid is not None:
            if not isinstance(to_jid,JID):
                to_jid=JID(to_jid)
            self.xmlnode.setProp("to",to_jid.as_utf8())

        if stanza_type:
            self.xmlnode.setProp("type",stanza_type)

        if stanza_id:
            self.xmlnode.setProp("id",stanza_id)

        if self.get_type()=="error":
            from pyxmpp.error import StanzaErrorNode
            if error:
                self._error=StanzaErrorNode(error,parent=self.xmlnode,copy=1)
            elif error_cond:
                self._error=StanzaErrorNode(error_cond,parent=self.xmlnode)
        self.stream = stream
Example #40
0
class Delay(StanzaPayloadObject):
    """
    Delayed delivery tag.

    Represents 'urn:xmpp:delay' (XEP-0203) element of a Jabber stanza.

    :Ivariables:
        - `delay_from`: the "from" value of the delay element
        - `reason`: the "reason" (content) of the delay element
        - `timestamp`: the UTC timestamp as naive datetime object
    """

    xml_element_name = "delay"
    xml_element_namespace = DELAY_NS

    _sort_order = 1
    _time_format = "%Y-%m-%dT%H:%M:%SZ"

    def __init__(self,node_or_datetime,delay_from=None,reason=None,utc=True):
        """
        Initialize the Delay object.

        :Parameters:
            - `node_or_datetime`: an XML node to parse or the timestamp.
            - `delay_from`: JID of the entity which adds the delay mark
              (when `node_or_datetime` is a timestamp).
            - `reason`: reason of the delay (when `node_or_datetime` is a
              timestamp).
            - `utc`: if `True` then the timestamp is assumed to be UTC,
              otherwise it is assumed to be local time.
        :Types:
            - `node_or_datetime`: `libxml2.xmlNode` or `datetime.datetime`
            - `delay_from`: `pyxmpp.JID`
            - `reason`: `unicode`
            - `utc`: `bool`"""
        if isinstance(node_or_datetime,libxml2.xmlNode):
            self.from_xml(node_or_datetime)
        else:
            if utc:
                self.timestamp=node_or_datetime
            else:
                self.timestamp=datetime_local_to_utc(node_or_datetime)
            self.delay_from=JID(delay_from)
            self.reason=unicode(reason)

    def from_xml(self,xmlnode):
        """Initialize Delay object from an XML node.

        :Parameters:
            - `xmlnode`: the jabber:x:delay XML element.
        :Types:
            - `xmlnode`: `libxml2.xmlNode`"""
        if xmlnode.type!="element":
            raise ValueError,"XML node is not a jabber:x:delay element (not an element)"
        ns=get_node_ns_uri(xmlnode)
        if ns and (ns != self.xml_element_namespace
                or xmlnode.name != self.xml_element_name):
            raise ValueError,"XML node is not a " + self.xml_element_namespace + " element"
        stamp=xmlnode.prop("stamp")
        tm = _parse_ts(stamp)
        tm=tm[0:8]+(0,)
        self.timestamp=datetime.datetime.fromtimestamp(time.mktime(tm))
        delay_from=from_utf8(xmlnode.prop("from"))
        if delay_from:
            try:
                self.delay_from = JID(delay_from)
            except JIDError:
                raise JIDMalformedProtocolError, "Bad JID in the " + self.xml_element_namespace + " 'from' attribute"
        else:
            self.delay_from = None
        self.reason = from_utf8(xmlnode.getContent())

    def complete_xml_element(self, xmlnode, _unused):
        """Complete the XML node with `self` content.

        Should be overriden in classes derived from `StanzaPayloadObject`.

        :Parameters:
            - `xmlnode`: XML node with the element being built. It has already
              right name and namespace, but no attributes or content.
            - `_unused`: document to which the element belongs.
        :Types:
            - `xmlnode`: `libxml2.xmlNode`
            - `_unused`: `libxml2.xmlDoc`"""
        tm=self.timestamp.strftime(self._time_format)
        xmlnode.setProp("stamp",tm)
        if self.delay_from:
            xmlnode.setProp("from",self.delay_from.as_utf8())
        if self.reason:
            xmlnode.setContent(to_utf8(self.reason))

    def get_datetime_local(self):
        """Get the timestamp as a local time.

        :return: the timestamp of the delay element represented in the local
          timezone.
        :returntype: `datetime.datetime`"""
        r=datetime_utc_to_local(self.timestamp)
        return r

    def get_datetime_utc(self):
        """Get the timestamp as a UTC.

        :return: the timestamp of the delay element represented in UTC.
        :returntype: `datetime.datetime`"""
        return self.timestamp

    def __str__(self):
        n=self.as_xml()
        r=n.serialize()
        n.freeNode()
        return r

    def __cmp__(self, other):
        return cmp((self._sort_order, self.timestamp),
                    (other._sort_order, other.timestamp))
Example #41
0
class Network(JabberClient):
    def __init__(self, **args):
        self.isconnected = False

        # Create a unique jabber resource
        resource = args.get('resource') or 'python_client'
        resource += '_' + gethostname() + ':' + str(os.getpid()) + '_' + \
            threading.currentThread().getName().lower()
        self.jid = JID(args['username'], args['host'], resource)

        osrf.log.log_debug("initializing network with JID %s and host=%s, "
            "port=%s, username=%s" % (self.jid.as_utf8(), args['host'], \
            args['port'], args['username']))

        #initialize the superclass
        JabberClient.__init__(self, self.jid, args['password'], args['host'])
        self.queue = []

        self.receive_callback = None
        self.transport_error_msg = None

    def connect(self):
        JabberClient.connect(self)
        while not self.isconnected:
            stream = self.get_stream()
            act = stream.loop_iter(10)
            if not act:
                self.idle()

    def set_receive_callback(self, func):
        """The callback provided is called when a message is received.
        
            The only argument to the function is the received message. """
        self.receive_callback = func

    def session_started(self):
        osrf.log.log_info("Successfully connected to the opensrf network")
        self.authenticated()
        self.stream.set_message_handler("normal", self.message_received)
        self.stream.set_message_handler("error", self.error_received)
        self.isconnected = True

    def send(self, message):
        """Sends the provided network message."""
        osrf.log.log_internal("jabber sending to %s: %s" % (message.recipient, message.body))
        message.sender = self.jid.as_utf8()
        msg = message.make_xmpp_msg()
        self.stream.send(msg)

    def error_received(self, stanza):
        self.transport_error_msg = NetworkMessage(stanza)
        osrf.log.log_error("XMPP error message received from %s" % self.transport_error_msg.sender)
    
    def message_received(self, stanza):
        """Handler for received messages."""
        if stanza.get_type()=="headline":
            return True
        # check for errors
        osrf.log.log_internal("jabber received message from %s : %s" 
            % (stanza.get_from().as_utf8(), stanza.get_body()))
        self.queue.append(NetworkMessage(stanza))
        return True

    def stream_closed(self, stream):
        osrf.log.log_debug("XMPP Stream closing...")

    def stream_error(self, err):
        osrf.log.log_error("XMPP Stream error: condition: %s %r"
            % (err.get_condition().name,err.serialize()))

    def disconnected(self):
        osrf.log.log_internal('XMPP Disconnected')

    def recv(self, timeout=120):
        """Attempts to receive a message from the network.

        timeout - max number of seconds to wait for a message.  
        If a message is received in 'timeout' seconds, the message is passed to 
        the receive_callback is called and True is returned.  Otherwise, false is
        returned.
        """

        forever = False
        if timeout < 0:
            forever = True
            timeout = None

        if len(self.queue) == 0:
            while (forever or timeout >= 0) and len(self.queue) == 0:
                starttime = time.time()

                stream = self.get_stream()
                if not stream:
                   raise XMPPNoConnection('We lost our server connection...') 

                act = stream.loop_iter(timeout)
                endtime = time.time() - starttime

                if not forever:
                    timeout -= endtime

                osrf.log.log_internal("exiting stream loop after %s seconds. "
                    "act=%s, queue size=%d" % (str(endtime), act, len(self.queue)))

                if self.transport_error_msg:
                    msg = self.transport_error_msg
                    self.transport_error_msg = None
                    raise XMPPNoRecipient(msg.sender)

                if not act:
                    self.idle()

        # if we've acquired a message, handle it
        msg = None
        if len(self.queue) > 0:
            msg = self.queue.pop(0)
            if self.receive_callback:
                self.receive_callback(msg)

        return msg


    def flush_inbound_data(self):
        ''' Read all pending inbound messages from the socket and discard them '''
        cb = self.receive_callback
        self.receive_callback = None
        while self.recv(0): pass 
        self.receive_callback = cb