Пример #1
0
    def _broadcast_privacy_list_change(self, dest, src, node):
        # broadcast to all resolvers
        iq = domish.Element((None, 'iq'))
        iq['from'] = dest
        iq['type'] = 'set'
        iq['id'] = util.rand_str(8)
        nodeElement = iq.addElement((xmlstream2.NS_IQ_BLOCKING, node))
        elem = nodeElement.addElement((None, 'item'))
        elem['jid'] = src

        from_component = util.component_jid(self.servername, util.COMPONENT_C2S)
        for server in self.keyring.hostlist():
            if server != self.servername:
                iq['to'] = util.component_jid(server, util.COMPONENT_C2S)
                self.send_wrapped(iq, from_component)
Пример #2
0
    def broadcast_public_key(self, userid, keydata):
        """Broadcasts to all remote c2s the given public key."""
        # create vcard
        iq_vcard = domish.Element((None, 'iq'))
        iq_vcard['type'] = 'set'
        iq_vcard['from'] = util.userid_to_jid(
            userid, self.xmlstream.thisEntity.host).full()

        # add vcard
        vcard = iq_vcard.addElement((xmlstream2.NS_XMPP_VCARD4, 'vcard'))
        if keydata:
            vcard_key = vcard.addElement((None, 'key'))
            vcard_data = vcard_key.addElement((None, 'uri'))
            vcard_data.addContent(xmlstream2.DATA_PGP_PREFIX +
                                  base64.b64encode(keydata))

        # send vcard to remote c2s
        for server in self.keyring.hostlist():
            if server != self.servername:
                iq_vcard['id'] = util.rand_str(8)
                # remote server
                iq_vcard['to'] = util.component_jid(server, util.COMPONENT_C2S)
                # consume any response (very high priority)
                self.xmlstream.addOnetimeObserver(
                    "/iq[@id='%s']" % iq_vcard['id'], self.consume, 500)
                # send!
                self.send_wrapped(iq_vcard, self.xmlstream.thisEntity.full())
Пример #3
0
 def forward_check(self, stanza, fn, componentfn):
     if not stanza.consumed:
         if stanza['to'] == util.component_jid(self.parent.servername,
                                               util.COMPONENT_C2S):
             return componentfn(stanza)
         else:
             return fn(stanza)
Пример #4
0
    def handle(self, stanza):
        # enforce sender
        stanza['from'] = self.resolveJID(self.xmlstream.otherEntity).full()

        to = stanza.getAttribute('to')
        if to is not None:
            try:
                to = jid.JID(to)
            except:
                # invalid destination, consume stanza and return error
                stanza.consumed = True
                log.debug("invalid address: %s" % (to, ))
                e = error.StanzaError('jid-malformed', 'modify')
                self.send(e.toResponse(stanza))
                return

            # stanza is for us
            if to.host == self.network:
                # sending to full JID, forward to router
                if to.user is not None and to.resource is not None:
                    self.forward(stanza)

            # stanza is not intended to component either
            elif to.host != util.component_jid(self.servername,
                                               util.COMPONENT_C2S):
                self.forward(stanza)
Пример #5
0
 def resolveJID(self, _jid):
     """Transform host attribute of JID from network name to component name."""
     host = util.component_jid(self.servername, util.COMPONENT_C2S)
     if isinstance(_jid, jid.JID):
         return jid.JID(tuple=(_jid.user, host, _jid.resource))
     else:
         _jid = jid.JID(_jid)
         _jid.host = host
         return _jid
