def output(data, user): log.debug("data: %r" % (data, )) to = user.full() for msg in data: log.debug("msg[%s]=%s" % (msg['id'], msg['stanza'].toXml().encode('utf-8'), )) try: """ Mark the stanza with our server name, so we'll receive a copy of the receipt """ if msg['stanza'].request: msg['stanza'].request['from'] = self.xmlstream.thisEntity.full() elif msg['stanza'].received: msg['stanza'].received['from'] = self.xmlstream.thisEntity.full() # mark delayed delivery if 'timestamp' in msg: delay = msg['stanza'].addElement((xmlstream2.NS_XMPP_DELAY, 'delay')) delay['stamp'] = msg['timestamp'].strftime(xmlstream2.XMPP_STAMP_FORMAT) msg['to'] = to self.send(msg['stanza']) """ If a receipt is requested, we won't delete the message from storage now; we must be sure client has received it. Otherwise just delete the message immediately. """ if not xmlstream2.extract_receipt(msg['stanza'], 'request') and \ not xmlstream2.extract_receipt(stanza, 'received'): self.parent.message_offline_delete(msg['id'], msg['stanza'].name) except: log.debug("offline message delivery failed (%s)" % (msg['id'], )) traceback.print_exc()
def _local_presence_output(self, data, user): log.debug("data: %r" % (data, )) # this will be used to set a safe recipient # WARNING this will create a JID anyway :( jid_to = self.resolveJID(user) for msg in data: log.debug("msg[%s]=%s" % (msg['id'], msg['stanza'].toXml().encode('utf-8'), )) stanza = msg['stanza'] try: """ Mark the stanza with our server name, so we'll receive a copy of the receipt """ if stanza.request: stanza.request['from'] = self.xmlstream.thisEntity.full() elif stanza.received: stanza.received['from'] = self.xmlstream.thisEntity.full() # mark delayed delivery if 'timestamp' in msg: delay = stanza.addElement((xmlstream2.NS_XMPP_DELAY, 'delay')) delay['stamp'] = msg['timestamp'].strftime(xmlstream2.XMPP_STAMP_FORMAT) # are we sending a message to a user we have blocked? jid_from = jid.JID(stanza['from']) if self.is_presence_allowed(jid_to, jid_from) == -1: log.debug("sending message to blocked user, bouncing error") e = error.StanzaError('not-acceptable', 'cancel') errstanza = e.toResponse(msg['stanza']) errstanza.error.addElement((xmlstream2.NS_IQ_BLOCKING_ERRORS, 'blocked')) self.send(errstanza) else: # check for permission allowed = self.is_presence_allowed(jid_from, jid_to) if allowed == -1: # user is blocked! log.debug("not allowed to send messages to %s, discarding message" % (stanza['to'], )) self.message_offline_delete(msg['id'], stanza.name) else: """ We use direct delivery here: it's faster and does not involve JID resolution """ stanza['to'] = jid_to.full() self.dispatch(stanza, hold=(allowed != 1)) """ If a receipt is requested, we won't delete the message from storage now; we must be sure client has received it. Otherwise just delete the message immediately. """ if not xmlstream2.extract_receipt(stanza, 'request') and \ not xmlstream2.extract_receipt(stanza, 'received'): self.message_offline_delete(msg['id'], stanza.name) except: log.debug("offline message delivery failed (%s)" % (msg['id'], )) traceback.print_exc()
def message(self, stanza): if not stanza.consumed: jid_from = self.parent.resolveJID(self.xmlstream.otherEntity) stanza["from"] = jid_from.full() # no destination - use sender bare JID if not stanza.hasAttribute("to"): jid_to = jid_from stanza["to"] = jid_to.userhost() else: jid_to = jid.JID(stanza["to"]) # are we sending a message to a user we have blocked? if self.parent.router.is_presence_allowed(jid_to, jid_from) == -1: log.debug("sending message to blocked user, bouncing error") e = error.StanzaError("not-acceptable", "cancel") errstanza = e.toResponse(stanza) errstanza.error.addElement((xmlstream2.NS_IQ_BLOCKING_ERRORS, "blocked")) self.parent.send(errstanza) else: # check for permission allowed = self.parent.router.is_presence_allowed(jid_from, jid_to) if allowed == -1: # user is blocked! log.debug("not allowed to send messages, sending fake response to %s" % (stanza["from"],)) if stanza.getAttribute("type") == "chat" and xmlstream2.extract_receipt(stanza, "request"): self.send_fake_receipt(stanza) else: # send to c2s hub (without implicitly consuming) self.parent.router.send(stanza, force_delivery=True, hold=(allowed != 1)) # we have now consumed the stanza stanza.consumed = True
def send_ack(self, stanza, status, stamp=None, receipt='request'): request = xmlstream2.extract_receipt(stanza, receipt) ack = xmlstream.toResponse(stanza, stanza.getAttribute('type')) rec = ack.addElement((xmlstream2.NS_XMPP_SERVER_RECEIPTS, status)) rec['id'] = request['id'] if stamp: rec['stamp'] = time.strftime(xmlstream2.XMPP_STAMP_FORMAT, time.gmtime(stamp)) self.send(ack)
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 output(data, user): log.debug("data: %r" % (data, )) to = user.full() for msg in data: log.debug("msg[%s]=%s" % ( msg['id'], msg['stanza'].toXml().encode('utf-8'), )) try: """ Mark the stanza with our server name, so we'll receive a copy of the receipt """ if msg['stanza'].request: msg['stanza'].request[ 'from'] = self.xmlstream.thisEntity.full() elif msg['stanza'].received: msg['stanza'].received[ 'from'] = self.xmlstream.thisEntity.full() # mark delayed delivery if 'timestamp' in msg: delay = msg['stanza'].addElement( (xmlstream2.NS_XMPP_DELAY, 'delay')) delay['stamp'] = msg['timestamp'].strftime( xmlstream2.XMPP_STAMP_FORMAT) msg['to'] = to self.send(msg['stanza']) """ If a receipt is requested, we won't delete the message from storage now; we must be sure client has received it. Otherwise just delete the message immediately. """ if not xmlstream2.extract_receipt(msg['stanza'], 'request') and \ not xmlstream2.extract_receipt(stanza, 'received'): self.parent.message_offline_delete( msg['id'], msg['stanza'].name) except: log.debug("offline message delivery failed (%s)" % (msg['id'], )) traceback.print_exc()
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 message(self, stanza): if not stanza.consumed: jid_from = self.parent.resolveJID(self.xmlstream.otherEntity) stanza['from'] = jid_from.full() # no destination - use sender bare JID if not stanza.hasAttribute('to'): jid_to = jid_from stanza['to'] = jid_to.userhost() else: jid_to = jid.JID(stanza['to']) # are we sending a message to a user we have blocked? if self.parent.router.is_presence_allowed(jid_to, jid_from) == -1: log.debug("sending message to blocked user, bouncing error") e = error.StanzaError('not-acceptable', 'cancel') errstanza = e.toResponse(stanza) errstanza.error.addElement( (xmlstream2.NS_IQ_BLOCKING_ERRORS, 'blocked')) self.parent.send(errstanza) else: # check for permission allowed = self.parent.router.is_presence_allowed( jid_from, jid_to) if allowed == -1: # user is blocked! log.debug( "not allowed to send messages, sending fake response to %s" % (stanza['from'], )) if stanza.getAttribute( 'type') == 'chat' and xmlstream2.extract_receipt( stanza, 'request'): self.send_fake_receipt(stanza) else: # send to c2s hub (without implicitly consuming) self.parent.router.send(stanza, force_delivery=True, hold=(allowed != 1)) # we have now consumed the stanza stanza.consumed = True
def _local_presence_output(self, data, user): log.debug("data: %r" % (data, )) # this will be used to set a safe recipient # WARNING this will create a JID anyway :( jid_to = self.resolveJID(user) for msg in data: log.debug("msg[%s]=%s" % ( msg['id'], msg['stanza'].toXml().encode('utf-8'), )) stanza = msg['stanza'] try: """ Mark the stanza with our server name, so we'll receive a copy of the receipt """ if stanza.request: stanza.request['from'] = self.xmlstream.thisEntity.full() elif stanza.received: stanza.received['from'] = self.xmlstream.thisEntity.full() # mark delayed delivery if 'timestamp' in msg: delay = stanza.addElement( (xmlstream2.NS_XMPP_DELAY, 'delay')) delay['stamp'] = msg['timestamp'].strftime( xmlstream2.XMPP_STAMP_FORMAT) # are we sending a message to a user we have blocked? jid_from = jid.JID(stanza['from']) if self.is_presence_allowed(jid_to, jid_from) == -1: log.debug( "sending message to blocked user, bouncing error") e = error.StanzaError('not-acceptable', 'cancel') errstanza = e.toResponse(msg['stanza']) errstanza.error.addElement( (xmlstream2.NS_IQ_BLOCKING_ERRORS, 'blocked')) self.send(errstanza) else: # check for permission allowed = self.is_presence_allowed(jid_from, jid_to) if allowed == -1: # user is blocked! log.debug( "not allowed to send messages to %s, discarding message" % (stanza['to'], )) self.message_offline_delete(msg['id'], stanza.name) else: """ We use direct delivery here: it's faster and does not involve JID resolution """ stanza['to'] = jid_to.full() self.dispatch(stanza, hold=(allowed != 1)) """ If a receipt is requested, we won't delete the message from storage now; we must be sure client has received it. Otherwise just delete the message immediately. """ if not xmlstream2.extract_receipt(stanza, 'request') and \ not xmlstream2.extract_receipt(stanza, 'received'): self.message_offline_delete(msg['id'], stanza.name) except: log.debug("offline message delivery failed (%s)" % (msg['id'], )) traceback.print_exc()
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 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")