Пример #1
0
    def roster(self, stanza):
        # enforce destination (resolver)
        stanza['to'] = self.parent.network

        if not xmlstream2.has_element(stanza.query, uri=xmlstream2.NS_IQ_ROSTER, name='item'):
            # requesting initial roster - enter XMPP compatibility mode
            self.parent.compatibility_mode = True

        # forward to resolver
        self.parent.forward(stanza)
Пример #2
0
    def roster(self, stanza):
        # enforce destination (resolver)
        stanza['to'] = self.parent.network

        if not xmlstream2.has_element(stanza.query, uri=xmlstream2.NS_IQ_ROSTER, name='item'):
            # requesting initial roster - enter XMPP compatibility mode
            self.parent.compatibility_mode = True

        # forward to resolver
        self.parent.forward(stanza)
Пример #3
0
    def roster(self, stanza):
        if not xmlstream2.has_element(
                stanza.query, uri=xmlstream2.NS_IQ_ROSTER, name='item'):
            # requesting initial roster - enter XMPP compatibility mode
            self.parent.compatibility_mode = True

        _items = stanza.query.elements(uri=xmlstream2.NS_IQ_ROSTER,
                                       name='item')
        requester = jid.JID(stanza['from'])
        stanza.consumed = True

        # items present, requesting roster lookup
        response = xmlstream.toResponse(stanza, 'result')
        roster = response.addElement((xmlstream2.NS_IQ_ROSTER, 'query'))

        probes = []
        # this will be true if roster lookup is requested
        roster_lookup = False
        for item in _items:
            # items present, meaning roster lookup
            roster_lookup = True

            itemJid = jid.internJID(item['jid'])

            # include the entry in the roster reply anyway
            entry = self.parent.router.cache.lookup(itemJid)
            if entry:
                allowed = self.parent.router.is_presence_allowed(
                    requester, itemJid)
                if allowed != -1:
                    item = roster.addElement((None, 'item'))
                    item['jid'] = self.parent.router.translateJID(
                        entry.jid).userhost()

                if allowed == 1:
                    probes.append(entry.presence())

        # roster lookup, send presence data and vcards
        if roster_lookup:

            # lookup response
            self.send(response)

            # simulate a presence probe and send vcards
            # we'll use one group ID so the client knows when to stop waiting
            gid = stanza.getAttribute('id')
            if not gid:
                gid = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE)

            i = sum([len(x) for x in probes])
            for presence_list in probes:
                for presence in presence_list:
                    presence = deepcopy(presence)
                    presence['to'] = stanza['from']
                    group = presence.addElement(
                        (xmlstream2.NS_XMPP_STANZA_GROUP, 'group'))
                    group['id'] = gid
                    group['count'] = str(i)
                    i -= 1
                    self.send(presence)

                # send vcard for this user
                jid_from = jid.JID(presence_list[0]['from'])
                iq = domish.Element((None, 'iq'))
                iq['type'] = 'set'
                iq['from'] = jid_from.userhost()
                iq['to'] = stanza['from']
                try:
                    self.parent.router.build_vcard(jid_from.user, iq)
                    self.send(iq)
                except keyring.KeyNotFoundException:
                    pass

        # no roster lookup, XMPP standard roster instead
        else:

            # include items from the user's whitelist
            wl = self.parent.router.get_whitelist(requester)
            probes = None
            if wl:
                subscriptions = []
                for e in wl:
                    item = roster.addElement((None, 'item'))
                    item['jid'] = e

                    itemJid = jid.JID(e)

                    # check if subscription status is 'both' or just 'from'
                    allowed = self.parent.router.is_presence_allowed(
                        requester, itemJid)
                    if allowed == 1:
                        status = 'both'
                    else:
                        status = 'from'

                    # TODO include name from PGP key?
                    item['subscription'] = status

                    # add to subscription list
                    subscriptions.append(itemJid)

            # send the roster
            self.send(response)

            # subscribe to all users (without sending subscribed stanza of course)
            if wl:
                for itemJid in subscriptions:
                    self.parent.router.subscribe(requester,
                                                 itemJid,
                                                 send_subscribed=False)