Пример #6
0
    def _presence_data(self, presence):
        log.debug("presence: %r" % (presence, ))
        if type(presence) == list and len(presence) > 0:

            for user in presence:
                # store fingerprint
                self.keyring.set_fingerprint(user['userid'],
                                             user['fingerprint'])

                host = util.component_jid(self.servername, util.COMPONENT_C2S)
                response_from = jid.JID(tuple=(user['userid'], host,
                                               None)).full()

                num_avail = 0
                try:
                    streams = self.sfactory.streams[user['userid']]
                    for x in streams.itervalues():
                        presence = x._presence
                        if presence and not presence.hasAttribute('type'):
                            self.cache.user_available(presence)

                            if self.logTraffic:
                                log.debug("local presence: %s" %
                                          (presence.toXml().encode('utf-8'), ))
                            else:
                                log.debug("local presence: %s" %
                                          (presence['from'], ))

                            num_avail += 1
                except KeyError:
                    pass

                # no available resources - send unavailable presence
                if not num_avail:
                    response = domish.Element((None, 'presence'))
                    response['from'] = response_from

                    if user['status'] is not None:
                        response.addElement((None, 'status'),
                                            content=user['status'])
                    if user['show'] is not None:
                        response.addElement((None, 'show'),
                                            content=user['show'])

                    response['type'] = 'unavailable'
                    delay = domish.Element(('urn:xmpp:delay', 'delay'))
                    delay['stamp'] = user['timestamp'].strftime(
                        xmlstream2.XMPP_STAMP_FORMAT)
                    response.addChild(delay)

                    self.cache.user_unavailable(response)

                    if self.logTraffic:
                        log.debug("local presence: %s" %
                                  (response.toXml().encode('utf-8'), ))
                    else:
                        log.debug("local presence: %s" % (response['from'], ))
Пример #7
0
    def allow(self, stanza, sender=None):
        jid_from = jid.JID(stanza['from'])
        broadcast = (jid_from.host == util.component_jid(self.parent.servername, util.COMPONENT_C2S))
        items = stanza.allow.elements(uri=xmlstream2.NS_IQ_BLOCKING, name='item')
        if items:
            self._whitelist(jid_from, items, broadcast=broadcast)

        if broadcast:
            self.parent.result(stanza)
Пример #8
0
    def unblock(self, stanza):
        jid_from = jid.JID(stanza['from'])
        broadcast = (jid_from.host == util.component_jid(self.parent.servername, util.COMPONENT_C2S))
        items = stanza.unblock.elements(uri=xmlstream2.NS_IQ_BLOCKING, name='item')
        if items:
            self._blacklist(jid_from, items, True, broadcast=broadcast)

        if broadcast:
            self.parent.result(stanza)
Пример #9
0
 def resolveJID(self, _jid):
     """Transform host attribute of JID from network name to component name."""
     host = util.component_jid(self.servername, util.COMPONENT_C2S)
     if isinstance(_jid, jid.JID):
         return jid.JID(tuple=(_jid.user, host, _jid.resource))
     else:
         _jid = jid.JID(_jid)
         _jid.host = host
         return _jid
Пример #10
0
    def _presence_data(self, presence):
        log.debug("presence: %r" % (presence, ))
        if type(presence) == list and len(presence) > 0:

            for user in presence:
                # store fingerprint
                self.keyring.set_fingerprint(user['userid'], user['fingerprint'])

                host = util.component_jid(self.servername, util.COMPONENT_C2S)
                response_from = jid.JID(tuple=(user['userid'], host, None)).full()

                num_avail = 0
                try:
                    streams = self.sfactory.streams[user['userid']]
                    for x in streams.itervalues():
                        presence = x._presence
                        if presence and not presence.hasAttribute('type'):
                            self.cache.user_available(presence)

                            if self.logTraffic:
                                log.debug("local presence: %s" % (presence.toXml().encode('utf-8'), ))
                            else:
                                log.debug("local presence: %s" % (presence['from'], ))

                            num_avail += 1
                except KeyError:
                    pass

                # no available resources - send unavailable presence
                if not num_avail:
                    response = domish.Element((None, 'presence'))
                    response['from'] = response_from

                    if user['status'] is not None:
                        response.addElement((None, 'status'), content=user['status'])
                    if user['show'] is not None:
                        response.addElement((None, 'show'), content=user['show'])

                    response['type'] = 'unavailable'
                    delay = domish.Element(('urn:xmpp:delay', 'delay'))
                    delay['stamp'] = user['timestamp'].strftime(xmlstream2.XMPP_STAMP_FORMAT)
                    response.addChild(delay)

                    self.cache.user_unavailable(response)

                    if self.logTraffic:
                        log.debug("local presence: %s" % (response.toXml().encode('utf-8'), ))
                    else:
                        log.debug("local presence: %s" % (response['from'], ))
