Mercurial > libervia-backend
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 | 9e63e02318ec |
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 |