# HG changeset patch # User Goffi # Date 1579982912 -3600 # Node ID 0c29155ac68b0db97f9ab2b38cc3191fca1c16ed # Parent 790489521b156e93a6c405169fcbfef222007ceb core: backend autoconnection: A new Connection/autoconnect_backend param can be set for a profile or component to be started automatically with backend. This is specially useful for components, but can be useful for client profile too (e.g. on Android we need to start profile with backend to get notifications, this part will come with following commits). The new Sqlite.getIndParamValues method allows to retrieve the same parameters for all profiles. diff -r 790489521b15 -r 0c29155ac68b sat/core/sat_main.py --- a/sat/core/sat_main.py Sat Jan 25 21:08:29 2020 +0100 +++ b/sat/core/sat_main.py Sat Jan 25 21:08:32 2020 +0100 @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # SAT: a jabber client # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) @@ -22,7 +21,7 @@ import os.path import uuid import sat -from sat.core.i18n import _, languageSwitch +from sat.core.i18n import _, D_, languageSwitch from sat.core import patches patches.apply() from twisted.application import service @@ -172,7 +171,7 @@ self.bridge.register_method("imageCheck", self._imageCheck) self.bridge.register_method("imageResize", self._imageResize) - self.memory.initialized.addCallback(self._postMemoryInit) + self.memory.initialized.addCallback(lambda __: defer.ensureDeferred(self._postMemoryInit())) @property def version(self): @@ -202,7 +201,7 @@ def bridge_name(self): return os.path.splitext(os.path.basename(self.bridge.__file__))[0] - def _postMemoryInit(self, ignore): + async def _postMemoryInit(self): """Method called after memory initialization is done""" self.common_cache = cache.Cache(self, None) log.info(_("Memory initialised")) @@ -218,9 +217,37 @@ ) sys.exit(1) self._addBaseMenus() + self.initialised.callback(None) log.info(_("Backend is ready")) + # profile autoconnection must be done after self.initialised is called because + # startSession waits for it. + autoconnect_dict = await self.memory.storage.getIndParamValues( + category='Connection', name='autoconnect_backend', + ) + profiles_autoconnect = [p for p, v in autoconnect_dict.items() if C.bool(v)] + if not self.trigger.point("profilesAutoconnect", profiles_autoconnect): + return + if profiles_autoconnect: + log.info(D_( + "Following profiles will be connected automatically: {profiles}" + ).format(profiles= ', '.join(profiles_autoconnect))) + connect_d_list = [] + for profile in profiles_autoconnect: + connect_d_list.append(defer.ensureDeferred(self.connect(profile))) + + if connect_d_list: + results = await defer.DeferredList(connect_d_list) + for idx, (success, result) in enumerate(results): + if not success: + profile = profiles_autoconnect[0] + log.warning( + _("Can't autoconnect profile {profile}: {reason}").format( + profile = profile, + reason = result) + ) + def _addBaseMenus(self): """Add base menus""" encryption.EncryptionHandler._importMenus(self) @@ -407,9 +434,10 @@ def _connect(self, profile_key, password="", options=None): profile = self.memory.getProfileName(profile_key) - return self.connect(profile, password, options) + return defer.ensureDeferred(self.connect(profile, password, options)) - def connect(self, profile, password="", options=None, max_retries=C.XMPP_MAX_RETRIES): + async def connect( + self, profile, password="", options=None, max_retries=C.XMPP_MAX_RETRIES): """Connect a profile (i.e. connect client.component to XMPP server) Retrieve the individual parameters, authenticate the profile @@ -427,20 +455,18 @@ if options is None: options = {} - def connectProfile(__=None): - if self.isConnected(profile): - log.info(_("already connected !")) - return True + await self.memory.startSession(password, profile) + + if self.isConnected(profile): + log.info(_("already connected !")) + return True - if self.memory.isComponent(profile): - d = xmpp.SatXMPPComponent.startConnection(self, profile, max_retries) - else: - d = xmpp.SatXMPPClient.startConnection(self, profile, max_retries) - return d.addCallback(lambda __: False) + if self.memory.isComponent(profile): + await xmpp.SatXMPPComponent.startConnection(self, profile, max_retries) + else: + await xmpp.SatXMPPClient.startConnection(self, profile, max_retries) - d = self.memory.startSession(password, profile) - d.addCallback(connectProfile) - return d + return False def disconnect(self, profile_key): """disconnect from jabber server""" diff -r 790489521b15 -r 0c29155ac68b sat/memory/params.py --- a/sat/memory/params.py Sat Jan 25 21:08:29 2020 +0100 +++ b/sat/memory/params.py Sat Jan 25 21:08:32 2020 +0100 @@ -74,6 +74,7 @@ + @@ -92,6 +93,7 @@ "force_server_param": C.FORCE_SERVER_PARAM, "force_port_param": C.FORCE_PORT_PARAM, "new_account_label": D_("Register new account"), + "autoconnect_backend_label": D_("Connect on backend startup"), "autoconnect_label": D_("Connect on frontend startup"), "autodisconnect_label": D_("Disconnect on frontend closure"), "check_certificate_label": D_("Check certificate (don't uncheck if unsure)"), diff -r 790489521b15 -r 0c29155ac68b sat/memory/sqlite.py --- a/sat/memory/sqlite.py Sat Jan 25 21:08:29 2020 +0100 +++ b/sat/memory/sqlite.py Sat Jan 25 21:08:32 2020 +0100 @@ -196,8 +196,10 @@ @param db_filename: full path to the Sqlite database """ - self.initialized = defer.Deferred() # triggered when memory is fully initialised and ready - self.profiles = {} # we keep cache for the profiles (key: profile name, value: profile id) + # triggered when memory is fully initialised and ready + self.initialized = defer.Deferred() + # we keep cache for the profiles (key: profile name, value: profile id) + self.profiles = {} log.info(_("Connecting database")) new_base = not os.path.exists(db_filename) # do we have to create the database ? @@ -231,10 +233,10 @@ init_defer.addCallback(self.commitStatements) def fillProfileCache(ignore): - d = self.dbpool.runQuery("SELECT profile_id, entry_point FROM components").addCallback(self._cacheComponentsAndProfiles) - d.chainDeferred(self.initialized) + return self.dbpool.runQuery("SELECT profile_id, entry_point FROM components").addCallback(self._cacheComponentsAndProfiles) init_defer.addCallback(fillProfileCache) + init_defer.chainDeferred(self.initialized) def commitStatements(self, statements): @@ -387,10 +389,26 @@ @param profile: %(doc_profile)s @return: deferred """ - d = self.dbpool.runQuery("SELECT value FROM param_ind WHERE category=? AND name=? AND profile_id=?", (category, name, self.profiles[profile])) + d = self.dbpool.runQuery( + "SELECT value FROM param_ind WHERE category=? AND name=? AND profile_id=?", + (category, name, self.profiles[profile])) d.addCallback(self.__getFirstResult) return d + async def getIndParamValues(self, category, name): + """Ask database for the individual values of a parameter for all profiles + + @param category: category of the parameter + @param name: name of the parameter + @return dict: profile => value map + """ + result = await self.dbpool.runQuery( + "SELECT profiles.name, param_ind.value FROM param_ind JOIN profiles ON " + "param_ind.profile_id = profiles.id WHERE param_ind.category=? " + "and param_ind.name=?", + (category, name)) + return dict(result) + def setGenParam(self, category, name, value): """Save the general parameters in database diff -r 790489521b15 -r 0c29155ac68b sat/plugins/plugin_misc_account.py --- a/sat/plugins/plugin_misc_account.py Sat Jan 25 21:08:29 2020 +0100 +++ b/sat/plugins/plugin_misc_account.py Sat Jan 25 21:08:32 2020 +0100 @@ -20,7 +20,6 @@ from sat.core.i18n import _, D_ from sat.core.log import getLogger -log = getLogger(__name__) from sat.core import exceptions from sat.tools import xml_tools from sat.memory.memory import Sessions @@ -32,6 +31,10 @@ from twisted.words.protocols.jabber import jid from sat.tools.common import email as sat_email + +log = getLogger(__name__) + + #  FIXME: this plugin code is old and need a cleaning # TODO: account deletion/password change need testing @@ -742,7 +745,8 @@ d.addCallback( lambda __: self.host.memory.getProfileName(jid_s) ) # checks if the profile has been successfuly created - d.addCallback(self.host.connect, password, {}, 0) + d.addCallback(lambda profile: defer.ensureDeferred( + self.host.connect(profile, password, {}, 0))) def connected(result): self.sendEmails(None, profile=jid_s) diff -r 790489521b15 -r 0c29155ac68b sat/plugins/plugin_misc_email_invitation.py --- a/sat/plugins/plugin_misc_email_invitation.py Sat Jan 25 21:08:29 2020 +0100 +++ b/sat/plugins/plugin_misc_email_invitation.py Sat Jan 25 21:08:32 2020 +0100 @@ -281,7 +281,7 @@ except KeyError: pass else: - yield self.host.connect(guest_profile, password) + yield defer.ensureDeferred(self.host.connect(guest_profile, password)) guest_client = self.host.getClient(guest_profile) yield id_plugin.setIdentity(guest_client, {'nick': name}) yield self.host.disconnect(guest_profile) diff -r 790489521b15 -r 0c29155ac68b sat/stdui/ui_profile_manager.py --- a/sat/stdui/ui_profile_manager.py Sat Jan 25 21:08:29 2020 +0100 +++ b/sat/stdui/ui_profile_manager.py Sat Jan 25 21:08:32 2020 +0100 @@ -21,14 +21,16 @@ from sat.core.i18n import D_ from sat.core.constants import Const as C from sat.core.log import getLogger - -log = getLogger(__name__) from sat.core import exceptions from sat.tools import xml_tools from sat.memory.memory import ProfileSessions +from twisted.internet import defer from twisted.words.protocols.jabber import jid +log = getLogger(__name__) + + class ProfileManager(object): """Manage profiles.""" @@ -143,7 +145,7 @@ d = self.host.memory.setParam( "Password", xmpp_password, "Connection", profile_key=profile ) - d.addCallback(lambda __: self.host.connect(profile)) + d.addCallback(lambda __: defer.ensureDeferred(self.host.connect(profile))) d.addCallback(lambda __: {}) d.addErrback(lambda __: self._changeXMPPPassword({}, profile)) return d