Esempio n. 1
0
     def act():
         mech = msg.conn.data['sasl']['mechObj']
         if not mech:
             # TODO: close connection
             logging.warning("[%s] Mech object doesn't exist in connection data for %s",
                             self.__class__, msg.conn.addr)
             logging.debug("[%s] %s", self.__class__, msg.conn.data)
             return
 
         text = tree.text
         if text:
             return chainOutput(lastRetVal, mech.handle(text.strip()))
         else:
             return chainOutput(lastRetVal, mech.handle(tree))
Esempio n. 2
0
        def act():
            mech = msg.conn.data['sasl']['mechObj']
            if not mech:
                # TODO: close connection
                logging.warning(
                    "[%s] Mech object doesn't exist in connection data for %s",
                    self.__class__, msg.conn.addr)
                logging.debug("[%s] %s", self.__class__, msg.conn.data)
                return

            text = tree.text
            if text:
                return chainOutput(lastRetVal, mech.handle(text.strip()))
            else:
                return chainOutput(lastRetVal, mech.handle(tree))
Esempio n. 3
0
    def handle(self, tree, msg, lastRetVal=None):
        iq = tree
        id = iq.get('id')
        if id:
            bind = iq[0]
            if len(bind) > 0:
                resource = bind[0].text
            else:
                # generate an id
                resource = generateId()[:6]

            # TODO: check that we don't already have such a resource
            jid = msg.conn.data['user']['jid']
            bindResource(msg, resource)

            res = Element('iq', {'type' : 'result', 'id' : id})
            bind = Element('bind', {'xmlns' : 'urn:ietf:params:xml:ns:xmpp-bind'})
            jidEl = Element('jid')
            jidEl.text = '%s/%s' % (jid, resource)
            bind.append(jidEl)
            res.append(bind)

            return chainOutput(lastRetVal, res)
        else:
            logging.warning("[%s] No id in <iq>:\n%s", self.__class__, tostring(iq))

        return lastRetVal
Esempio n. 4
0
    def handle(self, tree, msg, lastRetVal=None):
        if len(tree) > 0:
            # get the original iq msg
            origIQ = tree
        else:
            logging.warning("[%s] Original <iq> missing:\n%s", self.__class__,
                            tostring(tree))
            return

        id = origIQ.get('id')
        if id:
            res = Element('iq', {'type': 'error', 'id': id})
            res.append(origIQ)

            err = Element('error', {'type': 'cancel'})
            SubElement(err, 'service-unavailable',
                       {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})

            res.append(err)

            return chainOutput(lastRetVal, res)
        else:
            logging.warning("[%s] No id in <iq>:\n%s", self.__class__,
                            tostring(origIQ))

        return lastRetVal
Esempio n. 5
0
    def handle(self, tree, msg, lastRetVal=None):
        iq = tree
        id = iq.get('id')
        if id:
            bind = iq[0]
            if len(bind) > 0:
                resource = bind[0].text
            else:
                # generate an id
                resource = generateId()[:6]

            # TODO: check that we don't already have such a resource
            jid = msg.conn.data['user']['jid']
            bindResource(msg, resource)

            res = Element('iq', {'type': 'result', 'id': id})
            bind = Element('bind',
                           {'xmlns': 'urn:ietf:params:xml:ns:xmpp-bind'})
            jidEl = Element('jid')
            jidEl.text = '%s/%s' % (jid, resource)
            bind.append(jidEl)
            res.append(bind)

            return chainOutput(lastRetVal, res)
        else:
            logging.warning("[%s] No id in <iq>:\n%s", self.__class__,
                            tostring(iq))

        return lastRetVal
Esempio n. 6
0
    def broadcastToOtherResources(self,
                                  tree,
                                  msg,
                                  lastRetVal,
                                  jid=None,
                                  resource=None):
        """Takes in the stanza to broadcast to other resources of this
        user and sets the next handler to route-server. Returns the
        lastRetVal with chained route-server handlers.

        tree -- tree to modify with the 'to' address and send out
        """
        jid = jid or msg.conn.data['user']['jid']
        resource = resource or msg.conn.data['user']['resource']

        retVal = lastRetVal

        resources = msg.conn.server.data['resources'][jid]
        for r in resources:
            if r != resource:
                otherRes = jid + '/' + r
                tree.set('to', otherRes)
                presRouteData = {'to': otherRes, 'data': tree}
                retVal = chainOutput(lastRetVal, presRouteData)
                msg.setNextHandler('route-server')

        return retVal
Esempio n. 7
0
    def handle(self, tree, msg, lastRetVal=None):
        iq = tree
        id = iq.get("id")
        if id:
            bind = iq[0]
            if len(bind) > 0:
                resource = bind[0].text
            else:
                # generate an id
                resource = generateId()[:6]

            # TODO: check that we don't already have such a resource
            jid = msg.conn.data["user"]["jid"]
            bindResource(msg, resource)

            res = Element("iq", {"type": "result", "id": id})
            bind = Element("bind", {"xmlns": "urn:ietf:params:xml:ns:xmpp-bind"})
            jidEl = Element("jid")
            jidEl.text = "%s/%s" % (jid, resource)
            bind.append(jidEl)
            res.append(bind)

            return chainOutput(lastRetVal, res)
        else:
            logging.warning("[%s] No id in <iq>:\n%s", self.__class__, tostring(iq))

        return lastRetVal
Esempio n. 8
0
    def handle(self, tree, msg, lastRetVal=None):
        # find connections of the user's JID
        try:
            jid = JID(tree.get('to'))
        except:
            logging.debug("[%s] Couldn't create a JID from %s", self.__class__,
                          tree.get('to'))
            return
        jids = msg.conn.server.launcher.getC2SServer().data['resources']
        resources = jids.get(jid.getBare())
        if not resources:
            return

        lastPresences = []
        for res, con in resources.items():
            lp = con.data['user']['lastPresence']
            if lp is not None:
                lpcopy = deepcopy(lp)  # keep the last presence intact
                # modify the copy to include the 'to' attr for s2s routing
                lpcopy.set('to', tree.get('from'))
                lastPresences.append(lpcopy)

        if lastPresences:
            d = {
                'to': tree.get('from'),
                'data': lastPresences,
            }
            msg.setNextHandler('route-server')
            return chainOutput(lastRetVal, d)
Esempio n. 9
0
    def handle(self, tree, msg, lastRetVal=None):
        # TODO: continue with features, auth, etc.
        # for now, just assume it's localhost

        # forward any queued messages for this connection
        out = prepareDataForSending(msg.conn.outQueue)
        return chainOutput(lastRetVal, out)
Esempio n. 10
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element('stream:features')
        SubElement(res, 'bind', {'xmlns': 'urn:ietf:params:xml:ns:xmpp-bind'})
        SubElement(res, 'session',
                   {'xmlns': 'urn:ietf:params:xml:ns:xmpp-session'})

        return chainOutput(lastRetVal, res)
Esempio n. 11
0
    def broadcastToOtherResources(self, tree, msg, lastRetVal,
                                  jid=None, resource=None):
        """Takes in the stanza to broadcast to other resources of this
        user and sets the next handler to route-server. Returns the
        lastRetVal with chained route-server handlers.

        tree -- tree to modify with the 'to' address and send out
        """
        jid = jid or msg.conn.data['user']['jid']
        resource = resource or msg.conn.data['user']['resource']

        retVal = lastRetVal

        resources = msg.conn.server.data['resources'][jid]
        for r in resources:
            if r != resource:
                otherRes = jid + '/' + r
                tree.set('to', otherRes)
                presRouteData = {
                     'to' : otherRes,
                     'data' : tree
                     }
                retVal = chainOutput(lastRetVal, presRouteData)
                msg.setNextHandler('route-server')

        return retVal
Esempio n. 12
0
    def handle(self, tree, msg, lastRetVal=None):
        id = tree.get('id')
        if not id:
            logging.debug("[%s] No id specified in iq-auth get request",
                          self.__class__)

        # check for policy violation
        violation = checkPolicyViolation(msg)
        if violation is not None:
            msg.setLastHandler('close-stream')
            return chainOutput(lastRetVal, violation)
        else:
            msg.conn.data['iqauth']['in-progress'] = True
            if id:
                iqAuthEl.set('id', id)
            return chainOutput(lastRetVal, iqAuthEl)
Esempio n. 13
0
            def handle(self, tree, msg, lastRetVal=None):
                res = Element('stream:features')
                mechs = SubElement(res, 'mechanisms',
                                   {'xmlns' : 'urn:ietf:params:xml:ns:xmpp-sasl'})
                SubElement(mechs, 'mechanism').text = 'PLAIN'

                return chainOutput(lastRetVal, res)
Esempio n. 14
0
    def handle(self, tree, msg, lastRetVal=None):
        if len(tree) > 0:
            # get the original iq msg
            origIQ = tree
        else:
            logging.warning("[%s] Original <iq> missing:\n%s",
                            self.__class__, tostring(tree))
            return

        id = origIQ.get('id')
        if id:
            res = Element('iq', {
                                 'type' : 'error',
                                 'id' : id
                                })
            res.append(origIQ)

            err = Element('error', {'type' : 'cancel'})
            SubElement(err, 'service-unavailable',
                       {'xmlns' : 'urn:ietf:params:xml:ns:xmpp-stanzas'})

            res.append(err)

            return chainOutput(lastRetVal, res)
        else:
            logging.warning("[%s] No id in <iq>:\n%s",
                            self.__class__, tostring(origIQ))

        return lastRetVal
Esempio n. 15
0
    def handle(self, tree, msg, lastRetVal=None):
        # find connections of the user's JID
        try:
            jid = JID(tree.get('to'))
        except:
            logging.debug("[%s] Couldn't create a JID from %s",
                          self.__class__, tree.get('to'))
            return
        jids = msg.conn.server.launcher.getC2SServer().data['resources']
        resources = jids.get(jid.getBare())
        if not resources:
            return

        lastPresences = []
        for res, con in resources.items():
            lp = con.data['user']['lastPresence']
            if lp is not None:
                lpcopy = deepcopy(lp) # keep the last presence intact
                # modify the copy to include the 'to' attr for s2s routing
                lpcopy.set('to', tree.get('from'))
                lastPresences.append(lpcopy)

        if lastPresences:
            d = {
                 'to' : tree.get('from'),
                 'data' : lastPresences,
                 }
            msg.setNextHandler('route-server')
            return chainOutput(lastRetVal, d)
