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
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
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
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
def prepareDataForSending(lastRetVal): """Converts lastRetVal into unicode data that's ready to be sent over the wire. lastRetVal can be either a single unit or a list of: text, Element values. """ out = u'' # need to test for None in case it's Element without children if lastRetVal is not None and not isinstance(lastRetVal, Exception): # process lastRetVal as a list of values to write to socket if not isinstance(lastRetVal, list): lastRetVal = [lastRetVal] for item in lastRetVal: if isinstance(item, et.Element): out += tostring(item) elif isinstance(item, str): out += unicode(item) elif isinstance(item, unicode): out += item else: logging.debug("[prepareDataForSending] Attempting to " +\ "write an object of" +\ " type %s to socket", type(item)) return out
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
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
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
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
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
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
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
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)
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
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)
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
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()
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
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)
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)
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)
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)
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)