Пример #11
0
    def send_privacy_lists(self, pname, plist, addr_from):
        sender = util.component_jid(self.parent.servername, util.COMPONENT_C2S)
        for user, wl in plist.iteritems():
            iq = domish.Element((None, 'iq'))
            iq['from'] = '%s@%s' % (user, self.parent.network)
            iq['type'] = 'set'
            iq['id'] = util.rand_str(8)
            iq['to'] = addr_from
            allow = iq.addElement((xmlstream2.NS_IQ_BLOCKING, pname))

            for item in wl:
                elem = allow.addElement((None, 'item'))
                elem['jid'] = item

            self.parent.send_wrapped(iq, sender)
Пример #12
0
    def handle(self, stanza):
        to = stanza.getAttribute('to')
        if to is not None:
            try:
                to = jid.JID(to)
            except:
                # invalid destination, consume stanza and return error
                stanza.consumed = True
                log.debug("invalid address: %s" % (to, ))
                e = error.StanzaError('jid-malformed', 'modify')
                self.send(e.toResponse(stanza))
                return

            # stanza is for us
            if to.host == self.network:
                # sending to full JID, forward to router
                if to.user is not None and to.resource is not None:
                    self.forward(stanza)

            # stanza is not intended to component either
            elif to.host != util.component_jid(self.servername, util.COMPONENT_C2S):
                self.forward(stanza)
Пример #13
0
    def broadcast_public_key(self, userid, keydata):
        """Broadcasts to all remote c2s the given public key."""
        # create vcard
        iq_vcard = domish.Element((None, 'iq'))
        iq_vcard['type'] = 'set'
        iq_vcard['from'] = util.userid_to_jid(userid, self.xmlstream.thisEntity.host).full()

        # add vcard
        vcard = iq_vcard.addElement((xmlstream2.NS_XMPP_VCARD4, 'vcard'))
        if keydata:
            vcard_key = vcard.addElement((None, 'key'))
            vcard_data = vcard_key.addElement((None, 'uri'))
            vcard_data.addContent(xmlstream2.DATA_PGP_PREFIX + base64.b64encode(keydata))

        # send vcard to remote c2s
        for server in self.keyring.hostlist():
            if server != self.servername:
                iq_vcard['id'] = util.rand_str(8)
                # remote server
                iq_vcard['to'] = util.component_jid(server, util.COMPONENT_C2S)
                # consume any response (very high priority)
                self.xmlstream.addOnetimeObserver("/iq[@id='%s']" % iq_vcard['id'], self.consume, 500)
                # send!
                self.send_wrapped(iq_vcard, self.xmlstream.thisEntity.full())
Пример #14
0
 def bindNetRoute(self, host):
     name = util.component_jid(host, util.COMPONENT_NET)
     self.router.bind(name)
