comparison sat/memory/memory.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 9446f1ea9eac
children 5060cbeec01e
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _ 20 from sat.core.i18n import _
21 21
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23
23 log = getLogger(__name__) 24 log = getLogger(__name__)
24 25
25 import os.path 26 import os.path
26 import copy 27 import copy
27 from collections import namedtuple 28 from collections import namedtuple
42 import shortuuid 43 import shortuuid
43 import mimetypes 44 import mimetypes
44 import time 45 import time
45 46
46 47
47 PresenceTuple = namedtuple("PresenceTuple", ('show', 'priority', 'statuses')) 48 PresenceTuple = namedtuple("PresenceTuple", ("show", "priority", "statuses"))
48 MSG_NO_SESSION = "Session id doesn't exist or is finished" 49 MSG_NO_SESSION = "Session id doesn't exist or is finished"
50
49 51
50 class Sessions(object): 52 class Sessions(object):
51 """Sessions are data associated to key used for a temporary moment, with optional profile checking.""" 53 """Sessions are data associated to key used for a temporary moment, with optional profile checking."""
54
52 DEFAULT_TIMEOUT = 600 55 DEFAULT_TIMEOUT = 600
53 56
54 def __init__(self, timeout=None, resettable_timeout=True): 57 def __init__(self, timeout=None, resettable_timeout=True):
55 """ 58 """
56 @param timeout (int): nb of seconds before session destruction 59 @param timeout (int): nb of seconds before session destruction
70 @return: session_id, session_data 73 @return: session_id, session_data
71 """ 74 """
72 if session_id is None: 75 if session_id is None:
73 session_id = str(uuid4()) 76 session_id = str(uuid4())
74 elif session_id in self._sessions: 77 elif session_id in self._sessions:
75 raise exceptions.ConflictError(u"Session id {} is already used".format(session_id)) 78 raise exceptions.ConflictError(
79 u"Session id {} is already used".format(session_id)
80 )
76 timer = reactor.callLater(self.timeout, self._purgeSession, session_id) 81 timer = reactor.callLater(self.timeout, self._purgeSession, session_id)
77 if session_data is None: 82 if session_data is None:
78 session_data = {} 83 session_data = {}
79 self._sessions[session_id] = (timer, session_data) if profile is None else (timer, session_data, profile) 84 self._sessions[session_id] = (
85 (timer, session_data) if profile is None else (timer, session_data, profile)
86 )
80 return session_id, session_data 87 return session_id, session_data
81 88
82 def _purgeSession(self, session_id): 89 def _purgeSession(self, session_id):
83 try: 90 try:
84 timer, session_data, profile = self._sessions[session_id] 91 timer, session_data, profile = self._sessions[session_id]
89 timer.cancel() 96 timer.cancel()
90 except error.AlreadyCalled: 97 except error.AlreadyCalled:
91 # if the session is time-outed, the timer has been called 98 # if the session is time-outed, the timer has been called
92 pass 99 pass
93 del self._sessions[session_id] 100 del self._sessions[session_id]
94 log.debug(u"Session {} purged{}".format(session_id, u' (profile {})'.format(profile) if profile is not None else u'')) 101 log.debug(
102 u"Session {} purged{}".format(
103 session_id,
104 u" (profile {})".format(profile) if profile is not None else u"",
105 )
106 )
95 107
96 def __len__(self): 108 def __len__(self):
97 return len(self._sessions) 109 return len(self._sessions)
98 110
99 def __contains__(self, session_id): 111 def __contains__(self, session_id):
101 113
102 def profileGet(self, session_id, profile): 114 def profileGet(self, session_id, profile):
103 try: 115 try:
104 timer, session_data, profile_set = self._sessions[session_id] 116 timer, session_data, profile_set = self._sessions[session_id]
105 except ValueError: 117 except ValueError:
106 raise exceptions.InternalError("You need to use __getitem__ when profile is not set") 118 raise exceptions.InternalError(
119 "You need to use __getitem__ when profile is not set"
120 )
107 except KeyError: 121 except KeyError:
108 raise failure.Failure(KeyError(MSG_NO_SESSION)) 122 raise failure.Failure(KeyError(MSG_NO_SESSION))
109 if profile_set != profile: 123 if profile_set != profile:
110 raise exceptions.InternalError("current profile differ from set profile !") 124 raise exceptions.InternalError("current profile differ from set profile !")
111 if self.resettable_timeout: 125 if self.resettable_timeout:
114 128
115 def __getitem__(self, session_id): 129 def __getitem__(self, session_id):
116 try: 130 try:
117 timer, session_data = self._sessions[session_id] 131 timer, session_data = self._sessions[session_id]
118 except ValueError: 132 except ValueError:
119 raise exceptions.InternalError("You need to use profileGet instead of __getitem__ when profile is set") 133 raise exceptions.InternalError(
134 "You need to use profileGet instead of __getitem__ when profile is set"
135 )
120 except KeyError: 136 except KeyError:
121 raise failure.Failure(KeyError(MSG_NO_SESSION)) 137 raise failure.Failure(KeyError(MSG_NO_SESSION))
122 if self.resettable_timeout: 138 if self.resettable_timeout:
123 timer.reset(self.timeout) 139 timer.reset(self.timeout)
124 return session_data 140 return session_data
167 - None if no session is associated to the profile 183 - None if no session is associated to the profile
168 - raise an error if more than one session are found 184 - raise an error if more than one session are found
169 """ 185 """
170 ids = self._profileGetAllIds(profile) 186 ids = self._profileGetAllIds(profile)
171 if len(ids) > 1: 187 if len(ids) > 1:
172 raise exceptions.InternalError('profileGetUnique has been used but more than one session has been found!') 188 raise exceptions.InternalError(
173 return self.profileGet(ids[0], profile) if len(ids) == 1 else None # XXX: timeout might be reset 189 "profileGetUnique has been used but more than one session has been found!"
190 )
191 return (
192 self.profileGet(ids[0], profile) if len(ids) == 1 else None
193 ) # XXX: timeout might be reset
174 194
175 def profileDelUnique(self, profile): 195 def profileDelUnique(self, profile):
176 """Delete the unique session that is associated to the given profile. 196 """Delete the unique session that is associated to the given profile.
177 197
178 @param profile: %(doc_profile)s 198 @param profile: %(doc_profile)s
179 @return: None, but raise an error if more than one session are found 199 @return: None, but raise an error if more than one session are found
180 """ 200 """
181 ids = self._profileGetAllIds(profile) 201 ids = self._profileGetAllIds(profile)
182 if len(ids) > 1: 202 if len(ids) > 1:
183 raise exceptions.InternalError('profileDelUnique has been used but more than one session has been found!') 203 raise exceptions.InternalError(
204 "profileDelUnique has been used but more than one session has been found!"
205 )
184 if len(ids) == 1: 206 if len(ids) == 1:
185 del self._sessions[ids[0]] 207 del self._sessions[ids[0]]
186 208
187 209
188 class PasswordSessions(ProfileSessions): 210 class PasswordSessions(ProfileSessions):
192 # profile password should be asked again in order to decrypt it. 214 # profile password should be asked again in order to decrypt it.
193 def __init__(self, timeout=None): 215 def __init__(self, timeout=None):
194 ProfileSessions.__init__(self, timeout, resettable_timeout=False) 216 ProfileSessions.__init__(self, timeout, resettable_timeout=False)
195 217
196 def _purgeSession(self, session_id): 218 def _purgeSession(self, session_id):
197 log.debug("FIXME: PasswordSessions should ask for the profile password after the session expired") 219 log.debug(
220 "FIXME: PasswordSessions should ask for the profile password after the session expired"
221 )
198 222
199 223
200 # XXX: tmp update code, will be removed in the future 224 # XXX: tmp update code, will be removed in the future
201 # When you remove this, please add the default value for 225 # When you remove this, please add the default value for
202 # 'local_dir' in sat.core.constants.Const.DEFAULT_CONFIG 226 # 'local_dir' in sat.core.constants.Const.DEFAULT_CONFIG
209 try: 233 try:
210 user_config.read(C.CONFIG_FILES) 234 user_config.read(C.CONFIG_FILES)
211 except: 235 except:
212 pass # file is readable but its structure if wrong 236 pass # file is readable but its structure if wrong
213 try: 237 try:
214 current_value = user_config.get('DEFAULT', 'local_dir') 238 current_value = user_config.get("DEFAULT", "local_dir")
215 except (NoOptionError, NoSectionError): 239 except (NoOptionError, NoSectionError):
216 current_value = '' 240 current_value = ""
217 if current_value: 241 if current_value:
218 return # nothing to do 242 return # nothing to do
219 old_default = '~/.sat' 243 old_default = "~/.sat"
220 if os.path.isfile(os.path.expanduser(old_default) + '/' + C.SAVEFILE_DATABASE): 244 if os.path.isfile(os.path.expanduser(old_default) + "/" + C.SAVEFILE_DATABASE):
221 if not silent: 245 if not silent:
222 log.warning(_(u"A database has been found in the default local_dir for previous versions (< 0.5)")) 246 log.warning(
223 tools_config.fixConfigOption('', 'local_dir', old_default, silent) 247 _(
248 u"A database has been found in the default local_dir for previous versions (< 0.5)"
249 )
250 )
251 tools_config.fixConfigOption("", "local_dir", old_default, silent)
224 252
225 253
226 class Memory(object): 254 class Memory(object):
227 """This class manage all the persistent information""" 255 """This class manage all the persistent information"""
228 256
229 def __init__(self, host): 257 def __init__(self, host):
230 log.info(_("Memory manager init")) 258 log.info(_("Memory manager init"))
231 self.initialized = defer.Deferred() 259 self.initialized = defer.Deferred()
232 self.host = host 260 self.host = host
233 self._entities_cache = {} # XXX: keep presence/last resource/other data in cache 261 self._entities_cache = {} # XXX: keep presence/last resource/other data in cache
234 # /!\ an entity is not necessarily in roster 262 # /!\ an entity is not necessarily in roster
235 # main key is bare jid, value is a dict 263 # main key is bare jid, value is a dict
236 # where main key is resource, or None for bare jid 264 # where main key is resource, or None for bare jid
237 self._key_signals = set() # key which need a signal to frontends when updated 265 self._key_signals = set() # key which need a signal to frontends when updated
238 self.subscriptions = {} 266 self.subscriptions = {}
239 self.auth_sessions = PasswordSessions() # remember the authenticated profiles 267 self.auth_sessions = PasswordSessions() # remember the authenticated profiles
240 self.disco = Discovery(host) 268 self.disco = Discovery(host)
241 fixLocalDir(False) # XXX: tmp update code, will be removed in the future 269 fixLocalDir(False) # XXX: tmp update code, will be removed in the future
242 self.config = tools_config.parseMainConf() 270 self.config = tools_config.parseMainConf()
243 database_file = os.path.expanduser(os.path.join(self.getConfig('', 'local_dir'), C.SAVEFILE_DATABASE)) 271 database_file = os.path.expanduser(
272 os.path.join(self.getConfig("", "local_dir"), C.SAVEFILE_DATABASE)
273 )
244 self.storage = SqliteStorage(database_file, host.version) 274 self.storage = SqliteStorage(database_file, host.version)
245 PersistentDict.storage = self.storage 275 PersistentDict.storage = self.storage
246 self.params = Params(host, self.storage) 276 self.params = Params(host, self.storage)
247 log.info(_("Loading default params template")) 277 log.info(_("Loading default params template"))
248 self.params.load_default_params() 278 self.params.load_default_params()
288 @param filename (str): output file 318 @param filename (str): output file
289 @return: bool: True in case of success 319 @return: bool: True in case of success
290 """ 320 """
291 if not filename: 321 if not filename:
292 return False 322 return False
293 #TODO: need to encrypt files (at least passwords !) and set permissions 323 # TODO: need to encrypt files (at least passwords !) and set permissions
294 filename = os.path.expanduser(filename) 324 filename = os.path.expanduser(filename)
295 try: 325 try:
296 self.params.save_xml(filename) 326 self.params.save_xml(filename)
297 log.debug(_(u"Parameters saved to file: %s") % filename) 327 log.debug(_(u"Parameters saved to file: %s") % filename)
298 return True 328 return True
300 log.error(_(u"Can't save parameters to file: %s") % e) 330 log.error(_(u"Can't save parameters to file: %s") % e)
301 return False 331 return False
302 332
303 def load(self): 333 def load(self):
304 """Load parameters and all memory things from db""" 334 """Load parameters and all memory things from db"""
305 #parameters data 335 # parameters data
306 return self.params.loadGenParams() 336 return self.params.loadGenParams()
307 337
308 def loadIndividualParams(self, profile): 338 def loadIndividualParams(self, profile):
309 """Load individual parameters for a profile 339 """Load individual parameters for a profile
310 @param profile: %(doc_profile)s""" 340 @param profile: %(doc_profile)s"""
338 # if there is a value at this point in self._entities_cache, 368 # if there is a value at this point in self._entities_cache,
339 # it is the loadIndividualParams Deferred, the session is starting 369 # it is the loadIndividualParams Deferred, the session is starting
340 session_d = self._entities_cache[profile] 370 session_d = self._entities_cache[profile]
341 except KeyError: 371 except KeyError:
342 # else we do request the params 372 # else we do request the params
343 session_d = self._entities_cache[profile] = self.loadIndividualParams(profile) 373 session_d = self._entities_cache[profile] = self.loadIndividualParams(
374 profile
375 )
344 session_d.addCallback(createSession) 376 session_d.addCallback(createSession)
345 finally: 377 finally:
346 return session_d 378 return session_d
347 379
348 auth_d = self.profileAuthenticate(password, profile) 380 auth_d = self.profileAuthenticate(password, profile)
394 # submitting a form with empty passwords is restricted to local frontends. 426 # submitting a form with empty passwords is restricted to local frontends.
395 return defer.succeed(None) 427 return defer.succeed(None)
396 428
397 def check_result(result): 429 def check_result(result):
398 if not result: 430 if not result:
399 log.warning(u'Authentication failure of profile {}'.format(profile)) 431 log.warning(u"Authentication failure of profile {}".format(profile))
400 raise failure.Failure(exceptions.PasswordError(u"The provided profile password doesn't match.")) 432 raise failure.Failure(
401 if not session_data: # avoid to create two profile sessions when password if specified 433 exceptions.PasswordError(
434 u"The provided profile password doesn't match."
435 )
436 )
437 if (
438 not session_data
439 ): # avoid to create two profile sessions when password if specified
402 return self.newAuthSession(password, profile) 440 return self.newAuthSession(password, profile)
403 441
404 d = self.asyncGetParamA(C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile) 442 d = self.asyncGetParamA(
443 C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile
444 )
405 d.addCallback(lambda sat_cipher: PasswordHasher.verify(password, sat_cipher)) 445 d.addCallback(lambda sat_cipher: PasswordHasher.verify(password, sat_cipher))
406 return d.addCallback(check_result) 446 return d.addCallback(check_result)
407 447
408 def newAuthSession(self, key, profile): 448 def newAuthSession(self, key, profile):
409 """Start a new session for the authenticated profile. 449 """Start a new session for the authenticated profile.
412 452
413 @param key: the key to decrypt the personal key 453 @param key: the key to decrypt the personal key
414 @param profile: %(doc_profile)s 454 @param profile: %(doc_profile)s
415 @return: a deferred None value 455 @return: a deferred None value
416 """ 456 """
457
417 def gotPersonalKey(personal_key): 458 def gotPersonalKey(personal_key):
418 """Create the session for this profile and store the personal key""" 459 """Create the session for this profile and store the personal key"""
419 self.auth_sessions.newSession({C.MEMORY_CRYPTO_KEY: personal_key}, profile=profile) 460 self.auth_sessions.newSession(
420 log.debug(u'auth session created for profile %s' % profile) 461 {C.MEMORY_CRYPTO_KEY: personal_key}, profile=profile
462 )
463 log.debug(u"auth session created for profile %s" % profile)
421 464
422 d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load() 465 d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load()
423 d.addCallback(lambda data: BlockCipher.decrypt(key, data[C.MEMORY_CRYPTO_KEY])) 466 d.addCallback(lambda data: BlockCipher.decrypt(key, data[C.MEMORY_CRYPTO_KEY]))
424 return d.addCallback(gotPersonalKey) 467 return d.addCallback(gotPersonalKey)
425 468
429 log.info(_("[%s] Profile session purge" % profile)) 472 log.info(_("[%s] Profile session purge" % profile))
430 self.params.purgeProfile(profile) 473 self.params.purgeProfile(profile)
431 try: 474 try:
432 del self._entities_cache[profile] 475 del self._entities_cache[profile]
433 except KeyError: 476 except KeyError:
434 log.error(_(u"Trying to purge roster status cache for a profile not in memory: [%s]") % profile) 477 log.error(
478 _(
479 u"Trying to purge roster status cache for a profile not in memory: [%s]"
480 )
481 % profile
482 )
435 483
436 def getProfilesList(self, clients=True, components=False): 484 def getProfilesList(self, clients=True, components=False):
437 """retrieve profiles list 485 """retrieve profiles list
438 486
439 @param clients(bool): if True return clients profiles 487 @param clients(bool): if True return clients profiles
470 @param profile: %(doc_profile)s 518 @param profile: %(doc_profile)s
471 """ 519 """
472 # we want to be sure that the profile exists 520 # we want to be sure that the profile exists
473 profile = self.getProfileName(profile) 521 profile = self.getProfileName(profile)
474 522
475 self.memory_data['Profile_default'] = profile 523 self.memory_data["Profile_default"] = profile
476 524
477 def createProfile(self, name, password, component=None): 525 def createProfile(self, name, password, component=None):
478 """Create a new profile 526 """Create a new profile
479 527
480 @param name(unicode): profile name 528 @param name(unicode): profile name
484 @return: Deferred 532 @return: Deferred
485 @raise exceptions.NotFound: component is not a known plugin import name 533 @raise exceptions.NotFound: component is not a known plugin import name
486 """ 534 """
487 if not name: 535 if not name:
488 raise ValueError(u"Empty profile name") 536 raise ValueError(u"Empty profile name")
489 if name[0] == '@': 537 if name[0] == "@":
490 raise ValueError(u"A profile name can't start with a '@'") 538 raise ValueError(u"A profile name can't start with a '@'")
491 if '\n' in name: 539 if "\n" in name:
492 raise ValueError(u"A profile name can't contain line feed ('\\n')") 540 raise ValueError(u"A profile name can't contain line feed ('\\n')")
493 541
494 if name in self._entities_cache: 542 if name in self._entities_cache:
495 raise exceptions.ConflictError(u"A session for this profile exists") 543 raise exceptions.ConflictError(u"A session for this profile exists")
496 544
497 if component: 545 if component:
498 if not component in self.host.plugins: 546 if not component in self.host.plugins:
499 raise exceptions.NotFound(_(u"Can't find component {component} entry point".format( 547 raise exceptions.NotFound(
500 component = component))) 548 _(
549 u"Can't find component {component} entry point".format(
550 component=component
551 )
552 )
553 )
501 # FIXME: PLUGIN_INFO is not currently accessible after import, but type shoul be tested here 554 # FIXME: PLUGIN_INFO is not currently accessible after import, but type shoul be tested here
502 # if self.host.plugins[component].PLUGIN_INFO[u"type"] != C.PLUG_TYPE_ENTRY_POINT: 555 #  if self.host.plugins[component].PLUGIN_INFO[u"type"] != C.PLUG_TYPE_ENTRY_POINT:
503 #  raise ValueError(_(u"Plugin {component} is not an entry point !".format( 556 #   raise ValueError(_(u"Plugin {component} is not an entry point !".format(
504 #  component = component))) 557 #   component = component)))
505 558
506 d = self.params.createProfile(name, component) 559 d = self.params.createProfile(name, component)
507 560
508 def initPersonalKey(dummy): 561 def initPersonalKey(dummy):
509 # be sure to call this after checking that the profile doesn't exist yet 562 # be sure to call this after checking that the profile doesn't exist yet
510 personal_key = BlockCipher.getRandomKey(base64=True) # generated once for all and saved in a PersistentDict 563 personal_key = BlockCipher.getRandomKey(
511 self.auth_sessions.newSession({C.MEMORY_CRYPTO_KEY: personal_key}, profile=name) # will be encrypted by setParam 564 base64=True
565 ) # generated once for all and saved in a PersistentDict
566 self.auth_sessions.newSession(
567 {C.MEMORY_CRYPTO_KEY: personal_key}, profile=name
568 ) # will be encrypted by setParam
512 569
513 def startFakeSession(dummy): 570 def startFakeSession(dummy):
514 # avoid ProfileNotConnected exception in setParam 571 # avoid ProfileNotConnected exception in setParam
515 self._entities_cache[name] = None 572 self._entities_cache[name] = None
516 self.params.loadIndParams(name) 573 self.params.loadIndParams(name)
519 del self._entities_cache[name] 576 del self._entities_cache[name]
520 self.params.purgeProfile(name) 577 self.params.purgeProfile(name)
521 578
522 d.addCallback(initPersonalKey) 579 d.addCallback(initPersonalKey)
523 d.addCallback(startFakeSession) 580 d.addCallback(startFakeSession)
524 d.addCallback(lambda dummy: self.setParam(C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=name)) 581 d.addCallback(
582 lambda dummy: self.setParam(
583 C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=name
584 )
585 )
525 d.addCallback(stopFakeSession) 586 d.addCallback(stopFakeSession)
526 d.addCallback(lambda dummy: self.auth_sessions.profileDelUnique(name)) 587 d.addCallback(lambda dummy: self.auth_sessions.profileDelUnique(name))
527 return d 588 return d
528 589
529 def asyncDeleteProfile(self, name, force=False): 590 def asyncDeleteProfile(self, name, force=False):
532 @param name: Name of the profile 593 @param name: Name of the profile
533 @param force: force the deletion even if the profile is connected. 594 @param force: force the deletion even if the profile is connected.
534 To be used for direct calls only (not through the bridge). 595 To be used for direct calls only (not through the bridge).
535 @return: a Deferred instance 596 @return: a Deferred instance
536 """ 597 """
598
537 def cleanMemory(dummy): 599 def cleanMemory(dummy):
538 self.auth_sessions.profileDelUnique(name) 600 self.auth_sessions.profileDelUnique(name)
539 try: 601 try:
540 del self._entities_cache[name] 602 del self._entities_cache[name]
541 except KeyError: 603 except KeyError:
542 pass 604 pass
605
543 d = self.params.asyncDeleteProfile(name, force) 606 d = self.params.asyncDeleteProfile(name, force)
544 d.addCallback(cleanMemory) 607 d.addCallback(cleanMemory)
545 return d 608 return d
546 609
547 def isComponent(self, profile_name): 610 def isComponent(self, profile_name):
565 ## History ## 628 ## History ##
566 629
567 def addToHistory(self, client, data): 630 def addToHistory(self, client, data):
568 return self.storage.addToHistory(data, client.profile) 631 return self.storage.addToHistory(data, client.profile)
569 632
570 def _historyGet(self, from_jid_s, to_jid_s, limit=C.HISTORY_LIMIT_NONE, between=True, filters=None, profile=C.PROF_KEY_NONE): 633 def _historyGet(
571 return self.historyGet(jid.JID(from_jid_s), jid.JID(to_jid_s), limit, between, filters, profile) 634 self,
572 635 from_jid_s,
573 def historyGet(self, from_jid, to_jid, limit=C.HISTORY_LIMIT_NONE, between=True, filters=None, profile=C.PROF_KEY_NONE): 636 to_jid_s,
637 limit=C.HISTORY_LIMIT_NONE,
638 between=True,
639 filters=None,
640 profile=C.PROF_KEY_NONE,
641 ):
642 return self.historyGet(
643 jid.JID(from_jid_s), jid.JID(to_jid_s), limit, between, filters, profile
644 )
645
646 def historyGet(
647 self,
648 from_jid,
649 to_jid,
650 limit=C.HISTORY_LIMIT_NONE,
651 between=True,
652 filters=None,
653 profile=C.PROF_KEY_NONE,
654 ):
574 """Retrieve messages in history 655 """Retrieve messages in history
575 656
576 @param from_jid (JID): source JID (full, or bare for catchall) 657 @param from_jid (JID): source JID (full, or bare for catchall)
577 @param to_jid (JID): dest JID (full, or bare for catchall) 658 @param to_jid (JID): dest JID (full, or bare for catchall)
578 @param limit (int): maximum number of messages to get: 659 @param limit (int): maximum number of messages to get:
584 @param profile (str): %(doc_profile)s 665 @param profile (str): %(doc_profile)s
585 @return (D(list)): list of message data as in [messageNew] 666 @return (D(list)): list of message data as in [messageNew]
586 """ 667 """
587 assert profile != C.PROF_KEY_NONE 668 assert profile != C.PROF_KEY_NONE
588 if limit == C.HISTORY_LIMIT_DEFAULT: 669 if limit == C.HISTORY_LIMIT_DEFAULT:
589 limit = int(self.getParamA(C.HISTORY_LIMIT, 'General', profile_key=profile)) 670 limit = int(self.getParamA(C.HISTORY_LIMIT, "General", profile_key=profile))
590 elif limit == C.HISTORY_LIMIT_NONE: 671 elif limit == C.HISTORY_LIMIT_NONE:
591 limit = None 672 limit = None
592 if limit == 0: 673 if limit == 0:
593 return defer.succeed([]) 674 return defer.succeed([])
594 return self.storage.historyGet(from_jid, to_jid, limit, between, filters, profile) 675 return self.storage.historyGet(from_jid, to_jid, limit, between, filters, profile)
595 676
596 ## Statuses ## 677 ## Statuses ##
597 678
598 def _getPresenceStatuses(self, profile_key): 679 def _getPresenceStatuses(self, profile_key):
599 ret = self.getPresenceStatuses(profile_key) 680 ret = self.getPresenceStatuses(profile_key)
600 return {entity.full():data for entity, data in ret.iteritems()} 681 return {entity.full(): data for entity, data in ret.iteritems()}
601 682
602 def getPresenceStatuses(self, profile_key): 683 def getPresenceStatuses(self, profile_key):
603 """Get all the presence statuses of a profile 684 """Get all the presence statuses of a profile
604 685
605 @param profile_key: %(doc_profile_key)s 686 @param profile_key: %(doc_profile_key)s
615 full_jid.resource = resource 696 full_jid.resource = resource
616 try: 697 try:
617 presence_data = self.getEntityDatum(full_jid, "presence", profile_key) 698 presence_data = self.getEntityDatum(full_jid, "presence", profile_key)
618 except KeyError: 699 except KeyError:
619 continue 700 continue
620 entities_presence.setdefault(entity_jid, {})[resource or ''] = presence_data 701 entities_presence.setdefault(entity_jid, {})[
702 resource or ""
703 ] = presence_data
621 704
622 return entities_presence 705 return entities_presence
623 706
624 def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key): 707 def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key):
625 """Change the presence status of an entity 708 """Change the presence status of an entity
629 @param priority: priority 712 @param priority: priority
630 @param statuses: dictionary of statuses 713 @param statuses: dictionary of statuses
631 @param profile_key: %(doc_profile_key)s 714 @param profile_key: %(doc_profile_key)s
632 """ 715 """
633 presence_data = PresenceTuple(show, priority, statuses) 716 presence_data = PresenceTuple(show, priority, statuses)
634 self.updateEntityData(entity_jid, "presence", presence_data, profile_key=profile_key) 717 self.updateEntityData(
718 entity_jid, "presence", presence_data, profile_key=profile_key
719 )
635 if entity_jid.resource and show != C.PRESENCE_UNAVAILABLE: 720 if entity_jid.resource and show != C.PRESENCE_UNAVAILABLE:
636 # If a resource is available, bare jid should not have presence information 721 # If a resource is available, bare jid should not have presence information
637 try: 722 try:
638 self.delEntityDatum(entity_jid.userhostJID(), "presence", profile_key) 723 self.delEntityDatum(entity_jid.userhostJID(), "presence", profile_key)
639 except (KeyError, exceptions.UnknownEntityError): 724 except (KeyError, exceptions.UnknownEntityError):
655 @raise exceptions.UnknownEntityError: if entity is not in cache 740 @raise exceptions.UnknownEntityError: if entity is not in cache
656 @raise ValueError: entity_jid has a resource 741 @raise ValueError: entity_jid has a resource
657 """ 742 """
658 # FIXME: is there a need to keep cache data for resources which are not connected anymore? 743 # FIXME: is there a need to keep cache data for resources which are not connected anymore?
659 if entity_jid.resource: 744 if entity_jid.resource:
660 raise ValueError("getAllResources must be used with a bare jid (got {})".format(entity_jid)) 745 raise ValueError(
746 "getAllResources must be used with a bare jid (got {})".format(entity_jid)
747 )
661 profile_cache = self._getProfileCache(client) 748 profile_cache = self._getProfileCache(client)
662 try: 749 try:
663 entity_data = profile_cache[entity_jid.userhostJID()] 750 entity_data = profile_cache[entity_jid.userhostJID()]
664 except KeyError: 751 except KeyError:
665 raise exceptions.UnknownEntityError(u"Entity {} not in cache".format(entity_jid)) 752 raise exceptions.UnknownEntityError(
666 resources= set(entity_data.keys()) 753 u"Entity {} not in cache".format(entity_jid)
754 )
755 resources = set(entity_data.keys())
667 resources.discard(None) 756 resources.discard(None)
668 return resources 757 return resources
669 758
670 def getAvailableResources(self, client, entity_jid): 759 def getAvailableResources(self, client, entity_jid):
671 """Return available resource for entity_jid 760 """Return available resource for entity_jid
699 788
700 @param entity_jid: bare entity jid 789 @param entity_jid: bare entity jid
701 @return (unicode): main resource or None 790 @return (unicode): main resource or None
702 """ 791 """
703 if entity_jid.resource: 792 if entity_jid.resource:
704 raise ValueError("getMainResource must be used with a bare jid (got {})".format(entity_jid)) 793 raise ValueError(
794 "getMainResource must be used with a bare jid (got {})".format(entity_jid)
795 )
705 try: 796 try:
706 if self.host.plugins["XEP-0045"].isJoinedRoom(client, entity_jid): 797 if self.host.plugins["XEP-0045"].isJoinedRoom(client, entity_jid):
707 return None # MUC rooms have no main resource 798 return None # MUC rooms have no main resource
708 except KeyError: # plugin not found 799 except KeyError: # plugin not found
709 pass 800 pass
764 continue 855 continue
765 full_jid = copy.copy(bare_jid) 856 full_jid = copy.copy(bare_jid)
766 full_jid.resource = resource 857 full_jid.resource = resource
767 yield full_jid 858 yield full_jid
768 859
769 def updateEntityData(self, entity_jid, key, value, silent=False, profile_key=C.PROF_KEY_NONE): 860 def updateEntityData(
861 self, entity_jid, key, value, silent=False, profile_key=C.PROF_KEY_NONE
862 ):
770 """Set a misc data for an entity 863 """Set a misc data for an entity
771 864
772 If key was registered with setSignalOnUpdate, a signal will be sent to frontends 865 If key was registered with setSignalOnUpdate, a signal will be sent to frontends
773 @param entity_jid: JID of the entity, C.ENTITY_ALL_RESOURCES for all resources of all entities, 866 @param entity_jid: JID of the entity, C.ENTITY_ALL_RESOURCES for all resources of all entities,
774 C.ENTITY_ALL for all entities (all resources + bare jids) 867 C.ENTITY_ALL for all entities (all resources + bare jids)
778 @param profile_key: %(doc_profile_key)s 871 @param profile_key: %(doc_profile_key)s
779 """ 872 """
780 client = self.host.getClient(profile_key) 873 client = self.host.getClient(profile_key)
781 profile_cache = self._getProfileCache(client) 874 profile_cache = self._getProfileCache(client)
782 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL): 875 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL):
783 entities = self.getAllEntitiesIter(client, entity_jid==C.ENTITY_ALL) 876 entities = self.getAllEntitiesIter(client, entity_jid == C.ENTITY_ALL)
784 else: 877 else:
785 entities = (entity_jid,) 878 entities = (entity_jid,)
786 879
787 for jid_ in entities: 880 for jid_ in entities:
788 entity_data = profile_cache.setdefault(jid_.userhostJID(),{}).setdefault(jid_.resource, {}) 881 entity_data = profile_cache.setdefault(jid_.userhostJID(), {}).setdefault(
882 jid_.resource, {}
883 )
789 884
790 entity_data[key] = value 885 entity_data[key] = value
791 if key in self._key_signals and not silent: 886 if key in self._key_signals and not silent:
792 if not isinstance(value, basestring): 887 if not isinstance(value, basestring):
793 log.error(u"Setting a non string value ({}) for a key ({}) which has a signal flag".format(value, key)) 888 log.error(
889 u"Setting a non string value ({}) for a key ({}) which has a signal flag".format(
890 value, key
891 )
892 )
794 else: 893 else:
795 self.host.bridge.entityDataUpdated(jid_.full(), key, value, self.getProfileName(profile_key)) 894 self.host.bridge.entityDataUpdated(
895 jid_.full(), key, value, self.getProfileName(profile_key)
896 )
796 897
797 def delEntityDatum(self, entity_jid, key, profile_key): 898 def delEntityDatum(self, entity_jid, key, profile_key):
798 """Delete a data for an entity 899 """Delete a data for an entity
799 900
800 @param entity_jid: JID of the entity, C.ENTITY_ALL_RESOURCES for all resources of all entities, 901 @param entity_jid: JID of the entity, C.ENTITY_ALL_RESOURCES for all resources of all entities,
806 @raise KeyError: key is not in cache 907 @raise KeyError: key is not in cache
807 """ 908 """
808 client = self.host.getClient(profile_key) 909 client = self.host.getClient(profile_key)
809 profile_cache = self._getProfileCache(client) 910 profile_cache = self._getProfileCache(client)
810 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL): 911 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL):
811 entities = self.getAllEntitiesIter(client, entity_jid==C.ENTITY_ALL) 912 entities = self.getAllEntitiesIter(client, entity_jid == C.ENTITY_ALL)
812 else: 913 else:
813 entities = (entity_jid,) 914 entities = (entity_jid,)
814 915
815 for jid_ in entities: 916 for jid_ in entities:
816 try: 917 try:
817 entity_data = profile_cache[jid_.userhostJID()][jid_.resource] 918 entity_data = profile_cache[jid_.userhostJID()][jid_.resource]
818 except KeyError: 919 except KeyError:
819 raise exceptions.UnknownEntityError(u"Entity {} not in cache".format(jid_)) 920 raise exceptions.UnknownEntityError(
921 u"Entity {} not in cache".format(jid_)
922 )
820 try: 923 try:
821 del entity_data[key] 924 del entity_data[key]
822 except KeyError as e: 925 except KeyError as e:
823 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL): 926 if entity_jid in (C.ENTITY_ALL_RESOURCES, C.ENTITY_ALL):
824 continue # we ignore KeyError when deleting keys from several entities 927 continue # we ignore KeyError when deleting keys from several entities
825 else: 928 else:
826 raise e 929 raise e
827 930
828 def _getEntitiesData(self, entities_jids, keys_list, profile_key): 931 def _getEntitiesData(self, entities_jids, keys_list, profile_key):
829 ret = self.getEntitiesData([jid.JID(jid_) for jid_ in entities_jids], keys_list, profile_key) 932 ret = self.getEntitiesData(
933 [jid.JID(jid_) for jid_ in entities_jids], keys_list, profile_key
934 )
830 return {jid_.full(): data for jid_, data in ret.iteritems()} 935 return {jid_.full(): data for jid_, data in ret.iteritems()}
831 936
832 def getEntitiesData(self, entities_jids, keys_list=None, profile_key=C.PROF_KEY_NONE): 937 def getEntitiesData(self, entities_jids, keys_list=None, profile_key=C.PROF_KEY_NONE):
833 """Get a list of cached values for several entities at once 938 """Get a list of cached values for several entities at once
834 939
841 if an entity doesn't exist in cache, it will not appear 946 if an entity doesn't exist in cache, it will not appear
842 in resulting dict 947 in resulting dict
843 948
844 @raise exceptions.UnknownEntityError: if entity is not in cache 949 @raise exceptions.UnknownEntityError: if entity is not in cache
845 """ 950 """
951
846 def fillEntityData(entity_cache_data): 952 def fillEntityData(entity_cache_data):
847 entity_data = {} 953 entity_data = {}
848 if keys_list is None: 954 if keys_list is None:
849 entity_data = entity_cache_data 955 entity_data = entity_cache_data
850 else: 956 else:
859 profile_cache = self._getProfileCache(client) 965 profile_cache = self._getProfileCache(client)
860 ret_data = {} 966 ret_data = {}
861 if entities_jids: 967 if entities_jids:
862 for entity in entities_jids: 968 for entity in entities_jids:
863 try: 969 try:
864 entity_cache_data = profile_cache[entity.userhostJID()][entity.resource] 970 entity_cache_data = profile_cache[entity.userhostJID()][
971 entity.resource
972 ]
865 except KeyError: 973 except KeyError:
866 continue 974 continue
867 ret_data[entity.full()] = fillEntityData(entity_cache_data, keys_list) 975 ret_data[entity.full()] = fillEntityData(entity_cache_data, keys_list)
868 else: 976 else:
869 for bare_jid, data in profile_cache.iteritems(): 977 for bare_jid, data in profile_cache.iteritems():
889 client = self.host.getClient(profile_key) 997 client = self.host.getClient(profile_key)
890 profile_cache = self._getProfileCache(client) 998 profile_cache = self._getProfileCache(client)
891 try: 999 try:
892 entity_data = profile_cache[entity_jid.userhostJID()][entity_jid.resource] 1000 entity_data = profile_cache[entity_jid.userhostJID()][entity_jid.resource]
893 except KeyError: 1001 except KeyError:
894 raise exceptions.UnknownEntityError(u"Entity {} not in cache (was requesting {})".format(entity_jid, keys_list)) 1002 raise exceptions.UnknownEntityError(
1003 u"Entity {} not in cache (was requesting {})".format(
1004 entity_jid, keys_list
1005 )
1006 )
895 if keys_list is None: 1007 if keys_list is None:
896 return entity_data 1008 return entity_data
897 1009
898 return {key: entity_data[key] for key in keys_list if key in entity_data} 1010 return {key: entity_data[key] for key in keys_list if key in entity_data}
899 1011
908 @raise exceptions.UnknownEntityError: if entity is not in cache 1020 @raise exceptions.UnknownEntityError: if entity is not in cache
909 @raise KeyError: if there is no value for this key and this entity 1021 @raise KeyError: if there is no value for this key and this entity
910 """ 1022 """
911 return self.getEntityData(entity_jid, (key,), profile_key)[key] 1023 return self.getEntityData(entity_jid, (key,), profile_key)[key]
912 1024
913 def delEntityCache(self, entity_jid, delete_all_resources=True, profile_key=C.PROF_KEY_NONE): 1025 def delEntityCache(
1026 self, entity_jid, delete_all_resources=True, profile_key=C.PROF_KEY_NONE
1027 ):
914 """Remove all cached data for entity 1028 """Remove all cached data for entity
915 1029
916 @param entity_jid: JID of the entity to delete 1030 @param entity_jid: JID of the entity to delete
917 @param delete_all_resources: if True also delete all known resources from cache (a bare jid must be given in this case) 1031 @param delete_all_resources: if True also delete all known resources from cache (a bare jid must be given in this case)
918 @param profile_key: %(doc_profile_key)s 1032 @param profile_key: %(doc_profile_key)s
926 if entity_jid.resource: 1040 if entity_jid.resource:
927 raise ValueError(_("Need a bare jid to delete all resources")) 1041 raise ValueError(_("Need a bare jid to delete all resources"))
928 try: 1042 try:
929 del profile_cache[entity_jid] 1043 del profile_cache[entity_jid]
930 except KeyError: 1044 except KeyError:
931 raise exceptions.UnknownEntityError(u"Entity {} not in cache".format(entity_jid)) 1045 raise exceptions.UnknownEntityError(
1046 u"Entity {} not in cache".format(entity_jid)
1047 )
932 else: 1048 else:
933 try: 1049 try:
934 del profile_cache[entity_jid.userhostJID()][entity_jid.resource] 1050 del profile_cache[entity_jid.userhostJID()][entity_jid.resource]
935 except KeyError: 1051 except KeyError:
936 raise exceptions.UnknownEntityError(u"Entity {} not in cache".format(entity_jid)) 1052 raise exceptions.UnknownEntityError(
1053 u"Entity {} not in cache".format(entity_jid)
1054 )
937 1055
938 ## Encryption ## 1056 ## Encryption ##
939 1057
940 def encryptValue(self, value, profile): 1058 def encryptValue(self, value, profile):
941 """Encrypt a value for the given profile. The personal key must be loaded 1059 """Encrypt a value for the given profile. The personal key must be loaded
945 @param value (str): the value to encrypt 1063 @param value (str): the value to encrypt
946 @param profile (str): %(doc_profile)s 1064 @param profile (str): %(doc_profile)s
947 @return: the deferred encrypted value 1065 @return: the deferred encrypted value
948 """ 1066 """
949 try: 1067 try:
950 personal_key = self.auth_sessions.profileGetUnique(profile)[C.MEMORY_CRYPTO_KEY] 1068 personal_key = self.auth_sessions.profileGetUnique(profile)[
1069 C.MEMORY_CRYPTO_KEY
1070 ]
951 except TypeError: 1071 except TypeError:
952 raise exceptions.InternalError(_('Trying to encrypt a value for %s while the personal key is undefined!') % profile) 1072 raise exceptions.InternalError(
1073 _("Trying to encrypt a value for %s while the personal key is undefined!")
1074 % profile
1075 )
953 return BlockCipher.encrypt(personal_key, value) 1076 return BlockCipher.encrypt(personal_key, value)
954 1077
955 def decryptValue(self, value, profile): 1078 def decryptValue(self, value, profile):
956 """Decrypt a value for the given profile. The personal key must be loaded 1079 """Decrypt a value for the given profile. The personal key must be loaded
957 already in the profile session, that should be the case if the profile is 1080 already in the profile session, that should be the case if the profile is
960 @param value (str): the value to decrypt 1083 @param value (str): the value to decrypt
961 @param profile (str): %(doc_profile)s 1084 @param profile (str): %(doc_profile)s
962 @return: the deferred decrypted value 1085 @return: the deferred decrypted value
963 """ 1086 """
964 try: 1087 try:
965 personal_key = self.auth_sessions.profileGetUnique(profile)[C.MEMORY_CRYPTO_KEY] 1088 personal_key = self.auth_sessions.profileGetUnique(profile)[
1089 C.MEMORY_CRYPTO_KEY
1090 ]
966 except TypeError: 1091 except TypeError:
967 raise exceptions.InternalError(_('Trying to decrypt a value for %s while the personal key is undefined!') % profile) 1092 raise exceptions.InternalError(
1093 _("Trying to decrypt a value for %s while the personal key is undefined!")
1094 % profile
1095 )
968 return BlockCipher.decrypt(personal_key, value) 1096 return BlockCipher.decrypt(personal_key, value)
969 1097
970 def encryptPersonalData(self, data_key, data_value, crypto_key, profile): 1098 def encryptPersonalData(self, data_key, data_value, crypto_key, profile):
971 """Re-encrypt a personal data (saved to a PersistentDict). 1099 """Re-encrypt a personal data (saved to a PersistentDict).
972 1100
985 return data.force(data_key) 1113 return data.force(data_key)
986 1114
987 return d.addCallback(cb) 1115 return d.addCallback(cb)
988 1116
989 def done(dummy): 1117 def done(dummy):
990 log.debug(_(u'Personal data (%(ns)s, %(key)s) has been successfuly encrypted') % 1118 log.debug(
991 {'ns': C.MEMORY_CRYPTO_NAMESPACE, 'key': data_key}) 1119 _(u"Personal data (%(ns)s, %(key)s) has been successfuly encrypted")
1120 % {"ns": C.MEMORY_CRYPTO_NAMESPACE, "key": data_key}
1121 )
992 1122
993 d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load() 1123 d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load()
994 return d.addCallback(gotIndMemory).addCallback(done) 1124 return d.addCallback(gotIndMemory).addCallback(done)
995 1125
996 ## Subscription requests ## 1126 ## Subscription requests ##
1012 1142
1013 def getWaitingSub(self, profile_key): 1143 def getWaitingSub(self, profile_key):
1014 """Called to get a list of currently waiting subscription requests""" 1144 """Called to get a list of currently waiting subscription requests"""
1015 profile = self.getProfileName(profile_key) 1145 profile = self.getProfileName(profile_key)
1016 if not profile: 1146 if not profile:
1017 log.error(_('Asking waiting subscriptions for a non-existant profile')) 1147 log.error(_("Asking waiting subscriptions for a non-existant profile"))
1018 return {} 1148 return {}
1019 if profile not in self.subscriptions: 1149 if profile not in self.subscriptions:
1020 return {} 1150 return {}
1021 1151
1022 return self.subscriptions[profile] 1152 return self.subscriptions[profile]
1027 return self.params.getStringParamA(name, category, attr, profile_key) 1157 return self.params.getStringParamA(name, category, attr, profile_key)
1028 1158
1029 def getParamA(self, name, category, attr="value", profile_key=C.PROF_KEY_NONE): 1159 def getParamA(self, name, category, attr="value", profile_key=C.PROF_KEY_NONE):
1030 return self.params.getParamA(name, category, attr, profile_key=profile_key) 1160 return self.params.getParamA(name, category, attr, profile_key=profile_key)
1031 1161
1032 def asyncGetParamA(self, name, category, attr="value", security_limit=C.NO_SECURITY_LIMIT, profile_key=C.PROF_KEY_NONE): 1162 def asyncGetParamA(
1033 return self.params.asyncGetParamA(name, category, attr, security_limit, profile_key) 1163 self,
1034 1164 name,
1035 def asyncGetParamsValuesFromCategory(self, category, security_limit=C.NO_SECURITY_LIMIT, profile_key=C.PROF_KEY_NONE): 1165 category,
1036 return self.params.asyncGetParamsValuesFromCategory(category, security_limit, profile_key) 1166 attr="value",
1037 1167 security_limit=C.NO_SECURITY_LIMIT,
1038 def asyncGetStringParamA(self, name, category, attr="value", security_limit=C.NO_SECURITY_LIMIT, profile_key=C.PROF_KEY_NONE): 1168 profile_key=C.PROF_KEY_NONE,
1039 return self.params.asyncGetStringParamA(name, category, attr, security_limit, profile_key) 1169 ):
1040 1170 return self.params.asyncGetParamA(
1041 def getParamsUI(self, security_limit=C.NO_SECURITY_LIMIT, app='', profile_key=C.PROF_KEY_NONE): 1171 name, category, attr, security_limit, profile_key
1172 )
1173
1174 def asyncGetParamsValuesFromCategory(
1175 self, category, security_limit=C.NO_SECURITY_LIMIT, profile_key=C.PROF_KEY_NONE
1176 ):
1177 return self.params.asyncGetParamsValuesFromCategory(
1178 category, security_limit, profile_key
1179 )
1180
1181 def asyncGetStringParamA(
1182 self,
1183 name,
1184 category,
1185 attr="value",
1186 security_limit=C.NO_SECURITY_LIMIT,
1187 profile_key=C.PROF_KEY_NONE,
1188 ):
1189 return self.params.asyncGetStringParamA(
1190 name, category, attr, security_limit, profile_key
1191 )
1192
1193 def getParamsUI(
1194 self, security_limit=C.NO_SECURITY_LIMIT, app="", profile_key=C.PROF_KEY_NONE
1195 ):
1042 return self.params.getParamsUI(security_limit, app, profile_key) 1196 return self.params.getParamsUI(security_limit, app, profile_key)
1043 1197
1044 def getParamsCategories(self): 1198 def getParamsCategories(self):
1045 return self.params.getParamsCategories() 1199 return self.params.getParamsCategories()
1046 1200
1047 def setParam(self, name, value, category, security_limit=C.NO_SECURITY_LIMIT, profile_key=C.PROF_KEY_NONE): 1201 def setParam(
1202 self,
1203 name,
1204 value,
1205 category,
1206 security_limit=C.NO_SECURITY_LIMIT,
1207 profile_key=C.PROF_KEY_NONE,
1208 ):
1048 return self.params.setParam(name, value, category, security_limit, profile_key) 1209 return self.params.setParam(name, value, category, security_limit, profile_key)
1049 1210
1050 def updateParams(self, xml): 1211 def updateParams(self, xml):
1051 return self.params.updateParams(xml) 1212 return self.params.updateParams(xml)
1052 1213
1053 def paramsRegisterApp(self, xml, security_limit=C.NO_SECURITY_LIMIT, app=''): 1214 def paramsRegisterApp(self, xml, security_limit=C.NO_SECURITY_LIMIT, app=""):
1054 return self.params.paramsRegisterApp(xml, security_limit, app) 1215 return self.params.paramsRegisterApp(xml, security_limit, app)
1055 1216
1056 def setDefault(self, name, category, callback, errback=None): 1217 def setDefault(self, name, category, callback, errback=None):
1057 return self.params.setDefault(name, category, callback, errback) 1218 return self.params.setDefault(name, category, callback, errback)
1058 1219
1071 @raise exceptions.InternalError: perms_to_check is invalid 1232 @raise exceptions.InternalError: perms_to_check is invalid
1072 """ 1233 """
1073 if peer_jid is None and perms_to_check is None: 1234 if peer_jid is None and perms_to_check is None:
1074 return 1235 return
1075 peer_jid = peer_jid.userhostJID() 1236 peer_jid = peer_jid.userhostJID()
1076 if peer_jid == file_data['owner']: 1237 if peer_jid == file_data["owner"]:
1077 # the owner has all rights 1238 # the owner has all rights
1078 return 1239 return
1079 if not C.ACCESS_PERMS.issuperset(perms_to_check): 1240 if not C.ACCESS_PERMS.issuperset(perms_to_check):
1080 raise exceptions.InternalError(_(u'invalid permission')) 1241 raise exceptions.InternalError(_(u"invalid permission"))
1081 1242
1082 for perm in perms_to_check: 1243 for perm in perms_to_check:
1083 # we check each perm and raise PermissionError as soon as one condition is not valid 1244 # we check each perm and raise PermissionError as soon as one condition is not valid
1084 # we must never return here, we only return after the loop if nothing was blocking the access 1245 # we must never return here, we only return after the loop if nothing was blocking the access
1085 try: 1246 try:
1086 perm_data = file_data[u'access'][perm] 1247 perm_data = file_data[u"access"][perm]
1087 perm_type = perm_data[u'type'] 1248 perm_type = perm_data[u"type"]
1088 except KeyError: 1249 except KeyError:
1089 raise failure.Failure(exceptions.PermissionError()) 1250 raise failure.Failure(exceptions.PermissionError())
1090 if perm_type == C.ACCESS_TYPE_PUBLIC: 1251 if perm_type == C.ACCESS_TYPE_PUBLIC:
1091 continue 1252 continue
1092 elif perm_type == C.ACCESS_TYPE_WHITELIST: 1253 elif perm_type == C.ACCESS_TYPE_WHITELIST:
1093 try: 1254 try:
1094 jids = perm_data[u'jids'] 1255 jids = perm_data[u"jids"]
1095 except KeyError: 1256 except KeyError:
1096 raise failure.Failure(exceptions.PermissionError()) 1257 raise failure.Failure(exceptions.PermissionError())
1097 if peer_jid.full() in jids: 1258 if peer_jid.full() in jids:
1098 continue 1259 continue
1099 else: 1260 else:
1100 raise failure.Failure(exceptions.PermissionError()) 1261 raise failure.Failure(exceptions.PermissionError())
1101 else: 1262 else:
1102 raise exceptions.InternalError(_(u'unknown access type: {type}').format(type=perm_type)) 1263 raise exceptions.InternalError(
1264 _(u"unknown access type: {type}").format(type=perm_type)
1265 )
1103 1266
1104 @defer.inlineCallbacks 1267 @defer.inlineCallbacks
1105 def checkPermissionToRoot(self, client, file_data, peer_jid, perms_to_check): 1268 def checkPermissionToRoot(self, client, file_data, peer_jid, perms_to_check):
1106 """do checkFilePermission on file_data and all its parents until root""" 1269 """do checkFilePermission on file_data and all its parents until root"""
1107 current = file_data 1270 current = file_data
1108 while True: 1271 while True:
1109 self.checkFilePermission(current, peer_jid, perms_to_check) 1272 self.checkFilePermission(current, peer_jid, perms_to_check)
1110 parent = current[u'parent'] 1273 parent = current[u"parent"]
1111 if not parent: 1274 if not parent:
1112 break 1275 break
1113 files_data = yield self.getFile(self, client, peer_jid=None, file_id=parent, perms_to_check=None) 1276 files_data = yield self.getFile(
1277 self, client, peer_jid=None, file_id=parent, perms_to_check=None
1278 )
1114 try: 1279 try:
1115 current = files_data[0] 1280 current = files_data[0]
1116 except IndexError: 1281 except IndexError:
1117 raise exceptions.DataError(u'Missing parent') 1282 raise exceptions.DataError(u"Missing parent")
1118 1283
1119 @defer.inlineCallbacks 1284 @defer.inlineCallbacks
1120 def _getParentDir(self, client, path, parent, namespace, owner, peer_jid, perms_to_check): 1285 def _getParentDir(
1286 self, client, path, parent, namespace, owner, peer_jid, perms_to_check
1287 ):
1121 """Retrieve parent node from a path, or last existing directory 1288 """Retrieve parent node from a path, or last existing directory
1122 1289
1123 each directory of the path will be retrieved, until the last existing one 1290 each directory of the path will be retrieved, until the last existing one
1124 @return (tuple[unicode, list[unicode])): parent, remaining path elements: 1291 @return (tuple[unicode, list[unicode])): parent, remaining path elements:
1125 - parent is the id of the last retrieved directory (or u'' for root) 1292 - parent is the id of the last retrieved directory (or u'' for root)
1126 - remaining path elements are the directories which have not been retrieved 1293 - remaining path elements are the directories which have not been retrieved
1127 (i.e. which don't exist) 1294 (i.e. which don't exist)
1128 """ 1295 """
1129 # if path is set, we have to retrieve parent directory of the file(s) from it 1296 # if path is set, we have to retrieve parent directory of the file(s) from it
1130 if parent is not None: 1297 if parent is not None:
1131 raise exceptions.ConflictError(_(u"You can't use path and parent at the same time")) 1298 raise exceptions.ConflictError(
1132 path_elts = filter(None, path.split(u'/')) 1299 _(u"You can't use path and parent at the same time")
1133 if {u'..', u'.'}.intersection(path_elts): 1300 )
1301 path_elts = filter(None, path.split(u"/"))
1302 if {u"..", u"."}.intersection(path_elts):
1134 raise ValueError(_(u'".." or "." can\'t be used in path')) 1303 raise ValueError(_(u'".." or "." can\'t be used in path'))
1135 1304
1136 # we retrieve all directories from path until we get the parent container 1305 # we retrieve all directories from path until we get the parent container
1137 # non existing directories will be created 1306 # non existing directories will be created
1138 parent = u'' 1307 parent = u""
1139 for idx, path_elt in enumerate(path_elts): 1308 for idx, path_elt in enumerate(path_elts):
1140 directories = yield self.storage.getFiles(client, parent=parent, type_=C.FILE_TYPE_DIRECTORY, 1309 directories = yield self.storage.getFiles(
1141 name=path_elt, namespace=namespace, owner=owner) 1310 client,
1311 parent=parent,
1312 type_=C.FILE_TYPE_DIRECTORY,
1313 name=path_elt,
1314 namespace=namespace,
1315 owner=owner,
1316 )
1142 if not directories: 1317 if not directories:
1143 defer.returnValue((parent, path_elts[idx:])) 1318 defer.returnValue((parent, path_elts[idx:]))
1144 # from this point, directories don't exist anymore, we have to create them 1319 # from this point, directories don't exist anymore, we have to create them
1145 elif len(directories) > 1: 1320 elif len(directories) > 1:
1146 raise exceptions.InternalError(_(u"Several directories found, this should not happen")) 1321 raise exceptions.InternalError(
1322 _(u"Several directories found, this should not happen")
1323 )
1147 else: 1324 else:
1148 directory = directories[0] 1325 directory = directories[0]
1149 self.checkFilePermission(directory, peer_jid, perms_to_check) 1326 self.checkFilePermission(directory, peer_jid, perms_to_check)
1150 parent = directory[u'id'] 1327 parent = directory[u"id"]
1151 defer.returnValue((parent, [])) 1328 defer.returnValue((parent, []))
1152 1329
1153 @defer.inlineCallbacks 1330 @defer.inlineCallbacks
1154 def getFiles(self, client, peer_jid, file_id=None, version=None, parent=None, path=None, type_=None, 1331 def getFiles(
1155 file_hash=None, hash_algo=None, name=None, namespace=None, mime_type=None, 1332 self,
1156 owner=None, access=None, projection=None, unique=False, perms_to_check=(C.ACCESS_PERM_READ,)): 1333 client,
1334 peer_jid,
1335 file_id=None,
1336 version=None,
1337 parent=None,
1338 path=None,
1339 type_=None,
1340 file_hash=None,
1341 hash_algo=None,
1342 name=None,
1343 namespace=None,
1344 mime_type=None,
1345 owner=None,
1346 access=None,
1347 projection=None,
1348 unique=False,
1349 perms_to_check=(C.ACCESS_PERM_READ,),
1350 ):
1157 """retrieve files with with given filters 1351 """retrieve files with with given filters
1158 1352
1159 @param peer_jid(jid.JID, None): jid trying to access the file 1353 @param peer_jid(jid.JID, None): jid trying to access the file
1160 needed to check permission. 1354 needed to check permission.
1161 Use None to ignore permission (perms_to_check must be None too) 1355 Use None to ignore permission (perms_to_check must be None too)
1178 @raise exceptions.NotFound: parent directory not found (when path is specified) 1372 @raise exceptions.NotFound: parent directory not found (when path is specified)
1179 @raise exceptions.PermissionError: peer_jid can't use perms_to_check for one of the file 1373 @raise exceptions.PermissionError: peer_jid can't use perms_to_check for one of the file
1180 on the path 1374 on the path
1181 """ 1375 """
1182 if peer_jid is None and perms_to_check or perms_to_check is None and peer_jid: 1376 if peer_jid is None and perms_to_check or perms_to_check is None and peer_jid:
1183 raise exceptions.InternalError('if you want to disable permission check, both peer_jid and perms_to_check must be None') 1377 raise exceptions.InternalError(
1378 "if you want to disable permission check, both peer_jid and perms_to_check must be None"
1379 )
1184 if owner is not None: 1380 if owner is not None:
1185 owner = owner.userhostJID() 1381 owner = owner.userhostJID()
1186 if path is not None: 1382 if path is not None:
1187 # permission are checked by _getParentDir 1383 # permission are checked by _getParentDir
1188 parent, remaining_path_elts = yield self._getParentDir(client, path, parent, namespace, owner, peer_jid, perms_to_check) 1384 parent, remaining_path_elts = yield self._getParentDir(
1385 client, path, parent, namespace, owner, peer_jid, perms_to_check
1386 )
1189 if remaining_path_elts: 1387 if remaining_path_elts:
1190 # if we have remaining path elements, 1388 # if we have remaining path elements,
1191 # the parent directory is not found 1389 # the parent directory is not found
1192 raise failure.Failure(exceptions.NotFound()) 1390 raise failure.Failure(exceptions.NotFound())
1193 if parent and peer_jid: 1391 if parent and peer_jid:
1195 # we need to check all the parents 1393 # we need to check all the parents
1196 parent_data = yield self.storage.getFiles(client, file_id=parent) 1394 parent_data = yield self.storage.getFiles(client, file_id=parent)
1197 try: 1395 try:
1198 parent_data = parent_data[0] 1396 parent_data = parent_data[0]
1199 except IndexError: 1397 except IndexError:
1200 raise exceptions.DataError(u'mising parent') 1398 raise exceptions.DataError(u"mising parent")
1201 yield self.checkPermissionToRoot(client, parent_data, peer_jid, perms_to_check) 1399 yield self.checkPermissionToRoot(
1202 1400 client, parent_data, peer_jid, perms_to_check
1203 files = yield self.storage.getFiles(client, file_id=file_id, version=version, parent=parent, type_=type_, 1401 )
1204 file_hash=file_hash, hash_algo=hash_algo, name=name, namespace=namespace, 1402
1205 mime_type=mime_type, owner=owner, access=access, 1403 files = yield self.storage.getFiles(
1206 projection=projection, unique=unique) 1404 client,
1405 file_id=file_id,
1406 version=version,
1407 parent=parent,
1408 type_=type_,
1409 file_hash=file_hash,
1410 hash_algo=hash_algo,
1411 name=name,
1412 namespace=namespace,
1413 mime_type=mime_type,
1414 owner=owner,
1415 access=access,
1416 projection=projection,
1417 unique=unique,
1418 )
1207 1419
1208 if peer_jid: 1420 if peer_jid:
1209 # if permission are checked, we must remove all file tha use can't access 1421 #  if permission are checked, we must remove all file tha use can't access
1210 to_remove = [] 1422 to_remove = []
1211 for file_data in files: 1423 for file_data in files:
1212 try: 1424 try:
1213 self.checkFilePermission(file_data, peer_jid, perms_to_check) 1425 self.checkFilePermission(file_data, peer_jid, perms_to_check)
1214 except exceptions.PermissionError: 1426 except exceptions.PermissionError:
1216 for file_data in to_remove: 1428 for file_data in to_remove:
1217 files.remove(file_data) 1429 files.remove(file_data)
1218 defer.returnValue(files) 1430 defer.returnValue(files)
1219 1431
1220 @defer.inlineCallbacks 1432 @defer.inlineCallbacks
1221 def setFile(self, client, name, file_id=None, version=u'', parent=None, path=None, 1433 def setFile(
1222 type_=C.FILE_TYPE_FILE, file_hash=None, hash_algo=None, size=None, namespace=None, 1434 self,
1223 mime_type=None, created=None, modified=None, owner=None, access=None, extra=None, 1435 client,
1224 peer_jid = None, perms_to_check=(C.ACCESS_PERM_WRITE,)): 1436 name,
1437 file_id=None,
1438 version=u"",
1439 parent=None,
1440 path=None,
1441 type_=C.FILE_TYPE_FILE,
1442 file_hash=None,
1443 hash_algo=None,
1444 size=None,
1445 namespace=None,
1446 mime_type=None,
1447 created=None,
1448 modified=None,
1449 owner=None,
1450 access=None,
1451 extra=None,
1452 peer_jid=None,
1453 perms_to_check=(C.ACCESS_PERM_WRITE,),
1454 ):
1225 """set a file metadata 1455 """set a file metadata
1226 1456
1227 @param name(unicode): basename of the file 1457 @param name(unicode): basename of the file
1228 @param file_id(unicode): unique id of the file 1458 @param file_id(unicode): unique id of the file
1229 @param version(unicode): version of this file 1459 @param version(unicode): version of this file
1256 @param perms_to_check(tuple[unicode],None): permission to check 1486 @param perms_to_check(tuple[unicode],None): permission to check
1257 must be a tuple of C.ACCESS_PERM_* or None 1487 must be a tuple of C.ACCESS_PERM_* or None
1258 if None, permission will no be checked (peer_jid must be None too in this case) 1488 if None, permission will no be checked (peer_jid must be None too in this case)
1259 @param profile(unicode): profile owning the file 1489 @param profile(unicode): profile owning the file
1260 """ 1490 """
1261 if '/' in name: 1491 if "/" in name:
1262 raise ValueError('name must not contain a slash ("/")') 1492 raise ValueError('name must not contain a slash ("/")')
1263 if file_id is None: 1493 if file_id is None:
1264 file_id = shortuuid.uuid() 1494 file_id = shortuuid.uuid()
1265 if file_hash is not None and hash_algo is None or hash_algo is not None and file_hash is None: 1495 if (
1266 raise ValueError('file_hash and hash_algo must be set at the same time') 1496 file_hash is not None
1497 and hash_algo is None
1498 or hash_algo is not None
1499 and file_hash is None
1500 ):
1501 raise ValueError("file_hash and hash_algo must be set at the same time")
1267 if mime_type is None: 1502 if mime_type is None:
1268 mime_type, file_encoding = mimetypes.guess_type(name) 1503 mime_type, file_encoding = mimetypes.guess_type(name)
1269 if created is None: 1504 if created is None:
1270 created = time.time() 1505 created = time.time()
1271 if namespace is not None: 1506 if namespace is not None:
1272 namespace = namespace.strip() or None 1507 namespace = namespace.strip() or None
1273 if type_ == C.FILE_TYPE_DIRECTORY: 1508 if type_ == C.FILE_TYPE_DIRECTORY:
1274 if any(version, file_hash, size, mime_type): 1509 if any(version, file_hash, size, mime_type):
1275 raise ValueError(u"version, file_hash, size and mime_type can't be set for a directory") 1510 raise ValueError(
1511 u"version, file_hash, size and mime_type can't be set for a directory"
1512 )
1276 if owner is not None: 1513 if owner is not None:
1277 owner = owner.userhostJID() 1514 owner = owner.userhostJID()
1278 1515
1279 if path is not None: 1516 if path is not None:
1280 # _getParentDir will check permissions if peer_jid is set, so we use owner 1517 # _getParentDir will check permissions if peer_jid is set, so we use owner
1281 parent, remaining_path_elts = yield self._getParentDir(client, path, parent, namespace, owner, owner, perms_to_check) 1518 parent, remaining_path_elts = yield self._getParentDir(
1519 client, path, parent, namespace, owner, owner, perms_to_check
1520 )
1282 # if remaining directories don't exist, we have to create them 1521 # if remaining directories don't exist, we have to create them
1283 for new_dir in remaining_path_elts: 1522 for new_dir in remaining_path_elts:
1284 new_dir_id = shortuuid.uuid() 1523 new_dir_id = shortuuid.uuid()
1285 yield self.storage.setFile(client, name=new_dir, file_id=new_dir_id, version=u'', parent=parent, 1524 yield self.storage.setFile(
1286 type_=C.FILE_TYPE_DIRECTORY, namespace=namespace, 1525 client,
1287 created=time.time(), 1526 name=new_dir,
1288 owner=owner, 1527 file_id=new_dir_id,
1289 access=access, extra={}) 1528 version=u"",
1529 parent=parent,
1530 type_=C.FILE_TYPE_DIRECTORY,
1531 namespace=namespace,
1532 created=time.time(),
1533 owner=owner,
1534 access=access,
1535 extra={},
1536 )
1290 parent = new_dir_id 1537 parent = new_dir_id
1291 elif parent is None: 1538 elif parent is None:
1292 parent = u'' 1539 parent = u""
1293 1540
1294 yield self.storage.setFile(client, file_id=file_id, version=version, parent=parent, type_=type_, 1541 yield self.storage.setFile(
1295 file_hash=file_hash, hash_algo=hash_algo, name=name, size=size, 1542 client,
1296 namespace=namespace, mime_type=mime_type, created=created, modified=modified, 1543 file_id=file_id,
1297 owner=owner, 1544 version=version,
1298 access=access, extra=extra) 1545 parent=parent,
1546 type_=type_,
1547 file_hash=file_hash,
1548 hash_algo=hash_algo,
1549 name=name,
1550 size=size,
1551 namespace=namespace,
1552 mime_type=mime_type,
1553 created=created,
1554 modified=modified,
1555 owner=owner,
1556 access=access,
1557 extra=extra,
1558 )
1299 1559
1300 def fileUpdate(self, file_id, column, update_cb): 1560 def fileUpdate(self, file_id, column, update_cb):
1301 """update a file column taking care of race condition 1561 """update a file column taking care of race condition
1302 1562
1303 access is NOT checked in this method, it must be checked beforehand 1563 access is NOT checked in this method, it must be checked beforehand
1316 1576
1317 @param entity_jid (JID): the entity to check (if bare jid is used, all resources are tested) 1577 @param entity_jid (JID): the entity to check (if bare jid is used, all resources are tested)
1318 @return (bool): True if entity is available 1578 @return (bool): True if entity is available
1319 """ 1579 """
1320 if not entity_jid.resource: 1580 if not entity_jid.resource:
1321 return bool(self.getAvailableResources(client, entity_jid)) # is any resource is available, entity is available 1581 return bool(
1582 self.getAvailableResources(client, entity_jid)
1583 ) # is any resource is available, entity is available
1322 try: 1584 try:
1323 presence_data = self.getEntityDatum(entity_jid, "presence", client.profile) 1585 presence_data = self.getEntityDatum(entity_jid, "presence", client.profile)
1324 except KeyError: 1586 except KeyError:
1325 log.debug(u"No presence information for {}".format(entity_jid)) 1587 log.debug(u"No presence information for {}".format(entity_jid))
1326 return False 1588 return False