Beispiel #1
0
 def broadcast(self, stanza, same=False):
     """
     Broadcast a stanza to every component.
     This alters the to attribute in outgoing stanza for each component.
     """
     from_host = util.jid_host(stanza['from'])
     for host, xs in self.routes.iteritems():
         # do not send to the original sender
         if host is not None and (host != from_host or same):
             log.debug("sending to %s" % (host, ))
             stanza['to'] = host
             xs.send(stanza)
Beispiel #2
0
 def broadcast(self, stanza, same=False):
     """
     Broadcast a stanza to every component.
     This alters the to attribute in outgoing stanza for each component.
     """
     from_host = util.jid_host(stanza['from'])
     for host, xs in self.routes.iteritems():
         # do not send to the original sender
         if host is not None and (host != from_host or same):
             log.debug("sending to %s" % (host, ))
             stanza['to'] = host
             xs.send(stanza)
Beispiel #3
0
    def presence(self, stanza):
        """
        Presence broadcast from local c2s (intended for remote c2s), deliver
        also to remote resolver.
        """

        if not stanza.consumed:
            host = util.jid_host(stanza['from'])
            if host == self.servername:
                """
                Original stanza will be sent to remote c2s, which will provider
                offline storage delivery and network conflicts.
                """
                self.dispatch(stanza)

                """
                Intended destination is not remote resolver, so be sure to deliver.
                """
                if stanza.getAttribute('destination') != self.servername:
                    # be sure to unconsume for the next dispatch
                    stanza.consumed = False
                    stanza['destination'] = self.network
Beispiel #4
0
    def route(self, stanza, xs):
        """
        Route a stanza.

        @param stanza: The stanza to be routed.
        @type stanza: L{domish.Element}.
        """

        if stanza.consumed:
            return
        """"
        TEST check sender host is component
        stanzaFrom = jid.JID(stanza['from'])
        if stanzaFrom.host != xs.thisEntity.host:
            log.error("stanza is not from component - dropping")
            return
        """

        # reset namespace
        util.resetNamespace(stanza, component.NS_COMPONENT_ACCEPT)

        # send stanza to logging entities
        for lg in self.logs:
            lg.send(stanza)

        if not stanza.hasAttribute('to'):
            if self.logTraffic:
                log.debug("broadcasting stanza %s" %
                          (stanza.toXml().encode('utf-8'), ))
            self.broadcast(stanza)
        else:
            """
            FIXME we have encoding problems here... (why not in other components?!?!?)
            """

            # check for stanza loops
            errors = 0
            for child in stanza.children:
                if domish.IElement.providedBy(child) and child.name == 'error':
                    errors += 1
                    if errors > 1:
                        if self.logTraffic:
                            log.debug("error loop, dropping stanza %s" %
                                      (stanza.toXml().encode('utf-8'), ))
                        else:
                            log.debug("error loop, dropping stanza")

                        return

            if self.logTraffic:
                log.debug("routing stanza %s" %
                          (stanza.toXml().encode('utf-8'), ))
            try:
                destination_host = util.jid_host(stanza['to'])

                if destination_host in self.routes:
                    self.routes[destination_host].send(stanza)
                elif destination_host in self.private:
                    self.private[destination_host].send(stanza)
                else:
                    self.routes[None].send(stanza)

            except KeyError:
                log.warn("unroutable stanza, bouncing back to component")
                e = error.StanzaError('service-unavailable')
                xs.send(e.toResponse(stanza))
Beispiel #5
0
 def test_jid_host(self):
     jidstring = '[email protected]/resource'
     user = util.jid_host(jidstring)
     self.assertEqual(user, 'localhost.localdomain')
Beispiel #6
0
 def test_jid_host(self):
     jidstring = '[email protected]/resource'
     user = util.jid_host(jidstring)
     self.assertEqual(user, 'localhost.localdomain')
Beispiel #7
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")
Beispiel #8
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")
Beispiel #9
0
    def route(self, stanza, xs):
        """
        Route a stanza.

        @param stanza: The stanza to be routed.
        @type stanza: L{domish.Element}.
        """

        if stanza.consumed:
            return

        """"
        TEST check sender host is component
        stanzaFrom = jid.JID(stanza['from'])
        if stanzaFrom.host != xs.thisEntity.host:
            log.error("stanza is not from component - dropping")
            return
        """

        # reset namespace
        util.resetNamespace(stanza, component.NS_COMPONENT_ACCEPT)

        # send stanza to logging entities
        for lg in self.logs:
            lg.send(stanza)

        if not stanza.hasAttribute('to'):
            if self.logTraffic:
                log.debug("broadcasting stanza %s" % (stanza.toXml().encode('utf-8'), ))
            self.broadcast(stanza)
        else:
            """
            FIXME we have encoding problems here... (why not in other components?!?!?)
            """

            # check for stanza loops
            errors = 0
            for child in stanza.children:
                if domish.IElement.providedBy(child) and child.name == 'error':
                    errors += 1
                    if errors > 1:
                        if self.logTraffic:
                            log.debug("error loop, dropping stanza %s" % (stanza.toXml().encode('utf-8'), ))
                        else:
                            log.debug("error loop, dropping stanza")

                        return

            if self.logTraffic:
                log.debug("routing stanza %s" % (stanza.toXml().encode('utf-8'), ))
            try:
                destination_host = util.jid_host(stanza['to'])

                if destination_host in self.routes:
                    self.routes[destination_host].send(stanza)
                else:
                    self.routes[None].send(stanza)

            except KeyError:
                log.warn("unroutable stanza, bouncing back to component")
                e = error.StanzaError('service-unavailable')
                xs.send(xmlstream2.errorResponse(e, stanza))