comparison src/memory/memory.py @ 484:23cbdf0a0777

core: presence status + last resource refactored and kept in entitiesCache in memory.py, profile cache is purged on disconnection
author Goffi <goffi@goffi.org>
date Wed, 15 Aug 2012 15:50:46 +0200
parents 2a072735e459
children 0d9908ac775e
comparison
equal deleted inserted replaced
482:e0d1eed4a46b 484:23cbdf0a0777
297 except ProfileNotInCacheError: 297 except ProfileNotInCacheError:
298 #We have to ask data to the storage manager 298 #We have to ask data to the storage manager
299 d = self.storage.getIndParam(category, name, profile) 299 d = self.storage.getIndParam(category, name, profile)
300 return d.addCallback(lambda value: value if value!=None else default) 300 return d.addCallback(lambda value: value if value!=None else default)
301 301
302 def __getParam(self, profile, category, name, type='individual', cache=None): 302 def __getParam(self, profile, category, name, _type='individual', cache=None):
303 """Return the param, or None if it doesn't exist 303 """Return the param, or None if it doesn't exist
304 @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@) 304 @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@)
305 @param category: param category 305 @param category: param category
306 @param name: param name 306 @param name: param name
307 @param type: "general" or "individual" 307 @param type: "general" or "individual"
308 @param cache: temporary cache, to use when profile is not logged 308 @param cache: temporary cache, to use when profile is not logged
309 @return: param value or None if it doesn't exist 309 @return: param value or None if it doesn't exist
310 """ 310 """
311 if type == 'general': 311 if _type == 'general':
312 if self.params_gen.has_key((category, name)): 312 if self.params_gen.has_key((category, name)):
313 return self.params_gen[(category, name)] 313 return self.params_gen[(category, name)]
314 return None #This general param has the default value 314 return None #This general param has the default value
315 assert (type == 'individual') 315 assert (_type == 'individual')
316 if self.params.has_key(profile): 316 if self.params.has_key(profile):
317 cache = self.params[profile] # if profile is in main cache, we use it, 317 cache = self.params[profile] # if profile is in main cache, we use it,
318 # ignoring the temporary cache 318 # ignoring the temporary cache
319 elif cache == None: #else we use the temporary cache if it exists, or raise an exception 319 elif cache == None: #else we use the temporary cache if it exists, or raise an exception
320 raise ProfileNotInCacheError 320 raise ProfileNotInCacheError
418 return "<category />" 418 return "<category />"
419 419
420 d = self.__constructProfileXml(profile) 420 d = self.__constructProfileXml(profile)
421 return d.addCallback(returnCategoryXml) 421 return d.addCallback(returnCategoryXml)
422 422
423 def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ? 423 def __getParamNode(self, name, category, _type="@ALL@"): #FIXME: is _type useful ?
424 """Return a node from the param_xml 424 """Return a node from the param_xml
425 @param name: name of the node 425 @param name: name of the node
426 @param category: category of the node 426 @param category: category of the node
427 @type: keyword for search: 427 @_type: keyword for search:
428 @ALL@ search everywhere 428 @ALL@ search everywhere
429 @GENERAL@ only search in general type 429 @GENERAL@ only search in general type
430 @INDIVIDUAL@ only search in individual type 430 @INDIVIDUAL@ only search in individual type
431 @return: a tuple with the node type and the the node, or None if not found""" 431 @return: a tuple with the node type and the the node, or None if not found"""
432 432
433 for type_node in self.dom.documentElement.childNodes: 433 for type_node in self.dom.documentElement.childNodes:
434 if ( ((type == "@ALL@" or type == "@GENERAL@") and type_node.nodeName == 'general') 434 if ( ((_type == "@ALL@" or _type == "@GENERAL@") and type_node.nodeName == 'general')
435 or ( (type == "@ALL@" or type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ): 435 or ( (_type == "@ALL@" or _type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ):
436 for node in type_node.getElementsByTagName('category'): 436 for node in type_node.getElementsByTagName('category'):
437 if node.getAttribute("name") == category: 437 if node.getAttribute("name") == category:
438 params = node.getElementsByTagName("param") 438 params = node.getElementsByTagName("param")
439 for param in params: 439 for param in params:
440 if param.getAttribute("name") == name: 440 if param.getAttribute("name") == name:
473 return 473 return
474 474
475 assert (node[0] == 'individual') 475 assert (node[0] == 'individual')
476 assert (profile_key != "@NONE@") 476 assert (profile_key != "@NONE@")
477 477
478 type = node[1].getAttribute("type") 478 _type = node[1].getAttribute("type")
479 if type=="button": 479 if _type=="button":
480 print "clique",node.toxml() 480 print "clique",node.toxml()
481 else: 481 else:
482 if self.host.isConnected(profile): #key can not exists if profile is not connected 482 if self.host.isConnected(profile): #key can not exists if profile is not connected
483 self.params[profile][(category, name)] = value 483 self.params[profile][(category, name)] = value
484 self.host.bridge.paramUpdate(name, value, category, profile) 484 self.host.bridge.paramUpdate(name, value, category, profile)
489 489
490 def __init__(self, host): 490 def __init__(self, host):
491 info (_("Memory manager init")) 491 info (_("Memory manager init"))
492 self.initialized = defer.Deferred() 492 self.initialized = defer.Deferred()
493 self.host = host 493 self.host = host
494 self.presenceStatus={} 494 self.entitiesCache={} #XXX: keep presence/last resource/other data in cache
495 self.lastResource={} #tmp, will be refactored with bdd integration 495 # /!\ an entity is not necessarily in roster
496 self.subscriptions={} 496 self.subscriptions={}
497 self.server_features={} #used to store discovery's informations 497 self.server_features={} #used to store discovery's informations
498 self.server_identities={} 498 self.server_identities={}
499 self.config = self.parseMainConf() 499 self.config = self.parseMainConf()
500 host.set_const('savefile_database', SAVEFILE_DATABASE) 500 host.set_const('savefile_database', SAVEFILE_DATABASE)
560 def loadIndividualParams(self, profile): 560 def loadIndividualParams(self, profile):
561 """Load individual parameters for a profile 561 """Load individual parameters for a profile
562 @param profile: %(doc_profile)s""" 562 @param profile: %(doc_profile)s"""
563 return self.params.loadIndParams(profile) 563 return self.params.loadIndParams(profile)
564 564
565 def purgeProfile(self, profile): 565 def startProfileSession(self, profile):
566 """"Iniatialise session for a profile
567 @param profile: %(doc_profile)s"""
568 info(_("[%s] Profile session started" % profile))
569 self.entitiesCache[profile] = {}
570
571 def purgeProfileSession(self, profile):
566 """Delete cache of data of profile 572 """Delete cache of data of profile
567 @param profile: %(doc_profile)s""" 573 @param profile: %(doc_profile)s"""
574 info(_("[%s] Profile session purge" % profile))
568 self.params.purgeProfile(profile) 575 self.params.purgeProfile(profile)
576 try:
577 del self.entitiesCache[profile]
578 except KeyError:
579 error(_("Trying to purge roster status cache for a profile not in memory: [%s]") % profile)
580
569 581
570 def save(self): 582 def save(self):
571 """Save parameters and all memory things to file/db""" 583 """Save parameters and all memory things to file/db"""
572 #TODO: need to encrypt files (at least passwords !) and set permissions 584 #TODO: need to encrypt files (at least passwords !) and set permissions
573 param_file_xml = os.path.expanduser(self.getConfig('','local_dir')+ 585 param_file_xml = os.path.expanduser(self.getConfig('','local_dir')+
616 @param profile: which profile is using this server ?""" 628 @param profile: which profile is using this server ?"""
617 if not self.server_features.has_key(profile): 629 if not self.server_features.has_key(profile):
618 self.server_features[profile] = [] 630 self.server_features[profile] = []
619 self.server_features[profile].append(feature) 631 self.server_features[profile].append(feature)
620 632
621 def addServerIdentity(self, category, type, entity, profile): 633 def addServerIdentity(self, category, _type, entity, profile):
622 """Add an identity discovered from server 634 """Add an identity discovered from server
623 @param feature: string of the feature 635 @param feature: string of the feature
624 @param profile: which profile is using this server ?""" 636 @param profile: which profile is using this server ?"""
625 if not self.server_identities.has_key(profile): 637 if not self.server_identities.has_key(profile):
626 self.server_identities[profile] = {} 638 self.server_identities[profile] = {}
627 if not self.server_identities[profile].has_key((category, type)): 639 if not self.server_identities[profile].has_key((category, _type)):
628 self.server_identities[profile][(category, type)]=set() 640 self.server_identities[profile][(category, _type)]=set()
629 self.server_identities[profile][(category, type)].add(entity) 641 self.server_identities[profile][(category, _type)].add(entity)
630 642
631 def getServerServiceEntities(self, category, type, profile): 643 def getServerServiceEntities(self, category, _type, profile):
632 """Return all available entities for a service""" 644 """Return all available entities for a service"""
633 if self.server_identities.has_key(profile): 645 if self.server_identities.has_key(profile):
634 return self.server_identities[profile].get((category, type), set()) 646 return self.server_identities[profile].get((category, _type), set())
635 else: 647 else:
636 return None 648 return None
637 649
638 def getServerServiceEntity(self, category, type, profile): 650 def getServerServiceEntity(self, category, _type, profile):
639 """Helper method to get first available entity for a service""" 651 """Helper method to get first available entity for a service"""
640 entities = self.getServerServiceEntities(category, type, profile) 652 entities = self.getServerServiceEntities(category, _type, profile)
641 if entities == None: 653 if entities == None:
642 warning(_("Entities (%(category)s/%(type)s) not available, maybe they haven't been asked to server yet ?") % {"category":category, 654 warning(_("Entities (%(category)s/%(type)s) not available, maybe they haven't been asked to server yet ?") % {"category":category,
643 "type":type}) 655 "type":_type})
644 return None 656 return None
645 else: 657 else:
646 return list(entities)[0] if entities else None 658 return list(entities)[0] if entities else None
647 659
648 def hasServerFeature(self, feature, profile_key): 660 def hasServerFeature(self, feature, profile_key):
657 def getLastResource(self, contact, profile_key): 669 def getLastResource(self, contact, profile_key):
658 """Return the last resource used by a contact 670 """Return the last resource used by a contact
659 @param contact: contact jid (unicode) 671 @param contact: contact jid (unicode)
660 @param profile_key: %(doc_profile_key)s""" 672 @param profile_key: %(doc_profile_key)s"""
661 profile = self.getProfileName(profile_key) 673 profile = self.getProfileName(profile_key)
662 if not profile: 674 if not profile or not self.host.isConnected(profile):
663 error(_('Asking contacts for a non-existant profile')) 675 error(_('Asking contacts for a non-existant or not connected profile'))
676 return ""
677 entity = jid.JID(contact).userhost()
678 if not entity in self.entitiesCache[profile]:
679 info(_("Entity not in cache"))
664 return "" 680 return ""
665 try: 681 try:
666 return self.lastResource[profile][jid.JID(contact).userhost()] 682 return self.entitiesCache[profile][entity]["last_resource"]
667 except: 683 except KeyError:
668 return "" 684 return ""
669 685
670 def addPresenceStatus(self, contact_jid, show, priority, statuses, profile_key): 686 def setPresenceStatus(self, contact_jid, show, priority, statuses, profile_key):
687 """Change the presence status of an entity"""
671 profile = self.getProfileName(profile_key) 688 profile = self.getProfileName(profile_key)
672 if not profile: 689 if not profile:
673 error(_('Trying to add presence status to a non-existant profile')) 690 error(_('Trying to add presence status to a non-existant profile'))
674 return 691 return
675 if not self.presenceStatus.has_key(profile): 692 entity_data = self.entitiesCache[profile].setdefault(contact_jid.userhost(),{})
676 self.presenceStatus[profile] = {}
677 if not self.lastResource.has_key(profile):
678 self.lastResource[profile] = {}
679 if not self.presenceStatus[profile].has_key(contact_jid.userhost()):
680 self.presenceStatus[profile][contact_jid.userhost()] = {}
681 resource = jid.parse(contact_jid.full())[2] or '' 693 resource = jid.parse(contact_jid.full())[2] or ''
682 if resource: 694 if resource:
683 self.lastResource[profile][contact_jid.userhost()] = resource 695 entity_data["last_resource"] = resource
684 696 if not "last_resource" in entity_data:
685 self.presenceStatus[profile][contact_jid.userhost()][resource] = (show, priority, statuses) 697 entity_data["last_resource"] = ''
686 698
687 def addWaitingSub(self, type, contact_jid, profile_key): 699 entity_data.setdefault("presence",{})[resource] = (show, priority, statuses)
700
701 def addWaitingSub(self, _type, contact_jid, profile_key):
688 """Called when a subcription request is received""" 702 """Called when a subcription request is received"""
689 profile = self.getProfileName(profile_key) 703 profile = self.getProfileName(profile_key)
690 assert(profile) 704 assert(profile)
691 if not self.subscriptions.has_key(profile): 705 if not self.subscriptions.has_key(profile):
692 self.subscriptions[profile] = {} 706 self.subscriptions[profile] = {}
693 self.subscriptions[profile][contact_jid] = type 707 self.subscriptions[profile][contact_jid] = _type
694 708
695 def delWaitingSub(self, contact_jid, profile_key): 709 def delWaitingSub(self, contact_jid, profile_key):
696 """Called when a subcription request is finished""" 710 """Called when a subcription request is finished"""
697 profile = self.getProfileName(profile_key) 711 profile = self.getProfileName(profile_key)
698 assert(profile) 712 assert(profile)
713 def getPresenceStatus(self, profile_key): 727 def getPresenceStatus(self, profile_key):
714 profile = self.getProfileName(profile_key) 728 profile = self.getProfileName(profile_key)
715 if not profile: 729 if not profile:
716 error(_('Asking contacts for a non-existant profile')) 730 error(_('Asking contacts for a non-existant profile'))
717 return {} 731 return {}
718 if not self.presenceStatus.has_key(profile): 732 entities_presence = {}
719 self.presenceStatus[profile] = {} 733 for entity in self.entitiesCache[profile]:
720 debug ("Memory getPresenceStatus (%s)", self.presenceStatus[profile]) 734 # if entity exists, "presence" key must exist
721 return self.presenceStatus[profile] 735 entities_presence[entity] = self.entitiesCache[profile][entity]["presence"]
736
737 debug ("Memory getPresenceStatus (%s)", entities_presence)
738 return entities_presence
722 739
723 def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): 740 def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'):
724 return self.params.getParamA(name, category, attr, profile_key) 741 return self.params.getParamA(name, category, attr, profile_key)
725 742
726 def asyncGetParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): 743 def asyncGetParamA(self, name, category, attr="value", profile_key='@DEFAULT@'):