Пример #15
0
    def send(self, stanza, force_delivery=False, force_bare=False, hold=False):
        """
        Resolves stanza recipient and send the stanza to the router.
        @param stanza: the stanza to be sent
        @type stanza: domish.Element
        @param force_delivery: if true, deliver the stanza to the first available resource found if the intended one
        is not available
        @type force_delivery bool
        @param force_bare: if true, deliver the stanza to the bare JID despite of the indicated destination
        @type force_bare bool
        @param hold: this parameter is passed directly to the dispatch method, instructing it to not deliver the stanza
        and hold it in offline storage instead (e.g. delayed/unauthorized messages)
        @type hold bool
        """

        # send raw xml if you really know what you are doing
        if not domish.IElement.providedBy(stanza):
            return component.Component.send(self, stanza)

        util.resetNamespace(stanza, component.NS_COMPONENT_ACCEPT)

        # force host in sender
        component_jid = util.component_jid(self.servername, util.COMPONENT_C2S)
        sender = jid.JID(stanza['from'])
        if sender.host == self.network:
            sender.host = component_jid
            stanza['from'] = sender.full()

        if not stanza.hasAttribute('to'):
            # no destination - send to router
            component.Component.send(self, stanza)
            return

        # save original recipient for later
        stanza['original-to'] = stanza['to']
        to = jid.JID(stanza['to'])

        # stanza is intended to the network
        if to.full() == self.network:
            # TODO
            log.debug("stanza for the network: %s" % (stanza.toXml(), ))
            return

        # network JID - resolve and send to router
        elif to.host == self.network:
            rcpts = self.cache.lookup(to)
            log.debug("rcpts = %r" % (rcpts, ))

            if not rcpts:
                if not stanza.consumed:
                    stanza.consumed = True
                    log.debug("JID %s not found" % (to.full(), ))
                    e = error.StanzaError('item-not-found', 'cancel')
                    self.dispatch(e.toResponse(stanza))
                else:
                    log.debug("JID %s not found (stanza has been consumed)" % (to.full(), ))
                    return
            else:
                """
                Stanza delivery rules
                1. deliver to all available resources
                2. destination was a bare JID
                  a. if all resources are unavailable, deliver to the first network bare JID (then return)
                3. destination was a full JID:
                  a. deliver to full JID
                """
                log.debug("JID found: %r" % (rcpts, ))
                jids = rcpts.jids()

                # destination was a full JID
                if to.resource and not force_bare:
                    # no available resources, deliver to bare JID if force delivery
                    if len(jids) == 0 and force_delivery:
                        stanza['to'] = rcpts.jid.userhost()
                        self.dispatch(stanza, hold=hold)
                    # deliver if resource is available
                    else:
                        sent = False
                        for _to in jids:
                            if _to.resource == to.resource:
                                stanza['to'] = _to.full()
                                self.dispatch(stanza, hold=hold)
                                sent = True
                                break

                        # if sent=False it means that the intended resource has vanished
                        # if force delivery is enabled, deliver to the first available resource
                        if not sent and len(jids) > 0 and force_delivery:
                            stanza['to'] = jids[0].full()
                            self.dispatch(stanza, hold=hold)

                # destination was a bare JID
                else:
                    log.debug("destination was a bare JID (force_bare=%s)" % (force_bare, ))
                    log.debug("jids = %s, rcpts = %s" % (jids, rcpts))
                    # no available resources, send to first network bare JID
                    if len(jids) == 0 or force_bare:
                        stanza['to'] = rcpts.jid.userhost()
                        self.dispatch(stanza, hold=hold)
                    else:
                        for _to in jids:
                            stanza['to'] = _to.full()
                            self.dispatch(stanza, hold=hold)

        # otherwise send to router
        else:
            self.dispatch(stanza, hold=hold)
Пример #16
0
    def local_vcard(self, user, stanza):
        """
        Called by SM when receiving a vCard from a local client.
        It checks vcard info (including public key) and if positive, sends the
        vCard to all remote c2s.
        @return: iq result or error stanza for the client
        """
        if self.logTraffic:
            log.debug("client sent vcard: %s" % (stanza.toXml(), ))
        else:
            log.debug("client sent vcard: %s" % (stanza['to'], ))
        stanza.consumed = True

        stanza_to = stanza.getAttribute('to')
        if stanza_to:
            entity = jid.JID(stanza_to)
        else:
            entity = None

        # setting our own vcard
        if not entity or (entity.userhost() == user.userhost()):
            # TODO parse vcard for interesting sections

            if stanza.vcard.key is not None:
                # we do this because of the uri member in domish.Element
                keydata = stanza.vcard.key.firstChildElement()
                if keydata and keydata.name == 'uri':
                    keydata = str(keydata)

                    if keydata.startswith(xmlstream2.DATA_PGP_PREFIX):
                        try:
                            keydata = base64.b64decode(keydata[len(xmlstream2.DATA_PGP_PREFIX):])
                        except:
                            log.debug("invalid base64 data")
                            e = xmlstream.error.StanzaError('bad-request', text='Invalid public key.')
                            iq = xmlstream.toResponse(stanza, 'error')
                            iq.addChild(e.getElement())
                            return iq

                        # check key
                        fp = self.keyring.check_user_key(keydata, user.user)
                        if fp:
                            # generate response beforing tampering with the stanza
                            response = xmlstream.toResponse(stanza, 'result')
                            # update presencedb
                            self.presencedb.public_key(user.user, fp)

                            # send vcard to all remote c2s
                            for server in self.keyring.hostlist():
                                if server != self.servername:
                                    stanza['id'] = util.rand_str(8)
                                    stanza['to'] = util.component_jid(server, util.COMPONENT_C2S)
                                    # consume any response (very high priority)
                                    self.xmlstream.addOnetimeObserver("/iq[@id='%s']" % stanza['id'], self.consume, 500)

                                    # wrap stanza in an envelope because we want errors to return to us
                                    self.send_wrapped(stanza, self.xmlstream.thisEntity.full())

                            # send response
                            return response
                        else:
                            log.debug("invalid key - authorization to vCard denied")
                            e = xmlstream.error.StanzaError('bad-request', text='Invalid public key.')
                            iq = xmlstream.toResponse(stanza, 'error')
                            iq.addChild(e.getElement())
                            return iq
        else:
            log.debug("authorization to vCard denied")
            e = xmlstream.error.StanzaError('not-allowed', text='Not authorized.')
            iq = xmlstream.toResponse(stanza, 'error')
            iq.addChild(e.getElement())
            return iq
