Bug 742793 - Support vCard based avatars on JS-XMPP. r=clokep,a=bienvenu.
authorFlorian Quèze <florian@queze.net>
Mon, 16 Jul 2012 11:37:20 -0400
changeset 12432 faffa1007ffb045db96aedaf50f36c84b5a0fbb6
parent 12431 517923eb47b967954fddfe67122ce19057242c6c
child 12433 806fd54d5464782e7b9f0bae2c2e60cccf67f5ff
push id599
push usermconley@mozilla.com
push dateMon, 16 Jul 2012 20:33:12 +0000
treeherdercomm-beta@c3489d5b7b65 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersclokep, bienvenu
bugs742793
Bug 742793 - Support vCard based avatars on JS-XMPP. r=clokep,a=bienvenu.
chat/protocols/xmpp/xmpp.jsm
--- a/chat/protocols/xmpp/xmpp.jsm
+++ b/chat/protocols/xmpp/xmpp.jsm
@@ -338,29 +338,40 @@ const XMPPAccountBuddyPrototype = {
     let s = Stanza.iq("set", null, null,
                       Stanza.node("query", Stanza.NS.roster, null,
                                   Stanza.node("item", null,
                                               {jid: this.normalizedName,
                                                subscription: "remove"})));
     this._account._connection.sendStanza(s);
   },
 
+  _photoHash: null,
   _saveIcon: function(aPhotoNode) {
     let type = aPhotoNode.getElement(["TYPE"]).innerText;
     const kExt = {"image/gif": "gif", "image/jpeg": "jpg", "image/png": "png"};
     if (!kExt.hasOwnProperty(type))
       return;
 
     let data = aPhotoNode.getElement(["BINVAL"]).innerText;
     let content = atob(data.replace(/[^A-Za-z0-9\+\/\=]/g, ""));
+
+    // Store a sha1 hash of the photo we have just received.
+    let ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
+    ch.init(ch.SHA1);
+    let dataArray = [content.charCodeAt(i) for (i in content)];
+    ch.update(dataArray, dataArray.length);
+    let hash = ch.finish(false);
+    function toHexString(charCode) ("0" + charCode.toString(16)).slice(-2)
+    this._photoHash = [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
+
     let istream = Cc["@mozilla.org/io/string-input-stream;1"]
                   .createInstance(Ci.nsIStringInputStream);
     istream.setData(content, content.length);
 
-    let fileName = this.normalizedName + "." + kExt[type];
+    let fileName = this._photoHash + "." + kExt[type];
     let file = FileUtils.getFile("ProfD", ["icons",
                                            this.account.protocol.normalizedName,
                                            this.account.normalizedName,
                                            fileName]);
     let ostream = FileUtils.openSafeFileOutputStream(file);
     let buddy = this;
     NetUtil.asyncCopy(istream, ostream, function(rc) {
       if (Components.isSuccessCode(rc))
@@ -427,16 +438,27 @@ const XMPPAccountBuddyPrototype = {
         statusType: statusType,
         statusText: status,
         idleSince: idleSince,
         priority: priority,
         stanza: aStanza
       };
     }
 
+    let photo = aStanza.getElement(["x", "photo"]);
+    if (photo && photo.uri == Stanza.NS.vcard_update) {
+      let hash = photo.innerText;
+      if (hash && hash != this._photoHash)
+        this._account._requestVCard(this.normalizedName);
+      else if (!hash && this._photoHash) {
+        delete this._photoHash;
+        this.buddyIconFilename = "";
+      }
+    }
+
     for (let r in this._resources) {
       if (preferred === undefined ||
           this._resources[r].statusType > this._resources[preferred].statusType)
         // FIXME also compare priorities...
         preferred = r;
     }
     if (preferred != undefined && preferred == this._preferredResource &&
         resource != preferred) {
@@ -853,21 +875,18 @@ 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") {
-      let s = Stanza.iq("get", null, jid,
-                        Stanza.node("vCard", Stanza.NS.vcard));
-      this._connection.sendStanza(s, this.onVCard, this);
-    }
+    if (subscription == "both" || subscription == "to")
+      this._requestVCard(jid);
     else if (subscription == "remove") {
       this._forgetRosterItem(jid);
       return "";
     }
 
     let buddy;
     if (this._buddies.hasOwnProperty(jid))
       buddy = this._buddies[jid];
@@ -901,16 +920,21 @@ const XMPPAccountPrototype = {
     else if (aNotifyOfUpdates)
       buddy._notifyObservers("status-detail-changed");
     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,
+                      Stanza.node("vCard", Stanza.NS.vcard));
+    this._connection.sendStanza(s, this.onVCard, this);
+  },
 
   /* When the roster is received */
   onRoster: function(aStanza) {
     for each (let qe in aStanza.getChildren("query")) {
       if (qe.uri != Stanza.NS.roster)
         continue;
 
       let savedRoster = Object.keys(this._buddies);