Пример #4
0
    def process_message(self, stanza, hold=False):
        if stanza.hasAttribute('to'):
            to = jid.JID(stanza['to'])
            # process only our JIDs
            if util.jid_local(util.COMPONENT_C2S, self, to):
                chat_msg = (stanza.getAttribute('type') == 'chat')
                if to.user is not None:
                    keepId = None
                    receipt = xmlstream2.extract_receipt(stanza, 'request')
                    received = xmlstream2.extract_receipt(stanza, 'received')
                    has_storage = xmlstream2.has_element(
                        stanza, xmlstream2.NS_XMPP_STORAGE, 'storage')
                    try:
                        """
                        We are deliberately ignoring messages with sent
                        receipt because they are supposed to be volatile.
                        """
                        if chat_msg and not has_storage and (receipt
                                                             or received):
                            """
                            Apply generated id if we are getting a received receipt.
                            This way stanza is received by the client with the
                            correct id to cancel preemptive storage.
                            """
                            if received:
                                keepId = stanza['id'] = util.rand_str(
                                    30, util.CHARSBOX_AZN_LOWERCASE)

                            # send message to offline storage just to be safe (delayed)
                            keepId = self.message_offline_store(stanza,
                                                                delayed=True,
                                                                reuseId=keepId)

                        if hold:
                            raise Exception()

                        # send message to sm only to non-negative resources
                        log.debug("sending message %s" % (stanza['id'], ))
                        self.sfactory.dispatch(stanza)

                    except:
                        # manager not found or holding -- send to offline storage
                        if hold:
                            log.debug("holding stanza for %s" %
                                      (stanza['to'], ))
                        else:
                            log.debug("c2s manager for %s not found" %
                                      (stanza['to'], ))
                        """
                        Since our previous call to message_offline_store()
                        was with delayed parameter, we need to store for
                        real now.
                        Do not store messages from local storage because
                        """
                        if chat_msg and not has_storage and (stanza.body
                                                             or stanza.e2e
                                                             or received):
                            self.message_offline_store(stanza,
                                                       delayed=False,
                                                       reuseId=keepId)
                        if self.push_manager and chat_msg and (
                                stanza.body or stanza.e2e) and (
                                    not receipt or receipt.name == 'request'):
                            self.push_manager.notify(to)

                    # if message is a received receipt, we can delete the original message
                    if chat_msg and received:
                        # delete the received message
                        # TODO safe delete with sender/recipient
                        self.message_offline_delete(received['id'],
                                                    stanza.name)

                    stamp = time.time()
                    """
                    Receipts will be sent only if message is not coming from
                    storage or message is from a remote server.
                    This is because if the message is coming from storage,
                    it means that it's a user collecting its offline
                    messages, so we don't need to send a <sent/> again.
                    If a message is coming from a remote server, it means
                    that is being delivered by a remote c2s by either:
                     * sm request (direct message from client)
                     * offline delivery (triggered by an initial presence from this server)
                    """
                    host = util.jid_host(stanza['from'])

                    from_storage = xmlstream2.has_element(
                        stanza, xmlstream2.NS_XMPP_STORAGE, 'storage')

                    try:
                        log.debug("host(unparsed): %s" % (host, ))
                        unused, host = util.jid_component(
                            host, util.COMPONENT_C2S)
                        log.debug("host(parsed): %s" % (host, ))
                        from_remote = host != self.servername
                    except:
                        from_remote = False

                    if chat_msg and (not from_storage or from_remote):

                        # send ack only for chat messages (if requested)
                        # do not send if coming from remote storage
                        if receipt and not from_storage:
                            self.send_ack(stanza, 'sent', stamp)

                        # send receipt to originating server, if requested
                        receipt = None
                        # receipt request: send <sent/>
                        if stanza.request:
                            receipt = stanza.request
                            request = 'request'
                            delivery = 'sent'
                        # received receipt: send <ack/>
                        elif stanza.received:
                            receipt = stanza.received
                            request = 'received'
                            delivery = 'ack'

                        # now send what we prepared
                        if receipt:
                            try:
                                from_server = receipt['from']
                                if not util.hostjid_local(
                                        util.COMPONENT_C2S, self, from_server):
                                    stanza['from'] = from_server
                                    self.send_ack(stanza, delivery, stamp,
                                                  request)
                            except KeyError:
                                pass

                else:
                    # deliver local stanza
                    self.local(stanza)
                """
                If message is a receipt coming from a remote server, delete
                the message from our storage.
                """
                r_sent = xmlstream2.extract_receipt(stanza, 'sent')
                if chat_msg and r_sent:
                    sender_host = util.jid_host(stanza['from'])
                    """
                    We are receiving a sent receipt from another server,
                    meaning that the server has now responsibility for the
                    message - we can delete it now.
                    Special case is the sender domain being the network
                    domain, meaning the resolver rejected the message.
                    """
                    unused, sender_host = util.jid_component(sender_host)
                    if sender_host != self.servername:
                        log.debug(
                            "remote server now has responsibility for message %s - deleting"
                            % (r_sent['id'], ))
                        # TODO safe delete with sender/recipient
                        self.message_offline_delete(r_sent['id'], stanza.name)

            else:
                log.debug("stanza is not our concern or is an error")