Пример #17
0
 def bindNetRoute(self, host):
     name = util.component_jid(host, util.COMPONENT_NET)
     self.router.bind(name)
Пример #18
0
    def local_vcard(self, user, stanza):
        """
        Called by SM when receiving a vCard from a local client.
        It checks vcard info (including public key) and if positive, sends the
        vCard to all remote c2s.
        @return: iq result or error stanza for the client
        """
        if self.logTraffic:
            log.debug("client sent vcard: %s" % (stanza.toXml(), ))
        else:
            log.debug("client sent vcard: %s" % (stanza['to'], ))
        stanza.consumed = True

        stanza_to = stanza.getAttribute('to')
        if stanza_to:
            entity = jid.JID(stanza_to)
        else:
            entity = None

        # setting our own vcard
        if not entity or (entity.userhost() == user.userhost()):
            # TODO parse vcard for interesting sections

            if stanza.vcard.key is not None:
                # we do this because of the uri member in domish.Element
                keydata = stanza.vcard.key.firstChildElement()
                if keydata and keydata.name == 'uri':
                    keydata = str(keydata)

                    if keydata.startswith(xmlstream2.DATA_PGP_PREFIX):
                        try:
                            keydata = base64.b64decode(
                                keydata[len(xmlstream2.DATA_PGP_PREFIX):])
                        except:
                            log.debug("invalid base64 data")
                            e = xmlstream.error.StanzaError(
                                'bad-request', text='Invalid public key.')
                            iq = xmlstream.toResponse(stanza, 'error')
                            iq.addChild(e.getElement())
                            return iq

                        # check key
                        fp = self.keyring.check_user_key(keydata, user.user)
                        if fp:
                            # generate response beforing tampering with the stanza
                            response = xmlstream.toResponse(stanza, 'result')
                            # update presencedb
                            self.presencedb.public_key(user.user, fp)

                            # send vcard to all remote c2s
                            for server in self.keyring.hostlist():
                                if server != self.servername:
                                    stanza['id'] = util.rand_str(8)
                                    stanza['to'] = util.component_jid(
                                        server, util.COMPONENT_C2S)
                                    # consume any response (very high priority)
                                    self.xmlstream.addOnetimeObserver(
                                        "/iq[@id='%s']" % stanza['id'],
                                        self.consume, 500)

                                    # wrap stanza in an envelope because we want errors to return to us
                                    self.send_wrapped(
                                        stanza,
                                        self.xmlstream.thisEntity.full())

                            # send response
                            return response
                        else:
                            log.debug(
                                "invalid key - authorization to vCard denied")
                            e = xmlstream.error.StanzaError(
                                'bad-request', text='Invalid public key.')
                            iq = xmlstream.toResponse(stanza, 'error')
                            iq.addChild(e.getElement())
                            return iq
        else:
            log.debug("authorization to vCard denied")
            e = xmlstream.error.StanzaError('not-allowed',
                                            text='Not authorized.')
            iq = xmlstream.toResponse(stanza, 'error')
            iq.addChild(e.getElement())
            return iq
