Bug 884319 - Add http.jsm to toolkit for usage by Thunderbird FileLink, Lightning and Instantbird, r=fqueze,mconley,philipp,a=Standard8.
authorPatrick Cloke <clokep@gmail.com>
Thu, 20 Jun 2013 16:05:48 -0400
changeset 14330 c29c9f0c7405ef30201d15cb96878cab15d1e9b0
parent 14329 8d3cee8ca7c3ae64bb8aa629b1df3df586d59151
child 14331 b5afa9a133fb2a3941741077c603b5f72d4aac14
push id1009
push userbugzilla@standard8.plus.com
push dateMon, 05 Aug 2013 20:38:36 +0000
treeherdercomm-aurora@fb476910d19f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfqueze, mconley, philipp, Standard8
bugs884319
Bug 884319 - Add http.jsm to toolkit for usage by Thunderbird FileLink, Lightning and Instantbird, r=fqueze,mconley,philipp,a=Standard8.
calendar/base/modules/OAuth2.jsm
chat/modules/http.jsm
chat/modules/moz.build
chat/protocols/twitter/twitter.js
mail/base/modules/http.jsm
mail/base/modules/moz.build
mail/base/modules/oauth.jsm
mail/components/cloudfile/content/Box/auth.js
mail/components/cloudfile/nsBox.js
mail/components/cloudfile/nsUbuntuOne.js
--- a/calendar/base/modules/OAuth2.jsm
+++ b/calendar/base/modules/OAuth2.jsm
@@ -4,17 +4,17 @@
 
 /**
  * Provides OAuth 2.0 authentication
  */
 var EXPORTED_SYMBOLS = ["OAuth2"];
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
-Cu.import("resource:///modules/http.jsm");
+Cu.import("resource://gre/modules/Http.jsm");
 Cu.import("resource:///modules/Services.jsm");
 Cu.import("resource:///modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/gloda/log4moz.js");
 
 function parseURLData(aData) {
   let result = {};
   aData.split("?", 2)[1].split("&").forEach(function (aParam) {
     let [key, value] = aParam.split("=");
@@ -166,17 +166,22 @@ OAuth2.prototype = {
 
         if (aType == OAuth2.CODE_AUTHORIZATION) {
             params.push(["code", aCode]);
             params.push(["redirect_uri", this.completionURI]);
         } else if (aType == OAuth2.CODE_REFRESH) {
             params.push(["refresh_token", aCode]);
         }
 
-        doXHRequest(this.tokenURI, null, params, this.onAccessTokenReceived, this.onAccessTokenFailed, this);
+        let options = {
+          postData: params,
+          onLoad: this.onAccessTokenReceived.bind(this),
+          onError: this.onAccessTokenFailed.bind(this)
+        }
+        httpRequest(this.tokenURI, options);
     },
 
     onAccessTokenFailed: function onAccessTokenFailed(aData) {
         this.refreshToken = null;
         this.connecting = false;
         this.connectFailureCallback();
     },
 
deleted file mode 100644
--- a/chat/modules/http.jsm
+++ /dev/null
@@ -1,82 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const EXPORTED_SYMBOLS = ["doXHRequest", "percentEncode"];
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-// Strictly follow RFC 3986 when encoding URI components.
-function percentEncode(aString)
-  encodeURIComponent(aString).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
-
-function doXHRequest(aUrl, aHeaders, aPOSTData, aOnLoad, aOnError, aThis,
-                     aMethod, aLogger) {
-  let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
-              .createInstance(Ci.nsIXMLHttpRequest);
-  xhr.mozBackgroundRequest = true; // no error dialogs
-  xhr.open(aMethod || (aPOSTData ? "POST" : "GET"), aUrl);
-  xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS | // don't send cookies
-                          Ci.nsIChannel.LOAD_BYPASS_CACHE |
-                          Ci.nsIChannel.INHIBIT_CACHING;
-  xhr.onerror = function(aProgressEvent) {
-    if (aOnError) {
-      // adapted from toolkit/mozapps/extensions/nsBlocklistService.js
-      let request = aProgressEvent.target;
-      let status;
-      try {
-        // may throw (local file or timeout)
-        status = request.status;
-      }
-      catch (e) {
-        request = request.channel.QueryInterface(Ci.nsIRequest);
-        status = request.status;
-      }
-      // When status is 0 we don't have a valid channel.
-      let statusText = status ? request.statusText : "offline";
-      aOnError.call(aThis, statusText, null, this);
-    }
-  };
-  xhr.onload = function (aRequest) {
-    try {
-      let target = aRequest.target;
-      if (aLogger)
-        aLogger.DEBUG("Received response: " + target.responseText);
-      if (target.status < 200 || target.status >= 300) {
-        let errorText = target.responseText;
-        if (!errorText || /<(ht|\?x)ml\b/i.test(errorText))
-          errorText = target.statusText;
-        throw target.status + " - " + errorText;
-      }
-      if (aOnLoad)
-        aOnLoad.call(aThis, target.responseText, this);
-    } catch (e) {
-      Cu.reportError(e);
-      if (aOnError)
-        aOnError.call(aThis, e, aRequest.target.responseText, this);
-    }
-  };
-
-  if (aHeaders) {
-    aHeaders.forEach(function(header) {
-      xhr.setRequestHeader(header[0], header[1]);
-    });
-  }
-
-  // aPOSTData can be:
-  //  - a string: send it as is
-  //  - an array of parameters: encode as form values
-  //  - null/undefined: no POST data.
-  let POSTData = aPOSTData || "";
-  if (Array.isArray(POSTData)) {
-    xhr.setRequestHeader("Content-Type",
-                         "application/x-www-form-urlencoded; charset=utf-8");
-    POSTData = aPOSTData.map(function(p) p[0] + "=" + percentEncode(p[1]))
-                        .join("&");
-  }
-
-  if (aLogger)
-    aLogger.LOG("sending request to " + aUrl + " (POSTData = " + POSTData + ")");
-  xhr.send(POSTData);
-  return xhr;
-}
--- a/chat/modules/moz.build
+++ b/chat/modules/moz.build
@@ -10,11 +10,8 @@ EXTRA_JS_MODULES += [
     'imServices.jsm',
     'imSmileys.jsm',
     'imStatusUtils.jsm',
     'imThemes.jsm',
     'imXPCOMUtils.jsm',
     'jsProtoHelper.jsm',
     'socket.jsm',
 ]
-
-if not CONFIG['MOZ_THUNDERBIRD']:
-    EXTRA_JS_MODULES += ['http.jsm']
--- a/chat/protocols/twitter/twitter.js
+++ b/chat/protocols/twitter/twitter.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
-Cu.import("resource:///modules/http.jsm");
+Cu.import("resource://gre/modules/Http.jsm");
 Cu.import("resource:///modules/imServices.jsm");
 Cu.import("resource:///modules/imXPCOMUtils.jsm");
 Cu.import("resource:///modules/jsProtoHelper.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "_", function()
   l10nHelper("chrome://chat/locale/twitter.properties")
 );
 XPCOMUtils.defineLazyGetter(this, "_lang", function()
@@ -454,20 +454,26 @@ Account.prototype = {
     let bytes = [b.charCodeAt() for each (b in signatureBase)];
     hmac.update(bytes, bytes.length);
     let signature = hmac.finish(true);
 
     params.push(["oauth_signature", encodeURIComponent(signature)]);
 
     let authorization =
       "OAuth " + params.map(function (p) p[0] + "=\"" + p[1] + "\"").join(", ");
-    let headers = (aHeaders || []).concat([["Authorization", authorization]]);
 
-    return doXHRequest(url, headers, aPOSTData, aOnLoad, aOnError, aThis, null,
-                       this);
+    let options = {
+      headers: (aHeaders || []).concat([["Authorization", authorization]]),
+      postData: aPOSTData,
+      onLoad: aOnLoad.bind(this),
+      onError: aOnError.bind(this),
+      logger: {log: this.LOG.bind(this),
+               debug: this.DEBUG.bind(this)}
+    }
+    return httpRequest(url, options);
   },
   _parseURLData: function(aData) {
     let result = {};
     aData.split("&").forEach(function (aParam) {
       let [key, value] = aParam.split("=");
       result[key] = value;
     });
     return result;
deleted file mode 100644
--- a/mail/base/modules/http.jsm
+++ /dev/null
@@ -1,82 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const EXPORTED_SYMBOLS = ["doXHRequest", "percentEncode"];
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-// Strictly follow RFC 3986 when encoding URI components.
-function percentEncode(aString)
-  encodeURIComponent(aString).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
-
-function doXHRequest(aUrl, aHeaders, aPOSTData, aOnLoad, aOnError, aThis,
-                     aMethod, aLogger) {
-  let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
-              .createInstance(Ci.nsIXMLHttpRequest);
-  xhr.mozBackgroundRequest = true; // no error dialogs
-  xhr.open(aMethod || (aPOSTData ? "POST" : "GET"), aUrl);
-  xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS | // don't send cookies
-                          Ci.nsIChannel.LOAD_BYPASS_CACHE |
-                          Ci.nsIChannel.INHIBIT_CACHING;
-  xhr.onerror = function(aProgressEvent) {
-    if (aOnError) {
-      // adapted from toolkit/mozapps/extensions/nsBlocklistService.js
-      let request = aProgressEvent.target;
-      let status;
-      try {
-        // may throw (local file or timeout)
-        status = request.status;
-      }
-      catch (e) {
-        request = request.channel.QueryInterface(Ci.nsIRequest);
-        status = request.status;
-      }
-      // When status is 0 we don't have a valid channel.
-      let statusText = status ? request.statusText : "offline";
-      aOnError.call(aThis, statusText, null, this);
-    }
-  };
-  xhr.onload = function (aRequest) {
-    try {
-      let target = aRequest.target;
-      if (aLogger)
-        aLogger.DEBUG("Received response: " + target.responseText);
-      if (target.status < 200 || target.status >= 300) {
-        let errorText = target.responseText;
-        if (!errorText || /<(ht|\?x)ml\b/i.test(errorText))
-          errorText = target.statusText;
-        throw target.status + " - " + errorText;
-      }
-      if (aOnLoad)
-        aOnLoad.call(aThis, target.responseText, this);
-    } catch (e) {
-      Cu.reportError(e);
-      if (aOnError)
-        aOnError.call(aThis, e, aRequest.target.responseText, this);
-    }
-  };
-
-  if (aHeaders) {
-    aHeaders.forEach(function(header) {
-      xhr.setRequestHeader(header[0], header[1]);
-    });
-  }
-
-  // aPOSTData can be:
-  //  - a string: send it as is
-  //  - an array of parameters: encode as form values
-  //  - null/undefined: no POST data.
-  let POSTData = aPOSTData || "";
-  if (Array.isArray(POSTData)) {
-    xhr.setRequestHeader("Content-Type",
-                         "application/x-www-form-urlencoded; charset=utf-8");
-    POSTData = aPOSTData.map(function(p) p[0] + "=" + percentEncode(p[1]))
-                        .join("&");
-  }
-
-  if (aLogger)
-    aLogger.LOG("sending request to " + aUrl + " (POSTData = " + POSTData + ")");
-  xhr.send(POSTData);
-  return xhr;
-}
--- a/mail/base/modules/moz.build
+++ b/mail/base/modules/moz.build
@@ -6,17 +6,16 @@
 EXTRA_JS_MODULES += [
     'MailConsts.js',
     'MailUtils.js',
     'MsgHdrSyntheticView.js',
     'attachmentChecker.js',
     'dbViewWrapper.js',
     'distribution.js',
     'glodaWebSearch.js',
-    'http.jsm',
     'mailInstrumentation.js',
     'mailMigrator.js',
     'mailViewManager.js',
     'oauth.jsm',
     'quickFilterManager.js',
     'searchSpec.js',
     'sessionStoreManager.js',
     'summaryFrameManager.js',
--- a/mail/base/modules/oauth.jsm
+++ b/mail/base/modules/oauth.jsm
@@ -7,17 +7,17 @@
  * be shareable by various components that need Oauth, but some changes will
  * need to be made to support differences in OAuth usage.
  */
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
 var EXPORTED_SYMBOLS = ["OAuth"];
 
-Cu.import("resource:///modules/http.jsm");
+Cu.import("resource://gre/modules/Http.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/gloda/log4moz.js");
 
 function OAuth(aDisplayName, aBaseUri, aAuthUri, aAuthToken, aAuthTokenSecret,
                aAppKey, aAppSecret, aSignatureMethod, aTempCredentialsMethod,
                aAuthorizeMethod, aRequestCredentialsMethod)
 {
@@ -100,21 +100,16 @@ OAuth.prototype = {
       ["oauth_consumer_key", this.consumerKey],
       ["oauth_nonce", nonce],
       ["oauth_signature_method", this.signatureMethod],
       ["oauth_token", this.token],
       ["oauth_timestamp", Math.floor(((new Date()).getTime()) / 1000)],
       ["oauth_version", "1.0"]
     ]);
 
-    // encodeURI *AND* % escape characters that encodeURIComponent doesn't.
-    function percentEncode(aString)
-      encodeURIComponent(aString).replace(/\!|\*|\'|\(|\)/g, function(m)
-        ({"!": "%21", "*": "%2A", "'": "%27", "(": "%28", ")": "%29"}[m]))
-
     let dataParams = [];
     let url = /^https?:/.test(aUrl) ? aUrl : this.baseURI + aUrl;
     let urlSpec = url;
     let queryIndex = url.indexOf("?");
     if (queryIndex != -1) {
       urlSpec = url.slice(0, queryIndex);
       dataParams = url.slice(queryIndex + 1).split("&")
                       .map(function(p) p.split("=").map(percentEncode));
@@ -151,18 +146,24 @@ OAuth.prototype = {
     else {
       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
     }
 
     params.push(["oauth_signature", signature]);
 
     let authorization =
       "OAuth " + params.map(function (p) p[0] + "=\"" + p[1] + "\"").join(", ");
-    let headers = (aHeaders || []).concat([["Authorization", authorization]]);
-    return doXHRequest(url, headers, aPOSTData, aOnLoad, aOnError, aThis, aMethod);
+    let options = {
+      headers: (aHeaders || []).concat([["Authorization", authorization]]),
+      postData: aPOSTData,
+      method: aMethod,
+      onLoad: aOnLoad.bind(aThis),
+      onError: aOnError.bind(aThis)
+    };
+    return httpRequest(url, options);
   },
   _parseURLData: function(aData) {
     let result = {};
     aData.split("&").forEach(function (aParam) {
       let [key, value] = aParam.split("=");
       result[key] = value;
     });
     return result;
--- a/mail/components/cloudfile/content/Box/auth.js
+++ b/mail/components/cloudfile/content/Box/auth.js
@@ -4,17 +4,17 @@
 
 /* Modeled on browserRequest used by the OAuth module */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
-Cu.import("resource:///modules/http.jsm");
+Cu.import("resource://gre/modules/Http.jsm");
 Cu.import("resource:///modules/gloda/log4moz.js");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const wpl = Ci.nsIWebProgressListener;
 
 const kApiKey = "exs8m0agj1fa5728lxvn288ymz01dnzn";
 const kServerUrl = "https://www.box.com/api/1.0/rest";
 const kAuthUrl = "https://www.box.com/api/1.0/auth/";
@@ -209,19 +209,17 @@ var nsBoxAuth = {
       }
     }.bind(this);
     let ticketFailure = function(aException, aResponseText, aRequest) {
       log.error("Ticket acquisition error: " + aResponseText);
       failureCallback(aRequest);
     }.bind(this)
 
     // Request to get the ticket
-    doXHRequest(requestUrl,
-                null,
-                null,
-                ticketSuccess,
-                ticketFailure,
-                this,
-                "GET");
+    httpRequest(requestUrl, {
+                  onLoad: ticketSuccess,
+                  onError: ticketFailure,
+                  method: "GET"
+                });
     this.numTries++;
   }
 
 };
--- a/mail/components/cloudfile/nsBox.js
+++ b/mail/components/cloudfile/nsBox.js
@@ -9,17 +9,17 @@
  */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/gloda/log4moz.js");
 Cu.import("resource:///modules/cloudFileAccounts.js");
-Cu.import("resource:///modules/http.jsm");
+Cu.import("resource://gre/modules/Http.jsm");
 
 var gServerUrl = "https://www.box.com/api/1.0/rest";
 var gUploadUrl = "https://upload.box.net/api/1.0/upload/";
 var gSharingUrl = "https://www.box.com/shared/";
 
 const kApiKey = "exs8m0agj1fa5728lxvn288ymz01dnzn";
 
 XPCOMUtils.defineLazyServiceGetter(this, "gProtocolService",
@@ -318,23 +318,21 @@ nsBox.prototype = {
       this.log.info("Failed to acquire user info:" + aResponseText);
       this.log.error("user info failed, status = " + aRequest.status);
       this.log.error("response text = " + aResponseText);
       this.log.error("exception = " + aException);
       failureCallback();
     }.bind(this)
 
     // Request to get user info
-    doXHRequest(requestUrl,
-                null,
-                null,
-                accountInfoSuccess,
-                accountInfoFailure,
-                this,
-                "GET");
+    httpRequest(requestUrl, {
+                  onLoad: accountInfoSuccess,
+                  onError: accountInfoFailure,
+                  method: "GET"
+                });
   },
 
   /**
    * A private function that first ensures that the user is logged in, and then
    * retrieves the user's profile information.
    *
    * @param aSuccessCallback the function called on successful information
    *                         retrieval
@@ -443,23 +441,21 @@ nsBox.prototype = {
         this.log.error("Failed to create a new folder");
       }
     }.bind(this);
     let createFailure = function(aException, aResponseText, aRequest) {
       this.log.error("Failed to create a new folder: " + aRequest.status);
     }.bind(this);
 
     // Request to create the folder
-    doXHRequest(requestUrl,
-                null,
-                null,
-                createSuccess,
-                createFailure,
-                this,
-                "GET");
+    httpRequest(requestUrl, {
+                  onLoad: createSuccess,
+                  onError: createFailure,
+                  method: "GET"
+                });
   },
 
   /**
    * If a the user associated with this account key already has an account,
    * allows them to log in.
    *
    * @param aRequestObserver an nsIRequestObserver for monitoring the start and
    *                         stop states of the login procedure.
@@ -560,23 +556,21 @@ nsBox.prototype = {
       }
     }.bind(this);
     let deleteFailure = function(aException, aResponseText, aRequest) {
       this.log.error("Failed to delete file:" + aResponseText);
       aCallback.onStopRequest(null, null, Cr.NS_ERROR_FAILURE);
     }.bind(this);
 
     // Request to delete a file
-    doXHRequest(requestUrl,
-                null,
-                null,
-                deleteSuccess,
-                deleteFailure,
-                this,
-                "GET");
+    httpRequest(requestUrl, {
+                  onLoad: deleteSuccess,
+                  onError: deleteFailure,
+                  method: "GET"
+                });
   },
 
   _getUrlParameter: function nsBox_getAuthParameter(aUrl, aName)
   {
     var params = aUrl.substr(aUrl.indexOf("?") + 1);
     var pVal = "";
     params = params.split("&");
     for (var i=0; i<params.length; i++) {
--- a/mail/components/cloudfile/nsUbuntuOne.js
+++ b/mail/components/cloudfile/nsUbuntuOne.js
@@ -9,17 +9,17 @@
  */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/http.jsm");
+Cu.import("resource://gre/modules/Http.jsm");
 Cu.import("resource:///modules/oauth.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/gloda/log4moz.js");
 Cu.import("resource:///modules/cloudFileAccounts.js");
 
 const kBadAccessToken = 401;
 const kAuthSecretRealm = "Ubuntu One Auth Secret";
 const kConsumerKeyRealm = "Ubuntu One Consumer Key";
@@ -466,51 +466,53 @@ nsUbuntuOne.prototype = {
     let credentials = "Basic " + btoa(this._emailAddress + ":" + password);
     let dnsService = Cc["@mozilla.org/network/dns-service;1"]
       .getService(Components.interfaces.nsIDNSService);
     let tokenName = "Ubuntu One @ " + dnsService.myHostName + " [thunderbird]";
     let newTokenUrl = gSsoUrl + "?ws.op=authenticate&token_name=" +
       encodeURIComponent(tokenName);
 
     this.log.info("Requesting Authentication token");
-    doXHRequest(
-      newTokenUrl, [["Authorization", credentials]], "",
-      function(aResponseText, aRequest) {
-        this.log.info("Retrieved a new token from SSO");
-        let tokenInfo = JSON.parse(aResponseText);
+    httpRequest(newTokenUrl, {
+        headers: [["Authorization", credentials]],
+        onLoad: function(aResponseText, aRequest) {
+          this.log.info("Retrieved a new token from SSO");
+          let tokenInfo = JSON.parse(aResponseText);
 
-        // We need to tell Ubuntu One to pull the token from the SSO
-        // service now.
-        this._connection = new OAuth(this.displayName, null, null,
-                                     tokenInfo.token, tokenInfo.token_secret,
-                                     tokenInfo.consumer_key,
-                                     tokenInfo.consumer_secret,
-                                     "PLAINTEXT");
-        this._connection.signAndSend(
-          gSsoPingUrl, [], "POST", "",
-          function(aResponseText, aRequest) {
-            this.log.info("Token transferred to Ubuntu One: " + aResponseText);
-            // Now that the token has successfully been transferred,
-            // save it locally.
-            this._cachedAuthToken = tokenInfo.token;
-            this._cachedAuthSecret = tokenInfo.token_secret;
-            this._cachedConsumerKey = tokenInfo.consumer_key;
-            this._cachedConsumerSecret = tokenInfo.consumer_secret;
-            successCallback();
-          }.bind(this),
-          function(aException, aResponseText, aRequest) {
-            this.log.info("Failed to transfer access token to Ubuntu One:" +
-                          aResponseText);
-            failureCallback();
-          }.bind(this), this);
-      }.bind(this),
-      function(aException, aResponseText, aRequest) {
-        this.log.info("Failed to acquire an access token:" + aResponseText);
-        failureCallback();
-      }.bind(this), this, "GET");
+          // We need to tell Ubuntu One to pull the token from the SSO
+          // service now.
+          this._connection = new OAuth(this.displayName, null, null,
+                                       tokenInfo.token, tokenInfo.token_secret,
+                                       tokenInfo.consumer_key,
+                                       tokenInfo.consumer_secret,
+                                       "PLAINTEXT");
+          this._connection.signAndSend(
+            gSsoPingUrl, [], "POST", "",
+            function(aResponseText, aRequest) {
+              this.log.info("Token transferred to Ubuntu One: " + aResponseText);
+              // Now that the token has successfully been transferred,
+              // save it locally.
+              this._cachedAuthToken = tokenInfo.token;
+              this._cachedAuthSecret = tokenInfo.token_secret;
+              this._cachedConsumerKey = tokenInfo.consumer_key;
+              this._cachedConsumerSecret = tokenInfo.consumer_secret;
+              successCallback();
+            }.bind(this),
+            function(aException, aResponseText, aRequest) {
+              this.log.info("Failed to transfer access token to Ubuntu One:" +
+                            aResponseText);
+              failureCallback();
+            }.bind(this), this);
+        }.bind(this),
+        onError: function(aException, aResponseText, aRequest) {
+          this.log.info("Failed to acquire an access token:" + aResponseText);
+          failureCallback();
+        }.bind(this),
+        method: "GET"
+      });
   },
 
   /**
    * logon to the Ubuntu One account.
    *
    * @param successCallback - called if logon is successful
    * @param failureCallback - called back on error.
    * @param aWithUI if false, logon fails if it would have needed to put up UI.