Esempio n. 16
0
 def handle(self, tree, msg, lastRetVal=None):
     id = tree.get('id')
     if not id:
         logging.debug("[%s] No id specified in iq-auth get request",
                       self.__class__)
         
     # check for policy violation
     violation = checkPolicyViolation(msg)
     if violation is not None:
         msg.setLastHandler('close-stream')
         return chainOutput(lastRetVal, violation)
     else:
         msg.conn.data['iqauth']['in-progress'] = True
         if id:
             iqAuthEl.set('id', id)
         return chainOutput(lastRetVal, iqAuthEl)
Esempio n. 17
0
    def handle(self, tree, msg, lastRetVal=None):
        # TODO: continue with features, auth, etc.
        # for now, just assume it's localhost

        # forward any queued messages for this connection
        out = prepareDataForSending(msg.conn.outQueue)
        return chainOutput(lastRetVal, out)
Esempio n. 18
0
 def makeServiceUnavailableError():
     fromJID = cjid.__str__()
     reply = Element("message", {"type": "error", "to": fromJID})
     reply.append(copy(tree))
     error = Element("error", {"type": "cancel"})
     SubElement(error, "service-unavailable", {"xmlns": "urn:ietf:params:xml:ns:xmpp-stanzas"})
     routeData = {"to": fromJID, "data": reply}
     msg.setNextHandler("route-client")
     return chainOutput(lastRetVal, routeData)
Esempio n. 19
0
 def makeServiceUnavailableError():
     fromJID = cjid.__str__()
     reply = Element('message', {'type': 'error', 'to': fromJID})
     reply.append(copy(tree))
     error = Element('error', {'type': 'cancel'})
     SubElement(error, 'service-unavailable',
                {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})
     routeData = {'to': fromJID, 'data': reply}
     msg.setNextHandler('route-client')
     return chainOutput(lastRetVal, routeData)
Esempio n. 20
0
        def act():
            data = msg.conn.data
            data['sasl']['in-progress'] = True
            mech = tree.get('mechanism')

            if mech == 'PLAIN':
                data['sasl']['mech'] = 'PLAIN'
                authtext64 = tree.text
                plain = mechs.SASLPlain(msg)
                data['sasl']['mechObj'] = plain
                return chainOutput(lastRetVal, plain.handle(authtext64))
            elif mech == 'DIGEST-MD5':
                data['sasl']['mech'] = 'DIGEST-MD5'
                digest = mechs.SASLDigestMD5(msg)
                data['sasl']['mechObj'] = digest
                return chainOutput(lastRetVal, digest.handle())
            else:
                logging.warning("[%s] Mechanism %s not implemented",
                                self.__class__, mech)
Esempio n. 21
0
 def act():
     data = msg.conn.data
     data['sasl']['in-progress'] = True
     mech = tree.get('mechanism')
     
     if mech == 'PLAIN':
         data['sasl']['mech'] = 'PLAIN'
         authtext64 = tree.text
         plain = mechs.SASLPlain(msg)
         data['sasl']['mechObj'] = plain
         return chainOutput(lastRetVal, plain.handle(authtext64))
     elif mech == 'DIGEST-MD5':
         data['sasl']['mech'] = 'DIGEST-MD5'
         digest = mechs.SASLDigestMD5(msg)
         data['sasl']['mechObj'] = digest
         return chainOutput(lastRetVal, digest.handle())
     else:
         logging.warning("[%s] Mechanism %s not implemented",
                         self.__class__, mech)
Esempio n. 22
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element('iq', {
                             'from' : msg.conn.server.hostname,
                             'type' : 'result',
                             'id' : tree.get('id')
                             })

        msg.conn.data['user']['in-session'] = True

        return chainOutput(lastRetVal, res)
Esempio n. 23
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element("stream:features")
        mechs = SubElement(res, "mechanisms", {"xmlns": "urn:ietf:params:xml:ns:xmpp-sasl"})
        SubElement(mechs, "mechanism").text = "DIGEST-MD5"
        SubElement(mechs, "mechanism").text = "PLAIN"

        # we also support the old style jabber:iq:auth
        SubElement(res, "auth", {"xmlns": "http://jabber.org/features/iq-auth"})

        return chainOutput(lastRetVal, res)
Esempio n. 24
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element(
            'iq', {
                'from': msg.conn.server.hostname,
                'type': 'result',
                'id': tree.get('id')
            })

        msg.conn.data['user']['in-session'] = True

        return chainOutput(lastRetVal, res)
Esempio n. 25
0
 def handle(self, tree, msg, lastRetVal=None):
     if isinstance(lastRetVal, SASLError):
         el = Element('failure', {'xmlns' : 'urn:ietf:params:xml:ns:xmpp-sasl'})
         el.append(lastRetVal.errorElement())
         
         return chainOutput(lastRetVal, el)
     else:
         logging.warning("[%s] SASLErrorHandler was passed a non-SASL " +\
                         "exception. Exception: %s",
                         self.__class__, lastRetVal)
         raise Exception, "can't handle a non-SASL error"
