Mercurial > libervia-backend
comparison sat/core/sat_main.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 | 6959c71ab8bf |
children | fee60f17ebac |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SAT: a jabber client | 4 # SAT: a jabber client |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 | 6 |
64 self.initialised = defer.Deferred() | 64 self.initialised = defer.Deferred() |
65 self.profiles = {} | 65 self.profiles = {} |
66 self.plugins = {} | 66 self.plugins = {} |
67 # map for short name to whole namespace, | 67 # map for short name to whole namespace, |
68 self.ns_map = { | 68 self.ns_map = { |
69 u"x-data": xmpp.NS_X_DATA, | 69 "x-data": xmpp.NS_X_DATA, |
70 u"disco#info": xmpp.NS_DISCO_INFO, | 70 "disco#info": xmpp.NS_DISCO_INFO, |
71 } | 71 } |
72 # extended by plugins with registerNamespace | 72 # extended by plugins with registerNamespace |
73 self.memory = memory.Memory(self) | 73 self.memory = memory.Memory(self) |
74 self.trigger = ( | 74 self.trigger = ( |
75 trigger.TriggerManager() | 75 trigger.TriggerManager() |
77 | 77 |
78 bridge_name = self.memory.getConfig("", "bridge", "dbus") | 78 bridge_name = self.memory.getConfig("", "bridge", "dbus") |
79 | 79 |
80 bridge_module = dynamic_import.bridge(bridge_name) | 80 bridge_module = dynamic_import.bridge(bridge_name) |
81 if bridge_module is None: | 81 if bridge_module is None: |
82 log.error(u"Can't find bridge module of name {}".format(bridge_name)) | 82 log.error("Can't find bridge module of name {}".format(bridge_name)) |
83 sys.exit(1) | 83 sys.exit(1) |
84 log.info(u"using {} bridge".format(bridge_name)) | 84 log.info("using {} bridge".format(bridge_name)) |
85 try: | 85 try: |
86 self.bridge = bridge_module.Bridge() | 86 self.bridge = bridge_module.Bridge() |
87 except exceptions.BridgeInitError: | 87 except exceptions.BridgeInitError: |
88 log.error(u"Bridge can't be initialised, can't start SàT core") | 88 log.error("Bridge can't be initialised, can't start SàT core") |
89 sys.exit(1) | 89 sys.exit(1) |
90 self.bridge.register_method("getReady", lambda: self.initialised) | 90 self.bridge.register_method("getReady", lambda: self.initialised) |
91 self.bridge.register_method("getVersion", lambda: self.full_version) | 91 self.bridge.register_method("getVersion", lambda: self.full_version) |
92 self.bridge.register_method("getFeatures", self.getFeatures) | 92 self.bridge.register_method("getFeatures", self.getFeatures) |
93 self.bridge.register_method("profileNameGet", self.memory.getProfileName) | 93 self.bridge.register_method("profileNameGet", self.memory.getProfileName) |
179 if version[-1] == "D": | 179 if version[-1] == "D": |
180 # we are in debug version, we add extra data | 180 # we are in debug version, we add extra data |
181 try: | 181 try: |
182 return self._version_cache | 182 return self._version_cache |
183 except AttributeError: | 183 except AttributeError: |
184 self._version_cache = u"{} « {} » ({})".format( | 184 self._version_cache = "{} « {} » ({})".format( |
185 version, C.APP_RELEASE_NAME, utils.getRepositoryData(sat) | 185 version, C.APP_RELEASE_NAME, utils.getRepositoryData(sat) |
186 ) | 186 ) |
187 return self._version_cache | 187 return self._version_cache |
188 else: | 188 else: |
189 return version | 189 return version |
200 self._import_plugins() | 200 self._import_plugins() |
201 ui_contact_list.ContactList(self) | 201 ui_contact_list.ContactList(self) |
202 ui_profile_manager.ProfileManager(self) | 202 ui_profile_manager.ProfileManager(self) |
203 except Exception as e: | 203 except Exception as e: |
204 log.error( | 204 log.error( |
205 _(u"Could not initialize backend: {reason}").format( | 205 _("Could not initialize backend: {reason}").format( |
206 reason=str(e).decode("utf-8", "ignore") | 206 reason=str(e).decode("utf-8", "ignore") |
207 ) | 207 ) |
208 ) | 208 ) |
209 sys.exit(1) | 209 sys.exit(1) |
210 self._addBaseMenus() | 210 self._addBaseMenus() |
211 self.initialised.callback(None) | 211 self.initialised.callback(None) |
212 log.info(_(u"Backend is ready")) | 212 log.info(_("Backend is ready")) |
213 | 213 |
214 def _addBaseMenus(self): | 214 def _addBaseMenus(self): |
215 """Add base menus""" | 215 """Add base menus""" |
216 encryption.EncryptionHandler._importMenus(self) | 216 encryption.EncryptionHandler._importMenus(self) |
217 | 217 |
244 try: | 244 try: |
245 __import__(plugin_path) | 245 __import__(plugin_path) |
246 except exceptions.MissingModule as e: | 246 except exceptions.MissingModule as e: |
247 self._unimport_plugin(plugin_path) | 247 self._unimport_plugin(plugin_path) |
248 log.warning( | 248 log.warning( |
249 u"Can't import plugin [{path}] because of an unavailale third party " | 249 "Can't import plugin [{path}] because of an unavailale third party " |
250 u"module:\n{msg}".format( | 250 "module:\n{msg}".format( |
251 path=plugin_path, msg=e | 251 path=plugin_path, msg=e |
252 ) | 252 ) |
253 ) | 253 ) |
254 continue | 254 continue |
255 except exceptions.CancelError as e: | 255 except exceptions.CancelError as e: |
256 log.info( | 256 log.info( |
257 u"Plugin [{path}] cancelled its own import: {msg}".format( | 257 "Plugin [{path}] cancelled its own import: {msg}".format( |
258 path=plugin_path, msg=e | 258 path=plugin_path, msg=e |
259 ) | 259 ) |
260 ) | 260 ) |
261 self._unimport_plugin(plugin_path) | 261 self._unimport_plugin(plugin_path) |
262 continue | 262 continue |
263 except Exception as e: | 263 except Exception as e: |
264 import traceback | 264 import traceback |
265 | 265 |
266 log.error( | 266 log.error( |
267 _(u"Can't import plugin [{path}]:\n{error}").format( | 267 _("Can't import plugin [{path}]:\n{error}").format( |
268 path=plugin_path, error=traceback.format_exc() | 268 path=plugin_path, error=traceback.format_exc() |
269 ) | 269 ) |
270 ) | 270 ) |
271 self._unimport_plugin(plugin_path) | 271 self._unimport_plugin(plugin_path) |
272 continue | 272 continue |
273 mod = sys.modules[plugin_path] | 273 mod = sys.modules[plugin_path] |
274 plugin_info = mod.PLUGIN_INFO | 274 plugin_info = mod.PLUGIN_INFO |
275 import_name = plugin_info["import_name"] | 275 import_name = plugin_info["import_name"] |
276 | 276 |
277 plugin_modes = plugin_info[u"modes"] = set( | 277 plugin_modes = plugin_info["modes"] = set( |
278 plugin_info.setdefault(u"modes", C.PLUG_MODE_DEFAULT) | 278 plugin_info.setdefault("modes", C.PLUG_MODE_DEFAULT) |
279 ) | 279 ) |
280 | 280 |
281 # if the plugin is an entry point, it must work in component mode | 281 # if the plugin is an entry point, it must work in component mode |
282 if plugin_info[u"type"] == C.PLUG_TYPE_ENTRY_POINT: | 282 if plugin_info["type"] == C.PLUG_TYPE_ENTRY_POINT: |
283 # if plugin is an entrypoint, we cache it | 283 # if plugin is an entrypoint, we cache it |
284 if C.PLUG_MODE_COMPONENT not in plugin_modes: | 284 if C.PLUG_MODE_COMPONENT not in plugin_modes: |
285 log.error( | 285 log.error( |
286 _( | 286 _( |
287 u"{type} type must be used with {mode} mode, ignoring plugin" | 287 "{type} type must be used with {mode} mode, ignoring plugin" |
288 ).format(type=C.PLUG_TYPE_ENTRY_POINT, mode=C.PLUG_MODE_COMPONENT) | 288 ).format(type=C.PLUG_TYPE_ENTRY_POINT, mode=C.PLUG_MODE_COMPONENT) |
289 ) | 289 ) |
290 self._unimport_plugin(plugin_path) | 290 self._unimport_plugin(plugin_path) |
291 continue | 291 continue |
292 | 292 |
293 if import_name in plugins_to_import: | 293 if import_name in plugins_to_import: |
294 log.error( | 294 log.error( |
295 _( | 295 _( |
296 u"Name conflict for import name [{import_name}], can't import " | 296 "Name conflict for import name [{import_name}], can't import " |
297 u"plugin [{name}]" | 297 "plugin [{name}]" |
298 ).format(**plugin_info) | 298 ).format(**plugin_info) |
299 ) | 299 ) |
300 continue | 300 continue |
301 plugins_to_import[import_name] = (plugin_path, mod, plugin_info) | 301 plugins_to_import[import_name] = (plugin_path, mod, plugin_info) |
302 while True: | 302 while True: |
318 PLUGIN_INFO['import_name'] | 318 PLUGIN_INFO['import_name'] |
319 @param optional(bool): if False and plugin is not found, an ImportError exception | 319 @param optional(bool): if False and plugin is not found, an ImportError exception |
320 is raised | 320 is raised |
321 """ | 321 """ |
322 if import_name in self.plugins: | 322 if import_name in self.plugins: |
323 log.debug(u"Plugin {} already imported, passing".format(import_name)) | 323 log.debug("Plugin {} already imported, passing".format(import_name)) |
324 return | 324 return |
325 if not import_name: | 325 if not import_name: |
326 import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem() | 326 import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem() |
327 else: | 327 else: |
328 if not import_name in plugins_to_import: | 328 if not import_name in plugins_to_import: |
329 if optional: | 329 if optional: |
330 log.warning( | 330 log.warning( |
331 _(u"Recommended plugin not found: {}").format(import_name) | 331 _("Recommended plugin not found: {}").format(import_name) |
332 ) | 332 ) |
333 return | 333 return |
334 msg = u"Dependency not found: {}".format(import_name) | 334 msg = "Dependency not found: {}".format(import_name) |
335 log.error(msg) | 335 log.error(msg) |
336 raise ImportError(msg) | 336 raise ImportError(msg) |
337 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name) | 337 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name) |
338 dependencies = plugin_info.setdefault("dependencies", []) | 338 dependencies = plugin_info.setdefault("dependencies", []) |
339 recommendations = plugin_info.setdefault("recommendations", []) | 339 recommendations = plugin_info.setdefault("recommendations", []) |
340 for to_import in dependencies + recommendations: | 340 for to_import in dependencies + recommendations: |
341 if to_import not in self.plugins: | 341 if to_import not in self.plugins: |
342 log.debug( | 342 log.debug( |
343 u"Recursively import dependency of [%s]: [%s]" | 343 "Recursively import dependency of [%s]: [%s]" |
344 % (import_name, to_import) | 344 % (import_name, to_import) |
345 ) | 345 ) |
346 try: | 346 try: |
347 self._import_plugins_from_dict( | 347 self._import_plugins_from_dict( |
348 plugins_to_import, to_import, to_import not in dependencies | 348 plugins_to_import, to_import, to_import not in dependencies |
349 ) | 349 ) |
350 except ImportError as e: | 350 except ImportError as e: |
351 log.warning( | 351 log.warning( |
352 _(u"Can't import plugin {name}: {error}").format( | 352 _("Can't import plugin {name}: {error}").format( |
353 name=plugin_info["name"], error=e | 353 name=plugin_info["name"], error=e |
354 ) | 354 ) |
355 ) | 355 ) |
356 if optional: | 356 if optional: |
357 return | 357 return |
360 # we instanciate the plugin here | 360 # we instanciate the plugin here |
361 try: | 361 try: |
362 self.plugins[import_name] = getattr(mod, plugin_info["main"])(self) | 362 self.plugins[import_name] = getattr(mod, plugin_info["main"])(self) |
363 except Exception as e: | 363 except Exception as e: |
364 log.warning( | 364 log.warning( |
365 u'Error while loading plugin "{name}", ignoring it: {error}'.format( | 365 'Error while loading plugin "{name}", ignoring it: {error}'.format( |
366 name=plugin_info["name"], error=e | 366 name=plugin_info["name"], error=e |
367 ) | 367 ) |
368 ) | 368 ) |
369 if optional: | 369 if optional: |
370 return | 370 return |
371 raise ImportError(u"Error during initiation") | 371 raise ImportError("Error during initiation") |
372 if C.bool(plugin_info.get(C.PI_HANDLER, C.BOOL_FALSE)): | 372 if C.bool(plugin_info.get(C.PI_HANDLER, C.BOOL_FALSE)): |
373 self.plugins[import_name].is_handler = True | 373 self.plugins[import_name].is_handler = True |
374 else: | 374 else: |
375 self.plugins[import_name].is_handler = False | 375 self.plugins[import_name].is_handler = False |
376 # we keep metadata as a Class attribute | 376 # we keep metadata as a Class attribute |
384 """ | 384 """ |
385 # TODO: in the futur, it should be possible to hot unload a plugin | 385 # TODO: in the futur, it should be possible to hot unload a plugin |
386 # pluging depending on the unloaded one should be unloaded too | 386 # pluging depending on the unloaded one should be unloaded too |
387 # for now, just a basic call on plugin.unload is done | 387 # for now, just a basic call on plugin.unload is done |
388 defers_list = [] | 388 defers_list = [] |
389 for plugin in self.plugins.itervalues(): | 389 for plugin in self.plugins.values(): |
390 try: | 390 try: |
391 unload = plugin.unload | 391 unload = plugin.unload |
392 except AttributeError: | 392 except AttributeError: |
393 continue | 393 continue |
394 else: | 394 else: |
417 if options is None: | 417 if options is None: |
418 options = {} | 418 options = {} |
419 | 419 |
420 def connectProfile(__=None): | 420 def connectProfile(__=None): |
421 if self.isConnected(profile): | 421 if self.isConnected(profile): |
422 log.info(_(u"already connected !")) | 422 log.info(_("already connected !")) |
423 return True | 423 return True |
424 | 424 |
425 if self.memory.isComponent(profile): | 425 if self.memory.isComponent(profile): |
426 d = xmpp.SatXMPPComponent.startConnection(self, profile, max_retries) | 426 d = xmpp.SatXMPPComponent.startConnection(self, profile, max_retries) |
427 else: | 427 else: |
437 # FIXME: client should not be deleted if only disconnected | 437 # FIXME: client should not be deleted if only disconnected |
438 # it shoud be deleted only when session is finished | 438 # it shoud be deleted only when session is finished |
439 if not self.isConnected(profile_key): | 439 if not self.isConnected(profile_key): |
440 # isConnected is checked here and not on client | 440 # isConnected is checked here and not on client |
441 # because client is deleted when session is ended | 441 # because client is deleted when session is ended |
442 log.info(_(u"not connected !")) | 442 log.info(_("not connected !")) |
443 return defer.succeed(None) | 443 return defer.succeed(None) |
444 client = self.getClient(profile_key) | 444 client = self.getClient(profile_key) |
445 return client.entityDisconnect() | 445 return client.entityDisconnect() |
446 | 446 |
447 def getFeatures(self, profile_key=C.PROF_KEY_NONE): | 447 def getFeatures(self, profile_key=C.PROF_KEY_NONE): |
466 profile_key = C.PROF_KEY_NONE | 466 profile_key = C.PROF_KEY_NONE |
467 except exceptions.ProfileNotSetError: | 467 except exceptions.ProfileNotSetError: |
468 pass | 468 pass |
469 | 469 |
470 features = [] | 470 features = [] |
471 for import_name, plugin in self.plugins.iteritems(): | 471 for import_name, plugin in self.plugins.items(): |
472 try: | 472 try: |
473 features_d = defer.maybeDeferred(plugin.getFeatures, profile_key) | 473 features_d = defer.maybeDeferred(plugin.getFeatures, profile_key) |
474 except AttributeError: | 474 except AttributeError: |
475 features_d = defer.succeed({}) | 475 features_d = defer.succeed({}) |
476 features.append(features_d) | 476 features.append(features_d) |
483 for name, (success, data) in zip(import_names, result): | 483 for name, (success, data) in zip(import_names, result): |
484 if success: | 484 if success: |
485 ret[name] = data | 485 ret[name] = data |
486 else: | 486 else: |
487 log.warning( | 487 log.warning( |
488 u"Error while getting features for {name}: {failure}".format( | 488 "Error while getting features for {name}: {failure}".format( |
489 name=name, failure=data | 489 name=name, failure=data |
490 ) | 490 ) |
491 ) | 491 ) |
492 ret[name] = {} | 492 ret[name] = {} |
493 return ret | 493 return ret |
494 | 494 |
495 d_list.addCallback(buildFeatures, self.plugins.keys()) | 495 d_list.addCallback(buildFeatures, list(self.plugins.keys())) |
496 return d_list | 496 return d_list |
497 | 497 |
498 def getContacts(self, profile_key): | 498 def getContacts(self, profile_key): |
499 client = self.getClient(profile_key) | 499 client = self.getClient(profile_key) |
500 | 500 |
525 log.error(_("Trying to remove reference to a client not referenced")) | 525 log.error(_("Trying to remove reference to a client not referenced")) |
526 else: | 526 else: |
527 self.memory.purgeProfileSession(profile) | 527 self.memory.purgeProfileSession(profile) |
528 | 528 |
529 def startService(self): | 529 def startService(self): |
530 log.info(u"Salut à toi ô mon frère !") | 530 log.info("Salut à toi ô mon frère !") |
531 | 531 |
532 def stopService(self): | 532 def stopService(self): |
533 log.info(u"Salut aussi à Rantanplan") | 533 log.info("Salut aussi à Rantanplan") |
534 return self.pluginsUnload() | 534 return self.pluginsUnload() |
535 | 535 |
536 def run(self): | 536 def run(self): |
537 log.debug(_("running app")) | 537 log.debug(_("running app")) |
538 reactor.run() | 538 reactor.run() |
574 Manage list through profile_key like C.PROF_KEY_ALL | 574 Manage list through profile_key like C.PROF_KEY_ALL |
575 @param profile_key: %(doc_profile_key)s | 575 @param profile_key: %(doc_profile_key)s |
576 @return: list of clients | 576 @return: list of clients |
577 """ | 577 """ |
578 if not profile_key: | 578 if not profile_key: |
579 raise exceptions.DataError(_(u"profile_key must not be empty")) | 579 raise exceptions.DataError(_("profile_key must not be empty")) |
580 try: | 580 try: |
581 profile = self.memory.getProfileName(profile_key, True) | 581 profile = self.memory.getProfileName(profile_key, True) |
582 except exceptions.ProfileUnknownError: | 582 except exceptions.ProfileUnknownError: |
583 return [] | 583 return [] |
584 if profile == C.PROF_KEY_ALL: | 584 if profile == C.PROF_KEY_ALL: |
585 return self.profiles.values() | 585 return list(self.profiles.values()) |
586 elif profile[0] == "@": # only profile keys can start with "@" | 586 elif profile[0] == "@": # only profile keys can start with "@" |
587 raise exceptions.ProfileKeyUnknown | 587 raise exceptions.ProfileKeyUnknown |
588 return [self.profiles[profile]] | 588 return [self.profiles[profile]] |
589 | 589 |
590 def _getConfig(self, section, name): | 590 def _getConfig(self, section, name): |
592 | 592 |
593 @param section: section of the config file (None or '' for DEFAULT) | 593 @param section: section of the config file (None or '' for DEFAULT) |
594 @param name: name of the option | 594 @param name: name of the option |
595 @return: unicode representation of the option | 595 @return: unicode representation of the option |
596 """ | 596 """ |
597 return unicode(self.memory.getConfig(section, name, "")) | 597 return str(self.memory.getConfig(section, name, "")) |
598 | 598 |
599 def logErrback(self, failure_, msg=_(u"Unexpected error: {failure_}")): | 599 def logErrback(self, failure_, msg=_("Unexpected error: {failure_}")): |
600 """Generic errback logging | 600 """Generic errback logging |
601 | 601 |
602 @param msg(unicode): error message ("failure_" key will be use for format) | 602 @param msg(unicode): error message ("failure_" key will be use for format) |
603 can be used as last errback to show unexpected error | 603 can be used as last errback to show unexpected error |
604 """ | 604 """ |
608 # namespaces | 608 # namespaces |
609 | 609 |
610 def registerNamespace(self, short_name, namespace): | 610 def registerNamespace(self, short_name, namespace): |
611 """associate a namespace to a short name""" | 611 """associate a namespace to a short name""" |
612 if short_name in self.ns_map: | 612 if short_name in self.ns_map: |
613 raise exceptions.ConflictError(u"this short name is already used") | 613 raise exceptions.ConflictError("this short name is already used") |
614 self.ns_map[short_name] = namespace | 614 self.ns_map[short_name] = namespace |
615 | 615 |
616 def getNamespaces(self): | 616 def getNamespaces(self): |
617 return self.ns_map | 617 return self.ns_map |
618 | 618 |
619 def getNamespace(self, short_name): | 619 def getNamespace(self, short_name): |
620 try: | 620 try: |
621 return self.ns_map[short_name] | 621 return self.ns_map[short_name] |
622 except KeyError: | 622 except KeyError: |
623 raise exceptions.NotFound(u"namespace {short_name} is not registered" | 623 raise exceptions.NotFound("namespace {short_name} is not registered" |
624 .format(short_name=short_name)) | 624 .format(short_name=short_name)) |
625 | 625 |
626 def getSessionInfos(self, profile_key): | 626 def getSessionInfos(self, profile_key): |
627 """compile interesting data on current profile session""" | 627 """compile interesting data on current profile session""" |
628 client = self.getClient(profile_key) | 628 client = self.getClient(profile_key) |
629 data = { | 629 data = { |
630 "jid": client.jid.full(), | 630 "jid": client.jid.full(), |
631 "started": unicode(int(client.started)) | 631 "started": str(int(client.started)) |
632 } | 632 } |
633 return defer.succeed(data) | 633 return defer.succeed(data) |
634 | 634 |
635 # local dirs | 635 # local dirs |
636 | 636 |
712 def _encryptionPluginsGet(self): | 712 def _encryptionPluginsGet(self): |
713 plugins = encryption.EncryptionHandler.getPlugins() | 713 plugins = encryption.EncryptionHandler.getPlugins() |
714 ret = [] | 714 ret = [] |
715 for p in plugins: | 715 for p in plugins: |
716 ret.append({ | 716 ret.append({ |
717 u"name": p.name, | 717 "name": p.name, |
718 u"namespace": p.namespace, | 718 "namespace": p.namespace, |
719 u"priority": unicode(p.priority), | 719 "priority": str(p.priority), |
720 }) | 720 }) |
721 return ret | 721 return ret |
722 | 722 |
723 def _encryptionTrustUIGet(self, to_jid_s, namespace, profile_key): | 723 def _encryptionTrustUIGet(self, to_jid_s, namespace, profile_key): |
724 client = self.getClient(profile_key) | 724 client = self.getClient(profile_key) |
738 return client.sendMessage( | 738 return client.sendMessage( |
739 to_jid, | 739 to_jid, |
740 message, | 740 message, |
741 subject, | 741 subject, |
742 mess_type, | 742 mess_type, |
743 {unicode(key): unicode(value) for key, value in extra.items()}, | 743 {str(key): str(value) for key, value in list(extra.items())}, |
744 ) | 744 ) |
745 | 745 |
746 def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE): | 746 def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE): |
747 return self.setPresence(jid.JID(to) if to else None, show, statuses, profile_key) | 747 return self.setPresence(jid.JID(to) if to else None, show, statuses, profile_key) |
748 | 748 |
772 @param profile_key: profile""" | 772 @param profile_key: profile""" |
773 profile = self.memory.getProfileName(profile_key) | 773 profile = self.memory.getProfileName(profile_key) |
774 assert profile | 774 assert profile |
775 to_jid = jid.JID(raw_jid) | 775 to_jid = jid.JID(raw_jid) |
776 log.debug( | 776 log.debug( |
777 _(u"subsciption request [%(subs_type)s] for %(jid)s") | 777 _("subsciption request [%(subs_type)s] for %(jid)s") |
778 % {"subs_type": subs_type, "jid": to_jid.full()} | 778 % {"subs_type": subs_type, "jid": to_jid.full()} |
779 ) | 779 ) |
780 if subs_type == "subscribe": | 780 if subs_type == "subscribe": |
781 self.profiles[profile].presence.subscribe(to_jid) | 781 self.profiles[profile].presence.subscribe(to_jid) |
782 elif subs_type == "subscribed": | 782 elif subs_type == "subscribed": |
899 | 899 |
900 for idx, (success, infos) in enumerate(services_infos): | 900 for idx, (success, infos) in enumerate(services_infos): |
901 service_jid = services_jids[idx] | 901 service_jid = services_jids[idx] |
902 if not success: | 902 if not success: |
903 log.warning( | 903 log.warning( |
904 _(u"Can't find features for service {service_jid}, ignoring") | 904 _("Can't find features for service {service_jid}, ignoring") |
905 .format(service_jid=service_jid.full())) | 905 .format(service_jid=service_jid.full())) |
906 continue | 906 continue |
907 if (identities is not None | 907 if (identities is not None |
908 and not set(infos.identities.keys()).issuperset(identities)): | 908 and not set(infos.identities.keys()).issuperset(identities)): |
909 continue | 909 continue |
910 found_identities = [ | 910 found_identities = [ |
911 (cat, type_, name or u"") | 911 (cat, type_, name or "") |
912 for (cat, type_), name in infos.identities.iteritems() | 912 for (cat, type_), name in infos.identities.items() |
913 ] | 913 ] |
914 found_service[service_jid.full()] = found_identities | 914 found_service[service_jid.full()] = found_identities |
915 | 915 |
916 to_find = [] | 916 to_find = [] |
917 if own_jid: | 917 if own_jid: |
958 | 958 |
959 for idx, (success, infos) in enumerate(infos_data): | 959 for idx, (success, infos) in enumerate(infos_data): |
960 full_jid = full_jids[idx] | 960 full_jid = full_jids[idx] |
961 if not success: | 961 if not success: |
962 log.warning( | 962 log.warning( |
963 _(u"Can't retrieve {full_jid} infos, ignoring") | 963 _("Can't retrieve {full_jid} infos, ignoring") |
964 .format(full_jid=full_jid.full())) | 964 .format(full_jid=full_jid.full())) |
965 continue | 965 continue |
966 if infos.features.issuperset(namespaces): | 966 if infos.features.issuperset(namespaces): |
967 if identities is not None and not set( | 967 if identities is not None and not set( |
968 infos.identities.keys() | 968 infos.identities.keys() |
969 ).issuperset(identities): | 969 ).issuperset(identities): |
970 continue | 970 continue |
971 found_identities = [ | 971 found_identities = [ |
972 (cat, type_, name or u"") | 972 (cat, type_, name or "") |
973 for (cat, type_), name in infos.identities.iteritems() | 973 for (cat, type_), name in infos.identities.items() |
974 ] | 974 ] |
975 found[full_jid.full()] = found_identities | 975 found[full_jid.full()] = found_identities |
976 | 976 |
977 defer.returnValue((found_service, found_own, found_roster)) | 977 defer.returnValue((found_service, found_own, found_roster)) |
978 | 978 |
979 ## Generic HMI ## | 979 ## Generic HMI ## |
980 | 980 |
981 def _killAction(self, keep_id, client): | 981 def _killAction(self, keep_id, client): |
982 log.debug(u"Killing action {} for timeout".format(keep_id)) | 982 log.debug("Killing action {} for timeout".format(keep_id)) |
983 client.actions[keep_id] | 983 client.actions[keep_id] |
984 | 984 |
985 def actionNew( | 985 def actionNew( |
986 self, | 986 self, |
987 action_data, | 987 action_data, |
996 @param keep_id(None, unicode): if not None, used to keep action for differed | 996 @param keep_id(None, unicode): if not None, used to keep action for differed |
997 retrieval. Must be set to the callback_id. | 997 retrieval. Must be set to the callback_id. |
998 Action will be deleted after 30 min. | 998 Action will be deleted after 30 min. |
999 @param profile: %(doc_profile)s | 999 @param profile: %(doc_profile)s |
1000 """ | 1000 """ |
1001 id_ = unicode(uuid.uuid4()) | 1001 id_ = str(uuid.uuid4()) |
1002 if keep_id is not None: | 1002 if keep_id is not None: |
1003 client = self.getClient(profile) | 1003 client = self.getClient(profile) |
1004 action_timer = reactor.callLater(60 * 30, self._killAction, keep_id, client) | 1004 action_timer = reactor.callLater(60 * 30, self._killAction, keep_id, client) |
1005 client.actions[keep_id] = (action_data, id_, security_limit, action_timer) | 1005 client.actions[keep_id] = (action_data, id_, security_limit, action_timer) |
1006 | 1006 |
1010 """Return current non answered actions | 1010 """Return current non answered actions |
1011 | 1011 |
1012 @param profile: %(doc_profile)s | 1012 @param profile: %(doc_profile)s |
1013 """ | 1013 """ |
1014 client = self.getClient(profile) | 1014 client = self.getClient(profile) |
1015 return [action_tuple[:-1] for action_tuple in client.actions.itervalues()] | 1015 return [action_tuple[:-1] for action_tuple in client.actions.values()] |
1016 | 1016 |
1017 def registerProgressCb( | 1017 def registerProgressCb( |
1018 self, progress_id, callback, metadata=None, profile=C.PROF_KEY_NONE | 1018 self, progress_id, callback, metadata=None, profile=C.PROF_KEY_NONE |
1019 ): | 1019 ): |
1020 """Register a callback called when progress is requested for id""" | 1020 """Register a callback called when progress is requested for id""" |
1021 if metadata is None: | 1021 if metadata is None: |
1022 metadata = {} | 1022 metadata = {} |
1023 client = self.getClient(profile) | 1023 client = self.getClient(profile) |
1024 if progress_id in client._progress_cb: | 1024 if progress_id in client._progress_cb: |
1025 raise exceptions.ConflictError(u"Progress ID is not unique !") | 1025 raise exceptions.ConflictError("Progress ID is not unique !") |
1026 client._progress_cb[progress_id] = (callback, metadata) | 1026 client._progress_cb[progress_id] = (callback, metadata) |
1027 | 1027 |
1028 def removeProgressCb(self, progress_id, profile): | 1028 def removeProgressCb(self, progress_id, profile): |
1029 """Remove a progress callback""" | 1029 """Remove a progress callback""" |
1030 client = self.getClient(profile) | 1030 client = self.getClient(profile) |
1031 try: | 1031 try: |
1032 del client._progress_cb[progress_id] | 1032 del client._progress_cb[progress_id] |
1033 except KeyError: | 1033 except KeyError: |
1034 log.error(_(u"Trying to remove an unknow progress callback")) | 1034 log.error(_("Trying to remove an unknow progress callback")) |
1035 | 1035 |
1036 def _progressGet(self, progress_id, profile): | 1036 def _progressGet(self, progress_id, profile): |
1037 data = self.progressGet(progress_id, profile) | 1037 data = self.progressGet(progress_id, profile) |
1038 return {k: unicode(v) for k, v in data.iteritems()} | 1038 return {k: str(v) for k, v in data.items()} |
1039 | 1039 |
1040 def progressGet(self, progress_id, profile): | 1040 def progressGet(self, progress_id, profile): |
1041 """Return a dict with progress information | 1041 """Return a dict with progress information |
1042 | 1042 |
1043 @param progress_id(unicode): unique id of the progressing element | 1043 @param progress_id(unicode): unique id of the progressing element |
1055 data = {} | 1055 data = {} |
1056 return data | 1056 return data |
1057 | 1057 |
1058 def _progressGetAll(self, profile_key): | 1058 def _progressGetAll(self, profile_key): |
1059 progress_all = self.progressGetAll(profile_key) | 1059 progress_all = self.progressGetAll(profile_key) |
1060 for profile, progress_dict in progress_all.iteritems(): | 1060 for profile, progress_dict in progress_all.items(): |
1061 for progress_id, data in progress_dict.iteritems(): | 1061 for progress_id, data in progress_dict.items(): |
1062 for key, value in data.iteritems(): | 1062 for key, value in data.items(): |
1063 data[key] = unicode(value) | 1063 data[key] = str(value) |
1064 return progress_all | 1064 return progress_all |
1065 | 1065 |
1066 def progressGetAllMetadata(self, profile_key): | 1066 def progressGetAllMetadata(self, profile_key): |
1067 """Return all progress metadata at once | 1067 """Return all progress metadata at once |
1068 | 1068 |
1080 progress_dict = {} | 1080 progress_dict = {} |
1081 progress_all[profile] = progress_dict | 1081 progress_all[profile] = progress_dict |
1082 for ( | 1082 for ( |
1083 progress_id, | 1083 progress_id, |
1084 (__, progress_metadata), | 1084 (__, progress_metadata), |
1085 ) in client._progress_cb.iteritems(): | 1085 ) in client._progress_cb.items(): |
1086 progress_dict[progress_id] = progress_metadata | 1086 progress_dict[progress_id] = progress_metadata |
1087 return progress_all | 1087 return progress_all |
1088 | 1088 |
1089 def progressGetAll(self, profile_key): | 1089 def progressGetAll(self, profile_key): |
1090 """Return all progress status at once | 1090 """Return all progress status at once |
1099 progress_all = {} | 1099 progress_all = {} |
1100 for client in clients: | 1100 for client in clients: |
1101 profile = client.profile | 1101 profile = client.profile |
1102 progress_dict = {} | 1102 progress_dict = {} |
1103 progress_all[profile] = progress_dict | 1103 progress_all[profile] = progress_dict |
1104 for progress_id, (progress_cb, __) in client._progress_cb.iteritems(): | 1104 for progress_id, (progress_cb, __) in client._progress_cb.items(): |
1105 progress_dict[progress_id] = progress_cb(progress_id, profile) | 1105 progress_dict[progress_id] = progress_cb(progress_id, profile) |
1106 return progress_all | 1106 return progress_all |
1107 | 1107 |
1108 def registerCallback(self, callback, *args, **kwargs): | 1108 def registerCallback(self, callback, *args, **kwargs): |
1109 """Register a callback. | 1109 """Register a callback. |
1119 callback_id = kwargs.pop("force_id", None) | 1119 callback_id = kwargs.pop("force_id", None) |
1120 if callback_id is None: | 1120 if callback_id is None: |
1121 callback_id = str(uuid.uuid4()) | 1121 callback_id = str(uuid.uuid4()) |
1122 else: | 1122 else: |
1123 if callback_id in self._cb_map: | 1123 if callback_id in self._cb_map: |
1124 raise exceptions.ConflictError(_(u"id already registered")) | 1124 raise exceptions.ConflictError(_("id already registered")) |
1125 self._cb_map[callback_id] = (callback, args, kwargs) | 1125 self._cb_map[callback_id] = (callback, args, kwargs) |
1126 | 1126 |
1127 if "one_shot" in kwargs: # One Shot callback are removed after 30 min | 1127 if "one_shot" in kwargs: # One Shot callback are removed after 30 min |
1128 | 1128 |
1129 def purgeCallback(): | 1129 def purgeCallback(): |
1161 except exceptions.NotFound: | 1161 except exceptions.NotFound: |
1162 # client is not available yet | 1162 # client is not available yet |
1163 profile = self.memory.getProfileName(profile_key) | 1163 profile = self.memory.getProfileName(profile_key) |
1164 if not profile: | 1164 if not profile: |
1165 raise exceptions.ProfileUnknownError( | 1165 raise exceptions.ProfileUnknownError( |
1166 _(u"trying to launch action with a non-existant profile") | 1166 _("trying to launch action with a non-existant profile") |
1167 ) | 1167 ) |
1168 else: | 1168 else: |
1169 profile = client.profile | 1169 profile = client.profile |
1170 # we check if the action is kept, and remove it | 1170 # we check if the action is kept, and remove it |
1171 try: | 1171 try: |
1177 del client.actions[callback_id] | 1177 del client.actions[callback_id] |
1178 | 1178 |
1179 try: | 1179 try: |
1180 callback, args, kwargs = self._cb_map[callback_id] | 1180 callback, args, kwargs = self._cb_map[callback_id] |
1181 except KeyError: | 1181 except KeyError: |
1182 raise exceptions.DataError(u"Unknown callback id {}".format(callback_id)) | 1182 raise exceptions.DataError("Unknown callback id {}".format(callback_id)) |
1183 | 1183 |
1184 if kwargs.get("with_data", False): | 1184 if kwargs.get("with_data", False): |
1185 if data is None: | 1185 if data is None: |
1186 raise exceptions.DataError("Required data for this callback is missing") | 1186 raise exceptions.DataError("Required data for this callback is missing") |
1187 args, kwargs = ( | 1187 args, kwargs = ( |
1208 """ | 1208 """ |
1209 return tuple((p.lower().strip() for p in path)) | 1209 return tuple((p.lower().strip() for p in path)) |
1210 | 1210 |
1211 def importMenu(self, path, callback, security_limit=C.NO_SECURITY_LIMIT, | 1211 def importMenu(self, path, callback, security_limit=C.NO_SECURITY_LIMIT, |
1212 help_string="", type_=C.MENU_GLOBAL): | 1212 help_string="", type_=C.MENU_GLOBAL): |
1213 """register a new menu for frontends | 1213 r"""register a new menu for frontends |
1214 | 1214 |
1215 @param path(iterable[unicode]): path to go to the menu | 1215 @param path(iterable[unicode]): path to go to the menu |
1216 (category/subcategory/.../item) (e.g.: ("File", "Open")) | 1216 (category/subcategory/.../item) (e.g.: ("File", "Open")) |
1217 /!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open"))) | 1217 /!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open"))) |
1218 untranslated/lower case path can be used to identity a menu, for this reason | 1218 untranslated/lower case path can be used to identity a menu, for this reason |
1243 @return (unicode): menu_id (same as callback_id) | 1243 @return (unicode): menu_id (same as callback_id) |
1244 """ | 1244 """ |
1245 | 1245 |
1246 if callable(callback): | 1246 if callable(callback): |
1247 callback_id = self.registerCallback(callback, with_data=True) | 1247 callback_id = self.registerCallback(callback, with_data=True) |
1248 elif isinstance(callback, basestring): | 1248 elif isinstance(callback, str): |
1249 # The callback is already registered | 1249 # The callback is already registered |
1250 callback_id = callback | 1250 callback_id = callback |
1251 try: | 1251 try: |
1252 callback, args, kwargs = self._cb_map[callback_id] | 1252 callback, args, kwargs = self._cb_map[callback_id] |
1253 except KeyError: | 1253 except KeyError: |
1254 raise exceptions.DataError("Unknown callback id") | 1254 raise exceptions.DataError("Unknown callback id") |
1255 kwargs["with_data"] = True # we have to be sure that we use extra data | 1255 kwargs["with_data"] = True # we have to be sure that we use extra data |
1256 else: | 1256 else: |
1257 raise exceptions.DataError("Unknown callback type") | 1257 raise exceptions.DataError("Unknown callback type") |
1258 | 1258 |
1259 for menu_data in self._menus.itervalues(): | 1259 for menu_data in self._menus.values(): |
1260 if menu_data["path"] == path and menu_data["type"] == type_: | 1260 if menu_data["path"] == path and menu_data["type"] == type_: |
1261 raise exceptions.ConflictError( | 1261 raise exceptions.ConflictError( |
1262 _("A menu with the same path and type already exists") | 1262 _("A menu with the same path and type already exists") |
1263 ) | 1263 ) |
1264 | 1264 |
1265 path_canonical = self._getMenuCanonicalPath(path) | 1265 path_canonical = self._getMenuCanonicalPath(path) |
1266 menu_key = (type_, path_canonical) | 1266 menu_key = (type_, path_canonical) |
1267 | 1267 |
1268 if menu_key in self._menus_paths: | 1268 if menu_key in self._menus_paths: |
1269 raise exceptions.ConflictError( | 1269 raise exceptions.ConflictError( |
1270 u"this menu path is already used: {path} ({menu_key})".format( | 1270 "this menu path is already used: {path} ({menu_key})".format( |
1271 path=path_canonical, menu_key=menu_key | 1271 path=path_canonical, menu_key=menu_key |
1272 ) | 1272 ) |
1273 ) | 1273 ) |
1274 | 1274 |
1275 menu_data = { | 1275 menu_data = { |
1298 - extra (dict(unicode, unicode)): extra data where key can be: | 1298 - extra (dict(unicode, unicode)): extra data where key can be: |
1299 - icon: name of the icon to use (TODO) | 1299 - icon: name of the icon to use (TODO) |
1300 - help_url: link to a page with more complete documentation (TODO) | 1300 - help_url: link to a page with more complete documentation (TODO) |
1301 """ | 1301 """ |
1302 ret = [] | 1302 ret = [] |
1303 for menu_id, menu_data in self._menus.iteritems(): | 1303 for menu_id, menu_data in self._menus.items(): |
1304 type_ = menu_data["type"] | 1304 type_ = menu_data["type"] |
1305 path = menu_data["path"] | 1305 path = menu_data["path"] |
1306 menu_security_limit = menu_data["security_limit"] | 1306 menu_security_limit = menu_data["security_limit"] |
1307 if security_limit != C.NO_SECURITY_LIMIT and ( | 1307 if security_limit != C.NO_SECURITY_LIMIT and ( |
1308 menu_security_limit == C.NO_SECURITY_LIMIT | 1308 menu_security_limit == C.NO_SECURITY_LIMIT |
1337 menu_key = (menu_type, canonical_path) | 1337 menu_key = (menu_type, canonical_path) |
1338 try: | 1338 try: |
1339 callback_id = self._menus_paths[menu_key] | 1339 callback_id = self._menus_paths[menu_key] |
1340 except KeyError: | 1340 except KeyError: |
1341 raise exceptions.NotFound( | 1341 raise exceptions.NotFound( |
1342 u"Can't find menu {path} ({menu_type})".format( | 1342 "Can't find menu {path} ({menu_type})".format( |
1343 path=canonical_path, menu_type=menu_type | 1343 path=canonical_path, menu_type=menu_type |
1344 ) | 1344 ) |
1345 ) | 1345 ) |
1346 return self.launchCallback(callback_id, data, client.profile) | 1346 return self.launchCallback(callback_id, data, client.profile) |
1347 | 1347 |