Пример #5
0
    def process_message(self, stanza, hold=False):
        if stanza.hasAttribute('to'):
            to = jid.JID(stanza['to'])
            # process only our JIDs
            if util.jid_local(util.COMPONENT_C2S, self, to):
                chat_msg = (stanza.getAttribute('type') == 'chat')
                if to.user is not None:
                    keepId = None
                    receipt = xmlstream2.extract_receipt(stanza, 'request')
                    received = xmlstream2.extract_receipt(stanza, 'received')
                    has_storage = xmlstream2.has_element(stanza, xmlstream2.NS_XMPP_STORAGE, 'storage')
                    try:
                        """
                        We are deliberately ignoring messages with sent
                        receipt because they are supposed to be volatile.
                        """
                        if chat_msg and not has_storage and (receipt or received):
                            """
                            Apply generated id if we are getting a received receipt.
                            This way stanza is received by the client with the
                            correct id to cancel preemptive storage.
                            """
                            if received:
                                keepId = stanza['id'] = util.rand_str(30, util.CHARSBOX_AZN_LOWERCASE)

                            # send message to offline storage just to be safe (delayed)
                            keepId = self.message_offline_store(stanza, delayed=True, reuseId=keepId)

                        if hold:
                            raise Exception()

                        # send message to sm only to non-negative resources
                        log.debug("sending message %s" % (stanza['id'], ))
                        self.sfactory.dispatch(stanza)

                    except:
                        # manager not found or holding -- send to offline storage
                        if hold:
                            log.debug("holding stanza for %s" % (stanza['to'], ))
                        else:
                            log.debug("c2s manager for %s not found" % (stanza['to'], ))

                        """
                        Since our previous call to message_offline_store()
                        was with delayed parameter, we need to store for
                        real now.
                        Do not store messages from local storage because
                        """
                        if chat_msg and not has_storage and (stanza.body or stanza.e2e or received):
                            self.message_offline_store(stanza, delayed=False, reuseId=keepId)
                        if self.push_manager and chat_msg and (stanza.body or stanza.e2e) and (not receipt or receipt.name == 'request'):
                            self.push_manager.notify(to)

                    # if message is a received receipt, we can delete the original message
                    if chat_msg and received:
                        # delete the received message
                        # TODO safe delete with sender/recipient
                        self.message_offline_delete(received['id'], stanza.name)

                    stamp = time.time()

                    """
                    Receipts will be sent only if message is not coming from
                    storage or message is from a remote server.
                    This is because if the message is coming from storage,
                    it means that it's a user collecting its offline
                    messages, so we don't need to send a <sent/> again.
                    If a message is coming from a remote server, it means
                    that is being delivered by a remote c2s by either:
                     * sm request (direct message from client)
                     * offline delivery (triggered by an initial presence from this server)
                    """
                    host = util.jid_host(stanza['from'])

                    from_storage = xmlstream2.has_element(stanza, xmlstream2.NS_XMPP_STORAGE, 'storage')

                    try:
                        log.debug("host(unparsed): %s" % (host, ))
                        unused, host = util.jid_component(host, util.COMPONENT_C2S)
                        log.debug("host(parsed): %s" % (host, ))
                        from_remote = host != self.servername
                    except:
                        from_remote = False

                    if chat_msg and (not from_storage or from_remote):

                        # send ack only for chat messages (if requested)
                        # do not send if coming from remote storage
                        if receipt and not from_storage:
                            self.send_ack(stanza, 'sent', stamp)

                        # send receipt to originating server, if requested
                        receipt = None
                        # receipt request: send <sent/>
                        if stanza.request:
                            receipt = stanza.request
                            request = 'request'
                            delivery = 'sent'
                        # received receipt: send <ack/>
                        elif stanza.received:
                            receipt = stanza.received
                            request = 'received'
                            delivery = 'ack'

                        # now send what we prepared
                        if receipt:
                            try:
                                from_server = receipt['from']
                                if not util.hostjid_local(util.COMPONENT_C2S, self, from_server):
                                    stanza['from'] = from_server
                                    self.send_ack(stanza, delivery, stamp, request)
                            except KeyError:
                                pass

                else:
                    # deliver local stanza
                    self.local(stanza)

                """
                If message is a receipt coming from a remote server, delete
                the message from our storage.
                """
                r_sent = xmlstream2.extract_receipt(stanza, 'sent')
                if chat_msg and r_sent:
                    sender_host = util.jid_host(stanza['from'])
                    """
                    We are receiving a sent receipt from another server,
                    meaning that the server has now responsibility for the
                    message - we can delete it now.
                    Special case is the sender domain being the network
                    domain, meaning the resolver rejected the message.
                    """
                    unused, sender_host = util.jid_component(sender_host)
                    if sender_host != self.servername:
                        log.debug("remote server now has responsibility for message %s - deleting" % (r_sent['id'], ))
                        # TODO safe delete with sender/recipient
                        self.message_offline_delete(r_sent['id'], stanza.name)

            else:
                log.debug("stanza is not our concern or is an error")
