# HG changeset patch # User Goffi # Date 1345166440 -7200 # Node ID 385cd2169eb5ccaf630aac98ba31c4c4acbc8adb # Parent b7c4bb2c0668cc6b2efdcb0cb0ffc132eee5cc50# Parent 655695f85e5bf8f46c72d23dd2526846692815df core: memory bug fix diff -r 655695f85e5b -r 385cd2169eb5 frontends/src/bridge/DBus.py --- a/frontends/src/bridge/DBus.py Sun Aug 12 15:49:08 2012 +0200 +++ b/frontends/src/bridge/DBus.py Fri Aug 17 03:20:40 2012 +0200 @@ -150,7 +150,7 @@ def registerNewAccount(self, login, password, email, host, port=5222): return unicode(self.db_core_iface.registerNewAccount(login, password, email, host, port)) - def sendMessage(self, to_jid, message, subject='', mess_type="chat", profile_key="@DEFAULT@"): + def sendMessage(self, to_jid, message, subject='', mess_type="auto", profile_key="@DEFAULT@"): return self.db_core_iface.sendMessage(to_jid, message, subject, mess_type, profile_key) def setParam(self, name, value, category, profile_key="@DEFAULT@"): diff -r 655695f85e5b -r 385cd2169eb5 frontends/src/jp/jp --- a/frontends/src/jp/jp Sun Aug 12 15:49:08 2012 +0200 +++ b/frontends/src/jp/jp Fri Aug 17 03:20:40 2012 +0200 @@ -60,6 +60,7 @@ import tarfile import tempfile import shutil +import unicodedata try: from progressbar import ProgressBar, Percentage, Bar, ETA, FileTransferSpeed except ImportError, e: @@ -155,7 +156,7 @@ self.bridge.asyncConnect(self.profile, self.connected, cantConnect) return elif not self.bridge.isConnected(self.profile): - error(_(u"SàT is not conneted, please connect before using jp")) + error(_(u"Profile [%(profile)s] is not connected, please connect it before using jp, or use --connect option") % { "profile": self.profile }) exit(1) self.connected() @@ -163,15 +164,23 @@ def check_jids(self): """Check jids validity, transform roster name to corresponding jids""" names2jid = {} + nodes2jid = {} for contact in self.bridge.getContacts(self.options.profile): _jid, attr, groups = contact if attr.has_key("name"): names2jid[attr["name"].lower()] = _jid + nodes2jid[JID(_jid).node.lower()] = _jid def expandJid(jid): _jid = jid.lower() - return unicode(names2jid[_jid] if _jid in names2jid else jid) + if _jid in names2jid: + expanded = names2jid[_jid] + elif _jid in nodes2jid: + expanded = nodes2jid[_jid] + else: + expanded = jid + return unicode(expanded) def check(jid): if not jid.is_valid: @@ -190,6 +199,17 @@ except AttributeError: pass + def clean_ustr(self, ustr): + """Clean unicode string + remove special characters from unicode string""" + def valid_chars(unicode_source): + for char in unicode_source: + if unicodedata.category(char) == 'Cc' and char!='\n': + continue + yield char + return ''.join(valid_chars(ustr)) + + def send_stdin(self): """Send incomming data on stdin to jabber contact""" header = "\n" if self.options.new_line else "" @@ -198,12 +218,12 @@ if header: self.bridge.sendMessage(self.dest_jid, header, profile_key=self.profile) while (True): - line = sys.stdin.readline() + line = self.clean_ustr(sys.stdin.readline().decode('utf-8','ignore')) if not line: break self.bridge.sendMessage(self.dest_jid, line.replace("\n",""), profile_key=self.profile) else: - self.bridge.sendMessage(self.dest_jid, header + "".join(sys.stdin.readlines()), profile_key=self.profile) + self.bridge.sendMessage(self.dest_jid, header + self.clean_ustr(u"".join([stream.decode('utf-8','ignore') for stream in sys.stdin.readlines()])), profile_key=self.profile) def pipe_out(self): diff -r 655695f85e5b -r 385cd2169eb5 frontends/src/wix/main_window.py --- a/frontends/src/wix/main_window.py Sun Aug 12 15:49:08 2012 +0200 +++ b/frontends/src/wix/main_window.py Fri Aug 17 03:20:40 2012 +0200 @@ -442,9 +442,9 @@ dlg.ShowModal() dlg.Destroy() return - id = self.bridge.getCard(target.short, profile_key=self.profile) - self.current_action_ids.add(id) - self.current_action_ids_cb[id] = self.onProfileReceived + _id = self.bridge.getCard(target.short, profile_key=self.profile) + self.current_action_ids.add(_id) + self.current_action_ids_cb[_id] = self.onProfileReceived def onProfileReceived(self, data): """Called when a profile is received""" @@ -467,9 +467,9 @@ def onFindGateways(self, e): debug(_("Find Gateways request")) - id = self.bridge.findGateways(self.profiles[self.profile]['whoami'].domain, self.profile) - self.current_action_ids.add(id) - self.current_action_ids_cb[id] = self.onGatewaysFound + _id = self.bridge.findGateways(self.profiles[self.profile]['whoami'].domain, self.profile) + self.current_action_ids.add(_id) + self.current_action_ids_cb[_id] = self.onGatewaysFound def onGatewaysFound(self, data): """Called when SàT has found the server gateways""" diff -r 655695f85e5b -r 385cd2169eb5 src/bridge/bridge_constructor/bridge_template.ini --- a/src/bridge/bridge_constructor/bridge_template.ini Sun Aug 12 15:49:08 2012 +0200 +++ b/src/bridge/bridge_constructor/bridge_template.ini Fri Aug 17 03:20:40 2012 +0200 @@ -309,13 +309,13 @@ sig_in=sssss sig_out= param_2_default='' -param_3_default="chat" +param_3_default="auto" param_4_default="@DEFAULT@" doc=Send a message doc_param_0=to_jid: JID of the recipient doc_param_1=message: body of the message doc_param_2=subject: Subject of the message ('' if no subject) -doc_param_3=mess_type: Type of the message (cf RFC 3921 #2.1.1) +doc_param_3=mess_type: Type of the message (cf RFC 3921 #2.1.1) or "auto" for automatic type detection doc_param_4=%(doc_profile_key)s [setPresence] diff -r 655695f85e5b -r 385cd2169eb5 src/core/exceptions.py --- a/src/core/exceptions.py Sun Aug 12 15:49:08 2012 +0200 +++ b/src/core/exceptions.py Fri Aug 17 03:20:40 2012 +0200 @@ -21,3 +21,12 @@ class ProfileUnknownError(Exception): pass + +class ProfileNotInCacheError(Exception): + pass + +class ConnectedProfileError(Exception): + pass + +class UnknownEntityError(Exception): + pass diff -r 655695f85e5b -r 385cd2169eb5 src/core/sat_main.py --- a/src/core/sat_main.py Sun Aug 12 15:49:08 2012 +0200 +++ b/src/core/sat_main.py Fri Aug 17 03:20:40 2012 +0200 @@ -26,6 +26,7 @@ from twisted.application import service from twisted.internet import defer +from twisted.internet.defer import inlineCallbacks, returnValue from twisted.words.protocols.jabber import jid, xmlstream from twisted.words.xish import domish @@ -43,7 +44,7 @@ import os.path from sat.core import xmpp -from sat.core.exceptions import ProfileUnknownError +from sat.core.exceptions import ProfileUnknownError, UnknownEntityError from sat.memory.memory import Memory from sat.tools.xml_tools import tupleList2dataForm from sat.tools.misc import TriggerManager @@ -256,6 +257,7 @@ return current.getConnectionDeferred() + self.memory.startProfileSession(profile) return self.memory.loadIndividualParams(profile).addCallback(afterMemoryInit) def disconnect(self, profile_key): @@ -290,7 +292,7 @@ del self.profiles[profile] except KeyError: error(_("Trying to remove reference to a client not referenced")) - self.memory.purgeProfile(profile) + self.memory.purgeProfileSession(profile) def startService(self): info("Salut à toi ô mon frère !") @@ -444,24 +446,53 @@ ## jabber methods ## - def sendMessage(self, to, msg, subject=None, type='chat', profile_key='@DEFAULT@'): + def sendMessage(self, to, msg, subject=None, mess_type='auto', profile_key='@DEFAULT@'): #FIXME: check validity of recipient profile = self.memory.getProfileName(profile_key) assert(profile) - current_jid = self.profiles[profile].jid - debug(_("Sending jabber message to %s..."), to) + client = self.profiles[profile] + current_jid = client.jid + mess_data = { #we put data in a dict, so trigger methods can change them + "to": jid.JID(to), + "message": msg, + "subject": subject, + "type": mess_type + } + if not self.trigger.point("sendMessage", mess_data, profile): + return + if mess_data["type"] == "auto": + #XXX: if the type has not been changed by a trigger method, + # we try to guess it + if mess_data["subject"]: + mess_data["type"] = 'normal' + elif not mess_data["to"].resource: #if to JID has a resource, the type is not 'groupchat' + #we may have a groupchat message, we check if the we know this jid + try: + entity_type = self.memory.getEntityData(mess_data["to"], ['type'], profile)["type"] + #FIXME: should entity_type manage ressources ? + except (UnknownEntityError, KeyError): + entity_type = "contact" + + if entity_type == "chatroom": + mess_data["type"] = 'groupchat' + else: + mess_data["type"] = 'chat' + else: + mess_data["type"] == 'chat' + mess_data["type"] == "chat" if mess_data["subject"] else "normal" + debug(_("Sending jabber message of type [%(type)s] to %(to)s...") % {"type": mess_data["type"], "to": to}) message = domish.Element(('jabber:client','message')) - message["to"] = jid.JID(to).full() + message["to"] = mess_data["to"].full() message["from"] = current_jid.full() - message["type"] = type - if subject: + message["type"] = mess_data["type"] + if mess_data["subject"]: message.addElement("subject", "jabber:client", subject) message.addElement("body", "jabber:client", msg) - self.profiles[profile].xmlstream.send(message) - if type!="groupchat": + client.xmlstream.send(message) + if mess_data["type"]!="groupchat": self.memory.addToHistory(current_jid, jid.JID(to), unicode(msg), profile=profile) #we don't add groupchat message to history, as we get them back - #and they will we added then - self.bridge.newMessage(message['from'], unicode(msg), mess_type=type, to_jid=message['to'], profile=profile) #We send back the message, so all clients are aware of it + #and they will be added then + self.bridge.newMessage(message['from'], unicode(msg), mess_type=mess_data["type"], to_jid=message['to'], profile=profile) #We send back the message, so all clients are aware of it def setPresence(self, to="", show="", priority = 0, statuses={}, profile_key='@DEFAULT@'): diff -r 655695f85e5b -r 385cd2169eb5 src/core/xmpp.py --- a/src/core/xmpp.py Sun Aug 12 15:49:08 2012 +0200 +++ b/src/core/xmpp.py Fri Aug 17 03:20:40 2012 +0200 @@ -227,6 +227,10 @@ def getBareJids(self): """Return all bare jids (as unicode) of the roster""" return self._jids.keys() + + def isJidInRoster(self, entity_jid): + """Return True if jid is in roster""" + return entity_jid.userhost() in self._jids def getItems(self): """Return all items of the roster""" @@ -249,7 +253,7 @@ statuses["default"] = statuses[None] del statuses[None] - self.host.memory.addPresenceStatus(entity, show or "", + self.host.memory.setPresenceStatus(entity, show or "", int(priority), statuses, self.parent.profile) #now it's time to notify frontends @@ -265,7 +269,7 @@ if statuses.has_key(None): #we only want string keys statuses["default"] = statuses[None] del statuses[None] - self.host.memory.addPresenceStatus(entity, "unavailable", 0, statuses, self.parent.profile) + self.host.memory.setPresenceStatus(entity, "unavailable", 0, statuses, self.parent.profile) #now it's time to notify frontends self.host.bridge.presenceUpdate(entity.full(), "unavailable", 0, statuses, self.parent.profile) diff -r 655695f85e5b -r 385cd2169eb5 src/memory/memory.py --- a/src/memory/memory.py Sun Aug 12 15:49:08 2012 +0200 +++ b/src/memory/memory.py Fri Aug 17 03:20:40 2012 +0200 @@ -31,15 +31,11 @@ from sat.core.default_config import default_config from sat.memory.sqlite import SqliteStorage from sat.memory.persistent import PersistentDict +from sat.core import exceptions SAVEFILE_PARAM_XML="/param" #xml parameters template SAVEFILE_DATABASE="/sat.db" -class ProfileNotInCacheError(Exception): - pass - -class ConnectedProfileError(Exception): - pass class Params(): """This class manage parameters with xml""" @@ -153,7 +149,7 @@ return True if self.host.isConnected(profile): error(_("Trying to delete a connected profile")) - raise ConnectedProfileError + raise exceptions.ConnectedProfileError self.storage.deleteProfile(profile) return False @@ -293,12 +289,12 @@ try: value = self.__getParam(profile, category, name) return defer.succeed(value if value!=None else default) - except ProfileNotInCacheError: + except exceptions.ProfileNotInCacheError: #We have to ask data to the storage manager d = self.storage.getIndParam(category, name, profile) return d.addCallback(lambda value: value if value!=None else default) - def __getParam(self, profile, category, name, type='individual', cache=None): + def __getParam(self, profile, category, name, _type='individual', cache=None): """Return the param, or None if it doesn't exist @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@) @param category: param category @@ -307,16 +303,16 @@ @param cache: temporary cache, to use when profile is not logged @return: param value or None if it doesn't exist """ - if type == 'general': + if _type == 'general': if self.params_gen.has_key((category, name)): return self.params_gen[(category, name)] return None #This general param has the default value - assert (type == 'individual') + assert (_type == 'individual') if self.params.has_key(profile): cache = self.params[profile] # if profile is in main cache, we use it, # ignoring the temporary cache elif cache == None: #else we use the temporary cache if it exists, or raise an exception - raise ProfileNotInCacheError + raise exceptions.ProfileNotInCacheError if not cache.has_key((category, name)): return None return cache[(category, name)] @@ -419,19 +415,19 @@ d = self.__constructProfileXml(profile) return d.addCallback(returnCategoryXml) - def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ? + def __getParamNode(self, name, category, _type="@ALL@"): #FIXME: is _type useful ? """Return a node from the param_xml @param name: name of the node @param category: category of the node - @type: keyword for search: + @_type: keyword for search: @ALL@ search everywhere @GENERAL@ only search in general type @INDIVIDUAL@ only search in individual type @return: a tuple with the node type and the the node, or None if not found""" for type_node in self.dom.documentElement.childNodes: - if ( ((type == "@ALL@" or type == "@GENERAL@") and type_node.nodeName == 'general') - or ( (type == "@ALL@" or type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ): + if ( ((_type == "@ALL@" or _type == "@GENERAL@") and type_node.nodeName == 'general') + or ( (_type == "@ALL@" or _type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ): for node in type_node.getElementsByTagName('category'): if node.getAttribute("name") == category: params = node.getElementsByTagName("param") @@ -474,8 +470,8 @@ assert (node[0] == 'individual') assert (profile_key != "@NONE@") - type = node[1].getAttribute("type") - if type=="button": + _type = node[1].getAttribute("type") + if _type=="button": print "clique",node.toxml() else: if self.host.isConnected(profile): #key can not exists if profile is not connected @@ -490,8 +486,8 @@ info (_("Memory manager init")) self.initialized = defer.Deferred() self.host = host - self.presenceStatus={} - self.lastResource={} #tmp, will be refactored with bdd integration + self.entitiesCache={} #XXX: keep presence/last resource/other data in cache + # /!\ an entity is not necessarily in roster self.subscriptions={} self.server_features={} #used to store discovery's informations self.server_identities={} @@ -561,10 +557,22 @@ @param profile: %(doc_profile)s""" return self.params.loadIndParams(profile) - def purgeProfile(self, profile): + def startProfileSession(self, profile): + """"Iniatialise session for a profile + @param profile: %(doc_profile)s""" + info(_("[%s] Profile session started" % profile)) + self.entitiesCache[profile] = {} + + def purgeProfileSession(self, profile): """Delete cache of data of profile @param profile: %(doc_profile)s""" + info(_("[%s] Profile session purge" % profile)) self.params.purgeProfile(profile) + try: + del self.entitiesCache[profile] + except KeyError: + error(_("Trying to purge roster status cache for a profile not in memory: [%s]") % profile) + def save(self): """Save parameters and all memory things to file/db""" @@ -617,29 +625,29 @@ self.server_features[profile] = [] self.server_features[profile].append(feature) - def addServerIdentity(self, category, type, entity, profile): + def addServerIdentity(self, category, _type, entity, profile): """Add an identity discovered from server @param feature: string of the feature @param profile: which profile is using this server ?""" if not self.server_identities.has_key(profile): self.server_identities[profile] = {} - if not self.server_identities[profile].has_key((category, type)): - self.server_identities[profile][(category, type)]=set() - self.server_identities[profile][(category, type)].add(entity) + if not self.server_identities[profile].has_key((category, _type)): + self.server_identities[profile][(category, _type)]=set() + self.server_identities[profile][(category, _type)].add(entity) - def getServerServiceEntities(self, category, type, profile): + def getServerServiceEntities(self, category, _type, profile): """Return all available entities for a service""" if self.server_identities.has_key(profile): - return self.server_identities[profile].get((category, type), set()) + return self.server_identities[profile].get((category, _type), set()) else: return None - def getServerServiceEntity(self, category, type, profile): + def getServerServiceEntity(self, category, _type, profile): """Helper method to get first available entity for a service""" - entities = self.getServerServiceEntities(category, type, profile) + entities = self.getServerServiceEntities(category, _type, profile) if entities == None: warning(_("Entities (%(category)s/%(type)s) not available, maybe they haven't been asked to server yet ?") % {"category":category, - "type":type}) + "type":_type}) return None else: return list(entities)[0] if entities else None @@ -658,45 +666,102 @@ @param contact: contact jid (unicode) @param profile_key: %(doc_profile_key)s""" profile = self.getProfileName(profile_key) - if not profile: - error(_('Asking contacts for a non-existant profile')) + if not profile or not self.host.isConnected(profile): + error(_('Asking contacts for a non-existant or not connected profile')) + return "" + entity = jid.JID(contact).userhost() + if not entity in self.entitiesCache[profile]: + info(_("Entity not in cache")) return "" try: - return self.lastResource[profile][jid.JID(contact).userhost()] - except: + return self.entitiesCache[profile][entity]["last_resource"] + except KeyError: return "" - def addPresenceStatus(self, contact_jid, show, priority, statuses, profile_key): + def getPresenceStatus(self, profile_key): + profile = self.getProfileName(profile_key) + if not profile: + error(_('Asking contacts for a non-existant profile')) + return {} + entities_presence = {} + for entity in self.entitiesCache[profile]: + # if entity exists, "presence" key must exist + entities_presence[entity] = self.entitiesCache[profile][entity]["presence"] + + debug ("Memory getPresenceStatus (%s)", entities_presence) + return entities_presence + + def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key): + """Change the presence status of an entity""" profile = self.getProfileName(profile_key) if not profile: error(_('Trying to add presence status to a non-existant profile')) return - if not self.presenceStatus.has_key(profile): - self.presenceStatus[profile] = {} - if not self.lastResource.has_key(profile): - self.lastResource[profile] = {} - if not self.presenceStatus[profile].has_key(contact_jid.userhost()): - self.presenceStatus[profile][contact_jid.userhost()] = {} - resource = jid.parse(contact_jid.full())[2] or '' + entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(),{}) + resource = jid.parse(entity_jid.full())[2] or '' if resource: - self.lastResource[profile][contact_jid.userhost()] = resource + entity_data["last_resource"] = resource + if not "last_resource" in entity_data: + entity_data["last_resource"] = '' + + entity_data.setdefault("presence",{})[resource] = (show, priority, statuses) + + def updateEntityData(self, entity_jid, key, value, profile_key): + """Set a misc data for an entity + @param entity_jid: JID of the entity + @param key: key to set (eg: "type") + @param value: value for this key (eg: "chatroom") + @param profile_key: %(doc_profile_key)s + """ + profile = self.getProfileName(profile_key) + if not profile: + raise exceptions.UnknownProfileError(_('Trying to get entity data for a non-existant profile')) + if not profile in self.entitiesCache: + raise exceptions.ProfileNotInCacheError + entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(),{}) + entity_data[key] = value - self.presenceStatus[profile][contact_jid.userhost()][resource] = (show, priority, statuses) + def getEntityData(self, entity_jid, keys_list, profile_key): + """Get a list of cached values for entity + @param entity_jid: JID of the entity + @param keys_list: list of keys to get, empty list 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, resultint dict will + have nothing with that key nether + @raise: exceptions.UnknownEntityError if entity is not in cache + exceptions.ProfileNotInCacheError if profile is not in cache + """ + profile = self.getProfileName(profile_key) + if not profile: + raise exceptions.UnknownProfileError(_('Trying to get entity data for a non-existant profile')) + if not profile in self.entitiesCache: + raise exceptions.ProfileNotInCacheError + if not entity_jid.userhost() in self.entitiesCache[profile]: + raise exceptions.UnknownEntityError + entity_data = self.entitiesCache[profile][entity_jid.userhost()] + if not keys_list: + return entity_data + ret = {} + for key in keys_list: + if key in entity_data: + ret[key] = entity_data[key] + return ret - def addWaitingSub(self, type, contact_jid, profile_key): + def addWaitingSub(self, _type, entity_jid, profile_key): """Called when a subcription request is received""" profile = self.getProfileName(profile_key) assert(profile) if not self.subscriptions.has_key(profile): self.subscriptions[profile] = {} - self.subscriptions[profile][contact_jid] = type + self.subscriptions[profile][entity_jid] = _type - def delWaitingSub(self, contact_jid, profile_key): + def delWaitingSub(self, entity_jid, profile_key): """Called when a subcription request is finished""" profile = self.getProfileName(profile_key) assert(profile) - if self.subscriptions.has_key(profile) and self.subscriptions[profile].has_key(contact_jid): - del self.subscriptions[profile][contact_jid] + if self.subscriptions.has_key(profile) and self.subscriptions[profile].has_key(entity_jid): + del self.subscriptions[profile][entity_jid] def getWaitingSub(self, profile_key): """Called to get a list of currently waiting subscription requests""" @@ -709,16 +774,6 @@ return self.subscriptions[profile] - def getPresenceStatus(self, profile_key): - profile = self.getProfileName(profile_key) - if not profile: - error(_('Asking contacts for a non-existant profile')) - return {} - if not self.presenceStatus.has_key(profile): - self.presenceStatus[profile] = {} - debug ("Memory getPresenceStatus (%s)", self.presenceStatus[profile]) - return self.presenceStatus[profile] - def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'): return self.params.getParamA(name, category, attr, profile_key) diff -r 655695f85e5b -r 385cd2169eb5 src/plugins/plugin_xep_0045.py --- a/src/plugins/plugin_xep_0045.py Sun Aug 12 15:49:08 2012 +0200 +++ b/src/plugins/plugin_xep_0045.py Fri Aug 17 03:20:40 2012 +0200 @@ -85,6 +85,7 @@ self.host.bridge.roomJoined(room.roomJID.userhost(), [user.nick for user in room.roster.values()], room.nick, profile) room_jid_s = room.roomJID.userhost() + self.host.memory.updateEntityData(room.roomJID, "type", "chatroom", profile) self.clients[profile].joined_rooms[room_jid_s] = room if room.locked: #FIXME: the current behaviour is to create an instant room diff -r 655695f85e5b -r 385cd2169eb5 src/plugins/plugin_xep_0054.py --- a/src/plugins/plugin_xep_0054.py Sun Aug 12 15:49:08 2012 +0200 +++ b/src/plugins/plugin_xep_0054.py Fri Aug 17 03:20:40 2012 +0200 @@ -22,6 +22,7 @@ from logging import debug, info, error from twisted.words.xish import domish from twisted.internet import protocol, defer, threads, reactor +from twisted.internet.defer import inlineCallbacks, returnValue from twisted.words.protocols.jabber import client, jid, xmlstream from twisted.words.protocols.jabber import error as jab_error from twisted.words.protocols.jabber.xmlstream import IQ @@ -128,7 +129,7 @@ debug(_("file [%s] already in cache") % hash) return hash - @defer.deferredGenerator + @inlineCallbacks def vCard2Dict(self, vcard, target, profile): """Convert a VCard to a dict, and save binaries""" debug (_("parsing vcard")) @@ -148,10 +149,7 @@ elif elem.name == 'BDAY': dictionary['birthday'] = unicode(elem) elif elem.name == 'PHOTO': - d2 = defer.waitForDeferred( - threads.deferToThread(self.save_photo, elem)) - yield d2 - dictionary["avatar"] = d2.getResult() + dictionary["avatar"] = yield threads.deferToThread(self.save_photo, elem) if not dictionary["avatar"]: #can happen in case of e.g. empty photo elem del dictionary['avatar'] else: @@ -159,7 +157,7 @@ else: info (_('FIXME: [%s] VCard tag is not managed yet') % elem.name) - yield dictionary + returnValue(dictionary) def vcard_ok(self, answer, profile): """Called after the first get IQ""" @@ -170,12 +168,12 @@ d.addCallback(lambda data: self.host.bridge.actionResult("RESULT", answer['id'], data)) else: error (_("FIXME: vCard not found as first child element")) - self.host.bridge.actionResult("SUPPRESS", answer['id'], {}) #FIXME: maybe an error message would be best + self.host.bridge.actionResult("SUPPRESS", answer['id'], {}) #FIXME: maybe an error message would be better def vcard_err(self, failure): """Called when something is wrong with registration""" error (_("Can't find VCard of %s") % failure.value.stanza['from']) - self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}) #FIXME: maybe an error message would be best + self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}) #FIXME: maybe an error message would be better def getCard(self, target, profile_key='@DEFAULT@'): """Ask server for VCard diff -r 655695f85e5b -r 385cd2169eb5 src/plugins/plugin_xep_0277.py --- a/src/plugins/plugin_xep_0277.py Sun Aug 12 15:49:08 2012 +0200 +++ b/src/plugins/plugin_xep_0277.py Fri Aug 17 03:20:40 2012 +0200 @@ -92,7 +92,7 @@ microblog_data['author'] = _entry.authors[0].name.text microblog_data['timestamp'] = str(int(_entry.updated.tf)) microblog_data['id'] = item['id'] - except AttributeError, KeyError: + except (AttributeError, KeyError): error(_('Error while parsing atom entry for microblogging event')) return {} diff -r 655695f85e5b -r 385cd2169eb5 src/test/helpers.py --- a/src/test/helpers.py Sun Aug 12 15:49:08 2012 +0200 +++ b/src/test/helpers.py Fri Aug 17 03:20:40 2012 +0200 @@ -66,7 +66,7 @@ def addContact(self, contact_jid, attributes, groups, profile_key='@DEFAULT@'): pass - def addPresenceStatus(self, contact_jid, show, priority, statuses, profile_key='@DEFAULT@'): + def setPresenceStatus(self, contact_jid, show, priority, statuses, profile_key='@DEFAULT@'): pass def addWaitingSub(self, type, contact_jid, profile_key):