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)
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, username, digest): con = DBautocommit() c = con.cursor() c.execute("SELECT password FROM jids WHERE \ jid = ?", (username + '@%s' % self.msg.conn.server.hostname, )) res = c.fetchone() if res: password = res[0] else: c.close() con.close() raise IQAuthError c.close() con.close() s = sha1() s.update(self.streamid + password) hashtext = s.hexdigest() if hashtext == digest: d = self.msg.conn.data d['user']['jid'] = '%s@%s' % (username, self.msg.conn.server.hostname) # record the JID for local delivery self.msg.conn.server.conns[self.msg.conn.id] = (JID( d['user']['jid']), self.msg.conn) self.msg.conn.parser.resetParser() return else: raise IQAuthError
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 getJID(to): """Convert 'to' to a JID object""" if isinstance(to, JID): return to else: try: jid = JID(to) except Exception, e: raise Exception, "Can't convert %s to a JID object" % to
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(): 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): # check that we have the to and from fields in the message and # just forward toJID = tree.get('to') jid = msg.conn.data['user']['jid'] resource = msg.conn.data['user']['resource'] try: toJID = JID(toJID) except: logging.debug("[%s] 'to' attribute in message not a real JID", self.__class__) return stampedTree = copy(tree) stampedTree.set('from', '%s/%s' % (jid, resource)) routeData = {'to': toJID.__str__(), 'data': stampedTree} msg.setNextHandler('route-server') return chainOutput(lastRetVal, routeData)
def handle(self, b64text): """Verify the username/password in response""" authtext = '' if b64text: try: authtext = fromBase64(b64text) except: raise SASLIncorrectEncodingError auth = authtext.split('\x00') if len(auth) != 3: raise SASLIncorrectEncodingError con = DBautocommit() c = con.cursor() c.execute("SELECT * FROM jids WHERE \ jid = ? AND password = ?" , (auth[1] + \ '@' + self.msg.conn.server.hostname, auth[2])) res = c.fetchall() if len(res) == 0: c.close() con.close() raise SASLAuthError c.close() con.close() self.msg.conn.data['sasl']['complete'] = True self.msg.conn.data['sasl']['in-progress'] = False self.msg.conn.data['user']['jid'] = '%s@%s' % ( auth[1], self.msg.conn.server.hostname) # record the JID for local delivery self.msg.conn.server.conns[self.msg.conn.id] = (JID( self.msg.conn.data['user']['jid']), self.msg.conn) self.msg.conn.parser.resetParser() return Element('success', {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
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 handle(self, data=None): """Performs DIGEST-MD5 auth based on current state. data -- either None for initial challenge, base64-encoded text when the client responds to challenge 1, or the tree when the client responds to challenge 2. """ # TODO: authz # TODO: subsequent auth qop = 'qop="auth"' charset = 'charset=utf-8' algo = 'algorithm=md5-sess' if self.state == SASLDigestMD5.INIT: # initial challenge self.nonce = generateId() self.state = SASLDigestMD5.SENT_CHALLENGE1 nonce = 'nonce="%s"' % self.nonce realm = 'realm="%s"' % self.realm res = Element('challenge', {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'}) res.text = base64.b64encode(','.join( [realm, qop, nonce, charset, algo])) return res elif self.state == SASLDigestMD5.SENT_CHALLENGE1 and data: # response to client's reponse (ie. challenge 2) try: text = fromBase64(data) except: raise SASLIncorrectEncodingError pairs = self._parse(text) try: username = pairs['username'] nonce = pairs['nonce'] realm = pairs['realm'] cnonce = pairs['cnonce'] nc = pairs['nc'] qop = pairs['qop'] response = pairs['response'] digest_uri = pairs['digest-uri'] except KeyError: self._handleFailure() raise SASLAuthError self.username = username # authz is ignored for now if nonce != self.nonce or realm != self.realm \ or int(nc, 16) != 1 or qop[0] != 'auth' or not response\ or not digest_uri: self._handleFailure() raise SASLAuthError # fetch the password now con = DBautocommit() c = con.cursor() c.execute( "SELECT password FROM jids WHERE \ jid = ?", (username + '@%s' % self.msg.conn.server.hostname, )) for row in c: password = row['password'] break else: self._handleFailure() c.close() con.close() raise SASLAuthError c.close() con.close() # compute the digest as per RFC 2831 a1 = "%s:%s:%s" % (H("%s:%s:%s" % (username, realm, password)), nonce, cnonce) a2 = ":%s" % digest_uri a2client = "AUTHENTICATE:%s" % digest_uri digest = HEX( KD( HEX(H(a1)), "%s:%s:%s:%s:%s" % (nonce, nc, cnonce, "auth", HEX(H(a2client))))) if digest == response: rspauth = HEX( KD( HEX(H(a1)), "%s:%s:%s:%s:%s" % (nonce, nc, cnonce, "auth", HEX(H(a2))))) self.state = SASLDigestMD5.SENT_CHALLENGE2 res = Element('challenge', {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'}) res.text = base64.b64encode(u"rspauth=%s" % rspauth) return res else: self._handleFailure() raise SASLAuthError elif self.state == SASLDigestMD5.SENT_CHALLENGE2 and isinstance( data, Element): # expect to get <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/> respInd = data.tag.find( '{urn:ietf:params:xml:ns:xmpp-sasl}response') d = self.msg.conn.data if respInd != -1 and len(data) == 0: self.state = SASLDigestMD5.INIT d['sasl']['complete'] = True d['sasl']['in-progress'] = False d['user']['jid'] = '%s@%s' % (self.username, self.msg.conn.server.hostname) # record the JID for local delivery self.msg.conn.server.conns[self.msg.conn.id] = (JID( d['user']['jid']), self.msg.conn) self.msg.conn.parser.resetParser() res = Element('success', {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'}) return res else: self._handleFailure() raise SASLAuthError else: self._handleFailure() raise SASLAuthError