diff --git a/sat/core/sat_main.py b/sat/core/sat_main.py
index fe7aeee7..ba663177 100644
--- a/sat/core/sat_main.py
+++ b/sat/core/sat_main.py
@@ -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 sys
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 @@ class SAT(service.Service):
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 @@ class SAT(service.Service):
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 @@ class SAT(service.Service):
)
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 @@ class SAT(service.Service):
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 @@ class SAT(service.Service):
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.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.isConnected(profile):
+ log.info(_("already connected !"))
+ return True
- d = self.memory.startSession(password, profile)
- d.addCallback(connectProfile)
- return d
+ if self.memory.isComponent(profile):
+ await xmpp.SatXMPPComponent.startConnection(self, profile, max_retries)
+ else:
+ await xmpp.SatXMPPClient.startConnection(self, profile, max_retries)
+
+ return False
def disconnect(self, profile_key):
"""disconnect from jabber server"""
diff --git a/sat/memory/params.py b/sat/memory/params.py
index 3714e6f4..2160cac9 100644
--- a/sat/memory/params.py
+++ b/sat/memory/params.py
@@ -74,6 +74,7 @@ class Params(object):
+
@@ -92,6 +93,7 @@ class Params(object):
"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 --git a/sat/memory/sqlite.py b/sat/memory/sqlite.py
index eb792a1d..176f99c4 100644
--- a/sat/memory/sqlite.py
+++ b/sat/memory/sqlite.py
@@ -196,8 +196,10 @@ class SqliteStorage(object):
@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 @@ class SqliteStorage(object):
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 @@ class SqliteStorage(object):
@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 --git a/sat/plugins/plugin_misc_account.py b/sat/plugins/plugin_misc_account.py
index 16dfcecf..eff49d2a 100644
--- a/sat/plugins/plugin_misc_account.py
+++ b/sat/plugins/plugin_misc_account.py
@@ -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.python.failure import Failure
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 @@ class MiscAccount(object):
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 --git a/sat/plugins/plugin_misc_email_invitation.py b/sat/plugins/plugin_misc_email_invitation.py
index 7314b707..cd38d59a 100644
--- a/sat/plugins/plugin_misc_email_invitation.py
+++ b/sat/plugins/plugin_misc_email_invitation.py
@@ -281,7 +281,7 @@ class InvitationsPlugin(object):
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 --git a/sat/stdui/ui_profile_manager.py b/sat/stdui/ui_profile_manager.py
index 7a84c60c..944ea59b 100644
--- a/sat/stdui/ui_profile_manager.py
+++ b/sat/stdui/ui_profile_manager.py
@@ -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 @@ class ProfileManager(object):
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