Mercurial > libervia-backend
diff sat/core/xmpp.py @ 2624:56f94936df1e
code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 27 Jun 2018 20:14:46 +0200 |
parents | 93d64ce7a429 |
children | 189e38fb11ff |
line wrap: on
line diff
--- a/sat/core/xmpp.py Wed Jun 27 07:51:29 2018 +0200 +++ b/sat/core/xmpp.py Wed Jun 27 20:14:46 2018 +0200 @@ -31,6 +31,7 @@ from wokkel import component from wokkel import delay from sat.core.log import getLogger + log = getLogger(__name__) from sat.core import exceptions from zope.interface import implements @@ -53,10 +54,10 @@ self.profile = profile self.host_app = host_app self.cache = cache.Cache(host_app, profile) - self._mess_id_uid = {} # map from message id to uid used in history. Key: (full_jid,message_id) Value: uid + self._mess_id_uid = {} # map from message id to uid used in history. Key: (full_jid,message_id) Value: uid self.conn_deferred = defer.Deferred() self._progress_cb = {} # callback called when a progress is requested (key = progress id) - self.actions = {} # used to keep track of actions for retrieval (key = action_id) + self.actions = {} # used to keep track of actions for retrieval (key = action_id) ## initialisation ## @@ -117,24 +118,37 @@ # but client should not be deleted except if session is finished (independently of connection/deconnection # try: - port = int(host.memory.getParamA(C.FORCE_PORT_PARAM, "Connection", profile_key=profile)) + port = int( + host.memory.getParamA( + C.FORCE_PORT_PARAM, "Connection", profile_key=profile + ) + ) except ValueError: log.debug(_("Can't parse port value, using default value")) - port = None # will use default value 5222 or be retrieved from a DNS SRV record + port = ( + None + ) # will use default value 5222 or be retrieved from a DNS SRV record - password = yield host.memory.asyncGetParamA("Password", "Connection", profile_key=profile) - entity = host.profiles[profile] = cls(host, profile, + password = yield host.memory.asyncGetParamA( + "Password", "Connection", profile_key=profile + ) + entity = host.profiles[profile] = cls( + host, + profile, jid.JID(host.memory.getParamA("JabberID", "Connection", profile_key=profile)), - password, host.memory.getParamA(C.FORCE_SERVER_PARAM, "Connection", profile_key=profile) or None, - port, max_retries) + password, + host.memory.getParamA(C.FORCE_SERVER_PARAM, "Connection", profile_key=profile) + or None, + port, + max_retries, + ) entity._createSubProtocols() entity.fallBack = SatFallbackHandler(host) entity.fallBack.setHandlerParent(entity) - entity.versionHandler = SatVersionHandler(C.APP_NAME_FULL, - host.full_version) + entity.versionHandler = SatVersionHandler(C.APP_NAME_FULL, host.full_version) entity.versionHandler.setHandlerParent(entity) entity.identityHandler = SatIdentityHandler() @@ -162,10 +176,17 @@ log.error(_(u"Plugins initialisation error")) for idx, (success, result) in enumerate(results): if not success: - log.error(u"error (plugin %(name)s): %(failure)s" % - {'name': plugin_conn_cb[idx][0]._info['import_name'], 'failure': result}) + log.error( + u"error (plugin %(name)s): %(failure)s" + % { + "name": plugin_conn_cb[idx][0]._info["import_name"], + "failure": result, + } + ) - yield list_d.addCallback(logPluginResults) # FIXME: we should have a timeout here, and a way to know if a plugin freeze + yield list_d.addCallback( + logPluginResults + ) # FIXME: we should have a timeout here, and a way to know if a plugin freeze # TODO: mesure launch time of each plugin def getConnectionDeferred(self): @@ -190,9 +211,13 @@ self._connected.addCallback(self._disconnectionCb) self._connected.addErrback(self._disconnectionEb) - log.info(_(u"********** [{profile}] CONNECTED **********").format(profile=self.profile)) + log.info( + _(u"********** [{profile}] CONNECTED **********").format(profile=self.profile) + ) self.streamInitialized() - self.host_app.bridge.connected(self.profile, unicode(self.jid)) # we send the signal to the clients + self.host_app.bridge.connected( + self.profile, unicode(self.jid) + ) # we send the signal to the clients def _finish_connection(self, dummy): self.conn_deferred.callback(None) @@ -200,7 +225,9 @@ def streamInitialized(self): """Called after _authd""" log.debug(_(u"XML stream is initialized")) - self.keep_alife = task.LoopingCall(self.xmlstream.send, " ") # Needed to avoid disconnection (specially with openfire) + self.keep_alife = task.LoopingCall( + self.xmlstream.send, " " + ) # Needed to avoid disconnection (specially with openfire) self.keep_alife.start(C.XMPP_KEEP_ALIFE) self.disco = SatDiscoProtocol(self) @@ -215,7 +242,12 @@ disco_d.addCallback(self._finish_connection) def initializationFailed(self, reason): - log.error(_(u"ERROR: XMPP connection failed for profile '%(profile)s': %(reason)s" % {'profile': self.profile, 'reason': reason})) + log.error( + _( + u"ERROR: XMPP connection failed for profile '%(profile)s': %(reason)s" + % {"profile": self.profile, "reason": reason} + ) + ) self.conn_deferred.errback(reason.value) try: super(SatXMPPEntity, self).initializationFailed(reason) @@ -231,14 +263,24 @@ except AttributeError: log.debug(_("No keep_alife")) if self._connected is not None: - self.host_app.bridge.disconnected(self.profile) # we send the signal to the clients + self.host_app.bridge.disconnected( + self.profile + ) # we send the signal to the clients self._connected.callback(None) - self.host_app.purgeEntity(self.profile) # and we remove references to this client - log.info(_(u"********** [{profile}] DISCONNECTED **********").format(profile=self.profile)) + self.host_app.purgeEntity( + self.profile + ) # and we remove references to this client + log.info( + _(u"********** [{profile}] DISCONNECTED **********").format( + profile=self.profile + ) + ) if not self.conn_deferred.called: # FIXME: real error is not gotten here (e.g. if jid is not know by Prosody, # we should have the real error) - self.conn_deferred.errback(error.StreamError(u"Server unexpectedly closed the connection")) + self.conn_deferred.errback( + error.StreamError(u"Server unexpectedly closed the connection") + ) @defer.inlineCallbacks def _cleanConnection(self, dummy): @@ -265,7 +307,7 @@ ## sending ## - def IQ(self, type_=u'set', timeout=60): + def IQ(self, type_=u"set", timeout=60): """shortcut to create an IQ element managing deferred @param type_(unicode): IQ type ('set' or 'get') @@ -301,26 +343,28 @@ - extra @return (dict) message data """ - data['xml'] = message_elt = domish.Element((None, 'message')) + data["xml"] = message_elt = domish.Element((None, "message")) message_elt["to"] = data["to"].full() - message_elt["from"] = data['from'].full() + message_elt["from"] = data["from"].full() message_elt["type"] = data["type"] - if data['uid']: # key must be present but can be set to '' - # by a plugin to avoid id on purpose - message_elt['id'] = data['uid'] + if data["uid"]: # key must be present but can be set to '' + # by a plugin to avoid id on purpose + message_elt["id"] = data["uid"] for lang, subject in data["subject"].iteritems(): subject_elt = message_elt.addElement("subject", content=subject) if lang: - subject_elt[(C.NS_XML, 'lang')] = lang + subject_elt[(C.NS_XML, "lang")] = lang for lang, message in data["message"].iteritems(): body_elt = message_elt.addElement("body", content=message) if lang: - body_elt[(C.NS_XML, 'lang')] = lang + body_elt[(C.NS_XML, "lang")] = lang try: - thread = data['extra']['thread'] + thread = data["extra"]["thread"] except KeyError: - if 'thread_parent' in data['extra']: - raise exceptions.InternalError(u"thread_parent found while there is not associated thread") + if "thread_parent" in data["extra"]: + raise exceptions.InternalError( + u"thread_parent found while there is not associated thread" + ) else: thread_elt = message_elt.addElement("thread", content=thread) try: @@ -336,7 +380,16 @@ """ raise NotImplementedError - def sendMessage(self, to_jid, message, subject=None, mess_type='auto', extra=None, uid=None, no_trigger=False): + def sendMessage( + self, + to_jid, + message, + subject=None, + mess_type="auto", + extra=None, + uid=None, + no_trigger=False, + ): """Send a message to an entity @param to_jid(jid.JID): destinee of the message @@ -371,18 +424,26 @@ "extra": extra, "timestamp": time.time(), } - pre_xml_treatments = defer.Deferred() # XXX: plugin can add their pre XML treatments to this deferred - post_xml_treatments = defer.Deferred() # XXX: plugin can add their post XML treatments to this deferred + pre_xml_treatments = ( + defer.Deferred() + ) # XXX: plugin can add their pre XML treatments to this deferred + post_xml_treatments = ( + defer.Deferred() + ) # XXX: plugin can add their post XML treatments to this deferred if data["type"] == C.MESS_TYPE_AUTO: # we try to guess the type if data["subject"]: data["type"] = C.MESS_TYPE_NORMAL - elif not data["to"].resource: # if to JID has a resource, the type is not 'groupchat' + elif not data[ + "to" + ].resource: # if to JID has a resource, the type is not 'groupchat' # we may have a groupchat message, we check if the we know this jid try: - entity_type = self.host_app.memory.getEntityData(data["to"], ['type'], self.profile)["type"] - #FIXME: should entity_type manage resources ? + entity_type = self.host_app.memory.getEntityData( + data["to"], ["type"], self.profile + )["type"] + # FIXME: should entity_type manage resources ? except (exceptions.UnknownEntityError, KeyError): entity_type = "contact" @@ -397,19 +458,33 @@ # FIXME: send_only is used by libervia's OTR plugin to avoid # the triggers from frontend, and no_trigger do the same # thing internally, this could be unified - send_only = data['extra'].get('send_only', False) + send_only = data["extra"].get("send_only", False) if not no_trigger and not send_only: - if not self.host_app.trigger.point("sendMessage" + self.trigger_suffix, self, data, pre_xml_treatments, post_xml_treatments): + if not self.host_app.trigger.point( + "sendMessage" + self.trigger_suffix, + self, + data, + pre_xml_treatments, + post_xml_treatments, + ): return defer.succeed(None) - log.debug(_(u"Sending message (type {type}, to {to})").format(type=data["type"], to=to_jid.full())) + log.debug( + _(u"Sending message (type {type}, to {to})").format( + type=data["type"], to=to_jid.full() + ) + ) pre_xml_treatments.addCallback(lambda dummy: self.generateMessageXML(data)) pre_xml_treatments.chainDeferred(post_xml_treatments) post_xml_treatments.addCallback(self.sendMessageData) if send_only: - log.debug(_("Triggers, storage and echo have been inhibited by the 'send_only' parameter")) + log.debug( + _( + "Triggers, storage and echo have been inhibited by the 'send_only' parameter" + ) + ) else: self.addPostXmlCallbacks(post_xml_treatments) post_xml_treatments.addErrback(self._cancelErrorTrap) @@ -430,10 +505,12 @@ if data[u"type"] != C.MESS_TYPE_GROUPCHAT: # we don't add groupchat message to history, as we get them back # and they will be added then - if data[u'message'] or data[u'subject']: # we need a message to store + if data[u"message"] or data[u"subject"]: # we need a message to store self.host_app.memory.addToHistory(self, data) else: - log.warning(u"No message found") # empty body should be managed by plugins before this point + log.warning( + u"No message found" + ) # empty body should be managed by plugins before this point return data def messageSendToBridge(self, data): @@ -445,11 +522,23 @@ if data[u"type"] != C.MESS_TYPE_GROUPCHAT: # we don't send groupchat message to bridge, as we get them back # and they will be added the - if data[u'message'] or data[u'subject']: # we need a message to send something + if ( + data[u"message"] or data[u"subject"] + ): # we need a message to send something # We send back the message, so all frontends are aware of it - self.host_app.bridge.messageNew(data[u'uid'], data[u'timestamp'], data[u'from'].full(), data[u'to'].full(), data[u'message'], data[u'subject'], data[u'type'], data[u'extra'], profile=self.profile) + self.host_app.bridge.messageNew( + data[u"uid"], + data[u"timestamp"], + data[u"from"].full(), + data[u"to"].full(), + data[u"message"], + data[u"subject"], + data[u"type"], + data[u"extra"], + profile=self.profile, + ) else: - log.warning(_(u"No message found")) + log.warning(_(u"No message found")) return data @@ -458,7 +547,16 @@ trigger_suffix = "" is_component = False - def __init__(self, host_app, profile, user_jid, password, host=None, port=C.XMPP_C2S_PORT, max_retries=C.XMPP_MAX_RETRIES): + def __init__( + self, + host_app, + profile, + user_jid, + password, + host=None, + port=C.XMPP_C2S_PORT, + max_retries=C.XMPP_MAX_RETRIES, + ): # XXX: DNS SRV records are checked when the host is not specified. # If no SRV record is found, the host is directly extracted from the JID. self.started = time.time() @@ -482,25 +580,30 @@ if isinstance(host_data, basestring): host = host_data elif isinstance(host_data, dict): - if u'host' in host_data: - host = host_data[u'host'] - if u'port' in host_data: - port = host_data[u'port'] + if u"host" in host_data: + host = host_data[u"host"] + if u"port" in host_data: + port = host_data[u"port"] else: - log.warning(_(u"invalid data used for host: {data}").format(data=host_data)) + log.warning( + _(u"invalid data used for host: {data}").format(data=host_data) + ) host_data = None if host_data is not None: - log.info(u"using {host}:{port} for host {host_ori} as requested in config".format( - host_ori = user_jid.host, - host = host, - port = port)) + log.info( + u"using {host}:{port} for host {host_ori} as requested in config".format( + host_ori=user_jid.host, host=host, port=port + ) + ) - wokkel_client.XMPPClient.__init__(self, user_jid, password, host or None, port or C.XMPP_C2S_PORT) + wokkel_client.XMPPClient.__init__( + self, user_jid, password, host or None, port or C.XMPP_C2S_PORT + ) SatXMPPEntity.__init__(self, host_app, profile, max_retries) def _getPluginsList(self): for p in self.host_app.plugins.itervalues(): - if C.PLUG_MODE_CLIENT in p._info[u'modes']: + if C.PLUG_MODE_CLIENT in p._info[u"modes"]: yield p def _createSubProtocols(self): @@ -531,9 +634,9 @@ # (out of band transmission for instance). # e2e should have a priority of 0 here, and out of band transmission # a lower priority - # FIXME: trigger not used yet, can be uncommented when e2e full stanza encryption is implemented - # if not self.host_app.trigger.point("send", self, obj): - # return + # FIXME: trigger not used yet, can be uncommented when e2e full stanza encryption is implemented + # if not self.host_app.trigger.point("send", self, obj): + # return super(SatXMPPClient, self).send(obj) def sendMessageData(self, mess_data): @@ -549,7 +652,7 @@ # This is intented for e2e encryption which doesn't do full stanza encryption (e.g. OTR) # This trigger point can't cancel the method self.host_app.trigger.point("sendMessageData", self, mess_data) - self.send(mess_data[u'xml']) + self.send(mess_data[u"xml"]) return mess_data def feedback(self, to_jid, message): @@ -560,15 +663,17 @@ @param to_jid(jid.JID): destinee jid @param message(unicode): message to send to frontends """ - self.host_app.bridge.messageNew(uid=unicode(uuid.uuid4()), - timestamp=time.time(), - from_jid=self.jid.full(), - to_jid=to_jid.full(), - message={u'': message}, - subject={}, - mess_type=C.MESS_TYPE_INFO, - extra={}, - profile=self.profile) + self.host_app.bridge.messageNew( + uid=unicode(uuid.uuid4()), + timestamp=time.time(), + from_jid=self.jid.full(), + to_jid=to_jid.full(), + message={u"": message}, + subject={}, + mess_type=C.MESS_TYPE_INFO, + extra={}, + profile=self.profile, + ) def _finish_connection(self, dummy): self.roster.requestRoster() @@ -583,12 +688,26 @@ An entry point plugin is launched after component is connected. Component need to instantiate MessageProtocol itself """ + implements(iwokkel.IDisco) - trigger_suffix = "Component" # used for to distinguish some trigger points set in SatXMPPEntity + trigger_suffix = ( + "Component" + ) # used for to distinguish some trigger points set in SatXMPPEntity is_component = True - sendHistory = False # XXX: set to True from entry plugin to keep messages in history for received messages + sendHistory = ( + False + ) # XXX: set to True from entry plugin to keep messages in history for received messages - def __init__(self, host_app, profile, component_jid, password, host=None, port=None, max_retries=C.XMPP_MAX_RETRIES): + def __init__( + self, + host_app, + profile, + component_jid, + password, + host=None, + port=None, + max_retries=C.XMPP_MAX_RETRIES, + ): self.started = time.time() if port is None: port = C.XMPP_COMPONENT_PORT @@ -598,15 +717,18 @@ try: self.entry_plugin = host_app.plugins[entry_point] except KeyError: - raise exceptions.NotFound(_(u"The requested entry point ({entry_point}) is not available").format( - entry_point = entry_point)) + raise exceptions.NotFound( + _(u"The requested entry point ({entry_point}) is not available").format( + entry_point=entry_point + ) + ) self.identities = [disco.DiscoIdentity(u"component", u"generic", C.APP_NAME)] # jid is set automatically on bind by Twisted for Client, but not for Component self.jid = component_jid if host is None: try: - host = component_jid.host.split(u'.', 1)[1] + host = component_jid.host.split(u".", 1)[1] except IndexError: raise ValueError(u"Can't guess host from jid, please specify a host") # XXX: component.Component expect unicode jid, while Client expect jid.JID. @@ -628,14 +750,18 @@ @raise InternalError: one of the plugin is not handling components @raise KeyError: one plugin should be present in self.host_app.plugins but it is not """ - if C.PLUG_MODE_COMPONENT not in current._info[u'modes']: + if C.PLUG_MODE_COMPONENT not in current._info[u"modes"]: if not required: return else: - log.error(_(u"Plugin {current_name} is needed for {entry_name}, but it doesn't handle component mode").format( - current_name = current._info[u'import_name'], - entry_name = self.entry_plugin._info[u'import_name'] - )) + log.error( + _( + u"Plugin {current_name} is needed for {entry_name}, but it doesn't handle component mode" + ).format( + current_name=current._info[u"import_name"], + entry_name=self.entry_plugin._info[u"import_name"], + ) + ) raise exceptions.InternalError(_(u"invalid plugin mode")) for import_name in current._info.get(C.PI_DEPENDENCIES, []): @@ -651,7 +777,7 @@ dep = self.host_app.plugins[import_name] except KeyError: continue - self._buildDependencies(dep, plugins, required = False) + self._buildDependencies(dep, plugins, required=False) if current not in plugins: # current can be required for several plugins and so @@ -680,7 +806,6 @@ class SatMessageProtocol(xmppim.MessageProtocol): - def __init__(self, host): xmppim.MessageProtocol.__init__(self) self.host = host @@ -697,52 +822,60 @@ message = {} subject = {} extra = {} - data = {"from": jid.JID(message_elt['from']), - "to": jid.JID(message_elt['to']), - "uid": message_elt.getAttribute('uid', unicode(uuid.uuid4())), # XXX: uid is not a standard attribute but may be added by plugins - "message": message, - "subject": subject, - "type": message_elt.getAttribute('type', 'normal'), - "extra": extra} + data = { + "from": jid.JID(message_elt["from"]), + "to": jid.JID(message_elt["to"]), + "uid": message_elt.getAttribute( + "uid", unicode(uuid.uuid4()) + ), # XXX: uid is not a standard attribute but may be added by plugins + "message": message, + "subject": subject, + "type": message_elt.getAttribute("type", "normal"), + "extra": extra, + } if client is not None: try: - data['stanza_id'] = message_elt['id'] + data["stanza_id"] = message_elt["id"] except KeyError: pass else: - client._mess_id_uid[(data['from'], data['stanza_id'])] = data['uid'] + client._mess_id_uid[(data["from"], data["stanza_id"])] = data["uid"] # message - for e in message_elt.elements(C.NS_CLIENT, 'body'): - message[e.getAttribute((C.NS_XML,'lang'),'')] = unicode(e) + for e in message_elt.elements(C.NS_CLIENT, "body"): + message[e.getAttribute((C.NS_XML, "lang"), "")] = unicode(e) # subject - for e in message_elt.elements(C.NS_CLIENT, 'subject'): - subject[e.getAttribute((C.NS_XML, 'lang'),'')] = unicode(e) + for e in message_elt.elements(C.NS_CLIENT, "subject"): + subject[e.getAttribute((C.NS_XML, "lang"), "")] = unicode(e) # delay and timestamp try: - delay_elt = message_elt.elements(delay.NS_DELAY, 'delay').next() + delay_elt = message_elt.elements(delay.NS_DELAY, "delay").next() except StopIteration: - data['timestamp'] = time.time() + data["timestamp"] = time.time() else: parsed_delay = delay.Delay.fromElement(delay_elt) - data['timestamp'] = calendar.timegm(parsed_delay.stamp.utctimetuple()) - data['received_timestamp'] = unicode(time.time()) + data["timestamp"] = calendar.timegm(parsed_delay.stamp.utctimetuple()) + data["received_timestamp"] = unicode(time.time()) if parsed_delay.sender: - data['delay_sender'] = parsed_delay.sender.full() + data["delay_sender"] = parsed_delay.sender.full() return data def onMessage(self, message_elt): # TODO: handle threads client = self.parent - if not 'from' in message_elt.attributes: - message_elt['from'] = client.jid.host - log.debug(_(u"got message from: {from_}").format(from_=message_elt['from'])) - post_treat = defer.Deferred() # XXX: plugin can add their treatments to this deferred + if not "from" in message_elt.attributes: + message_elt["from"] = client.jid.host + log.debug(_(u"got message from: {from_}").format(from_=message_elt["from"])) + post_treat = ( + defer.Deferred() + ) # XXX: plugin can add their treatments to this deferred - if not self.host.trigger.point("MessageReceived", client, message_elt, post_treat): + if not self.host.trigger.point( + "MessageReceived", client, message_elt, post_treat + ): return data = self.parseMessage(message_elt, client) @@ -754,25 +887,35 @@ post_treat.callback(data) def skipEmptyMessage(self, data): - if not data['message'] and not data['extra'] and not data['subject']: + if not data["message"] and not data["extra"] and not data["subject"]: raise failure.Failure(exceptions.CancelError("Cancelled empty message")) return data def addToHistory(self, data, client): - if data.pop(u'history', None) == C.HISTORY_SKIP: - log.info(u'history is skipped as requested') - data[u'extra'][u'history'] = C.HISTORY_SKIP + if data.pop(u"history", None) == C.HISTORY_SKIP: + log.info(u"history is skipped as requested") + data[u"extra"][u"history"] = C.HISTORY_SKIP else: return self.host.memory.addToHistory(client, data) def bridgeSignal(self, dummy, client, data): try: - data['extra']['received_timestamp'] = data['received_timestamp'] - data['extra']['delay_sender'] = data['delay_sender'] + data["extra"]["received_timestamp"] = data["received_timestamp"] + data["extra"]["delay_sender"] = data["delay_sender"] except KeyError: pass if data is not None: - self.host.bridge.messageNew(data['uid'], data['timestamp'], data['from'].full(), data['to'].full(), data['message'], data['subject'], data['type'], data['extra'], profile=client.profile) + self.host.bridge.messageNew( + data["uid"], + data["timestamp"], + data["from"].full(), + data["to"].full(), + data["message"], + data["subject"], + data["type"], + data["extra"], + profile=client.profile, + ) return data def cancelErrorTrap(self, failure_): @@ -781,26 +924,29 @@ class SatRosterProtocol(xmppim.RosterClientProtocol): - def __init__(self, host): xmppim.RosterClientProtocol.__init__(self) self.host = host - self.got_roster = defer.Deferred() # called when roster is received and ready - #XXX: the two following dicts keep a local copy of the roster + self.got_roster = defer.Deferred() # called when roster is received and ready + # XXX: the two following dicts keep a local copy of the roster self._groups = {} # map from groups to jids: key=group value=set of jids self._jids = None # map from jids to RosterItem: key=jid value=RosterItem def rosterCb(self, roster): - assert roster is not None # FIXME: must be managed with roster versioning + assert roster is not None # FIXME: must be managed with roster versioning self._groups.clear() self._jids = roster for item in roster.itervalues(): if not item.subscriptionTo and not item.subscriptionFrom and not item.ask: - #XXX: current behaviour: we don't want contact in our roster list + # XXX: current behaviour: we don't want contact in our roster list # if there is no presence subscription # may change in the future - log.info(u"Removing contact {} from roster because there is no presence subscription".format(item.jid)) - self.removeItem(item.entity) # FIXME: to be checked + log.info( + u"Removing contact {} from roster because there is no presence subscription".format( + item.jid + ) + ) + self.removeItem(item.entity) # FIXME: to be checked else: self._registerItem(item) @@ -812,10 +958,16 @@ """ log.debug(u"registering item: {}".format(item.entity.full())) if item.entity.resource: - log.warning(u"Received a roster item with a resource, this is not common but not restricted by RFC 6121, this case may be not well tested.") + log.warning( + u"Received a roster item with a resource, this is not common but not restricted by RFC 6121, this case may be not well tested." + ) if not item.subscriptionTo: if not item.subscriptionFrom: - log.info(_(u"There's no subscription between you and [{}]!").format(item.entity.full())) + log.info( + _(u"There's no subscription between you and [{}]!").format( + item.entity.full() + ) + ) else: log.info(_(u"You are not subscribed to [{}]!").format(item.entity.full())) if not item.subscriptionFrom: @@ -843,16 +995,17 @@ @param item: RosterItem @return: dictionary of attributes """ - item_attr = {'to': unicode(item.subscriptionTo), - 'from': unicode(item.subscriptionFrom), - 'ask': unicode(item.ask) - } + item_attr = { + "to": unicode(item.subscriptionTo), + "from": unicode(item.subscriptionFrom), + "ask": unicode(item.ask), + } if item.name: - item_attr['name'] = item.name + item_attr["name"] = item.name return item_attr def setReceived(self, request): - #TODO: implement roster versioning (cf RFC 6121 §2.6) + # TODO: implement roster versioning (cf RFC 6121 §2.6) item = request.item try: # update the cache for the groups the contact has been removed from left_groups = set(self._jids[item.entity].groups).difference(item.groups) @@ -865,7 +1018,9 @@ pass # no previous item registration (or it's been cleared) self._jids[item.entity] = item self._registerItem(item) - self.host.bridge.newContact(item.entity.full(), self.getAttributes(item), item.groups, self.parent.profile) + self.host.bridge.newContact( + item.entity.full(), self.getAttributes(item), item.groups, self.parent.profile + ) def removeReceived(self, request): entity = request.item.entity @@ -875,7 +1030,11 @@ try: item = self._jids.pop(entity) except KeyError: - log.error(u"Received a roster remove event for an item not in cache ({})".format(entity)) + log.error( + u"Received a roster remove event for an item not in cache ({})".format( + entity + ) + ) return for group in item.groups: try: @@ -884,8 +1043,10 @@ if not jids_set: del self._groups[group] except KeyError: - log.warning(u"there is no cache for the group [%(group)s] of the removed roster item [%(jid)s]" % - {"group": group, "jid": entity}) + log.warning( + u"there is no cache for the group [%(group)s] of the removed roster item [%(jid)s]" + % {"group": group, "jid": entity} + ) # then we send the bridge signal self.host.bridge.contactDeleted(entity.full(), self.parent.profile) @@ -939,7 +1100,7 @@ @return (set(jid.JID)): set of selected jids """ if type_ == C.ALL and groups is not None: - raise ValueError('groups must not be set for {} type'.format(C.ALL)) + raise ValueError("groups must not be set for {} type".format(C.ALL)) if type_ == C.ALL: return set(self.getJids()) @@ -949,7 +1110,7 @@ jids.update(self.getJidsFromGroup(group)) return jids else: - raise ValueError(u'Unexpected type_ {}'.format(type_)) + raise ValueError(u"Unexpected type_ {}".format(type_)) def getNick(self, entity_jid): """Return a nick name for an entity @@ -965,7 +1126,6 @@ class SatPresenceProtocol(xmppim.PresenceClientProtocol): - def __init__(self, host): xmppim.PresenceClientProtocol.__init__(self) self.host = host @@ -977,7 +1137,17 @@ presence_d.addCallback(lambda __: super(SatPresenceProtocol, self).send(obj)) def availableReceived(self, entity, show=None, statuses=None, priority=0): - log.debug(_(u"presence update for [%(entity)s] (available, show=%(show)s statuses=%(statuses)s priority=%(priority)d)") % {'entity': entity, C.PRESENCE_SHOW: show, C.PRESENCE_STATUSES: statuses, C.PRESENCE_PRIORITY: priority}) + log.debug( + _( + u"presence update for [%(entity)s] (available, show=%(show)s statuses=%(statuses)s priority=%(priority)d)" + ) + % { + "entity": entity, + C.PRESENCE_SHOW: show, + C.PRESENCE_STATUSES: statuses, + C.PRESENCE_PRIORITY: priority, + } + ) if not statuses: statuses = {} @@ -985,20 +1155,25 @@ if None in statuses: # we only want string keys statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop(None) - if not self.host.trigger.point("presenceReceived", entity, show, priority, statuses, self.parent.profile): + if not self.host.trigger.point( + "presenceReceived", entity, show, priority, statuses, self.parent.profile + ): return - self.host.memory.setPresenceStatus(entity, show or "", - int(priority), statuses, - self.parent.profile) + self.host.memory.setPresenceStatus( + entity, show or "", int(priority), statuses, self.parent.profile + ) # now it's time to notify frontends - self.host.bridge.presenceUpdate(entity.full(), show or "", - int(priority), statuses, - self.parent.profile) + self.host.bridge.presenceUpdate( + entity.full(), show or "", int(priority), statuses, self.parent.profile + ) def unavailableReceived(self, entity, statuses=None): - log.debug(_(u"presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") % {'entity': entity, C.PRESENCE_STATUSES: statuses}) + log.debug( + _(u"presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") + % {"entity": entity, C.PRESENCE_STATUSES: statuses} + ) if not statuses: statuses = {} @@ -1006,21 +1181,33 @@ if None in statuses: # we only want string keys statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop(None) - if not self.host.trigger.point("presenceReceived", entity, "unavailable", 0, statuses, self.parent.profile): + if not self.host.trigger.point( + "presenceReceived", entity, "unavailable", 0, statuses, self.parent.profile + ): return # now it's time to notify frontends # if the entity is not known yet in this session or is already unavailable, there is no need to send an unavailable signal try: - presence = self.host.memory.getEntityDatum(entity, "presence", self.parent.profile) + presence = self.host.memory.getEntityDatum( + entity, "presence", self.parent.profile + ) except (KeyError, exceptions.UnknownEntityError): # the entity has not been seen yet in this session pass else: if presence.show != C.PRESENCE_UNAVAILABLE: - self.host.bridge.presenceUpdate(entity.full(), C.PRESENCE_UNAVAILABLE, 0, statuses, self.parent.profile) + self.host.bridge.presenceUpdate( + entity.full(), + C.PRESENCE_UNAVAILABLE, + 0, + statuses, + self.parent.profile, + ) - self.host.memory.setPresenceStatus(entity, C.PRESENCE_UNAVAILABLE, 0, statuses, self.parent.profile) + self.host.memory.setPresenceStatus( + entity, C.PRESENCE_UNAVAILABLE, 0, statuses, self.parent.profile + ) def available(self, entity=None, show=None, statuses=None, priority=None): """Set a presence and statuses. @@ -1032,7 +1219,11 @@ """ if priority is None: try: - priority = int(self.host.memory.getParamA("Priority", "Connection", profile_key=self.parent.profile)) + priority = int( + self.host.memory.getParamA( + "Priority", "Connection", profile_key=self.parent.profile + ) + ) except ValueError: priority = 0 @@ -1048,7 +1239,7 @@ # ... before switching back if None in statuses: - statuses['default'] = statuses.pop(None) + statuses["default"] = statuses.pop(None) if not self.host.trigger.point("presence_available", presence_elt, self.parent): return @@ -1060,7 +1251,9 @@ xmppim.PresenceClientProtocol.subscribed(self, entity) self.host.memory.delWaitingSub(entity.userhost(), self.parent.profile) item = self.parent.roster.getItem(entity) - if not item or not item.subscriptionTo: # we automatically subscribe to 'to' presence + if ( + not item or not item.subscriptionTo + ): # we automatically subscribe to 'to' presence log.debug(_('sending automatic "from" subscription request')) self.subscribe(entity) @@ -1070,11 +1263,11 @@ def subscribedReceived(self, entity): log.debug(_(u"subscription approved for [%s]") % entity.userhost()) - self.host.bridge.subscribe('subscribed', entity.userhost(), self.parent.profile) + self.host.bridge.subscribe("subscribed", entity.userhost(), self.parent.profile) def unsubscribedReceived(self, entity): log.debug(_(u"unsubscription confirmed for [%s]") % entity.userhost()) - self.host.bridge.subscribe('unsubscribed', entity.userhost(), self.parent.profile) + self.host.bridge.subscribe("unsubscribed", entity.userhost(), self.parent.profile) @defer.inlineCallbacks def subscribeReceived(self, entity): @@ -1083,11 +1276,15 @@ item = self.parent.roster.getItem(entity) if item and item.subscriptionTo: # We automatically accept subscription if we are already subscribed to contact presence - log.debug(_('sending automatic subscription acceptance')) + log.debug(_("sending automatic subscription acceptance")) self.subscribed(entity) else: - self.host.memory.addWaitingSub('subscribe', entity.userhost(), self.parent.profile) - self.host.bridge.subscribe('subscribe', entity.userhost(), self.parent.profile) + self.host.memory.addWaitingSub( + "subscribe", entity.userhost(), self.parent.profile + ) + self.host.bridge.subscribe( + "subscribe", entity.userhost(), self.parent.profile + ) @defer.inlineCallbacks def unsubscribeReceived(self, entity): @@ -1095,9 +1292,9 @@ yield self.parent.roster.got_roster item = self.parent.roster.getItem(entity) if item and item.subscriptionFrom: # we automatically remove contact - log.debug(_('automatic contact deletion')) + log.debug(_("automatic contact deletion")) self.host.delContact(entity, self.parent.profile) - self.host.bridge.subscribe('unsubscribe', entity.userhost(), self.parent.profile) + self.host.bridge.subscribe("unsubscribe", entity.userhost(), self.parent.profile) class SatDiscoProtocol(disco.DiscoClientProtocol): @@ -1117,9 +1314,8 @@ class SatVersionHandler(generic.VersionHandler): - def getDiscoInfo(self, requestor, target, node): - #XXX: We need to work around wokkel's behaviour (namespace not added if there is a + # XXX: We need to work around wokkel's behaviour (namespace not added if there is a # node) as it cause issues with XEP-0115 & PEP (XEP-0163): there is a node when server # ask for disco info, and not when we generate the key, so the hash is used with different # disco features, and when the server (seen on ejabberd) generate its own hash for security check @@ -1131,11 +1327,12 @@ """ Manage disco Identity of SàT. """ - #TODO: dynamic identity update (see docstring). Note that a XMPP entity can have several identities + + # TODO: dynamic identity update (see docstring). Note that a XMPP entity can have several identities implements(iwokkel.IDisco) - def getDiscoInfo(self, requestor, target, nodeIdentifier=''): + def getDiscoInfo(self, requestor, target, nodeIdentifier=""): return self.parent.identities - def getDiscoItems(self, requestor, target, nodeIdentifier=''): + def getDiscoItems(self, requestor, target, nodeIdentifier=""): return []