示例#1
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)
示例#2
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))
示例#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
文件: iq.py 项目: linusyang/pjabberd
        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)
示例#5
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)