示例#1
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)
示例#2
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)
示例#3
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)
示例#4
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)
示例#5
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