comparison sat/core/xmpp.py @ 3244:b10d207f95f9

core (xmpp): properly clean profile data in case of startConnection failure: - profile is now always removed from profiles_connecting - an exception is raised if client already exists only if client is connected. Otherwise, it's deleted (this allow to keep session open to e.g. modify settings in case of failure during connection)
author Goffi <goffi@goffi.org>
date Wed, 01 Apr 2020 16:17:09 +0200
parents 199fc4c551e9
children f16c96c7a91a
comparison
equal deleted inserted replaced
3243:f2e30aa031e9 3244:b10d207f95f9
161 # (independently of connection/deconnection) 161 # (independently of connection/deconnection)
162 if profile in cls.profiles_connecting: 162 if profile in cls.profiles_connecting:
163 raise exceptions.CancelError(f"{profile} is already being connected") 163 raise exceptions.CancelError(f"{profile} is already being connected")
164 cls.profiles_connecting.add(profile) 164 cls.profiles_connecting.add(profile)
165 try: 165 try:
166 port = int( 166 try:
167 host.memory.getParamA( 167 port = int(
168 C.FORCE_PORT_PARAM, "Connection", profile_key=profile 168 host.memory.getParamA(
169 C.FORCE_PORT_PARAM, "Connection", profile_key=profile
170 )
169 ) 171 )
172 except ValueError:
173 log.debug(_("Can't parse port value, using default value"))
174 port = (
175 None
176 ) # will use default value 5222 or be retrieved from a DNS SRV record
177
178 password = await host.memory.asyncGetParamA(
179 "Password", "Connection", profile_key=profile
170 ) 180 )
171 except ValueError: 181
172 log.debug(_("Can't parse port value, using default value")) 182 entity_jid_s = await host.memory.asyncGetParamA(
173 port = ( 183 "JabberID", "Connection", profile_key=profile)
174 None 184 entity_jid = jid.JID(entity_jid_s)
175 ) # will use default value 5222 or be retrieved from a DNS SRV record 185
176 186 if not entity_jid.resource and not cls.is_component and entity_jid.user:
177 password = await host.memory.asyncGetParamA( 187 # if no resource is specified, we create our own instead of using
178 "Password", "Connection", profile_key=profile 188 # server returned one, as it will then stay stable in case of
179 ) 189 # reconnection. we only do that for client and if there is a user part, to
180 190 # let server decide for anonymous login
181 entity_jid_s = await host.memory.asyncGetParamA( 191 resource_dict = await host.memory.storage.getPrivates(
182 "JabberID", "Connection", profile_key=profile) 192 "core:xmpp", ["resource"] , profile=profile)
183 entity_jid = jid.JID(entity_jid_s) 193 try:
184 194 resource = resource_dict["resource"]
185 if not entity_jid.resource and not cls.is_component and entity_jid.user: 195 except KeyError:
186 # if no resource is specified, we create our own instead of using 196 resource = f"{C.APP_NAME_FILE}.{shortuuid.uuid()}"
187 # server returned one, as it will then stay stable in case of reconnection. 197 await host.memory.storage.setPrivateValue(
188 # we only do that for client and if there is a user part, to let server 198 "core:xmpp", "resource", resource, profile=profile)
189 # decide for anonymous login 199
190 resource_dict = await host.memory.storage.getPrivates( 200 log.info(_("We'll use the stable resource {resource}").format(
191 "core:xmpp", ["resource"] , profile=profile) 201 resource=resource))
192 try: 202 entity_jid.resource = resource
193 resource = resource_dict["resource"] 203
194 except KeyError: 204 if profile in host.profiles:
195 resource = f"{C.APP_NAME_FILE}.{shortuuid.uuid()}" 205 if host.profiles[profile].isConnected():
196 await host.memory.storage.setPrivateValue( 206 raise exceptions.InternalError(
197 "core:xmpp", "resource", resource, profile=profile) 207 f"There is already a connected profile of name {profile!r} in "
198 208 f"host")
199 log.info(_("We'll use the stable resource {resource}").format( 209 log.debug(
200 resource=resource)) 210 "removing unconnected profile {profile!r}")
201 entity_jid.resource = resource 211 del host.profiles[profile]
202 212 entity = host.profiles[profile] = cls(
203 if profile in host.profiles: 213 host, profile, entity_jid, password,
204 raise exceptions.InternalError( 214 host.memory.getParamA(C.FORCE_SERVER_PARAM, "Connection",
205 f"There is already a profile of name {profile} in host") 215 profile_key=profile) or None,
206 entity = host.profiles[profile] = cls( 216 port, max_retries,
207 host, profile, entity_jid, password, 217 )
208 host.memory.getParamA(C.FORCE_SERVER_PARAM, "Connection", 218
209 profile_key=profile) or None, 219 await entity.encryption.loadSessions()
210 port, max_retries, 220
211 ) 221 entity._createSubProtocols()
212 222
213 await entity.encryption.loadSessions() 223 entity.fallBack = SatFallbackHandler(host)
214 224 entity.fallBack.setHandlerParent(entity)
215 entity._createSubProtocols() 225
216 226 entity.versionHandler = SatVersionHandler(C.APP_NAME_FULL, host.full_version)
217 entity.fallBack = SatFallbackHandler(host) 227 entity.versionHandler.setHandlerParent(entity)
218 entity.fallBack.setHandlerParent(entity) 228
219 229 entity.identityHandler = SatIdentityHandler()
220 entity.versionHandler = SatVersionHandler(C.APP_NAME_FULL, host.full_version) 230 entity.identityHandler.setHandlerParent(entity)
221 entity.versionHandler.setHandlerParent(entity) 231
222 232 log.debug(_("setting plugins parents"))
223 entity.identityHandler = SatIdentityHandler() 233
224 entity.identityHandler.setHandlerParent(entity) 234 plugin_conn_cb = await entity._callConnectionTriggers()
225 235
226 log.debug(_("setting plugins parents")) 236 entity.startService()
227 237
228 plugin_conn_cb = await entity._callConnectionTriggers() 238 await entity.conn_deferred
229 239
230 entity.startService() 240 await defer.maybeDeferred(entity.entityConnected)
231 241
232 await entity.conn_deferred 242 # Call profileConnected callback for all plugins,
233 243 # and print error message if any of them fails
234 await defer.maybeDeferred(entity.entityConnected) 244 conn_cb_list = []
235 245 for __, callback in plugin_conn_cb:
236 # Call profileConnected callback for all plugins, 246 conn_cb_list.append(utils.asDeferred(callback, entity))
237 # and print error message if any of them fails 247 list_d = defer.DeferredList(conn_cb_list)
238 conn_cb_list = [] 248
239 for __, callback in plugin_conn_cb: 249 def logPluginResults(results):
240 conn_cb_list.append(utils.asDeferred(callback, entity)) 250 all_succeed = all([success for success, result in results])
241 list_d = defer.DeferredList(conn_cb_list) 251 if not all_succeed:
242 252 log.error(_("Plugins initialisation error"))
243 def logPluginResults(results): 253 for idx, (success, result) in enumerate(results):
244 all_succeed = all([success for success, result in results]) 254 if not success:
245 if not all_succeed: 255 log.error(
246 log.error(_("Plugins initialisation error")) 256 "error (plugin %(name)s): %(failure)s"
247 for idx, (success, result) in enumerate(results): 257 % {
248 if not success: 258 "name": plugin_conn_cb[idx][0]._info["import_name"],
249 log.error( 259 "failure": result,
250 "error (plugin %(name)s): %(failure)s" 260 }
251 % { 261 )
252 "name": plugin_conn_cb[idx][0]._info["import_name"], 262
253 "failure": result, 263 await list_d.addCallback(
254 } 264 logPluginResults
255 ) 265 ) # FIXME: we should have a timeout here, and a way to know if a plugin freeze
256 266 # TODO: mesure launch time of each plugin
257 await list_d.addCallback( 267 finally:
258 logPluginResults 268 cls.profiles_connecting.remove(profile)
259 ) # FIXME: we should have a timeout here, and a way to know if a plugin freeze
260 # TODO: mesure launch time of each plugin
261
262 cls.profiles_connecting.remove(profile)
263 269
264 def _disconnectionCb(self, __): 270 def _disconnectionCb(self, __):
265 self._connected_d = None 271 self._connected_d = None
266 272
267 def _disconnectionEb(self, failure_): 273 def _disconnectionEb(self, failure_):