Mercurial > libervia-backend
comparison src/memory/memory.py @ 432:31e8c48b5f5d
core: - memory refactoring (moved memory.py and sqlite.py from tools to memory)
- private values are now stored in storage (currently sqlite database) and splitted in normal/binary storage. Normal storage is for string key/values, binary storage is for string key and any python data for value
- PersistentDict and PersistentBinaryDict are sugar classes to manage the data in storage thought a python dictionary like class.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 20 Nov 2011 15:34:37 +0100 |
parents | src/tools/memory.py@a4a9efadabfc |
children | 7099ea9c1b12 |
comparison
equal
deleted
inserted
replaced
431:482b9bcf0ca4 | 432:31e8c48b5f5d |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 SAT: a jabber client | |
6 Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org) | |
7 | |
8 This program is free software: you can redistribute it and/or modify | |
9 it under the terms of the GNU General Public License as published by | |
10 the Free Software Foundation, either version 3 of the License, or | |
11 (at your option) any later version. | |
12 | |
13 This program is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 GNU General Public License for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 """ | |
21 | |
22 from __future__ import with_statement | |
23 | |
24 import os.path | |
25 import time | |
26 import cPickle as pickle | |
27 from ConfigParser import SafeConfigParser, NoOptionError, NoSectionError | |
28 from xml.dom import minidom | |
29 from logging import debug, info, warning, error | |
30 from twisted.internet import defer | |
31 from twisted.words.protocols.jabber import jid | |
32 from sat.tools.xml_tools import paramsXml2xmlUI | |
33 from sat.core.default_config import default_config | |
34 from sat.memory.sqlite import SqliteStorage | |
35 from sat.memory.persistent import PersistentDict | |
36 | |
37 SAVEFILE_PARAM_XML="/param" #xml parameters template | |
38 SAVEFILE_PRIVATE="/private" #file used to store misc values (mainly for plugins) | |
39 SAVEFILE_DATABASE="/sat.db" | |
40 | |
41 class ProfileNotInCacheError(Exception): | |
42 pass | |
43 | |
44 class ConnectedProfileError(Exception): | |
45 pass | |
46 | |
47 class Params(): | |
48 """This class manage parameters with xml""" | |
49 ### TODO: add desciption in params | |
50 | |
51 #TODO: move Watched in a plugin | |
52 default_xml = u""" | |
53 <params> | |
54 <general> | |
55 </general> | |
56 <individual> | |
57 <category name="Connection" label="%(category_connection)s"> | |
58 <param name="JabberID" value="name@example.org/SàT" type="string" /> | |
59 <param name="Password" value="" type="password" /> | |
60 <param name="Server" value="example.org" type="string" /> | |
61 <param name="NewAccount" value="%(label_NewAccount)s" type="button" callback_id="registerNewAccount"/> | |
62 <param name="autoconnect" label="%(label_autoconnect)s" value="true" type="bool" /> | |
63 <param name="autodisconnect" label="%(label_autodisconnect)s" value="false" type="bool" /> | |
64 </category> | |
65 <category name="Misc" label="%(category_misc)s"> | |
66 <param name="Watched" value="test@Jabber.goffi.int" type="string" /> | |
67 </category> | |
68 </individual> | |
69 </params> | |
70 """ % {'category_connection': _("Connection"), | |
71 'label_NewAccount': _("Register new account"), | |
72 'label_autoconnect': _('Connect on frontend startup'), | |
73 'label_autodisconnect': _('Disconnect on frontend closure'), | |
74 'category_misc': _("Misc") | |
75 } | |
76 | |
77 def load_default_params(self): | |
78 self.dom = minidom.parseString(Param.default_xml.encode('utf-8')) | |
79 | |
80 def load_xml(self, file): | |
81 """Load parameters template from file""" | |
82 self.dom = minidom.parse(file) | |
83 | |
84 def loadGenParams(self): | |
85 """Load general parameters data from storage | |
86 @return: deferred triggered once params are loaded""" | |
87 return self.storage.loadGenParams(self.params_gen) | |
88 | |
89 def loadIndParams(self, profile, cache=None): | |
90 """Load individual parameters | |
91 set self.params cache or a temporary cache | |
92 @param profile: profile to load (*must exist*) | |
93 @param cache: if not None, will be used to store the value, as a short time cache | |
94 @return: deferred triggered once params are loaded""" | |
95 if cache == None: | |
96 self.params[profile] = {} | |
97 return self.storage.loadIndParams(self.params[profile] if cache==None else cache, profile) | |
98 | |
99 def purgeProfile(self, profile): | |
100 """Remove cache data of a profile | |
101 @param profile: %(doc_profile)s""" | |
102 try: | |
103 del self.params[profile] | |
104 except KeyError: | |
105 error(_("Trying to purge cache of a profile not in memory: [%s]") % profile) | |
106 | |
107 def save_xml(self, file): | |
108 """Save parameters template to xml file""" | |
109 with open(file, 'wb') as xml_file: | |
110 xml_file.write(self.dom.toxml('utf-8')) | |
111 | |
112 def __init__(self, host, storage): | |
113 debug("Parameters init") | |
114 self.host = host | |
115 self.storage = storage | |
116 self.default_profile = None | |
117 self.params = {} | |
118 self.params_gen = {} | |
119 host.set_const('savefile_param_xml', SAVEFILE_PARAM_XML) | |
120 host.registerGeneralCB("registerNewAccount", host.registerNewAccountCB) | |
121 | |
122 def createProfile(self, profile): | |
123 """Create a new profile | |
124 @param profile: profile of the profile""" | |
125 #FIXME: must be asynchronous and call the callback once the profile actually exists | |
126 if self.storage.hasProfile(profile): | |
127 info (_('The profile profile already exists')) | |
128 return True | |
129 if not self.host.trigger.point("ProfileCreation", profile): | |
130 return False | |
131 self.storage.createProfile(profile) | |
132 return False | |
133 | |
134 def asyncCreateProfile(self, profile): | |
135 """Create a new profile | |
136 @param profile: name of the profile | |
137 @param callback: called when the profile actually exists in database and memory | |
138 @param errback: called with a string constant as parameter: | |
139 - CONFLICT: the profile already exists | |
140 - CANCELED: profile creation canceled | |
141 """ | |
142 if self.storage.hasProfile(profile): | |
143 info (_('The profile name already exists')) | |
144 return defer.fail("CONFLICT") | |
145 if not self.host.trigger.point("ProfileCreation", profile): | |
146 return defer.fail("CANCEL") | |
147 return self.storage.createProfile(profile) | |
148 | |
149 | |
150 def deleteProfile(self, profile): | |
151 """Delete an existing profile | |
152 @param profile: name of the profile""" | |
153 #TODO: async equivalent, like for createProfile | |
154 if not self.storage.hasProfile(profile): | |
155 error(_('Trying to delete an unknown profile')) | |
156 return True | |
157 if self.host.isConnected(profile): | |
158 error(_("Trying to delete a connected profile")) | |
159 raise ConnectedProfileError | |
160 self.storage.deleteProfile(profile) | |
161 return False | |
162 | |
163 def getProfileName(self, profile_key): | |
164 """return profile according to profile_key | |
165 @param profile_key: profile name or key which can be | |
166 @ALL@ for all profiles | |
167 @DEFAULT@ for default profile | |
168 @return: requested profile name or None if it doesn't exist""" | |
169 if profile_key=='@DEFAULT@': | |
170 if not self.params: | |
171 return "" | |
172 default = self.host.memory.getPrivate('Profile_default') | |
173 if not default or not default in self.params: | |
174 info(_('No default profile, returning first one')) #TODO: manage real default profile | |
175 default = self.storage.getProfilesList()[0] | |
176 self.host.memory.setPrivate('Profile_default', default) | |
177 return default #FIXME: temporary, must use real default value, and fallback to first one if it doesn't exists | |
178 if not self.storage.hasProfile(profile_key): | |
179 info (_('Trying to access an unknown profile')) | |
180 return "" | |
181 return profile_key | |
182 | |
183 def __get_unique_node(self, parent, tag, name): | |
184 """return node with given tag | |
185 @param parent: parent of nodes to check (e.g. documentElement) | |
186 @param tag: tag to check (e.g. "category") | |
187 @param name: name to check (e.g. "JID") | |
188 @return: node if it exist or None | |
189 """ | |
190 for node in parent.childNodes: | |
191 if node.nodeName == tag and node.getAttribute("name") == name: | |
192 #the node already exists | |
193 return node | |
194 #the node is new | |
195 return None | |
196 | |
197 def importParams(self, xml): | |
198 """import xml in parameters, do nothing if the param already exist | |
199 @param xml: parameters in xml form""" | |
200 src_dom = minidom.parseString(xml.encode('utf-8')) | |
201 | |
202 def import_node(tgt_parent, src_parent): | |
203 for child in src_parent.childNodes: | |
204 if child.nodeName == '#text': | |
205 continue | |
206 node = self.__get_unique_node(tgt_parent, child.nodeName, child.getAttribute("name")) | |
207 if not node: #The node is new | |
208 tgt_parent.appendChild(child) | |
209 else: | |
210 import_node(node, child) | |
211 | |
212 import_node(self.dom.documentElement, src_dom.documentElement) | |
213 | |
214 def __default_ok(self, value, name, category): | |
215 #FIXME: gof: will not work with individual parameters | |
216 self.setParam(name, value, category) #FIXME: better to set param xml value ??? | |
217 | |
218 def __default_ko(self, failure, name, category): | |
219 error (_("Can't determine default value for [%(category)s/%(name)s]: %(reason)s") % {'category':category, 'name':name, 'reason':str(failure.value)}) | |
220 | |
221 def setDefault(self, name, category, callback, errback=None): | |
222 """Set default value of parameter | |
223 'default_cb' attibute of parameter must be set to 'yes' | |
224 @param name: name of the parameter | |
225 @param category: category of the parameter | |
226 @param callback: must return a string with the value (use deferred if needed) | |
227 @param errback: must manage the error with args failure, name, category | |
228 """ | |
229 #TODO: send signal param update if value changed | |
230 node = self.__getParamNode(name, category, '@ALL@') | |
231 if not node: | |
232 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category}) | |
233 return | |
234 if node[1].getAttribute('default_cb') == 'yes': | |
235 del node[1].attributes['default_cb'] | |
236 d = defer.maybeDeferred(callback) | |
237 d.addCallback(self.__default_ok, name, category) | |
238 d.addErrback(errback or self.__default_ko, name, category) | |
239 | |
240 def getParamA(self, name, category, attr="value", profile_key="@DEFAULT@"): | |
241 """Helper method to get a specific attribute | |
242 @param name: name of the parameter | |
243 @param category: category of the parameter | |
244 @param attr: name of the attribute (default: "value") | |
245 @param profile: owner of the param (@ALL@ for everyone) | |
246 | |
247 @return: attribute""" | |
248 node = self.__getParamNode(name, category) | |
249 if not node: | |
250 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category}) | |
251 return "" | |
252 | |
253 if node[0] == 'general': | |
254 value = self.__getParam(None, category, name, 'general') | |
255 return value if value!=None else node[1].getAttribute(attr) | |
256 | |
257 assert(node[0] == 'individual') | |
258 | |
259 profile = self.getProfileName(profile_key) | |
260 if not profile: | |
261 error(_('Requesting a param for an non-existant profile')) | |
262 return "" | |
263 | |
264 if profile not in self.params: | |
265 error(_('Requesting synchronous param for not connected profile')) | |
266 return "" | |
267 | |
268 if attr == "value": | |
269 value = self.__getParam(profile, category, name) | |
270 return value if value!=None else node[1].getAttribute(attr) | |
271 else: | |
272 return node[1].getAttribute(attr) | |
273 | |
274 def asyncGetParamA(self, name, category, attr="value", profile_key="@DEFAULT@"): | |
275 """Helper method to get a specific attribute | |
276 @param name: name of the parameter | |
277 @param category: category of the parameter | |
278 @param attr: name of the attribute (default: "value") | |
279 @param profile: owner of the param (@ALL@ for everyone)""" | |
280 node = self.__getParamNode(name, category) | |
281 if not node: | |
282 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category}) | |
283 return None | |
284 | |
285 if node[0] == 'general': | |
286 value = self.__getParam(None, category, name, 'general') | |
287 return defer.succeed(value if value!=None else node[1].getAttribute(attr)) | |
288 | |
289 assert(node[0] == 'individual') | |
290 | |
291 profile = self.getProfileName(profile_key) | |
292 if not profile: | |
293 error(_('Requesting a param for a non-existant profile')) | |
294 return defer.fail() | |
295 | |
296 if attr != "value": | |
297 return defer.succeed(node[1].getAttribute(attr)) | |
298 default = node[1].getAttribute(attr) | |
299 try: | |
300 value = self.__getParam(profile, category, name) | |
301 return defer.succeed(value if value!=None else default) | |
302 except ProfileNotInCacheError: | |
303 #We have to ask data to the storage manager | |
304 d = self.storage.getIndParam(category, name, profile) | |
305 return d.addCallback(lambda value: value if value!=None else default) | |
306 | |
307 def __getParam(self, profile, category, name, type='individual', cache=None): | |
308 """Return the param, or None if it doesn't exist | |
309 @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@) | |
310 @param category: param category | |
311 @param name: param name | |
312 @param type: "general" or "individual" | |
313 @param cache: temporary cache, to use when profile is not logged | |
314 @return: param value or None if it doesn't exist | |
315 """ | |
316 if type == 'general': | |
317 if self.params_gen.has_key((category, name)): | |
318 return self.params_gen[(category, name)] | |
319 return None #This general param has the default value | |
320 assert (type == 'individual') | |
321 if self.params.has_key(profile): | |
322 cache = self.params[profile] # if profile is in main cache, we use it, | |
323 # ignoring the temporary cache | |
324 elif cache == None: #else we use the temporary cache if it exists, or raise an exception | |
325 raise ProfileNotInCacheError | |
326 if not cache.has_key((category, name)): | |
327 return None | |
328 return cache[(category, name)] | |
329 | |
330 def __constructProfileXml(self, profile): | |
331 """Construct xml for asked profile, filling values when needed | |
332 /!\ as noticed in doc, don't forget to unlink the minidom.Document | |
333 @param profile: profile name (not key !) | |
334 @return: a deferred that fire a minidom.Document of the profile xml (cf warning above) | |
335 """ | |
336 def constructProfile(ignore,profile_cache): | |
337 prof_xml = minidom.parseString('<params/>') | |
338 cache = {} | |
339 | |
340 for type_node in self.dom.documentElement.childNodes: | |
341 if type_node.nodeName == 'general' or type_node.nodeName == 'individual': #we use all params, general and individual | |
342 for cat_node in type_node.childNodes: | |
343 if cat_node.nodeName == 'category': | |
344 category = cat_node.getAttribute('name') | |
345 if not cache.has_key(category): | |
346 cache[category] = dest_cat = cat_node.cloneNode(True) #we make a copy for the new xml | |
347 new_node = True | |
348 else: | |
349 dest_cat = cache[category] | |
350 new_node = False #It's not a new node, we will merge information | |
351 params = cat_node.getElementsByTagName("param") | |
352 dest_params = {} | |
353 for node in dest_cat.childNodes: | |
354 if node.nodeName != "param": | |
355 continue | |
356 dest_params[node.getAttribute('name')] = node | |
357 | |
358 for param_node in params: | |
359 name = param_node.getAttribute('name') | |
360 | |
361 if name not in dest_params: | |
362 dest_params[name] = param_node.cloneNode(True) | |
363 dest_cat.appendChild(dest_params[name]) | |
364 | |
365 profile_value = self.__getParam(profile, category, name, type_node.nodeName, cache=profile_cache) | |
366 if profile_value!=None: #there is a value for this profile, we must change the default | |
367 dest_params[name].setAttribute('value', profile_value) | |
368 if new_node: | |
369 prof_xml.documentElement.appendChild(dest_cat) | |
370 return prof_xml | |
371 | |
372 | |
373 if self.params.has_key(profile): | |
374 d = defer.succeed(None) | |
375 profile_cache = self.params[profile] | |
376 else: | |
377 #profile is not in cache, we load values in a short time cache | |
378 profile_cache = {} | |
379 d = self.loadIndParams(profile, profile_cache) | |
380 | |
381 return d.addCallback(constructProfile, profile_cache) | |
382 | |
383 def getParamsUI(self, profile_key): | |
384 """Return a SàT XMLUI for parameters, with given profile""" | |
385 profile = self.getProfileName(profile_key) | |
386 if not profile: | |
387 error(_("Asking params for inexistant profile")) | |
388 return "" | |
389 d = self.getParams(profile) | |
390 return d.addCallback(lambda param_xml:paramsXml2xmlUI(param_xml)) | |
391 | |
392 def getParams(self, profile_key): | |
393 """Construct xml for asked profile | |
394 Take params xml as skeleton""" | |
395 profile = self.getProfileName(profile_key) | |
396 if not profile: | |
397 error(_("Asking params for inexistant profile")) | |
398 return "" | |
399 | |
400 def returnXML(prof_xml): | |
401 return_xml = prof_xml.toxml() | |
402 prof_xml.unlink() | |
403 return return_xml | |
404 | |
405 return self.__constructProfileXml(profile).addCallback(returnXML) | |
406 | |
407 def getParamsForCategory(self, category, profile_key): | |
408 """Return node's xml for selected category""" | |
409 #TODO: manage category of general type (without existant profile) | |
410 profile = self.getProfileName(profile_key) | |
411 if not profile: | |
412 error(_("Asking params for inexistant profile")) | |
413 return "" | |
414 | |
415 def returnCategoryXml(prof_xml): | |
416 for node in prof_xml.getElementsByTagName("category"): | |
417 if node.nodeName == "category" and node.getAttribute("name") == category: | |
418 result = node.toxml() | |
419 prof_xml.unlink() | |
420 return result | |
421 | |
422 prof_xml.unlink() | |
423 return "<category />" | |
424 | |
425 d = self.__constructProfileXml(profile) | |
426 return d.addCallback(returnCategoryXml) | |
427 | |
428 def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ? | |
429 """Return a node from the param_xml | |
430 @param name: name of the node | |
431 @param category: category of the node | |
432 @type: keyword for search: | |
433 @ALL@ search everywhere | |
434 @GENERAL@ only search in general type | |
435 @INDIVIDUAL@ only search in individual type | |
436 @return: a tuple with the node type and the the node, or None if not found""" | |
437 | |
438 for type_node in self.dom.documentElement.childNodes: | |
439 if ( ((type == "@ALL@" or type == "@GENERAL@") and type_node.nodeName == 'general') | |
440 or ( (type == "@ALL@" or type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ): | |
441 for node in type_node.getElementsByTagName('category'): | |
442 if node.getAttribute("name") == category: | |
443 params = node.getElementsByTagName("param") | |
444 for param in params: | |
445 if param.getAttribute("name") == name: | |
446 return (type_node.nodeName, param) | |
447 return None | |
448 | |
449 def getParamsCategories(self): | |
450 """return the categories availables""" | |
451 categories=[] | |
452 for cat in self.dom.getElementsByTagName("category"): | |
453 name = cat.getAttribute("name") | |
454 if name not in categories: | |
455 categories.append(cat.getAttribute("name")) | |
456 return categories | |
457 | |
458 def setParam(self, name, value, category, profile_key='@NONE@'): | |
459 """Set a parameter, return None if the parameter is not in param xml""" | |
460 #TODO: use different behaviour depending of the data type (e.g. password encrypted) | |
461 if profile_key!="@NONE@": | |
462 profile = self.getProfileName(profile_key) | |
463 if not profile: | |
464 error(_('Trying to set parameter for an unknown profile')) | |
465 return #TODO: throw an error | |
466 | |
467 node = self.__getParamNode(name, category, '@ALL@') | |
468 if not node: | |
469 error(_('Requesting an unknown parameter (%(category)s/%(name)s)') % {'category':category, 'name':name}) | |
470 return | |
471 | |
472 if node[0] == 'general': | |
473 self.params_gen[(category, name)] = value | |
474 self.storage.setGenParam(category, name, value) | |
475 for profile in self.storage.getProfilesList(): | |
476 if self.host.isConnected(profile): | |
477 self.host.bridge.paramUpdate(name, value, category, profile) | |
478 return | |
479 | |
480 assert (node[0] == 'individual') | |
481 assert (profile_key != "@NONE@") | |
482 | |
483 type = node[1].getAttribute("type") | |
484 if type=="button": | |
485 print "clique",node.toxml() | |
486 else: | |
487 if self.host.isConnected(profile): #key can not exists if profile is not connected | |
488 self.params[profile][(category, name)] = value | |
489 self.host.bridge.paramUpdate(name, value, category, profile) | |
490 self.storage.setIndParam(category, name, value, profile) | |
491 | |
492 class Memory: | |
493 """This class manage all persistent informations""" | |
494 | |
495 def __init__(self, host): | |
496 info (_("Memory manager init")) | |
497 self.initialized = defer.Deferred() | |
498 self.host = host | |
499 self.contacts={} | |
500 self.presenceStatus={} | |
501 self.lastResource={} #tmp, will be refactored with bdd integration | |
502 self.subscriptions={} | |
503 self.private={} #used to store private value | |
504 self.server_features={} #used to store discovery's informations | |
505 self.server_identities={} | |
506 self.config = self.parseMainConf() | |
507 host.set_const('savefile_private', SAVEFILE_PRIVATE) | |
508 host.set_const('savefile_database', SAVEFILE_DATABASE) | |
509 database_file = os.path.expanduser(self.getConfig('','local_dir')+ | |
510 self.host.get_const('savefile_database')) | |
511 self.storage = SqliteStorage(database_file) | |
512 PersistentDict.storage = self.storage | |
513 self.params=Params(host, self.storage) | |
514 self.loadFiles() | |
515 d = self.storage.initialized.addCallback(self.load) | |
516 d.chainDeferred(self.initialized) | |
517 | |
518 def parseMainConf(self): | |
519 """look for main .ini configuration file, and parse it""" | |
520 _config = SafeConfigParser(defaults=default_config) | |
521 try: | |
522 _config.read(map(os.path.expanduser, ['/etc/sat.conf', '~/sat.conf', '~/.sat.conf', 'sat.conf', '.sat.conf'])) | |
523 except: | |
524 error (_("Can't read main config !")) | |
525 | |
526 return _config | |
527 | |
528 def getConfig(self, section, name): | |
529 """Get the main configuration option | |
530 @param section: section of the config file (None or '' for DEFAULT) | |
531 @param name: name of the option | |
532 """ | |
533 if not section: | |
534 section='DEFAULT' | |
535 try: | |
536 _value = self.config.get(section, name) | |
537 except NoOptionError, NoSectionError: | |
538 _value = '' | |
539 | |
540 return os.path.expanduser(_value) if name.endswith('_path') or name.endswith('_dir') else _value | |
541 | |
542 | |
543 def loadFiles(self): | |
544 """Load parameters and all memory things from file/db""" | |
545 param_file_xml = os.path.expanduser(self.getConfig('','local_dir')+ | |
546 self.host.get_const('savefile_param_xml')) | |
547 private_file = os.path.expanduser(self.getConfig('','local_dir')+ | |
548 self.host.get_const('savefile_private')) | |
549 | |
550 #parameters template | |
551 if os.path.exists(param_file_xml): | |
552 try: | |
553 self.params.load_xml(param_file_xml) | |
554 debug(_("params template loaded")) | |
555 except: | |
556 error (_("Can't load params template !")) | |
557 self.params.load_default_params() | |
558 else: | |
559 info (_("No params template, using default template")) | |
560 self.params.load_default_params() | |
561 | |
562 #private | |
563 if os.path.exists(private_file): | |
564 try: | |
565 with open(private_file, 'r') as private_pickle: | |
566 self.private=pickle.load(private_pickle) | |
567 debug(_("private values loaded")) | |
568 except: | |
569 error (_("Can't load private values !")) | |
570 | |
571 def load(self, ignore): | |
572 """Load parameters and all memory things from db""" | |
573 #parameters data | |
574 return self.params.loadGenParams() | |
575 | |
576 def loadIndividualParams(self, profile): | |
577 """Load individual parameters for a profile | |
578 @param profile: %(doc_profile)s""" | |
579 return self.params.loadIndParams(profile) | |
580 | |
581 def purgeProfile(self, profile): | |
582 """Delete cache of data of profile | |
583 @param profile: %(doc_profile)s""" | |
584 self.params.purgeProfile(profile) | |
585 | |
586 def save(self): | |
587 """Save parameters and all memory things to file/db""" | |
588 #TODO: need to encrypt files (at least passwords !) and set permissions | |
589 param_file_xml = os.path.expanduser(self.getConfig('','local_dir')+ | |
590 self.host.get_const('savefile_param_xml')) | |
591 private_file = os.path.expanduser(self.getConfig('','local_dir')+ | |
592 self.host.get_const('savefile_private')) | |
593 | |
594 self.params.save_xml(param_file_xml) | |
595 debug(_("params saved")) | |
596 with open(private_file, 'w') as private_pickle: | |
597 pickle.dump(self.private, private_pickle) | |
598 debug(_("private values saved")) | |
599 | |
600 def getProfilesList(self): | |
601 return self.storage.getProfilesList() | |
602 | |
603 | |
604 def getProfileName(self, profile_key): | |
605 """Return name of profile from keyword | |
606 @param profile_key: can be the profile name or a keywork (like @DEFAULT@) | |
607 @return: profile name or None if it doesn't exist""" | |
608 return self.params.getProfileName(profile_key) | |
609 | |
610 def createProfile(self, name): | |
611 """Create a new profile | |
612 @param name: Profile name | |
613 """ | |
614 return self.params.createProfile(name) | |
615 | |
616 def asyncCreateProfile(self, name): | |
617 """Create a new profile | |
618 @param name: Profile name | |
619 """ | |
620 return self.params.asyncCreateProfile(name) | |
621 | |
622 def deleteProfile(self, name): | |
623 """Delete an existing profile | |
624 @param name: Name of the profile""" | |
625 return self.params.deleteProfile(name) | |
626 | |
627 def addToHistory(self, from_jid, to_jid, message, timestamp=None, profile="@NONE@"): | |
628 assert(profile!="@NONE@") | |
629 return self.storage.addToHistory(from_jid, to_jid, message, timestamp, profile) | |
630 | |
631 def getHistory(self, from_jid, to_jid, limit=0, between=True): | |
632 return self.storage.getHistory(jid.JID(from_jid), jid.JID(to_jid), limit, between) | |
633 | |
634 def setPrivate(self, key, value): | |
635 """Save a misc private value (mainly useful for plugins)""" | |
636 self.private[key] = value | |
637 | |
638 def getPrivate(self, key): | |
639 """return a private value | |
640 @param key: name of wanted value | |
641 @return: value or None if value don't exist""" | |
642 if self.private.has_key(key): | |
643 return self.private[key] | |
644 return None | |
645 | |
646 def addServerFeature(self, feature, profile): | |
647 """Add a feature discovered from server | |
648 @param feature: string of the feature | |
649 @param profile: which profile is using this server ?""" | |
650 if not self.server_features.has_key(profile): | |
651 self.server_features[profile] = [] | |
652 self.server_features[profile].append(feature) | |
653 | |
654 def addServerIdentity(self, category, type, entity, profile): | |
655 """Add an identity discovered from server | |
656 @param feature: string of the feature | |
657 @param profile: which profile is using this server ?""" | |
658 if not self.server_identities.has_key(profile): | |
659 self.server_identities[profile] = {} | |
660 if not self.server_identities[profile].has_key((category, type)): | |
661 self.server_identities[profile][(category, type)]=set() | |
662 self.server_identities[profile][(category, type)].add(entity) | |
663 | |
664 def getServerServiceEntities(self, category, type, profile): | |
665 """Return all available entities for a service""" | |
666 if self.server_identities.has_key(profile): | |
667 return self.server_identities[profile].get((category, type), set()) | |
668 else: | |
669 return None | |
670 | |
671 def getServerServiceEntity(self, category, type, profile): | |
672 """Helper method to get first available entity for a service""" | |
673 entities = self.getServerServiceEntities(category, type, profile) | |
674 if entities == None: | |
675 warning(_("Entities not available, maybe they haven't been asked to server yet ?")) | |
676 return None | |
677 else: | |
678 return list(entities)[0] if entities else None | |
679 | |
680 def hasServerFeature(self, feature, profile_key): | |
681 """Tell if the server of the profile has the required feature""" | |
682 profile = self.getProfileName(profile_key) | |
683 if not profile: | |
684 error (_('Trying find server feature for a non-existant profile')) | |
685 return | |
686 assert(self.server_features.has_key(profile)) | |
687 return feature in self.server_features[profile] | |
688 | |
689 | |
690 def addContact(self, contact_jid, attributes, groups, profile_key): | |
691 debug("Memory addContact: %s",contact_jid.userhost()) | |
692 profile = self.getProfileName(profile_key) | |
693 if not profile: | |
694 error (_('Trying to add a contact to a non-existant profile')) | |
695 return | |
696 assert(isinstance(attributes,dict)) | |
697 assert(isinstance(groups,set)) | |
698 if not self.contacts.has_key(profile): | |
699 self.contacts[profile] = {} | |
700 self.contacts[profile][contact_jid.userhost()]=[attributes, groups] | |
701 | |
702 def delContact(self, contact_jid, profile_key): | |
703 debug("Memory delContact: %s",contact_jid.userhost()) | |
704 profile = self.getProfileName(profile_key) | |
705 if not profile: | |
706 error (_('Trying to delete a contact for a non-existant profile')) | |
707 return | |
708 if self.contacts.has_key(profile) and self.contacts[profile].has_key(contact_jid.userhost()): | |
709 del self.contacts[profile][contact_jid.userhost()] | |
710 | |
711 def getContact(self, contact_jid, profile_key): | |
712 profile = self.getProfileName(profile_key) | |
713 if not profile: | |
714 error(_('Asking a contact for a non-existant profile')) | |
715 return None | |
716 if self.contacts.has_key(profile) and self.contacts[profile].has_key(contact_jid.userhost()): | |
717 return self.contacts[profile][contact_jid.userhost()] | |
718 | |
719 def getContacts(self, profile_key): | |
720 """Return list of contacts for given profile | |
721 @param profile_key: profile key | |
722 @return list of [contact, attr, groups]""" | |
723 profile = self.getProfileName(profile_key) | |
724 if not profile: | |
725 error(_('Asking contacts for a non-existant profile')) | |
726 return [] | |
727 ret=[] | |
728 if self.contacts.has_key(profile): | |
729 for contact in self.contacts[profile]: | |
730 attr, groups = self.contacts[profile][contact] | |
731 ret.append([contact, attr, groups ]) | |
732 return ret | |
733 | |
734 def getLastResource(self, contact, profile_key): | |
735 """Return the last resource used by a contact | |
736 @param contact: contact jid (unicode) | |
737 @param profile_key: %(doc_profile_key)s""" | |
738 profile = self.getProfileName(profile_key) | |
739 if not profile: | |
740 error(_('Asking contacts for a non-existant profile')) | |
741 return "" | |
742 try: | |
743 return self.lastResource[profile][jid.JID(contact).userhost()] | |
744 except: | |
745 return "" | |
746 | |
747 def addPresenceStatus(self, contact_jid, show, priority, statuses, profile_key): | |
748 profile = self.getProfileName(profile_key) | |
749 if not profile: | |
750 error(_('Trying to add presence status to a non-existant profile')) | |
751 return | |
752 if not self.presenceStatus.has_key(profile): | |
753 self.presenceStatus[profile] = {} | |
754 if not self.lastResource.has_key(profile): | |
755 self.lastResource[profile] = {} | |
756 if not self.presenceStatus[profile].has_key(contact_jid.userhost()): | |
757 self.presenceStatus[profile][contact_jid.userhost()] = {} | |
758 resource = jid.parse(contact_jid.full())[2] or '' | |
759 if resource: | |
760 self.lastResource[profile][contact_jid.userhost()] = resource | |
761 | |
762 self.presenceStatus[profile][contact_jid.userhost()][resource] = (show, priority, statuses) | |
763 | |
764 def addWaitingSub(self, type, contact_jid, profile_key): | |
765 """Called when a subcription request is received""" | |
766 profile = self.getProfileName(profile_key) | |
767 assert(profile) | |
768 if not self.subscriptions.has_key(profile): | |
769 self.subscriptions[profile] = {} | |
770 self.subscriptions[profile][contact_jid] = type | |
771 | |
772 def delWaitingSub(self, contact_jid, profile_key): | |
773 """Called when a subcription request is finished""" | |
774 profile = self.getProfileName(profile_key) | |
775 assert(profile) | |
776 if self.subscriptions.has_key(profile) and self.subscriptions[profile].has_key(contact_jid): | |
777 del self.subscriptions[profile][contact_jid] | |
778 | |
779 def getWaitingSub(self, profile_key): | |
780 """Called to get a list of currently waiting subscription requests""" | |
781 profile = self.getProfileName(profile_key) | |
782 if not profile: | |
783 error(_('Asking waiting subscriptions for a non-existant profile')) | |
784 return {} | |
785 if not self.subscriptions.has_key(profile): | |
786 return {} | |
787 | |
788 return self.subscriptions[profile] | |
789 | |
790 def getPresenceStatus(self, profile_key): | |
791 profile = self.getProfileName(profile_key) | |
792 if not profile: | |
793 error(_('Asking contacts for a non-existant profile')) | |
794 return {} | |
795 if not self.presenceStatus.has_key(profile): | |
796 self.presenceStatus[profile] = {} | |
797 debug ("Memory getPresenceStatus (%s)", self.presenceStatus[profile]) | |
798 return self.presenceStatus[profile] | |
799 | |
800 def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): | |
801 return self.params.getParamA(name, category, attr, profile_key) | |
802 | |
803 def asyncGetParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): | |
804 return self.params.asyncGetParamA(name, category, attr, profile_key) | |
805 | |
806 def getParamsUI(self, profile_key): | |
807 return self.params.getParamsUI(profile_key) | |
808 | |
809 def getParams(self, profile_key): | |
810 return self.params.getParams(profile_key) | |
811 | |
812 def getParamsForCategory(self, category, profile_key): | |
813 return self.params.getParamsForCategory(category, profile_key) | |
814 | |
815 def getParamsCategories(self): | |
816 return self.params.getParamsCategories() | |
817 | |
818 def setParam(self, name, value, category, profile_key): | |
819 return self.params.setParam(name, value, category, profile_key) | |
820 | |
821 def importParams(self, xml): | |
822 return self.params.importParams(xml) | |
823 | |
824 def setDefault(self, name, category, callback, errback=None): | |
825 return self.params.setDefault(name, category, callback, errback) |