Mercurial > libervia-backend
diff sat/core/xmpp.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | 94708a7d3ecf |
children | fee60f17ebac |
line wrap: on
line diff
--- a/sat/core/xmpp.py Wed Jul 31 11:31:22 2019 +0200 +++ b/sat/core/xmpp.py Tue Aug 13 19:08:41 2019 +0200 @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # SAT: a jabber client @@ -40,18 +40,18 @@ from sat.memory import encryption from sat.memory import persistent from sat.tools import xml_tools -from zope.interface import implements +from zope.interface import implementer log = getLogger(__name__) -NS_X_DATA = u"jabber:x:data" -NS_DISCO_INFO = u"http://jabber.org/protocol/disco#info" -NS_XML_ELEMENT = u"urn:xmpp:xml-element" -NS_ROSTER_VER = u"urn:xmpp:features:rosterver" +NS_X_DATA = "jabber:x:data" +NS_DISCO_INFO = "http://jabber.org/protocol/disco#info" +NS_XML_ELEMENT = "urn:xmpp:xml-element" +NS_ROSTER_VER = "urn:xmpp:features:rosterver" # we use 2 "@" which is illegal in a jid, to be sure we are not mixing keys # with roster jids -ROSTER_VER_KEY = u"@version@" +ROSTER_VER_KEY = "@version@" class SatXMPPEntity(object): @@ -65,9 +65,9 @@ clientConnectionFailed_ori = factory.clientConnectionFailed clientConnectionLost_ori = factory.clientConnectionLost factory.clientConnectionFailed = partial( - self.connectionTerminated, term_type=u"failed", cb=clientConnectionFailed_ori) + self.connectionTerminated, term_type="failed", cb=clientConnectionFailed_ori) factory.clientConnectionLost = partial( - self.connectionTerminated, term_type=u"lost", cb=clientConnectionLost_ori) + self.connectionTerminated, term_type="lost", cb=clientConnectionLost_ori) factory.maxRetries = max_retries factory.maxDelay = 30 @@ -87,7 +87,7 @@ self.encryption = encryption.EncryptionHandler(self) def __unicode__(self): - return u"Client instance for profile {profile}".format(profile=self.profile) + return "Client instance for profile {profile}".format(profile=self.profile) def __str__(self): return self.__unicode__.encode('utf-8') @@ -206,11 +206,11 @@ def logPluginResults(results): all_succeed = all([success for success, result in results]) if not all_succeed: - log.error(_(u"Plugins initialisation error")) + log.error(_("Plugins initialisation error")) for idx, (success, result) in enumerate(results): if not success: log.error( - u"error (plugin %(name)s): %(failure)s" + "error (plugin %(name)s): %(failure)s" % { "name": plugin_conn_cb[idx][0]._info["import_name"], "failure": result, @@ -226,11 +226,11 @@ self._connected_d = None def _disconnectionEb(self, failure_): - log.error(_(u"Error while disconnecting: {}".format(failure_))) + log.error(_("Error while disconnecting: {}".format(failure_))) def _authd(self, xmlstream): super(SatXMPPEntity, self)._authd(xmlstream) - log.debug(_(u"{profile} identified").format(profile=self.profile)) + log.debug(_("{profile} identified").format(profile=self.profile)) self.streamInitialized() def _finish_connection(self, __): @@ -238,7 +238,7 @@ def streamInitialized(self): """Called after _authd""" - log.debug(_(u"XML stream is initialized")) + log.debug(_("XML stream is initialized")) if not self.host_app.trigger.point("xml_init", self): return self.postStreamInit() @@ -246,7 +246,7 @@ def postStreamInit(self): """Workflow after stream initalisation.""" log.info( - _(u"********** [{profile}] CONNECTED **********").format(profile=self.profile) + _("********** [{profile}] CONNECTED **********").format(profile=self.profile) ) # the following Deferred is used to know when we are connected @@ -273,7 +273,7 @@ def initializationFailed(self, reason): log.error( _( - u"ERROR: XMPP connection failed for profile '%(profile)s': %(reason)s" + "ERROR: XMPP connection failed for profile '%(profile)s': %(reason)s" % {"profile": self.profile, "reason": reason} ) ) @@ -306,17 +306,17 @@ if reason is not None and not isinstance(reason.value, internet_error.ConnectionDone): try: - reason_str = unicode(reason.value) + reason_str = str(reason.value) except Exception: # FIXME: workaround for Android were p4a strips docstrings # while Twisted use docstring in __str__ # TODO: create a ticket upstream, Twisted should work when optimization # is used - reason_str = unicode(reason.value.__class__) - log.warning(u"Connection {term_type}: {reason}".format( + reason_str = str(reason.value.__class__) + log.warning("Connection {term_type}: {reason}".format( term_type = term_type, reason=reason_str)) - if not self.host_app.trigger.point(u"connection_" + term_type, connector, reason): + if not self.host_app.trigger.point("connection_" + term_type, connector, reason): return return cb(connector, reason) @@ -327,7 +327,7 @@ Retrying is disabled too, as it makes no sense to try without network, and it may use resources (notably battery on mobiles). """ - log.info(_(u"stopping connection because of network disabled")) + log.info(_("stopping connection because of network disabled")) self.factory.continueTrying = 0 self._network_disabled = True if self.xmlstream is not None: @@ -344,13 +344,13 @@ except AttributeError: # connection has not been stopped by networkDisabled # we don't have to restart it - log.debug(u"no connection to restart") + log.debug("no connection to restart") return else: del self._network_disabled if not network_disabled: - raise exceptions.InternalError(u"network_disabled should be True") - log.info(_(u"network is available, trying to connect")) + raise exceptions.InternalError("network_disabled should be True") + log.info(_("network is available, trying to connect")) # we want to be sure to start fresh self.factory.resetDelay() # we have a saved connector, meaning the connection has been stopped previously @@ -378,23 +378,23 @@ self.profile ) # and we remove references to this client log.info( - _(u"********** [{profile}] DISCONNECTED **********").format( + _("********** [{profile}] DISCONNECTED **********").format( profile=self.profile ) ) if not self.conn_deferred.called: if reason is None: - err = error.StreamError(u"Server unexpectedly closed the connection") + err = error.StreamError("Server unexpectedly closed the connection") else: err = reason try: if err.value.args[0][0][2] == "certificate verify failed": err = exceptions.InvalidCertificate( - _(u"Your server certificate is not valid " - u"(its identity can't be checked).\n\n" - u"This should never happen and may indicate that " - u"somebody is trying to spy on you.\n" - u"Please contact your server administrator.")) + _("Your server certificate is not valid " + "(its identity can't be checked).\n\n" + "This should never happen and may indicate that " + "somebody is trying to spy on you.\n" + "Please contact your server administrator.")) self.factory.stopTrying() try: # with invalid certificate, we should not retry to connect @@ -434,7 +434,7 @@ def entityDisconnect(self): if not self.host_app.trigger.point("disconnecting", self): return - log.info(_(u"Disconnecting...")) + log.info(_("Disconnecting...")) self.stopService() if self._connected_d is not None: return self._connected_d @@ -443,7 +443,7 @@ ## sending ## - def IQ(self, type_=u"set", timeout=60): + def IQ(self, type_="set", timeout=60): """shortcut to create an IQ element managing deferred @param type_(unicode): IQ type ('set' or 'get') @@ -486,11 +486,11 @@ 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(): + for lang, subject in data["subject"].items(): subject_elt = message_elt.addElement("subject", content=subject) if lang: subject_elt[(C.NS_XML, "lang")] = lang - for lang, message in data["message"].iteritems(): + for lang, message in data["message"].items(): body_elt = message_elt.addElement("body", content=message) if lang: body_elt[(C.NS_XML, "lang")] = lang @@ -499,7 +499,7 @@ except KeyError: if "thread_parent" in data["extra"]: raise exceptions.InternalError( - u"thread_parent found while there is not associated thread" + "thread_parent found while there is not associated thread" ) else: thread_elt = message_elt.addElement("thread", content=thread) @@ -546,7 +546,7 @@ data = { # dict is similar to the one used in client.onMessage "from": self.jid, "to": to_jid, - "uid": uid or unicode(uuid.uuid4()), + "uid": uid or str(uuid.uuid4()), "message": message, "subject": subject, "type": mess_type, @@ -599,15 +599,15 @@ ): return defer.succeed(None) - log.debug(_(u"Sending message (type {type}, to {to})") + log.debug(_("Sending message (type {type}, to {to})") .format(type=data["type"], to=to_jid.full())) pre_xml_treatments.addCallback(lambda __: self.generateMessageXML(data)) pre_xml_treatments.chainDeferred(post_xml_treatments) post_xml_treatments.addCallback(self.sendMessageData) if send_only: - log.debug(_(u"Triggers, storage and echo have been inhibited by the " - u"'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) @@ -625,22 +625,22 @@ @param data: message data dictionnary @param client: profile's client """ - if data[u"type"] != C.MESS_TYPE_GROUPCHAT: + if data["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["message"] or data["subject"]: # we need a message to store self.host_app.memory.addToHistory(self, data) else: log.warning( - u"No message found" + "No message found" ) # empty body should be managed by plugins before this point return data def messageGetBridgeArgs(self, data): """Generate args to use with bridge from data dict""" - return (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"]) + return (data["uid"], data["timestamp"], data["from"].full(), + data["to"].full(), data["message"], data["subject"], + data["type"], data["extra"]) def messageSendToBridge(self, data): @@ -649,10 +649,10 @@ @param data: message data dictionnary @param client: profile's client """ - if data[u"type"] != C.MESS_TYPE_GROUPCHAT: + if data["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 + if (data["message"] or data["subject"]): # we need a message to send # something # We send back the message, so all frontends are aware of it @@ -661,12 +661,12 @@ profile=self.profile ) else: - log.warning(_(u"No message found")) + log.warning(_("No message found")) return data +@implementer(iwokkel.IDisco) class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient): - implements(iwokkel.IDisco) trigger_suffix = "" is_component = False @@ -681,34 +681,34 @@ # with a web frontend, # etc., we should implement a way to dynamically update identities through the # bridge - self.identities = [disco.DiscoIdentity(u"client", u"pc", C.APP_NAME)] + self.identities = [disco.DiscoIdentity("client", "pc", C.APP_NAME)] if sys.platform == "android": # FIXME: temporary hack as SRV is not working on android # TODO: remove this hack and fix SRV - log.info(u"FIXME: Android hack, ignoring SRV") + log.info("FIXME: Android hack, ignoring SRV") if host is None: host = user_jid.host # for now we consider Android devices to be always phones - self.identities = [disco.DiscoIdentity(u"client", u"phone", C.APP_NAME)] + self.identities = [disco.DiscoIdentity("client", "phone", C.APP_NAME)] hosts_map = host_app.memory.getConfig(None, "hosts_dict", {}) if host is None and user_jid.host in hosts_map: host_data = hosts_map[user_jid.host] - if isinstance(host_data, basestring): + if isinstance(host_data, str): 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 "host" in host_data: + host = host_data["host"] + if "port" in host_data: + port = host_data["port"] else: log.warning( - _(u"invalid data used for host: {data}").format(data=host_data) + _("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" + "using {host}:{port} for host {host_ori} as requested in config" .format(host_ori=user_jid.host, host=host, port=port) ) @@ -717,22 +717,22 @@ wokkel_client.XMPPClient.__init__( self, user_jid, password, host or None, port or C.XMPP_C2S_PORT, - check_certificate = self.check_certificate + # check_certificate = self.check_certificate # FIXME: currently disabled with Python 3 port ) SatXMPPEntity.__init__(self, host_app, profile, max_retries) if not self.check_certificate: - msg = (_(u"Certificate validation is deactivated, this is unsecure and " - u"somebody may be spying on you. If you have no good reason to disable " - u"certificate validation, please activate \"Check certificate\" in your " - u"settings in \"Connection\" tab.")) - xml_tools.quickNote(host_app, self, msg, _(u"Security notice"), + msg = (_("Certificate validation is deactivated, this is unsecure and " + "somebody may be spying on you. If you have no good reason to disable " + "certificate validation, please activate \"Check certificate\" in your " + "settings in \"Connection\" tab.")) + xml_tools.quickNote(host_app, self, msg, _("Security notice"), level = C.XMLUI_DATA_LVL_WARNING) def _getPluginsList(self): - for p in self.host_app.plugins.itervalues(): - if C.PLUG_MODE_CLIENT in p._info[u"modes"]: + for p in self.host_app.plugins.values(): + if C.PLUG_MODE_CLIENT in p._info["modes"]: yield p def _createSubProtocols(self): @@ -795,7 +795,7 @@ # This trigger point can't cancel the method yield self.host_app.trigger.asyncPoint("sendMessageData", self, mess_data, triggers_no_cancel=True) - self.send(mess_data[u"xml"]) + self.send(mess_data["xml"]) defer.returnValue(mess_data) def feedback(self, to_jid, message, extra=None): @@ -811,11 +811,11 @@ if extra is None: extra = {} self.host_app.bridge.messageNew( - uid=unicode(uuid.uuid4()), + uid=str(uuid.uuid4()), timestamp=time.time(), from_jid=self.jid.full(), to_jid=to_jid.full(), - message={u"": message}, + message={"": message}, subject={}, mess_type=C.MESS_TYPE_INFO, extra=extra, @@ -827,6 +827,7 @@ d.addCallback(lambda __: super(SatXMPPClient, self)._finish_connection(__)) +@implementer(iwokkel.IDisco) class SatXMPPComponent(SatXMPPEntity, component.Component): """XMPP component @@ -835,7 +836,6 @@ Component need to instantiate MessageProtocol itself """ - implements(iwokkel.IDisco) trigger_suffix = ( "Component" ) # used for to distinguish some trigger points set in SatXMPPEntity @@ -857,19 +857,19 @@ self.entry_plugin = host_app.plugins[entry_point] except KeyError: raise exceptions.NotFound( - _(u"The requested entry point ({entry_point}) is not available").format( + _("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)] + self.identities = [disco.DiscoIdentity("component", "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(".", 1)[1] except IndexError: - raise ValueError(u"Can't guess host from jid, please specify a host") + raise ValueError("Can't guess host from jid, please specify a host") # XXX: component.Component expect unicode jid, while Client expect jid.JID. # this is not consistent, so we use jid.JID for SatXMPP* component.Component.__init__(self, host, port, component_jid.full(), password) @@ -890,20 +890,20 @@ @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["modes"]: if not required: return else: log.error( _( - u"Plugin {current_name} is needed for {entry_name}, " - u"but it doesn't handle component mode" + "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"], + current_name=current._info["import_name"], + entry_name=self.entry_plugin._info["import_name"], ) ) - raise exceptions.InternalError(_(u"invalid plugin mode")) + raise exceptions.InternalError(_("invalid plugin mode")) for import_name in current._info.get(C.PI_DEPENDENCIES, []): # plugins are already loaded as dependencies @@ -960,9 +960,9 @@ if None, mapping will not be done @return(dict): message data """ - if message_elt.name != u"message": + if message_elt.name != "message": log.warning(_( - u"parseMessage used with a non <message/> stanza, ignoring: {xml}" + "parseMessage used with a non <message/> stanza, ignoring: {xml}" .format(xml=message_elt.toXml()))) return {} @@ -974,31 +974,31 @@ c.uri = C.NS_CLIENT elif message_elt.uri != C.NS_CLIENT: log.warning(_( - u"received <message> with a wrong namespace: {xml}" + "received <message> with a wrong namespace: {xml}" .format(xml=message_elt.toXml()))) client = self.parent - if not message_elt.hasAttribute(u'to'): + if not message_elt.hasAttribute('to'): message_elt['to'] = client.jid.full() message = {} subject = {} extra = {} data = { - u"from": jid.JID(message_elt["from"]), - u"to": jid.JID(message_elt["to"]), - u"uid": message_elt.getAttribute( - u"uid", unicode(uuid.uuid4()) + "from": jid.JID(message_elt["from"]), + "to": jid.JID(message_elt["to"]), + "uid": message_elt.getAttribute( + "uid", str(uuid.uuid4()) ), # XXX: uid is not a standard attribute but may be added by plugins - u"message": message, - u"subject": subject, - u"type": message_elt.getAttribute(u"type", u"normal"), - u"extra": extra, + "message": message, + "subject": subject, + "type": message_elt.getAttribute("type", "normal"), + "extra": extra, } try: - message_id = data[u"extra"][u"message_id"] = message_elt[u"id"] + message_id = data["extra"]["message_id"] = message_elt["id"] except KeyError: pass else: @@ -1006,11 +1006,11 @@ # message for e in message_elt.elements(C.NS_CLIENT, "body"): - message[e.getAttribute((C.NS_XML, "lang"), "")] = unicode(e) + message[e.getAttribute((C.NS_XML, "lang"), "")] = str(e) # subject for e in message_elt.elements(C.NS_CLIENT, "subject"): - subject[e.getAttribute((C.NS_XML, "lang"), "")] = unicode(e) + subject[e.getAttribute((C.NS_XML, "lang"), "")] = str(e) # delay and timestamp try: @@ -1018,12 +1018,12 @@ except AttributeError: # message_elt._received_timestamp should have been set in onMessage # but if parseMessage is called directly, it can be missing - log.debug(u"missing received timestamp for {message_elt}".format( + log.debug("missing received timestamp for {message_elt}".format( message_elt=message_elt)) received_timestamp = time.time() try: - delay_elt = message_elt.elements(delay.NS_DELAY, "delay").next() + delay_elt = next(message_elt.elements(delay.NS_DELAY, "delay")) except StopIteration: data["timestamp"] = received_timestamp else: @@ -1060,7 +1060,7 @@ 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"])) + log.debug(_("got message from: {from_}").format(from_=message_elt["from"])) # plugin can add their treatments to this deferred post_treat = defer.Deferred() @@ -1077,24 +1077,24 @@ return data def addToHistory(self, data): - 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("history", None) == C.HISTORY_SKIP: + log.info("history is skipped as requested") + data["extra"]["history"] = C.HISTORY_SKIP else: - if data[u"message"] or data[u"subject"]: # we need a message to store + if data["message"] or data["subject"]: # we need a message to store return self.host.memory.addToHistory(self.parent, data) else: - log.debug(u"not storing empty message to history: {data}" + log.debug("not storing empty message to history: {data}" .format(data=data)) def bridgeSignal(self, __, data): try: - data["extra"]["received_timestamp"] = unicode(data["received_timestamp"]) + data["extra"]["received_timestamp"] = str(data["received_timestamp"]) data["extra"]["delay_sender"] = data["delay_sender"] except KeyError: pass if C.MESS_KEY_ENCRYPTION in data: - data[u"extra"][u"encrypted"] = C.BOOL_TRUE + data["extra"]["encrypted"] = C.BOOL_TRUE if data is not None: if data["message"] or data["subject"] or data["type"] == C.MESS_TYPE_INFO: self.host.bridge.messageNew( @@ -1109,7 +1109,7 @@ profile=self.parent.profile, ) else: - log.debug(u"Discarding bridge signal for empty message: {data}".format( + log.debug("Discarding bridge signal for empty message: {data}".format( data=data)) return data @@ -1131,7 +1131,7 @@ @property def versioning(self): """True if server support roster versioning""" - return (NS_ROSTER_VER, u'ver') in self.parent.xmlstream.features + return (NS_ROSTER_VER, 'ver') in self.parent.xmlstream.features @property def roster_cache(self): @@ -1148,23 +1148,23 @@ item must be already registered in self._jids before this method is called @param item (RosterIem): item added """ - log.debug(u"registering item: {}".format(item.entity.full())) + log.debug("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 " - u"restricted by RFC 6121, this case may be not well tested." + "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( + _("There's no subscription between you and [{}]!").format( item.entity.full() ) ) else: - log.info(_(u"You are not subscribed to [{}]!").format(item.entity.full())) + log.info(_("You are not subscribed to [{}]!").format(item.entity.full())) if not item.subscriptionFrom: - log.info(_(u"[{}] is not subscribed to you!").format(item.entity.full())) + log.info(_("[{}] is not subscribed to you!").format(item.entity.full())) for group in item.groups: self._groups.setdefault(group, set()).add(item.entity) @@ -1178,7 +1178,7 @@ roster_cache = self.roster_cache yield roster_cache.clear() roster_cache[ROSTER_VER_KEY] = version - for roster_jid, roster_item in self._jids.iteritems(): + for roster_jid, roster_item in self._jids.items(): roster_jid_s = roster_jid.full() roster_item_elt = roster_item.toElement().toXml() roster_cache[roster_jid_s] = roster_item_elt @@ -1200,19 +1200,19 @@ def requestRoster(self): """Ask the server for Roster list """ if self.versioning: - log.info(_(u"our server support roster versioning, we use it")) + log.info(_("our server support roster versioning, we use it")) roster_cache = self.roster_cache yield roster_cache.load() try: version = roster_cache[ROSTER_VER_KEY] except KeyError: - log.info(_(u"no roster in cache, we start fresh")) + log.info(_("no roster in cache, we start fresh")) # u"" means we use versioning without valid roster in cache - version = u"" + version = "" else: - log.info(_(u"We have roster v{version} in cache").format(version=version)) + log.info(_("We have roster v{version} in cache").format(version=version)) # we deserialise cached roster to our local cache - for roster_jid_s, roster_item_elt_s in roster_cache.iteritems(): + for roster_jid_s, roster_item_elt_s in roster_cache.items(): if roster_jid_s == ROSTER_VER_KEY: continue roster_jid = jid.JID(roster_jid_s) @@ -1221,26 +1221,26 @@ self._jids[roster_jid] = roster_item self._registerItem(roster_item) else: - log.warning(_(u"our server doesn't support roster versioning")) + log.warning(_("our server doesn't support roster versioning")) version = None log.debug("requesting roster") roster = yield self.getRoster(version=version) if roster is None: - log.debug(u"empty roster result received, we'll get roster item with roster " - u"pushes") + log.debug("empty roster result received, we'll get roster item with roster " + "pushes") else: # a full roster is received self._groups.clear() self._jids = roster - for item in roster.itervalues(): + for item in roster.values(): if not item.subscriptionTo and not item.subscriptionFrom and not item.ask: # 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 " - u"subscription".format( + "Removing contact {} from roster because there is no presence " + "subscription".format( item.jid ) ) @@ -1267,9 +1267,9 @@ @return: dictionary of attributes """ item_attr = { - "to": unicode(item.subscriptionTo), - "from": unicode(item.subscriptionFrom), - "ask": unicode(item.ask), + "to": str(item.subscriptionTo), + "from": str(item.subscriptionFrom), + "ask": str(item.ask), } if item.name: item_attr["name"] = item.name @@ -1278,7 +1278,7 @@ def setReceived(self, request): item = request.item entity = item.entity - log.info(_(u"adding {entity} to roster").format(entity=entity.full())) + log.info(_("adding {entity} to roster").format(entity=entity.full())) if request.version is not None: # we update the cache in storage roster_cache = self.roster_cache @@ -1302,7 +1302,7 @@ def removeReceived(self, request): entity = request.item.entity - log.info(_(u"removing {entity} from roster").format(entity=entity.full())) + log.info(_("removing {entity} from roster").format(entity=entity.full())) if request.version is not None: # we update the cache in storage roster_cache = self.roster_cache @@ -1319,7 +1319,7 @@ item = self._jids.pop(entity) except KeyError: log.error( - u"Received a roster remove event for an item not in cache ({})".format( + "Received a roster remove event for an item not in cache ({})".format( entity ) ) @@ -1332,8 +1332,8 @@ del self._groups[group] except KeyError: log.warning( - u"there is no cache for the group [{group}] of the removed roster " - u"item [{jid_}]".format(group=group, jid=entity) + "there is no cache for the group [{group}] of the removed roster " + "item [{jid_}]".format(group=group, jid=entity) ) # then we send the bridge signal @@ -1341,7 +1341,7 @@ def getGroups(self): """Return a list of groups""" - return self._groups.keys() + return list(self._groups.keys()) def getItem(self, entity_jid): """Return RosterItem for a given jid @@ -1354,7 +1354,7 @@ def getJids(self): """Return all jids of the roster""" - return self._jids.keys() + return list(self._jids.keys()) def isJidInRoster(self, entity_jid): """Return True if jid is in roster""" @@ -1370,7 +1370,7 @@ def getItems(self): """Return all items of the roster""" - return self._jids.values() + return list(self._jids.values()) def getJidsFromGroup(self, group): try: @@ -1398,7 +1398,7 @@ jids.update(self.getJidsFromGroup(group)) return jids else: - raise ValueError(u"Unexpected type_ {}".format(type_)) + raise ValueError("Unexpected type_ {}".format(type_)) def getNick(self, entity_jid): """Return a nick name for an entity @@ -1447,7 +1447,7 @@ def unavailableReceived(self, entity, statuses=None): log.debug( - _(u"presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") + _("presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") % {"entity": entity, C.PRESENCE_STATUSES: statuses} ) @@ -1539,16 +1539,16 @@ self.host.memory.delWaitingSub(entity.userhost(), self.parent.profile) def subscribedReceived(self, entity): - log.debug(_(u"subscription approved for [%s]") % entity.userhost()) + log.debug(_("subscription approved for [%s]") % entity.userhost()) self.host.bridge.subscribe("subscribed", entity.userhost(), self.parent.profile) def unsubscribedReceived(self, entity): - log.debug(_(u"unsubscription confirmed for [%s]") % entity.userhost()) + log.debug(_("unsubscription confirmed for [%s]") % entity.userhost()) self.host.bridge.subscribe("unsubscribed", entity.userhost(), self.parent.profile) @defer.inlineCallbacks def subscribeReceived(self, entity): - log.debug(_(u"subscription request from [%s]") % entity.userhost()) + log.debug(_("subscription request from [%s]") % entity.userhost()) yield self.parent.roster.got_roster item = self.parent.roster.getItem(entity) if item and item.subscriptionTo: @@ -1566,7 +1566,7 @@ @defer.inlineCallbacks def unsubscribeReceived(self, entity): - log.debug(_(u"unsubscription asked for [%s]") % entity.userhost()) + log.debug(_("unsubscription asked for [%s]") % entity.userhost()) yield self.parent.roster.got_roster item = self.parent.roster.getItem(entity) if item and item.subscriptionFrom: # we automatically remove contact @@ -1575,8 +1575,8 @@ self.host.bridge.subscribe("unsubscribe", entity.userhost(), self.parent.profile) +@implementer(iwokkel.IDisco) class SatDiscoProtocol(disco.DiscoClientProtocol): - implements(iwokkel.IDisco) def __init__(self, host): disco.DiscoClientProtocol.__init__(self) @@ -1599,7 +1599,7 @@ def iqFallback(self, iq): if iq.handled is True: return - log.debug(u"iqFallback: xml = [%s]" % (iq.toXml())) + log.debug("iqFallback: xml = [%s]" % (iq.toXml())) generic.FallbackHandler.iqFallback(self, iq) @@ -1615,9 +1615,9 @@ return generic.VersionHandler.getDiscoInfo(self, requestor, target, None) +@implementer(iwokkel.IDisco) class SatIdentityHandler(XMPPHandler): """Manage disco Identity of SàT.""" - implements(iwokkel.IDisco) # TODO: dynamic identity update (see docstring). Note that a XMPP entity can have # several identities