comparison src/memory/memory.py @ 592:e5a875a3311b

Fix pep8 support in src/memory.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Fri, 18 Jan 2013 17:55:35 +0100
parents beaf6bec2fcd
children 84a6e83157c2
comparison
equal deleted inserted replaced
591:65821b3fa7ab 592:e5a875a3311b
31 from sat.core.default_config import default_config 31 from sat.core.default_config import default_config
32 from sat.memory.sqlite import SqliteStorage 32 from sat.memory.sqlite import SqliteStorage
33 from sat.memory.persistent import PersistentDict 33 from sat.memory.persistent import PersistentDict
34 from sat.core import exceptions 34 from sat.core import exceptions
35 35
36 SAVEFILE_PARAM_XML="/param" #xml parameters template 36 SAVEFILE_PARAM_XML = "/param" # xml parameters template
37 SAVEFILE_DATABASE="/sat.db" 37 SAVEFILE_DATABASE = "/sat.db"
38 38
39 39
40 class Params(object): 40 class Params(object):
41 """This class manage parameters with xml""" 41 """This class manage parameters with xml"""
42 ### TODO: add desciption in params 42 ### TODO: add desciption in params
59 <category name="Misc" label="%(category_misc)s"> 59 <category name="Misc" label="%(category_misc)s">
60 <param name="Watched" value="test@Jabber.goffi.int" type="string" /> 60 <param name="Watched" value="test@Jabber.goffi.int" type="string" />
61 </category> 61 </category>
62 </individual> 62 </individual>
63 </params> 63 </params>
64 """ % {'category_connection': _("Connection"), 64 """ % {
65 'label_NewAccount': _("Register new account"), 65 'category_connection': _("Connection"),
66 'label_autoconnect': _('Connect on frontend startup'), 66 'label_NewAccount': _("Register new account"),
67 'label_autodisconnect': _('Disconnect on frontend closure'), 67 'label_autoconnect': _('Connect on frontend startup'),
68 'category_misc': _("Misc") 68 'label_autodisconnect': _('Disconnect on frontend closure'),
69 } 69 'category_misc': _("Misc")
70 }
70 71
71 def load_default_params(self): 72 def load_default_params(self):
72 self.dom = minidom.parseString(Params.default_xml.encode('utf-8')) 73 self.dom = minidom.parseString(Params.default_xml.encode('utf-8'))
73 74
74 def _mergeParams(self, source_node, dest_node): 75 def _mergeParams(self, source_node, dest_node):
75 """Look for every node in source_node and recursively copy them to dest if they don't exists""" 76 """Look for every node in source_node and recursively copy them to dest if they don't exists"""
77
76 def getNodesMap(children): 78 def getNodesMap(children):
77 ret = {} 79 ret = {}
78 for child in children: 80 for child in children:
79 if child.nodeType == child.ELEMENT_NODE: 81 if child.nodeType == child.ELEMENT_NODE:
80 ret[(child.tagName, child.getAttribute('name'))] = child 82 ret[(child.tagName, child.getAttribute('name'))] = child
107 """Load individual parameters 109 """Load individual parameters
108 set self.params cache or a temporary cache 110 set self.params cache or a temporary cache
109 @param profile: profile to load (*must exist*) 111 @param profile: profile to load (*must exist*)
110 @param cache: if not None, will be used to store the value, as a short time cache 112 @param cache: if not None, will be used to store the value, as a short time cache
111 @return: deferred triggered once params are loaded""" 113 @return: deferred triggered once params are loaded"""
112 if cache == None: 114 if cache is None:
113 self.params[profile] = {} 115 self.params[profile] = {}
114 return self.storage.loadIndParams(self.params[profile] if cache==None else cache, profile) 116 return self.storage.loadIndParams(self.params[profile] if cache is None else cache, profile)
115 117
116 def purgeProfile(self, profile): 118 def purgeProfile(self, profile):
117 """Remove cache data of a profile 119 """Remove cache data of a profile
118 @param profile: %(doc_profile)s""" 120 @param profile: %(doc_profile)s"""
119 try: 121 try:
120 del self.params[profile] 122 del self.params[profile]
121 except KeyError: 123 except KeyError:
122 error(_("Trying to purge cache of a profile not in memory: [%s]") % profile) 124 error(_("Trying to purge cache of a profile not in memory: [%s]") % profile)
123 125
124 def save_xml(self, file): 126 def save_xml(self, filename):
125 """Save parameters template to xml file""" 127 """Save parameters template to xml file"""
126 with open(file, 'wb') as xml_file: 128 with open(filename, 'wb') as xml_file:
127 xml_file.write(self.dom.toxml('utf-8')) 129 xml_file.write(self.dom.toxml('utf-8'))
128 130
129 def __init__(self, host, storage): 131 def __init__(self, host, storage):
130 debug("Parameters init") 132 debug("Parameters init")
131 self.host = host 133 self.host = host
139 def createProfile(self, profile): 141 def createProfile(self, profile):
140 """Create a new profile 142 """Create a new profile
141 @param profile: profile of the profile""" 143 @param profile: profile of the profile"""
142 #FIXME: must be asynchronous and call the callback once the profile actually exists 144 #FIXME: must be asynchronous and call the callback once the profile actually exists
143 if self.storage.hasProfile(profile): 145 if self.storage.hasProfile(profile):
144 info (_('The profile [%s] already exists') % (profile,)) 146 info(_('The profile [%s] already exists') % (profile, ))
145 return True 147 return True
146 if not self.host.trigger.point("ProfileCreation", profile): 148 if not self.host.trigger.point("ProfileCreation", profile):
147 return False 149 return False
148 self.storage.createProfile(profile) 150 self.storage.createProfile(profile)
149 return False 151 return False
155 @param errback: called with a string constant as parameter: 157 @param errback: called with a string constant as parameter:
156 - CONFLICT: the profile already exists 158 - CONFLICT: the profile already exists
157 - CANCELED: profile creation canceled 159 - CANCELED: profile creation canceled
158 """ 160 """
159 if self.storage.hasProfile(profile): 161 if self.storage.hasProfile(profile):
160 info (_('The profile name already exists')) 162 info(_('The profile name already exists'))
161 return defer.fail("CONFLICT") 163 return defer.fail("CONFLICT")
162 if not self.host.trigger.point("ProfileCreation", profile): 164 if not self.host.trigger.point("ProfileCreation", profile):
163 return defer.fail("CANCEL") 165 return defer.fail("CANCEL")
164 return self.storage.createProfile(profile) 166 return self.storage.createProfile(profile)
165
166 167
167 def deleteProfile(self, profile): 168 def deleteProfile(self, profile):
168 """Delete an existing profile 169 """Delete an existing profile
169 @param profile: name of the profile""" 170 @param profile: name of the profile"""
170 #TODO: async equivalent, like for createProfile 171 #TODO: async equivalent, like for createProfile
181 """return profile according to profile_key 182 """return profile according to profile_key
182 @param profile_key: profile name or key which can be 183 @param profile_key: profile name or key which can be
183 @ALL@ for all profiles 184 @ALL@ for all profiles
184 @DEFAULT@ for default profile 185 @DEFAULT@ for default profile
185 @return: requested profile name or None if it doesn't exist""" 186 @return: requested profile name or None if it doesn't exist"""
186 if profile_key=='@DEFAULT@': 187 if profile_key == '@DEFAULT@':
187 default = self.host.memory.memory_data.get('Profile_default') 188 default = self.host.memory.memory_data.get('Profile_default')
188 if not default: 189 if not default:
189 info(_('No default profile, returning first one')) #TODO: manage real default profile 190 info(_('No default profile, returning first one')) # TODO: manage real default profile
190 default = self.host.memory.memory_data['Profile_default'] = self.storage.getProfilesList()[0] 191 default = self.host.memory.memory_data['Profile_default'] = self.storage.getProfilesList()[0]
191 return default #FIXME: temporary, must use real default value, and fallback to first one if it doesn't exists 192 return default # FIXME: temporary, must use real default value, and fallback to first one if it doesn't exists
192 if not self.storage.hasProfile(profile_key): 193 if not self.storage.hasProfile(profile_key):
193 info (_('Trying to access an unknown profile')) 194 info(_('Trying to access an unknown profile'))
194 return "" 195 return ""
195 return profile_key 196 return profile_key
196 197
197 def __get_unique_node(self, parent, tag, name): 198 def __get_unique_node(self, parent, tag, name):
198 """return node with given tag 199 """return node with given tag
216 def import_node(tgt_parent, src_parent): 217 def import_node(tgt_parent, src_parent):
217 for child in src_parent.childNodes: 218 for child in src_parent.childNodes:
218 if child.nodeName == '#text': 219 if child.nodeName == '#text':
219 continue 220 continue
220 node = self.__get_unique_node(tgt_parent, child.nodeName, child.getAttribute("name")) 221 node = self.__get_unique_node(tgt_parent, child.nodeName, child.getAttribute("name"))
221 if not node: #The node is new 222 if not node: # The node is new
222 tgt_parent.appendChild(child) 223 tgt_parent.appendChild(child)
223 else: 224 else:
224 import_node(node, child) 225 import_node(node, child)
225 226
226 import_node(self.dom.documentElement, src_dom.documentElement) 227 import_node(self.dom.documentElement, src_dom.documentElement)
227 228
228 def __default_ok(self, value, name, category): 229 def __default_ok(self, value, name, category):
229 #FIXME: gof: will not work with individual parameters 230 #FIXME: gof: will not work with individual parameters
230 self.setParam(name, value, category) #FIXME: better to set param xml value ??? 231 self.setParam(name, value, category) # FIXME: better to set param xml value ???
231 232
232 def __default_ko(self, failure, name, category): 233 def __default_ko(self, failure, name, category):
233 error (_("Can't determine default value for [%(category)s/%(name)s]: %(reason)s") % {'category':category, 'name':name, 'reason':str(failure.value)}) 234 error(_("Can't determine default value for [%(category)s/%(name)s]: %(reason)s") % {'category': category, 'name': name, 'reason': str(failure.value)})
234 235
235 def setDefault(self, name, category, callback, errback=None): 236 def setDefault(self, name, category, callback, errback=None):
236 """Set default value of parameter 237 """Set default value of parameter
237 'default_cb' attibute of parameter must be set to 'yes' 238 'default_cb' attibute of parameter must be set to 'yes'
238 @param name: name of the parameter 239 @param name: name of the parameter
239 @param category: category of the parameter 240 @param category: category of the parameter
240 @param callback: must return a string with the value (use deferred if needed) 241 @param callback: must return a string with the value (use deferred if needed)
241 @param errback: must manage the error with args failure, name, category 242 @param errback: must manage the error with args failure, name, category
242 """ 243 """
243 #TODO: send signal param update if value changed 244 #TODO: send signal param update if value changed
244 node = self.__getParamNode(name, category, '@ALL@') 245 node = self.__getParamNode(name, category, '@ALL@')
245 if not node: 246 if not node:
246 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category}) 247 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category})
247 return 248 return
248 if node[1].getAttribute('default_cb') == 'yes': 249 if node[1].getAttribute('default_cb') == 'yes':
249 del node[1].attributes['default_cb'] 250 del node[1].attributes['default_cb']
250 d = defer.maybeDeferred(callback) 251 d = defer.maybeDeferred(callback)
251 d.addCallback(self.__default_ok, name, category) 252 d.addCallback(self.__default_ok, name, category)
255 """ get attribute value 256 """ get attribute value
256 @param node: XML param node 257 @param node: XML param node
257 @param attr: name of the attribute to get (e.g.: 'value' or 'type') 258 @param attr: name of the attribute to get (e.g.: 'value' or 'type')
258 @param value: user defined value""" 259 @param value: user defined value"""
259 if attr == 'value': 260 if attr == 'value':
260 value_to_use = value if value!=None else node.getAttribute(attr) #we use value (user defined) if it exist, else we use node's default value 261 value_to_use = value if value is not None else node.getAttribute(attr) # we use value (user defined) if it exist, else we use node's default value
261 if node.getAttribute('type') == 'bool': 262 if node.getAttribute('type') == 'bool':
262 return value_to_use.lower() not in ('false','0') 263 return value_to_use.lower() not in ('false', '0')
263 return value_to_use 264 return value_to_use
264 return node.getAttribute(attr) 265 return node.getAttribute(attr)
265 266
266 def __type_to_string(self, result): 267 def __type_to_string(self, result):
267 """ convert result to string, according to its type """ 268 """ convert result to string, according to its type """
268 if isinstance(result,bool): 269 if isinstance(result, bool):
269 return "true" if result else "false" 270 return "true" if result else "false"
270 return result 271 return result
271 272
272 def getStringParamA(self, name, category, attr="value", profile_key="@DEFAULT@"): 273 def getStringParamA(self, name, category, attr="value", profile_key="@DEFAULT@"):
273 """ Same as getParamA but for bridge: convert non string value to string """ 274 """ Same as getParamA but for bridge: convert non string value to string """
282 283
283 @return: attribute""" 284 @return: attribute"""
284 #FIXME: looks really dirty and buggy, need to be reviewed/refactored 285 #FIXME: looks really dirty and buggy, need to be reviewed/refactored
285 node = self.__getParamNode(name, category) 286 node = self.__getParamNode(name, category)
286 if not node: 287 if not node:
287 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category}) 288 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category})
288 raise exceptions.NotFound 289 raise exceptions.NotFound
289 290
290 if node[0] == 'general': 291 if node[0] == 'general':
291 value = self.__getParam(None, category, name, 'general') 292 value = self.__getParam(None, category, name, 'general')
292 return self.__getAttr(node[1], attr, value) 293 return self.__getAttr(node[1], attr, value)
293 294
294 assert(node[0] == 'individual') 295 assert node[0] == 'individual'
295 296
296 profile = self.getProfileName(profile_key) 297 profile = self.getProfileName(profile_key)
297 if not profile: 298 if not profile:
298 error(_('Requesting a param for an non-existant profile')) 299 error(_('Requesting a param for an non-existant profile'))
299 raise exceptions.ProfileUnknownError 300 raise exceptions.ProfileUnknownError
317 @param category: category of the parameter 318 @param category: category of the parameter
318 @param attr: name of the attribute (default: "value") 319 @param attr: name of the attribute (default: "value")
319 @param profile: owner of the param (@ALL@ for everyone)""" 320 @param profile: owner of the param (@ALL@ for everyone)"""
320 node = self.__getParamNode(name, category) 321 node = self.__getParamNode(name, category)
321 if not node: 322 if not node:
322 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category}) 323 error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name': name, 'category': category})
323 return None 324 return None
324 325
325 if node[0] == 'general': 326 if node[0] == 'general':
326 value = self.__getParam(None, category, name, 'general') 327 value = self.__getParam(None, category, name, 'general')
327 return defer.succeed(self.__getAttr(node[1], attr, value)) 328 return defer.succeed(self.__getAttr(node[1], attr, value))
328 329
329 assert(node[0] == 'individual') 330 assert node[0] == 'individual'
330 331
331 profile = self.getProfileName(profile_key) 332 profile = self.getProfileName(profile_key)
332 if not profile: 333 if not profile:
333 error(_('Requesting a param for a non-existant profile')) 334 error(_('Requesting a param for a non-existant profile'))
334 return defer.fail() 335 return defer.fail()
351 @param _type: "general" or "individual" 352 @param _type: "general" or "individual"
352 @param cache: temporary cache, to use when profile is not logged 353 @param cache: temporary cache, to use when profile is not logged
353 @return: param value or None if it doesn't exist 354 @return: param value or None if it doesn't exist
354 """ 355 """
355 if _type == 'general': 356 if _type == 'general':
356 if self.params_gen.has_key((category, name)): 357 if (category, name) in self.params_gen:
357 return self.params_gen[(category, name)] 358 return self.params_gen[(category, name)]
358 return None #This general param has the default value 359 return None # This general param has the default value
359 assert (_type == 'individual') 360 assert (_type == 'individual')
360 if self.params.has_key(profile): 361 if profile in self.params:
361 cache = self.params[profile] # if profile is in main cache, we use it, 362 cache = self.params[profile] # if profile is in main cache, we use it,
362 # ignoring the temporary cache 363 # ignoring the temporary cache
363 elif cache == None: #else we use the temporary cache if it exists, or raise an exception 364 elif cache is None: # else we use the temporary cache if it exists, or raise an exception
364 raise exceptions.ProfileNotInCacheError 365 raise exceptions.ProfileNotInCacheError
365 if not cache.has_key((category, name)): 366 if (category, name) not in cache:
366 return None 367 return None
367 return cache[(category, name)] 368 return cache[(category, name)]
368 369
369 def __constructProfileXml(self, profile): 370 def __constructProfileXml(self, profile):
370 """Construct xml for asked profile, filling values when needed 371 """Construct xml for asked profile, filling values when needed
371 /!\ as noticed in doc, don't forget to unlink the minidom.Document 372 /!\ as noticed in doc, don't forget to unlink the minidom.Document
372 @param profile: profile name (not key !) 373 @param profile: profile name (not key !)
373 @return: a deferred that fire a minidom.Document of the profile xml (cf warning above) 374 @return: a deferred that fire a minidom.Document of the profile xml (cf warning above)
374 """ 375 """
375 def constructProfile(ignore,profile_cache): 376
377 def constructProfile(ignore, profile_cache):
376 prof_xml = minidom.parseString('<params/>') 378 prof_xml = minidom.parseString('<params/>')
377 cache = {} 379 cache = {}
378 380
379 for type_node in self.dom.documentElement.childNodes: 381 for type_node in self.dom.documentElement.childNodes:
380 if type_node.nodeName == 'general' or type_node.nodeName == 'individual': #we use all params, general and individual 382 if type_node.nodeName == 'general' or type_node.nodeName == 'individual': # we use all params, general and individual
381 for cat_node in type_node.childNodes: 383 for cat_node in type_node.childNodes:
382 if cat_node.nodeName == 'category': 384 if cat_node.nodeName == 'category':
383 category = cat_node.getAttribute('name') 385 category = cat_node.getAttribute('name')
384 if not cache.has_key(category): 386 if category not in cache:
385 cache[category] = dest_cat = cat_node.cloneNode(True) #we make a copy for the new xml 387 cache[category] = dest_cat = cat_node.cloneNode(True) # we make a copy for the new xml
386 new_node = True 388 new_node = True
387 else: 389 else:
388 dest_cat = cache[category] 390 dest_cat = cache[category]
389 new_node = False #It's not a new node, we will merge information 391 new_node = False # It's not a new node, we will merge information
390 params = cat_node.getElementsByTagName("param") 392 params = cat_node.getElementsByTagName("param")
391 dest_params = {} 393 dest_params = {}
392 for node in dest_cat.childNodes: 394 for node in dest_cat.childNodes:
393 if node.nodeName != "param": 395 if node.nodeName != "param":
394 continue 396 continue
400 if name not in dest_params: 402 if name not in dest_params:
401 dest_params[name] = param_node.cloneNode(True) 403 dest_params[name] = param_node.cloneNode(True)
402 dest_cat.appendChild(dest_params[name]) 404 dest_cat.appendChild(dest_params[name])
403 405
404 profile_value = self.__getParam(profile, category, name, type_node.nodeName, cache=profile_cache) 406 profile_value = self.__getParam(profile, category, name, type_node.nodeName, cache=profile_cache)
405 if profile_value!=None: #there is a value for this profile, we must change the default 407 if profile_value is not None: # there is a value for this profile, we must change the default
406 dest_params[name].setAttribute('value', profile_value) 408 dest_params[name].setAttribute('value', profile_value)
407 if new_node: 409 if new_node:
408 prof_xml.documentElement.appendChild(dest_cat) 410 prof_xml.documentElement.appendChild(dest_cat)
409 return prof_xml 411 return prof_xml
410 412
411 413 if profile in self.params:
412 if self.params.has_key(profile):
413 d = defer.succeed(None) 414 d = defer.succeed(None)
414 profile_cache = self.params[profile] 415 profile_cache = self.params[profile]
415 else: 416 else:
416 #profile is not in cache, we load values in a short time cache 417 #profile is not in cache, we load values in a short time cache
417 profile_cache = {} 418 profile_cache = {}
424 profile = self.getProfileName(profile_key) 425 profile = self.getProfileName(profile_key)
425 if not profile: 426 if not profile:
426 error(_("Asking params for inexistant profile")) 427 error(_("Asking params for inexistant profile"))
427 return "" 428 return ""
428 d = self.getParams(profile) 429 d = self.getParams(profile)
429 return d.addCallback(lambda param_xml:paramsXml2xmlUI(param_xml)) 430 return d.addCallback(lambda param_xml: paramsXml2xmlUI(param_xml))
430 431
431 def getParams(self, profile_key): 432 def getParams(self, profile_key):
432 """Construct xml for asked profile 433 """Construct xml for asked profile
433 Take params xml as skeleton""" 434 Take params xml as skeleton"""
434 profile = self.getProfileName(profile_key) 435 profile = self.getProfileName(profile_key)
462 return "<category />" 463 return "<category />"
463 464
464 d = self.__constructProfileXml(profile) 465 d = self.__constructProfileXml(profile)
465 return d.addCallback(returnCategoryXml) 466 return d.addCallback(returnCategoryXml)
466 467
467 def __getParamNode(self, name, category, _type="@ALL@"): #FIXME: is _type useful ? 468 def __getParamNode(self, name, category, _type="@ALL@"): # FIXME: is _type useful ?
468 """Return a node from the param_xml 469 """Return a node from the param_xml
469 @param name: name of the node 470 @param name: name of the node
470 @param category: category of the node 471 @param category: category of the node
471 @_type: keyword for search: 472 @_type: keyword for search:
472 @ALL@ search everywhere 473 @ALL@ search everywhere
473 @GENERAL@ only search in general type 474 @GENERAL@ only search in general type
474 @INDIVIDUAL@ only search in individual type 475 @INDIVIDUAL@ only search in individual type
475 @return: a tuple with the node type and the the node, or None if not found""" 476 @return: a tuple with the node type and the the node, or None if not found"""
476 477
477 for type_node in self.dom.documentElement.childNodes: 478 for type_node in self.dom.documentElement.childNodes:
478 if ( ((_type == "@ALL@" or _type == "@GENERAL@") and type_node.nodeName == 'general') 479 if (((_type == "@ALL@" or _type == "@GENERAL@") and type_node.nodeName == 'general')
479 or ( (_type == "@ALL@" or _type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ): 480 or ((_type == "@ALL@" or _type == "@INDIVIDUAL@") and type_node.nodeName == 'individual')):
480 for node in type_node.getElementsByTagName('category'): 481 for node in type_node.getElementsByTagName('category'):
481 if node.getAttribute("name") == category: 482 if node.getAttribute("name") == category:
482 params = node.getElementsByTagName("param") 483 params = node.getElementsByTagName("param")
483 for param in params: 484 for param in params:
484 if param.getAttribute("name") == name: 485 if param.getAttribute("name") == name:
485 return (type_node.nodeName, param) 486 return (type_node.nodeName, param)
486 return None 487 return None
487 488
488 def getParamsCategories(self): 489 def getParamsCategories(self):
489 """return the categories availables""" 490 """return the categories availables"""
490 categories=[] 491 categories = []
491 for cat in self.dom.getElementsByTagName("category"): 492 for cat in self.dom.getElementsByTagName("category"):
492 name = cat.getAttribute("name") 493 name = cat.getAttribute("name")
493 if name not in categories: 494 if name not in categories:
494 categories.append(cat.getAttribute("name")) 495 categories.append(cat.getAttribute("name"))
495 return categories 496 return categories
496 497
497 def setParam(self, name, value, category, profile_key='@NONE@'): 498 def setParam(self, name, value, category, profile_key='@NONE@'):
498 """Set a parameter, return None if the parameter is not in param xml""" 499 """Set a parameter, return None if the parameter is not in param xml"""
499 #TODO: use different behaviour depending of the data type (e.g. password encrypted) 500 #TODO: use different behaviour depending of the data type (e.g. password encrypted)
500 if profile_key!="@NONE@": 501 if profile_key != "@NONE@":
501 profile = self.getProfileName(profile_key) 502 profile = self.getProfileName(profile_key)
502 if not profile: 503 if not profile:
503 error(_('Trying to set parameter for an unknown profile')) 504 error(_('Trying to set parameter for an unknown profile'))
504 return #TODO: throw an error 505 return # TODO: throw an error
505 506
506 node = self.__getParamNode(name, category, '@ALL@') 507 node = self.__getParamNode(name, category, '@ALL@')
507 if not node: 508 if not node:
508 error(_('Requesting an unknown parameter (%(category)s/%(name)s)') % {'category':category, 'name':name}) 509 error(_('Requesting an unknown parameter (%(category)s/%(name)s)') % {'category': category, 'name': name})
509 return 510 return
510 511
511 if node[0] == 'general': 512 if node[0] == 'general':
512 self.params_gen[(category, name)] = value 513 self.params_gen[(category, name)] = value
513 self.storage.setGenParam(category, name, value) 514 self.storage.setGenParam(category, name, value)
518 519
519 assert (node[0] == 'individual') 520 assert (node[0] == 'individual')
520 assert (profile_key != "@NONE@") 521 assert (profile_key != "@NONE@")
521 522
522 _type = node[1].getAttribute("type") 523 _type = node[1].getAttribute("type")
523 if _type=="button": 524 if _type == "button":
524 print "clique",node.toxml() 525 print "clique", node.toxml()
525 else: 526 else:
526 if self.host.isConnected(profile): #key can not exists if profile is not connected 527 if self.host.isConnected(profile): # key can not exists if profile is not connected
527 self.params[profile][(category, name)] = value 528 self.params[profile][(category, name)] = value
528 self.host.bridge.paramUpdate(name, value, category, profile) 529 self.host.bridge.paramUpdate(name, value, category, profile)
529 self.storage.setIndParam(category, name, value, profile) 530 self.storage.setIndParam(category, name, value, profile)
530 531
532
531 class Memory(object): 533 class Memory(object):
532 """This class manage all persistent informations""" 534 """This class manage all persistent informations"""
533 535
534 def __init__(self, host): 536 def __init__(self, host):
535 info (_("Memory manager init")) 537 info(_("Memory manager init"))
536 self.initialized = defer.Deferred() 538 self.initialized = defer.Deferred()
537 self.host = host 539 self.host = host
538 self.entitiesCache={} #XXX: keep presence/last resource/other data in cache 540 self.entitiesCache = {} # XXX: keep presence/last resource/other data in cache
539 # /!\ an entity is not necessarily in roster 541 # /!\ an entity is not necessarily in roster
540 self.subscriptions={} 542 self.subscriptions = {}
541 self.server_features={} #used to store discovery's informations 543 self.server_features = {} # used to store discovery's informations
542 self.server_identities={} 544 self.server_identities = {}
543 self.config = self.parseMainConf() 545 self.config = self.parseMainConf()
544 host.set_const('savefile_database', SAVEFILE_DATABASE) 546 host.set_const('savefile_database', SAVEFILE_DATABASE)
545 database_file = os.path.expanduser(self.getConfig('','local_dir')+ 547 database_file = os.path.expanduser(self.getConfig('', 'local_dir') +
546 self.host.get_const('savefile_database')) 548 self.host.get_const('savefile_database'))
547 self.storage = SqliteStorage(database_file) 549 self.storage = SqliteStorage(database_file)
548 PersistentDict.storage = self.storage 550 PersistentDict.storage = self.storage
549 self.params=Params(host, self.storage) 551 self.params = Params(host, self.storage)
550 self.loadFiles() 552 self.loadFiles()
551 d = self.storage.initialized.addCallback(lambda ignore:self.load()) 553 d = self.storage.initialized.addCallback(lambda ignore: self.load())
552 self.memory_data = PersistentDict("memory") 554 self.memory_data = PersistentDict("memory")
553 d.addCallback(lambda ignore: self.memory_data.load()) 555 d.addCallback(lambda ignore: self.memory_data.load())
554 d.chainDeferred(self.initialized) 556 d.chainDeferred(self.initialized)
555 557
556 def parseMainConf(self): 558 def parseMainConf(self):
557 """look for main .ini configuration file, and parse it""" 559 """look for main .ini configuration file, and parse it"""
558 _config = SafeConfigParser(defaults=default_config) 560 _config = SafeConfigParser(defaults=default_config)
559 try: 561 try:
560 _config.read(map(os.path.expanduser, ['/etc/sat.conf', '~/sat.conf', '~/.sat.conf', 'sat.conf', '.sat.conf'])) 562 _config.read(map(os.path.expanduser, ['/etc/sat.conf', '~/sat.conf', '~/.sat.conf', 'sat.conf', '.sat.conf']))
561 except: 563 except:
562 error (_("Can't read main config !")) 564 error(_("Can't read main config !"))
563 565
564 return _config 566 return _config
565 567
566 def getConfig(self, section, name): 568 def getConfig(self, section, name):
567 """Get the main configuration option 569 """Get the main configuration option
568 @param section: section of the config file (None or '' for DEFAULT) 570 @param section: section of the config file (None or '' for DEFAULT)
569 @param name: name of the option 571 @param name: name of the option
570 """ 572 """
571 if not section: 573 if not section:
572 section='DEFAULT' 574 section = 'DEFAULT'
573 try: 575 try:
574 _value = self.config.get(section, name) 576 _value = self.config.get(section, name)
575 except (NoOptionError, NoSectionError): 577 except (NoOptionError, NoSectionError):
576 _value = '' 578 _value = ''
577 579
578 return os.path.expanduser(_value) if name.endswith('_path') or name.endswith('_dir') else _value 580 return os.path.expanduser(_value) if name.endswith('_path') or name.endswith('_dir') else _value
579 581
580
581 def loadFiles(self): 582 def loadFiles(self):
582 """Load parameters and all memory things from file/db""" 583 """Load parameters and all memory things from file/db"""
583 param_file_xml = os.path.expanduser(self.getConfig('','local_dir')+ 584 param_file_xml = os.path.expanduser(self.getConfig('', 'local_dir') +
584 self.host.get_const('savefile_param_xml')) 585 self.host.get_const('savefile_param_xml'))
585 586
586 #parameters template 587 #parameters template
587 if os.path.exists(param_file_xml): 588 if os.path.exists(param_file_xml):
588 try: 589 try:
589 self.params.load_xml(param_file_xml) 590 self.params.load_xml(param_file_xml)
590 debug(_("params template loaded")) 591 debug(_("params template loaded"))
591 except Exception as e: 592 except Exception as e:
592 error (_("Can't load params template: %s") % (e,)) 593 error(_("Can't load params template: %s") % (e, ))
593 self.params.load_default_params() 594 self.params.load_default_params()
594 else: 595 else:
595 info (_("No params template, using default template")) 596 info(_("No params template, using default template"))
596 self.params.load_default_params() 597 self.params.load_default_params()
597
598 598
599 def load(self): 599 def load(self):
600 """Load parameters and all memory things from db""" 600 """Load parameters and all memory things from db"""
601 #parameters data 601 #parameters data
602 return self.params.loadGenParams() 602 return self.params.loadGenParams()
620 try: 620 try:
621 del self.entitiesCache[profile] 621 del self.entitiesCache[profile]
622 except KeyError: 622 except KeyError:
623 error(_("Trying to purge roster status cache for a profile not in memory: [%s]") % profile) 623 error(_("Trying to purge roster status cache for a profile not in memory: [%s]") % profile)
624 624
625
626 def save(self): 625 def save(self):
627 """Save parameters and all memory things to file/db""" 626 """Save parameters and all memory things to file/db"""
628 #TODO: need to encrypt files (at least passwords !) and set permissions 627 #TODO: need to encrypt files (at least passwords !) and set permissions
629 param_file_xml = os.path.expanduser(self.getConfig('','local_dir')+ 628 param_file_xml = os.path.expanduser(self.getConfig('', 'local_dir') +
630 self.host.get_const('savefile_param_xml')) 629 self.host.get_const('savefile_param_xml'))
631 630
632 self.params.save_xml(param_file_xml) 631 self.params.save_xml(param_file_xml)
633 debug(_("params saved")) 632 debug(_("params saved"))
634 633
635 def getProfilesList(self): 634 def getProfilesList(self):
636 return self.storage.getProfilesList() 635 return self.storage.getProfilesList()
637
638 636
639 def getProfileName(self, profile_key): 637 def getProfileName(self, profile_key):
640 """Return name of profile from keyword 638 """Return name of profile from keyword
641 @param profile_key: can be the profile name or a keywork (like @DEFAULT@) 639 @param profile_key: can be the profile name or a keywork (like @DEFAULT@)
642 @return: profile name or None if it doesn't exist""" 640 @return: profile name or None if it doesn't exist"""
658 """Delete an existing profile 656 """Delete an existing profile
659 @param name: Name of the profile""" 657 @param name: Name of the profile"""
660 return self.params.deleteProfile(name) 658 return self.params.deleteProfile(name)
661 659
662 def addToHistory(self, from_jid, to_jid, message, _type='chat', timestamp=None, profile="@NONE@"): 660 def addToHistory(self, from_jid, to_jid, message, _type='chat', timestamp=None, profile="@NONE@"):
663 assert(profile!="@NONE@") 661 assert profile != "@NONE@"
664 return self.storage.addToHistory(from_jid, to_jid, message, _type, timestamp, profile) 662 return self.storage.addToHistory(from_jid, to_jid, message, _type, timestamp, profile)
665 663
666 def getHistory(self, from_jid, to_jid, limit=0, between=True, profile="@NONE@"): 664 def getHistory(self, from_jid, to_jid, limit=0, between=True, profile="@NONE@"):
667 assert(profile != "@NONE@") 665 assert profile != "@NONE@"
668 return self.storage.getHistory(jid.JID(from_jid), jid.JID(to_jid), limit, between, profile) 666 return self.storage.getHistory(jid.JID(from_jid), jid.JID(to_jid), limit, between, profile)
669 667
670 def addServerFeature(self, feature, profile): 668 def addServerFeature(self, feature, profile):
671 """Add a feature discovered from server 669 """Add a feature discovered from server
672 @param feature: string of the feature 670 @param feature: string of the feature
673 @param profile: which profile is using this server ?""" 671 @param profile: which profile is using this server ?"""
674 if not self.server_features.has_key(profile): 672 if profile not in self.server_features:
675 self.server_features[profile] = [] 673 self.server_features[profile] = []
676 self.server_features[profile].append(feature) 674 self.server_features[profile].append(feature)
677 675
678 def addServerIdentity(self, category, _type, entity, profile): 676 def addServerIdentity(self, category, _type, entity, profile):
679 """Add an identity discovered from server 677 """Add an identity discovered from server
680 @param feature: string of the feature 678 @param feature: string of the feature
681 @param profile: which profile is using this server ?""" 679 @param profile: which profile is using this server ?"""
682 if not profile in self.server_identities: 680 if not profile in self.server_identities:
683 self.server_identities[profile] = {} 681 self.server_identities[profile] = {}
684 if not self.server_identities[profile].has_key((category, _type)): 682 if (category, _type) not in self.server_identities[profile]:
685 self.server_identities[profile][(category, _type)]=set() 683 self.server_identities[profile][(category, _type)] = set()
686 self.server_identities[profile][(category, _type)].add(entity) 684 self.server_identities[profile][(category, _type)].add(entity)
687 685
688 def getServerServiceEntities(self, category, _type, profile): 686 def getServerServiceEntities(self, category, _type, profile):
689 """Return all available entities for a service""" 687 """Return all available entities for a service"""
690 if profile in self.server_identities: 688 if profile in self.server_identities:
693 return None 691 return None
694 692
695 def getServerServiceEntity(self, category, _type, profile): 693 def getServerServiceEntity(self, category, _type, profile):
696 """Helper method to get first available entity for a service""" 694 """Helper method to get first available entity for a service"""
697 entities = self.getServerServiceEntities(category, _type, profile) 695 entities = self.getServerServiceEntities(category, _type, profile)
698 if entities == None: 696 if entities is None:
699 warning(_("Entities (%(category)s/%(type)s) not available, maybe they haven't been asked to server yet ?") % {"category":category, 697 warning(_("Entities (%(category)s/%(type)s) not available, maybe they haven't been asked to server yet ?") % {"category": category,
700 "type":_type}) 698 "type": _type})
701 return None 699 return None
702 else: 700 else:
703 return list(entities)[0] if entities else None 701 return list(entities)[0] if entities else None
704 702
705 def hasServerFeature(self, feature, profile_key): 703 def hasServerFeature(self, feature, profile_key):
706 """Tell if the server of the profile has the required feature""" 704 """Tell if the server of the profile has the required feature"""
707 profile = self.getProfileName(profile_key) 705 profile = self.getProfileName(profile_key)
708 if not profile: 706 if not profile:
709 error (_('Trying find server feature for a non-existant profile')) 707 error(_('Trying find server feature for a non-existant profile'))
710 return 708 return
711 assert(self.server_features.has_key(profile)) 709 assert profile in self.server_features
712 return feature in self.server_features[profile] 710 return feature in self.server_features[profile]
713 711
714 def getLastResource(self, contact, profile_key): 712 def getLastResource(self, contact, profile_key):
715 """Return the last resource used by a contact 713 """Return the last resource used by a contact
716 @param contact: contact jid (unicode) 714 @param contact: contact jid (unicode)
736 entities_presence = {} 734 entities_presence = {}
737 for entity in self.entitiesCache[profile]: 735 for entity in self.entitiesCache[profile]:
738 if "presence" in self.entitiesCache[profile][entity]: 736 if "presence" in self.entitiesCache[profile][entity]:
739 entities_presence[entity] = self.entitiesCache[profile][entity]["presence"] 737 entities_presence[entity] = self.entitiesCache[profile][entity]["presence"]
740 738
741 debug ("Memory getPresenceStatus (%s)", entities_presence) 739 debug("Memory getPresenceStatus (%s)", entities_presence)
742 return entities_presence 740 return entities_presence
743 741
744 def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key): 742 def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key):
745 """Change the presence status of an entity""" 743 """Change the presence status of an entity"""
746 profile = self.getProfileName(profile_key) 744 profile = self.getProfileName(profile_key)
747 if not profile: 745 if not profile:
748 error(_('Trying to add presence status to a non-existant profile')) 746 error(_('Trying to add presence status to a non-existant profile'))
749 return 747 return
750 entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(),{}) 748 entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(), {})
751 resource = jid.parse(entity_jid.full())[2] or '' 749 resource = jid.parse(entity_jid.full())[2] or ''
752 if resource: 750 if resource:
753 entity_data["last_resource"] = resource 751 entity_data["last_resource"] = resource
754 if not "last_resource" in entity_data: 752 if not "last_resource" in entity_data:
755 entity_data["last_resource"] = '' 753 entity_data["last_resource"] = ''
756 754
757 entity_data.setdefault("presence",{})[resource] = (show, priority, statuses) 755 entity_data.setdefault("presence", {})[resource] = (show, priority, statuses)
758 756
759 def updateEntityData(self, entity_jid, key, value, profile_key): 757 def updateEntityData(self, entity_jid, key, value, profile_key):
760 """Set a misc data for an entity 758 """Set a misc data for an entity
761 @param entity_jid: JID of the entity 759 @param entity_jid: JID of the entity
762 @param key: key to set (eg: "type") 760 @param key: key to set (eg: "type")
766 profile = self.getProfileName(profile_key) 764 profile = self.getProfileName(profile_key)
767 if not profile: 765 if not profile:
768 raise exceptions.UnknownProfileError(_('Trying to get entity data for a non-existant profile')) 766 raise exceptions.UnknownProfileError(_('Trying to get entity data for a non-existant profile'))
769 if not profile in self.entitiesCache: 767 if not profile in self.entitiesCache:
770 raise exceptions.ProfileNotInCacheError 768 raise exceptions.ProfileNotInCacheError
771 entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(),{}) 769 entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(), {})
772 entity_data[key] = value 770 entity_data[key] = value
773 if isinstance(value,basestring): 771 if isinstance(value, basestring):
774 self.host.bridge.entityDataUpdated(entity_jid.userhost(), key, value, profile) 772 self.host.bridge.entityDataUpdated(entity_jid.userhost(), key, value, profile)
775 773
776 def getEntityData(self, entity_jid, keys_list, profile_key): 774 def getEntityData(self, entity_jid, keys_list, profile_key):
777 """Get a list of cached values for entity 775 """Get a list of cached values for entity
778 @param entity_jid: JID of the entity 776 @param entity_jid: JID of the entity
808 try: 806 try:
809 del self.entitiesCache[profile][entity_jid.userhost()] 807 del self.entitiesCache[profile][entity_jid.userhost()]
810 except KeyError: 808 except KeyError:
811 pass 809 pass
812 810
813
814
815 def addWaitingSub(self, _type, entity_jid, profile_key): 811 def addWaitingSub(self, _type, entity_jid, profile_key):
816 """Called when a subcription request is received""" 812 """Called when a subcription request is received"""
817 profile = self.getProfileName(profile_key) 813 profile = self.getProfileName(profile_key)
818 assert(profile) 814 assert profile
819 if not self.subscriptions.has_key(profile): 815 if profile not in self.subscriptions:
820 self.subscriptions[profile] = {} 816 self.subscriptions[profile] = {}
821 self.subscriptions[profile][entity_jid] = _type 817 self.subscriptions[profile][entity_jid] = _type
822 818
823 def delWaitingSub(self, entity_jid, profile_key): 819 def delWaitingSub(self, entity_jid, profile_key):
824 """Called when a subcription request is finished""" 820 """Called when a subcription request is finished"""
825 profile = self.getProfileName(profile_key) 821 profile = self.getProfileName(profile_key)
826 assert(profile) 822 assert profile
827 if self.subscriptions.has_key(profile) and self.subscriptions[profile].has_key(entity_jid): 823 if profile in self.subscriptions and entity_jid in self.subscriptions[profile]:
828 del self.subscriptions[profile][entity_jid] 824 del self.subscriptions[profile][entity_jid]
829 825
830 def getWaitingSub(self, profile_key): 826 def getWaitingSub(self, profile_key):
831 """Called to get a list of currently waiting subscription requests""" 827 """Called to get a list of currently waiting subscription requests"""
832 profile = self.getProfileName(profile_key) 828 profile = self.getProfileName(profile_key)
833 if not profile: 829 if not profile:
834 error(_('Asking waiting subscriptions for a non-existant profile')) 830 error(_('Asking waiting subscriptions for a non-existant profile'))
835 return {} 831 return {}
836 if not self.subscriptions.has_key(profile): 832 if profile not in self.subscriptions:
837 return {} 833 return {}
838 834
839 return self.subscriptions[profile] 835 return self.subscriptions[profile]
840 836
841 def getStringParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): 837 def getStringParamA(self, name, category, attr="value", profile_key='@DEFAULT@'):