def testRoster(): global count, num num = 400 count = 0 def _presence(stanza): global count, num count += 1 if count >= 400: print 'received all presence' xs.addObserver('/presence', _presence) _jid = jid.JID(tuple=(None, self.network, None)) r = domish.Element((None, 'iq')) r.addUniqueId() r['type'] = 'get' q = r.addElement((xmppim.NS_ROSTER, 'query')) for n in range(num): _jid.user = util.rand_str(util.USERID_LENGTH, util.CHARSBOX_HEX_LOWERCASE) item = q.addElement((None, 'item')) item['jid'] = _jid.userhost() xs.send(r) if self.peer is not None: _jid = util.userid_to_jid(self.peer, self.network) r = domish.Element((None, 'iq')) r['type'] = 'get' r['id'] = util.rand_str(8) q = r.addElement((xmppim.NS_ROSTER, 'query')) item = q.addElement((None, 'item')) item['jid'] = _jid.userhost() xs.send(r)
def testProbe(): if self.peer is not None: userid, resource = util.split_userid(self.peer) presence = xmppim.Presence( jid.JID(tuple=(userid, self.network, resource)), 'probe') presence['id'] = util.rand_str(8) xs.send(presence)
def doSubscribe(self, to, subscriber, gid=None, response_only=False, send_subscribed=True): """Subscribe a given user to events from another one.""" if not response_only: try: if subscriber not in self.subscriptions[to]: self.subscriptions[to].append(subscriber) except: self.subscriptions[to] = [subscriber] if send_subscribed: # send subscription accepted immediately pres = domish.Element((None, "presence")) if gid: pres['id'] = gid pres['to'] = subscriber.full() pres['from'] = to.userhost() pres['type'] = 'subscribed' pres.consumed = True self.send(pres) if not response_only: # simulate a presence probe response if not gid: gid = util.rand_str(8) self.cache.send_user_presence(gid, subscriber, to) """
def network_presence_probe(self, to): """ Broadcast a presence probe to find the given L{JID}. @return: a list of stanza IDs sent to the network that can be watched to for responses """ """ presence = domish.Element((None, 'presence')) presence['type'] = 'probe' presence['from'] = self.parent.network idList = [] #to = jid.JID(tuple=(to.user, to.host, to.resource)) for server in self.parent.keyring.hostlist(): #to.host = server presence['to'] = to.user + '@' + server if to.resource: presence['to'] += '/' + to.resource presence.addUniqueId() #presence['id'] = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) self.send(presence) idList.append(presence['id']) """ # we need to be fast here idList = [] presence = "<presence type='probe' from='%s' to='%%s' id='%%s'/>" % (self.parent.network, ) for server in self.parent.keyring.hostlist(): packetId = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) dest = to.user + '@' + server if to.resource: dest += '/' + to.resource self.send(presence % (dest, packetId)) idList.append(packetId) return idList
def broadcast_public_key(self, userid, keydata): """Broadcasts to all remote c2s the given public key.""" # create vcard iq_vcard = domish.Element((None, 'iq')) iq_vcard['type'] = 'set' iq_vcard['from'] = util.userid_to_jid( userid, self.xmlstream.thisEntity.host).full() # add vcard vcard = iq_vcard.addElement((xmlstream2.NS_XMPP_VCARD4, 'vcard')) if keydata: vcard_key = vcard.addElement((None, 'key')) vcard_data = vcard_key.addElement((None, 'uri')) vcard_data.addContent(xmlstream2.DATA_PGP_PREFIX + base64.b64encode(keydata)) # send vcard to remote c2s for server in self.keyring.hostlist(): if server != self.servername: iq_vcard['id'] = util.rand_str(8) # remote server iq_vcard['to'] = util.component_jid(server, util.COMPONENT_C2S) # consume any response (very high priority) self.xmlstream.addOnetimeObserver( "/iq[@id='%s']" % iq_vcard['id'], self.consume, 500) # send! self.send_wrapped(iq_vcard, self.xmlstream.thisEntity.full())
def testBad(): receipt = domish.Element((None, 'message')) receipt['id'] = util.rand_str(8) receipt['type'] = 'chat' receipt['to'] = self.peer + '@' + self.network child = receipt.addElement(('urn:xmpp:server-receipts', 'received')) #child['id'] = util.rand_str(40) xs.send(receipt)
def testSubscribe(self): jid_from = jid.JID('*****@*****.**') jid_to = jid.JID('*****@*****.**') gid = util.rand_str(8) self.resolver.subscribe(self.resolver.translateJID(jid_from), self.resolver.translateJID(jid_to), gid, False) subscriptions = { jid.JID('*****@*****.**') : [ jid.JID('*****@*****.**') ] } self.assertDictEqual(self.resolver.subscriptions, subscriptions, 'Subscriptions not maching.')
def testSubscribe(self): jid_from = jid.JID('[email protected]/TEST001') jid_to = jid.JID('*****@*****.**') gid = util.rand_str(8) self.resolver.subscribe(self.resolver.translateJID(jid_from), self.resolver.translateJID(jid_to), gid, False) subscriptions = { jid.JID('*****@*****.**') : [ jid.JID('[email protected]/TEST001') ] } self.assertDictEqual(self.resolver.subscriptions, subscriptions, 'Subscriptions not maching.')
def testMessage(): jid = xs.authenticator.jid message = domish.Element((None, 'message')) message['id'] = 'kontalk' + util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) message['type'] = 'chat' if self.peer: message['to'] = util.userid_to_jid(self.peer, self.network).full() else: message['to'] = jid.userhost() message.addElement((None, 'body'), content='test message') message.addElement(('urn:xmpp:server-receipts', 'request')) xs.send(message)
def _ping(self): """Sends a ping request to client.""" ping = domish.Element((None, 'iq')) ping['from'] = self.parent.network ping['type'] = 'get' ping['id'] = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) ping.addElement((xmlstream2.NS_XMPP_PING, 'ping')) self.send(ping) # setup ping timeout self.pinger = None self.ping_timeout = reactor.callLater(self.PING_TIMEOUT, self._timeout) # observe pong self.xmlstream.addObserver("/iq[@type='result'][@id='%s']" % (ping['id'], ), self.pong, 600)
def execute(self, stanza): stanza.consumed = True res = xmlstream.toResponse(stanza, "result") cmd = res.addElement((xmlstream2.NS_PROTO_COMMANDS, "command")) cmd["sessionid"] = util.rand_str(16) cmd["node"] = stanza.command["node"] cmd["status"] = "completed" slist = cmd.addElement(("http://kontalk.org/extensions/serverlist", "serverlist")) for host in self.handler.parent.router.keyring.hostlist(): item = slist.addElement((None, "item")) item["node"] = host return res
def _broadcast_privacy_list_change(self, dest, src, node): # broadcast to all resolvers iq = domish.Element((None, 'iq')) iq['from'] = dest iq['type'] = 'set' iq['id'] = util.rand_str(8) nodeElement = iq.addElement((xmlstream2.NS_IQ_BLOCKING, node)) elem = nodeElement.addElement((None, 'item')) elem['jid'] = src from_component = util.component_jid(self.servername, util.COMPONENT_C2S) for server in self.keyring.hostlist(): if server != self.servername: iq['to'] = util.component_jid(server, util.COMPONENT_C2S) self.send_wrapped(iq, from_component)
def testSubscribe(): # subscription request self.index = 0 if self.peer is not None: userid, resource = util.split_userid(self.peer) presence = xmppim.Presence(jid.JID(tuple=(userid, self.network, None)), 'subscribe') presence['id'] = util.rand_str(8) xs.send(presence) else: def pres(): self.index += 1 presence = xmppim.AvailablePresence(statuses={None: 'status message (%d)' % (self.index, )}) xs.send(presence) LoopingCall(pres).start(2, False)
def execute(self, stanza): stanza.consumed = True res = xmlstream.toResponse(stanza, 'result') cmd = res.addElement((xmlstream2.NS_PROTO_COMMANDS, 'command')) cmd['sessionid'] = util.rand_str(16) cmd['node'] = stanza.command['node'] cmd['status'] = 'completed' slist = cmd.addElement( ('http://kontalk.org/extensions/serverlist', 'serverlist')) for host in self.handler.parent.router.keyring.hostlist(): item = slist.addElement((None, 'item')) item['node'] = host return res
def testMassProbe(): global count, num num = 400 count = 0 def _presence(stanza): global count, num count += 1 if count >= 400: print 'received all presence' xs.addObserver('/presence', _presence) for n in range(num): userid = util.rand_str(util.USERID_LENGTH, util.CHARSBOX_HEX_LOWERCASE) presence = xmppim.Presence(jid.JID(tuple=(userid, self.network, None)), 'probe') xs.send(presence)
def send_privacy_lists(self, pname, plist, addr_from): sender = util.component_jid(self.parent.servername, util.COMPONENT_C2S) for user, wl in plist.iteritems(): iq = domish.Element((None, 'iq')) iq['from'] = '%s@%s' % (user, self.parent.network) iq['type'] = 'set' iq['id'] = util.rand_str(8) iq['to'] = addr_from allow = iq.addElement((xmlstream2.NS_IQ_BLOCKING, pname)) for item in wl: elem = allow.addElement((None, 'item')) elem['jid'] = item self.parent.send_wrapped(iq, sender)
def message(self, stanza): # generate message id if receipt is requested by client if xmlstream2.extract_receipt(stanza, 'request'): stanza.request['id'] = util.rand_str(30, util.CHARSBOX_AZN_LOWERCASE) # no to address, presume sender bare JID if not stanza.hasAttribute('to'): stanza['to'] = self.xmlstream.otherEntity.userhost() # if message is a received receipt, we can delete the original message # TODO move this to MessageHandler if stanza.getAttribute('type') == 'chat': received = xmlstream2.extract_receipt(stanza, 'received') if stanza.received: # delete the received message # TODO safe delete with sender/recipient self.router.message_offline_delete(received['id'], stanza.name)
def _send(): _jid = self.client.xmlstream.authenticator.jid message = domish.Element((None, 'message')) message['id'] = 'kontalk' + util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) message['type'] = 'chat' if peer: message['to'] = peer else: message['to'] = _jid.userhost() message.addElement((None, 'body'), content=content) if request: message.addElement(('urn:xmpp:server-receipts', 'request')) self.client.send(message) self.stats('messages:outgoing') if request: self.stats('messages:pending') else: self.stats('messages:sent')
def render_POST(self, request): #log.debug("request from %s: %s" % (self.userid, request.requestHeaders)) # check mime type mime = request.getHeader('content-type') if mime not in self.config['upload']['accept_content']: return self._quick_response(request, 406, 'unacceptable content type') # check length length = request.getHeader('content-length') if length != None: length = long(length) if length <= self.config['upload']['max_size']: # store file to storage # TODO convert to file-object management for less memory consumption data = request.content.read() if len(data) == length: fileid = util.rand_str(40) filename = self.fileserver.storage.store_data( fileid, mime, data) if filename: log.debug( "file stored to disk (filename=%s, fileid=%s)" % (filename, fileid)) request.setHeader('content-type', 'text/url') return str(self.config['upload']['url']) % (fileid, ) else: log.error("error storing file") return self._quick_response(request, 500, 'unable to store file') else: log.warn( "file length not matching content-length header (%d/%d)" % (len(data), length)) return self._quick_response(request, 400, 'bad request') else: log.warn("file too big (%d bytes)" % length) return self._quick_response(request, 413, 'request too large') else: log.warn("content-length header not found") return self._quick_response(request, 411, 'content length not declared')
def _send(): _jid = self.client.xmlstream.authenticator.jid message = domish.Element((None, 'message')) message['id'] = 'kontalk' + util.rand_str( 8, util.CHARSBOX_AZN_LOWERCASE) message['type'] = 'chat' if peer: message['to'] = peer else: message['to'] = _jid.userhost() message.addElement((None, 'body'), content=content) if request: message.addElement(('urn:xmpp:server-receipts', 'request')) self.client.send(message) self.stats('messages:outgoing') if request: self.stats('messages:pending') else: self.stats('messages:sent')
def message(self, stanza): # generate message id if receipt is requested by client if xmlstream2.extract_receipt(stanza, 'request'): stanza.request['id'] = util.rand_str(30, util.CHARSBOX_AZN_LOWERCASE) # no to address, presume sender bare JID if not stanza.hasAttribute('to'): stanza['to'] = self.xmlstream.otherEntity.userhost() # if message is a received receipt, we can delete the original message # TODO move this to MessageHandler if stanza.getAttribute('type') == 'chat': received = xmlstream2.extract_receipt(stanza, 'received') if stanza.received: # delete the received message # TODO safe delete with sender/recipient self.router.message_offline_delete(received['id'], stanza.name) self.handle(stanza)
def bind(self, name): stanzaId = util.rand_str(8) # register for response def _bind(stanza, name): if not stanza.hasAttribute('error'): # no errors, register route unused, host = util.jid_component(name) if host not in self.routes: self.routes[host] = [] self.routes[host].append(name) log.debug("ROUTES: %s" % (self.routes, )) self.xmlstream.addOnetimeObserver("/bind[@id='%s']" % (stanzaId, ), _bind, name=name) bind = domish.Element((None, 'bind')) bind['id'] = stanzaId bind['name'] = name self.send(bind)
def broadcast_public_key(self, userid, keydata): """Broadcasts to all remote c2s the given public key.""" # create vcard iq_vcard = domish.Element((None, 'iq')) iq_vcard['type'] = 'set' iq_vcard['from'] = util.userid_to_jid(userid, self.xmlstream.thisEntity.host).full() # add vcard vcard = iq_vcard.addElement((xmlstream2.NS_XMPP_VCARD4, 'vcard')) if keydata: vcard_key = vcard.addElement((None, 'key')) vcard_data = vcard_key.addElement((None, 'uri')) vcard_data.addContent(xmlstream2.DATA_PGP_PREFIX + base64.b64encode(keydata)) # send vcard to remote c2s for server in self.keyring.hostlist(): if server != self.servername: iq_vcard['id'] = util.rand_str(8) # remote server iq_vcard['to'] = util.component_jid(server, util.COMPONENT_C2S) # consume any response (very high priority) self.xmlstream.addOnetimeObserver("/iq[@id='%s']" % iq_vcard['id'], self.consume, 500) # send! self.send_wrapped(iq_vcard, self.xmlstream.thisEntity.full())
def render_POST(self, request): # log.debug("request from %s: %s" % (self.userid, request.requestHeaders)) # check mime type mime = request.getHeader("content-type") if mime not in self.config["upload"]["accept_content"]: return self._quick_response(request, 406, "unacceptable content type") # check length length = request.getHeader("content-length") if length != None: length = long(length) if length <= self.config["upload"]["max_size"]: # store file to storage # TODO convert to file-object management for less memory consumption data = request.content.read() if len(data) == length: fileid = util.rand_str(40) filename = self.fileserver.storage.store_data(fileid, mime, data) if filename: log.debug("file stored to disk (filename=%s, fileid=%s)" % (filename, fileid)) request.setHeader("content-type", "text/url") return str(self.config["upload"]["url"]) % (fileid,) else: log.error("error storing file") return self._quick_response(request, 500, "unable to store file") else: log.warn("file length not matching content-length header (%d/%d)" % (len(data), length)) return self._quick_response(request, 400, "bad request") else: log.warn("file too big (%d bytes)" % length) return self._quick_response(request, 413, "request too large") else: log.warn("content-length header not found") return self._quick_response(request, 411, "content length not declared")
def testProbe(): if self.peer is not None: userid, resource = util.split_userid(self.peer) presence = xmppim.Presence(jid.JID(tuple=(userid, self.network, resource)), 'probe') presence['id'] = util.rand_str(8) xs.send(presence)
def roster(self, stanza): if not xmlstream2.has_element(stanza.query, uri=xmlstream2.NS_IQ_ROSTER, name="item"): # requesting initial roster - enter XMPP compatibility mode self.parent.compatibility_mode = True _items = stanza.query.elements(uri=xmlstream2.NS_IQ_ROSTER, name="item") requester = jid.JID(stanza["from"]) stanza.consumed = True # items present, requesting roster lookup response = xmlstream.toResponse(stanza, "result") roster = response.addElement((xmlstream2.NS_IQ_ROSTER, "query")) probes = [] # this will be true if roster lookup is requested roster_lookup = False for item in _items: # items present, meaning roster lookup roster_lookup = True itemJid = jid.internJID(item["jid"]) # include the entry in the roster reply anyway entry = self.parent.router.cache.lookup(itemJid) if entry: allowed = self.parent.router.is_presence_allowed(requester, itemJid) if allowed != -1: item = roster.addElement((None, "item")) item["jid"] = self.parent.router.translateJID(entry.jid).userhost() if allowed == 1: probes.append(entry.presence()) # roster lookup, send presence data and vcards if roster_lookup: # lookup response self.send(response) # simulate a presence probe and send vcards # we'll use one group ID so the client knows when to stop waiting gid = stanza.getAttribute("id") if not gid: gid = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) i = sum([len(x) for x in probes]) for presence_list in probes: for presence in presence_list: presence = deepcopy(presence) presence["to"] = stanza["from"] group = presence.addElement((xmlstream2.NS_XMPP_STANZA_GROUP, "group")) group["id"] = gid group["count"] = str(i) i -= 1 self.send(presence) # send vcard for this user jid_from = jid.JID(presence_list[0]["from"]) iq = domish.Element((None, "iq")) iq["type"] = "set" iq["from"] = jid_from.userhost() iq["to"] = stanza["from"] try: self.parent.router.build_vcard(jid_from.user, iq) self.send(iq) except keyring.KeyNotFoundException: pass # no roster lookup, XMPP standard roster instead else: # include items from the user's whitelist wl = self.parent.router.get_whitelist(requester) probes = None if wl: subscriptions = [] for e in wl: item = roster.addElement((None, "item")) item["jid"] = e itemJid = jid.JID(e) # check if subscription status is 'both' or just 'from' allowed = self.parent.router.is_presence_allowed(requester, itemJid) if allowed == 1: status = "both" else: status = "from" # TODO include name from PGP key? item["subscription"] = status # add to subscription list subscriptions.append(itemJid) # send the roster self.send(response) # subscribe to all users (without sending subscribed stanza of course) if wl: for itemJid in subscriptions: self.parent.router.subscribe(requester, itemJid, send_subscribed=False)
def send_fake_receipt(self, stanza): """Sends back a fake sent receipt, while silently discard the message.""" msg = xmlstream.toResponse(stanza, stanza["type"]) r = msg.addElement((xmlstream2.NS_XMPP_SERVER_RECEIPTS, "sent")) r["id"] = util.rand_str(30, util.CHARSBOX_AZN_LOWERCASE) self.parent.send(msg)
def send_fake_receipt(self, stanza): """Sends back a fake sent receipt, while silently discard the message.""" msg = xmlstream.toResponse(stanza, stanza['type']) r = msg.addElement((xmlstream2.NS_XMPP_SERVER_RECEIPTS, 'sent')) r['id'] = util.rand_str(30, util.CHARSBOX_AZN_LOWERCASE) self.parent.send(msg)
def roster(self, stanza): if not xmlstream2.has_element( stanza.query, uri=xmlstream2.NS_IQ_ROSTER, name='item'): # requesting initial roster - enter XMPP compatibility mode self.parent.compatibility_mode = True _items = stanza.query.elements(uri=xmlstream2.NS_IQ_ROSTER, name='item') requester = jid.JID(stanza['from']) stanza.consumed = True # items present, requesting roster lookup response = xmlstream.toResponse(stanza, 'result') roster = response.addElement((xmlstream2.NS_IQ_ROSTER, 'query')) probes = [] # this will be true if roster lookup is requested roster_lookup = False for item in _items: # items present, meaning roster lookup roster_lookup = True itemJid = jid.internJID(item['jid']) # include the entry in the roster reply anyway entry = self.parent.router.cache.lookup(itemJid) if entry: allowed = self.parent.router.is_presence_allowed( requester, itemJid) if allowed != -1: item = roster.addElement((None, 'item')) item['jid'] = self.parent.router.translateJID( entry.jid).userhost() if allowed == 1: probes.append(entry.presence()) # roster lookup, send presence data and vcards if roster_lookup: # lookup response self.send(response) # simulate a presence probe and send vcards # we'll use one group ID so the client knows when to stop waiting gid = stanza.getAttribute('id') if not gid: gid = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) i = sum([len(x) for x in probes]) for presence_list in probes: for presence in presence_list: presence = deepcopy(presence) presence['to'] = stanza['from'] group = presence.addElement( (xmlstream2.NS_XMPP_STANZA_GROUP, 'group')) group['id'] = gid group['count'] = str(i) i -= 1 self.send(presence) # send vcard for this user jid_from = jid.JID(presence_list[0]['from']) iq = domish.Element((None, 'iq')) iq['type'] = 'set' iq['from'] = jid_from.userhost() iq['to'] = stanza['from'] try: self.parent.router.build_vcard(jid_from.user, iq) self.send(iq) except keyring.KeyNotFoundException: pass # no roster lookup, XMPP standard roster instead else: # include items from the user's whitelist wl = self.parent.router.get_whitelist(requester) probes = None if wl: subscriptions = [] for e in wl: item = roster.addElement((None, 'item')) item['jid'] = e itemJid = jid.JID(e) # check if subscription status is 'both' or just 'from' allowed = self.parent.router.is_presence_allowed( requester, itemJid) if allowed == 1: status = 'both' else: status = 'from' # TODO include name from PGP key? item['subscription'] = status # add to subscription list subscriptions.append(itemJid) # send the roster self.send(response) # subscribe to all users (without sending subscribed stanza of course) if wl: for itemJid in subscriptions: self.parent.router.subscribe(requester, itemJid, send_subscribed=False)
def process_message(self, stanza, hold=False): if stanza.hasAttribute('to'): to = jid.JID(stanza['to']) # process only our JIDs if util.jid_local(util.COMPONENT_C2S, self, to): chat_msg = (stanza.getAttribute('type') == 'chat') if to.user is not None: keepId = None receipt = xmlstream2.extract_receipt(stanza, 'request') received = xmlstream2.extract_receipt(stanza, 'received') has_storage = xmlstream2.has_element(stanza, xmlstream2.NS_XMPP_STORAGE, 'storage') try: """ We are deliberately ignoring messages with sent receipt because they are supposed to be volatile. """ if chat_msg and not has_storage and (receipt or received): """ Apply generated id if we are getting a received receipt. This way stanza is received by the client with the correct id to cancel preemptive storage. """ if received: keepId = stanza['id'] = util.rand_str(30, util.CHARSBOX_AZN_LOWERCASE) # send message to offline storage just to be safe (delayed) keepId = self.message_offline_store(stanza, delayed=True, reuseId=keepId) if hold: raise Exception() # send message to sm only to non-negative resources log.debug("sending message %s" % (stanza['id'], )) self.sfactory.dispatch(stanza) except: # manager not found or holding -- send to offline storage if hold: log.debug("holding stanza for %s" % (stanza['to'], )) else: log.debug("c2s manager for %s not found" % (stanza['to'], )) """ Since our previous call to message_offline_store() was with delayed parameter, we need to store for real now. Do not store messages from local storage because """ if chat_msg and not has_storage and (stanza.body or stanza.e2e or received): self.message_offline_store(stanza, delayed=False, reuseId=keepId) if self.push_manager and chat_msg and (stanza.body or stanza.e2e) and (not receipt or receipt.name == 'request'): self.push_manager.notify(to) # if message is a received receipt, we can delete the original message if chat_msg and received: # delete the received message # TODO safe delete with sender/recipient self.message_offline_delete(received['id'], stanza.name) stamp = time.time() """ Receipts will be sent only if message is not coming from storage or message is from a remote server. This is because if the message is coming from storage, it means that it's a user collecting its offline messages, so we don't need to send a <sent/> again. If a message is coming from a remote server, it means that is being delivered by a remote c2s by either: * sm request (direct message from client) * offline delivery (triggered by an initial presence from this server) """ host = util.jid_host(stanza['from']) from_storage = xmlstream2.has_element(stanza, xmlstream2.NS_XMPP_STORAGE, 'storage') try: log.debug("host(unparsed): %s" % (host, )) unused, host = util.jid_component(host, util.COMPONENT_C2S) log.debug("host(parsed): %s" % (host, )) from_remote = host != self.servername except: from_remote = False if chat_msg and (not from_storage or from_remote): # send ack only for chat messages (if requested) # do not send if coming from remote storage if receipt and not from_storage: self.send_ack(stanza, 'sent', stamp) # send receipt to originating server, if requested receipt = None # receipt request: send <sent/> if stanza.request: receipt = stanza.request request = 'request' delivery = 'sent' # received receipt: send <ack/> elif stanza.received: receipt = stanza.received request = 'received' delivery = 'ack' # now send what we prepared if receipt: try: from_server = receipt['from'] if not util.hostjid_local(util.COMPONENT_C2S, self, from_server): stanza['from'] = from_server self.send_ack(stanza, delivery, stamp, request) except KeyError: pass else: # deliver local stanza self.local(stanza) """ If message is a receipt coming from a remote server, delete the message from our storage. """ r_sent = xmlstream2.extract_receipt(stanza, 'sent') if chat_msg and r_sent: sender_host = util.jid_host(stanza['from']) """ We are receiving a sent receipt from another server, meaning that the server has now responsibility for the message - we can delete it now. Special case is the sender domain being the network domain, meaning the resolver rejected the message. """ unused, sender_host = util.jid_component(sender_host) if sender_host != self.servername: log.debug("remote server now has responsibility for message %s - deleting" % (r_sent['id'], )) # TODO safe delete with sender/recipient self.message_offline_delete(r_sent['id'], stanza.name) else: log.debug("stanza is not our concern or is an error")
def _db(presence, stanza): log.debug("presence: %r" % (presence, )) if type(presence) == list and len(presence) > 0: chain = domish.Element( (xmlstream2.NS_XMPP_STANZA_GROUP, 'group')) chain['id'] = stanza['id'] chain['count'] = str(len(presence)) for user in presence: response = xmlstream.toResponse(stanza) response['id'] = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) response_from = util.userid_to_jid( user['userid'], self.xmlstream.thisEntity.host) response['from'] = response_from.full() if user['status'] is not None: response.addElement((None, 'status'), content=user['status']) if user['show'] is not None: response.addElement((None, 'show'), content=user['show']) if not self.parent.sfactory.client_connected( response_from): response['type'] = 'unavailable' delay = domish.Element(('urn:xmpp:delay', 'delay')) delay['stamp'] = user['timestamp'].strftime( xmlstream2.XMPP_STAMP_FORMAT) response.addChild(delay) response.addChild(chain) self.send(response) if self.parent.logTraffic: log.debug("probe result sent: %s" % (response.toXml().encode('utf-8'), )) else: log.debug("probe result sent: %s" % (response['from'], )) elif presence is not None and type(presence) != list: chain = domish.Element( (xmlstream2.NS_XMPP_STANZA_GROUP, 'group')) chain['id'] = stanza['id'] chain['count'] = '1' response = xmlstream.toResponse(stanza) if presence['status'] is not None: response.addElement((None, 'status'), content=presence['status']) if presence['show'] is not None: response.addElement((None, 'show'), content=presence['show']) response_from = util.userid_to_jid(presence['userid'], self.parent.servername) if not self.parent.sfactory.client_connected(response_from): response['type'] = 'unavailable' delay = domish.Element(('urn:xmpp:delay', 'delay')) delay['stamp'] = presence['timestamp'].strftime( xmlstream2.XMPP_STAMP_FORMAT) response.addChild(delay) response.addChild(chain) self.send(response) if self.parent.logTraffic: log.debug("probe result sent: %s" % (response.toXml().encode('utf-8'), )) else: log.debug("probe result sent: %s" % (response['from'], )) else: log.debug("probe: user not found") # TODO return error? response = xmlstream.toResponse(stanza, 'error') chain = domish.Element( (xmlstream2.NS_XMPP_STANZA_GROUP, 'group')) chain['id'] = stanza['id'] chain['count'] = '1' response.addChild(chain) self.send(response)
def local_vcard(self, user, stanza): """ Called by SM when receiving a vCard from a local client. It checks vcard info (including public key) and if positive, sends the vCard to all remote c2s. @return: iq result or error stanza for the client """ if self.logTraffic: log.debug("client sent vcard: %s" % (stanza.toXml(), )) else: log.debug("client sent vcard: %s" % (stanza['to'], )) stanza.consumed = True stanza_to = stanza.getAttribute('to') if stanza_to: entity = jid.JID(stanza_to) else: entity = None # setting our own vcard if not entity or (entity.userhost() == user.userhost()): # TODO parse vcard for interesting sections if stanza.vcard.key is not None: # we do this because of the uri member in domish.Element keydata = stanza.vcard.key.firstChildElement() if keydata and keydata.name == 'uri': keydata = str(keydata) if keydata.startswith(xmlstream2.DATA_PGP_PREFIX): try: keydata = base64.b64decode(keydata[len(xmlstream2.DATA_PGP_PREFIX):]) except: log.debug("invalid base64 data") e = xmlstream.error.StanzaError('bad-request', text='Invalid public key.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq # check key fp = self.keyring.check_user_key(keydata, user.user) if fp: # generate response beforing tampering with the stanza response = xmlstream.toResponse(stanza, 'result') # update presencedb self.presencedb.public_key(user.user, fp) # send vcard to all remote c2s for server in self.keyring.hostlist(): if server != self.servername: stanza['id'] = util.rand_str(8) stanza['to'] = util.component_jid(server, util.COMPONENT_C2S) # consume any response (very high priority) self.xmlstream.addOnetimeObserver("/iq[@id='%s']" % stanza['id'], self.consume, 500) # wrap stanza in an envelope because we want errors to return to us self.send_wrapped(stanza, self.xmlstream.thisEntity.full()) # send response return response else: log.debug("invalid key - authorization to vCard denied") e = xmlstream.error.StanzaError('bad-request', text='Invalid public key.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq else: log.debug("authorization to vCard denied") e = xmlstream.error.StanzaError('not-allowed', text='Not authorized.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq
def send(self, stanza, force=False): """Send stanza to client, setting to and id attributes if not present.""" if stanza.hasAttribute('original-to'): origTo = stanza.getAttribute('original-to') del stanza['original-to'] """ Extract original recipient from stanza. If original-to is not present, we will assume that stanza was intended to the full JID. """ origTo = jid.JID(origTo) if self.router.logTraffic: log.debug("sending message to client %s (original was %s)" % (self.xmlstream.otherEntity, origTo)) if self._presence: log.debug("_presence: %s" % (self._presence.toXml(), )) # sending to bare JID # initial presence found # negative resource # => DROP STANZA try: if not origTo.resource and int(str(self._presence.priority)) < 0: return None except: pass # FIXME using deepcopy is not safe from copy import deepcopy stanza = deepcopy(stanza) util.resetNamespace(stanza, component.NS_COMPONENT_ACCEPT, self.namespace) # translate sender to network JID sender = stanza.getAttribute('from') if sender and sender != self.network: sender = jid.JID(stanza['from']) sender.host = self.network stanza['from'] = sender.full() # TODO should we force self.network if no sender? # remove reserved elements if stanza.name in ('presence', 'message'): # storage child if stanza.storage and stanza.storage.uri == xmlstream2.NS_XMPP_STORAGE: stanza.children.remove(stanza.storage) # origin in receipt if stanza.request and stanza.request.hasAttribute('from'): del stanza.request['from'] elif stanza.received and stanza.received.hasAttribute('from'): del stanza.received['from'] if stanza.name == 'presence': # push device id for c in stanza.elements(name='c', uri=xmlstream2.NS_PRESENCE_PUSH): stanza.children.remove(c) break # force destination address if self.xmlstream.otherEntity: stanza['to'] = self.xmlstream.otherEntity.full() if not stanza.hasAttribute('id'): stanza['id'] = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) xmlstream2.StreamManager.send(self, stanza, force)
def local_vcard(self, user, stanza): """ Called by SM when receiving a vCard from a local client. It checks vcard info (including public key) and if positive, sends the vCard to all remote c2s. @return: iq result or error stanza for the client """ if self.logTraffic: log.debug("client sent vcard: %s" % (stanza.toXml(), )) else: log.debug("client sent vcard: %s" % (stanza['to'], )) stanza.consumed = True stanza_to = stanza.getAttribute('to') if stanza_to: entity = jid.JID(stanza_to) else: entity = None # setting our own vcard if not entity or (entity.userhost() == user.userhost()): # TODO parse vcard for interesting sections if stanza.vcard.key is not None: # we do this because of the uri member in domish.Element keydata = stanza.vcard.key.firstChildElement() if keydata and keydata.name == 'uri': keydata = str(keydata) if keydata.startswith(xmlstream2.DATA_PGP_PREFIX): try: keydata = base64.b64decode( keydata[len(xmlstream2.DATA_PGP_PREFIX):]) except: log.debug("invalid base64 data") e = xmlstream.error.StanzaError( 'bad-request', text='Invalid public key.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq # check key fp = self.keyring.check_user_key(keydata, user.user) if fp: # generate response beforing tampering with the stanza response = xmlstream.toResponse(stanza, 'result') # update presencedb self.presencedb.public_key(user.user, fp) # send vcard to all remote c2s for server in self.keyring.hostlist(): if server != self.servername: stanza['id'] = util.rand_str(8) stanza['to'] = util.component_jid( server, util.COMPONENT_C2S) # consume any response (very high priority) self.xmlstream.addOnetimeObserver( "/iq[@id='%s']" % stanza['id'], self.consume, 500) # wrap stanza in an envelope because we want errors to return to us self.send_wrapped( stanza, self.xmlstream.thisEntity.full()) # send response return response else: log.debug( "invalid key - authorization to vCard denied") e = xmlstream.error.StanzaError( 'bad-request', text='Invalid public key.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq else: log.debug("authorization to vCard denied") e = xmlstream.error.StanzaError('not-allowed', text='Not authorized.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq
def process_message(self, stanza, hold=False): if stanza.hasAttribute('to'): to = jid.JID(stanza['to']) # process only our JIDs if util.jid_local(util.COMPONENT_C2S, self, to): chat_msg = (stanza.getAttribute('type') == 'chat') if to.user is not None: keepId = None receipt = xmlstream2.extract_receipt(stanza, 'request') received = xmlstream2.extract_receipt(stanza, 'received') has_storage = xmlstream2.has_element( stanza, xmlstream2.NS_XMPP_STORAGE, 'storage') try: """ We are deliberately ignoring messages with sent receipt because they are supposed to be volatile. """ if chat_msg and not has_storage and (receipt or received): """ Apply generated id if we are getting a received receipt. This way stanza is received by the client with the correct id to cancel preemptive storage. """ if received: keepId = stanza['id'] = util.rand_str( 30, util.CHARSBOX_AZN_LOWERCASE) # send message to offline storage just to be safe (delayed) keepId = self.message_offline_store(stanza, delayed=True, reuseId=keepId) if hold: raise Exception() # send message to sm only to non-negative resources log.debug("sending message %s" % (stanza['id'], )) self.sfactory.dispatch(stanza) except: # manager not found or holding -- send to offline storage if hold: log.debug("holding stanza for %s" % (stanza['to'], )) else: log.debug("c2s manager for %s not found" % (stanza['to'], )) """ Since our previous call to message_offline_store() was with delayed parameter, we need to store for real now. Do not store messages from local storage because """ if chat_msg and not has_storage and (stanza.body or stanza.e2e or received): self.message_offline_store(stanza, delayed=False, reuseId=keepId) if self.push_manager and chat_msg and ( stanza.body or stanza.e2e) and ( not receipt or receipt.name == 'request'): self.push_manager.notify(to) # if message is a received receipt, we can delete the original message if chat_msg and received: # delete the received message # TODO safe delete with sender/recipient self.message_offline_delete(received['id'], stanza.name) stamp = time.time() """ Receipts will be sent only if message is not coming from storage or message is from a remote server. This is because if the message is coming from storage, it means that it's a user collecting its offline messages, so we don't need to send a <sent/> again. If a message is coming from a remote server, it means that is being delivered by a remote c2s by either: * sm request (direct message from client) * offline delivery (triggered by an initial presence from this server) """ host = util.jid_host(stanza['from']) from_storage = xmlstream2.has_element( stanza, xmlstream2.NS_XMPP_STORAGE, 'storage') try: log.debug("host(unparsed): %s" % (host, )) unused, host = util.jid_component( host, util.COMPONENT_C2S) log.debug("host(parsed): %s" % (host, )) from_remote = host != self.servername except: from_remote = False if chat_msg and (not from_storage or from_remote): # send ack only for chat messages (if requested) # do not send if coming from remote storage if receipt and not from_storage: self.send_ack(stanza, 'sent', stamp) # send receipt to originating server, if requested receipt = None # receipt request: send <sent/> if stanza.request: receipt = stanza.request request = 'request' delivery = 'sent' # received receipt: send <ack/> elif stanza.received: receipt = stanza.received request = 'received' delivery = 'ack' # now send what we prepared if receipt: try: from_server = receipt['from'] if not util.hostjid_local( util.COMPONENT_C2S, self, from_server): stanza['from'] = from_server self.send_ack(stanza, delivery, stamp, request) except KeyError: pass else: # deliver local stanza self.local(stanza) """ If message is a receipt coming from a remote server, delete the message from our storage. """ r_sent = xmlstream2.extract_receipt(stanza, 'sent') if chat_msg and r_sent: sender_host = util.jid_host(stanza['from']) """ We are receiving a sent receipt from another server, meaning that the server has now responsibility for the message - we can delete it now. Special case is the sender domain being the network domain, meaning the resolver rejected the message. """ unused, sender_host = util.jid_component(sender_host) if sender_host != self.servername: log.debug( "remote server now has responsibility for message %s - deleting" % (r_sent['id'], )) # TODO safe delete with sender/recipient self.message_offline_delete(r_sent['id'], stanza.name) else: log.debug("stanza is not our concern or is an error")
def _db(presence, stanza): log.debug("presence: %r" % (presence, )) if type(presence) == list and len(presence) > 0: chain = domish.Element((xmlstream2.NS_XMPP_STANZA_GROUP, 'group')) chain['id'] = stanza['id'] chain['count'] = str(len(presence)) for user in presence: response = xmlstream.toResponse(stanza) response['id'] = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) response_from = util.userid_to_jid(user['userid'], self.xmlstream.thisEntity.host) response['from'] = response_from.full() if user['status'] is not None: response.addElement((None, 'status'), content=user['status']) if user['show'] is not None: response.addElement((None, 'show'), content=user['show']) if not self.parent.sfactory.client_connected(response_from): response['type'] = 'unavailable' delay = domish.Element(('urn:xmpp:delay', 'delay')) delay['stamp'] = user['timestamp'].strftime(xmlstream2.XMPP_STAMP_FORMAT) response.addChild(delay) response.addChild(chain) self.send(response) if self.parent.logTraffic: log.debug("probe result sent: %s" % (response.toXml().encode('utf-8'), )) else: log.debug("probe result sent: %s" % (response['from'], )) elif presence is not None and type(presence) != list: chain = domish.Element((xmlstream2.NS_XMPP_STANZA_GROUP, 'group')) chain['id'] = stanza['id'] chain['count'] = '1' response = xmlstream.toResponse(stanza) if presence['status'] is not None: response.addElement((None, 'status'), content=presence['status']) if presence['show'] is not None: response.addElement((None, 'show'), content=presence['show']) response_from = util.userid_to_jid(presence['userid'], self.parent.servername) if not self.parent.sfactory.client_connected(response_from): response['type'] = 'unavailable' delay = domish.Element(('urn:xmpp:delay', 'delay')) delay['stamp'] = presence['timestamp'].strftime(xmlstream2.XMPP_STAMP_FORMAT) response.addChild(delay) response.addChild(chain) self.send(response) if self.parent.logTraffic: log.debug("probe result sent: %s" % (response.toXml().encode('utf-8'), )) else: log.debug("probe result sent: %s" % (response['from'], )) else: log.debug("probe: user not found") # TODO return error? response = xmlstream.toResponse(stanza, 'error') chain = domish.Element((xmlstream2.NS_XMPP_STANZA_GROUP, 'group')) chain['id'] = stanza['id'] chain['count'] = '1' response.addChild(chain) self.send(response)
def send(self, stanza, force=False): """Send stanza to client, setting to and id attributes if not present.""" if stanza.hasAttribute('original-to'): origTo = stanza.getAttribute('original-to') del stanza['original-to'] """ Extract original recipient from stanza. If original-to is not present, we will assume that stanza was intended to the full JID. """ origTo = jid.JID(origTo) if self.router.logTraffic: log.debug("sending message to client %s (original was %s)" % (self.xmlstream.otherEntity, origTo)) if self._presence: log.debug("_presence: %s" % (self._presence.toXml().encode('utf-8'), )) # sending to bare JID # initial presence found # negative resource # => DROP STANZA try: if not origTo.resource and int(str( self._presence.priority)) < 0: return None except: pass # FIXME using deepcopy is not safe from copy import deepcopy stanza = deepcopy(stanza) util.resetNamespace(stanza, component.NS_COMPONENT_ACCEPT, self.namespace) # translate sender to network JID sender = stanza.getAttribute('from') if sender and sender != self.network: sender = jid.JID(stanza['from']) sender.host = self.network stanza['from'] = sender.full() # TODO should we force self.network if no sender? # remove reserved elements if stanza.direct and stanza.direct.uri == xmlstream2.NS_XMPP_DIRECT: stanza.children.remove(stanza.direct) if stanza.name in ('presence', 'message'): # storage child if stanza.storage and stanza.storage.uri == xmlstream2.NS_XMPP_STORAGE: stanza.children.remove(stanza.storage) # origin in receipt if stanza.request and stanza.request.hasAttribute('from'): del stanza.request['from'] elif stanza.received and stanza.received.hasAttribute('from'): del stanza.received['from'] if stanza.name == 'presence': # push device id for c in stanza.elements(name='c', uri=xmlstream2.NS_PRESENCE_PUSH): stanza.children.remove(c) break # force destination address if self.xmlstream.otherEntity: stanza['to'] = self.xmlstream.otherEntity.full() if not stanza.hasAttribute('id'): stanza['id'] = util.rand_str(8, util.CHARSBOX_AZN_LOWERCASE) xmlstream2.StreamManager.send(self, stanza, force)