Пример #6
0
    def roster(self, stanza):
        if not xmlstream2.has_element(stanza.query, uri=xmlstream2.NS_IQ_ROSTER, name="item"):
            # requesting initial roster - enter XMPP compatibility mode
            self.parent.compatibility_mode = True

        _items = stanza.query.elements(uri=xmlstream2.NS_IQ_ROSTER, name="item")
        requester = jid.JID(stanza["from"])
        stanza.consumed = True

        # items present, requesting roster lookup
        response = xmlstream.toResponse(stanza, "result")
        roster = response.addElement((xmlstream2.NS_IQ_ROSTER, "query"))

        probes = []
        # this will be true if roster lookup is requested
        roster_lookup = False
        for item in _items:
            # items present, meaning roster lookup
            roster_lookup = True

            itemJid = jid.internJID(item["jid"])

            # include the entry in the roster reply anyway
            entry = self.parent.router.cache.lookup(itemJid)
            if entry:
                allowed = self.parent.router.is_presence_allowed(requester, itemJid)
                if allowed != -1:
                    item = roster.addElement((None, "item"))
                    item["jid"] = self.parent.router.translateJID(entry.jid).userhost()

                if allowed == 1:
                    probes.append(entry.presence())

        # roster lookup, send presence data and vcards
        if roster_lookup:

            # lookup response
            self.send(response)

            # simulate a presence probe and send vcards
            # we'll use one group ID so the client knows when to stop waiting
            gid = stanza.getAttribute("id")
            if not gid:
                gid = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE)

            i = sum([len(x) for x in probes])
            for presence_list in probes:
                for presence in presence_list:
                    presence = deepcopy(presence)
                    presence["to"] = stanza["from"]
                    group = presence.addElement((xmlstream2.NS_XMPP_STANZA_GROUP, "group"))
                    group["id"] = gid
                    group["count"] = str(i)
                    i -= 1
                    self.send(presence)

                # send vcard for this user
                jid_from = jid.JID(presence_list[0]["from"])
                iq = domish.Element((None, "iq"))
                iq["type"] = "set"
                iq["from"] = jid_from.userhost()
                iq["to"] = stanza["from"]
                try:
                    self.parent.router.build_vcard(jid_from.user, iq)
                    self.send(iq)
                except keyring.KeyNotFoundException:
                    pass

        # no roster lookup, XMPP standard roster instead
        else:

            # include items from the user's whitelist
            wl = self.parent.router.get_whitelist(requester)
            probes = None
            if wl:
                subscriptions = []
                for e in wl:
                    item = roster.addElement((None, "item"))
                    item["jid"] = e

                    itemJid = jid.JID(e)

                    # check if subscription status is 'both' or just 'from'
                    allowed = self.parent.router.is_presence_allowed(requester, itemJid)
                    if allowed == 1:
                        status = "both"
                    else:
                        status = "from"

                    # TODO include name from PGP key?
                    item["subscription"] = status

                    # add to subscription list
                    subscriptions.append(itemJid)

            # send the roster
            self.send(response)

            # subscribe to all users (without sending subscribed stanza of course)
            if wl:
                for itemJid in subscriptions:
                    self.parent.router.subscribe(requester, itemJid, send_subscribed=False)