# HG changeset patch # User Goffi # Date 1423514445 -3600 # Node ID 3012c2f15dae1a5b165cc7e4fb40946c950a3631 # Parent 781ee3539252eff2484f563d2613b07edb46c5e2# Parent 9e904f8a094e466c6278d723011691ebac56b4cc merges souliane commits diff -r 9e904f8a094e -r 3012c2f15dae frontends/src/bridge/DBus.py --- a/frontends/src/bridge/DBus.py Mon Feb 09 09:19:30 2015 +0100 +++ b/frontends/src/bridge/DBus.py Mon Feb 09 21:40:45 2015 +0100 @@ -265,6 +265,20 @@ kwargs['error_handler'] = error_handler return self.db_core_iface.getContactsFromGroup(group, profile_key, **kwargs) + def getEntitiesData(self, jids, keys, profile, callback=None, errback=None): + if callback is None: + error_handler = None + else: + if errback is None: + errback = log.error + error_handler = lambda err:errback(dbus_to_bridge_exception(err)) + kwargs={} + if callback is not None: + kwargs['timeout'] = const_TIMEOUT + kwargs['reply_handler'] = callback + kwargs['error_handler'] = error_handler + return self.db_core_iface.getEntitiesData(jids, keys, profile, **kwargs) + def getEntityData(self, jid, keys, profile, callback=None, errback=None): if callback is None: error_handler = None diff -r 9e904f8a094e -r 3012c2f15dae frontends/src/quick_frontend/constants.py --- a/frontends/src/quick_frontend/constants.py Mon Feb 09 09:19:30 2015 +0100 +++ b/frontends/src/quick_frontend/constants.py Mon Feb 09 21:40:45 2015 +0100 @@ -30,3 +30,5 @@ CONTACT_SPECIAL_GROUP = 'group' # group chat special entity CONTACT_SPECIAL_ALLOWED = (CONTACT_SPECIAL_GROUP,) # set of allowed values for special flag CONTACT_DATA_FORBIDDEN = {CONTACT_GROUPS, CONTACT_RESOURCES, CONTACT_MAIN_RESOURCE} # set of forbidden names for contact data + + LISTENERS = {'avatar'} diff -r 9e904f8a094e -r 3012c2f15dae frontends/src/quick_frontend/quick_app.py --- a/frontends/src/quick_frontend/quick_app.py Mon Feb 09 09:19:30 2015 +0100 +++ b/frontends/src/quick_frontend/quick_app.py Mon Feb 09 21:40:45 2015 +0100 @@ -33,6 +33,7 @@ """Class managing all data relative to one profile, and plugging in mechanism""" host = None bridge = None + cache_keys_to_get = ['avatar'] def __init__(self, profile): self.profile = profile @@ -65,9 +66,21 @@ def _plug_profile_afterconnect(self): # Profile can be connected or not + # we get cached data + self.host.bridge.getEntitiesData([], ProfileManager.cache_keys_to_get, profile=self.profile, callback=self._plug_profile_gotCachedValues, errback=self._plug_profile_failedCachedValues) + + def _plug_profile_failedCachedValues(self, failure): + log.error("Couldn't get cached values: {}".format(failure)) + self._plug_profile_gotCachedValues({}) + + def _plug_profile_gotCachedValues(self, cached_values): # TODO: watched plugin contact_list = self.host.addContactList(self.profile) + for entity, data in cached_values.iteritems(): + for key, value in data.iteritems(): + self.host.contact_lists[self.profile].setCache(jid.JID(entity), key, value) + if not self.bridge.isConnected(self.profile): self.host.setStatusOnline(False, profile=self.profile) else: @@ -182,6 +195,9 @@ # widgets self.selected_widget = None # widget currently selected (must be filled by frontend) + # listeners + self._listeners = {} # key: listerner type ("avatar", "selected", etc), value: list of callbacks + ## bridge ## try: self.bridge = create_bridge() @@ -271,6 +287,42 @@ handler(*args, **kwargs) self.bridge.register(functionName, signalReceived, iface) + def addListerner(self, type_, callback): + """Add a listerner for an event + + /!\ don't forget to remove listener when not used anymore (e.g. if you delete a widget) + @param type_: type of event, can be: + - avatar: called when avatar data is updated + args: (entity, avatar file, profile) + @param callback: method to call on event + """ + assert type_ in C.LISTENERS + self._listeners.setdefault(type_, []).append(callback) + + def removeListener(self, type_, callback): + """Remove a callback from listeners + + @param type_: same as for [addListerner] + @param callback: callback to remove + """ + assert type_ in C.LISTENERS + self._listeners[type_].remove(callback) + + def callListeners(self, type_, *args): + """Call all methods which listen of type_ event + + @param type_: same as for [addListerner] + @param *args: arguments sent to callback + """ + assert type_ in C.LISTENERS + try: + listeners = self._listeners[type_] + except KeyError: + pass + else: + for listener in listeners: + listener(*args) + def check_profile(self, profile): """Tell if the profile is currently followed by the application""" return profile in self.profiles @@ -598,6 +650,7 @@ if entity in self.contact_lists[profile]: def gotFilename(filename): self.contact_lists[profile].setCache(entity, 'avatar', filename) + self.callListeners('avatar', entity, filename, profile) self.bridge.getAvatarFile(value, callback=gotFilename) def askConfirmationHandler(self, confirm_id, confirm_type, data, profile): diff -r 9e904f8a094e -r 3012c2f15dae src/bridge/DBus.py --- a/src/bridge/DBus.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/bridge/DBus.py Mon Feb 09 21:40:45 2015 +0100 @@ -271,6 +271,12 @@ return self._callback("getContactsFromGroup", unicode(group), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, + in_signature='asass', out_signature='a{sa{ss}}', + async_callbacks=None) + def getEntitiesData(self, jids, keys, profile): + return self._callback("getEntitiesData", jids, keys, unicode(profile)) + + @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='sass', out_signature='a{ss}', async_callbacks=None) def getEntityData(self, jid, keys, profile): diff -r 9e904f8a094e -r 3012c2f15dae src/bridge/bridge_constructor/bridge_template.ini --- a/src/bridge/bridge_constructor/bridge_template.ini Mon Feb 09 09:19:30 2015 +0100 +++ b/src/bridge/bridge_constructor/bridge_template.ini Mon Feb 09 21:40:45 2015 +0100 @@ -184,12 +184,24 @@ category=core sig_in=sass sig_out=a{ss} -doc=Get data for an entity +doc=Get data in cache for an entity doc_param_0=jid: entity's bare jid doc_param_1=keys: list of keys to get doc_param_2=%(doc_profile)s doc_return=dictionary of asked key, - if key doesn't exist, the resulting dictionary will neither have the key + if key doesn't exist, the resulting dictionary will not have the key + +[getEntitiesData] +type=method +category=core +sig_in=asass +sig_out=a{sa{ss}} +doc=Get data in cache for several entities at once +doc_param_0=jids: list of entities bare jid, or empty list to have all jids in cache +doc_param_1=keys: list of keys to get +doc_param_2=%(doc_profile)s +doc_return=dictionary with jids as keys and dictionary of asked key as values + if key doesn't exist for a jid, the resulting dictionary will not have it [asyncCreateProfile] async= diff -r 9e904f8a094e -r 3012c2f15dae src/core/sat_main.py --- a/src/core/sat_main.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/core/sat_main.py Mon Feb 09 21:40:45 2015 +0100 @@ -83,7 +83,8 @@ self.bridge.register("getVersion", lambda: C.APP_VERSION) self.bridge.register("getProfileName", self.memory.getProfileName) self.bridge.register("getProfilesList", self.memory.getProfilesList) - self.bridge.register("getEntityData", lambda _jid, keys, profile: self.memory.getEntityData(jid.JID(_jid), keys, profile)) + self.bridge.register("getEntityData", lambda jid_, keys, profile: self.memory.getEntityData(jid.JID(jid_), keys, profile)) + self.bridge.register("getEntitiesData", self.memory._getEntitiesData) self.bridge.register("asyncCreateProfile", self.memory.asyncCreateProfile) self.bridge.register("asyncDeleteProfile", self.memory.asyncDeleteProfile) self.bridge.register("asyncConnect", self.asyncConnect) @@ -264,9 +265,13 @@ for plugin in self.plugins.iteritems(): if plugin[1].is_handler: plugin[1].getHandler(profile).setHandlerParent(current) - connected_cb = getattr(plugin[1], "profileConnected", None) + connected_cb = getattr(plugin[1], "profileConnected", None) # profile connected is called after client is ready and roster is got if connected_cb: plugin_conn_cb.append((plugin[0], connected_cb)) + try: + yield plugin[1].profileConnecting(profile) # profile connecting is called before actually starting client + except AttributeError: + pass current.startService() @@ -289,7 +294,7 @@ {'name': plugin_conn_cb[idx][0], 'failure': result}) yield list_d.addCallback(logPluginResults) # FIXME: we should have a timeout here, and a way to know if a plugin freeze - # TODO: mesure time to launch of each plugin + # TODO: mesure launch time of each plugin def _authenticateProfile(self, password, profile): """Authenticate the profile. diff -r 9e904f8a094e -r 3012c2f15dae src/core/xmpp.py --- a/src/core/xmpp.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/core/xmpp.py Mon Feb 09 21:40:45 2015 +0100 @@ -319,10 +319,6 @@ int(priority), statuses, self.parent.profile) - # uncomment these two lines if you need the trigger - #if not self.host.trigger.point("presenceReceived", entity, "unavailable", 0, statuses, self.parent.profile): - # return - # now it's time to notify frontends self.host.bridge.presenceUpdate(entity.full(), show or "", int(priority), statuses, diff -r 9e904f8a094e -r 3012c2f15dae src/memory/disco.py --- a/src/memory/disco.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/memory/disco.py Mon Feb 09 21:40:45 2015 +0100 @@ -107,7 +107,7 @@ def infosCb(disco_infos): cap_hash = self.generateHash(disco_infos) self.hashes[cap_hash] = disco_infos - self.host.memory.updateEntityData(jid_, C.ENTITY_CAP_HASH, cap_hash, client.profile) + self.host.memory.updateEntityData(jid_, C.ENTITY_CAP_HASH, cap_hash, profile_key=client.profile) return disco_infos d = client.disco.requestInfo(jid_) d.addCallback(infosCb) @@ -135,7 +135,7 @@ except (KeyError, exceptions.UnknownEntityError): log.debug("Caching [%s] disco items" % jid_.full()) items = yield client.disco.requestItems(jid_, nodeIdentifier) - self.host.memory.updateEntityData(jid_, "DISCO_ITEMS", items, client.profile) + self.host.memory.updateEntityData(jid_, "DISCO_ITEMS", items, profile_key=client.profile) else: items = yield client.disco.requestItems(jid_, nodeIdentifier) diff -r 9e904f8a094e -r 3012c2f15dae src/memory/memory.py --- a/src/memory/memory.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/memory/memory.py Mon Feb 09 21:40:45 2015 +0100 @@ -444,7 +444,7 @@ @param profile_key: %(doc_profile_key)s """ presence_data = PresenceTuple(show, priority, statuses) - self.updateEntityData(entity_jid, "presence", presence_data, profile_key) + self.updateEntityData(entity_jid, "presence", presence_data, profile_key=profile_key) if entity_jid.resource and show != C.PRESENCE_UNAVAILABLE: # If a resource is available, bare jid should not have presence information try: @@ -580,7 +580,7 @@ full_jid.resource = resource yield full_jid - def updateEntityData(self, entity_jid, key, value, profile_key): + def updateEntityData(self, entity_jid, key, value, silent=False, profile_key=C.PROF_KEY_NONE): """Set a misc data for an entity If key was registered with setSignalOnUpdate, a signal will be sent to frontends @@ -588,6 +588,7 @@ C.ENTITY_ALL for all entities (all resources + bare jids) @param key: key to set (eg: "type") @param value: value for this key (eg: "chatroom") + @param silent(bool): if True, doesn't send signal to frontend, even there is a signal flag (see setSignalOnUpdate) @param profile_key: %(doc_profile_key)s """ profile_cache = self._getProfileCache(profile_key) @@ -600,7 +601,7 @@ entity_data = profile_cache.setdefault(jid_.userhostJID(),{}).setdefault(jid_.resource, {}) entity_data[key] = value - if key in self._key_signals: + if key in self._key_signals and not silent: if not isinstance(value, basestring): log.error(u"Setting a non string value ({}) for a key ({}) which has a signal flag".format(value, key)) else: @@ -636,6 +637,54 @@ else: raise e + def _getEntitiesData(self, entities_jids, keys_list, profile_key): + ret = self.getEntitiesData([jid.JID(jid_) for jid_ in entities_jids], keys_list, profile_key) + return {jid_.full(): data for jid_, data in ret.iteritems()} + + def getEntitiesData(self, entities_jids, keys_list=None, profile_key=C.PROF_KEY_NONE): + """Get a list of cached values for several entities at once + + @param entities_jids: jids of the entities, or empty list for all entities in cache + @param keys_list (iterable,None): list of keys to get, None for everything + @param profile_key: %(doc_profile_key)s + @return: dict withs values for each key in keys_list. + if there is no value of a given key, resulting dict will + have nothing with that key nether + if an entity doesn't exist in cache, it will not appear + in resulting dict + + @raise exceptions.UnknownEntityError: if entity is not in cache + """ + def fillEntityData(entity_cache_data): + entity_data = {} + if keys_list is None: + entity_data = entity_cache_data + else: + for key in keys_list: + try: + entity_data[key] = entity_cache_data[key] + except KeyError: + continue + return entity_data + + profile_cache = self._getProfileCache(profile_key) + ret_data = {} + if entities_jids: + for entity in entities_jids: + try: + entity_cache_data = profile_cache[entity.userhostJID()][entity.resource] + except KeyError: + continue + ret_data[entity.full()] = fillEntityData(entity_cache_data, keys_list) + else: + for bare_jid, data in profile_cache.iteritems(): + for resource, entity_cache_data in data.iteritems(): + full_jid = copy.copy(bare_jid) + full_jid.resource = resource + ret_data[full_jid] = fillEntityData(entity_cache_data) + + return ret_data + def getEntityData(self, entity_jid, keys_list=None, profile_key=C.PROF_KEY_NONE): """Get a list of cached values for entity diff -r 9e904f8a094e -r 3012c2f15dae src/plugins/plugin_xep_0045.py --- a/src/plugins/plugin_xep_0045.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/plugins/plugin_xep_0045.py Mon Feb 09 21:40:45 2015 +0100 @@ -696,7 +696,7 @@ self.userLeftRoom(room, user) def userJoinedRoom(self, room, user): - self.host.memory.updateEntityData(room.roomJID, "type", "chatroom", self.parent.profile) + self.host.memory.updateEntityData(room.roomJID, "type", "chatroom", profile_key=self.parent.profile) if user.nick in self.__changing_nicks: self.__changing_nicks.remove(user.nick) else: diff -r 9e904f8a094e -r 3012c2f15dae src/plugins/plugin_xep_0054.py --- a/src/plugins/plugin_xep_0054.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/plugins/plugin_xep_0054.py Mon Feb 09 21:40:45 2015 +0100 @@ -79,9 +79,8 @@ self.avatar_path = os.path.join(self.host.memory.getConfig('', 'local_dir'), AVATAR_PATH) if not os.path.exists(self.avatar_path): os.makedirs(self.avatar_path) - self.avatars_cache = PersistentDict(NS_VCARD) - self.initialised = self.avatars_cache.load() # FIXME: resulting deferred must be correctly managed - host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self.getCard) + self.avatars_cache = {} + host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self._getCard) host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile) host.bridge.addMethod("setAvatar", ".plugin", in_sign='ss', out_sign='', method=self.setAvatar, async=True) host.trigger.add("presence_available", self.presenceTrigger) @@ -91,9 +90,9 @@ return XEP_0054_handler(self) def presenceTrigger(self, presence_elt, client): - if client.jid.userhost() in self.avatars_cache: + if client.jid.userhost() in self.avatars_cache[client.profile]: x_elt = domish.Element((NS_VCARD_UPDATE, 'x')) - x_elt.addElement('photo', content=self.avatars_cache[client.jid.userhost()]) + x_elt.addElement('photo', content=self.avatars_cache[client.profile][client.jid.userhost()]) presence_elt.addChild(x_elt) return True @@ -103,28 +102,33 @@ # the current naive approach keeps a map between all jids of all profiles # in persistent cache, then put avatar # hashs in memory. Hashed should be shared between profiles - for jid_s, avatar_hash in self.avatars_cache.iteritems(): + for jid_s, avatar_hash in self.avatars_cache[profile].iteritems(): jid_ = jid.JID(jid_s) - self.host.memory.updateEntityData(jid_, "avatar", avatar_hash, profile) + self.host.memory.updateEntityData(jid_, "avatar", avatar_hash, silent=True, profile_key=profile) @defer.inlineCallbacks - def profileConnected(self, profile): - yield self.initialised + def profileConnecting(self, profile): + self.avatars_cache[profile] = PersistentDict(NS_VCARD, profile) + yield self.avatars_cache[profile].load() self._fillCachedValues(profile) + def profileDisconnected(self, profile): + log.debug(u"Deleting profile cache for avatars") + del self.avatars_cache[profile] + def updateCache(self, jid_, name, value, profile): """update cache value save value in memory in case of change - @param jid_: jid of the owner of the vcard - @param name: name of the item which changed - @param value: new value of the item - @param profile: profile which received the update + @param jid_(jid.JID): jid of the owner of the vcard + @param name(str): name of the item which changed + @param value(unicode): new value of the item + @param profile(unicode): profile which received the update """ assert not jid_.resource # VCard are retrieved with bare jid - self.host.memory.updateEntityData(jid_, name, value, profile) + self.host.memory.updateEntityData(jid_, name, value, profile_key=profile) if name == "avatar": - self.avatars_cache[jid_.userhost()] = value + self.avatars_cache[profile][jid_.userhost()] = value def getCache(self, entity_jid, name, profile): """return cached value for jid @@ -140,6 +144,28 @@ return None return data.get(name) + def _getFilename(self, hash_): + """Get filename from hash + + @param hash_: hash of the avatar + @return (str): absolute filename of the avatar + """ + return os.path.join(self.avatar_path, hash_) + + def saveAvatarFile(self, data, hash_): + """Save the avatar picture if it doesn't already exists + + @param data(str): binary image of the avatar + @param hash_(str): hash of the binary data (will be used for the filename) + """ + filename = self._getFilename(hash_) + if not os.path.exists(filename): + with open(filename, 'wb') as file_: + file_.write(data) + log.debug(_("file saved to %s") % hash_) + else: + log.debug(_("file [%s] already in cache") % hash_) + def savePhoto(self, photo_xml): """Parse a elem and save the picture""" for elem in photo_xml.elements(): @@ -149,13 +175,7 @@ log.debug(_('Decoding binary')) decoded = b64decode(str(elem)) image_hash = sha1(decoded).hexdigest() - filename = self.avatar_path + '/' + image_hash - if not os.path.exists(filename): - with open(filename, 'wb') as file_: - file_.write(decoded) - log.debug(_("file saved to %s") % image_hash) - else: - log.debug(_("file [%s] already in cache") % image_hash) + self.saveAvatarFile(decoded, image_hash) return image_hash @defer.inlineCallbacks @@ -212,15 +232,20 @@ except AttributeError: # 'ConnectionLost' object has no attribute 'stanza' log.warning(_("Can't find VCard: %s") % failure.getErrorMessage()) - def getCard(self, target_s, profile_key=C.PROF_KEY_NONE): + def _getCard(self, target_s, profile_key=C.PROF_KEY_NONE): + return self.getCard(jid.JID(target_s), profile_key) + + def getCard(self, target, profile_key=C.PROF_KEY_NONE): """Ask server for VCard - @param target_s: jid from which we want the VCard - @result: id to retrieve the profile""" + + @param target(jid.JID): jid from which we want the VCard + @result: id to retrieve the profile + """ current_jid, xmlstream = self.host.getJidNStream(profile_key) if not xmlstream: raise exceptions.ProfileUnknownError('Asking vcard for a non-existant or not connected profile ({})'.format(profile_key)) profile = self.host.memory.getProfileName(profile_key) - to_jid = jid.JID(target_s) + to_jid = target.userhostJID() log.debug(_("Asking for %s's VCard") % to_jid.userhost()) reg_request = IQ(xmlstream, 'get') reg_request["from"] = current_jid.full() @@ -267,6 +292,7 @@ photo_elt.addElement('TYPE', content='image/png') photo_elt.addElement('BINVAL', content=b64encode(img_buf.getvalue())) img_hash = sha1(img_buf.getvalue()).hexdigest() + self.saveAvatarFile(img_buf.getvalue(), img_hash) return (vcard_set, img_hash) def setAvatar(self, filepath, profile_key=C.PROF_KEY_NONE): @@ -307,8 +333,10 @@ return [] def update(self, presence): - """Request for VCard's nickname - return the cached nickname if exists, else get VCard + """Called on stanza with vcard data + + Check for avatar information, and get VCard if needed + @param presend(domish.Element): stanza """ # FIXME: doesn't manage MUC correctly from_jid = jid.JID(presence['from']).userhostJID() @@ -316,10 +344,20 @@ x_elem = filter(lambda x: x.name == "x", presence.elements())[0] # We only want the "x" element for elem in x_elem.elements(): if elem.name == 'photo': - _hash = str(elem) + hash_ = str(elem) old_avatar = self.plugin_parent.getCache(from_jid, 'avatar', self.parent.profile) - if not old_avatar or old_avatar != _hash: - log.debug(_('New avatar found, requesting vcard')) - self.plugin_parent.getCard(from_jid.userhost(), self.parent.profile) + filename = self.plugin_parent._getFilename(hash_) + if not old_avatar or old_avatar != hash_: + if os.path.exists(filename): + log.debug(u"New avatar found for [{}], it's already in cache, we use it".format(from_jid.full())) + self.plugin_parent.updateCache(from_jid, 'avatar', hash_, self.parent.profile) + else: + log.debug(u'New avatar found for [{}], requesting vcard'.format(from_jid.full())) + self.plugin_parent.getCard(from_jid, self.parent.profile) else: - log.debug("avatar for {} already in cache".format(from_jid)) + if os.path.exists(filename): + log.debug(u"avatar for {} already in cache".format(from_jid.full())) + else: + log.error(u"Avatar for [{}] should be in cache but it is not ! We get it".format(from_jid.full())) + self.plugin_parent.getCard(from_jid, self.parent.profile) + diff -r 9e904f8a094e -r 3012c2f15dae src/plugins/plugin_xep_0085.py --- a/src/plugins/plugin_xep_0085.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/plugins/plugin_xep_0085.py Mon Feb 09 21:40:45 2015 +0100 @@ -137,7 +137,7 @@ if value == DELETE_VALUE: self.host.memory.delEntityData(entity_jid, ENTITY_KEY, profile) else: - self.host.memory.updateEntityData(entity_jid, ENTITY_KEY, value, profile) + self.host.memory.updateEntityData(entity_jid, ENTITY_KEY, value, profile_key=profile) if not value or value == DELETE_VALUE: # reinit chat state UI for this or these contact(s) self.host.bridge.chatStateReceived(entity_jid.full(), "", profile) @@ -151,7 +151,7 @@ @param type_: parameter type """ if (category, name) == (PARAM_KEY, PARAM_NAME): - self.updateEntityData(C.ENTITY_ALL, True if bool("true") else DELETE_VALUE, profile) + self.updateEntityData(C.ENTITY_ALL, True if bool("true") else DELETE_VALUE, profile_key=profile) return False return True @@ -173,11 +173,11 @@ try: domish.generateElementsNamed(message.elements(), name="active").next() # contact enabled Chat State Notifications - self.updateEntityData(from_jid, True, profile) + self.updateEntityData(from_jid, True, profile_key=profile) except StopIteration: if message.getAttribute('type') == 'chat': # contact didn't enable Chat State Notifications - self.updateEntityData(from_jid, False, profile) + self.updateEntityData(from_jid, False, profile_key=profile) return True except StopIteration: pass @@ -260,7 +260,7 @@ except (exceptions.UnknownEntityError, KeyError): if forceEntityData: # enable it for the first time - self.updateEntityData(to_jid, True, profile) + self.updateEntityData(to_jid, True, profile_key=profile) return True # wait for the first message before sending states return False diff -r 9e904f8a094e -r 3012c2f15dae src/plugins/plugin_xep_0115.py --- a/src/plugins/plugin_xep_0115.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/plugins/plugin_xep_0115.py Mon Feb 09 21:40:45 2015 +0100 @@ -107,7 +107,7 @@ client.caps_sent = False if cap_hash not in self.host.memory.disco.hashes: self.host.memory.disco.hashes[cap_hash] = disco_infos - self.host.memory.updateEntityData(client.jid, C.ENTITY_CAP_HASH, cap_hash, profile) + self.host.memory.updateEntityData(client.jid, C.ENTITY_CAP_HASH, cap_hash, profile_key=profile) class XEP_0115_handler(XMPPHandler): @@ -146,7 +146,7 @@ if c_ver in self.host.memory.disco.hashes: # we already know the hash, we update the jid entity log.debug ("hash [%(hash)s] already in cache, updating entity [%(jid)s]" % {'hash': c_ver, 'jid': from_jid.full()}) - self.host.memory.updateEntityData(from_jid, C.ENTITY_CAP_HASH, c_ver, self.profile) + self.host.memory.updateEntityData(from_jid, C.ENTITY_CAP_HASH, c_ver, profile_key=self.profile) return if c_hash != 'sha-1': # unknown hash method diff -r 9e904f8a094e -r 3012c2f15dae src/stdui/ui_profile_manager.py --- a/src/stdui/ui_profile_manager.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/stdui/ui_profile_manager.py Mon Feb 09 21:40:45 2015 +0100 @@ -48,7 +48,7 @@ def gotProfileCipher(profile_cipher): if self.host.memory.auth_sessions.profileGetUnique(profile): # case 1: profile already authenticated - return {'validated': C.str(True)} + return {'validated': C.boolConst(True)} self.profile_ciphers[profile] = profile_cipher if 'profile_password' in data: # case 2: password is provided by the caller @@ -81,7 +81,7 @@ def getParamError(self, failure): _dialog = xml_tools.XMLUI('popup', title=D_('Error')) _dialog.addText(D_("Can't get profile parameter.")) - return {'xmlui': _dialog.toXml(), 'validated': C.str(False)} + return {'xmlui': _dialog.toXml(), 'validated': C.boolConst(False)} @defer.inlineCallbacks def _verifyPassword(self, data, profile): @@ -101,10 +101,10 @@ if not verified: _dialog = xml_tools.XMLUI('popup', title=D_('Connection error')) _dialog.addText(D_("The provided profile password doesn't match.")) - defer.returnValue({'xmlui': _dialog.toXml(), 'validated': C.str(False)}) + defer.returnValue({'xmlui': _dialog.toXml(), 'validated': C.boolConst(False)}) yield self.host.memory.newAuthSession(profile_password, profile) - defer.returnValue({'validated': C.str(True)}) + defer.returnValue({'validated': C.boolConst(True)}) def _changeXMPPPassword(self, data, profile): session_data = self._sessions.profileGetUnique(profile) diff -r 9e904f8a094e -r 3012c2f15dae src/test/helpers.py --- a/src/test/helpers.py Mon Feb 09 09:19:30 2015 +0100 +++ b/src/test/helpers.py Mon Feb 09 21:40:45 2015 +0100 @@ -286,7 +286,7 @@ def delWaitingSub(self, contact_jid, profile_key): pass - def updateEntityData(self, entity_jid, key, value, profile_key): + def updateEntityData(self, entity_jid, key, value, silent=False, profile_key="@NONE@"): self.entities_data.setdefault(entity_jid, {}) self.entities_data[entity_jid][key] = value