Bug 780930 - Store contact aliases on the XMPP server, r=clokep, a=Standard8.
authorFlorian Quèze <florian@queze.net>
Tue, 07 Aug 2012 20:03:56 +0200
changeset 12533 23dcc539a65ade72c34a1b1dd572063ed96d3ec4
parent 12532 ca5e57b83447d0eab2a64caa33a2e047a5adbefc
child 12534 ca70cd11efabbf43979de05457b7dd935f2554ea
push idunknown
push userunknown
push dateunknown
reviewersclokep, Standard8
bugs780930
Bug 780930 - Store contact aliases on the XMPP server, r=clokep, a=Standard8.
chat/protocols/xmpp/xmpp.jsm
--- a/chat/protocols/xmpp/xmpp.jsm
+++ b/chat/protocols/xmpp/xmpp.jsm
@@ -338,16 +338,62 @@ const XMPPAccountBuddyPrototype = {
       tooltipInfo.push(new TooltipInfo(_("tooltip.subscription"),
                                        this.subscription));
     }
 
     return new nsSimpleEnumerator(tooltipInfo);
   },
 
   get normalizedName() this.userName,
+
+  // _rosterAlias is the value stored in the roster on the XMPP
+  // server. For most servers we will be read/write.
+  _rosterAlias: "",
+  set rosterAlias(aNewAlias) {
+    let old = this.displayName;
+    this._rosterAlias = aNewAlias;
+    if (old != this.displayName)
+      this._notifyObservers("display-name-changed", old);
+  },
+  // _vCardFormattedName is the display name the contact has set for
+  // himself in his vCard. It's read-only from our point of view.
+  _vCardFormattedName: "",
+  set vCardFormattedName(aNewFormattedName) {
+    let old = this.displayName;
+    this._vCardFormattedName = aNewFormattedName;
+    if (old != this.displayName)
+      this._notifyObservers("display-name-changed", old);
+  },
+
+  // _serverAlias is set by jsProtoHelper to the value we cached in sqlite.
+  // Use it only if we have neither of the other two values; usually because
+  // we haven't connected to the server yet.
+  get serverAlias() this._rosterAlias || this._vCardFormattedName || this._serverAlias,
+  set serverAlias(aNewAlias) {
+    if (!this._rosterItem) {
+      ERROR("attempting to update the server alias of an account buddy for " +
+            "which we haven't received a roster item.");
+      return;
+    }
+
+    let item = this._rosterItem;
+    if (aNewAlias)
+      item.attributes["name"] = aNewAlias;
+    else if ("name" in item.attributes)
+      delete item.attributes["name"];
+
+    let s = Stanza.iq("set", null, null,
+                      Stanza.node("query", Stanza.NS.roster, null, item));
+    this._account._connection.sendStanza(s);
+
+    // If we are going to change the alias on the server, discard the cached
+    // value that we got from our local sqlite storage at startup.
+    delete this._serverAlias;
+  },
+
   /* Display name of the buddy */
   get contactDisplayName() this.buddy.contact.displayName || this.displayName,
 
   remove: function() {
     if (!this._account.connected)
       return;
 
     let s = Stanza.iq("set", null, null,
@@ -856,35 +902,42 @@ const XMPPAccountPrototype = {
   /* Called when there is an error in the xmpp session */
   onError: function(aError, aException) {
     if (aError === null || aError === undefined)
       aError = Ci.prplIAccount.ERROR_OTHER_ERROR;
     this._disconnect(aError, aException.toString());
   },
 
   /* Callbacks for Query stanzas */
-  /* When a vCard is recieved */
+  /* When a vCard is received */
+  _vCardReceived: false,
   onVCard: function(aStanza) {
     let jid = this._normalizeJID(aStanza.attributes["from"]);
     if (!jid || !this._buddies.hasOwnProperty(jid))
       return;
     let buddy = this._buddies[jid];
 
     let vCard = aStanza.getElement(["vCard"]);
     if (!vCard)
       return;
 
+    let foundFormattedName = false;
     for each (let c in vCard.children) {
       if (c.type != "node")
         continue;
-      if (c.localName == "FN")
-        buddy.serverAlias = c.innerText;
+      if (c.localName == "FN") {
+        buddy.vCardFormattedName = c.innerText;
+        foundFormattedName = true;
+      }
       if (c.localName == "PHOTO")
         buddy._saveIcon(c);
     }
+    if (!foundFormattedName && buddy._vCardFormattedName)
+      buddy.vCardFormattedName = "";
+    buddy._vCardReceived = true;
   },
 
   _normalizeJID: function(aJID) {
     let slashIndex = aJID.indexOf("/");
     if (slashIndex != -1)
       aJID = aJID.substr(0, slashIndex);
     return aJID.toLowerCase();
   },
@@ -915,19 +968,17 @@ const XMPPAccountPrototype = {
       WARN("Received a roster item without jid: " + aItem.getXML());
       return "";
     }
     jid = this._normalizeJID(jid);
 
     let subscription =  "";
     if ("subscription" in aItem.attributes)
       subscription = aItem.attributes["subscription"];
-    if (subscription == "both" || subscription == "to")
-      this._requestVCard(jid);
-    else if (subscription == "remove") {
+    if (subscription == "remove") {
       this._forgetRosterItem(jid);
       return "";
     }
 
     let buddy;
     if (this._buddies.hasOwnProperty(jid))
       buddy = this._buddies[jid];
     else {
@@ -939,31 +990,44 @@ const XMPPAccountPrototype = {
           break; // TODO we should create an accountBuddy per group,
                  // but this._buddies would probably not like that...
         }
       }
       let tag = Services.tags.createTag(tagName);
       buddy = new this._accountBuddyConstructor(this, null, tag, jid);
     }
 
+    // We request the vCard only if we haven't received it yet and are
+    // subscribed to presence for that contact.
+    if ((subscription == "both" || subscription == "to") && !buddy._vCardReceived)
+      this._requestVCard(jid);
+
     let alias = "name" in aItem.attributes ? aItem.attributes["name"] : "";
     if (alias) {
       if (aNotifyOfUpdates && this._buddies.hasOwnProperty(jid))
-        buddy.serverAlias = alias;
+        buddy.rosterAlias = alias;
       else
-        buddy._serverAlias = alias;
+        buddy._rosterAlias = alias;
     }
+    else if (buddy._rosterAlias)
+      buddy.rosterAlias = "";
+
     if (subscription)
       buddy.subscription = subscription;
     if (!this._buddies.hasOwnProperty(jid)) {
       this._buddies[jid] = buddy;
       Services.contacts.accountBuddyAdded(buddy);
     }
     else if (aNotifyOfUpdates)
       buddy._notifyObservers("status-detail-changed");
+
+    // Keep the xml nodes of the item so that we don't have to
+    // recreate them when changing something (eg. the alias) in it.
+    buddy._rosterItem = aItem;
+
     return jid;
   },
   _forgetRosterItem: function(aJID) {
     Services.contacts.accountBuddyRemoved(this._buddies[aJID]);
     delete this._buddies[aJID];
   },
   _requestVCard: function(aJID) {
     let s = Stanza.iq("get", null, aJID,