comparison sat/memory/memory.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents 7af29260ecb8
children
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
63 """ 63 """
64 self._sessions = dict() 64 self._sessions = dict()
65 self.timeout = timeout or Sessions.DEFAULT_TIMEOUT 65 self.timeout = timeout or Sessions.DEFAULT_TIMEOUT
66 self.resettable_timeout = resettable_timeout 66 self.resettable_timeout = resettable_timeout
67 67
68 def newSession(self, session_data=None, session_id=None, profile=None): 68 def new_session(self, session_data=None, session_id=None, profile=None):
69 """Create a new session 69 """Create a new session
70 70
71 @param session_data: mutable data to use, default to a dict 71 @param session_data: mutable data to use, default to a dict
72 @param session_id (str): force the session_id to the given string 72 @param session_id (str): force the session_id to the given string
73 @param profile: if set, the session is owned by the profile, 73 @param profile: if set, the session is owned by the profile,
74 and profileGet must be used instead of __getitem__ 74 and profile_get must be used instead of __getitem__
75 @return: session_id, session_data 75 @return: session_id, session_data
76 """ 76 """
77 if session_id is None: 77 if session_id is None:
78 session_id = str(uuid4()) 78 session_id = str(uuid4())
79 elif session_id in self._sessions: 79 elif session_id in self._sessions:
80 raise exceptions.ConflictError( 80 raise exceptions.ConflictError(
81 "Session id {} is already used".format(session_id) 81 "Session id {} is already used".format(session_id)
82 ) 82 )
83 timer = reactor.callLater(self.timeout, self._purgeSession, session_id) 83 timer = reactor.callLater(self.timeout, self._purge_session, session_id)
84 if session_data is None: 84 if session_data is None:
85 session_data = {} 85 session_data = {}
86 self._sessions[session_id] = ( 86 self._sessions[session_id] = (
87 (timer, session_data) if profile is None else (timer, session_data, profile) 87 (timer, session_data) if profile is None else (timer, session_data, profile)
88 ) 88 )
89 return session_id, session_data 89 return session_id, session_data
90 90
91 def _purgeSession(self, session_id): 91 def _purge_session(self, session_id):
92 try: 92 try:
93 timer, session_data, profile = self._sessions[session_id] 93 timer, session_data, profile = self._sessions[session_id]
94 except ValueError: 94 except ValueError:
95 timer, session_data = self._sessions[session_id] 95 timer, session_data = self._sessions[session_id]
96 profile = None 96 profile = None
111 return len(self._sessions) 111 return len(self._sessions)
112 112
113 def __contains__(self, session_id): 113 def __contains__(self, session_id):
114 return session_id in self._sessions 114 return session_id in self._sessions
115 115
116 def profileGet(self, session_id, profile): 116 def profile_get(self, session_id, profile):
117 try: 117 try:
118 timer, session_data, profile_set = self._sessions[session_id] 118 timer, session_data, profile_set = self._sessions[session_id]
119 except ValueError: 119 except ValueError:
120 raise exceptions.InternalError( 120 raise exceptions.InternalError(
121 "You need to use __getitem__ when profile is not set" 121 "You need to use __getitem__ when profile is not set"
131 def __getitem__(self, session_id): 131 def __getitem__(self, session_id):
132 try: 132 try:
133 timer, session_data = self._sessions[session_id] 133 timer, session_data = self._sessions[session_id]
134 except ValueError: 134 except ValueError:
135 raise exceptions.InternalError( 135 raise exceptions.InternalError(
136 "You need to use profileGet instead of __getitem__ when profile is set" 136 "You need to use profile_get instead of __getitem__ when profile is set"
137 ) 137 )
138 except KeyError: 138 except KeyError:
139 raise failure.Failure(KeyError(MSG_NO_SESSION)) 139 raise failure.Failure(KeyError(MSG_NO_SESSION))
140 if self.resettable_timeout: 140 if self.resettable_timeout:
141 timer.reset(self.timeout) 141 timer.reset(self.timeout)
142 return session_data 142 return session_data
143 143
144 def __setitem__(self, key, value): 144 def __setitem__(self, key, value):
145 raise NotImplementedError("You need do use newSession to create a session") 145 raise NotImplementedError("You need do use new_session to create a session")
146 146
147 def __delitem__(self, session_id): 147 def __delitem__(self, session_id):
148 """ delete the session data """ 148 """ delete the session data """
149 self._purgeSession(session_id) 149 self._purge_session(session_id)
150 150
151 def keys(self): 151 def keys(self):
152 return list(self._sessions.keys()) 152 return list(self._sessions.keys())
153 153
154 def iterkeys(self): 154 def iterkeys(self):
158 class ProfileSessions(Sessions): 158 class ProfileSessions(Sessions):
159 """ProfileSessions extends the Sessions class, but here the profile can be 159 """ProfileSessions extends the Sessions class, but here the profile can be
160 used as the key to retrieve data or delete a session (instead of session id). 160 used as the key to retrieve data or delete a session (instead of session id).
161 """ 161 """
162 162
163 def _profileGetAllIds(self, profile): 163 def _profile_get_all_ids(self, profile):
164 """Return a list of the sessions ids that are associated to the given profile. 164 """Return a list of the sessions ids that are associated to the given profile.
165 165
166 @param profile: %(doc_profile)s 166 @param profile: %(doc_profile)s
167 @return: a list containing the sessions ids 167 @return: a list containing the sessions ids
168 """ 168 """
174 continue 174 continue
175 if profile == profile_set: 175 if profile == profile_set:
176 ret.append(session_id) 176 ret.append(session_id)
177 return ret 177 return ret
178 178
179 def profileGetUnique(self, profile): 179 def profile_get_unique(self, profile):
180 """Return the data of the unique session that is associated to the given profile. 180 """Return the data of the unique session that is associated to the given profile.
181 181
182 @param profile: %(doc_profile)s 182 @param profile: %(doc_profile)s
183 @return: 183 @return:
184 - mutable data (default: dict) of the unique session 184 - mutable data (default: dict) of the unique session
185 - None if no session is associated to the profile 185 - None if no session is associated to the profile
186 - raise an error if more than one session are found 186 - raise an error if more than one session are found
187 """ 187 """
188 ids = self._profileGetAllIds(profile) 188 ids = self._profile_get_all_ids(profile)
189 if len(ids) > 1: 189 if len(ids) > 1:
190 raise exceptions.InternalError( 190 raise exceptions.InternalError(
191 "profileGetUnique has been used but more than one session has been found!" 191 "profile_get_unique has been used but more than one session has been found!"
192 ) 192 )
193 return ( 193 return (
194 self.profileGet(ids[0], profile) if len(ids) == 1 else None 194 self.profile_get(ids[0], profile) if len(ids) == 1 else None
195 ) # XXX: timeout might be reset 195 ) # XXX: timeout might be reset
196 196
197 def profileDelUnique(self, profile): 197 def profile_del_unique(self, profile):
198 """Delete the unique session that is associated to the given profile. 198 """Delete the unique session that is associated to the given profile.
199 199
200 @param profile: %(doc_profile)s 200 @param profile: %(doc_profile)s
201 @return: None, but raise an error if more than one session are found 201 @return: None, but raise an error if more than one session are found
202 """ 202 """
203 ids = self._profileGetAllIds(profile) 203 ids = self._profile_get_all_ids(profile)
204 if len(ids) > 1: 204 if len(ids) > 1:
205 raise exceptions.InternalError( 205 raise exceptions.InternalError(
206 "profileDelUnique has been used but more than one session has been found!" 206 "profile_del_unique has been used but more than one session has been found!"
207 ) 207 )
208 if len(ids) == 1: 208 if len(ids) == 1:
209 del self._sessions[ids[0]] 209 del self._sessions[ids[0]]
210 210
211 211
215 # must actually be purged and later, when the personal key is needed, the 215 # must actually be purged and later, when the personal key is needed, the
216 # profile password should be asked again in order to decrypt it. 216 # profile password should be asked again in order to decrypt it.
217 def __init__(self, timeout=None): 217 def __init__(self, timeout=None):
218 ProfileSessions.__init__(self, timeout, resettable_timeout=False) 218 ProfileSessions.__init__(self, timeout, resettable_timeout=False)
219 219
220 def _purgeSession(self, session_id): 220 def _purge_session(self, session_id):
221 log.debug( 221 log.debug(
222 "FIXME: PasswordSessions should ask for the profile password after the session expired" 222 "FIXME: PasswordSessions should ask for the profile password after the session expired"
223 ) 223 )
224 224
225 225
235 # where main key is resource, or None for bare jid 235 # where main key is resource, or None for bare jid
236 self._key_signals = set() # key which need a signal to frontends when updated 236 self._key_signals = set() # key which need a signal to frontends when updated
237 self.subscriptions = {} 237 self.subscriptions = {}
238 self.auth_sessions = PasswordSessions() # remember the authenticated profiles 238 self.auth_sessions = PasswordSessions() # remember the authenticated profiles
239 self.disco = Discovery(host) 239 self.disco = Discovery(host)
240 self.config = tools_config.parseMainConf(log_filenames=True) 240 self.config = tools_config.parse_main_conf(log_filenames=True)
241 self._cache_path = Path(self.getConfig("", "local_dir"), C.CACHE_DIR) 241 self._cache_path = Path(self.config_get("", "local_dir"), C.CACHE_DIR)
242 self.admins = self.getConfig("", "admins_list", []) 242 self.admins = self.config_get("", "admins_list", [])
243 self.admin_jids = set() 243 self.admin_jids = set()
244 244
245 245
246 async def initialise(self): 246 async def initialise(self):
247 self.storage = Storage() 247 self.storage = Storage()
254 self.memory_data = PersistentDict("memory") 254 self.memory_data = PersistentDict("memory")
255 await self.memory_data.load() 255 await self.memory_data.load()
256 await self.disco.load() 256 await self.disco.load()
257 for admin in self.admins: 257 for admin in self.admins:
258 try: 258 try:
259 admin_jid_s = await self.asyncGetParamA( 259 admin_jid_s = await self.param_get_a_async(
260 "JabberID", "Connection", profile_key=admin 260 "JabberID", "Connection", profile_key=admin
261 ) 261 )
262 except Exception as e: 262 except Exception as e:
263 log.warning(f"Can't retrieve jid of admin {admin!r}: {e}") 263 log.warning(f"Can't retrieve jid of admin {admin!r}: {e}")
264 else: 264 else:
271 self.admin_jids.add(admin_jid) 271 self.admin_jids.add(admin_jid)
272 272
273 273
274 ## Configuration ## 274 ## Configuration ##
275 275
276 def getConfig(self, section, name, default=None): 276 def config_get(self, section, name, default=None):
277 """Get the main configuration option 277 """Get the main configuration option
278 278
279 @param section: section of the config file (None or '' for DEFAULT) 279 @param section: section of the config file (None or '' for DEFAULT)
280 @param name: name of the option 280 @param name: name of the option
281 @param default: value to use if not found 281 @param default: value to use if not found
282 @return: str, list or dict 282 @return: str, list or dict
283 """ 283 """
284 return tools_config.getConfig(self.config, section, name, default) 284 return tools_config.config_get(self.config, section, name, default)
285 285
286 def load_xml(self, filename): 286 def load_xml(self, filename):
287 """Load parameters template from xml file 287 """Load parameters template from xml file
288 288
289 @param filename (str): input file 289 @param filename (str): input file
320 return False 320 return False
321 321
322 def load(self): 322 def load(self):
323 """Load parameters and all memory things from db""" 323 """Load parameters and all memory things from db"""
324 # parameters data 324 # parameters data
325 return self.params.loadGenParams() 325 return self.params.load_gen_params()
326 326
327 def loadIndividualParams(self, profile): 327 def load_individual_params(self, profile):
328 """Load individual parameters for a profile 328 """Load individual parameters for a profile
329 @param profile: %(doc_profile)s""" 329 @param profile: %(doc_profile)s"""
330 return self.params.loadIndParams(profile) 330 return self.params.load_ind_params(profile)
331 331
332 ## Profiles/Sessions management ## 332 ## Profiles/Sessions management ##
333 333
334 def startSession(self, password, profile): 334 def start_session(self, password, profile):
335 """"Iniatialise session for a profile 335 """"Iniatialise session for a profile
336 336
337 @param password(unicode): profile session password 337 @param password(unicode): profile session password
338 or empty string is no password is set 338 or empty string is no password is set
339 @param profile: %(doc_profile)s 339 @param profile: %(doc_profile)s
340 @raise exceptions.ProfileUnknownError if profile doesn't exists 340 @raise exceptions.ProfileUnknownError if profile doesn't exists
341 @raise exceptions.PasswordError: the password does not match 341 @raise exceptions.PasswordError: the password does not match
342 """ 342 """
343 profile = self.getProfileName(profile) 343 profile = self.get_profile_name(profile)
344 344
345 def createSession(__): 345 def create_session(__):
346 """Called once params are loaded.""" 346 """Called once params are loaded."""
347 self._entities_cache[profile] = {} 347 self._entities_cache[profile] = {}
348 log.info("[{}] Profile session started".format(profile)) 348 log.info("[{}] Profile session started".format(profile))
349 return False 349 return False
350 350
351 def backendInitialised(__): 351 def backend_initialised(__):
352 def doStartSession(__=None): 352 def do_start_session(__=None):
353 if self.isSessionStarted(profile): 353 if self.is_session_started(profile):
354 log.info("Session already started!") 354 log.info("Session already started!")
355 return True 355 return True
356 try: 356 try:
357 # if there is a value at this point in self._entities_cache, 357 # if there is a value at this point in self._entities_cache,
358 # it is the loadIndividualParams Deferred, the session is starting 358 # it is the load_individual_params Deferred, the session is starting
359 session_d = self._entities_cache[profile] 359 session_d = self._entities_cache[profile]
360 except KeyError: 360 except KeyError:
361 # else we do request the params 361 # else we do request the params
362 session_d = self._entities_cache[profile] = self.loadIndividualParams( 362 session_d = self._entities_cache[profile] = self.load_individual_params(
363 profile 363 profile
364 ) 364 )
365 session_d.addCallback(createSession) 365 session_d.addCallback(create_session)
366 finally: 366 finally:
367 return session_d 367 return session_d
368 368
369 auth_d = defer.ensureDeferred(self.profileAuthenticate(password, profile)) 369 auth_d = defer.ensureDeferred(self.profile_authenticate(password, profile))
370 auth_d.addCallback(doStartSession) 370 auth_d.addCallback(do_start_session)
371 return auth_d 371 return auth_d
372 372
373 if self.host.initialised.called: 373 if self.host.initialised.called:
374 return defer.succeed(None).addCallback(backendInitialised) 374 return defer.succeed(None).addCallback(backend_initialised)
375 else: 375 else:
376 return self.host.initialised.addCallback(backendInitialised) 376 return self.host.initialised.addCallback(backend_initialised)
377 377
378 def stopSession(self, profile): 378 def stop_session(self, profile):
379 """Delete a profile session 379 """Delete a profile session
380 380
381 @param profile: %(doc_profile)s 381 @param profile: %(doc_profile)s
382 """ 382 """
383 if self.host.isConnected(profile): 383 if self.host.is_connected(profile):
384 log.debug("Disconnecting profile because of session stop") 384 log.debug("Disconnecting profile because of session stop")
385 self.host.disconnect(profile) 385 self.host.disconnect(profile)
386 self.auth_sessions.profileDelUnique(profile) 386 self.auth_sessions.profile_del_unique(profile)
387 try: 387 try:
388 self._entities_cache[profile] 388 self._entities_cache[profile]
389 except KeyError: 389 except KeyError:
390 log.warning("Profile was not in cache") 390 log.warning("Profile was not in cache")
391 391
392 def _isSessionStarted(self, profile_key): 392 def _is_session_started(self, profile_key):
393 return self.isSessionStarted(self.getProfileName(profile_key)) 393 return self.is_session_started(self.get_profile_name(profile_key))
394 394
395 def isSessionStarted(self, profile): 395 def is_session_started(self, profile):
396 try: 396 try:
397 # XXX: if the value in self._entities_cache is a Deferred, 397 # XXX: if the value in self._entities_cache is a Deferred,
398 # the session is starting but not started yet 398 # the session is starting but not started yet
399 return not isinstance(self._entities_cache[profile], defer.Deferred) 399 return not isinstance(self._entities_cache[profile], defer.Deferred)
400 except KeyError: 400 except KeyError:
401 return False 401 return False
402 402
403 async def profileAuthenticate(self, password, profile): 403 async def profile_authenticate(self, password, profile):
404 """Authenticate the profile. 404 """Authenticate the profile.
405 405
406 @param password (unicode): the SàT profile password 406 @param password (unicode): the SàT profile password
407 @return: None in case of success (an exception is raised otherwise) 407 @return: None in case of success (an exception is raised otherwise)
408 @raise exceptions.PasswordError: the password does not match 408 @raise exceptions.PasswordError: the password does not match
409 """ 409 """
410 if not password and self.auth_sessions.profileGetUnique(profile): 410 if not password and self.auth_sessions.profile_get_unique(profile):
411 # XXX: this allows any frontend to connect with the empty password as soon as 411 # XXX: this allows any frontend to connect with the empty password as soon as
412 # the profile has been authenticated at least once before. It is OK as long as 412 # the profile has been authenticated at least once before. It is OK as long as
413 # submitting a form with empty passwords is restricted to local frontends. 413 # submitting a form with empty passwords is restricted to local frontends.
414 return 414 return
415 415
416 sat_cipher = await self.asyncGetParamA( 416 sat_cipher = await self.param_get_a_async(
417 C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile 417 C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile
418 ) 418 )
419 valid = PasswordHasher.verify(password, sat_cipher) 419 valid = PasswordHasher.verify(password, sat_cipher)
420 if not valid: 420 if not valid:
421 log.warning(_("Authentication failure of profile {profile}").format( 421 log.warning(_("Authentication failure of profile {profile}").format(
422 profile=profile)) 422 profile=profile))
423 raise exceptions.PasswordError("The provided profile password doesn't match.") 423 raise exceptions.PasswordError("The provided profile password doesn't match.")
424 return await self.newAuthSession(password, profile) 424 return await self.new_auth_session(password, profile)
425 425
426 async def newAuthSession(self, key, profile): 426 async def new_auth_session(self, key, profile):
427 """Start a new session for the authenticated profile. 427 """Start a new session for the authenticated profile.
428 428
429 If there is already an existing session, no new one is created 429 If there is already an existing session, no new one is created
430 The personal key is loaded encrypted from a PersistentDict before being decrypted. 430 The personal key is loaded encrypted from a PersistentDict before being decrypted.
431 431
433 @param profile: %(doc_profile)s 433 @param profile: %(doc_profile)s
434 """ 434 """
435 data = await PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load() 435 data = await PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load()
436 personal_key = BlockCipher.decrypt(key, data[C.MEMORY_CRYPTO_KEY]) 436 personal_key = BlockCipher.decrypt(key, data[C.MEMORY_CRYPTO_KEY])
437 # Create the session for this profile and store the personal key 437 # Create the session for this profile and store the personal key
438 session_data = self.auth_sessions.profileGetUnique(profile) 438 session_data = self.auth_sessions.profile_get_unique(profile)
439 if not session_data: 439 if not session_data:
440 self.auth_sessions.newSession( 440 self.auth_sessions.new_session(
441 {C.MEMORY_CRYPTO_KEY: personal_key}, profile=profile 441 {C.MEMORY_CRYPTO_KEY: personal_key}, profile=profile
442 ) 442 )
443 log.debug("auth session created for profile %s" % profile) 443 log.debug("auth session created for profile %s" % profile)
444 444
445 def purgeProfileSession(self, profile): 445 def purge_profile_session(self, profile):
446 """Delete cache of data of profile 446 """Delete cache of data of profile
447 @param profile: %(doc_profile)s""" 447 @param profile: %(doc_profile)s"""
448 log.info(_("[%s] Profile session purge" % profile)) 448 log.info(_("[%s] Profile session purge" % profile))
449 self.params.purgeProfile(profile) 449 self.params.purge_profile(profile)
450 try: 450 try:
451 del self._entities_cache[profile] 451 del self._entities_cache[profile]
452 except KeyError: 452 except KeyError:
453 log.error( 453 log.error(
454 _( 454 _(
455 "Trying to purge roster status cache for a profile not in memory: [%s]" 455 "Trying to purge roster status cache for a profile not in memory: [%s]"
456 ) 456 )
457 % profile 457 % profile
458 ) 458 )
459 459
460 def getProfilesList(self, clients=True, components=False): 460 def get_profiles_list(self, clients=True, components=False):
461 """retrieve profiles list 461 """retrieve profiles list
462 462
463 @param clients(bool): if True return clients profiles 463 @param clients(bool): if True return clients profiles
464 @param components(bool): if True return components profiles 464 @param components(bool): if True return components profiles
465 @return (list[unicode]): selected profiles 465 @return (list[unicode]): selected profiles
466 """ 466 """
467 if not clients and not components: 467 if not clients and not components:
468 log.warning(_("requesting no profiles at all")) 468 log.warning(_("requesting no profiles at all"))
469 return [] 469 return []
470 profiles = self.storage.getProfilesList() 470 profiles = self.storage.get_profiles_list()
471 if clients and components: 471 if clients and components:
472 return sorted(profiles) 472 return sorted(profiles)
473 isComponent = self.storage.profileIsComponent 473 is_component = self.storage.profile_is_component
474 if clients: 474 if clients:
475 p_filter = lambda p: not isComponent(p) 475 p_filter = lambda p: not is_component(p)
476 else: 476 else:
477 p_filter = lambda p: isComponent(p) 477 p_filter = lambda p: is_component(p)
478 478
479 return sorted(p for p in profiles if p_filter(p)) 479 return sorted(p for p in profiles if p_filter(p))
480 480
481 def getProfileName(self, profile_key, return_profile_keys=False): 481 def get_profile_name(self, profile_key, return_profile_keys=False):
482 """Return name of profile from keyword 482 """Return name of profile from keyword
483 483
484 @param profile_key: can be the profile name or a keyword (like @DEFAULT@) 484 @param profile_key: can be the profile name or a keyword (like @DEFAULT@)
485 @param return_profile_keys: if True, return unmanaged profile keys (like "@ALL@"). This keys must be managed by the caller 485 @param return_profile_keys: if True, return unmanaged profile keys (like "@ALL@"). This keys must be managed by the caller
486 @return: requested profile name 486 @return: requested profile name
487 @raise exceptions.ProfileUnknownError if profile doesn't exists 487 @raise exceptions.ProfileUnknownError if profile doesn't exists
488 """ 488 """
489 return self.params.getProfileName(profile_key, return_profile_keys) 489 return self.params.get_profile_name(profile_key, return_profile_keys)
490 490
491 def profileSetDefault(self, profile): 491 def profile_set_default(self, profile):
492 """Set default profile 492 """Set default profile
493 493
494 @param profile: %(doc_profile)s 494 @param profile: %(doc_profile)s
495 """ 495 """
496 # we want to be sure that the profile exists 496 # we want to be sure that the profile exists
497 profile = self.getProfileName(profile) 497 profile = self.get_profile_name(profile)
498 498
499 self.memory_data["Profile_default"] = profile 499 self.memory_data["Profile_default"] = profile
500 500
501 def createProfile(self, name, password, component=None): 501 def create_profile(self, name, password, component=None):
502 """Create a new profile 502 """Create a new profile
503 503
504 @param name(unicode): profile name 504 @param name(unicode): profile name
505 @param password(unicode): profile password 505 @param password(unicode): profile password
506 Can be empty to disable password 506 Can be empty to disable password
530 # FIXME: PLUGIN_INFO is not currently accessible after import, but type shoul be tested here 530 # FIXME: PLUGIN_INFO is not currently accessible after import, but type shoul be tested here
531 #  if self.host.plugins[component].PLUGIN_INFO[u"type"] != C.PLUG_TYPE_ENTRY_POINT: 531 #  if self.host.plugins[component].PLUGIN_INFO[u"type"] != C.PLUG_TYPE_ENTRY_POINT:
532 #   raise ValueError(_(u"Plugin {component} is not an entry point !".format( 532 #   raise ValueError(_(u"Plugin {component} is not an entry point !".format(
533 #   component = component))) 533 #   component = component)))
534 534
535 d = self.params.createProfile(name, component) 535 d = self.params.create_profile(name, component)
536 536
537 def initPersonalKey(__): 537 def init_personal_key(__):
538 # be sure to call this after checking that the profile doesn't exist yet 538 # be sure to call this after checking that the profile doesn't exist yet
539 539
540 # generated once for all and saved in a PersistentDict 540 # generated once for all and saved in a PersistentDict
541 personal_key = BlockCipher.getRandomKey( 541 personal_key = BlockCipher.get_random_key(
542 base64=True 542 base64=True
543 ).decode('utf-8') 543 ).decode('utf-8')
544 self.auth_sessions.newSession( 544 self.auth_sessions.new_session(
545 {C.MEMORY_CRYPTO_KEY: personal_key}, profile=name 545 {C.MEMORY_CRYPTO_KEY: personal_key}, profile=name
546 ) # will be encrypted by setParam 546 ) # will be encrypted by param_set
547 547
548 def startFakeSession(__): 548 def start_fake_session(__):
549 # avoid ProfileNotConnected exception in setParam 549 # avoid ProfileNotConnected exception in param_set
550 self._entities_cache[name] = None 550 self._entities_cache[name] = None
551 self.params.loadIndParams(name) 551 self.params.load_ind_params(name)
552 552
553 def stopFakeSession(__): 553 def stop_fake_session(__):
554 del self._entities_cache[name] 554 del self._entities_cache[name]
555 self.params.purgeProfile(name) 555 self.params.purge_profile(name)
556 556
557 d.addCallback(initPersonalKey) 557 d.addCallback(init_personal_key)
558 d.addCallback(startFakeSession) 558 d.addCallback(start_fake_session)
559 d.addCallback( 559 d.addCallback(
560 lambda __: self.setParam( 560 lambda __: self.param_set(
561 C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=name 561 C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=name
562 ) 562 )
563 ) 563 )
564 d.addCallback(stopFakeSession) 564 d.addCallback(stop_fake_session)
565 d.addCallback(lambda __: self.auth_sessions.profileDelUnique(name)) 565 d.addCallback(lambda __: self.auth_sessions.profile_del_unique(name))
566 return d 566 return d
567 567
568 def asyncDeleteProfile(self, name, force=False): 568 def profile_delete_async(self, name, force=False):
569 """Delete an existing profile 569 """Delete an existing profile
570 570
571 @param name: Name of the profile 571 @param name: Name of the profile
572 @param force: force the deletion even if the profile is connected. 572 @param force: force the deletion even if the profile is connected.
573 To be used for direct calls only (not through the bridge). 573 To be used for direct calls only (not through the bridge).
574 @return: a Deferred instance 574 @return: a Deferred instance
575 """ 575 """
576 576
577 def cleanMemory(__): 577 def clean_memory(__):
578 self.auth_sessions.profileDelUnique(name) 578 self.auth_sessions.profile_del_unique(name)
579 try: 579 try:
580 del self._entities_cache[name] 580 del self._entities_cache[name]
581 except KeyError: 581 except KeyError:
582 pass 582 pass
583 583
584 d = self.params.asyncDeleteProfile(name, force) 584 d = self.params.profile_delete_async(name, force)
585 d.addCallback(cleanMemory) 585 d.addCallback(clean_memory)
586 return d 586 return d
587 587
588 def isComponent(self, profile_name): 588 def is_component(self, profile_name):
589 """Tell if a profile is a component 589 """Tell if a profile is a component
590 590
591 @param profile_name(unicode): name of the profile 591 @param profile_name(unicode): name of the profile
592 @return (bool): True if profile is a component 592 @return (bool): True if profile is a component
593 @raise exceptions.NotFound: profile doesn't exist 593 @raise exceptions.NotFound: profile doesn't exist
594 """ 594 """
595 return self.storage.profileIsComponent(profile_name) 595 return self.storage.profile_is_component(profile_name)
596 596
597 def getEntryPoint(self, profile_name): 597 def get_entry_point(self, profile_name):
598 """Get a component entry point 598 """Get a component entry point
599 599
600 @param profile_name(unicode): name of the profile 600 @param profile_name(unicode): name of the profile
601 @return (bool): True if profile is a component 601 @return (bool): True if profile is a component
602 @raise exceptions.NotFound: profile doesn't exist 602 @raise exceptions.NotFound: profile doesn't exist
603 """ 603 """
604 return self.storage.getEntryPoint(profile_name) 604 return self.storage.get_entry_point(profile_name)
605 605
606 ## History ## 606 ## History ##
607 607
608 def addToHistory(self, client, data): 608 def add_to_history(self, client, data):
609 return self.storage.addToHistory(data, client.profile) 609 return self.storage.add_to_history(data, client.profile)
610 610
611 def _historyGetSerialise(self, history_data): 611 def _history_get_serialise(self, history_data):
612 return [ 612 return [
613 (uid, timestamp, from_jid, to_jid, message, subject, mess_type, 613 (uid, timestamp, from_jid, to_jid, message, subject, mess_type,
614 data_format.serialise(extra)) for uid, timestamp, from_jid, to_jid, message, 614 data_format.serialise(extra)) for uid, timestamp, from_jid, to_jid, message,
615 subject, mess_type, extra in history_data 615 subject, mess_type, extra in history_data
616 ] 616 ]
617 617
618 def _historyGet(self, from_jid_s, to_jid_s, limit=C.HISTORY_LIMIT_NONE, between=True, 618 def _history_get(self, from_jid_s, to_jid_s, limit=C.HISTORY_LIMIT_NONE, between=True,
619 filters=None, profile=C.PROF_KEY_NONE): 619 filters=None, profile=C.PROF_KEY_NONE):
620 d = self.historyGet(jid.JID(from_jid_s), jid.JID(to_jid_s), limit, between, 620 d = self.history_get(jid.JID(from_jid_s), jid.JID(to_jid_s), limit, between,
621 filters, profile) 621 filters, profile)
622 d.addCallback(self._historyGetSerialise) 622 d.addCallback(self._history_get_serialise)
623 return d 623 return d
624 624
625 def historyGet(self, from_jid, to_jid, limit=C.HISTORY_LIMIT_NONE, between=True, 625 def history_get(self, from_jid, to_jid, limit=C.HISTORY_LIMIT_NONE, between=True,
626 filters=None, profile=C.PROF_KEY_NONE): 626 filters=None, profile=C.PROF_KEY_NONE):
627 """Retrieve messages in history 627 """Retrieve messages in history
628 628
629 @param from_jid (JID): source JID (full, or bare for catchall) 629 @param from_jid (JID): source JID (full, or bare for catchall)
630 @param to_jid (JID): dest JID (full, or bare for catchall) 630 @param to_jid (JID): dest JID (full, or bare for catchall)
634 - C.HISTORY_LIMIT_DEFAULT to use the HISTORY_LIMIT parameter value 634 - C.HISTORY_LIMIT_DEFAULT to use the HISTORY_LIMIT parameter value
635 @param between (bool): confound source and dest (ignore the direction) 635 @param between (bool): confound source and dest (ignore the direction)
636 @param filters (dict[unicode, unicode]): pattern to filter the history results 636 @param filters (dict[unicode, unicode]): pattern to filter the history results
637 (see bridge API for details) 637 (see bridge API for details)
638 @param profile (str): %(doc_profile)s 638 @param profile (str): %(doc_profile)s
639 @return (D(list)): list of message data as in [messageNew] 639 @return (D(list)): list of message data as in [message_new]
640 """ 640 """
641 assert profile != C.PROF_KEY_NONE 641 assert profile != C.PROF_KEY_NONE
642 if limit == C.HISTORY_LIMIT_DEFAULT: 642 if limit == C.HISTORY_LIMIT_DEFAULT:
643 limit = int(self.getParamA(C.HISTORY_LIMIT, "General", profile_key=profile)) 643 limit = int(self.param_get_a(C.HISTORY_LIMIT, "General", profile_key=profile))
644 elif limit == C.HISTORY_LIMIT_NONE: 644 elif limit == C.HISTORY_LIMIT_NONE:
645 limit = None 645 limit = None
646 if limit == 0: 646 if limit == 0:
647 return defer.succeed([]) 647 return defer.succeed([])
648 return self.storage.historyGet(from_jid, to_jid, limit, between, filters, profile) 648 return self.storage.history_get(from_jid, to_jid, limit, between, filters, profile)
649 649
650 ## Statuses ## 650 ## Statuses ##
651 651
652 def _getPresenceStatuses(self, profile_key): 652 def _get_presence_statuses(self, profile_key):
653 ret = self.getPresenceStatuses(profile_key) 653 ret = self.presence_statuses_get(profile_key)
654 return {entity.full(): data for entity, data in ret.items()} 654 return {entity.full(): data for entity, data in ret.items()}
655 655
656 def getPresenceStatuses(self, profile_key): 656 def presence_statuses_get(self, profile_key):
657 """Get all the presence statuses of a profile 657 """Get all the presence statuses of a profile
658 658
659 @param profile_key: %(doc_profile_key)s 659 @param profile_key: %(doc_profile_key)s
660 @return: presence data: key=entity JID, value=presence data for this entity 660 @return: presence data: key=entity JID, value=presence data for this entity
661 """ 661 """
662 client = self.host.getClient(profile_key) 662 client = self.host.get_client(profile_key)
663 profile_cache = self._getProfileCache(client) 663 profile_cache = self._get_profile_cache(client)
664 entities_presence = {} 664 entities_presence = {}
665 665
666 for entity_jid, entity_data in profile_cache.items(): 666 for entity_jid, entity_data in profile_cache.items():
667 for resource, resource_data in entity_data.items(): 667 for resource, resource_data in entity_data.items():
668 full_jid = copy.copy(entity_jid) 668 full_jid = copy.copy(entity_jid)
669 full_jid.resource = resource 669 full_jid.resource = resource
670 try: 670 try:
671 presence_data = self.getEntityDatum(client, full_jid, "presence") 671 presence_data = self.get_entity_datum(client, full_jid, "presence")
672 except KeyError: 672 except KeyError:
673 continue 673 continue
674 entities_presence.setdefault(entity_jid, {})[ 674 entities_presence.setdefault(entity_jid, {})[
675 resource or "" 675 resource or ""
676 ] = presence_data 676 ] = presence_data
677 677
678 return entities_presence 678 return entities_presence
679 679
680 def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key): 680 def set_presence_status(self, entity_jid, show, priority, statuses, profile_key):
681 """Change the presence status of an entity 681 """Change the presence status of an entity
682 682
683 @param entity_jid: jid.JID of the entity 683 @param entity_jid: jid.JID of the entity
684 @param show: show status 684 @param show: show status
685 @param priority: priority 685 @param priority: priority
686 @param statuses: dictionary of statuses 686 @param statuses: dictionary of statuses
687 @param profile_key: %(doc_profile_key)s 687 @param profile_key: %(doc_profile_key)s
688 """ 688 """
689 client = self.host.getClient(profile_key) 689 client = self.host.get_client(profile_key)
690 presence_data = PresenceTuple(show, priority, statuses) 690 presence_data = PresenceTuple(show, priority, statuses)
691 self.updateEntityData( 691 self.update_entity_data(
692 client, entity_jid, "presence", presence_data 692 client, entity_jid, "presence", presence_data
693 ) 693 )
694 if entity_jid.resource and show != C.PRESENCE_UNAVAILABLE: 694 if entity_jid.resource and show != C.PRESENCE_UNAVAILABLE:
695 # If a resource is available, bare jid should not have presence information 695 # If a resource is available, bare jid should not have presence information
696 try: 696 try:
697 self.delEntityDatum(client, entity_jid.userhostJID(), "presence") 697 self.del_entity_datum(client, entity_jid.userhostJID(), "presence")
698 except (KeyError, exceptions.UnknownEntityError): 698 except (KeyError, exceptions.UnknownEntityError):
699 pass 699 pass
700 700
701 ## Resources ## 701 ## Resources ##
702 702
703 def _getAllResource(self, jid_s, profile_key): 703 def _get_all_resource(self, jid_s, profile_key):
704 client = self.host.getClient(profile_key) 704 client = self.host.get_client(profile_key)
705 jid_ = jid.JID(jid_s) 705 jid_ = jid.JID(jid_s)
706 return self.getAllResources(client, jid_) 706 return self.get_all_resources(client, jid_)
707 707
708 def getAllResources(self, client, entity_jid): 708 def get_all_resources(self, client, entity_jid):
709 """Return all resource from jid for which we have had data in this session 709 """Return all resource from jid for which we have had data in this session
710 710
711 @param entity_jid: bare jid of the entity 711 @param entity_jid: bare jid of the entity
712 return (set[unicode]): set of resources 712 return (set[unicode]): set of resources
713 713
715 @raise ValueError: entity_jid has a resource 715 @raise ValueError: entity_jid has a resource
716 """ 716 """
717 # FIXME: is there a need to keep cache data for resources which are not connected anymore? 717 # FIXME: is there a need to keep cache data for resources which are not connected anymore?
718 if entity_jid.resource: 718 if entity_jid.resource:
719 raise ValueError( 719 raise ValueError(
720 "getAllResources must be used with a bare jid (got {})".format(entity_jid) 720 "get_all_resources must be used with a bare jid (got {})".format(entity_jid)
721 ) 721 )
722 profile_cache = self._getProfileCache(client) 722 profile_cache = self._get_profile_cache(client)
723 try: 723 try:
724 entity_data = profile_cache[entity_jid.userhostJID()] 724 entity_data = profile_cache[entity_jid.userhostJID()]
725 except KeyError: 725 except KeyError:
726 raise exceptions.UnknownEntityError( 726 raise exceptions.UnknownEntityError(
727 "Entity {} not in cache".format(entity_jid) 727 "Entity {} not in cache".format(entity_jid)
728 ) 728 )
729 resources = set(entity_data.keys()) 729 resources = set(entity_data.keys())
730 resources.discard(None) 730 resources.discard(None)
731 return resources 731 return resources
732 732
733 def getAvailableResources(self, client, entity_jid): 733 def get_available_resources(self, client, entity_jid):
734 """Return available resource for entity_jid 734 """Return available resource for entity_jid
735 735
736 This method differs from getAllResources by returning only available resources 736 This method differs from get_all_resources by returning only available resources
737 @param entity_jid: bare jid of the entit 737 @param entity_jid: bare jid of the entit
738 return (list[unicode]): list of available resources 738 return (list[unicode]): list of available resources
739 739
740 @raise exceptions.UnknownEntityError: if entity is not in cache 740 @raise exceptions.UnknownEntityError: if entity is not in cache
741 """ 741 """
742 available = [] 742 available = []
743 for resource in self.getAllResources(client, entity_jid): 743 for resource in self.get_all_resources(client, entity_jid):
744 full_jid = copy.copy(entity_jid) 744 full_jid = copy.copy(entity_jid)
745 full_jid.resource = resource 745 full_jid.resource = resource
746 try: 746 try:
747 presence_data = self.getEntityDatum(client, full_jid, "presence") 747 presence_data = self.get_entity_datum(client, full_jid, "presence")
748 except KeyError: 748 except KeyError:
749 log.debug("Can't get presence data for {}".format(full_jid)) 749 log.debug("Can't get presence data for {}".format(full_jid))
750 else: 750 else:
751 if presence_data.show != C.PRESENCE_UNAVAILABLE: 751 if presence_data.show != C.PRESENCE_UNAVAILABLE:
752 available.append(resource) 752 available.append(resource)
753 return available 753 return available
754 754
755 def _getMainResource(self, jid_s, profile_key): 755 def _get_main_resource(self, jid_s, profile_key):
756 client = self.host.getClient(profile_key) 756 client = self.host.get_client(profile_key)
757 jid_ = jid.JID(jid_s) 757 jid_ = jid.JID(jid_s)
758 return self.getMainResource(client, jid_) or "" 758 return self.main_resource_get(client, jid_) or ""
759 759
760 def getMainResource(self, client, entity_jid): 760 def main_resource_get(self, client, entity_jid):
761 """Return the main resource used by an entity 761 """Return the main resource used by an entity
762 762
763 @param entity_jid: bare entity jid 763 @param entity_jid: bare entity jid
764 @return (unicode): main resource or None 764 @return (unicode): main resource or None
765 """ 765 """
766 if entity_jid.resource: 766 if entity_jid.resource:
767 raise ValueError( 767 raise ValueError(
768 "getMainResource must be used with a bare jid (got {})".format(entity_jid) 768 "main_resource_get must be used with a bare jid (got {})".format(entity_jid)
769 ) 769 )
770 try: 770 try:
771 if self.host.plugins["XEP-0045"].isJoinedRoom(client, entity_jid): 771 if self.host.plugins["XEP-0045"].is_joined_room(client, entity_jid):
772 return None # MUC rooms have no main resource 772 return None # MUC rooms have no main resource
773 except KeyError: # plugin not found 773 except KeyError: # plugin not found
774 pass 774 pass
775 try: 775 try:
776 resources = self.getAllResources(client, entity_jid) 776 resources = self.get_all_resources(client, entity_jid)
777 except exceptions.UnknownEntityError: 777 except exceptions.UnknownEntityError:
778 log.warning("Entity is not in cache, we can't find any resource") 778 log.warning("Entity is not in cache, we can't find any resource")
779 return None 779 return None
780 priority_resources = [] 780 priority_resources = []
781 for resource in resources: 781 for resource in resources:
782 full_jid = copy.copy(entity_jid) 782 full_jid = copy.copy(entity_jid)
783 full_jid.resource = resource 783 full_jid.resource = resource
784 try: 784 try:
785 presence_data = self.getEntityDatum(client, full_jid, "presence") 785 presence_data = self.get_entity_datum(client, full_jid, "presence")
786 except KeyError: 786 except KeyError:
787 log.debug("No presence information for {}".format(full_jid)) 787 log.debug("No presence information for {}".format(full_jid))
788 continue 788 continue
789 priority_resources.append((resource, presence_data.priority)) 789 priority_resources.append((resource, presence_data.priority))
790 try: 790 try:
793 log.warning("No resource found at all for {}".format(entity_jid)) 793 log.warning("No resource found at all for {}".format(entity_jid))
794 return None 794 return None
795 795
796 ## Entities data ## 796 ## Entities data ##
797 797
798 def _getProfileCache(self, client): 798 def _get_profile_cache(self, client):
799 """Check profile validity and return its cache 799 """Check profile validity and return its cache
800 800
801 @param client: SatXMPPClient 801 @param client: SatXMPPClient
802 @return (dict): profile cache 802 @return (dict): profile cache
803 """ 803 """
804 return self._entities_cache[client.profile] 804 return self._entities_cache[client.profile]
805 805
806 def setSignalOnUpdate(self, key, signal=True): 806 def set_signal_on_update(self, key, signal=True):
807 """Set a signal flag on the key 807 """Set a signal flag on the key
808 808
809 When the key will be updated, a signal will be sent to frontends 809 When the key will be updated, a signal will be sent to frontends
810 @param key: key to signal 810 @param key: key to signal
811 @param signal(boolean): if True, do the signal 811 @param signal(boolean): if True, do the signal
813 if signal: 813 if signal:
814 self._key_signals.add(key) 814 self._key_signals.add(key)
815 else: 815 else:
816 self._key_signals.discard(key) 816 self._key_signals.discard(key)
817 817
818 def getAllEntitiesIter(self, client, with_bare=False): 818 def get_all_entities_iter(self, client, with_bare=False):
819 """Return an iterator of full jids of all entities in cache 819 """Return an iterator of full jids of all entities in cache
820 820
821 @param with_bare: if True, include bare jids 821 @param with_bare: if True, include bare jids
822 @return (list[unicode]): list of jids 822 @return (list[unicode]): list of jids
823 """ 823 """
824 profile_cache = self._getProfileCache(client) 824 profile_cache = self._get_profile_cache(client)
825 # we construct a list of all known full jids (bare jid of entities x resources) 825 # we construct a list of all known full jids (bare jid of entities x resources)
826 for bare_jid, entity_data in profile_cache.items(): 826 for bare_jid, entity_data in profile_cache.items():
827 for resource in entity_data.keys(): 827 for resource in entity_data.keys():
828 if resource is None: 828 if resource is None:
829 continue 829 continue
830 full_jid = copy.copy(bare_jid) 830 full_jid = copy.copy(bare_jid)
831 full_jid.resource = resource 831 full_jid.resource = resource
832 yield full_jid 832 yield full_jid
833 833
834 def updateEntityData( 834 def update_entity_data(
835 self, client, entity_jid, key, value, silent=False 835 self, client, entity_jid, key, value, silent=False
836 ): 836 ):
837 """Set a misc data for an entity 837 """Set a misc data for an entity
838 838
839 If key was registered with setSignalOnUpdate, a signal will be sent to frontends 839 If key was registered with set_signal_on_update, a signal will be sent to frontends
840 @param entity_jid: JID of the entity, C.ENTITY_ALL_RESOURCES for all resources of 840 @param entity_jid: JID of the entity, C.ENTITY_ALL_RESOURCES for all resources of
841 all entities, C.ENTITY_ALL for all entities (all resources + bare jids) 841 all entities, C.ENTITY_ALL for all entities (all resources + bare jids)
842 @param key: key to set (eg: C.ENTITY_TYPE) 842 @param key: key to set (eg: C.ENTITY_TYPE)
843 @param value: value for this key (eg: C.ENTITY_TYPE_MUC) 843 @param value: value for this key (eg: C.ENTITY_TYPE_MUC)
844 @param silent(bool): if True, doesn't send signal to frontend, even if there is a 844 @param silent(bool): if True, doesn't send signal to frontend, even if there is a
845 signal flag (see setSignalOnUpdate) 845 signal flag (see set_signal_on_update)
846 """ 846 """
847 profile_cache = self._getProfileCache(client) 847 profile_cache = self._get_profile_cache(client)
848 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL): 848 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL):
849 entities = self.getAllEntitiesIter(client, entity_jid == C.ENTITY_ALL) 849 entities = self.get_all_entities_iter(client, entity_jid == C.ENTITY_ALL)
850 else: 850 else:
851 entities = (entity_jid,) 851 entities = (entity_jid,)
852 852
853 for jid_ in entities: 853 for jid_ in entities:
854 entity_data = profile_cache.setdefault(jid_.userhostJID(), {}).setdefault( 854 entity_data = profile_cache.setdefault(jid_.userhostJID(), {}).setdefault(
855 jid_.resource, {} 855 jid_.resource, {}
856 ) 856 )
857 857
858 entity_data[key] = value 858 entity_data[key] = value
859 if key in self._key_signals and not silent: 859 if key in self._key_signals and not silent:
860 self.host.bridge.entityDataUpdated( 860 self.host.bridge.entity_data_updated(
861 jid_.full(), 861 jid_.full(),
862 key, 862 key,
863 data_format.serialise(value), 863 data_format.serialise(value),
864 client.profile 864 client.profile
865 ) 865 )
866 866
867 def delEntityDatum(self, client, entity_jid, key): 867 def del_entity_datum(self, client, entity_jid, key):
868 """Delete a data for an entity 868 """Delete a data for an entity
869 869
870 @param entity_jid: JID of the entity, C.ENTITY_ALL_RESOURCES for all resources of all entities, 870 @param entity_jid: JID of the entity, C.ENTITY_ALL_RESOURCES for all resources of all entities,
871 C.ENTITY_ALL for all entities (all resources + bare jids) 871 C.ENTITY_ALL for all entities (all resources + bare jids)
872 @param key: key to delete (eg: C.ENTITY_TYPE) 872 @param key: key to delete (eg: C.ENTITY_TYPE)
873 873
874 @raise exceptions.UnknownEntityError: if entity is not in cache 874 @raise exceptions.UnknownEntityError: if entity is not in cache
875 @raise KeyError: key is not in cache 875 @raise KeyError: key is not in cache
876 """ 876 """
877 profile_cache = self._getProfileCache(client) 877 profile_cache = self._get_profile_cache(client)
878 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL): 878 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL):
879 entities = self.getAllEntitiesIter(client, entity_jid == C.ENTITY_ALL) 879 entities = self.get_all_entities_iter(client, entity_jid == C.ENTITY_ALL)
880 else: 880 else:
881 entities = (entity_jid,) 881 entities = (entity_jid,)
882 882
883 for jid_ in entities: 883 for jid_ in entities:
884 try: 884 try:
893 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL): 893 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL):
894 continue # we ignore KeyError when deleting keys from several entities 894 continue # we ignore KeyError when deleting keys from several entities
895 else: 895 else:
896 raise e 896 raise e
897 897
898 def _getEntitiesData(self, entities_jids, keys_list, profile_key): 898 def _get_entities_data(self, entities_jids, keys_list, profile_key):
899 client = self.host.getClient(profile_key) 899 client = self.host.get_client(profile_key)
900 ret = self.getEntitiesData( 900 ret = self.entities_data_get(
901 client, [jid.JID(jid_) for jid_ in entities_jids], keys_list 901 client, [jid.JID(jid_) for jid_ in entities_jids], keys_list
902 ) 902 )
903 return { 903 return {
904 jid_.full(): {k: data_format.serialise(v) for k,v in data.items()} 904 jid_.full(): {k: data_format.serialise(v) for k,v in data.items()}
905 for jid_, data in ret.items() 905 for jid_, data in ret.items()
906 } 906 }
907 907
908 def getEntitiesData(self, client, entities_jids, keys_list=None): 908 def entities_data_get(self, client, entities_jids, keys_list=None):
909 """Get a list of cached values for several entities at once 909 """Get a list of cached values for several entities at once
910 910
911 @param entities_jids: jids of the entities, or empty list for all entities in cache 911 @param entities_jids: jids of the entities, or empty list for all entities in cache
912 @param keys_list (iterable,None): list of keys to get, None for everything 912 @param keys_list (iterable,None): list of keys to get, None for everything
913 @param profile_key: %(doc_profile_key)s 913 @param profile_key: %(doc_profile_key)s
918 in resulting dict 918 in resulting dict
919 919
920 @raise exceptions.UnknownEntityError: if entity is not in cache 920 @raise exceptions.UnknownEntityError: if entity is not in cache
921 """ 921 """
922 922
923 def fillEntityData(entity_cache_data): 923 def fill_entity_data(entity_cache_data):
924 entity_data = {} 924 entity_data = {}
925 if keys_list is None: 925 if keys_list is None:
926 entity_data = entity_cache_data 926 entity_data = entity_cache_data
927 else: 927 else:
928 for key in keys_list: 928 for key in keys_list:
930 entity_data[key] = entity_cache_data[key] 930 entity_data[key] = entity_cache_data[key]
931 except KeyError: 931 except KeyError:
932 continue 932 continue
933 return entity_data 933 return entity_data
934 934
935 profile_cache = self._getProfileCache(client) 935 profile_cache = self._get_profile_cache(client)
936 ret_data = {} 936 ret_data = {}
937 if entities_jids: 937 if entities_jids:
938 for entity in entities_jids: 938 for entity in entities_jids:
939 try: 939 try:
940 entity_cache_data = profile_cache[entity.userhostJID()][ 940 entity_cache_data = profile_cache[entity.userhostJID()][
941 entity.resource 941 entity.resource
942 ] 942 ]
943 except KeyError: 943 except KeyError:
944 continue 944 continue
945 ret_data[entity.full()] = fillEntityData(entity_cache_data, keys_list) 945 ret_data[entity.full()] = fill_entity_data(entity_cache_data, keys_list)
946 else: 946 else:
947 for bare_jid, data in profile_cache.items(): 947 for bare_jid, data in profile_cache.items():
948 for resource, entity_cache_data in data.items(): 948 for resource, entity_cache_data in data.items():
949 full_jid = copy.copy(bare_jid) 949 full_jid = copy.copy(bare_jid)
950 full_jid.resource = resource 950 full_jid.resource = resource
951 ret_data[full_jid] = fillEntityData(entity_cache_data) 951 ret_data[full_jid] = fill_entity_data(entity_cache_data)
952 952
953 return ret_data 953 return ret_data
954 954
955 def _getEntityData(self, entity_jid_s, keys_list=None, profile=C.PROF_KEY_NONE): 955 def _get_entity_data(self, entity_jid_s, keys_list=None, profile=C.PROF_KEY_NONE):
956 return self.getEntityData( 956 return self.entity_data_get(
957 self.host.getClient(profile), jid.JID(entity_jid_s), keys_list) 957 self.host.get_client(profile), jid.JID(entity_jid_s), keys_list)
958 958
959 def getEntityData(self, client, entity_jid, keys_list=None): 959 def entity_data_get(self, client, entity_jid, keys_list=None):
960 """Get a list of cached values for entity 960 """Get a list of cached values for entity
961 961
962 @param entity_jid: JID of the entity 962 @param entity_jid: JID of the entity
963 @param keys_list (iterable,None): list of keys to get, None for everything 963 @param keys_list (iterable,None): list of keys to get, None for everything
964 @param profile_key: %(doc_profile_key)s 964 @param profile_key: %(doc_profile_key)s
966 if there is no value of a given key, resulting dict will 966 if there is no value of a given key, resulting dict will
967 have nothing with that key nether 967 have nothing with that key nether
968 968
969 @raise exceptions.UnknownEntityError: if entity is not in cache 969 @raise exceptions.UnknownEntityError: if entity is not in cache
970 """ 970 """
971 profile_cache = self._getProfileCache(client) 971 profile_cache = self._get_profile_cache(client)
972 try: 972 try:
973 entity_data = profile_cache[entity_jid.userhostJID()][entity_jid.resource] 973 entity_data = profile_cache[entity_jid.userhostJID()][entity_jid.resource]
974 except KeyError: 974 except KeyError:
975 raise exceptions.UnknownEntityError( 975 raise exceptions.UnknownEntityError(
976 "Entity {} not in cache (was requesting {})".format( 976 "Entity {} not in cache (was requesting {})".format(
980 if keys_list is None: 980 if keys_list is None:
981 return entity_data 981 return entity_data
982 982
983 return {key: entity_data[key] for key in keys_list if key in entity_data} 983 return {key: entity_data[key] for key in keys_list if key in entity_data}
984 984
985 def getEntityDatum(self, client, entity_jid, key): 985 def get_entity_datum(self, client, entity_jid, key):
986 """Get a datum from entity 986 """Get a datum from entity
987 987
988 @param entity_jid: JID of the entity 988 @param entity_jid: JID of the entity
989 @param key: key to get 989 @param key: key to get
990 @return: requested value 990 @return: requested value
991 991
992 @raise exceptions.UnknownEntityError: if entity is not in cache 992 @raise exceptions.UnknownEntityError: if entity is not in cache
993 @raise KeyError: if there is no value for this key and this entity 993 @raise KeyError: if there is no value for this key and this entity
994 """ 994 """
995 return self.getEntityData(client, entity_jid, (key,))[key] 995 return self.entity_data_get(client, entity_jid, (key,))[key]
996 996
997 def delEntityCache( 997 def del_entity_cache(
998 self, entity_jid, delete_all_resources=True, profile_key=C.PROF_KEY_NONE 998 self, entity_jid, delete_all_resources=True, profile_key=C.PROF_KEY_NONE
999 ): 999 ):
1000 """Remove all cached data for entity 1000 """Remove all cached data for entity
1001 1001
1002 @param entity_jid: JID of the entity to delete 1002 @param entity_jid: JID of the entity to delete
1003 @param delete_all_resources: if True also delete all known resources from cache (a bare jid must be given in this case) 1003 @param delete_all_resources: if True also delete all known resources from cache (a bare jid must be given in this case)
1004 @param profile_key: %(doc_profile_key)s 1004 @param profile_key: %(doc_profile_key)s
1005 1005
1006 @raise exceptions.UnknownEntityError: if entity is not in cache 1006 @raise exceptions.UnknownEntityError: if entity is not in cache
1007 """ 1007 """
1008 client = self.host.getClient(profile_key) 1008 client = self.host.get_client(profile_key)
1009 profile_cache = self._getProfileCache(client) 1009 profile_cache = self._get_profile_cache(client)
1010 1010
1011 if delete_all_resources: 1011 if delete_all_resources:
1012 if entity_jid.resource: 1012 if entity_jid.resource:
1013 raise ValueError(_("Need a bare jid to delete all resources")) 1013 raise ValueError(_("Need a bare jid to delete all resources"))
1014 try: 1014 try:
1025 "Entity {} not in cache".format(entity_jid) 1025 "Entity {} not in cache".format(entity_jid)
1026 ) 1026 )
1027 1027
1028 ## Encryption ## 1028 ## Encryption ##
1029 1029
1030 def encryptValue(self, value, profile): 1030 def encrypt_value(self, value, profile):
1031 """Encrypt a value for the given profile. The personal key must be loaded 1031 """Encrypt a value for the given profile. The personal key must be loaded
1032 already in the profile session, that should be the case if the profile is 1032 already in the profile session, that should be the case if the profile is
1033 already authenticated. 1033 already authenticated.
1034 1034
1035 @param value (str): the value to encrypt 1035 @param value (str): the value to encrypt
1036 @param profile (str): %(doc_profile)s 1036 @param profile (str): %(doc_profile)s
1037 @return: the deferred encrypted value 1037 @return: the deferred encrypted value
1038 """ 1038 """
1039 try: 1039 try:
1040 personal_key = self.auth_sessions.profileGetUnique(profile)[ 1040 personal_key = self.auth_sessions.profile_get_unique(profile)[
1041 C.MEMORY_CRYPTO_KEY 1041 C.MEMORY_CRYPTO_KEY
1042 ] 1042 ]
1043 except TypeError: 1043 except TypeError:
1044 raise exceptions.InternalError( 1044 raise exceptions.InternalError(
1045 _("Trying to encrypt a value for %s while the personal key is undefined!") 1045 _("Trying to encrypt a value for %s while the personal key is undefined!")
1046 % profile 1046 % profile
1047 ) 1047 )
1048 return BlockCipher.encrypt(personal_key, value) 1048 return BlockCipher.encrypt(personal_key, value)
1049 1049
1050 def decryptValue(self, value, profile): 1050 def decrypt_value(self, value, profile):
1051 """Decrypt a value for the given profile. The personal key must be loaded 1051 """Decrypt a value for the given profile. The personal key must be loaded
1052 already in the profile session, that should be the case if the profile is 1052 already in the profile session, that should be the case if the profile is
1053 already authenticated. 1053 already authenticated.
1054 1054
1055 @param value (str): the value to decrypt 1055 @param value (str): the value to decrypt
1056 @param profile (str): %(doc_profile)s 1056 @param profile (str): %(doc_profile)s
1057 @return: the deferred decrypted value 1057 @return: the deferred decrypted value
1058 """ 1058 """
1059 try: 1059 try:
1060 personal_key = self.auth_sessions.profileGetUnique(profile)[ 1060 personal_key = self.auth_sessions.profile_get_unique(profile)[
1061 C.MEMORY_CRYPTO_KEY 1061 C.MEMORY_CRYPTO_KEY
1062 ] 1062 ]
1063 except TypeError: 1063 except TypeError:
1064 raise exceptions.InternalError( 1064 raise exceptions.InternalError(
1065 _("Trying to decrypt a value for %s while the personal key is undefined!") 1065 _("Trying to decrypt a value for %s while the personal key is undefined!")
1066 % profile 1066 % profile
1067 ) 1067 )
1068 return BlockCipher.decrypt(personal_key, value) 1068 return BlockCipher.decrypt(personal_key, value)
1069 1069
1070 def encryptPersonalData(self, data_key, data_value, crypto_key, profile): 1070 def encrypt_personal_data(self, data_key, data_value, crypto_key, profile):
1071 """Re-encrypt a personal data (saved to a PersistentDict). 1071 """Re-encrypt a personal data (saved to a PersistentDict).
1072 1072
1073 @param data_key: key for the individual PersistentDict instance 1073 @param data_key: key for the individual PersistentDict instance
1074 @param data_value: the value to be encrypted 1074 @param data_value: the value to be encrypted
1075 @param crypto_key: the key to encrypt the value 1075 @param crypto_key: the key to encrypt the value
1076 @param profile: %(profile_doc)s 1076 @param profile: %(profile_doc)s
1077 @return: a deferred None value 1077 @return: a deferred None value
1078 """ 1078 """
1079 1079
1080 def gotIndMemory(data): 1080 def got_ind_memory(data):
1081 data[data_key] = BlockCipher.encrypt(crypto_key, data_value) 1081 data[data_key] = BlockCipher.encrypt(crypto_key, data_value)
1082 return data.force(data_key) 1082 return data.force(data_key)
1083 1083
1084 def done(__): 1084 def done(__):
1085 log.debug( 1085 log.debug(
1086 _("Personal data (%(ns)s, %(key)s) has been successfuly encrypted") 1086 _("Personal data (%(ns)s, %(key)s) has been successfuly encrypted")
1087 % {"ns": C.MEMORY_CRYPTO_NAMESPACE, "key": data_key} 1087 % {"ns": C.MEMORY_CRYPTO_NAMESPACE, "key": data_key}
1088 ) 1088 )
1089 1089
1090 d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load() 1090 d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load()
1091 return d.addCallback(gotIndMemory).addCallback(done) 1091 return d.addCallback(got_ind_memory).addCallback(done)
1092 1092
1093 ## Subscription requests ## 1093 ## Subscription requests ##
1094 1094
1095 def addWaitingSub(self, type_, entity_jid, profile_key): 1095 def add_waiting_sub(self, type_, entity_jid, profile_key):
1096 """Called when a subcription request is received""" 1096 """Called when a subcription request is received"""
1097 profile = self.getProfileName(profile_key) 1097 profile = self.get_profile_name(profile_key)
1098 assert profile 1098 assert profile
1099 if profile not in self.subscriptions: 1099 if profile not in self.subscriptions:
1100 self.subscriptions[profile] = {} 1100 self.subscriptions[profile] = {}
1101 self.subscriptions[profile][entity_jid] = type_ 1101 self.subscriptions[profile][entity_jid] = type_
1102 1102
1103 def delWaitingSub(self, entity_jid, profile_key): 1103 def del_waiting_sub(self, entity_jid, profile_key):
1104 """Called when a subcription request is finished""" 1104 """Called when a subcription request is finished"""
1105 profile = self.getProfileName(profile_key) 1105 profile = self.get_profile_name(profile_key)
1106 assert profile 1106 assert profile
1107 if profile in self.subscriptions and entity_jid in self.subscriptions[profile]: 1107 if profile in self.subscriptions and entity_jid in self.subscriptions[profile]:
1108 del self.subscriptions[profile][entity_jid] 1108 del self.subscriptions[profile][entity_jid]
1109 1109
1110 def getWaitingSub(self, profile_key): 1110 def sub_waiting_get(self, profile_key):
1111 """Called to get a list of currently waiting subscription requests""" 1111 """Called to get a list of currently waiting subscription requests"""
1112 profile = self.getProfileName(profile_key) 1112 profile = self.get_profile_name(profile_key)
1113 if not profile: 1113 if not profile:
1114 log.error(_("Asking waiting subscriptions for a non-existant profile")) 1114 log.error(_("Asking waiting subscriptions for a non-existant profile"))
1115 return {} 1115 return {}
1116 if profile not in self.subscriptions: 1116 if profile not in self.subscriptions:
1117 return {} 1117 return {}
1118 1118
1119 return self.subscriptions[profile] 1119 return self.subscriptions[profile]
1120 1120
1121 ## Parameters ## 1121 ## Parameters ##
1122 1122
1123 def getStringParamA(self, name, category, attr="value", profile_key=C.PROF_KEY_NONE): 1123 def get_string_param_a(self, name, category, attr="value", profile_key=C.PROF_KEY_NONE):
1124 return self.params.getStringParamA(name, category, attr, profile_key) 1124 return self.params.get_string_param_a(name, category, attr, profile_key)
1125 1125
1126 def getParamA(self, name, category, attr="value", profile_key=C.PROF_KEY_NONE): 1126 def param_get_a(self, name, category, attr="value", profile_key=C.PROF_KEY_NONE):
1127 return self.params.getParamA(name, category, attr, profile_key=profile_key) 1127 return self.params.param_get_a(name, category, attr, profile_key=profile_key)
1128 1128
1129 def asyncGetParamA( 1129 def param_get_a_async(
1130 self, 1130 self,
1131 name, 1131 name,
1132 category, 1132 category,
1133 attr="value", 1133 attr="value",
1134 security_limit=C.NO_SECURITY_LIMIT, 1134 security_limit=C.NO_SECURITY_LIMIT,
1135 profile_key=C.PROF_KEY_NONE, 1135 profile_key=C.PROF_KEY_NONE,
1136 ): 1136 ):
1137 return self.params.asyncGetParamA( 1137 return self.params.param_get_a_async(
1138 name, category, attr, security_limit, profile_key 1138 name, category, attr, security_limit, profile_key
1139 ) 1139 )
1140 1140
1141 def _getParamsValuesFromCategory( 1141 def _get_params_values_from_category(
1142 self, category, security_limit, app, extra_s, profile_key 1142 self, category, security_limit, app, extra_s, profile_key
1143 ): 1143 ):
1144 return self.params._getParamsValuesFromCategory( 1144 return self.params._get_params_values_from_category(
1145 category, security_limit, app, extra_s, profile_key 1145 category, security_limit, app, extra_s, profile_key
1146 ) 1146 )
1147 1147
1148 def asyncGetStringParamA( 1148 def async_get_string_param_a(
1149 self, name, category, attribute="value", security_limit=C.NO_SECURITY_LIMIT, 1149 self, name, category, attribute="value", security_limit=C.NO_SECURITY_LIMIT,
1150 profile_key=C.PROF_KEY_NONE): 1150 profile_key=C.PROF_KEY_NONE):
1151 1151
1152 profile = self.getProfileName(profile_key) 1152 profile = self.get_profile_name(profile_key)
1153 return defer.ensureDeferred(self.params.asyncGetStringParamA( 1153 return defer.ensureDeferred(self.params.async_get_string_param_a(
1154 name, category, attribute, security_limit, profile 1154 name, category, attribute, security_limit, profile
1155 )) 1155 ))
1156 1156
1157 def _getParamsUI(self, security_limit, app, extra_s, profile_key): 1157 def _get_params_ui(self, security_limit, app, extra_s, profile_key):
1158 return self.params._getParamsUI(security_limit, app, extra_s, profile_key) 1158 return self.params._get_params_ui(security_limit, app, extra_s, profile_key)
1159 1159
1160 def getParamsCategories(self): 1160 def params_categories_get(self):
1161 return self.params.getParamsCategories() 1161 return self.params.params_categories_get()
1162 1162
1163 def setParam( 1163 def param_set(
1164 self, 1164 self,
1165 name, 1165 name,
1166 value, 1166 value,
1167 category, 1167 category,
1168 security_limit=C.NO_SECURITY_LIMIT, 1168 security_limit=C.NO_SECURITY_LIMIT,
1169 profile_key=C.PROF_KEY_NONE, 1169 profile_key=C.PROF_KEY_NONE,
1170 ): 1170 ):
1171 return self.params.setParam(name, value, category, security_limit, profile_key) 1171 return self.params.param_set(name, value, category, security_limit, profile_key)
1172 1172
1173 def updateParams(self, xml): 1173 def update_params(self, xml):
1174 return self.params.updateParams(xml) 1174 return self.params.update_params(xml)
1175 1175
1176 def paramsRegisterApp(self, xml, security_limit=C.NO_SECURITY_LIMIT, app=""): 1176 def params_register_app(self, xml, security_limit=C.NO_SECURITY_LIMIT, app=""):
1177 return self.params.paramsRegisterApp(xml, security_limit, app) 1177 return self.params.params_register_app(xml, security_limit, app)
1178 1178
1179 def setDefault(self, name, category, callback, errback=None): 1179 def set_default(self, name, category, callback, errback=None):
1180 return self.params.setDefault(name, category, callback, errback) 1180 return self.params.set_default(name, category, callback, errback)
1181 1181
1182 ## Private Data ## 1182 ## Private Data ##
1183 1183
1184 def _privateDataSet(self, namespace, key, data_s, profile_key): 1184 def _private_data_set(self, namespace, key, data_s, profile_key):
1185 client = self.host.getClient(profile_key) 1185 client = self.host.get_client(profile_key)
1186 # we accept any type 1186 # we accept any type
1187 data = data_format.deserialise(data_s, type_check=None) 1187 data = data_format.deserialise(data_s, type_check=None)
1188 return defer.ensureDeferred(self.storage.setPrivateValue( 1188 return defer.ensureDeferred(self.storage.set_private_value(
1189 namespace, key, data, binary=True, profile=client.profile)) 1189 namespace, key, data, binary=True, profile=client.profile))
1190 1190
1191 def _privateDataGet(self, namespace, key, profile_key): 1191 def _private_data_get(self, namespace, key, profile_key):
1192 client = self.host.getClient(profile_key) 1192 client = self.host.get_client(profile_key)
1193 d = defer.ensureDeferred( 1193 d = defer.ensureDeferred(
1194 self.storage.getPrivates( 1194 self.storage.get_privates(
1195 namespace, [key], binary=True, profile=client.profile) 1195 namespace, [key], binary=True, profile=client.profile)
1196 ) 1196 )
1197 d.addCallback(lambda data_dict: data_format.serialise(data_dict.get(key))) 1197 d.addCallback(lambda data_dict: data_format.serialise(data_dict.get(key)))
1198 return d 1198 return d
1199 1199
1200 def _privateDataDelete(self, namespace, key, profile_key): 1200 def _private_data_delete(self, namespace, key, profile_key):
1201 client = self.host.getClient(profile_key) 1201 client = self.host.get_client(profile_key)
1202 return defer.ensureDeferred(self.storage.delPrivateValue( 1202 return defer.ensureDeferred(self.storage.del_private_value(
1203 namespace, key, binary=True, profile=client.profile)) 1203 namespace, key, binary=True, profile=client.profile))
1204 1204
1205 ## Files ## 1205 ## Files ##
1206 1206
1207 def checkFilePermission( 1207 def check_file_permission(
1208 self, 1208 self,
1209 file_data: dict, 1209 file_data: dict,
1210 peer_jid: Optional[jid.JID], 1210 peer_jid: Optional[jid.JID],
1211 perms_to_check: Optional[Tuple[str]], 1211 perms_to_check: Optional[Tuple[str]],
1212 set_affiliation: bool = False 1212 set_affiliation: bool = False
1213 ) -> None: 1213 ) -> None:
1214 """Check that an entity has the right permission on a file 1214 """Check that an entity has the right permission on a file
1215 1215
1216 @param file_data: data of one file, as returned by getFiles 1216 @param file_data: data of one file, as returned by get_files
1217 @param peer_jid: entity trying to access the file 1217 @param peer_jid: entity trying to access the file
1218 @param perms_to_check: permissions to check 1218 @param perms_to_check: permissions to check
1219 tuple of C.ACCESS_PERM_* 1219 tuple of C.ACCESS_PERM_*
1220 @param check_parents: if True, also check all parents until root node 1220 @param check_parents: if True, also check all parents until root node
1221 @parma set_affiliation: if True, "affiliation" metadata will be set 1221 @parma set_affiliation: if True, "affiliation" metadata will be set
1266 else: 1266 else:
1267 raise exceptions.InternalError( 1267 raise exceptions.InternalError(
1268 _("unknown access type: {type}").format(type=perm_type) 1268 _("unknown access type: {type}").format(type=perm_type)
1269 ) 1269 )
1270 1270
1271 async def checkPermissionToRoot(self, client, file_data, peer_jid, perms_to_check): 1271 async def check_permission_to_root(self, client, file_data, peer_jid, perms_to_check):
1272 """do checkFilePermission on file_data and all its parents until root""" 1272 """do check_file_permission on file_data and all its parents until root"""
1273 current = file_data 1273 current = file_data
1274 while True: 1274 while True:
1275 self.checkFilePermission(current, peer_jid, perms_to_check) 1275 self.check_file_permission(current, peer_jid, perms_to_check)
1276 parent = current["parent"] 1276 parent = current["parent"]
1277 if not parent: 1277 if not parent:
1278 break 1278 break
1279 files_data = await self.getFiles( 1279 files_data = await self.get_files(
1280 client, peer_jid=None, file_id=parent, perms_to_check=None 1280 client, peer_jid=None, file_id=parent, perms_to_check=None
1281 ) 1281 )
1282 try: 1282 try:
1283 current = files_data[0] 1283 current = files_data[0]
1284 except IndexError: 1284 except IndexError:
1285 raise exceptions.DataError("Missing parent") 1285 raise exceptions.DataError("Missing parent")
1286 1286
1287 async def _getParentDir( 1287 async def _get_parent_dir(
1288 self, client, path, parent, namespace, owner, peer_jid, perms_to_check 1288 self, client, path, parent, namespace, owner, peer_jid, perms_to_check
1289 ): 1289 ):
1290 """Retrieve parent node from a path, or last existing directory 1290 """Retrieve parent node from a path, or last existing directory
1291 1291
1292 each directory of the path will be retrieved, until the last existing one 1292 each directory of the path will be retrieved, until the last existing one
1306 1306
1307 # we retrieve all directories from path until we get the parent container 1307 # we retrieve all directories from path until we get the parent container
1308 # non existing directories will be created 1308 # non existing directories will be created
1309 parent = "" 1309 parent = ""
1310 for idx, path_elt in enumerate(path_elts): 1310 for idx, path_elt in enumerate(path_elts):
1311 directories = await self.storage.getFiles( 1311 directories = await self.storage.get_files(
1312 client, 1312 client,
1313 parent=parent, 1313 parent=parent,
1314 type_=C.FILE_TYPE_DIRECTORY, 1314 type_=C.FILE_TYPE_DIRECTORY,
1315 name=path_elt, 1315 name=path_elt,
1316 namespace=namespace, 1316 namespace=namespace,
1323 raise exceptions.InternalError( 1323 raise exceptions.InternalError(
1324 _("Several directories found, this should not happen") 1324 _("Several directories found, this should not happen")
1325 ) 1325 )
1326 else: 1326 else:
1327 directory = directories[0] 1327 directory = directories[0]
1328 self.checkFilePermission(directory, peer_jid, perms_to_check) 1328 self.check_file_permission(directory, peer_jid, perms_to_check)
1329 parent = directory["id"] 1329 parent = directory["id"]
1330 return (parent, []) 1330 return (parent, [])
1331 1331
1332 def getFileAffiliations(self, file_data: dict) -> Dict[jid.JID, str]: 1332 def get_file_affiliations(self, file_data: dict) -> Dict[jid.JID, str]:
1333 """Convert file access to pubsub like affiliations""" 1333 """Convert file access to pubsub like affiliations"""
1334 affiliations = {} 1334 affiliations = {}
1335 access_data = file_data['access'] 1335 access_data = file_data['access']
1336 1336
1337 read_data = access_data.get(C.ACCESS_PERM_READ, {}) 1337 read_data = access_data.get(C.ACCESS_PERM_READ, {})
1350 if owner: 1350 if owner:
1351 affiliations[owner] = 'owner' 1351 affiliations[owner] = 'owner'
1352 1352
1353 return affiliations 1353 return affiliations
1354 1354
1355 def _setFileAffiliationsUpdate( 1355 def _set_file_affiliations_update(
1356 self, 1356 self,
1357 access: dict, 1357 access: dict,
1358 file_data: dict, 1358 file_data: dict,
1359 affiliations: Dict[jid.JID, str] 1359 affiliations: Dict[jid.JID, str]
1360 ) -> None: 1360 ) -> None:
1399 elif affiliation == "owner": 1399 elif affiliation == "owner":
1400 raise NotImplementedError('"owner" affiliation can\'t be set') 1400 raise NotImplementedError('"owner" affiliation can\'t be set')
1401 else: 1401 else:
1402 raise ValueError(f"unknown affiliation: {affiliation!r}") 1402 raise ValueError(f"unknown affiliation: {affiliation!r}")
1403 1403
1404 async def setFileAffiliations( 1404 async def set_file_affiliations(
1405 self, 1405 self,
1406 client, 1406 client,
1407 file_data: dict, 1407 file_data: dict,
1408 affiliations: Dict[jid.JID, str] 1408 affiliations: Dict[jid.JID, str]
1409 ) -> None: 1409 ) -> None:
1415 - "publisher" gives read and write permissions 1415 - "publisher" gives read and write permissions
1416 - "member" gives read permission only 1416 - "member" gives read permission only
1417 - "none" removes both read and write permissions 1417 - "none" removes both read and write permissions
1418 """ 1418 """
1419 file_id = file_data['id'] 1419 file_id = file_data['id']
1420 await self.fileUpdate( 1420 await self.file_update(
1421 file_id, 1421 file_id,
1422 'access', 1422 'access',
1423 update_cb=partial( 1423 update_cb=partial(
1424 self._setFileAffiliationsUpdate, 1424 self._set_file_affiliations_update,
1425 file_data=file_data, 1425 file_data=file_data,
1426 affiliations=affiliations 1426 affiliations=affiliations
1427 ), 1427 ),
1428 ) 1428 )
1429 1429
1430 def _setFileAccessModelUpdate( 1430 def _set_file_access_model_update(
1431 self, 1431 self,
1432 access: dict, 1432 access: dict,
1433 file_data: dict, 1433 file_data: dict,
1434 access_model: str 1434 access_model: str
1435 ) -> None: 1435 ) -> None:
1443 1443
1444 read_data['type'] = requested_type 1444 read_data['type'] = requested_type
1445 if requested_type == C.ACCESS_TYPE_WHITELIST and 'jids' not in read_data: 1445 if requested_type == C.ACCESS_TYPE_WHITELIST and 'jids' not in read_data:
1446 read_data['jids'] = [] 1446 read_data['jids'] = []
1447 1447
1448 async def setFileAccessModel( 1448 async def set_file_access_model(
1449 self, 1449 self,
1450 client, 1450 client,
1451 file_data: dict, 1451 file_data: dict,
1452 access_model: str, 1452 access_model: str,
1453 ) -> None: 1453 ) -> None:
1456 Only 2 access models are supported so far: 1456 Only 2 access models are supported so far:
1457 - "open": set public access to file/dir 1457 - "open": set public access to file/dir
1458 - "whitelist": set whitelist to file/dir 1458 - "whitelist": set whitelist to file/dir
1459 """ 1459 """
1460 file_id = file_data['id'] 1460 file_id = file_data['id']
1461 await self.fileUpdate( 1461 await self.file_update(
1462 file_id, 1462 file_id,
1463 'access', 1463 'access',
1464 update_cb=partial( 1464 update_cb=partial(
1465 self._setFileAccessModelUpdate, 1465 self._set_file_access_model_update,
1466 file_data=file_data, 1466 file_data=file_data,
1467 access_model=access_model 1467 access_model=access_model
1468 ), 1468 ),
1469 ) 1469 )
1470 1470
1471 def getFilesOwner( 1471 def get_files_owner(
1472 self, 1472 self,
1473 client, 1473 client,
1474 owner: Optional[jid.JID], 1474 owner: Optional[jid.JID],
1475 peer_jid: Optional[jid.JID], 1475 peer_jid: Optional[jid.JID],
1476 file_id: Optional[str] = None, 1476 file_id: Optional[str] = None,
1497 raise exceptions.InternalError( 1497 raise exceptions.InternalError(
1498 "Owner must be set for component if peer_jid is None" 1498 "Owner must be set for component if peer_jid is None"
1499 ) 1499 )
1500 return peer_jid.userhostJID() 1500 return peer_jid.userhostJID()
1501 1501
1502 async def getFiles( 1502 async def get_files(
1503 self, client, peer_jid, file_id=None, version=None, parent=None, path=None, 1503 self, client, peer_jid, file_id=None, version=None, parent=None, path=None,
1504 type_=None, file_hash=None, hash_algo=None, name=None, namespace=None, 1504 type_=None, file_hash=None, hash_algo=None, name=None, namespace=None,
1505 mime_type=None, public_id=None, owner=None, access=None, projection=None, 1505 mime_type=None, public_id=None, owner=None, access=None, projection=None,
1506 unique=False, perms_to_check=(C.ACCESS_PERM_READ,)): 1506 unique=False, perms_to_check=(C.ACCESS_PERM_READ,)):
1507 """Retrieve files with with given filters 1507 """Retrieve files with with given filters
1524 @param name(unicode, None): name of the file to retrieve 1524 @param name(unicode, None): name of the file to retrieve
1525 @param namespace(unicode, None): namespace of the files to retrieve 1525 @param namespace(unicode, None): namespace of the files to retrieve
1526 @param mime_type(unicode, None): filter on this mime type 1526 @param mime_type(unicode, None): filter on this mime type
1527 @param public_id(unicode, None): filter on this public id 1527 @param public_id(unicode, None): filter on this public id
1528 @param owner(jid.JID, None): if not None, only get files from this owner 1528 @param owner(jid.JID, None): if not None, only get files from this owner
1529 @param access(dict, None): get file with given access (see [setFile]) 1529 @param access(dict, None): get file with given access (see [set_file])
1530 @param projection(list[unicode], None): name of columns to retrieve 1530 @param projection(list[unicode], None): name of columns to retrieve
1531 None to retrieve all 1531 None to retrieve all
1532 @param unique(bool): if True will remove duplicates 1532 @param unique(bool): if True will remove duplicates
1533 @param perms_to_check(tuple[unicode],None): permission to check 1533 @param perms_to_check(tuple[unicode],None): permission to check
1534 must be a tuple of C.ACCESS_PERM_* or None 1534 must be a tuple of C.ACCESS_PERM_* or None
1535 if None, permission will no be checked (peer_jid must be None too in this 1535 if None, permission will no be checked (peer_jid must be None too in this
1536 case) 1536 case)
1537 other params are the same as for [setFile] 1537 other params are the same as for [set_file]
1538 @return (list[dict]): files corresponding to filters 1538 @return (list[dict]): files corresponding to filters
1539 @raise exceptions.NotFound: parent directory not found (when path is specified) 1539 @raise exceptions.NotFound: parent directory not found (when path is specified)
1540 @raise exceptions.PermissionError: peer_jid can't use perms_to_check for one of 1540 @raise exceptions.PermissionError: peer_jid can't use perms_to_check for one of
1541 the file 1541 the file
1542 on the path 1542 on the path
1544 if peer_jid is None and perms_to_check or perms_to_check is None and peer_jid: 1544 if peer_jid is None and perms_to_check or perms_to_check is None and peer_jid:
1545 raise exceptions.InternalError( 1545 raise exceptions.InternalError(
1546 "if you want to disable permission check, both peer_jid and " 1546 "if you want to disable permission check, both peer_jid and "
1547 "perms_to_check must be None" 1547 "perms_to_check must be None"
1548 ) 1548 )
1549 owner = self.getFilesOwner(client, owner, peer_jid, file_id, parent) 1549 owner = self.get_files_owner(client, owner, peer_jid, file_id, parent)
1550 if path is not None: 1550 if path is not None:
1551 path = str(path) 1551 path = str(path)
1552 # permission are checked by _getParentDir 1552 # permission are checked by _get_parent_dir
1553 parent, remaining_path_elts = await self._getParentDir( 1553 parent, remaining_path_elts = await self._get_parent_dir(
1554 client, path, parent, namespace, owner, peer_jid, perms_to_check 1554 client, path, parent, namespace, owner, peer_jid, perms_to_check
1555 ) 1555 )
1556 if remaining_path_elts: 1556 if remaining_path_elts:
1557 # if we have remaining path elements, 1557 # if we have remaining path elements,
1558 # the parent directory is not found 1558 # the parent directory is not found
1559 raise failure.Failure(exceptions.NotFound()) 1559 raise failure.Failure(exceptions.NotFound())
1560 if parent and peer_jid: 1560 if parent and peer_jid:
1561 # if parent is given directly and permission check is requested, 1561 # if parent is given directly and permission check is requested,
1562 # we need to check all the parents 1562 # we need to check all the parents
1563 parent_data = await self.storage.getFiles(client, file_id=parent) 1563 parent_data = await self.storage.get_files(client, file_id=parent)
1564 try: 1564 try:
1565 parent_data = parent_data[0] 1565 parent_data = parent_data[0]
1566 except IndexError: 1566 except IndexError:
1567 raise exceptions.DataError("mising parent") 1567 raise exceptions.DataError("mising parent")
1568 await self.checkPermissionToRoot( 1568 await self.check_permission_to_root(
1569 client, parent_data, peer_jid, perms_to_check 1569 client, parent_data, peer_jid, perms_to_check
1570 ) 1570 )
1571 1571
1572 files = await self.storage.getFiles( 1572 files = await self.storage.get_files(
1573 client, 1573 client,
1574 file_id=file_id, 1574 file_id=file_id,
1575 version=version, 1575 version=version,
1576 parent=parent, 1576 parent=parent,
1577 type_=type_, 1577 type_=type_,
1590 if peer_jid: 1590 if peer_jid:
1591 # if permission are checked, we must remove all file that user can't access 1591 # if permission are checked, we must remove all file that user can't access
1592 to_remove = [] 1592 to_remove = []
1593 for file_data in files: 1593 for file_data in files:
1594 try: 1594 try:
1595 self.checkFilePermission( 1595 self.check_file_permission(
1596 file_data, peer_jid, perms_to_check, set_affiliation=True 1596 file_data, peer_jid, perms_to_check, set_affiliation=True
1597 ) 1597 )
1598 except exceptions.PermissionError: 1598 except exceptions.PermissionError:
1599 to_remove.append(file_data) 1599 to_remove.append(file_data)
1600 for file_data in to_remove: 1600 for file_data in to_remove:
1601 files.remove(file_data) 1601 files.remove(file_data)
1602 return files 1602 return files
1603 1603
1604 async def setFile( 1604 async def set_file(
1605 self, client, name, file_id=None, version="", parent=None, path=None, 1605 self, client, name, file_id=None, version="", parent=None, path=None,
1606 type_=C.FILE_TYPE_FILE, file_hash=None, hash_algo=None, size=None, 1606 type_=C.FILE_TYPE_FILE, file_hash=None, hash_algo=None, size=None,
1607 namespace=None, mime_type=None, public_id=None, created=None, modified=None, 1607 namespace=None, mime_type=None, public_id=None, created=None, modified=None,
1608 owner=None, access=None, extra=None, peer_jid=None, 1608 owner=None, access=None, extra=None, peer_jid=None,
1609 perms_to_check=(C.ACCESS_PERM_WRITE,) 1609 perms_to_check=(C.ACCESS_PERM_WRITE,)
1676 if type_ == C.FILE_TYPE_DIRECTORY: 1676 if type_ == C.FILE_TYPE_DIRECTORY:
1677 if any((version, file_hash, size, mime_type)): 1677 if any((version, file_hash, size, mime_type)):
1678 raise ValueError( 1678 raise ValueError(
1679 "version, file_hash, size and mime_type can't be set for a directory" 1679 "version, file_hash, size and mime_type can't be set for a directory"
1680 ) 1680 )
1681 owner = self.getFilesOwner(client, owner, peer_jid, file_id, parent) 1681 owner = self.get_files_owner(client, owner, peer_jid, file_id, parent)
1682 1682
1683 if path is not None: 1683 if path is not None:
1684 path = str(path) 1684 path = str(path)
1685 # _getParentDir will check permissions if peer_jid is set, so we use owner 1685 # _get_parent_dir will check permissions if peer_jid is set, so we use owner
1686 parent, remaining_path_elts = await self._getParentDir( 1686 parent, remaining_path_elts = await self._get_parent_dir(
1687 client, path, parent, namespace, owner, owner, perms_to_check 1687 client, path, parent, namespace, owner, owner, perms_to_check
1688 ) 1688 )
1689 # if remaining directories don't exist, we have to create them 1689 # if remaining directories don't exist, we have to create them
1690 for new_dir in remaining_path_elts: 1690 for new_dir in remaining_path_elts:
1691 new_dir_id = shortuuid.uuid() 1691 new_dir_id = shortuuid.uuid()
1692 await self.storage.setFile( 1692 await self.storage.set_file(
1693 client, 1693 client,
1694 name=new_dir, 1694 name=new_dir,
1695 file_id=new_dir_id, 1695 file_id=new_dir_id,
1696 version="", 1696 version="",
1697 parent=parent, 1697 parent=parent,
1704 ) 1704 )
1705 parent = new_dir_id 1705 parent = new_dir_id
1706 elif parent is None: 1706 elif parent is None:
1707 parent = "" 1707 parent = ""
1708 1708
1709 await self.storage.setFile( 1709 await self.storage.set_file(
1710 client, 1710 client,
1711 file_id=file_id, 1711 file_id=file_id,
1712 version=version, 1712 version=version,
1713 parent=parent, 1713 parent=parent,
1714 type_=type_, 1714 type_=type_,
1724 owner=owner, 1724 owner=owner,
1725 access=access, 1725 access=access,
1726 extra=extra, 1726 extra=extra,
1727 ) 1727 )
1728 1728
1729 async def fileGetUsedSpace( 1729 async def file_get_used_space(
1730 self, 1730 self,
1731 client, 1731 client,
1732 peer_jid: jid.JID, 1732 peer_jid: jid.JID,
1733 owner: Optional[jid.JID] = None 1733 owner: Optional[jid.JID] = None
1734 ) -> int: 1734 ) -> int:
1735 """Get space taken by all files owned by an entity 1735 """Get space taken by all files owned by an entity
1736 1736
1737 @param peer_jid: entity requesting the size 1737 @param peer_jid: entity requesting the size
1738 @param owner: entity owning the file to check. If None, will be determined by 1738 @param owner: entity owning the file to check. If None, will be determined by
1739 getFilesOwner 1739 get_files_owner
1740 @return: size of total space used by files of this owner 1740 @return: size of total space used by files of this owner
1741 """ 1741 """
1742 owner = self.getFilesOwner(client, owner, peer_jid) 1742 owner = self.get_files_owner(client, owner, peer_jid)
1743 if peer_jid.userhostJID() != owner and client.profile not in self.admins: 1743 if peer_jid.userhostJID() != owner and client.profile not in self.admins:
1744 raise exceptions.PermissionError("You are not allowed to check this size") 1744 raise exceptions.PermissionError("You are not allowed to check this size")
1745 return await self.storage.fileGetUsedSpace(client, owner) 1745 return await self.storage.file_get_used_space(client, owner)
1746 1746
1747 def fileUpdate(self, file_id, column, update_cb): 1747 def file_update(self, file_id, column, update_cb):
1748 """Update a file column taking care of race condition 1748 """Update a file column taking care of race condition
1749 1749
1750 access is NOT checked in this method, it must be checked beforehand 1750 access is NOT checked in this method, it must be checked beforehand
1751 @param file_id(unicode): id of the file to update 1751 @param file_id(unicode): id of the file to update
1752 @param column(unicode): one of "access" or "extra" 1752 @param column(unicode): one of "access" or "extra"
1753 @param update_cb(callable): method to update the value of the colum 1753 @param update_cb(callable): method to update the value of the colum
1754 the method will take older value as argument, and must update it in place 1754 the method will take older value as argument, and must update it in place
1755 Note that the callable must be thread-safe 1755 Note that the callable must be thread-safe
1756 """ 1756 """
1757 return self.storage.fileUpdate(file_id, column, update_cb) 1757 return self.storage.file_update(file_id, column, update_cb)
1758 1758
1759 @defer.inlineCallbacks 1759 @defer.inlineCallbacks
1760 def _deleteFile( 1760 def _delete_file(
1761 self, 1761 self,
1762 client, 1762 client,
1763 peer_jid: jid.JID, 1763 peer_jid: jid.JID,
1764 recursive: bool, 1764 recursive: bool,
1765 files_path: Path, 1765 files_path: Path,
1776 if file_data['owner'] != peer_jid: 1776 if file_data['owner'] != peer_jid:
1777 raise exceptions.PermissionError( 1777 raise exceptions.PermissionError(
1778 "file {file_name} can't be deleted, {peer_jid} is not the owner" 1778 "file {file_name} can't be deleted, {peer_jid} is not the owner"
1779 .format(file_name=file_data['name'], peer_jid=peer_jid.full())) 1779 .format(file_name=file_data['name'], peer_jid=peer_jid.full()))
1780 if file_data['type'] == C.FILE_TYPE_DIRECTORY: 1780 if file_data['type'] == C.FILE_TYPE_DIRECTORY:
1781 sub_files = yield self.getFiles(client, peer_jid, parent=file_data['id']) 1781 sub_files = yield self.get_files(client, peer_jid, parent=file_data['id'])
1782 if sub_files and not recursive: 1782 if sub_files and not recursive:
1783 raise exceptions.DataError(_("Can't delete directory, it is not empty")) 1783 raise exceptions.DataError(_("Can't delete directory, it is not empty"))
1784 # we first delete the sub-files 1784 # we first delete the sub-files
1785 for sub_file_data in sub_files: 1785 for sub_file_data in sub_files:
1786 if sub_file_data['type'] == C.FILE_TYPE_DIRECTORY: 1786 if sub_file_data['type'] == C.FILE_TYPE_DIRECTORY:
1787 sub_file_path = files_path / sub_file_data['name'] 1787 sub_file_path = files_path / sub_file_data['name']
1788 else: 1788 else:
1789 sub_file_path = files_path 1789 sub_file_path = files_path
1790 yield self._deleteFile( 1790 yield self._delete_file(
1791 client, peer_jid, recursive, sub_file_path, sub_file_data) 1791 client, peer_jid, recursive, sub_file_path, sub_file_data)
1792 # then the directory itself 1792 # then the directory itself
1793 yield self.storage.fileDelete(file_data['id']) 1793 yield self.storage.file_delete(file_data['id'])
1794 elif file_data['type'] == C.FILE_TYPE_FILE: 1794 elif file_data['type'] == C.FILE_TYPE_FILE:
1795 log.info(_("deleting file {name} with hash {file_hash}").format( 1795 log.info(_("deleting file {name} with hash {file_hash}").format(
1796 name=file_data['name'], file_hash=file_data['file_hash'])) 1796 name=file_data['name'], file_hash=file_data['file_hash']))
1797 yield self.storage.fileDelete(file_data['id']) 1797 yield self.storage.file_delete(file_data['id'])
1798 references = yield self.getFiles( 1798 references = yield self.get_files(
1799 client, peer_jid, file_hash=file_data['file_hash']) 1799 client, peer_jid, file_hash=file_data['file_hash'])
1800 if references: 1800 if references:
1801 log.debug("there are still references to the file, we keep it") 1801 log.debug("there are still references to the file, we keep it")
1802 else: 1802 else:
1803 file_path = os.path.join(files_path, file_data['file_hash']) 1803 file_path = os.path.join(files_path, file_data['file_hash'])
1809 log.error(f"file at {file_path!r} doesn't exist but it was referenced in files database") 1809 log.error(f"file at {file_path!r} doesn't exist but it was referenced in files database")
1810 else: 1810 else:
1811 raise exceptions.InternalError('Unexpected file type: {file_type}' 1811 raise exceptions.InternalError('Unexpected file type: {file_type}'
1812 .format(file_type=file_data['type'])) 1812 .format(file_type=file_data['type']))
1813 1813
1814 async def fileDelete(self, client, peer_jid, file_id, recursive=False): 1814 async def file_delete(self, client, peer_jid, file_id, recursive=False):
1815 """Delete a single file or a directory and all its sub-files 1815 """Delete a single file or a directory and all its sub-files
1816 1816
1817 @param file_id(unicode): id of the file to delete 1817 @param file_id(unicode): id of the file to delete
1818 @param peer_jid(jid.JID): entity requesting the deletion, 1818 @param peer_jid(jid.JID): entity requesting the deletion,
1819 must be owner of all files to delete 1819 must be owner of all files to delete
1820 @param recursive(boolean): must be True to delete a directory and all sub-files 1820 @param recursive(boolean): must be True to delete a directory and all sub-files
1821 """ 1821 """
1822 # FIXME: we only allow owner of file to delete files for now, but WRITE access 1822 # FIXME: we only allow owner of file to delete files for now, but WRITE access
1823 # should be checked too 1823 # should be checked too
1824 files_data = await self.getFiles(client, peer_jid, file_id) 1824 files_data = await self.get_files(client, peer_jid, file_id)
1825 if not files_data: 1825 if not files_data:
1826 raise exceptions.NotFound("Can't find the file with id {file_id}".format( 1826 raise exceptions.NotFound("Can't find the file with id {file_id}".format(
1827 file_id=file_id)) 1827 file_id=file_id))
1828 file_data = files_data[0] 1828 file_data = files_data[0]
1829 if file_data["type"] != C.FILE_TYPE_DIRECTORY and recursive: 1829 if file_data["type"] != C.FILE_TYPE_DIRECTORY and recursive:
1830 raise ValueError("recursive can only be set for directories") 1830 raise ValueError("recursive can only be set for directories")
1831 files_path = self.host.get_local_path(None, C.FILES_DIR) 1831 files_path = self.host.get_local_path(None, C.FILES_DIR)
1832 await self._deleteFile(client, peer_jid, recursive, files_path, file_data) 1832 await self._delete_file(client, peer_jid, recursive, files_path, file_data)
1833 1833
1834 ## Cache ## 1834 ## Cache ##
1835 1835
1836 def getCachePath(self, namespace: str, *args: str) -> Path: 1836 def get_cache_path(self, namespace: str, *args: str) -> Path:
1837 """Get path to use to get a common path for a namespace 1837 """Get path to use to get a common path for a namespace
1838 1838
1839 This can be used by plugins to manage permanent data. It's the responsability 1839 This can be used by plugins to manage permanent data. It's the responsability
1840 of plugins to clean this directory from unused data. 1840 of plugins to clean this directory from unused data.
1841 @param namespace: unique namespace to use 1841 @param namespace: unique namespace to use
1842 @param args: extra identifier which will be added to the path 1842 @param args: extra identifier which will be added to the path
1843 """ 1843 """
1844 namespace = namespace.strip().lower() 1844 namespace = namespace.strip().lower()
1845 return Path( 1845 return Path(
1846 self._cache_path, 1846 self._cache_path,
1847 regex.pathEscape(namespace), 1847 regex.path_escape(namespace),
1848 *(regex.pathEscape(a) for a in args) 1848 *(regex.path_escape(a) for a in args)
1849 ) 1849 )
1850 1850
1851 ## Misc ## 1851 ## Misc ##
1852 1852
1853 def isEntityAvailable(self, client, entity_jid): 1853 def is_entity_available(self, client, entity_jid):
1854 """Tell from the presence information if the given entity is available. 1854 """Tell from the presence information if the given entity is available.
1855 1855
1856 @param entity_jid (JID): the entity to check (if bare jid is used, all resources are tested) 1856 @param entity_jid (JID): the entity to check (if bare jid is used, all resources are tested)
1857 @return (bool): True if entity is available 1857 @return (bool): True if entity is available
1858 """ 1858 """
1859 if not entity_jid.resource: 1859 if not entity_jid.resource:
1860 return bool( 1860 return bool(
1861 self.getAvailableResources(client, entity_jid) 1861 self.get_available_resources(client, entity_jid)
1862 ) # is any resource is available, entity is available 1862 ) # is any resource is available, entity is available
1863 try: 1863 try:
1864 presence_data = self.getEntityDatum(client, entity_jid, "presence") 1864 presence_data = self.get_entity_datum(client, entity_jid, "presence")
1865 except KeyError: 1865 except KeyError:
1866 log.debug("No presence information for {}".format(entity_jid)) 1866 log.debug("No presence information for {}".format(entity_jid))
1867 return False 1867 return False
1868 return presence_data.show != C.PRESENCE_UNAVAILABLE 1868 return presence_data.show != C.PRESENCE_UNAVAILABLE
1869 1869
1870 def isAdmin(self, profile: str) -> bool: 1870 def is_admin(self, profile: str) -> bool:
1871 """Tell if given profile has administrator privileges""" 1871 """Tell if given profile has administrator privileges"""
1872 return profile in self.admins 1872 return profile in self.admins
1873 1873
1874 def isAdminJID(self, entity: jid.JID) -> bool: 1874 def is_admin_jid(self, entity: jid.JID) -> bool:
1875 """Tells if an entity jid correspond to an admin one 1875 """Tells if an entity jid correspond to an admin one
1876 1876
1877 It is sometime not possible to use the profile alone to check if an entity is an 1877 It is sometime not possible to use the profile alone to check if an entity is an
1878 admin (e.g. a request managed by a component). In this case we check if the JID 1878 admin (e.g. a request managed by a component). In this case we check if the JID
1879 correspond to an admin profile 1879 correspond to an admin profile