Esempio n. 26
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element('stream:features')
        mechs = SubElement(res, 'mechanisms',
                           {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
        SubElement(mechs, 'mechanism').text = 'DIGEST-MD5'
        SubElement(mechs, 'mechanism').text = 'PLAIN'

        # we also support the old style jabber:iq:auth
        SubElement(res, 'auth',
                   {'xmlns': 'http://jabber.org/features/iq-auth'})

        return chainOutput(lastRetVal, res)
Esempio n. 27
0
    def handle(self, tree, msg, lastRetVal=None):
        if isinstance(lastRetVal, SASLError):
            el = Element('failure',
                         {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
            el.append(lastRetVal.errorElement())

            return chainOutput(lastRetVal, el)
        else:
            logging.warning("[%s] SASLErrorHandler was passed a non-SASL " +\
                            "exception. Exception: %s",
                            self.__class__, lastRetVal)
            raise Exception, "can't handle a non-SASL error"
Esempio n. 28
0
    def handle(self, tree, msg, lastRetVal=None):
        # we need to rewrite the "to" attribute of the <presence>
        # stanza for each resource of the user we send it to
        def rewriteTo(data, conn):
            jid = conn.data['user']['jid']
            res = conn.data['user']['resource']
            data.set('to', '%s/%s' % (jid, res))
            return data

        logging.debug("[%s] Routing %s", self.__class__, tostring(tree))
        d = {'to': tree.get('to'), 'data': tree, 'preprocessFunc': rewriteTo}
        msg.setNextHandler('route-client')
        return chainOutput(lastRetVal, d)
Esempio n. 29
0
        def act():
            data = msg.conn.data
            # check for policy violation
            violation = checkPolicyViolation(msg)
            if violation is not None:
                msg.setLastHandler('close-stream')
                return chainOutput(lastRetVal, violation)

            id = tree.get('id')
            if not id:
                logging.debug("[%s] No id specified in iq-auth set request",
                              self.__class__)

            data['iqauth']['in-progress'] = True

            username = tree[0].find('{jabber:iq:auth}username')
            if username is not None:
                username = username.text
            resource = tree[0].find('{jabber:iq:auth}resource')
            if resource is not None:
                resource = resource.text

            if username is None or resource is None:
                iq = makeNotAcceptable(id)
                return chainOutput(lastRetVal, iq)

            digest = tree[0].find('{jabber:iq:auth}digest')
            if digest is not None:
                digest = digest.text
            password = tree[0].find('{jabber:iq:auth}password')
            if password is not None:
                password = password.text

            if digest:
                data['iqauth']['mech'] = 'digest'
                auth = mechs.IQAuthDigest(msg)
                try:
                    auth.handle(username, digest)
                except IQAuthError:
                    return chainOutput(lastRetVal, makeNotAuthorized(id))
            elif password:
                data['iqauth']['mech'] = 'plain'
                plain = mechs.IQAuthPlain(msg)
                try:
                    plain.handle(username, password)
                except IQAuthError:
                    return chainOutput(lastRetVal, makeNotAuthorized(id))
            else:
                iq = makeNotAcceptable(id)
                return chainOutput(lastRetVal, iq)

            # do the resource binding
            # TODO: check that we don't already have such a resource
            bindResource(msg, resource)

            data['iqauth']['complete'] = True

            return chainOutput(lastRetVal, makeSuccess(id))
Esempio n. 30
0
 def act():
     data = msg.conn.data
     # check for policy violation
     violation = checkPolicyViolation(msg)
     if violation is not None:
         msg.setLastHandler('close-stream')
         return chainOutput(lastRetVal, violation)
     
     id = tree.get('id')
     if not id:
         logging.debug("[%s] No id specified in iq-auth set request",
                       self.__class__)
     
     data['iqauth']['in-progress'] = True
     
     username = tree[0].find('{jabber:iq:auth}username')
     if username is not None:
         username = username.text
     resource = tree[0].find('{jabber:iq:auth}resource')
     if resource is not None:
         resource = resource.text
     
     if username is None or resource is None:
         iq = makeNotAcceptable(id)
         return chainOutput(lastRetVal, iq)
     
     digest = tree[0].find('{jabber:iq:auth}digest')
     if digest is not None:
         digest = digest.text
     password = tree[0].find('{jabber:iq:auth}password')
     if password is not None:
         password = password.text
     
     if digest:
         data['iqauth']['mech'] = 'digest'
         auth = mechs.IQAuthDigest(msg)
         try:
             auth.handle(username, digest)
         except IQAuthError:
             return chainOutput(lastRetVal, makeNotAuthorized(id))
     elif password:
         data['iqauth']['mech'] = 'plain'
         plain = mechs.IQAuthPlain(msg)
         try:
             plain.handle(username, password)
         except IQAuthError:
             return chainOutput(lastRetVal, makeNotAuthorized(id))
     else:
         iq = makeNotAcceptable(id)
         return chainOutput(lastRetVal, iq)
     
     # do the resource binding
     # TODO: check that we don't already have such a resource
     bindResource(msg, resource)
     
     data['iqauth']['complete'] = True
     
     return chainOutput(lastRetVal, makeSuccess(id))
Esempio n. 31
0
    def handle(self, tree, msg, lastRetVal=None):
        # we need to rewrite the "to" attribute of the <presence>
        # stanza for each resource of the user we send it to
        def rewriteTo(data, conn):
            jid = conn.data['user']['jid']
            res = conn.data['user']['resource']
            data.set('to', '%s/%s' % (jid, res))
            return data

        logging.debug("[%s] Routing %s", self.__class__, tostring(tree))
        d = {
             'to' : tree.get('to'),
             'data' : tree,
             'preprocessFunc' : rewriteTo
             }
        msg.setNextHandler('route-client')
        return chainOutput(lastRetVal, d)
Esempio n. 32
0
 def makeServiceUnavailableError():
     fromJID = cjid.__str__()
     reply = Element('message', {
                                 'type' : 'error',
                                 'to' : fromJID
                                 })
     reply.append(copy(tree))
     error = Element('error', {'type' : 'cancel'})
     SubElement(error, 'service-unavailable', {
                   'xmlns' : 'urn:ietf:params:xml:ns:xmpp-stanzas'
                   })
     routeData = {
                  'to' : fromJID,
                  'data' : reply
                  }
     msg.setNextHandler('route-client')
     return chainOutput(lastRetVal, routeData)
Esempio n. 33
0
        def act():
            # TODO: verify that it's coming from a known user
            jid = msg.conn.data["user"]["jid"]
            resource = msg.conn.data["user"]["resource"]
            id = tree.get("id")
            if id is None:
                logging.warning("[%s] No id in roster get query. Tree: %s", self.__class__, tree)
                # TODO: throw exception here
                return

            roster = Roster(jid)

            roster.loadRoster()

            res = Element("iq", {"to": "/".join([jid, resource]), "type": "result", "id": id})

            res.append(roster.getAsTree())
            return chainOutput(lastRetVal, res)
Esempio n. 34
0
    def handle(self, tree, msg, lastRetVal=None):
        # check that we have the to and from fields in the message and
        # just forward
        toJID = tree.get("to")
        jid = msg.conn.data["user"]["jid"]
        resource = msg.conn.data["user"]["resource"]

        try:
            toJID = JID(toJID)
        except:
            logging.debug("[%s] 'to' attribute in message not a real JID", self.__class__)
            return

        stampedTree = copy(tree)
        stampedTree.set("from", "%s/%s" % (jid, resource))

        routeData = {"to": toJID.__str__(), "data": stampedTree}
        msg.setNextHandler("route-server")

        return chainOutput(lastRetVal, routeData)
Esempio n. 35
0
    def handle(self, tree, msg, lastRetVal=None):
        # check that we have the to and from fields in the message and
        # just forward
        toJID = tree.get('to')
        jid = msg.conn.data['user']['jid']
        resource = msg.conn.data['user']['resource']

        try:
            toJID = JID(toJID)
        except:
            logging.debug("[%s] 'to' attribute in message not a real JID",
                          self.__class__)
            return

        stampedTree = copy(tree)
        stampedTree.set('from', '%s/%s' % (jid, resource))

        routeData = {'to': toJID.__str__(), 'data': stampedTree}
        msg.setNextHandler('route-server')

        return chainOutput(lastRetVal, routeData)
Esempio n. 36
0
        def act():
            # TODO: verify that it's coming from a known user
            jid = msg.conn.data['user']['jid']
            resource = msg.conn.data['user']['resource']
            id = tree.get('id')
            if id is None:
                logging.warning('[%s] No id in roster get query. Tree: %s',
                                self.__class__, tree)
                # TODO: throw exception here
                return

            roster = Roster(jid)

            roster.loadRoster()

            res = Element('iq', {
                                 'to' : '/'.join([jid, resource]),
                                 'type' : 'result',
                                 'id' : id
                                 })

            res.append(roster.getAsTree())
            return chainOutput(lastRetVal, res)
Esempio n. 37
0
    def handle(self, tree, msg, lastRetVal=None):
        if len(tree) > 0:
            # get the original iq msg
            origIQ = tree
        else:
            logging.warning("[%s] Original <iq> missing:\n%s", self.__class__, tostring(tree))
            return

        id = origIQ.get("id")
        if id:
            res = Element("iq", {"type": "error", "id": id})
            res.append(origIQ)

            err = Element("error", {"type": "cancel"})
            SubElement(err, "service-unavailable", {"xmlns": "urn:ietf:params:xml:ns:xmpp-stanzas"})

            res.append(err)

            return chainOutput(lastRetVal, res)
        else:
            logging.warning("[%s] No id in <iq>:\n%s", self.__class__, tostring(origIQ))

        return lastRetVal
Esempio n. 38
0
        def act():
            # TODO: verify that it's coming from a known user
            jid = msg.conn.data['user']['jid']
            resource = msg.conn.data['user']['resource']
            id = tree.get('id')
            if id is None:
                logging.warning('[%s] No id in roster get query. Tree: %s',
                                self.__class__, tree)
                # TODO: throw exception here
                return

            roster = Roster(jid)

            roster.loadRoster()

            res = Element('iq', {
                'to': '/'.join([jid, resource]),
                'type': 'result',
                'id': id
            })

            res.append(roster.getAsTree())
            return chainOutput(lastRetVal, res)
Esempio n. 39
0
    def handle(self, tree, msg, lastRetVal=None):
        # check that we have the to and from fields in the message and
        # just forward
        toJID = tree.get('to')
        jid = msg.conn.data['user']['jid']
        resource = msg.conn.data['user']['resource']

        try:
            toJID = JID(toJID)
        except:
            logging.debug("[%s] 'to' attribute in message not a real JID",
                          self.__class__)
            return

        stampedTree = copy(tree)
        stampedTree.set('from', '%s/%s' % (jid, resource))

        routeData = {
                     'to' : toJID.__str__(),
                     'data' : stampedTree
                     }
        msg.setNextHandler('route-server')

        return chainOutput(lastRetVal, routeData)
Esempio n. 40
0
        def act():
            # TODO: verify that it's coming from a known user
            jid = msg.conn.data['user']['jid']
            id = tree.get('id')
            if id is None:
                logging.warning('[%s] No id in roster get query. Tree: %s',
                                self.__class__, tree)
                # TODO: throw exception here
                return

            # RFC 3921 says in section 7.4 "an item", so we only handle the
            # first <item>
            item = tree[0][0]  # iq -> query -> item
            cjid = item.get('jid')
            name = item.get('name')
            if cjid is None:
                logging.warning("[%s] Client trying to add a roster item " + \
                                "without a jid. Tree: %s",
                                self.__class__, tree)
                # TODO: throw exception here
                return

            roster = Roster(jid)

            xpath = './{jabber:iq:roster}query/{jabber:iq:roster}item[@subscription="remove"]'
            if tree.find(xpath) is not None:
                # we're removing the roster item. See 3921 8.6
                out = "<presence from='%s' to='%s' type='unsubscribe'/>" \
                                                          % (jid, cjid)
                out += "<presence from='%s' to='%s' type='unsubscribed'/>" \
                                                          % (jid, cjid)
                # create unavailable presence stanzas for all resources of the user
                resources = msg.conn.server.launcher.getC2SServer(
                ).data['resources']
                jidForResources = resources.has_key(jid) and resources[jid]
                if jidForResources:
                    for i in jidForResources:
                        out += "<presence from='%s/%s'" % (jid, i)
                        out += " to='%s' type='unavailable'/>" % cjid

                # prepare routing data
                d = {'to': cjid, 'data': out}

                query = deepcopy(tree[0])

                retVal = chainOutput(lastRetVal, query)

                if roster.removeContact(cjid) is False:
                    # We don't even have this contact in the roster anymore.
                    # The contact is probably local (like ourselves).
                    # This happens with some clients (like pidgin/gaim) who
                    # cache the roster and don't delete some items even when
                    # they're not present in the roster the server sends out
                    # anymore. If we send the presence here it
                    # will probably arrive after roster-push (due to s2s)
                    # and will confuse the clients into thinking they still
                    # have that contact in their roster. This creates an
                    # undeletable contact. We can't do much about this.
                    # If/when the s2s component can do a shortcut delivery of
                    # stanzas to local users, while in the same phase, this
                    # problem should go away, as it will allow the roster-push
                    # to arrive after presences every time.
                    pass

                # route the presence first, then do a roster push
                msg.setNextHandler('roster-push')
                msg.setNextHandler('route-server')

                return chainOutput(retVal, d)

            # we're updating/adding the roster item

            groups = [
                i.text for i in list(item.findall('{jabber:iq:roster}group'))
            ]

            cid = roster.updateContact(cjid, groups, name)

            # get the subscription status before roster push
            sub = roster.getSubPrimaryName(cid)

            # prepare the result for roster push
            query = Roster.createRosterQuery(cjid, sub, name, groups)

            msg.setNextHandler('roster-push')

            return chainOutput(lastRetVal, query)
Esempio n. 41
0
        def act():
            # we have to be passed a tree to work
            # or a tuple with routingData and a tree
            if not isinstance(lastRetVal, list):
                logging.warning('[%s] lastRetVal is not a list',
                                self.__class__)
                return
            if isinstance(lastRetVal[-1], Element):
                if lastRetVal[-1].tag.find('query') == -1:
                    logging.warning('[%s] Got a non-query Element last return value' +\
                                '. Last return value: %s',
                                self.__class__, lastRetVal)
            elif isinstance(lastRetVal[-1], tuple):
                if not isinstance(lastRetVal[-1][0], dict) \
                or not isinstance(lastRetVal[-1][1], Element):
                    logging.warning('[%s] Got a non-query Element last return value' +\
                                '. Last return value: %s',
                                self.__class__, lastRetVal)
                    return
            else:
                logging.warning('[%s] Roster push needs either a <query> Element ' +\
                                'as the last item in lastRetVal or a tuple ' + \
                                'with (routeData, query Element)', self.__class__)
                return

            # this is the roster <query> that we'll send
            # it could be a tuple if we got routing data as well
            query = lastRetVal.pop(-1)
            routeData = None

            # did we get routing data (from S2S)
            if isinstance(query, tuple):
                routeData = query[0]
                query = query[1]

            if routeData:
                jid = routeData['jid']
                resources = routeData['resources']
            else:
                jid = msg.conn.data['user']['jid']
                resource = msg.conn.data['user']['resource']
                resources = msg.conn.server.data['resources'][jid]

            for res, con in resources.items():
                # don't send the roster to clients that didn't request it
                if con.data['user']['requestedRoster']:
                    iq = Element(
                        'iq', {
                            'to': '%s/%s' % (jid, res),
                            'type': 'set',
                            'id': generateId()[:10]
                        })
                    iq.append(query)

                    # TODO: remove this. debug.
                    logging.debug("Sending " + tostring(iq))
                    con.send(tostring(iq))

            if tree.tag == '{jabber:client}iq' and tree.get('id'):
                # send an ack to client if this is in reply to a roster get/set
                id = tree.get('id')
                d = {
                    'to': '%s/%s' % (jid, resource),
                    'type': 'result',
                    'id': id
                }
                iq = Element('iq', d)
                return chainOutput(lastRetVal, iq)
Esempio n. 42
0
class S2SMessageHandler(ThreadedHandler):
    """Handles <message>s coming in from remote servers"""
    def __init__(self):
        # this is true when the threaded handler returns
        self.done = False
        # used to pass the output to the next handler
        self.retVal = None

    def handle(self, tree, msg, lastRetVal=None):
        self.done = False
        self.retVal = lastRetVal
        tpool = msg.conn.server.threadpool

        def act():
            cjid = tree.get('from')
            if not cjid:
                logging.debug("[%s] No 'from' attribute in <message> " + \
                              "stanza from server. Dropping: %s",
                              self.__class__, tostring(tree))
                return

            try:
                cjid = JID(cjid)
            except Exception, e:
                logging.debug("[%s] 'from' attribute in <message> not a " +\
                              "real JID: %s. Dropping: %s",
                              self.__class__, cjid, tostring(tree))
                return

            to = tree.get('to')
            if not to:
                logging.debug(
                    "[%s] No 'to' attribute in <message> stanza from server",
                    self.__class__)
                return

            try:
                to = JID(to)
            except Exception, e:
                logging.debug("[%s] 'to' attribute in <message> not a " +\
                              "real JID: %s. Dropping: %s",
                              self.__class__, to, tostring(tree))
                return

            if to.domain != msg.conn.server.hostname:
                logging.debug("[%s] <message> stanza recipient not handled " +\
                              "by this server: %s",
                              self.__class__, tostring(msg))
                return

            def makeServiceUnavailableError():
                fromJID = cjid.__str__()
                reply = Element('message', {'type': 'error', 'to': fromJID})
                reply.append(copy(tree))
                error = Element('error', {'type': 'cancel'})
                SubElement(error, 'service-unavailable',
                           {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})
                routeData = {'to': fromJID, 'data': reply}
                msg.setNextHandler('route-client')
                return chainOutput(lastRetVal, routeData)

            if to.exists():
                # user exists in the DB. check if they're online,
                # then forward to server
                conns = msg.conn.server.launcher.getC2SServer(
                ).data['resources']
                toJID = to.getBare()

                # we may need to strip the resource if it's not available
                # and send to the bare JID
                modifiedTo = to.__str__()

                if conns.has_key(toJID) and conns[toJID]:
                    # the user has one or more resources available
                    if to.resource:
                        # if sending to a specific resource,
                        # check if it's available
                        if not conns[toJID].has_key(to.resource):
                            # resource is unavailable, so send to bare JID
                            modifiedTo = toJID
                else:
                    # user is unavailable, so send an error, unless
                    # this message is an error already
                    if tree.get('type') == 'error':
                        return
                    return makeServiceUnavailableError()

                routeData = {'to': modifiedTo, 'data': tree}
                msg.setNextHandler('route-client')
                return chainOutput(lastRetVal, routeData)
            else:
                # user does not exist
                # reply with <service-unavailable>.

                if tree.get('type') == 'error':
                    # unless this message was an error itself
                    return

                return makeServiceUnavailableError()
Esempio n. 43
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element("iq", {"from": msg.conn.server.hostname, "type": "result", "id": tree.get("id")})

        msg.conn.data["user"]["in-session"] = True

        return chainOutput(lastRetVal, res)
Esempio n. 44
0
        def act():
            d = msg.conn.data

            retVal = lastRetVal

            jid = d['user']['jid']
            resource = d['user']['resource']

            roster = Roster(jid)

            presTree = deepcopy(tree)
            presTree.set('from', '%s/%s' % (jid, resource))

            probes = []
            if tree.get('to') is None and not d['user']['active']:
                # initial presence
                # TODO: we don't need to do it every time. we can cache the
                # data after the first resource is active and just resend
                # that to all new resources
                d['user']['active'] = True

                # get jids of the contacts whose status we're interested in
                cjids = roster.getPresenceSubscriptions()

                probeTree = Element('presence', {
                                                 'type': 'probe',
                                                 'from' : '%s/%s' \
                                                    % (jid, resource)
                                                 })

                # TODO: replace this with a more efficient router handler
                for cjid in cjids:
                    probeTree.set('to', cjid)
                    probeRouteData = {'to': cjid, 'data': deepcopy(probeTree)}
                    probes.append(probeRouteData)
                    # they're sent first. see below

                # broadcast to other resources of this user
                retVal = self.broadcastToOtherResources(
                    presTree, msg, retVal, jid, resource)

            elif tree.get('to') is not None:
                # TODO: directed presence
                return
            elif tree.get('type') == 'unavailable':
                # broadcast to other resources of this user
                d['user']['active'] = False
                retVal = self.broadcastToOtherResources(
                    presTree, msg, retVal, jid, resource)

            # record this stanza as the last presence sent from this client
            lastPresence = deepcopy(tree)
            lastPresence.set('from', '%s/%s' % (jid, resource))
            d['user']['lastPresence'] = lastPresence

            # lookup contacts interested in presence
            cjids = roster.getPresenceSubscribers()

            # TODO: replace this with another router handler that would send
            # it out to all cjids in a batch instead of queuing a handler
            # for each
            for cjid in cjids:
                presTree.set('to', cjid)
                presRouteData = {'to': cjid, 'data': deepcopy(presTree)}
                retVal = chainOutput(retVal, presRouteData)
                msg.setNextHandler('route-server')

            # send the probes first
            for probe in probes:
                msg.setNextHandler('route-server')
                retVal = chainOutput(retVal, probe)

            return retVal
Esempio n. 45
0
        def act():
            # we have to be passed a tree to work
            # or a tuple with routingData and a tree
            if not isinstance(lastRetVal, list):
                logging.warning('[%s] lastRetVal is not a list', self.__class__)
                return
            if isinstance(lastRetVal[-1], Element):
                if lastRetVal[-1].tag.find('query') == -1:
                    logging.warning('[%s] Got a non-query Element last return value' +\
                                '. Last return value: %s',
                                self.__class__, lastRetVal)
            elif isinstance(lastRetVal[-1], tuple):
                if not isinstance(lastRetVal[-1][0], dict) \
                or not isinstance(lastRetVal[-1][1], Element):
                    logging.warning('[%s] Got a non-query Element last return value' +\
                                '. Last return value: %s',
                                self.__class__, lastRetVal)
                    return
            else:
                logging.warning('[%s] Roster push needs either a <query> Element ' +\
                                'as the last item in lastRetVal or a tuple ' + \
                                'with (routeData, query Element)', self.__class__)
                return

            # this is the roster <query> that we'll send
            # it could be a tuple if we got routing data as well
            query = lastRetVal.pop(-1)
            routeData = None

            # did we get routing data (from S2S)
            if isinstance(query, tuple):
                routeData = query[0]
                query = query[1]

            if routeData:
                jid = routeData['jid']
                resources = routeData['resources']
            else:
                jid = msg.conn.data['user']['jid']
                resource = msg.conn.data['user']['resource']
                resources = msg.conn.server.data['resources'][jid]

            for res, con in resources.items():
                # don't send the roster to clients that didn't request it
                if con.data['user']['requestedRoster']:
                    iq = Element('iq', {
                                        'to' : '%s/%s' % (jid, res),
                                        'type' : 'set',
                                        'id' : generateId()[:10]
                                        })
                    iq.append(query)

                    # TODO: remove this. debug.
                    logging.debug("Sending " + tostring(iq))
                    con.send(tostring(iq))

            if tree.tag == '{jabber:client}iq' and tree.get('id'):
                # send an ack to client if this is in reply to a roster get/set
                id = tree.get('id')
                d = {
                     'to' : '%s/%s' % (jid, resource),
                     'type' : 'result',
                     'id' : id
                     }
                iq = Element('iq', d)
                return chainOutput(lastRetVal, iq)
Esempio n. 46
0
        def act():
            # TODO: verify that it's coming from a known user
            jid = msg.conn.data['user']['jid']
            id = tree.get('id')
            if id is None:
                logging.warning('[%s] No id in roster get query. Tree: %s',
                                self.__class__, tree)
                # TODO: throw exception here
                return

            # RFC 3921 says in section 7.4 "an item", so we only handle the
            # first <item>
            item = tree[0][0] # iq -> query -> item
            cjid = item.get('jid')
            name = item.get('name')
            if cjid is None:
                logging.warning("[%s] Client trying to add a roster item " + \
                                "without a jid. Tree: %s",
                                self.__class__, tree)
                # TODO: throw exception here
                return

            roster = Roster(jid)

            xpath = './{jabber:iq:roster}query/{jabber:iq:roster}item[@subscription="remove"]'
            if tree.find(xpath) is not None:
                # we're removing the roster item. See 3921 8.6
                out = "<presence from='%s' to='%s' type='unsubscribe'/>" \
                                                          % (jid, cjid)
                out += "<presence from='%s' to='%s' type='unsubscribed'/>" \
                                                          % (jid, cjid)
                # create unavailable presence stanzas for all resources of the user
                resources = msg.conn.server.launcher.getC2SServer().data['resources']
                jidForResources = resources.has_key(jid) and resources[jid]
                if jidForResources:
                    for i in jidForResources:
                        out += "<presence from='%s/%s'" % (jid, i)
                        out += " to='%s' type='unavailable'/>" % cjid

                # prepare routing data
                d = {
                     'to' : cjid,
                     'data' : out
                     }

                query = deepcopy(tree[0])

                retVal = chainOutput(lastRetVal, query)

                if roster.removeContact(cjid) is False:
                    # We don't even have this contact in the roster anymore.
                    # The contact is probably local (like ourselves).
                    # This happens with some clients (like pidgin/gaim) who
                    # cache the roster and don't delete some items even when
                    # they're not present in the roster the server sends out
                    # anymore. If we send the presence here it
                    # will probably arrive after roster-push (due to s2s)
                    # and will confuse the clients into thinking they still
                    # have that contact in their roster. This creates an
                    # undeletable contact. We can't do much about this.
                    # If/when the s2s component can do a shortcut delivery of
                    # stanzas to local users, while in the same phase, this
                    # problem should go away, as it will allow the roster-push
                    # to arrive after presences every time.
                    pass

                # route the presence first, then do a roster push
                msg.setNextHandler('roster-push')
                msg.setNextHandler('route-server')

                return chainOutput(retVal, d)

            # we're updating/adding the roster item

            groups = [i.text for i in list(item.findall('{jabber:iq:roster}group'))]

            cid = roster.updateContact(cjid, groups, name)

            # get the subscription status before roster push
            sub = roster.getSubPrimaryName(cid)

            # prepare the result for roster push
            query = Roster.createRosterQuery(cjid, sub, name, groups)

            msg.setNextHandler('roster-push')

            return chainOutput(lastRetVal, query)
Esempio n. 47
0
        def act():
            # TODO: verify that it's coming from a known user
            jid = msg.conn.data['user']['jid']
            cjid = JID(tree.get('to'))
            type = tree.get('type')

            if not cjid:
                logging.warning('[%s] No contact jid specified in subscription ' +\
                                'query. Tree: %s', self.__class__, tree)
                # TODO: throw exception here
                return

            roster = Roster(jid)
            # get the RosterItem
            cinfo = roster.getContactInfo(cjid.getBare())

            retVal = lastRetVal

            # C2S SUBSCRIBE
            if type == 'subscribe':

                # we must always route the subscribe presence so as to allow
                # the other servers to resynchronize their sub lists.
                # RFC 3921 9.2
                if not cinfo:
                    # contact doesn't exist, but according to RFC 3921
                    # section 8.2 bullet 4 we MUST create a new roster entry
                    # for it with empty name and groups.
                    roster.updateContact(cjid.getBare())

                    # now refetch the contact info
                    cinfo = roster.getContactInfo(cjid.getBare())

                cid = cinfo.id
                name = cinfo.name
                subscription = cinfo.subscription
                groups = cinfo.groups

                # update the subscription state
                if subscription == Subscription.NONE:
                    roster.setSubscription(cid, Subscription.NONE_PENDING_OUT)
                    subscription = Subscription.NONE_PENDING_OUT
                elif subscription == Subscription.NONE_PENDING_IN:
                    roster.setSubscription(cid, Subscription.NONE_PENDING_IN_OUT)
                    subscription = Subscription.NONE_PENDING_IN_OUT
                elif subscription == Subscription.FROM:
                    roster.setSubscription(cid, Subscription.FROM_PENDING_OUT)
                    subscription = Subscription.FROM_PENDING_OUT

                # send a roster push with ask
                query = Roster.createRosterQuery(cjid.getBare(),
                            Subscription.getPrimaryNameFromState(subscription),
                            name, groups, {'ask' : 'subscribe'})

                # stamp presence with 'from' JID
                treeCopy = deepcopy(tree)
                treeCopy.set('from', jid)

                # prepare the presence data for routing
                d = {
                     'to' : cjid,
                     'data' : treeCopy,
                     }
                retVal = chainOutput(retVal, d)

                # sequence of events in reverse order
                # push the roster first, in case we have to create a new
                # s2s connection
                msg.setNextHandler('route-server')
                msg.setNextHandler('roster-push')

                return chainOutput(retVal, query)

            # C2S SUBSCRIBED
            elif type == 'subscribed':

                if not cinfo:
                    logging.warning("[%s] 'subscribed' presence received for " +\
                                    "non-existent contact %s", self.__class__, cjid)
                else:
                    subscription = cinfo.subscription
                    if cinfo.subscription in (Subscription.NONE_PENDING_IN,
                                              Subscription.NONE_PENDING_IN_OUT,
                                              Subscription.TO_PENDING_IN):
                        # update state and deliver
                        if cinfo.subscription == Subscription.NONE_PENDING_IN:
                            roster.setSubscription(cinfo.id, Subscription.FROM)
                            subscription = Subscription.FROM
                        elif cinfo.subscription == Subscription.NONE_PENDING_IN_OUT:
                            roster.setSubscription(cinfo.id, Subscription.FROM_PENDING_OUT)
                            subscription = Subscription.FROM_PENDING_OUT
                        elif cinfo.subscription == Subscription.TO_PENDING_IN:
                            roster.setSubscription(cinfo.id, Subscription.BOTH)
                            subscription = Subscription.BOTH

                        # roster stanza
                        query = Roster.createRosterQuery(cjid.getBare(),
                                    Subscription.getPrimaryNameFromState(subscription),
                                    cinfo.name, cinfo.groups)

                        # stamp presence with 'from'
                        treeCopy = deepcopy(tree)
                        treeCopy.set('from', jid)

                        toRoute = tostring(treeCopy)

                        # create available presence stanzas for all resources of the user
                        resources = msg.conn.server.launcher.getC2SServer().data['resources']
                        jidForResources = resources.has_key(jid) and resources[jid]
                        if jidForResources:
                            out = u''
                            for i in jidForResources:
                                out += "<presence from='%s/%s'" % (jid, i)
                                out += " to='%s'/>" % cjid.getBare()
                            # and queue for routing
                            toRoute += out

                        # prepare the presence data for routing
                        d = {
                             'to' : cjid,
                             'data' : toRoute,
                             }
                        retVal = chainOutput(retVal, d)

                        # next handlers in reverse order
                        msg.setNextHandler('route-server')
                        msg.setNextHandler('roster-push')

                        return chainOutput(retVal, query)

            # C2S UNSUBSCRIBE
            elif type == 'unsubscribe':

                # we must always route the unsubscribe presence so as to allow
                # the other servers to resynchronize their sub lists.
                # RFC 3921 9.2
                if not cinfo:
                    # we don't have this contact in our roster, but route the
                    # presence anyway
                    # stamp presence with 'from'
                    treeCopy = deepcopy(tree)
                    treeCopy.set('from', jid)

                    # prepare the presence data for routing
                    d = {
                         'to' : cjid,
                         'data' : treeCopy,
                         }
                    msg.setNextHandler('route-server')

                    return chainOutput(retVal, d)
                else:
                    subscription = cinfo.subscription
                    if subscription == Subscription.BOTH: # mutual
                        roster.setSubscription(cinfo.id, Subscription.FROM)
                        subscription = Subscription.FROM
                    elif subscription in (Subscription.NONE_PENDING_OUT, # one way
                                          Subscription.NONE_PENDING_IN_OUT,
                                          Subscription.TO,
                                          Subscription.TO_PENDING_IN):
                        if subscription == Subscription.NONE_PENDING_OUT \
                          or subscription == Subscription.TO:
                            roster.setSubscription(cinfo.id, Subscription.NONE)
                            subscription = Subscription.NONE
                        elif subscription == Subscription.NONE_PENDING_IN_OUT \
                          or subscription == Subscription.TO_PENDING_IN:
                            roster.setSubscription(cinfo.id, Subscription.NONE_PENDING_IN)
                            subscription = Subscription.NONE_PENDING_IN

                    # roster stanza
                    query = Roster.createRosterQuery(cjid.getBare(),
                                Subscription.getPrimaryNameFromState(subscription),
                                cinfo.name, cinfo.groups)

                    # stamp presence with 'from'
                    treeCopy = deepcopy(tree)
                    treeCopy.set('from', jid)

                    # prepare the presence data for routing
                    d = {
                         'to' : cjid,
                         'data' : treeCopy,
                         }
                    retVal = chainOutput(retVal, d)

                    # schedules handlers in reverse order
                    msg.setNextHandler('route-server')
                    msg.setNextHandler('roster-push')

                    return chainOutput(retVal, query)

            # C2S UNSUBSCRIBED
            elif type == 'unsubscribed':

                if not cinfo:
                    logging.warning("[%s] 'unsubscribed' presence received for " +\
                                    "non-existent contact %s", self.__class__, cjid)
                else:
                    subscription = cinfo.subscription
                    if subscription not in (Subscription.NONE,
                                            Subscription.NONE_PENDING_OUT,
                                            Subscription.TO):
                        if subscription == Subscription.NONE_PENDING_IN \
                          or subscription == Subscription.FROM:
                            roster.setSubscription(cinfo.id, Subscription.NONE)
                            subscription = Subscription.NONE
                        elif subscription == Subscription.NONE_PENDING_IN_OUT \
                          or subscription == Subscription.FROM_PENDING_OUT:
                            roster.setSubscription(cinfo.id, Subscription.NONE_PENDING_OUT)
                            subscription = Subscription.NONE
                        elif subscription == Subscription.TO_PENDING_IN \
                          or subscription == Subscription.BOTH:
                            roster.setSubscription(cinfo.id, Subscription.TO)
                            subscription = Subscription.TO

                        # roster query
                        if subscription == Subscription.NONE_PENDING_OUT:
                            itemArgs = {'ask' : 'subscribe'}
                        else:
                            itemArgs = {}
                        query = roster.createRosterQuery(cjid.getBare(),
                                        Subscription.getPrimaryNameFromState(subscription),
                                        cinfo.name, cinfo.groups, itemArgs)

                        # stamp presence with 'from'
                        treeCopy = deepcopy(tree)
                        treeCopy.set('from', jid)

                        toRoute = tostring(treeCopy)

                        # create unavailable presence stanzas for all resources of the user
                        resources = msg.conn.server.launcher.getC2SServer().data['resources']
                        jidForResources = resources.has_key(jid) and resources[jid]
                        if jidForResources:
                            out = u''
                            for i in jidForResources:
                                out += "<presence from='%s/%s'" % (jid, i)
                                out += " to='%s' type='unavailable'/>" % cjid.getBare()
                            # and add to output
                            toRoute += out

                        # prepare the presence data for routing
                        d = {
                             'to' : cjid,
                             'data' : toRoute,
                             }
                        retVal = chainOutput(retVal, d)

                        # handlers in reverse order
                        msg.setNextHandler('route-server')
                        msg.setNextHandler('roster-push')

                        return chainOutput(retVal, query)
Esempio n. 48
0
        def act():
            d = msg.conn.data

            retVal = lastRetVal

            jid = d['user']['jid']
            resource = d['user']['resource']

            roster = Roster(jid)

            presTree = deepcopy(tree)
            presTree.set('from', '%s/%s' % (jid, resource))

            probes = []
            init_rosters = []
            offline_msgs = []
            if tree.get('to') is None and not d['user']['active']:
                # initial presence
                # TODO: we don't need to do it every time. we can cache the
                # data after the first resource is active and just resend
                # that to all new resources
                d['user']['active'] = True

                # get jids of the contacts whose status we're interested in
                cjids = roster.getPresenceSubscriptions()

                probeTree = Element('presence', {
                                                 'type': 'probe',
                                                 'from' : '%s/%s' \
                                                    % (jid, resource)
                                                 })

                # TODO: replace this with a more efficient router handler
                for cjid in cjids:
                    probeTree.set('to', cjid)
                    probeRouteData = {
                                      'to' : cjid,
                                      'data' : deepcopy(probeTree)
                                      }
                    probes.append(probeRouteData)
                    # they're sent first. see below

                # send initial roster list to this user
                rosterTree = Element('presence', {
                                                 'type': 'unavailable',
                                                 'to' : '%s/%s' \
                                                    % (jid, resource)
                                                 })
                for cjid in cjids:
                    rosterTree.set('from', cjid)
                    rosterRouterData = {
                                           'to' : '%s/%s' % (jid, resource),
                                           'data' : deepcopy(rosterTree)
                                       }
                    init_rosters.append(rosterRouterData)

                # send offline message to this user
                try:
                    con = DB()
                    result = []
                    to_jid = JID(jid)
                    with closing(con.cursor()) as cursor:
                        cursor.execute("SELECT fromid, time, content FROM offline WHERE toid = %d ORDER BY time DESC" %
                                       (to_jid.getNumId()))
                        con.commit()
                        result = cursor.fetchall()
                    with closing(con.cursor()) as cursor:
                        cursor.execute("DELETE FROM offline WHERE toid = %d" %
                                       (to_jid.getNumId()))
                        con.commit()
                    for fromid, time, content in result:
                        fromJID = JID(fromid, True).getBare()
                        toJID = '%s/%s' % (jid, resource)

                        reply = Element('message', {
                            'to': toJID,
                            'from': fromJID,
                            'type': 'chat'
                        })

                        body = Element('body')
                        body.text = content
                        reply.append(body)

                        delay = Element('delay', {
                            'xmlns': 'urn:xmpp:delay',
                            'from': fromJID,
                            'stamp': time.strftime("%Y-%m-%dT%H:%M:%SZ")
                        })
                        reply.append(delay)

                        routeData = {
                            'to' : toJID,
                            'data': reply
                        }
                        offline_msgs.append(routeData)
                    logging.debug("[%s] Sending %d offline messages to %s", self.__class__, len(offline_msgs), to_jid.getBare())
                except Exception as e:
                    logging.warning("[%s] Failed to read offline messages: %s", self.__class__, str(e))

                # broadcast to other resources of this user
                retVal = self.broadcastToOtherResources(presTree, msg, retVal, jid, resource)

            elif tree.get('to') is not None:
                # TODO: directed presence
                return
            elif tree.get('type') == 'unavailable':
                # broadcast to other resources of this user
                d['user']['active'] = False
                retVal = self.broadcastToOtherResources(presTree, msg, retVal, jid, resource)

            # record this stanza as the last presence sent from this client
            lastPresence = deepcopy(tree)
            lastPresence.set('from', '%s/%s' % (jid, resource))
            d['user']['lastPresence'] = lastPresence

            # lookup contacts interested in presence
            cjids = roster.getPresenceSubscribers()

            # TODO: replace this with another router handler that would send
            # it out to all cjids in a batch instead of queuing a handler
            # for each
            for cjid in cjids:
                presTree.set('to', cjid)
                presRouteData = {
                     'to' : cjid,
                     'data' : deepcopy(presTree)
                     }
                retVal = chainOutput(retVal, presRouteData)
                msg.setNextHandler('route-server')

            # send the probes first
            for probe in probes:
                msg.setNextHandler('route-server')
                retVal = chainOutput(retVal, probe)

            # send initial rosters
            for init_roster in init_rosters:
                msg.setNextHandler('route-client')
                retVal = chainOutput(retVal, init_roster)

            # send offline messages
            for offline_msg in offline_msgs:
                msg.setNextHandler('route-client')
                retVal = chainOutput(retVal, offline_msg)

            return retVal
Esempio n. 49
0
        def act():
            # we have to be passed a tree to work
            # or a tuple with routingData and a tree
            if not isinstance(lastRetVal, list):
                logging.warning("[%s] lastRetVal is not a list", self.__class__)
                return
            if isinstance(lastRetVal[-1], Element):
                if lastRetVal[-1].tag.find("query") == -1:
                    logging.warning(
                        "[%s] Got a non-query Element last return value" + ". Last return value: %s",
                        self.__class__,
                        lastRetVal,
                    )
            elif isinstance(lastRetVal[-1], tuple):
                if not isinstance(lastRetVal[-1][0], dict) or not isinstance(lastRetVal[-1][1], Element):
                    logging.warning(
                        "[%s] Got a non-query Element last return value" + ". Last return value: %s",
                        self.__class__,
                        lastRetVal,
                    )
                    return
            else:
                logging.warning(
                    "[%s] Roster push needs either a <query> Element "
                    + "as the last item in lastRetVal or a tuple "
                    + "with (routeData, query Element)",
                    self.__class__,
                )
                return

            # this is the roster <query> that we'll send
            # it could be a tuple if we got routing data as well
            query = lastRetVal.pop(-1)
            routeData = None

            # did we get routing data (from S2S)
            if isinstance(query, tuple):
                routeData = query[0]
                query = query[1]

            if routeData:
                jid = routeData["jid"]
                resources = routeData["resources"]
            else:
                jid = msg.conn.data["user"]["jid"]
                resource = msg.conn.data["user"]["resource"]
                resources = msg.conn.server.data["resources"][jid]

            for res, con in resources.items():
                # don't send the roster to clients that didn't request it
                if con.data["user"]["requestedRoster"]:
                    iq = Element("iq", {"to": "%s/%s" % (jid, res), "type": "set", "id": generateId()[:10]})
                    iq.append(query)

                    # TODO: remove this. debug.
                    logging.debug("Sending " + tostring(iq))
                    con.send(tostring(iq))

            if tree.tag == "{jabber:client}iq" and tree.get("id"):
                # send an ack to client if this is in reply to a roster get/set
                id = tree.get("id")
                d = {"to": "%s/%s" % (jid, resource), "type": "result", "id": id}
                iq = Element("iq", d)
                return chainOutput(lastRetVal, iq)
Esempio n. 50
0
class S2SSubscriptionHandler(ThreadedHandler):
    """Handles subscriptions sent from servers within <presence> stanzas.
    ie. <presence> elements with types.
    """
    def __init__(self):
        # this is true when the threaded handler returns
        self.done = False
        # used to pass the output to the next handler
        self.retVal = None

    def handle(self, tree, msg, lastRetVal=None):
        self.done = False
        self.retVal = lastRetVal

        tpool = msg.conn.server.threadpool

        def act():
            # get the contact's jid
            fromAddr = tree.get('from')
            try:
                cjid = JID(fromAddr)
            except Exception, e:
                logging.warning(
                    "[%s] 'from' JID is not properly formatted. Tree: %s",
                    self.__class__, tostring(tree))
                return

            # get the user's jid
            toAddr = tree.get('to')
            try:
                jid = JID(toAddr)
            except Exception, e:
                logging.warning(
                    "[%s] 'to' JID is not properly formatted. Tree: %s",
                    self.__class__, tostring(tree))
                return

            roster = Roster(jid.getBare())

            doRoute = False

            cinfo = roster.getContactInfo(cjid.getBare())
            subType = tree.get('type')

            retVal = lastRetVal

            # S2S SUBSCRIBE
            if subType == 'subscribe':

                if not cinfo:
                    # contact doesn't exist, so it's a first-time add
                    # need to add the contact with subscription None + Pending In
                    roster.updateContact(cjid.getBare(), None, None,
                                         Subscription.NONE_PENDING_IN)
                    cinfo = roster.getContactInfo(cjid.getBare())
                    doRoute = True
                if cinfo.subscription in (Subscription.NONE,
                                          Subscription.NONE_PENDING_OUT,
                                          Subscription.TO):
                    # change state
                    if cinfo.subscription == Subscription.NONE:
                        roster.setSubscription(cinfo.id,
                                               Subscription.NONE_PENDING_IN)
                    elif cinfo.subscription == Subscription.NONE_PENDING_OUT:
                        roster.setSubscription(
                            cinfo.id, Subscription.NONE_PENDING_IN_OUT)
                    elif cinfo.subscription == Subscription.TO:
                        roster.setSubscription(cinfo.id,
                                               Subscription.TO_PENDING_IN)

                    doRoute = True
                elif cinfo.subscription in (Subscription.FROM,
                                            Subscription.FROM_PENDING_OUT,
                                            Subscription.BOTH):
                    # auto-reply with "subscribed" stanza
                    doRoute = False
                    out = "<presence to='%s' from='%s' type='subscribed'/>" % (
                        cjid.getBare(), jid.getBare())
                    # prepare the data for routing
                    subscribedRouting = {
                        'to': cjid.getBare(),
                        'data': out,
                    }
                    retVal = chainOutput(retVal, subscribedRouting)
                    msg.setNextHandler('route-server')

                # ignore presence in other states

                if doRoute:
                    # queue the stanza for delivery
                    stanzaRouting = {'to': jid, 'data': tree}
                    retVal = chainOutput(retVal, stanzaRouting)
                    msg.setNextHandler('route-client')

                return retVal

            # S2S SUBSCRIBED
            elif subType == 'subscribed':

                if cinfo:
                    subscription = cinfo.subscription
                    if cinfo.subscription in (Subscription.NONE_PENDING_OUT,
                                              Subscription.NONE_PENDING_IN_OUT,
                                              Subscription.FROM_PENDING_OUT):
                        # change state
                        if cinfo.subscription == Subscription.NONE_PENDING_OUT:
                            roster.setSubscription(cinfo.id, Subscription.TO)
                            subscription = Subscription.TO
                        elif cinfo.subscription == Subscription.NONE_PENDING_IN_OUT:
                            roster.setSubscription(cinfo.id,
                                                   Subscription.TO_PENDING_IN)
                            subscription = Subscription.TO_PENDING_IN
                        elif cinfo.subscription == Subscription.FROM_PENDING_OUT:
                            roster.setSubscription(cinfo.id, Subscription.BOTH)
                            subscription = Subscription.BOTH

                        # forward the subscribed presence
                        # prepare the presence data for routing
                        d = {
                            'to': jid,
                            'data': tree,
                        }
                        retVal = chainOutput(retVal, d)

                        # create an updated roster item for roster push
                        query = Roster.createRosterQuery(
                            cinfo.jid,
                            Subscription.getPrimaryNameFromState(subscription),
                            cinfo.name, cinfo.groups)

                        routeData = {}
                        conns = msg.conn.server.launcher.getC2SServer(
                        ).data['resources']
                        bareJID = jid.getBare()
                        if conns.has_key(bareJID):
                            routeData['jid'] = bareJID
                            routeData['resources'] = conns[bareJID]

                        # next handlers (reverse order)
                        msg.setNextHandler('route-client')
                        msg.setNextHandler('roster-push')

                        return chainOutput(retVal, (routeData, query))

            # S2S UNSUBSCRIBE
            elif subType == 'unsubscribe':

                if cinfo:
                    subscription = cinfo.subscription
                    if subscription not in (Subscription.NONE,
                                            Subscription.NONE_PENDING_OUT,
                                            Subscription.TO):
                        if subscription == Subscription.NONE_PENDING_IN \
                          or subscription == Subscription.FROM:
                            roster.setSubscription(cinfo.id, Subscription.NONE)
                            subscription = Subscription.NONE
                        elif subscription == Subscription.NONE_PENDING_IN_OUT \
                          or subscription == Subscription.FROM_PENDING_OUT:
                            roster.setSubscription(
                                cinfo.id, Subscription.NONE_PENDING_OUT)
                            subscription = Subscription.NONE_PENDING_OUT
                        elif subscription == Subscription.TO_PENDING_IN \
                          or subscription == Subscription.BOTH:
                            roster.setSubscription(cinfo.id, Subscription.TO)
                            subscription = Subscription.TO

                        # these steps are really in reverse order due to handler queuing

                        # send unavailable presence from all resources
                        resources = msg.conn.server.launcher.getC2SServer(
                        ).data['resources']
                        bareJID = jid.getBare()
                        jidForResources = resources.has_key(
                            bareJID) and resources[bareJID]
                        if jidForResources:
                            out = u''
                            for i in jidForResources:
                                out += "<presence from='%s/%s'" % (bareJID, i)
                                out += " to='%s' type='unavailable'/>" % cjid.getBare(
                                )
                            # and route it
                            unavailableRouting = {'to': cjid, 'data': out}
                            retVal = chainOutput(retVal, unavailableRouting)
                            # 4. route the unavailable presence back to server
                            msg.setNextHandler('route-server')

                        # auto-reply with "unsubscribed" stanza
                        out = "<presence to='%s' from='%s' type='unsubscribed'/>" % (
                            cjid.getBare(), jid.getBare())
                        unsubscribedRouting = {
                            'to': jid.getBare(),
                            'data': out
                        }
                        retVal = chainOutput(retVal, unsubscribedRouting)

                        # prepare the unsubscribe presence data for routing to client
                        unsubscribeRouting = {
                            'to': jid,
                            'data': tree,
                        }
                        retVal = chainOutput(retVal, unsubscribeRouting)

                        # create an updated roster item for roster push
                        # we should really create add an ask='subscribe' for
                        # the NONE_PENDING_OUT state, but the spec doesn't
                        # say anything about this, so leave it out for now.
                        query = Roster.createRosterQuery(
                            cinfo.jid,
                            Subscription.getPrimaryNameFromState(subscription),
                            cinfo.name, cinfo.groups)

                        # needed for S2S roster push
                        routeData = {}
                        conns = msg.conn.server.launcher.getC2SServer(
                        ).data['resources']
                        bareJID = jid.getBare()
                        if conns.has_key(bareJID):
                            routeData['jid'] = bareJID
                            routeData['resources'] = conns[bareJID]

                        # handlers in reverse order. actual order:
                        # 1. push the updated roster
                        # 2. route the unsubscribe presence to client
                        # 3. route the unsubscribed presence back to server
                        # 4. see above. it's optional, since no resources could
                        #    be online by this point
                        msg.setNextHandler('route-server')
                        msg.setNextHandler('route-client')
                        msg.setNextHandler('roster-push')

                        return chainOutput(retVal, (routeData, query))

            # S2S UNSUBSCRIBED
            elif subType == 'unsubscribed':

                if cinfo:
                    subscription = cinfo.subscription
                    if subscription not in (Subscription.NONE,
                                            Subscription.NONE_PENDING_IN,
                                            Subscription.FROM):
                        # change state
                        if subscription == Subscription.NONE_PENDING_OUT \
                          or subscription == Subscription.TO:
                            roster.setSubscription(cinfo.id, Subscription.NONE)
                            subscription = Subscription.NONE
                        elif subscription == Subscription.NONE_PENDING_IN_OUT \
                          or subscription == Subscription.TO_PENDING_IN:
                            roster.setSubscription(
                                cinfo.id, Subscription.NONE_PENDING_IN)
                            subscription = Subscription.NONE_PENDING_IN
                        elif subscription == Subscription.FROM_PENDING_OUT \
                          or subscription == Subscription.BOTH:
                            roster.setSubscription(cinfo.id, Subscription.FROM)
                            subscription = Subscription.FROM

                        # prepare the unsubscribed presence data for routing
                        d = {
                            'to': jid,
                            'data': tree,
                        }
                        retVal = chainOutput(retVal, d)

                        # create an updated roster item for roster push
                        query = Roster.createRosterQuery(
                            cinfo.jid,
                            Subscription.getPrimaryNameFromState(subscription),
                            cinfo.name, cinfo.groups)

                        # needed for S2S roster push
                        routeData = {}
                        conns = msg.conn.server.launcher.getC2SServer(
                        ).data['resources']
                        bareJID = jid.getBare()
                        if conns.has_key(bareJID):
                            routeData['jid'] = bareJID
                            routeData['resources'] = conns[bareJID]

                        # handlers in reverse order
                        # actually: push roster first, then route presence
                        msg.setNextHandler('route-client')
                        msg.setNextHandler('roster-push')

                        return chainOutput(retVal, (routeData, query))
Esempio n. 51
0
    def handle(self, tree, msg, lastRetVal=None):

        # generate error response tree
        def get_error_tree(iq, type, code, tag):
            result = Element('iq', {'type': 'error', 'id': iq.get('id')})
            result.append(iq)
            err_tree = Element('error', {'type': type, 'code': code,})
            SubElement(err_tree, tag, {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})
            result.append(err_tree)
            return result

        if len(tree) > 0:
            # get the original iq msg
            origIQ = tree
        else:
            logging.warning("[%s] Original <iq> missing:\n%s",
                            self.__class__, tostring(tree))
            return lastRetVal

        id = origIQ.get('id')
        if id:
            try:
                username_tree = origIQ[0][0]
                password_tree = origIQ[0][1]
                if username_tree.tag == '{jabber:iq:register}username' and password_tree.tag == '{jabber:iq:register}password':
                    username = username_tree.text
                    password = password_tree.text

                    # ruling out illegal username
                    if not re.match("^[a-zA-Z0-9_.-]+$", username):
                        raise Exception('Username not accepted')

                    # write to database
                    try:
                        con = DB()
                        with closing(con.cursor()) as cursor:
                            cursor.execute("INSERT INTO jids (jid, password) VALUES ('%s@%s', '%s')" %
                                           (username, msg.conn.server.hostname, password))
                            con.commit()
                        res = Element('iq', {'type': 'result', 'id': id})
                        query = deepcopy(origIQ[0])
                        query.insert(0, Element('registered'))
                        res.append(query)
                        return chainOutput(lastRetVal, res)

                    # conflict response
                    except sqlite.IntegrityError as e:
                        if e.message.find('column jid is not unique') >= 0:
                            logging.warning("[%s] Username conflict in <iq>:\n%s",
                            self.__class__, str(e), tostring(origIQ))
                            res = get_error_tree(origIQ, 'cancel', '409', 'conflict')
                            return chainOutput(lastRetVal, res)
                        else:
                            raise e

                else:
                    raise Exception('IQ missing registration fields')

            # error response
            except Exception as e:
                error_string = str(e)
                logging.warning("[%s] Register failed '%s' in <iq>:\n%s",
                            self.__class__, str(e) if error_string else 'Unknown error',
                            tostring(origIQ))
                res = get_error_tree(origIQ, 'modify', '406', 'not-acceptable')
                return chainOutput(lastRetVal, res)

        else:
            logging.warning("[%s] No id in <iq>:\n%s",
                            self.__class__, tostring(origIQ))

        return lastRetVal
Esempio n. 52
0
        def act():
            # TODO: verify that it's coming from a known user
            jid = msg.conn.data['user']['jid']
            cjid = JID(tree.get('to'))
            type = tree.get('type')

            if not cjid:
                logging.warning('[%s] No contact jid specified in subscription ' +\
                                'query. Tree: %s', self.__class__, tree)
                # TODO: throw exception here
                return

            roster = Roster(jid)
            # get the RosterItem
            cinfo = roster.getContactInfo(cjid.getBare())

            retVal = lastRetVal

            # C2S SUBSCRIBE
            if type == 'subscribe':

                # we must always route the subscribe presence so as to allow
                # the other servers to resynchronize their sub lists.
                # RFC 3921 9.2
                if not cinfo:
                    # contact doesn't exist, but according to RFC 3921
                    # section 8.2 bullet 4 we MUST create a new roster entry
                    # for it with empty name and groups.
                    roster.updateContact(cjid.getBare())

                    # now refetch the contact info
                    cinfo = roster.getContactInfo(cjid.getBare())

                cid = cinfo.id
                name = cinfo.name
                subscription = cinfo.subscription
                groups = cinfo.groups

                # update the subscription state
                if subscription == Subscription.NONE:
                    roster.setSubscription(cid, Subscription.NONE_PENDING_OUT)
                    subscription = Subscription.NONE_PENDING_OUT
                elif subscription == Subscription.NONE_PENDING_IN:
                    roster.setSubscription(cid,
                                           Subscription.NONE_PENDING_IN_OUT)
                    subscription = Subscription.NONE_PENDING_IN_OUT
                elif subscription == Subscription.FROM:
                    roster.setSubscription(cid, Subscription.FROM_PENDING_OUT)
                    subscription = Subscription.FROM_PENDING_OUT

                # send a roster push with ask
                query = Roster.createRosterQuery(
                    cjid.getBare(),
                    Subscription.getPrimaryNameFromState(subscription), name,
                    groups, {'ask': 'subscribe'})

                # stamp presence with 'from' JID
                treeCopy = deepcopy(tree)
                treeCopy.set('from', jid)

                # prepare the presence data for routing
                d = {
                    'to': cjid,
                    'data': treeCopy,
                }
                retVal = chainOutput(retVal, d)

                # sequence of events in reverse order
                # push the roster first, in case we have to create a new
                # s2s connection
                msg.setNextHandler('route-server')
                msg.setNextHandler('roster-push')

                return chainOutput(retVal, query)

            # C2S SUBSCRIBED
            elif type == 'subscribed':

                if not cinfo:
                    logging.warning("[%s] 'subscribed' presence received for " +\
                                    "non-existent contact %s", self.__class__, cjid)
                else:
                    subscription = cinfo.subscription
                    if cinfo.subscription in (Subscription.NONE_PENDING_IN,
                                              Subscription.NONE_PENDING_IN_OUT,
                                              Subscription.TO_PENDING_IN):
                        # update state and deliver
                        if cinfo.subscription == Subscription.NONE_PENDING_IN:
                            roster.setSubscription(cinfo.id, Subscription.FROM)
                            subscription = Subscription.FROM
                        elif cinfo.subscription == Subscription.NONE_PENDING_IN_OUT:
                            roster.setSubscription(
                                cinfo.id, Subscription.FROM_PENDING_OUT)
                            subscription = Subscription.FROM_PENDING_OUT
                        elif cinfo.subscription == Subscription.TO_PENDING_IN:
                            roster.setSubscription(cinfo.id, Subscription.BOTH)
                            subscription = Subscription.BOTH

                        # roster stanza
                        query = Roster.createRosterQuery(
                            cjid.getBare(),
                            Subscription.getPrimaryNameFromState(subscription),
                            cinfo.name, cinfo.groups)

                        # stamp presence with 'from'
                        treeCopy = deepcopy(tree)
                        treeCopy.set('from', jid)

                        toRoute = tostring(treeCopy)

                        # create available presence stanzas for all resources of the user
                        resources = msg.conn.server.launcher.getC2SServer(
                        ).data['resources']
                        jidForResources = resources.has_key(
                            jid) and resources[jid]
                        if jidForResources:
                            out = u''
                            for i in jidForResources:
                                out += "<presence from='%s/%s'" % (jid, i)
                                out += " to='%s'/>" % cjid.getBare()
                            # and queue for routing
                            toRoute += out

                        # prepare the presence data for routing
                        d = {
                            'to': cjid,
                            'data': toRoute,
                        }
                        retVal = chainOutput(retVal, d)

                        # next handlers in reverse order
                        msg.setNextHandler('route-server')
                        msg.setNextHandler('roster-push')

                        return chainOutput(retVal, query)

            # C2S UNSUBSCRIBE
            elif type == 'unsubscribe':

                # we must always route the unsubscribe presence so as to allow
                # the other servers to resynchronize their sub lists.
                # RFC 3921 9.2
                if not cinfo:
                    # we don't have this contact in our roster, but route the
                    # presence anyway
                    # stamp presence with 'from'
                    treeCopy = deepcopy(tree)
                    treeCopy.set('from', jid)

                    # prepare the presence data for routing
                    d = {
                        'to': cjid,
                        'data': treeCopy,
                    }
                    msg.setNextHandler('route-server')

                    return chainOutput(retVal, d)
                else:
                    subscription = cinfo.subscription
                    if subscription == Subscription.BOTH:  # mutual
                        roster.setSubscription(cinfo.id, Subscription.FROM)
                        subscription = Subscription.FROM
                    elif subscription in (
                            Subscription.NONE_PENDING_OUT,  # one way
                            Subscription.NONE_PENDING_IN_OUT,
                            Subscription.TO,
                            Subscription.TO_PENDING_IN):
                        if subscription == Subscription.NONE_PENDING_OUT \
                          or subscription == Subscription.TO:
                            roster.setSubscription(cinfo.id, Subscription.NONE)
                            subscription = Subscription.NONE
                        elif subscription == Subscription.NONE_PENDING_IN_OUT \
                          or subscription == Subscription.TO_PENDING_IN:
                            roster.setSubscription(
                                cinfo.id, Subscription.NONE_PENDING_IN)
                            subscription = Subscription.NONE_PENDING_IN

                    # roster stanza
                    query = Roster.createRosterQuery(
                        cjid.getBare(),
                        Subscription.getPrimaryNameFromState(subscription),
                        cinfo.name, cinfo.groups)

                    # stamp presence with 'from'
                    treeCopy = deepcopy(tree)
                    treeCopy.set('from', jid)

                    # prepare the presence data for routing
                    d = {
                        'to': cjid,
                        'data': treeCopy,
                    }
                    retVal = chainOutput(retVal, d)

                    # schedules handlers in reverse order
                    msg.setNextHandler('route-server')
                    msg.setNextHandler('roster-push')

                    return chainOutput(retVal, query)

            # C2S UNSUBSCRIBED
            elif type == 'unsubscribed':

                if not cinfo:
                    logging.warning("[%s] 'unsubscribed' presence received for " +\
                                    "non-existent contact %s", self.__class__, cjid)
                else:
                    subscription = cinfo.subscription
                    if subscription not in (Subscription.NONE,
                                            Subscription.NONE_PENDING_OUT,
                                            Subscription.TO):
                        if subscription == Subscription.NONE_PENDING_IN \
                          or subscription == Subscription.FROM:
                            roster.setSubscription(cinfo.id, Subscription.NONE)
                            subscription = Subscription.NONE
                        elif subscription == Subscription.NONE_PENDING_IN_OUT \
                          or subscription == Subscription.FROM_PENDING_OUT:
                            roster.setSubscription(
                                cinfo.id, Subscription.NONE_PENDING_OUT)
                            subscription = Subscription.NONE
                        elif subscription == Subscription.TO_PENDING_IN \
                          or subscription == Subscription.BOTH:
                            roster.setSubscription(cinfo.id, Subscription.TO)
                            subscription = Subscription.TO

                        # roster query
                        if subscription == Subscription.NONE_PENDING_OUT:
                            itemArgs = {'ask': 'subscribe'}
                        else:
                            itemArgs = {}
                        query = roster.createRosterQuery(
                            cjid.getBare(),
                            Subscription.getPrimaryNameFromState(subscription),
                            cinfo.name, cinfo.groups, itemArgs)

                        # stamp presence with 'from'
                        treeCopy = deepcopy(tree)
                        treeCopy.set('from', jid)

                        toRoute = tostring(treeCopy)

                        # create unavailable presence stanzas for all resources of the user
                        resources = msg.conn.server.launcher.getC2SServer(
                        ).data['resources']
                        jidForResources = resources.has_key(
                            jid) and resources[jid]
                        if jidForResources:
                            out = u''
                            for i in jidForResources:
                                out += "<presence from='%s/%s'" % (jid, i)
                                out += " to='%s' type='unavailable'/>" % cjid.getBare(
                                )
                            # and add to output
                            toRoute += out

                        # prepare the presence data for routing
                        d = {
                            'to': cjid,
                            'data': toRoute,
                        }
                        retVal = chainOutput(retVal, d)

                        # handlers in reverse order
                        msg.setNextHandler('route-server')
                        msg.setNextHandler('roster-push')

                        return chainOutput(retVal, query)
Esempio n. 53
0
        def act():
            d = msg.conn.data

            retVal = lastRetVal

            jid = d['user']['jid']
            resource = d['user']['resource']

            roster = Roster(jid)

            presTree = deepcopy(tree)
            presTree.set('from', '%s/%s' % (jid, resource))

            probes = []
            if tree.get('to') is None and not d['user']['active']:
                # initial presence
                # TODO: we don't need to do it every time. we can cache the
                # data after the first resource is active and just resend
                # that to all new resources
                d['user']['active'] = True

                # get jids of the contacts whose status we're interested in
                cjids = roster.getPresenceSubscriptions()

                probeTree = Element('presence', {
                                                 'type': 'probe',
                                                 'from' : '%s/%s' \
                                                    % (jid, resource)
                                                 })

                # TODO: replace this with a more efficient router handler
                for cjid in cjids:
                    probeTree.set('to', cjid)
                    probeRouteData = {
                                      'to' : cjid,
                                      'data' : deepcopy(probeTree)
                                      }
                    probes.append(probeRouteData)
                    # they're sent first. see below

                # broadcast to other resources of this user
                retVal = self.broadcastToOtherResources(presTree, msg, retVal, jid, resource)

            elif tree.get('to') is not None:
                # TODO: directed presence
                return
            elif tree.get('type') == 'unavailable':
                # broadcast to other resources of this user
                d['user']['active'] = False
                retVal = self.broadcastToOtherResources(presTree, msg, retVal, jid, resource)

            # record this stanza as the last presence sent from this client
            lastPresence = deepcopy(tree)
            lastPresence.set('from', '%s/%s' % (jid, resource))
            d['user']['lastPresence'] = lastPresence

            # lookup contacts interested in presence
            cjids = roster.getPresenceSubscribers()

            # TODO: replace this with another router handler that would send
            # it out to all cjids in a batch instead of queuing a handler
            # for each
            for cjid in cjids:
                presTree.set('to', cjid)
                presRouteData = {
                     'to' : cjid,
                     'data' : deepcopy(presTree)
                     }
                retVal = chainOutput(retVal, presRouteData)
                msg.setNextHandler('route-server')

            # send the probes first
            for probe in probes:
                msg.setNextHandler('route-server')
                retVal = chainOutput(retVal, probe)

            return retVal