comparison libervia/backend/core/main.py @ 4270:0d7bb4df2343

Reformatted code base using black.
author Goffi <goffi@goffi.org>
date Wed, 19 Jun 2024 18:44:57 +0200
parents 9fc3d28bc3f6
children
comparison
equal deleted inserted replaced
4269:64a85ce8be70 4270:0d7bb4df2343
27 27
28 from wokkel.data_form import Option 28 from wokkel.data_form import Option
29 from libervia import backend 29 from libervia import backend
30 from libervia.backend.core.i18n import _, D_, language_switch 30 from libervia.backend.core.i18n import _, D_, language_switch
31 from libervia.backend.core import patches 31 from libervia.backend.core import patches
32
32 patches.apply() 33 patches.apply()
33 from twisted.application import service 34 from twisted.application import service
34 from twisted.internet import defer 35 from twisted.internet import defer
35 from twisted.words.protocols.jabber import jid 36 from twisted.words.protocols.jabber import jid
36 from twisted.internet import reactor 37 from twisted.internet import reactor
55 import libervia.backend.plugins 56 import libervia.backend.plugins
56 57
57 58
58 log = getLogger(__name__) 59 log = getLogger(__name__)
59 60
61
60 class LiberviaBackend(service.Service): 62 class LiberviaBackend(service.Service):
61 63
62 def _init(self): 64 def _init(self):
63 # we don't use __init__ to avoid doule initialisation with twistd 65 # we don't use __init__ to avoid doule initialisation with twistd
64 # this _init is called in startService 66 # this _init is called in startService
65 log.info(f"{C.APP_NAME} {self.full_version}") 67 log.info(f"{C.APP_NAME} {self.full_version}")
66 self._cb_map = {} # map from callback_id to callbacks 68 self._cb_map = {} # map from callback_id to callbacks
67 # dynamic menus. key: callback_id, value: menu data (dictionnary) 69 # dynamic menus. key: callback_id, value: menu data (dictionnary)
68 self._menus = {} 70 self._menus = {}
69 self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), 71 self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path),
70 # value: menu id 72 # value: menu id
71 73
72 # like initialised, but launched before init script is done, mainly useful for CLI 74 # like initialised, but launched before init script is done, mainly useful for CLI
73 # frontend, so it can be used in init script, while other frontends are waiting. 75 # frontend, so it can be used in init script, while other frontends are waiting.
74 self.init_pre_script = defer.Deferred() 76 self.init_pre_script = defer.Deferred()
75 self.initialised = defer.Deferred() 77 self.initialised = defer.Deferred()
76 self.profiles = {} 78 self.profiles = {}
77 self.plugins = {} 79 self.plugins = {}
78 # map for short name to whole namespace, 80 # map for short name to whole namespace,
79 # extended by plugins with register_namespace 81 # extended by plugins with register_namespace
80 self.ns_map = { 82 self.ns_map = {
81 "x-data": xmpp.NS_X_DATA, 83 "x-data": xmpp.NS_X_DATA,
82 "disco#info": xmpp.NS_DISCO_INFO, 84 "disco#info": xmpp.NS_DISCO_INFO,
83 } 85 }
84 86
85 self.memory = memory.Memory(self) 87 self.memory = memory.Memory(self)
86 88
87 # trigger are used to change Libervia behaviour 89 # trigger are used to change Libervia behaviour
88 self.trigger = ( 90 self.trigger = trigger.TriggerManager()
89 trigger.TriggerManager() 91
90 ) 92 bridge_name = os.getenv("LIBERVIA_BRIDGE_NAME") or self.memory.config_get(
91 93 "", "bridge", "dbus"
92 bridge_name = (
93 os.getenv("LIBERVIA_BRIDGE_NAME")
94 or self.memory.config_get("", "bridge", "dbus")
95 ) 94 )
96 95
97 bridge_module = dynamic_import.bridge(bridge_name) 96 bridge_module = dynamic_import.bridge(bridge_name)
98 if bridge_module is None: 97 if bridge_module is None:
99 log.error(f"Can't find bridge module of name {bridge_name}") 98 log.error(f"Can't find bridge module of name {bridge_name}")
117 ) 116 )
118 shutil.rmtree(self.local_shared_path, ignore_errors=True) 117 shutil.rmtree(self.local_shared_path, ignore_errors=True)
119 self.local_shared_path.mkdir(0o700, parents=True, exist_ok=True) 118 self.local_shared_path.mkdir(0o700, parents=True, exist_ok=True)
120 else: 119 else:
121 self.local_shared_path = None 120 self.local_shared_path = None
122
123 121
124 defer.ensureDeferred(self._post_init()) 122 defer.ensureDeferred(self._post_init())
125 123
126 @property 124 @property
127 def version(self): 125 def version(self):
172 self.bridge.register_method("profile_name_get", self.memory.get_profile_name) 170 self.bridge.register_method("profile_name_get", self.memory.get_profile_name)
173 self.bridge.register_method("profiles_list_get", self.memory.get_profiles_list) 171 self.bridge.register_method("profiles_list_get", self.memory.get_profiles_list)
174 self.bridge.register_method("entity_data_get", self.memory._get_entity_data) 172 self.bridge.register_method("entity_data_get", self.memory._get_entity_data)
175 self.bridge.register_method("entities_data_get", self.memory._get_entities_data) 173 self.bridge.register_method("entities_data_get", self.memory._get_entities_data)
176 self.bridge.register_method("profile_create", self.memory.create_profile) 174 self.bridge.register_method("profile_create", self.memory.create_profile)
177 self.bridge.register_method("profile_delete_async", self.memory.profile_delete_async) 175 self.bridge.register_method(
176 "profile_delete_async", self.memory.profile_delete_async
177 )
178 self.bridge.register_method("profile_start_session", self.memory.start_session) 178 self.bridge.register_method("profile_start_session", self.memory.start_session)
179 self.bridge.register_method( 179 self.bridge.register_method(
180 "profile_is_session_started", self.memory._is_session_started 180 "profile_is_session_started", self.memory._is_session_started
181 ) 181 )
182 self.bridge.register_method("profile_set_default", self.memory.profile_set_default) 182 self.bridge.register_method(
183 "profile_set_default", self.memory.profile_set_default
184 )
183 self.bridge.register_method("connect", self._connect) 185 self.bridge.register_method("connect", self._connect)
184 self.bridge.register_method("disconnect", self.disconnect) 186 self.bridge.register_method("disconnect", self.disconnect)
185 self.bridge.register_method("contact_get", self._contact_get) 187 self.bridge.register_method("contact_get", self._contact_get)
186 self.bridge.register_method("contacts_get", self.contacts_get) 188 self.bridge.register_method("contacts_get", self.contacts_get)
187 self.bridge.register_method("contacts_get_from_group", self.contacts_get_from_group) 189 self.bridge.register_method(
190 "contacts_get_from_group", self.contacts_get_from_group
191 )
188 self.bridge.register_method("main_resource_get", self.memory._get_main_resource) 192 self.bridge.register_method("main_resource_get", self.memory._get_main_resource)
189 self.bridge.register_method( 193 self.bridge.register_method(
190 "presence_statuses_get", self.memory._get_presence_statuses 194 "presence_statuses_get", self.memory._get_presence_statuses
191 ) 195 )
192 self.bridge.register_method("sub_waiting_get", self.memory.sub_waiting_get) 196 self.bridge.register_method("sub_waiting_get", self.memory.sub_waiting_get)
193 self.bridge.register_method("message_send", self._message_send) 197 self.bridge.register_method("message_send", self._message_send)
194 self.bridge.register_method("message_encryption_start", 198 self.bridge.register_method(
195 self._message_encryption_start) 199 "message_encryption_start", self._message_encryption_start
196 self.bridge.register_method("message_encryption_stop", 200 )
197 self._message_encryption_stop) 201 self.bridge.register_method(
198 self.bridge.register_method("message_encryption_get", 202 "message_encryption_stop", self._message_encryption_stop
199 self._message_encryption_get) 203 )
200 self.bridge.register_method("encryption_namespace_get", 204 self.bridge.register_method(
201 self._encryption_namespace_get) 205 "message_encryption_get", self._message_encryption_get
202 self.bridge.register_method("encryption_plugins_get", self._encryption_plugins_get) 206 )
203 self.bridge.register_method("encryption_trust_ui_get", self._encryption_trust_ui_get) 207 self.bridge.register_method(
208 "encryption_namespace_get", self._encryption_namespace_get
209 )
210 self.bridge.register_method(
211 "encryption_plugins_get", self._encryption_plugins_get
212 )
213 self.bridge.register_method(
214 "encryption_trust_ui_get", self._encryption_trust_ui_get
215 )
204 self.bridge.register_method("config_get", self._get_config) 216 self.bridge.register_method("config_get", self._get_config)
205 self.bridge.register_method("param_set", self.param_set) 217 self.bridge.register_method("param_set", self.param_set)
206 self.bridge.register_method("param_get_a", self.memory.get_string_param_a) 218 self.bridge.register_method("param_get_a", self.memory.get_string_param_a)
207 self.bridge.register_method("private_data_get", self.memory._private_data_get) 219 self.bridge.register_method("private_data_get", self.memory._private_data_get)
208 self.bridge.register_method("private_data_set", self.memory._private_data_set) 220 self.bridge.register_method("private_data_set", self.memory._private_data_set)
209 self.bridge.register_method("private_data_delete", self.memory._private_data_delete) 221 self.bridge.register_method(
210 self.bridge.register_method("param_get_a_async", self.memory.async_get_string_param_a) 222 "private_data_delete", self.memory._private_data_delete
223 )
224 self.bridge.register_method(
225 "param_get_a_async", self.memory.async_get_string_param_a
226 )
211 self.bridge.register_method( 227 self.bridge.register_method(
212 "params_values_from_category_get_async", 228 "params_values_from_category_get_async",
213 self.memory._get_params_values_from_category, 229 self.memory._get_params_values_from_category,
214 ) 230 )
215 self.bridge.register_method("param_ui_get", self.memory._get_params_ui) 231 self.bridge.register_method("param_ui_get", self.memory._get_params_ui)
216 self.bridge.register_method( 232 self.bridge.register_method(
217 "params_categories_get", self.memory.params_categories_get 233 "params_categories_get", self.memory.params_categories_get
218 ) 234 )
219 self.bridge.register_method("params_register_app", self.memory.params_register_app) 235 self.bridge.register_method(
236 "params_register_app", self.memory.params_register_app
237 )
220 self.bridge.register_method("history_get", self.memory._history_get) 238 self.bridge.register_method("history_get", self.memory._history_get)
221 self.bridge.register_method("presence_set", self._set_presence) 239 self.bridge.register_method("presence_set", self._set_presence)
222 self.bridge.register_method("subscription", self.subscription) 240 self.bridge.register_method("subscription", self.subscription)
223 self.bridge.register_method("contact_add", self._add_contact) 241 self.bridge.register_method("contact_add", self._add_contact)
224 self.bridge.register_method("contact_update", self._update_contact) 242 self.bridge.register_method("contact_update", self._update_contact)
240 self.bridge.register_method("session_infos_get", self.get_session_infos) 258 self.bridge.register_method("session_infos_get", self.get_session_infos)
241 self.bridge.register_method("devices_infos_get", self._get_devices_infos) 259 self.bridge.register_method("devices_infos_get", self._get_devices_infos)
242 self.bridge.register_method("namespaces_get", self.get_namespaces) 260 self.bridge.register_method("namespaces_get", self.get_namespaces)
243 self.bridge.register_method("image_check", self._image_check) 261 self.bridge.register_method("image_check", self._image_check)
244 self.bridge.register_method("image_resize", self._image_resize) 262 self.bridge.register_method("image_resize", self._image_resize)
245 self.bridge.register_method("image_generate_preview", self._image_generate_preview) 263 self.bridge.register_method(
264 "image_generate_preview", self._image_generate_preview
265 )
246 self.bridge.register_method("image_convert", self._image_convert) 266 self.bridge.register_method("image_convert", self._image_convert)
247 self.bridge.register_method("notification_add", self.memory._add_notification) 267 self.bridge.register_method("notification_add", self.memory._add_notification)
248 self.bridge.register_method("notifications_get", self.memory._get_notifications) 268 self.bridge.register_method("notifications_get", self.memory._get_notifications)
249 self.bridge.register_method("notification_delete", self.memory._delete_notification) 269 self.bridge.register_method(
250 self.bridge.register_method("notifications_expired_clean", self.memory._notifications_expired_clean) 270 "notification_delete", self.memory._delete_notification
251 271 )
272 self.bridge.register_method(
273 "notifications_expired_clean", self.memory._notifications_expired_clean
274 )
252 275
253 await self.memory.initialise() 276 await self.memory.initialise()
254 self.common_cache = cache.Cache(self, None) 277 self.common_cache = cache.Cache(self, None)
255 log.info(_("Memory initialised")) 278 log.info(_("Memory initialised"))
256 try: 279 try:
275 log.error(f"Init script is not a file: {init_script}") 298 log.error(f"Init script is not a file: {init_script}")
276 sys.exit(C.EXIT_BAD_ARG) 299 sys.exit(C.EXIT_BAD_ARG)
277 else: 300 else:
278 log.info(f"Running init script {init_script!r}.") 301 log.info(f"Running init script {init_script!r}.")
279 try: 302 try:
280 await async_process.run( 303 await async_process.run(str(init_script), verbose=True)
281 str(init_script),
282 verbose=True
283 )
284 except RuntimeError as e: 304 except RuntimeError as e:
285 log.error(f"Init script failed: {e}") 305 log.error(f"Init script failed: {e}")
286 self.stopService() 306 self.stopService()
287 sys.exit(C.EXIT_ERROR) 307 sys.exit(C.EXIT_ERROR)
288 308
290 log.info(_("Backend is ready")) 310 log.info(_("Backend is ready"))
291 311
292 # profile autoconnection must be done after self.initialised is called because 312 # profile autoconnection must be done after self.initialised is called because
293 # start_session waits for it. 313 # start_session waits for it.
294 autoconnect_dict = await self.memory.storage.get_ind_param_values( 314 autoconnect_dict = await self.memory.storage.get_ind_param_values(
295 category='Connection', name='autoconnect_backend', 315 category="Connection",
316 name="autoconnect_backend",
296 ) 317 )
297 profiles_autoconnect = [p for p, v in autoconnect_dict.items() if C.bool(v)] 318 profiles_autoconnect = [p for p, v in autoconnect_dict.items() if C.bool(v)]
298 if not self.trigger.point("profilesAutoconnect", profiles_autoconnect): 319 if not self.trigger.point("profilesAutoconnect", profiles_autoconnect):
299 return 320 return
300 if profiles_autoconnect: 321 if profiles_autoconnect:
301 log.info(D_( 322 log.info(
302 "Following profiles will be connected automatically: {profiles}" 323 D_(
303 ).format(profiles= ', '.join(profiles_autoconnect))) 324 "Following profiles will be connected automatically: {profiles}"
325 ).format(profiles=", ".join(profiles_autoconnect))
326 )
304 connect_d_list = [] 327 connect_d_list = []
305 for profile in profiles_autoconnect: 328 for profile in profiles_autoconnect:
306 connect_d_list.append(defer.ensureDeferred(self.connect(profile))) 329 connect_d_list.append(defer.ensureDeferred(self.connect(profile)))
307 330
308 if connect_d_list: 331 if connect_d_list:
310 for idx, (success, result) in enumerate(results): 333 for idx, (success, result) in enumerate(results):
311 if not success: 334 if not success:
312 profile = profiles_autoconnect[0] 335 profile = profiles_autoconnect[0]
313 log.warning( 336 log.warning(
314 _("Can't autoconnect profile {profile}: {reason}").format( 337 _("Can't autoconnect profile {profile}: {reason}").format(
315 profile = profile, 338 profile=profile, reason=result
316 reason = result) 339 )
317 ) 340 )
318 341
319 def _add_base_menus(self): 342 def _add_base_menus(self):
320 """Add base menus""" 343 """Add base menus"""
321 encryption.EncryptionHandler._import_menus(self) 344 encryption.EncryptionHandler._import_menus(self)
340 for plug_path in plugins_path.glob("plugin_*"): 363 for plug_path in plugins_path.glob("plugin_*"):
341 if plug_path.is_dir(): 364 if plug_path.is_dir():
342 init_path = plug_path / f"__init__.{C.PLUGIN_EXT}" 365 init_path = plug_path / f"__init__.{C.PLUGIN_EXT}"
343 if not init_path.exists(): 366 if not init_path.exists():
344 log.warning( 367 log.warning(
345 f"{plug_path} doesn't appear to be a package, can't load it") 368 f"{plug_path} doesn't appear to be a package, can't load it"
369 )
346 continue 370 continue
347 plug_name = plug_path.name 371 plug_name = plug_path.name
348 elif plug_path.is_file(): 372 elif plug_path.is_file():
349 if plug_path.suffix != f".{C.PLUGIN_EXT}": 373 if plug_path.suffix != f".{C.PLUGIN_EXT}":
350 continue 374 continue
351 plug_name = plug_path.stem 375 plug_name = plug_path.stem
352 else: 376 else:
353 log.warning( 377 log.warning(f"{plug_path} is not a file or a dir, ignoring it")
354 f"{plug_path} is not a file or a dir, ignoring it")
355 continue 378 continue
356 if not plug_name.isidentifier(): 379 if not plug_name.isidentifier():
357 log.warning( 380 log.warning(
358 f"{plug_name!r} is not a valid name for a plugin, ignoring it") 381 f"{plug_name!r} is not a valid name for a plugin, ignoring it"
382 )
359 continue 383 continue
360 plugin_path = f"libervia.backend.plugins.{plug_name}" 384 plugin_path = f"libervia.backend.plugins.{plug_name}"
361 try: 385 try:
362 __import__(plugin_path) 386 __import__(plugin_path)
363 except exceptions.MissingModule as e: 387 except exceptions.MissingModule as e:
364 self._unimport_plugin(plugin_path) 388 self._unimport_plugin(plugin_path)
365 log.warning( 389 log.warning(
366 "Can't import plugin [{path}] because of an unavailale third party " 390 "Can't import plugin [{path}] because of an unavailale third party "
367 "module:\n{msg}".format( 391 "module:\n{msg}".format(path=plugin_path, msg=e)
368 path=plugin_path, msg=e
369 )
370 ) 392 )
371 continue 393 continue
372 except exceptions.CancelError as e: 394 except exceptions.CancelError as e:
373 log.info( 395 log.info(
374 "Plugin [{path}] cancelled its own import: {msg}".format( 396 "Plugin [{path}] cancelled its own import: {msg}".format(
448 if not import_name: 470 if not import_name:
449 import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem() 471 import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem()
450 else: 472 else:
451 if not import_name in plugins_to_import: 473 if not import_name in plugins_to_import:
452 if optional: 474 if optional:
453 log.warning( 475 log.warning(_("Recommended plugin not found: {}").format(import_name))
454 _("Recommended plugin not found: {}").format(import_name)
455 )
456 return 476 return
457 msg = "Dependency not found: {}".format(import_name) 477 msg = "Dependency not found: {}".format(import_name)
458 log.error(msg) 478 log.error(msg)
459 raise ImportError(msg) 479 raise ImportError(msg)
460 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name) 480 plugin_path, mod, plugin_info = plugins_to_import.pop(import_name)
519 def _connect(self, profile_key, password="", options=None): 539 def _connect(self, profile_key, password="", options=None):
520 profile = self.memory.get_profile_name(profile_key) 540 profile = self.memory.get_profile_name(profile_key)
521 return defer.ensureDeferred(self.connect(profile, password, options)) 541 return defer.ensureDeferred(self.connect(profile, password, options))
522 542
523 async def connect( 543 async def connect(
524 self, profile, password="", options=None, max_retries=C.XMPP_MAX_RETRIES): 544 self, profile, password="", options=None, max_retries=C.XMPP_MAX_RETRIES
545 ):
525 """Connect a profile (i.e. connect client.component to XMPP server) 546 """Connect a profile (i.e. connect client.component to XMPP server)
526 547
527 Retrieve the individual parameters, authenticate the profile 548 Retrieve the individual parameters, authenticate the profile
528 and initiate the connection to the associated XMPP server. 549 and initiate the connection to the associated XMPP server.
529 @param profile: %(doc_profile)s 550 @param profile: %(doc_profile)s
759 780
760 def get_namespace(self, short_name): 781 def get_namespace(self, short_name):
761 try: 782 try:
762 return self.ns_map[short_name] 783 return self.ns_map[short_name]
763 except KeyError: 784 except KeyError:
764 raise exceptions.NotFound("namespace {short_name} is not registered" 785 raise exceptions.NotFound(
765 .format(short_name=short_name)) 786 "namespace {short_name} is not registered".format(short_name=short_name)
787 )
766 788
767 def get_session_infos(self, profile_key): 789 def get_session_infos(self, profile_key):
768 """compile interesting data on current profile session""" 790 """compile interesting data on current profile session"""
769 client = self.get_client(profile_key) 791 client = self.get_client(profile_key)
770 data = { 792 data = {"jid": client.jid.full(), "started": str(int(client.started))}
771 "jid": client.jid.full(),
772 "started": str(int(client.started))
773 }
774 return defer.succeed(data) 793 return defer.succeed(data)
775 794
776 def _get_devices_infos(self, bare_jid, profile_key): 795 def _get_devices_infos(self, bare_jid, profile_key):
777 client = self.get_client(profile_key) 796 client = self.get_client(profile_key)
778 if not bare_jid: 797 if not bare_jid:
806 cache_data = self.memory.entity_data_get(client, res_jid) 825 cache_data = self.memory.entity_data_get(client, res_jid)
807 res_data = { 826 res_data = {
808 "resource": resource, 827 "resource": resource,
809 } 828 }
810 try: 829 try:
811 presence = cache_data['presence'] 830 presence = cache_data["presence"]
812 except KeyError: 831 except KeyError:
813 pass 832 pass
814 else: 833 else:
815 res_data['presence'] = { 834 res_data["presence"] = {
816 "show": presence.show, 835 "show": presence.show,
817 "priority": presence.priority, 836 "priority": presence.priority,
818 "statuses": presence.statuses, 837 "statuses": presence.statuses,
819 } 838 }
820 839
821 disco = await self.get_disco_infos(client, res_jid) 840 disco = await self.get_disco_infos(client, res_jid)
822 841
823 for (category, type_), name in disco.identities.items(): 842 for (category, type_), name in disco.identities.items():
824 identities = res_data.setdefault('identities', []) 843 identities = res_data.setdefault("identities", [])
825 identities.append({ 844 identities.append(
826 "name": name, 845 {
827 "category": category, 846 "name": name,
828 "type": type_, 847 "category": category,
829 }) 848 "type": type_,
849 }
850 )
830 851
831 ret_data.append(res_data) 852 ret_data.append(res_data)
832 853
833 return ret_data 854 return ret_data
834 855
855 @param path(Path): path to the image 876 @param path(Path): path to the image
856 @return (Path): path to the generated preview 877 @return (Path): path to the generated preview
857 """ 878 """
858 report = image.check(self, path, max_size=(300, 300)) 879 report = image.check(self, path, max_size=(300, 300))
859 880
860 if not report['too_large']: 881 if not report["too_large"]:
861 # in the unlikely case that image is already smaller than a preview 882 # in the unlikely case that image is already smaller than a preview
862 preview_path = path 883 preview_path = path
863 else: 884 else:
864 # we use hash as id, to re-use potentially existing preview 885 # we use hash as id, to re-use potentially existing preview
865 path_hash = hashlib.sha256(str(path).encode()).hexdigest() 886 path_hash = hashlib.sha256(str(path).encode()).hexdigest()
866 uid = f"{path.stem}_{path_hash}_preview" 887 uid = f"{path.stem}_{path_hash}_preview"
867 filename = f"{uid}{path.suffix.lower()}" 888 filename = f"{uid}{path.suffix.lower()}"
868 metadata = client.cache.get_metadata(uid=uid) 889 metadata = client.cache.get_metadata(uid=uid)
869 if metadata is not None: 890 if metadata is not None:
870 preview_path = metadata['path'] 891 preview_path = metadata["path"]
871 else: 892 else:
872 with client.cache.cache_data( 893 with client.cache.cache_data(
873 source='HOST_PREVIEW', 894 source="HOST_PREVIEW", uid=uid, filename=filename
874 uid=uid, 895 ) as cache_f:
875 filename=filename) as cache_f:
876 896
877 preview_path = await image.resize( 897 preview_path = await image.resize(
878 path, 898 path, new_size=report["recommended_size"], dest=cache_f
879 new_size=report['recommended_size'],
880 dest=cache_f
881 ) 899 )
882 900
883 return preview_path 901 return preview_path
884 902
885 def _image_convert(self, source, dest, extra, profile_key): 903 def _image_convert(self, source, dest, extra, profile_key):
920 else: 938 else:
921 cache = client.cache 939 cache = client.cache
922 metadata = cache.get_metadata(uid=uid) 940 metadata = cache.get_metadata(uid=uid)
923 if metadata is not None: 941 if metadata is not None:
924 # there is already a conversion for this image in cache 942 # there is already a conversion for this image in cache
925 return metadata['path'] 943 return metadata["path"]
926 else: 944 else:
927 with cache.cache_data( 945 with cache.cache_data(
928 source='HOST_IMAGE_CONVERT', 946 source="HOST_IMAGE_CONVERT", uid=uid, filename=filename
929 uid=uid, 947 ) as cache_f:
930 filename=filename) as cache_f:
931 948
932 converted_path = await image.convert( 949 converted_path = await image.convert(
933 source, 950 source, dest=cache_f, extra=extra
934 dest=cache_f,
935 extra=extra
936 ) 951 )
937 return converted_path 952 return converted_path
938 else: 953 else:
939 return await image.convert(source, dest, extra) 954 return await image.convert(source, dest, extra)
940
941 955
942 # local dirs 956 # local dirs
943 957
944 def get_local_path( 958 def get_local_path(
945 self, 959 self,
996 ## Encryption ## 1010 ## Encryption ##
997 1011
998 def register_encryption_plugin(self, *args, **kwargs): 1012 def register_encryption_plugin(self, *args, **kwargs):
999 return encryption.EncryptionHandler.register_plugin(*args, **kwargs) 1013 return encryption.EncryptionHandler.register_plugin(*args, **kwargs)
1000 1014
1001 def _message_encryption_start(self, to_jid_s, namespace, replace=False, 1015 def _message_encryption_start(
1002 profile_key=C.PROF_KEY_NONE): 1016 self, to_jid_s, namespace, replace=False, profile_key=C.PROF_KEY_NONE
1017 ):
1003 client = self.get_client(profile_key) 1018 client = self.get_client(profile_key)
1004 to_jid = jid.JID(to_jid_s) 1019 to_jid = jid.JID(to_jid_s)
1005 return defer.ensureDeferred( 1020 return defer.ensureDeferred(
1006 client.encryption.start(to_jid, namespace or None, replace)) 1021 client.encryption.start(to_jid, namespace or None, replace)
1022 )
1007 1023
1008 def _message_encryption_stop(self, to_jid_s, profile_key=C.PROF_KEY_NONE): 1024 def _message_encryption_stop(self, to_jid_s, profile_key=C.PROF_KEY_NONE):
1009 client = self.get_client(profile_key) 1025 client = self.get_client(profile_key)
1010 to_jid = jid.JID(to_jid_s) 1026 to_jid = jid.JID(to_jid_s)
1011 return defer.ensureDeferred( 1027 return defer.ensureDeferred(client.encryption.stop(to_jid))
1012 client.encryption.stop(to_jid))
1013 1028
1014 def _message_encryption_get(self, to_jid_s, profile_key=C.PROF_KEY_NONE): 1029 def _message_encryption_get(self, to_jid_s, profile_key=C.PROF_KEY_NONE):
1015 client = self.get_client(profile_key) 1030 client = self.get_client(profile_key)
1016 to_jid = jid.JID(to_jid_s) 1031 to_jid = jid.JID(to_jid_s)
1017 session_data = client.encryption.getSession(to_jid) 1032 session_data = client.encryption.getSession(to_jid)
1022 1037
1023 def _encryption_plugins_get(self): 1038 def _encryption_plugins_get(self):
1024 plugins = encryption.EncryptionHandler.getPlugins() 1039 plugins = encryption.EncryptionHandler.getPlugins()
1025 ret = [] 1040 ret = []
1026 for p in plugins: 1041 for p in plugins:
1027 ret.append({ 1042 ret.append(
1028 "name": p.name, 1043 {
1029 "namespace": p.namespace, 1044 "name": p.name,
1030 "priority": p.priority, 1045 "namespace": p.namespace,
1031 "directed": p.directed, 1046 "priority": p.priority,
1032 }) 1047 "directed": p.directed,
1048 }
1049 )
1033 return data_format.serialise(ret) 1050 return data_format.serialise(ret)
1034 1051
1035 def _encryption_trust_ui_get(self, to_jid_s, namespace, profile_key): 1052 def _encryption_trust_ui_get(self, to_jid_s, namespace, profile_key):
1036 client = self.get_client(profile_key) 1053 client = self.get_client(profile_key)
1037 to_jid = jid.JID(to_jid_s) 1054 to_jid = jid.JID(to_jid_s)
1038 d = defer.ensureDeferred( 1055 d = defer.ensureDeferred(
1039 client.encryption.get_trust_ui(to_jid, namespace=namespace or None)) 1056 client.encryption.get_trust_ui(to_jid, namespace=namespace or None)
1057 )
1040 d.addCallback(lambda xmlui: xmlui.toXml()) 1058 d.addCallback(lambda xmlui: xmlui.toXml())
1041 return d 1059 return d
1042 1060
1043 ## XMPP methods ## 1061 ## XMPP methods ##
1044 1062
1045 def _message_send( 1063 def _message_send(
1046 self, to_jid_s, message, subject=None, mess_type="auto", extra_s="", 1064 self,
1047 profile_key=C.PROF_KEY_NONE): 1065 to_jid_s,
1066 message,
1067 subject=None,
1068 mess_type="auto",
1069 extra_s="",
1070 profile_key=C.PROF_KEY_NONE,
1071 ):
1048 client = self.get_client(profile_key) 1072 client = self.get_client(profile_key)
1049 to_jid = jid.JID(to_jid_s) 1073 to_jid = jid.JID(to_jid_s)
1050 return client.sendMessage( 1074 return client.sendMessage(
1051 to_jid, 1075 to_jid, message, subject, mess_type, data_format.deserialise(extra_s)
1052 message,
1053 subject,
1054 mess_type,
1055 data_format.deserialise(extra_s)
1056 ) 1076 )
1057 1077
1058 def _set_presence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE): 1078 def _set_presence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE):
1059 return self.presence_set(jid.JID(to) if to else None, show, statuses, profile_key) 1079 return self.presence_set(jid.JID(to) if to else None, show, statuses, profile_key)
1060 1080
1061 def presence_set(self, to_jid=None, show="", statuses=None, 1081 def presence_set(
1062 profile_key=C.PROF_KEY_NONE): 1082 self, to_jid=None, show="", statuses=None, profile_key=C.PROF_KEY_NONE
1083 ):
1063 """Send our presence information""" 1084 """Send our presence information"""
1064 if statuses is None: 1085 if statuses is None:
1065 statuses = {} 1086 statuses = {}
1066 profile = self.memory.get_profile_name(profile_key) 1087 profile = self.memory.get_profile_name(profile_key)
1067 assert profile 1088 assert profile
1114 return self.contact_update(client, jid.JID(to_jid_s), name, groups) 1135 return self.contact_update(client, jid.JID(to_jid_s), name, groups)
1115 1136
1116 def contact_update(self, client, to_jid, name, groups): 1137 def contact_update(self, client, to_jid, name, groups):
1117 """update a contact in roster list""" 1138 """update a contact in roster list"""
1118 roster_item = RosterItem(to_jid) 1139 roster_item = RosterItem(to_jid)
1119 roster_item.name = name or u'' 1140 roster_item.name = name or ""
1120 roster_item.groups = set(groups) 1141 roster_item.groups = set(groups)
1121 if not self.trigger.point("roster_update", client, roster_item): 1142 if not self.trigger.point("roster_update", client, roster_item):
1122 return 1143 return
1123 return client.roster.setItem(roster_item) 1144 return client.roster.setItem(roster_item)
1124 1145
1165 return self.memory.disco.find_service_entities(*args, **kwargs) 1186 return self.memory.disco.find_service_entities(*args, **kwargs)
1166 1187
1167 def find_features_set(self, *args, **kwargs): 1188 def find_features_set(self, *args, **kwargs):
1168 return self.memory.disco.find_features_set(*args, **kwargs) 1189 return self.memory.disco.find_features_set(*args, **kwargs)
1169 1190
1170 def _find_by_features(self, namespaces, identities, bare_jids, service, roster, own_jid, 1191 def _find_by_features(
1171 local_device, profile_key): 1192 self,
1193 namespaces,
1194 identities,
1195 bare_jids,
1196 service,
1197 roster,
1198 own_jid,
1199 local_device,
1200 profile_key,
1201 ):
1172 client = self.get_client(profile_key) 1202 client = self.get_client(profile_key)
1173 identities = [tuple(i) for i in identities] if identities else None 1203 identities = [tuple(i) for i in identities] if identities else None
1174 return defer.ensureDeferred(self.find_by_features( 1204 return defer.ensureDeferred(
1175 client, namespaces, identities, bare_jids, service, roster, own_jid, 1205 self.find_by_features(
1176 local_device)) 1206 client,
1207 namespaces,
1208 identities,
1209 bare_jids,
1210 service,
1211 roster,
1212 own_jid,
1213 local_device,
1214 )
1215 )
1177 1216
1178 async def find_by_features( 1217 async def find_by_features(
1179 self, 1218 self,
1180 client: SatXMPPEntity, 1219 client: SatXMPPEntity,
1181 namespaces: List[str], 1220 namespaces: List[str],
1182 identities: Optional[List[Tuple[str, str]]]=None, 1221 identities: Optional[List[Tuple[str, str]]] = None,
1183 bare_jids: bool=False, 1222 bare_jids: bool = False,
1184 service: bool=True, 1223 service: bool = True,
1185 roster: bool=True, 1224 roster: bool = True,
1186 own_jid: bool=True, 1225 own_jid: bool = True,
1187 local_device: bool=False 1226 local_device: bool = False,
1188 ) -> Tuple[ 1227 ) -> Tuple[
1189 Dict[jid.JID, Tuple[str, str, str]], 1228 Dict[jid.JID, Tuple[str, str, str]],
1190 Dict[jid.JID, Tuple[str, str, str]], 1229 Dict[jid.JID, Tuple[str, str, str]],
1191 Dict[jid.JID, Tuple[str, str, str]] 1230 Dict[jid.JID, Tuple[str, str, str]],
1192 ]: 1231 ]:
1193 """Retrieve all services or contacts managing a set a features 1232 """Retrieve all services or contacts managing a set a features
1194 1233
1195 @param namespaces: features which must be handled 1234 @param namespaces: features which must be handled
1196 @param identities: if not None or empty, 1235 @param identities: if not None or empty,
1222 found_own = {} 1261 found_own = {}
1223 found_roster = {} 1262 found_roster = {}
1224 if service: 1263 if service:
1225 services_jids = await self.find_features_set(client, namespaces) 1264 services_jids = await self.find_features_set(client, namespaces)
1226 services_jids = list(services_jids) # we need a list to map results below 1265 services_jids = list(services_jids) # we need a list to map results below
1227 services_infos = await defer.DeferredList( 1266 services_infos = await defer.DeferredList(
1228 [self.get_disco_infos(client, service_jid) for service_jid in services_jids] 1267 [
1268 self.get_disco_infos(client, service_jid)
1269 for service_jid in services_jids
1270 ]
1229 ) 1271 )
1230 1272
1231 for idx, (success, infos) in enumerate(services_infos): 1273 for idx, (success, infos) in enumerate(services_infos):
1232 service_jid = services_jids[idx] 1274 service_jid = services_jids[idx]
1233 if not success: 1275 if not success:
1234 log.warning( 1276 log.warning(
1235 _("Can't find features for service {service_jid}, ignoring") 1277 _(
1236 .format(service_jid=service_jid.full())) 1278 "Can't find features for service {service_jid}, ignoring"
1279 ).format(service_jid=service_jid.full())
1280 )
1237 continue 1281 continue
1238 if (identities is not None 1282 if identities is not None and not set(infos.identities.keys()).issuperset(
1239 and not set(infos.identities.keys()).issuperset(identities)): 1283 identities
1284 ):
1240 continue 1285 continue
1241 found_identities = [ 1286 found_identities = [
1242 (cat, type_, name or "") 1287 (cat, type_, name or "")
1243 for (cat, type_), name in infos.identities.items() 1288 for (cat, type_), name in infos.identities.items()
1244 ] 1289 ]
1289 1334
1290 for idx, (success, infos) in enumerate(infos_data): 1335 for idx, (success, infos) in enumerate(infos_data):
1291 full_jid = full_jids[idx] 1336 full_jid = full_jids[idx]
1292 if not success: 1337 if not success:
1293 log.warning( 1338 log.warning(
1294 _("Can't retrieve {full_jid} infos, ignoring") 1339 _("Can't retrieve {full_jid} infos, ignoring").format(
1295 .format(full_jid=full_jid.full())) 1340 full_jid=full_jid.full()
1341 )
1342 )
1296 continue 1343 continue
1297 if infos.features.issuperset(namespaces): 1344 if infos.features.issuperset(namespaces):
1298 if identities is not None and not set( 1345 if identities is not None and not set(
1299 infos.identities.keys() 1346 infos.identities.keys()
1300 ).issuperset(identities): 1347 ).issuperset(identities):
1474 reactor.callLater(1800, purge_callback) 1521 reactor.callLater(1800, purge_callback)
1475 1522
1476 return callback_id 1523 return callback_id
1477 1524
1478 def remove_callback(self, callback_id): 1525 def remove_callback(self, callback_id):
1479 """ Remove a previously registered callback 1526 """Remove a previously registered callback
1480 @param callback_id: id returned by [register_callback] """ 1527 @param callback_id: id returned by [register_callback]"""
1481 log.debug("Removing callback [%s]" % callback_id) 1528 log.debug("Removing callback [%s]" % callback_id)
1482 del self._cb_map[callback_id] 1529 del self._cb_map[callback_id]
1483 1530
1484 def _action_launch( 1531 def _action_launch(
1485 self, 1532 self, callback_id: str, data_s: str, profile_key: str
1486 callback_id: str,
1487 data_s: str,
1488 profile_key: str
1489 ) -> defer.Deferred: 1533 ) -> defer.Deferred:
1490 d = self.launch_callback( 1534 d = self.launch_callback(
1491 callback_id, 1535 callback_id, data_format.deserialise(data_s), profile_key
1492 data_format.deserialise(data_s),
1493 profile_key
1494 ) 1536 )
1495 d.addCallback(data_format.serialise) 1537 d.addCallback(data_format.serialise)
1496 return d 1538 return d
1497 1539
1498 def launch_callback( 1540 def launch_callback(
1499 self, 1541 self,
1500 callback_id: str, 1542 callback_id: str,
1501 data: Optional[dict] = None, 1543 data: Optional[dict] = None,
1502 profile_key: str = C.PROF_KEY_NONE 1544 profile_key: str = C.PROF_KEY_NONE,
1503 ) -> defer.Deferred: 1545 ) -> defer.Deferred:
1504 """Launch a specific callback 1546 """Launch a specific callback
1505 1547
1506 @param callback_id: id of the action (callback) to launch 1548 @param callback_id: id of the action (callback) to launch
1507 @param data: optional data 1549 @param data: optional data
1566 @param path(iterable[unicode]): untranslated path to menu 1608 @param path(iterable[unicode]): untranslated path to menu
1567 @return (tuple[unicode]): canonical form of path 1609 @return (tuple[unicode]): canonical form of path
1568 """ 1610 """
1569 return tuple((p.lower().strip() for p in path)) 1611 return tuple((p.lower().strip() for p in path))
1570 1612
1571 def import_menu(self, path, callback, security_limit=C.NO_SECURITY_LIMIT, 1613 def import_menu(
1572 help_string="", type_=C.MENU_GLOBAL): 1614 self,
1615 path,
1616 callback,
1617 security_limit=C.NO_SECURITY_LIMIT,
1618 help_string="",
1619 type_=C.MENU_GLOBAL,
1620 ):
1573 r"""register a new menu for frontends 1621 r"""register a new menu for frontends
1574 1622
1575 @param path(iterable[unicode]): path to go to the menu 1623 @param path(iterable[unicode]): path to go to the menu
1576 (category/subcategory/.../item) (e.g.: ("File", "Open")) 1624 (category/subcategory/.../item) (e.g.: ("File", "Open"))
1577 /!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open"))) 1625 /!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open")))
1675 extra = {} # TODO: manage extra data like icon 1723 extra = {} # TODO: manage extra data like icon
1676 ret.append((menu_id, type_, path, path_i18n, extra)) 1724 ret.append((menu_id, type_, path, path_i18n, extra))
1677 1725
1678 return ret 1726 return ret
1679 1727
1680 def _launch_menu(self, menu_type, path, data=None, security_limit=C.NO_SECURITY_LIMIT, 1728 def _launch_menu(
1681 profile_key=C.PROF_KEY_NONE): 1729 self,
1730 menu_type,
1731 path,
1732 data=None,
1733 security_limit=C.NO_SECURITY_LIMIT,
1734 profile_key=C.PROF_KEY_NONE,
1735 ):
1682 client = self.get_client(profile_key) 1736 client = self.get_client(profile_key)
1683 return self.launch_menu(client, menu_type, path, data, security_limit) 1737 return self.launch_menu(client, menu_type, path, data, security_limit)
1684 1738
1685 def launch_menu(self, client, menu_type, path, data=None, 1739 def launch_menu(
1686 security_limit=C.NO_SECURITY_LIMIT): 1740 self, client, menu_type, path, data=None, security_limit=C.NO_SECURITY_LIMIT
1741 ):
1687 """launch action a menu action 1742 """launch action a menu action
1688 1743
1689 @param menu_type(unicode): type of menu to launch 1744 @param menu_type(unicode): type of menu to launch
1690 @param path(iterable[unicode]): canonical path of the menu 1745 @param path(iterable[unicode]): canonical path of the menu
1691 @params data(dict): menu data 1746 @params data(dict): menu data