Пример #19
0
    def send(self, stanza, force_delivery=False, force_bare=False, hold=False):
        """
        Resolves stanza recipient and send the stanza to the router.
        @param stanza: the stanza to be sent
        @type stanza: domish.Element
        @param force_delivery: if true, deliver the stanza to the first available resource found if the intended one
        is not available
        @type force_delivery bool
        @param force_bare: if true, deliver the stanza to the bare JID despite of the indicated destination
        @type force_bare bool
        @param hold: this parameter is passed directly to the dispatch method, instructing it to not deliver the stanza
        and hold it in offline storage instead (e.g. delayed/unauthorized messages)
        @type hold bool
        """

        # send raw xml if you really know what you are doing
        if not domish.IElement.providedBy(stanza):
            return component.Component.send(self, stanza)

        util.resetNamespace(stanza, component.NS_COMPONENT_ACCEPT)

        # force host in sender
        component_jid = util.component_jid(self.servername, util.COMPONENT_C2S)
        sender = jid.JID(stanza['from'])
        if sender.host == self.network:
            sender.host = component_jid
            stanza['from'] = sender.full()

        if not stanza.hasAttribute('to'):
            # no destination - send to router
            component.Component.send(self, stanza)
            return

        # save original recipient for later
        stanza['original-to'] = stanza['to']
        to = jid.JID(stanza['to'])

        # stanza is intended to the network
        if to.full() == self.network:
            # TODO
            log.debug("stanza for the network: %s" % (stanza.toXml(), ))
            return

        # network JID - resolve and send to router
        elif to.host == self.network:
            rcpts = self.cache.lookup(to)
            log.debug("rcpts = %r" % (rcpts, ))

            if not rcpts:
                if not stanza.consumed:
                    stanza.consumed = True
                    log.debug("JID %s not found" % (to.full(), ))
                    e = error.StanzaError('item-not-found', 'cancel')
                    self.dispatch(e.toResponse(stanza))
                else:
                    log.debug("JID %s not found (stanza has been consumed)" %
                              (to.full(), ))
                    return
            else:
                """
                Stanza delivery rules
                1. deliver to all available resources
                2. destination was a bare JID
                  a. if all resources are unavailable, deliver to the first network bare JID (then return)
                3. destination was a full JID:
                  a. deliver to full JID
                """
                log.debug("JID found: %r" % (rcpts, ))
                jids = rcpts.jids()

                # destination was a full JID
                if to.resource and not force_bare:
                    # no available resources, deliver to bare JID if force delivery
                    if len(jids) == 0 and force_delivery:
                        stanza['to'] = rcpts.jid.userhost()
                        self.dispatch(stanza, hold=hold)
                    # deliver if resource is available
                    else:
                        sent = False
                        for _to in jids:
                            if _to.resource == to.resource:
                                stanza['to'] = _to.full()
                                self.dispatch(stanza, hold=hold)
                                sent = True
                                break

                        # if sent=False it means that the intended resource has vanished
                        # if force delivery is enabled, deliver to the first available resource
                        if not sent and len(jids) > 0 and force_delivery:
                            stanza['to'] = jids[0].full()
                            self.dispatch(stanza, hold=hold)

                # destination was a bare JID
                else:
                    log.debug("destination was a bare JID (force_bare=%s)" %
                              (force_bare, ))
                    log.debug("jids = %s, rcpts = %s" % (jids, rcpts))
                    # no available resources, send to first network bare JID
                    if len(jids) == 0 or force_bare:
                        stanza['to'] = rcpts.jid.userhost()
                        self.dispatch(stanza, hold=hold)
                    else:
                        for _to in jids:
                            stanza['to'] = _to.full()
                            self.dispatch(stanza, hold=hold)

        # otherwise send to router
        else:
            self.dispatch(stanza, hold=hold)
Пример #20
0
 def forward_check(self, stanza, fn, componentfn):
     if not stanza.consumed:
         if stanza['to'] == util.component_jid(self.parent.servername, util.COMPONENT_C2S):
             return componentfn(stanza)
         else:
             return fn(stanza)
Пример #21
0
 def test_component_jid(self):
     host = 'localhost.localdomain'
     component = 'c2s'
     data = util.component_jid(host, component)
     self.assertEqual(data, 'c2s.localhost.localdomain')
Пример #22
0
 def test_component_jid(self):
     host = 'localhost.localdomain'
     component = 'c2s'
     data = util.component_jid(host, component)
     self.assertEqual(data, 'c2s.localhost.localdomain')