add ui for cloud files, changes by mconley, squib, andreas, r=mconley, squib, etc, bug 698925
authorDavid Bienvenu <bienvenu@nventure.com>
Mon, 12 Mar 2012 16:15:30 -0700
changeset 11072 42e362b0c7c6ae8185daa6e4bd033c9193d675b1
parent 11071 0d9014f1f95325841c3cd01d7183b845c8043be5
child 11073 cccf1c19d8a244e6cd55272246ba8fd812edcd9a
push id463
push userbugzilla@standard8.plus.com
push dateTue, 24 Apr 2012 17:34:51 +0000
treeherdercomm-beta@e53588e8f7b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley, squib, etc, bug
bugs698925
add ui for cloud files, changes by mconley, squib, andreas, r=mconley, squib, etc, bug 698925
mail/app/profile/all-thunderbird.js
mail/base/content/browserRequest.js
mail/base/content/browserRequest.xul
mail/base/content/mailCore.js
mail/base/content/mailWidgets.xml
mail/base/jar.mn
mail/base/modules/Makefile.in
mail/base/modules/http.jsm
mail/base/modules/oauth.jsm
mail/components/Makefile.in
mail/components/compose/content/MsgComposeCommands.js
mail/components/compose/content/bigFileObserver.js
mail/components/compose/content/cloudAttachmentLinkManager.js
mail/components/compose/content/messengercompose.xul
mail/components/compose/jar.mn
mail/components/preferences/applications.js
mail/components/preferences/applications.xul
mail/components/preferences/preferences.xul
mail/installer/package-manifest.in
mail/locales/en-US/chrome/messenger/cloudfile/Dropbox/management.dtd
mail/locales/en-US/chrome/messenger/cloudfile/YouSendIt/management.dtd
mail/locales/en-US/chrome/messenger/cloudfile/YouSendIt/settings.dtd
mail/locales/en-US/chrome/messenger/cloudfile/addAccountDialog.dtd
mail/locales/en-US/chrome/messenger/cloudfile/management.dtd
mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
mail/locales/en-US/chrome/messenger/messengercompose/messengercompose.dtd
mail/locales/en-US/chrome/messenger/preferences/applications.dtd
mail/locales/jar.mn
mail/themes/gnomestripe/jar.mn
mail/themes/gnomestripe/mail/browserRequest.css
mail/themes/gnomestripe/mail/compose/messengercompose.css
mail/themes/gnomestripe/mail/preferences/applications.css
mail/themes/gnomestripe/mail/preferences/preferences.css
mail/themes/pinstripe/jar.mn
mail/themes/pinstripe/mail/browserRequest.css
mail/themes/pinstripe/mail/compose/messengercompose.css
mail/themes/pinstripe/mail/preferences/applications.css
mail/themes/pinstripe/mail/preferences/preferences.css
mail/themes/qute/jar.mn
mail/themes/qute/mail/browserRequest.css
mail/themes/qute/mail/preferences/applications.css
mail/themes/qute/mail/preferences/preferences.css
--- a/mail/app/profile/all-thunderbird.js
+++ b/mail/app/profile/all-thunderbird.js
@@ -477,16 +477,22 @@ pref("toolbar.customization.usesheet", f
 // Check for missing attachments?
 pref("mail.compose.attachment_reminder", true);
 // Words that should trigger a missing attachments warning.
 pref("mail.compose.attachment_reminder_keywords", "chrome://messenger/locale/messengercompose/composeMsgs.properties");
 // When no action is taken on the inline missing attachement notification,
 // show an alert on send?
 pref("mail.compose.attachment_reminder_aggressive", true);
 
+// True if the user should be notified when attaching big files
+pref("mail.compose.big_attachments.notify", true);
+// Size (in kB) to automatically prompt for conversion of attachments to
+// cloud links
+pref("mail.compose.big_attachments.threshold_kb", 1024);
+
 // Set this to false to prevent instrumentation from happening, e.g., user
 // has opted out, or an enterprise wants to disable it from the git go.
 pref("mail.instrumentation.askUser", true);
 pref("mail.instrumentation.userOptedIn", false);
 pref("mail.instrumentation.postUrl", "https://www.mozillamessaging.com/instrumentation");
 // not sure how this will be formatted - would be nice to make it extensible.
 pref("mail.instrumentation.lastNotificationSent", "");
 
@@ -762,8 +768,12 @@ pref("browser.search.order.3", "chrome:/
 // XXX Don't update yet, until we've verified how that affects us.
 pref("browser.search.update", false);
 
 // Check whether we need to perform engine updates every 6 hours
 pref("browser.search.update.interval", 21600);
 
 // Disable remote debugging protocol logging
 pref("devtools.debugger.log", false);
+
+// BigFiles
+pref("mail.cloud_files.enabled", true);
+pref("mail.cloud_files.inserted_urls.footer.link", "http://www.getthunderbird.com");
new file mode 100644
--- /dev/null
+++ b/mail/base/content/browserRequest.js
@@ -0,0 +1,113 @@
+/* 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 wpl = Components.interfaces.nsIWebProgressListener;
+
+var reporterListener = {
+  _isBusy: false,
+  get securityButton() {
+    delete this.securityButton;
+    return this.securityButton = document.getElementById("security-button");
+  },
+
+  QueryInterface: function(aIID) {
+    if (aIID.equals(Components.interfaces.nsIWebProgressListener)   ||
+        aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
+        aIID.equals(Components.interfaces.nsISupports))
+      return this;
+    throw Components.results.NS_NOINTERFACE;
+  },
+
+  onStateChange: function(/*in nsIWebProgress*/ aWebProgress,
+                     /*in nsIRequest*/ aRequest,
+                     /*in unsigned long*/ aStateFlags,
+                     /*in nsresult*/ aStatus) {
+  },
+
+  onProgressChange: function(/*in nsIWebProgress*/ aWebProgress,
+                        /*in nsIRequest*/ aRequest,
+                        /*in long*/ aCurSelfProgress,
+                        /*in long */aMaxSelfProgress,
+                        /*in long */aCurTotalProgress,
+                        /*in long */aMaxTotalProgress) {
+  },
+
+  onLocationChange: function(/*in nsIWebProgress*/ aWebProgress,
+                        /*in nsIRequest*/ aRequest,
+                        /*in nsIURI*/ aLocation) {
+    document.getElementById("headerMessage").textContent = aLocation.spec;
+  },
+
+  onStatusChange: function(/*in nsIWebProgress*/ aWebProgress,
+                      /*in nsIRequest*/ aRequest,
+                      /*in nsresult*/ aStatus,
+                      /*in wstring*/ aMessage) {
+  },
+
+  onSecurityChange: function(/*in nsIWebProgress*/ aWebProgress,
+                        /*in nsIRequest*/ aRequest,
+                        /*in unsigned long*/ aState) {
+    const wpl_security_bits = wpl.STATE_IS_SECURE |
+                              wpl.STATE_IS_BROKEN |
+                              wpl.STATE_IS_INSECURE |
+                              wpl.STATE_SECURE_HIGH |
+                              wpl.STATE_SECURE_MED |
+                              wpl.STATE_SECURE_LOW;
+    var browser = document.getElementById("requestFrame");
+    var level;
+
+    switch (aState & wpl_security_bits) {
+      case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_HIGH:
+        level = "high";
+        break;
+      case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_MED:
+      case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_LOW:
+        level = "low";
+        break;
+      case wpl.STATE_IS_BROKEN:
+        level = "broken";
+        break;
+    }
+    if (level) {
+      this.securityButton.setAttribute("level", level);
+      this.securityButton.hidden = false;
+    } else {
+      this.securityButton.hidden = true;
+      this.securityButton.removeAttribute("level");
+    }
+    this.securityButton.setAttribute("tooltiptext",
+                                     browser.securityUI.tooltipText);
+  }
+}
+
+function cancelRequest()
+{
+  reportUserClosed();
+  window.close();
+}
+
+function reportUserClosed()
+{
+  let request = window.arguments[0].wrappedJSObject;
+  request.cancelled();
+}
+
+function loadRequestedUrl()
+{
+  let request = window.arguments[0].wrappedJSObject;
+  document.getElementById("headerMessage").textContent = request.promptText;
+  let account = request.account;
+  if (request.iconURI != "")
+    document.getElementById("headerImage").src = request.iconURI;
+
+  var browser = document.getElementById("requestFrame");
+  browser.addProgressListener(reporterListener,
+                              Components.interfaces.nsIWebProgress.NOTIFY_ALL);
+  var url = request.url;
+  if (url != "") {
+    browser.setAttribute("src", url);
+    document.getElementById("headerMessage").textContent = url;
+  }
+  request.loaded(window, browser.webProgress);
+}
new file mode 100644
--- /dev/null
+++ b/mail/base/content/browserRequest.xul
@@ -0,0 +1,33 @@
+<?xml version="1.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/.
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/browserRequest.css" type="text/css"?>
+
+<!DOCTYPE window>
+<window id="browserRequest"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        buttons=","
+        onload="loadRequestedUrl()"
+        onclose="reportUserClosed()"
+        title=""
+        width="800"
+        height="500"
+        orient="vertical">
+
+  <script type="application/javascript" src="chrome://messenger/content/browserRequest.js"/>
+
+  <keyset id="mainKeyset">
+    <key id="key_close" key="w" modifiers="accel" oncommand="cancelRequest()"/>
+    <key id="key_close2"  keycode="VK_ESCAPE" oncommand="cancelRequest()"/>
+  </keyset>
+  <hbox id="header">
+  <hbox id="addressbox" flex="1" disabled="true">
+    <image id="security-button" src="chrome://messenger/skin/icons/mailicon32.png"/>
+    <description id="headerMessage"/>
+  </hbox>
+  </hbox>
+  <browser type="content" disablehistory="true" src="about:blank" id="requestFrame" flex="1"/>
+</window>
--- a/mail/base/content/mailCore.js
+++ b/mail/base/content/mailCore.js
@@ -623,17 +623,20 @@ function SanitizeAttachmentDisplayName(a
  * Create a TransferData object for a message attachment, either from the
  * message reader or the composer.
  *
  * @param aAttachment the attachment object
  * @return the TransferData
  */
 function CreateAttachmentTransferData(aAttachment)
 {
-  if (aAttachment.contentType == "text/x-moz-deleted")
+  // For now, disallow drag-and-drop on cloud attachments. In the future, we
+  // should allow this.
+  if (aAttachment.contentType == "text/x-moz-deleted" ||
+      aAttachment.sendViaCloud)
     return;
 
   var name = aAttachment.name || aAttachment.displayName;
 
   var data = new TransferData();
   if (aAttachment.url && name)
   {
     // Only add type/filename info for non-file URLs that don't already
--- a/mail/base/content/mailWidgets.xml
+++ b/mail/base/content/mailWidgets.xml
@@ -221,16 +221,29 @@
         ]]></body>
       </method>
 
       <!-- Get the preferred height (the height that would allow us to fit
            everything without scrollbars) of the attachmentlist's boxObject. -->
       <property name="preferredHeight" readonly="true"
                 onget="return this.scrollbox.scrollHeight - this.scrollbox.clientHeight + this.boxObject.height;"/>
 
+      <!-- Find the attachmentitem node for the specified nsIMsgAttachment. -->
+      <method name="findItemForAttachment">
+        <parameter name="aAttachment"/>
+        <body><![CDATA[
+          for (let i = 0; i < this.itemCount; i++) {
+            let item = this.getItemAtIndex(i);
+            if (item.attachment == aAttachment)
+              return item;
+          }
+          return null;
+        ]]></body>
+      </method>
+
       <!-- ///////////////// private members ///////////////// -->
 
       <property name="children" readonly="true"
                 onget="return Array.slice(this.getElementsByTagName('attachmentitem'));"/>
       <property name="scrollbox" readonly="true"
                 onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'scrollbox');"/>
 
       <method name="_fireOnSelect">
@@ -360,16 +373,30 @@
         ]]></getter>
         <setter><![CDATA[
           this.setAttribute("imagesize", val);
           this._updateImage();
           return val;
         ]]></setter>
       </property>
 
+      <property name="image">
+        <getter><![CDATA[
+          return this.getAttribute("image");
+        ]]></getter>
+        <setter><![CDATA[
+          if (val)
+            this.setAttribute("image", val);
+          else
+            this.removeAttribute("image");
+          this._updateImage();
+          return val;
+        ]]></setter>
+      </property>
+
       <method name="_updateImage">
         <body><![CDATA[
           if (!this.hasAttribute("image")) {
             let icon = document.getAnonymousElementByAttribute(this, "anonid",
                                                                "icon");
             let attr = "image"+this.imageSize;
             if (this.hasAttribute(attr))
               icon.setAttribute("src", this.getAttribute(attr));
--- a/mail/base/jar.mn
+++ b/mail/base/jar.mn
@@ -112,16 +112,18 @@ messenger.jar:
     content/messenger/glodaFacetView.css            (content/glodaFacetView.css)
     content/messenger/glodaFacetBindings.css        (content/glodaFacetBindings.css)
     content/messenger/glodaFacetBindings.xml        (content/glodaFacetBindings.xml)
     content/messenger/glodaFacetVis.js              (content/glodaFacetVis.js)
     content/messenger/quickFilterBar.xul            (content/quickFilterBar.xul)
     content/messenger/quickFilterBar.js             (content/quickFilterBar.js)
     content/messenger/quickFilterBar.css            (content/quickFilterBar.css)
     content/messenger/downloadsOverlay.xul          (content/downloadsOverlay.xul)
+    content/messenger/browserRequest.js             (content/browserRequest.js)
+    content/messenger/browserRequest.xul            (content/browserRequest.xul)
 *   content/messenger/safeMode.xul                  (content/safeMode.xul)
     content/messenger/safeMode.js                   (content/safeMode.js)
 # the following files are mail-specific overrides
 *+  content/messenger/license.html                  (/mozilla/toolkit/content/license.html)
 % override chrome://global/content/license.html chrome://messenger/content/license.html
 
 comm.jar:
 % content communicator %content/communicator/
--- a/mail/base/modules/Makefile.in
+++ b/mail/base/modules/Makefile.in
@@ -52,12 +52,14 @@ EXTRA_JS_MODULES = \
   quickFilterManager.js \
   searchSpec.js \
   MsgHdrSyntheticView.js \
   sessionStoreManager.js \
   summaryFrameManager.js \
   mailMigrator.js \
   mailInstrumentation.js \
   glodaWebSearch.js \
+  oauth.jsm \
+  http.jsm \
   distribution.js \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/mail/base/modules/http.jsm
@@ -0,0 +1,61 @@
+/* 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/. */
+
+var EXPORTED_SYMBOLS = ["doXHRequest"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+function doXHRequest(aUrl, aHeaders, aMethod, aPOSTData, aOnLoad, aOnError, aThis) {
+  var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+              .createInstance(Ci.nsIXMLHttpRequest);
+  xhr.mozBackgroundRequest = true; // no error dialogs
+  xhr.open(aMethod, 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 (target.status != 200) {
+        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]);
+    });
+  }
+
+  xhr.send(aPOSTData);
+  return xhr;
+}
new file mode 100644
--- /dev/null
+++ b/mail/base/modules/oauth.jsm
@@ -0,0 +1,291 @@
+/* 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/. */
+
+/**
+ * This js module does OAuth 1.0 authentication. It was originally intended to
+ * 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/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)
+{
+  this._userInfo = {};
+  this.displayName = aDisplayName;
+  this.baseURI = aBaseUri;
+  this.authURI = aAuthUri;
+  this.token = aAuthToken;
+  this.tokenSecret = aAuthTokenSecret;
+  this.consumerKey = aAppKey;
+  this.consumerSecret = aAppSecret;
+  this.log = Log4Moz.getConfiguredLogger("TBOAuth");
+}
+
+OAuth.prototype = {
+  consumerKey: "",
+  consumerSecret: "",
+  completionURI: "http://oauthcallback.local/",
+  baseURI: "",
+  token : "",
+  tokenSecret : "",
+  connectSuccessCallback : null,
+  connectFailureCallback : null,
+  /**
+   * Start the OAuth connection process, if not connected.
+   *
+   * @param aSuccess success callback, called synchronously if we are already
+                     connected, or the connection succeeds.
+   * @param aFailure failure callback.
+   */
+  connect: function(aSuccess, aFailure, aWithUI) {
+    if (this.connected || this.connecting)
+      return;
+
+    this._enabled = true;
+    // Get a new token if needed...
+    if (!this.token || !this.tokenSecret) {
+      if (!aWithUI) {
+        aFailure();
+        return;
+      }
+      this.connectSuccessCallback = aSuccess;
+      this.connectFailureCallback = aFailure;
+      this.log.info("don't think we have a token or token secret");
+      this.requestToken();
+      return;
+    }
+
+    this.log.info("Connecting using existing token");
+    aSuccess();
+  },
+  /**
+   * Sign a request and send it, using the oauth connection.
+   * 
+   * @param aUrl Url to open
+   * @param aHeaders Additional headers to send
+   * @param aMethod "Get"/"Post"/"Put"
+   * @param aPOSTData Data to post
+   * @param aOnLoad Success callback
+   * @param aOnError Error callback
+   * @param aThis passed as first param to success and error callbacks
+   * @param aOAuthParams additional params to pass to request
+   */
+  signAndSend: function(aUrl, aHeaders, aMethod, aPOSTData, aOnLoad, aOnError, aThis,
+                        aOAuthParams) {
+    this.log.info("sign and send aUrl = " + aUrl);
+    const kChars =
+      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
+    const kNonceLength = 6;
+    let nonce = "";
+    for (let i = 0; i < kNonceLength; ++i)
+      nonce += kChars[Math.floor(Math.random() * kChars.length)];
+
+    let params = (aOAuthParams || []).concat([
+      ["oauth_consumer_key", this.consumerKey],
+      ["oauth_nonce", nonce],
+      ["oauth_signature_method", "HMAC-SHA1"],
+      ["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));
+    }
+    this.log.info("in sign and send url = " + url + "\nurlSpec = " + urlSpec);
+    this.log.info("dataParams = " + dataParams);
+    let signatureKey = this.consumerSecret + "&" + this.tokenSecret;
+    let signatureBase =
+      aMethod + "&" + encodeURIComponent(urlSpec) + "&" +
+      params.concat(dataParams)
+            .sort(function(a,b) (a[0] < b[0]) ? -1 : (a[0] > b[0]) ? 1 : 0)
+            .map(function(p) p.map(percentEncode).join("%3D"))
+            .join("%26");
+
+    this.log.info("sig base = " + signatureBase);
+    let keyFactory = Cc["@mozilla.org/security/keyobjectfactory;1"]
+                     .getService(Ci.nsIKeyObjectFactory);
+    let hmac =
+      Cc["@mozilla.org/security/hmac;1"].createInstance(Ci.nsICryptoHMAC);
+    hmac.init(hmac.SHA1,
+              keyFactory.keyFromString(Ci.nsIKeyObject.HMAC, signatureKey));
+    // No UTF-8 encoding, special chars are already escaped.
+    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, aMethod, aPOSTData, aOnLoad, aOnError, aThis);
+  },
+  _parseURLData: function(aData) {
+    let result = {};
+    aData.split("&").forEach(function (aParam) {
+      let [key, value] = aParam.split("=");
+      result[key] = value;
+    });
+    return result;
+  },
+
+  _streamingRequest: null,
+  _pendingData: "",
+  _receivedLength: 0,
+  onDataAvailable: function(aRequest) {
+    let text = aRequest.target.responseText;
+    let newText = this._pendingData + text.slice(this._receivedLength);
+    this.log.info("Received data: " + newText);
+    let messages = newText.split(/\r\n?/);
+  },
+
+  requestToken: function() {
+    let oauthParams =
+      [["oauth_callback", encodeURIComponent(this.completionURI)]];
+    this.signAndSend("oauth/request_token",
+                     null, "POST", null,
+                     this.onRequestTokenReceived,
+                     this.connectFailureCallback, this,
+                     oauthParams);
+  },
+  onRequestTokenReceived: function(aData) {
+    this.log.info("Received request token.");
+    let data = this._parseURLData(aData);
+    if (!data.oauth_token || !data.oauth_token_secret) {
+      this.log.info("didn't get request token");
+      this.connectFailureCallback();
+      return;
+    }
+    this.token = data.oauth_token;
+    this.tokenSecret = data.oauth_token_secret;
+
+    this.requestAuthorization();
+  },
+  requestAuthorization: function() {
+    this.log.info("requesting oauth");
+    const url = this.authURI + "oauth/authorize?oauth_token=";
+    this._browserRequest =
+      { promptText : "auth prompt",
+        account: this,
+        url: url + this.token + "&oauth_callback=" + this.completionURI,
+        _active: true,
+        iconURI : "",  // would be nice to set this from the oauth user...
+        cancelled: function() {
+          if (!this._active)
+            return;
+          this.account.log.info("auth cancelled");
+          this.account.finishAuthorizationRequest();
+          this.account.onAuthorizationReceived(null);
+          this.connectFailureCallback();
+        // ### auth cancelled.
+        },
+        loaded: function(aWindow, aWebProgress) {
+          if (!this._active)
+            return;
+
+          this._listener = {
+            QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                                   Ci.nsISupportsWeakReference]),
+            _cleanUp: function() {
+              this.webProgress.removeProgressListener(this);
+              this.window.close();
+              delete this.window;
+            },
+            _checkForRedirect: function(aURL) {
+              if (aURL.indexOf(this._parent.completionURI) != 0)
+                return;
+
+              this._parent.finishAuthorizationRequest();
+              this._parent.onAuthorizationReceived(aURL);
+            },
+            onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
+              const wpl = Ci.nsIWebProgressListener;
+              if (aStateFlags & (wpl.STATE_START | wpl.STATE_IS_NETWORK))
+                this._checkForRedirect(aRequest.name);
+            },
+            onLocationChange: function(aWebProgress, aRequest, aLocation) {
+              this._checkForRedirect(aLocation.spec);
+            },
+            onProgressChange: function() {},
+            onStatusChange: function() {},
+            onSecurityChange: function() {},
+
+            window: aWindow,
+            webProgress: aWebProgress,
+            _parent: this.account
+          };
+        aWebProgress.addProgressListener(this._listener,
+                                         Ci.nsIWebProgress.NOTIFY_ALL);
+      },
+    };
+     this.wrappedJSObject = this._browserRequest;
+     Services.ww.openWindow(null,
+                            "chrome://messenger/content/browserRequest.xul",
+                             null, "chrome,centerscreen,width=980px,height=600px", this);
+  },
+  finishAuthorizationRequest: function() {
+    if (!("_browserRequest" in this))
+      return;
+
+    this._browserRequest._active = false;
+    if ("_listener" in this._browserRequest)
+      this._browserRequest._listener._cleanUp();
+    delete this._browserRequest;
+  },
+  onAuthorizationReceived: function(aData) {
+    this.log.info("authorization received");
+    this.requestAccessToken();
+  },
+  requestAccessToken: function() {
+    this.signAndSend("oauth/access_token", null, "POST", null,
+                     this.onAccessTokenReceived, this.connectFailureCallback, this,
+                     null);
+  },
+  onAccessTokenReceived: function(aData) {
+    this.log.info("Received access token. urlData = " + aData);
+    let result = this._parseURLData(aData);
+
+    this.token = result.oauth_token;
+    this.tokenSecret = result.oauth_token_secret;
+    this.connectSuccessCallback();
+  },
+
+
+  cleanUp: function() {
+    this.finishAuthorizationRequest();
+    if (this._pendingRequests.length != 0) {
+      for each (let request in this._pendingRequests)
+        request.abort();
+      delete this._pendingRequests;
+    }
+    if (this._streamingRequest) {
+      this._streamingRequest.abort();
+      delete this._streamingRequest;
+    }
+  },
+  unInit: function() {
+    this.cleanUp();
+  },
+};
+
--- a/mail/components/Makefile.in
+++ b/mail/components/Makefile.in
@@ -39,17 +39,17 @@ DEPTH   = ../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 # Only Mac and Windows have search integration components, but we include at
 # least one module from search/ on all platforms
-DIRS    = compose preferences addrbook migration activity search about-support wintaskbar newmailaccount
+DIRS    = compose cloudfile preferences addrbook migration activity search about-support wintaskbar newmailaccount
 
 ifneq (,$(filter windows gtk2 cocoa, $(MOZ_WIDGET_TOOLKIT)))
 DIRS += shell
 endif
 
 ifdef MOZ_SAFE_BROWSING
 DIRS += phishing 
 endif
--- a/mail/components/compose/content/MsgComposeCommands.js
+++ b/mail/components/compose/content/MsgComposeCommands.js
@@ -43,16 +43,18 @@ Components.utils.import("resource:///mod
 Components.utils.import("resource://gre/modules/PluralForm.jsm");
 Components.utils.import("resource:///modules/attachmentChecker.js");
 
 Components.utils.import("resource:///modules/MailUtils.js");
 Components.utils.import("resource:///modules/errUtils.js");
 Components.utils.import("resource:///modules/iteratorUtils.jsm");
 Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm")
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource:///modules/cloudFileAccounts.js");
 
 /**
  * interfaces
  */
 const nsIMsgCompDeliverMode = Components.interfaces.nsIMsgCompDeliverMode;
 const nsIMsgCompSendFormat = Components.interfaces.nsIMsgCompSendFormat;
 const nsIMsgCompConvertible = Components.interfaces.nsIMsgCompConvertible;
 const nsIMsgCompType = Components.interfaces.nsIMsgCompType;
@@ -100,16 +102,17 @@ var gSavedSendNowKey;
 var gSendFormat;
 
 var gMsgIdentityElement;
 var gMsgAddressingWidgetTreeElement;
 var gMsgSubjectElement;
 var gMsgAttachmentElement;
 var gMsgHeadersToolbarElement;
 var gRemindLater;
+var gComposeType;
 
 // i18n globals
 var gSendDefaultCharset;
 var gCharsetTitle;
 var gCharsetConvertManager;
 var _gComposeBundle;
 function getComposeBundle() {
   // That one has to be lazy. Getting a reference to an element with a XBL
@@ -130,16 +133,17 @@ var gDSNOptionChanged;
 var gAttachVCardOptionChanged;
 
 var gMailSession;
 var gAutoSaveInterval;
 var gAutoSaveTimeout;
 var gAutoSaveKickedIn;
 var gEditingDraft;
 var gAttachmentsSize;
+var gNumUploadingAttachments;
 
 const kComposeAttachDirPrefName = "mail.compose.attach.dir";
 
 function InitializeGlobalVariables()
 {
   gAccountManager = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Components.interfaces.nsIMsgAccountManager);
   gIOService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
   gPromptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
@@ -166,16 +170,17 @@ function InitializeGlobalVariables()
   gHideMenus = false;
   gRemindLater = false;
 
   gLastWindowToHaveFocus = null;
   gReceiptOptionChanged = false;
   gDSNOptionChanged = false;
   gAttachVCardOptionChanged = false;
   gAttachmentsSize = 0;
+  gNumUploadingAttachments = 0;
 }
 InitializeGlobalVariables();
 
 function ReleaseGlobalVariables()
 {
   gAccountManager = null;
   gIOService = null;
   gPromptService = null;
@@ -427,16 +432,31 @@ var defaultController = {
       isEnabled: function() {
         return !gWindowLocked;
       },
       doCommand: function() {
         AttachFile();
       }
     },
 
+    cmd_attachCloud: {
+      isEnabled: function() {
+        // Hide the command entirely if there are no cloud accounts or
+        // the feature is disbled.
+        let cmd = document.getElementById("cmd_attachCloud");
+        cmd.hidden = !Services.prefs.getBoolPref("mail.cloud_files.enabled")
+                     || (cloudFileAccounts.accounts.length == 0);
+        return !cmd.hidden;
+      },
+      doCommand: function() {
+        // We should never actually call this, since the <command> node calls
+        // a different function.
+      }
+    },
+
     cmd_attachPage: {
       isEnabled: function() {
         return !gWindowLocked;
       },
       doCommand: function() {
         AttachPage();
       }
     },
@@ -483,47 +503,47 @@ var defaultController = {
       },
       doCommand: function() {
         SaveAsTemplate();
       }
     },
 
     cmd_sendButton: {
       isEnabled: function() {
-        return !gWindowLocked;
+        return !gWindowLocked && !gNumUploadingAttachments;
       },
       doCommand: function() {
         if (gIOService && gIOService.offline)
           SendMessageLater();
         else
           SendMessage();
       }
     },
 
     cmd_sendNow: {
       isEnabled: function() {
-        return !gWindowLocked && !gIsOffline;
+        return !gWindowLocked && !gIsOffline && !gNumUploadingAttachments;
       },
       doCommand: function() {
         SendMessage();
       }
     },
 
     cmd_sendLater: {
       isEnabled: function() {
-        return !gWindowLocked;
+        return !gWindowLocked && !gNumUploadingAttachments;
       },
       doCommand: function() {
         SendMessageLater();
       }
     },
 
     cmd_sendWithCheck: {
       isEnabled: function() {
-        return !gWindowLocked;
+        return !gWindowLocked && !gNumUploadingAttachments;
       },
       doCommand: function() {
         SendMessageWithCheck();
       }
     },
 
     cmd_printSetup: {
       isEnabled: function() {
@@ -649,16 +669,81 @@ var attachmentBucketController = {
     cmd_renameAttachment: {
       isEnabled: function() {
         return MessageGetNumSelectedAttachments() == 1;
       },
       doCommand: function() {
         RenameSelectedAttachment();
       }
     },
+
+    cmd_convertCloud: {
+      isEnabled: function() {
+        // Hide the command entirely if there are no cloud accounts.
+        let cmd = document.getElementById("cmd_convertCloud");
+        cmd.hidden = (cloudFileAccounts.accounts.length == 0);
+        if (cmd.hidden)
+          return false;
+
+        let bucket = document.getElementById("attachmentBucket");
+        for (let [,item] in Iterator(bucket.selectedItems)) {
+          if (item.uploading)
+            return false;
+        }
+        return true;
+      },
+      doCommand: function() {
+        // We should never actually call this, since the <command> node calls
+        // a different function.
+      }
+    },
+
+    cmd_convertAttachment: {
+      isEnabled: function() {
+        let bucket = document.getElementById("attachmentBucket");
+        for (let [,item] in Iterator(bucket.selectedItems)) {
+          if (item.uploading)
+            return false;
+        }
+        return true;
+      },
+      doCommand: function() {
+        convertSelectedToRegularAttachment();
+      }
+    },
+
+    cmd_cancelUpload: {
+      isEnabled: function() {
+        let bucket = document.getElementById("attachmentBucket");
+        for (let [,item] in Iterator(bucket.selectedItems)) {
+          if (item.uploading)
+            return true;
+        }
+
+        // Hide the command entirely if the selected attachments aren't cloud
+        // files.
+        // FOr some reason, the hidden property isn't propagating from the cmd
+        // to the menuitem.
+        let menuitem = document.getElementById("context_cancelUpload");
+        menuitem.hidden = true;
+        return false;
+      },
+      doCommand: function() {
+        let fileHandler = Services.io.getProtocolHandler("file")
+                                  .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
+
+        let bucket = document.getElementById("attachmentBucket");
+        for (let [,item] in Iterator(bucket.selectedItems)) {
+          if (item.uploading) {
+            let file = fileHandler.getFileFromURLSpec(item.attachment.url);
+            item.cloudProvider.cancelFileUpload(file);
+          }
+        }
+      },
+    },
   },
 
   supportsCommand: function(aCommand) {
     return (aCommand in this.commands);
   },
 
   isCommandEnabled: function(aCommand) {
     if (!this.supportsCommand(aCommand))
@@ -822,16 +907,442 @@ function updateEditItems()
   goUpdateCommand("cmd_renameAttachment");
   goUpdateCommand("cmd_selectAll");
   goUpdateCommand("cmd_openAttachment");
   goUpdateCommand("cmd_find");
   goUpdateCommand("cmd_findNext");
   goUpdateCommand("cmd_findPrev");
 }
 
+function updateAttachmentItems()
+{
+  goUpdateCommand("cmd_attachCloud");
+  goUpdateCommand("cmd_convertCloud");
+  goUpdateCommand("cmd_convertAttachment");
+  goUpdateCommand("cmd_cancelUpload");
+  goUpdateCommand("cmd_delete");
+  goUpdateCommand("cmd_renameAttachment");
+  goUpdateCommand("cmd_selectAll");
+  goUpdateCommand("cmd_openAttachment");
+}
+
+/**
+ * Update all the commands for sending a message to reflect their current state.
+ */
+function updateSendCommands()
+{
+  goUpdateCommand("cmd_sendButton");
+  goUpdateCommand("cmd_sendNow");
+  goUpdateCommand("cmd_sendLater");
+  goUpdateCommand("cmd_sendWithCheck");
+}
+
+function addAttachCloudMenuItems(aParentMenu)
+{
+  while (aParentMenu.hasChildNodes())
+    aParentMenu.removeChild(aParentMenu.lastChild);
+
+  for (let [,cloudProvider] in Iterator(cloudFileAccounts.accounts)) {
+    let item = document.createElement("menuitem");
+    let iconClass = cloudProvider.iconClass;
+    item.cloudProvider = cloudProvider;
+    item.setAttribute("label", cloudFileAccounts.getDisplayName(cloudProvider));
+
+    if (iconClass) {
+      item.setAttribute("class", "menu-iconic");
+      item.setAttribute("image", iconClass);
+    }
+    aParentMenu.appendChild(item);
+  }
+}
+
+function addConvertCloudMenuItems(aParentMenu, aAfterNodeId, aRadioGroup)
+{
+  let attachment = document.getElementById("attachmentBucket").selectedItem;
+  let afterNode = document.getElementById(aAfterNodeId);
+  while (afterNode.nextSibling)
+    aParentMenu.removeChild(afterNode.nextSibling);
+
+  if (!attachment.sendViaCloud) {
+    let item = document.getElementById("context_convertAttachment");
+    item.setAttribute("checked", "true");
+  }
+
+  for (let [,cloudProvider] in Iterator(cloudFileAccounts.accounts)) {
+    let item = document.createElement("menuitem");
+    let iconClass = cloudProvider.iconClass;
+    item.cloudProvider = cloudProvider;
+    item.setAttribute("label", cloudFileAccounts.getDisplayName(cloudProvider));
+    item.setAttribute("type", "radio");
+    item.setAttribute("name", aRadioGroup);
+
+    if (attachment.cloudProvider &&
+        attachment.cloudProvider.accountKey == cloudProvider.accountKey) {
+      item.setAttribute("checked", "true");
+    }
+    else if (iconClass) {
+      item.setAttribute("class", "menu-iconic");
+      item.setAttribute("image", iconClass);
+    }
+
+    aParentMenu.appendChild(item);
+  }
+}
+
+function uploadListener(aAttachment, aFile, aCloudProvider)
+{
+  this.attachment = aAttachment;
+  this.file = aFile;
+  this.cloudProvider = aCloudProvider;
+
+  // Notify the UI that we're starting the upload process: disable send commands
+  // and show a "connecting" icon for the attachment.
+  this.attachment.sendViaCloud = true;
+  gNumUploadingAttachments++;
+  updateSendCommands();
+
+  let bucket = document.getElementById("attachmentBucket");
+  let item = bucket.findItemForAttachment(this.attachment);
+  if (item) {
+    item.image = "chrome://messenger/skin/icons/connecting.png";
+    item.setAttribute("tooltiptext",
+      getComposeBundle().getFormattedString("cloudFileUploadingTooltip", [
+        cloudFileAccounts.getDisplayName(this.cloudProvider)
+      ]));
+    item.uploading = true;
+    item.cloudProvider = this.cloudProvider;
+  }
+}
+
+uploadListener.prototype = {
+  onStartRequest: function uploadListener_onStartRequest(aRequest, aContext) {
+    let bucket = document.getElementById("attachmentBucket");
+    let item = bucket.findItemForAttachment(this.attachment);
+    if (item)
+      item.image = "chrome://messenger/skin/icons/loading.png";
+  },
+
+  onStopRequest: function uploadListener_onStopRequest(aRequest, aContext,
+                                                       aStatusCode) {
+    let bucket = document.getElementById("attachmentBucket");
+    let attachmentItem = bucket.findItemForAttachment(this.attachment);
+
+    if (Components.isSuccessCode(aStatusCode)) {
+      let originalUrl = this.attachment.url;
+      this.attachment.contentLocation = this.cloudProvider.urlForFile(this.file);
+      this.attachment.cloudProviderKey = this.cloudProvider.accountKey;
+      if (attachmentItem) {
+        // Update relevant bits on the attachment list item.
+        if (!attachmentItem.originalUrl)
+          attachmentItem.originalUrl = originalUrl;
+        attachmentItem.setAttribute("tooltiptext",
+          getComposeBundle().getFormattedString("cloudFileUploadedTooltip", [
+            cloudFileAccounts.getDisplayName(this.cloudProvider)
+          ]));
+        attachmentItem.uploading = false;
+
+        // Set the icon for the attachment.
+        let iconClass = this.cloudProvider.iconClass;
+        if (iconClass)
+          attachmentItem.image = iconClass;
+        else {
+          // Should we use a generic "cloud" icon here? Or an overlay icon?
+          // I think the provider should provide an icon, end of story.
+          attachmentItem.image = null;
+        }
+      }
+
+      let event = document.createEvent("Events");
+      event.initEvent("attachment-uploaded", true, true);
+      attachmentItem.dispatchEvent(event);
+    }
+    else if (aStatusCode == this.cloudProvider.uploadCanceled) {
+      attachmentItem.setAttribute("tooltiptext", attachmentItem.attachment.url);
+      attachmentItem.image = null;
+    }
+    else {
+      let title;
+      let msg;
+      let displayName = cloudFileAccounts.getDisplayName(this.cloudProvider);
+      let bundle = getComposeBundle();
+
+      switch (aStatusCode) {
+      case this.cloudProvider.authErr:
+        title = bundle.getString("errorCloudFileAuth.title");
+        msg = bundle.getFormattedString("errorCloudFileAuth.message",
+                                        [displayName]);
+        break;
+      case this.cloudProvider.uploadErr:
+        title = bundle.getString("errorCloudFileUpload.title");
+        msg = bundle.getFormattedString("errorCloudFileUpload.message",
+                                        [displayName,
+                                         this.attachment.name]);
+        break;
+      case this.cloudProvider.uploadWouldExceedQuota:
+        title = bundle.getString("errorCloudFileQuota.title");
+        msg = bundle.getFormattedString("errorCloudFileQuota.message",
+                                        [displayName,
+                                         this.attachment.name]);
+        break;
+      case this.cloudProvider.uploadExceedsFileLimit:
+        title = bundle.getString("errorCloudFileLimit.title");
+        msg = bundle.getFormattedString("errorCloudFileLimit.message",
+                                        [displayName,
+                                         this.attachment.name]);
+        break;
+      default:
+        title = bundle.getString("errorCloudFileOther.title");
+        msg = bundle.getFormattedString("errorCloudFileOther.message",
+                                        [displayName]);
+        break;
+      }
+
+      // TODO: support actions other than "Upgrade"
+      const prompt = Services.prompt;
+      let url = this.cloudProvider.providerUrlForError(aStatusCode);
+      let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK;
+      if (url)
+        flags += prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_IS_STRING;
+      if (prompt.confirmEx(window, title, msg, flags, null,
+                           bundle.getString("errorCloudFileUpgrade.label"),
+                           null, null, {})) {
+        openLinkExternally(url);
+      }
+
+      if (attachmentItem) {
+        // Remove the loading throbber.
+        attachmentItem.image = null;
+      }
+    }
+
+    gNumUploadingAttachments--;
+    updateSendCommands();
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIRequestObserver,
+                                         Components.interfaces.nsISupportsWeakReference])
+};
+
+function deletionListener(aAttachment, aCloudProvider)
+{
+  this.attachment = aAttachment;
+  this.cloudProvider = aCloudProvider;
+}
+
+deletionListener.prototype = {
+  onStartRequest: function deletionListener_onStartRequest(aRequest, aContext) {
+  },
+
+  onStopRequest: function deletionListener_onStopRequest(aRequest, aContext,
+                                                         aStatusCode) {
+    if (!Components.isSuccessCode(aStatusCode)) {
+      let displayName = cloudFileAccounts.getDisplayName(this.cloudProvider);
+      Services.prompt.alert(window,
+        getComposeBundle().getString("errorCloudFileDeletion.title"),
+        getComposeBundle().getFormattedString("errorCloudFileDeletion.message",
+                                              [displayName,
+                                               this.attachment.name]));
+    }
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIRequestObserver,
+                                         Components.interfaces.nsISupportsWeakReference])
+};
+
+/**
+ * Prompt the user for a list of files to attach via a cloud provider.
+ *
+ * @param aProvider the cloud provider to upload the files to
+ */
+function attachToCloud(aProvider)
+{
+  // We need to let the user pick local file(s) to upload to the cloud and
+  // gather url(s) to those files.
+  var fp = Components.classes["@mozilla.org/filepicker;1"]
+                     .createInstance(nsIFilePicker);
+  fp.init(window, getComposeBundle().getFormattedString(
+            "chooseFileToAttachViaCloud",
+            [cloudFileAccounts.getDisplayName(aProvider)]),
+          nsIFilePicker.modeOpenMultiple);
+
+  var lastDirectory = GetLastAttachDirectory();
+  if (lastDirectory)
+    fp.displayDirectory = lastDirectory;
+
+  let files = [];
+
+  fp.appendFilters(nsIFilePicker.filterAll);
+  if (fp.show() == nsIFilePicker.returnOK)
+  {
+    if (!fp.files)
+      return;
+
+    let files = [f for (f in fixIterator(fp.files,
+                                         Components.interfaces.nsILocalFile))];
+    let attachments = [FileToAttachment(f) for each (f in files)];
+
+    let i = 0;
+    let items = AddAttachments(attachments, function(aItem) {
+      let listener = new uploadListener(attachments[i], files[i], aProvider);
+      aProvider.uploadFile(files[i], listener);
+      i++;
+    });
+
+    SetLastAttachDirectory(files[files.length-1]);
+  }
+}
+
+/**
+ * Convert an array of attachments to cloud attachments.
+ *
+ * @param aItems an array of <attachmentitem>s containing the attachments in
+ *        question
+ * @param aProvider the cloud provider to upload the files to
+ */
+function convertListItemsToCloudAttachment(aItems, aProvider)
+{
+  let fileHandler = Services.io.getProtocolHandler("file")
+                            .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
+  let convertedAttachments = Components.classes["@mozilla.org/array;1"]
+                                       .createInstance(Components.interfaces.nsIMutableArray);
+
+  for (let [,item] in Iterator(aItems)) {
+    let url = item.attachment.url;
+
+    if (item.attachment.sendViaCloud) {
+      if (item.cloudProvider && item.cloudProvider == aProvider)
+        continue;
+      url = item.originalUrl;
+    }
+
+    let file = fileHandler.getFileFromURLSpec(url);
+    if (item.cloudProvider) {
+      item.cloudProvider.deleteFile(
+        file, new deletionListener(item.attachment, item.cloudProvider));
+    }
+
+    aProvider.uploadFile(file,
+                         new uploadListener(item.attachment, file, aProvider));
+    convertedAttachments.appendElement(item.attachment, false);
+  }
+
+  Services.obs.notifyObservers(convertedAttachments,
+                               "mail:attachmentsConverted",
+                               aProvider.accountKey);
+}
+
+/**
+ * Convert the selected attachments to cloud attachments.
+ *
+ * @param aProvider the cloud provider to upload the files to
+ */
+function convertSelectedToCloudAttachment(aProvider)
+{
+  let bucket = document.getElementById("attachmentBucket");
+  convertListItemsToCloudAttachment(bucket.selectedItems, aProvider);
+}
+
+/**
+ * Convert an array of nsIMsgAttachments to cloud attachments.
+ *
+ * @param aAttachments an array of nsIMsgAttachments
+ * @param aProvider the cloud provider to upload the files to
+ */
+function convertToCloudAttachment(aAttachments, aProvider)
+{
+  let bucket = document.getElementById("attachmentBucket");
+  let items = [];
+  for (let [,attachment] in Iterator(aAttachments)) {
+    let item = bucket.findItemForAttachment(attachment);
+    if (item)
+      items.push(item);
+  }
+
+  convertListItemsToCloudAttachment(items, aProvider);
+}
+
+/**
+ * Convert an array of attachments to regular (non-cloud) attachments.
+ *
+ * @param aItems an array of <attachmentitem>s containing the attachments in
+ *        question
+ */
+function convertListItemsToRegularAttachment(aItems)
+{
+  let fileHandler = Services.io.getProtocolHandler("file")
+                            .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
+  let convertedAttachments = Components.classes["@mozilla.org/array;1"]
+                                       .createInstance(Components.interfaces.nsIMutableArray);
+
+  for (let [,item] in Iterator(aItems)) {
+    if (!item.attachment.sendViaCloud || !item.cloudProvider)
+      continue;
+
+    let file = fileHandler.getFileFromURLSpec(item.originalUrl);
+    try {
+      // This will fail for drafts, but we can still send the message
+      // with a normal attachment.
+      item.cloudProvider.deleteFile(
+        file, new deletionListener(item.attachment, item.cloudProvider));
+    }
+    catch (ex) {
+       Components.utils.reportError(ex);
+    }
+
+    item.attachment.url = item.originalUrl;
+    item.setAttribute("tooltiptext", item.attachment.url);
+    item.attachment.sendViaCloud = false;
+
+    delete item.cloudProvider;
+    delete item.originalUrl;
+    item.image = null;
+
+    convertedAttachments.appendElement(item.attachment, false);
+  }
+  let bucket = document.getElementById("attachmentBucket");
+  let event = document.createEvent("CustomEvent");
+  event.initCustomEvent("attachments-converted", true, true, convertedAttachments);
+  bucket.dispatchEvent(event);
+
+  Services.obs.notifyObservers(convertedAttachments,
+                               "mail:attachmentsConverted", null);
+                               
+  // We leave the content location in for the notifications because
+  // it may be needed to identify the attachment. But clear it out now.
+  for (let [,item] in Iterator(aItems))
+    delete item.attachment.contentLocation;
+}
+
+/**
+ * Convert the selected attachments to regular (non-cloud) attachments.
+ */
+function convertSelectedToRegularAttachment()
+{
+  let bucket = document.getElementById("attachmentBucket");
+  convertListItemsToRegularAttachment(bucket.selectedItems);
+}
+
+/**
+ * Convert an array of nsIMsgAttachments to regular (non-cloud) attachments.
+ *
+ * @param aAttachments an array of nsIMsgAttachments
+ */
+function convertToRegularAttachment(aAttachments)
+{
+  let bucket = document.getElementById("attachmentBucket");
+  let items = [];
+  for (let [,attachment] in Iterator(aAttachments)) {
+    let item = bucket.findItemForAttachment(attachment);
+    if (item)
+      items.push(item);
+  }
+
+  convertListItemsToRegularAttachment(items);
+}
+
 function updateOptionItems()
 {
   goUpdateCommand("cmd_quoteMessage");
 }
 
 /* messageComposeOfflineQuitObserver is notified whenever the network
  * connection status has switched to offline, or when the application
  * has received a request to quit.
@@ -1682,16 +2193,18 @@ function ComposeStartup(recycled, aParam
       }
       if (args.newshost)
         composeFields.newshost = args.newshost;
       if (args.body)
          composeFields.body = args.body;
     }
   }
 
+  gComposeType = params.type;
+
   // " <>" is an empty identity, and most likely not valid
   if (!params.identity || params.identity.identityName == " <>") {
     // no pre selected identity, so use the default account
     var identities = gAccountManager.defaultAccount.identities;
     if (identities.Count() == 0)
       identities = gAccountManager.allIdentities;
     params.identity = identities.QueryElementAt(0, Components.interfaces.nsIMsgIdentity);
   }
@@ -2075,19 +2588,20 @@ function GenericSendMessage(msgType)
 {
   var msgCompFields = gMsgCompose.compFields;
 
   Recipients2CompFields(msgCompFields);
   var subject = GetMsgSubjectElement().value;
   msgCompFields.subject = subject;
   Attachments2CompFields(msgCompFields);
 
-  if (msgType == nsIMsgCompDeliverMode.Now ||
+  let sending = msgType == nsIMsgCompDeliverMode.Now ||
       msgType == nsIMsgCompDeliverMode.Later ||
-      msgType == nsIMsgCompDeliverMode.Background)
+      msgType == nsIMsgCompDeliverMode.Background;
+  if (sending)
   {
     // Do we need to check the spelling?
     if (DoSpellCheckBeforeSend())
     {
       // We disable spellcheck for the following -subject line, attachment
       // pane, identity and addressing widget therefore we need to explicitly
       // focus on the mail body when we have to do a spellcheck.
       SetMsgBodyFrameFocus();
@@ -2999,40 +3513,42 @@ function AttachFile()
 /**
  * Convert an nsILocalFile instance into an nsIMsgAttachment.
  *
  * @param file the nsILocalFile
  * @return an attachment pointing to the file
  */
 function FileToAttachment(file)
 {
-  let fileHandler = Components.classes["@mozilla.org/network/io-service;1"]
-                              .getService(Components.interfaces.nsIIOService)
-                              .getProtocolHandler("file")
-                              .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
+  let fileHandler = Services.io.getProtocolHandler("file")
+                            .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
   let attachment = Components.classes["@mozilla.org/messengercompose/attachment;1"]
                              .createInstance(Components.interfaces.nsIMsgAttachment);
 
   attachment.url = fileHandler.getURLSpecFromFile(file);
   attachment.size = file.fileSize;
   return attachment;
 }
 
 /**
  * Add a list of attachment objects as attachments. The attachment URLs must be
  * set.
  *
  * @param aAttachments an iterable list of nsIMsgAttachment objects to add as
  *        attachments. Anything iterable with fixIterator is accepted.
+ * @param aCallback an optional callback function called immediately after
+ *        adding each attachment. Takes one argument: the newly-added
+ *        <attachmentitem> node.
  */
-function AddAttachments(aAttachments)
+function AddAttachments(aAttachments, aCallback)
 {
   let bucket = document.getElementById("attachmentBucket");
   let addedAttachments = Components.classes["@mozilla.org/array;1"]
                                    .createInstance(Components.interfaces.nsIMutableArray);
+  let items = [];
 
   for (let attachment in fixIterator(aAttachments,
                                      Components.interfaces.nsIMsgAttachment)) {
     if (!(attachment && attachment.url) ||
         DuplicateFileAlreadyAttached(attachment.url))
       continue;
 
     if (!attachment.name)
@@ -3056,41 +3572,58 @@ function AddAttachments(aAttachments)
     try {
       item.setAttribute("tooltiptext", decodeURI(attachment.url));
     }
     catch(e) {
       item.setAttribute("tooltiptext", attachment.url);
     }
     item.addEventListener("command", OpenSelectedAttachment, false);
 
-    // For local file urls, we are better off using the full file url because
-    // moz-icon will actually resolve the file url and get the right icon from
-    // the file url. All other urls, we should try to extract the file name from
-    // them. This fixes issues where an icon wasn't showing up if you dragged a
-    // web url that had a query or reference string after the file name and for
-    // mailnews urls where the filename is hidden in the url as a &filename=
-    // part.
-    var url = gIOService.newURI(attachment.url, null, null);
-    if (url instanceof Components.interfaces.nsIURL &&
-        url.fileName && !url.schemeIs("file"))
-      item.setAttribute("image", "moz-icon://" + url.fileName);
-    else
-      item.setAttribute("image", "moz-icon:" + attachment.url);
+    if (attachment.sendViaCloud) {
+      try {
+        let cloudProvider = cloudFileAccounts.getAccount(attachment.cloudProviderKey);
+        item.cloudProvider = cloudProvider;
+        item.image = cloudProvider.iconClass;
+        item.originalUrl = attachment.url;
+      } catch (ex) {dump(ex);}
+    }
+    else {
+      // For local file urls, we are better off using the full file url because
+      // moz-icon will actually resolve the file url and get the right icon from
+      // the file url. All other urls, we should try to extract the file name from
+      // them. This fixes issues where an icon wasn't showing up if you dragged a
+      // web url that had a query or reference string after the file name and for
+      // mailnews urls where the filename is hidden in the url as a &filename=
+      // part.
+      var url = gIOService.newURI(attachment.url, null, null);
+      if (url instanceof Components.interfaces.nsIURL &&
+          url.fileName && !url.schemeIs("file"))
+        item.image = "moz-icon://" + url.fileName;
+      else
+        item.image = "moz-icon:" + attachment.url;
+      }
+
+    items.push(item);
+
+    if (aCallback)
+      aCallback(item);
 
     CheckForAttachmentNotification(null);
   }
 
   if (addedAttachments.length) {
     gContentChanged = true;
 
     UpdateAttachmentBucket(true);
     let event = document.createEvent("CustomEvent");
     event.initCustomEvent("attachments-added", true, true, addedAttachments);
     bucket.dispatchEvent(event);
   }
+
+  return items;
 }
 
 /**
  * Add a file object as attachment. This is mostly just a helper function to
  * wrap a file into an nsIMsgAttachment object with it's URL set. Note: this
  * function is DEPRECATED. Use AddAttachments and FileToAttachment instead.
  *
  * @param file the nsIFile object to add as attachment
@@ -3215,35 +3748,40 @@ function UpdateAttachmentBucket(aShowBuc
   }
 
   document.getElementById("attachments-box").collapsed = !aShowBucket;
   document.getElementById("attachmentbucket-sizer").collapsed = !aShowBucket;
 }
 
 function RemoveSelectedAttachment()
 {
-  var child;
-  var bucket = document.getElementById("attachmentBucket");
+  let bucket = document.getElementById("attachmentBucket");
   if (bucket.selectedItems.length > 0) {
+    let fileHandler = Services.io.getProtocolHandler("file")
+                              .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
     let removedAttachments = Components.classes["@mozilla.org/array;1"]
                                        .createInstance(Components.interfaces.nsIMutableArray);
 
-    for (let i = bucket.selectedCount - 1; i >= 0; i--)
-    {
-      child = bucket.removeItemAt(bucket.getIndexOfItem(bucket.getSelectedItem(i)));
-      if (child.attachment.size != -1)
-      {
-        gAttachmentsSize -= child.attachment.size;
+    for (let i = bucket.selectedCount - 1; i >= 0; i--) {
+      let item = bucket.removeItemAt(bucket.getIndexOfItem(bucket.getSelectedItem(i)));
+      if (item.attachment.size != -1) {
+        gAttachmentsSize -= item.attachment.size;
         UpdateAttachmentBucket(true);
       }
 
-      removedAttachments.appendElement(child.attachment, false);
+      if (item.attachment.sendViaCloud && item.cloudProvider) {
+        let file = fileHandler.getFileFromURLSpec(item.originalUrl);
+        item.cloudProvider.deleteFile(
+          file, new deletionListener(item.attachment, item.cloudProvider));
+      }
+
+      removedAttachments.appendElement(item.attachment, false);
       // Let's release the attachment object held by the node else it won't go
       // away until the window is destroyed
-      child.attachment = null;
+      item.attachment = null;
     }
 
     gContentChanged = true;
 
     let event = document.createEvent("CustomEvent");
     event.initCustomEvent("attachments-removed", true, true,
                           removedAttachments);
     bucket.dispatchEvent(event);
new file mode 100644
--- /dev/null
+++ b/mail/components/compose/content/bigFileObserver.js
@@ -0,0 +1,173 @@
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/cloudFileAccounts.js");
+
+var gBigFileObserver = {
+  bigFiles: [],
+  sessionHidden: false,
+
+  get hidden() {
+    return this.sessionHidden ||
+           !Services.prefs.getBoolPref("mail.cloud_files.enabled") ||
+           !Services.prefs.getBoolPref("mail.compose.big_attachments.notify");
+  },
+  hide: function(aPermanent) {
+    if (aPermanent)
+      Services.prefs.setBoolPref("mail.compose.big_attachments.notify", false);
+    else
+      this.sessionHidden = true;
+  },
+
+  init: function() {
+    let bucket = document.getElementById("attachmentBucket");
+    bucket.addEventListener("attachments-added", this, false);
+    bucket.addEventListener("attachments-removed", this, false);
+    bucket.addEventListener("attachment-renamed", this, false);
+
+    this.sessionHidden = false;
+    this.bigFiles = [];
+  },
+
+  uninit: function() {
+    let bucket = document.getElementById("attachmentBucket");
+    bucket.removeEventListener("attachments-added", this, false);
+    bucket.removeEventListener("attachments-removed", this, false);
+    bucket.removeEventListener("attachment-renamed", this, false);
+  },
+
+  handleEvent: function(event) {
+    if (this.hidden)
+      return;
+
+    const callbacks = {
+      "attachments-added": this.attachmentAdded,
+      "attachments-removed": this.attachmentRemoved,
+      "attachments-converted": this.attachmentConverted,
+    };
+
+    for (let attachment in fixIterator(
+         event.detail, Components.interfaces.nsIMsgAttachment)) {
+      callbacks[event.type].call(this, attachment);
+    }
+    this.updateNotification();
+  },
+
+  formatString: function (key, replacements, plural) {
+    let str = getComposeBundle().getString(key);
+    if (plural !== undefined)
+      str = PluralForm.get(plural, str);
+    if (replacements !== undefined) {
+      for (let i = 0; i < replacements.length; i++)
+        str = str.replace("#" + (i+1), replacements[i]);
+    }
+    return str;
+  },
+
+  attachmentAdded: function(aAttachment) {
+    let threshold = Services.prefs.getIntPref(
+                    "mail.compose.big_attachments.threshold_kb") * 1024;
+
+    if (aAttachment.size >= threshold && !aAttachment.sendViaCloud)
+      this.bigFiles.push(aAttachment);
+  },
+
+  attachmentRemoved: function(aAttachment) {
+    let index = this.bigFiles.indexOf(aAttachment);
+    if (index != -1)
+      this.bigFiles.splice(index, 1);
+  },
+
+  attachmentConverted: function(aAttachment) {
+    if (aAttachment.sendViaCloud)
+      this.attachmentRemoved(aAttachment);
+    else
+      this.attachmentAdded(aAttachment);
+  },
+
+  updateNotification: function() {
+    let nb = document.getElementById("attachmentNotificationBox");
+    let notification = nb.getNotificationWithValue("bigAttachment");
+    let numAccounts = cloudFileAccounts.accounts.length;
+
+    if (this.bigFiles.length) {
+      if (notification) {
+        notification.label = this.formatString("bigFileDescription",
+                                               [this.bigFiles.length],
+                                               this.bigFiles.length);
+        return;
+      }
+
+      let buttons = [
+        { label: this.formatString("bigFileShare.label",
+                                   []),
+          accessKey: this.formatString("bigFileShare.accesskey"),
+          callback: this.convertAttachments.bind(this),
+        },
+
+        { label: this.formatString("bigFileAttach.label",
+                                   []),
+          accessKey: this.formatString("bigFileAttach.accesskey"),
+          callback: this.hideNotification.bind(this),
+        },
+      ];
+
+      nb.appendNotification(this.formatString("bigFileDescription",
+                                              [this.bigFiles.length],
+                                              this.bigFiles.length),
+                            "bigAttachment", "null", nb.PRIORITY_WARNING_MEDIUM,
+                            buttons);
+    }
+    else {
+      if (notification)
+        nb.removeNotification(notification);
+    }
+  },
+
+  convertAttachments: function() {
+    let cloudProvider;
+    let accounts = cloudFileAccounts.accounts;
+
+    if(accounts.length == 1) {
+      cloudProvider = accounts[0];
+    }
+    else if(accounts.length > 1) {
+      let selection = {};
+      let names = [cloudFileAccounts.getDisplayName(i) for each (i in accounts)];
+      if (Services.prompt.select(window,
+                                 this.formatString("bigFileChooseAccount.title"),
+                                 this.formatString("bigFileChooseAccount.text"),
+                                 names.length, names, selection))
+        cloudProvider = accounts[selection.value];
+    }
+    else {
+      let accountKey = cloudFileAccounts.addAccountDialog();
+      if (accountKey)
+        cloudProvider = cloudFileAccounts.getAccount(accountKey);
+      else
+        return true;
+    }
+
+    if (cloudProvider)
+      convertToCloudAttachment(this.bigFiles, cloudProvider);
+  },
+
+  hideNotification: function() {
+    let never = {};
+    if (Services.prompt.confirmCheck(window,
+                                     this.formatString("bigFileHideNotification.title"),
+                                     this.formatString("bigFileHideNotification.text"),
+                                     this.formatString("bigFileHideNotification.check"),
+                                     never))
+      this.hide(never.value);
+    else
+      return true;
+  },
+};
+
+document.documentElement.addEventListener("compose-window-init",
+  gBigFileObserver.init.bind(gBigFileObserver), false);
+document.documentElement.addEventListener("compose-window-close",
+  gBigFileObserver.uninit.bind(gBigFileObserver), false);
new file mode 100644
--- /dev/null
+++ b/mail/components/compose/content/cloudAttachmentLinkManager.js
@@ -0,0 +1,387 @@
+/* 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/. */
+
+var gCloudAttachmentLinkManager = {
+  init: function() {
+    this.cloudAttachments = [];
+
+    let bucket = document.getElementById("attachmentBucket");
+    bucket.addEventListener("attachment-uploaded", this, false);
+    bucket.addEventListener("attachments-removed", this, false);
+    bucket.addEventListener("attachments-converted", this, false);
+
+    // If we're restoring a draft that has some attachments,
+    // check to see if any of them are marked to be sent via
+    // cloud, and if so, add them to our list.
+    for (let i = 0; i < bucket.getRowCount(); ++i) {
+      let attachment = bucket.getItemAtIndex(i).attachment;
+      if (attachment && attachment.sendViaCloud)
+        this.cloudAttachments.push(attachment);
+    }
+
+    gMsgCompose.RegisterStateListener(this);
+  },
+
+  uninit: function() {
+    let bucket = document.getElementById("attachmentBucket");
+    bucket.removeEventListener("attachment-uploaded", this, false);
+    bucket.removeEventListener("attachments-removed", this, false);
+    bucket.removeEventListener("attachments-converted", this, false);
+
+    gMsgCompose.UnregisterStateListener(this);
+  },
+
+  NotifyComposeFieldsReady: function() {},
+
+  NotifyComposeBodyReady: function() {
+    // If we're doing an inline-forward in plain-text mode, let's
+    // take all of the current message text, and wrap it up into
+    // its own DIV.
+    if (gComposeType != Components.interfaces.nsIMsgCompType.ForwardInline ||
+        gMsgCompose.composeHTML)
+      return;
+
+    let mailDoc = document.getElementById("content-frame").contentDocument;
+    let mailBody = mailDoc.getElementsByTagName("body")[0];
+    let editor = GetCurrentEditor();
+
+    let childNodes = mailBody.childNodes;
+    let container = editor.createElementWithDefaults("div");
+
+    for (let i = 0; i < childNodes.length; ++i) {
+      let oldChild = childNodes[i];
+      // It's strange, but for some reason, carriage-returns seem to be truncated
+      // during the node transfer...so we put them back.
+      if (oldChild.nodeType == Node.TEXT_NODE)
+        oldChild.nodeValue += "\r\n";
+
+      let removedChild = oldChild.parentNode.removeChild(oldChild);
+      container.appendChild(removedChild);
+    }
+
+    editor.insertLineBreak();
+    editor.insertElementAtSelection(container, false);
+    editor.insertLineBreak();
+  },
+  ComposeProcessDone: function() {},
+  SaveInFolderDone: function() {},
+
+  handleEvent: function(event) {
+    let mailDoc = document.getElementById("content-frame").contentDocument;
+
+    if (event.type == "attachment-uploaded") {
+      if (this.cloudAttachments.length == 0)
+        this._insertHeader(mailDoc);
+
+      let attachment = event.target.attachment;
+      let provider = event.target.cloudProvider;
+      this.cloudAttachments.push(attachment);
+      this._insertItem(mailDoc, attachment, provider);
+    }
+    else if (event.type == "attachments-removed" ||
+             event.type == "attachments-converted") {
+      let list = mailDoc.getElementById("cloudAttachmentList");
+      let items = list.getElementsByClassName("cloudAttachmentItem");
+
+      for (let attachment in fixIterator(
+           event.detail, Components.interfaces.nsIMsgAttachment)) {
+        // Remove the attachment from the message body.
+        for (let i = 0; i < items.length; i++) {
+          // if the attachment was removed, or was converted and is no
+          // longer to be sent via cloud, remove from html node.
+          if ((event.type == "attachments-removed" || !attachment.sendViaCloud) &&
+              items[i].contentLocation == attachment.contentLocation)
+            list.removeChild(items[i]);
+        }
+
+        // Now, remove the attachment from our internal list.
+        let index = this.cloudAttachments.indexOf(attachment);
+        if (index != -1)
+          this.cloudAttachments.splice(index, 1);
+      }
+
+      this._updateAttachmentCount(mailDoc);
+
+      if (items.length == 0) {
+        list.parentNode.removeChild(list);
+        this._removeRoot(mailDoc);
+      }
+    }
+  },
+
+  /**
+   * Removes the root node for an attachment list in an HTML email.
+   *
+   * @param aDocument the document to remove the root node from.
+   */
+  _removeRoot: function(aDocument) {
+    let header = aDocument.getElementById("cloudAttachmentListRoot");
+    if (header)
+      header.parentNode.removeChild(header);
+  },
+
+  /**
+   * Given some node, returns the textual HTML representation for the node
+   * and its children.
+   *
+   * @param aDocument the document that the node is embedded in
+   * @param aNode the node to get the textual representation from
+   */
+  _getHTMLRepresentation: function(aDocument, aNode) {
+    let tmp = aDocument.createElement("p");
+    tmp.appendChild(aNode);
+    return tmp.innerHTML;
+  },
+
+  /**
+   * Generates an appropriately styled link.
+   *
+   * @param aDocument the document to append the link to - doesn't actually
+   *                  get appended, but is used to generate the anchor node.
+   * @param aContent the textual content of the link
+   * @param aHref the HREF attribute for the generated link
+   */
+  _generateLink: function(aDocument, aContent, aHref) {
+    const LINK_COLOR = "#0F7EDB";
+    let link = aDocument.createElement("a");
+    link.href = aHref;
+    link.textContent = aContent;
+    link.style.cssText = "color: " + LINK_COLOR + " !important";
+    return link;
+  },
+
+  /**
+   * Insert the header for the cloud attachment list, which we'll use to
+   * as an insertion point for the individual cloud attachments.
+   *
+   * @param aDocument the document to insert the header into
+   */
+  _insertHeader: function(aDocument) {
+    let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
+    let mailBody = aDocument.getElementsByTagName("body")[0];
+    let editor = GetCurrentEditor();
+    let selection = editor.selection;
+
+    // Save off the selection ranges so we can restore them later.
+    let ranges = [];
+    for (let i = 0; i < selection.rangeCount; i++)
+      ranges.push(selection.getRangeAt(i));
+
+    // Find the last text node that's a first level child of the body.
+    // This will skip quoted text.
+    let childNodes = mailBody.childNodes;
+    // If we don't find any text nodes, we'll insert at the start of the
+    // body, which may not be quite right.
+    let childToInsertAfter = childNodes[0];
+    for (let i = childNodes.length - 1; i >= 0; i--) {
+      if (childNodes[i].nodeType == Node.TEXT_NODE &&
+          childNodes[i].nodeValue && childNodes[i].nodeValue.length > 0) {
+        childToInsertAfter = childNodes[i];
+        break;
+      }
+    }
+
+    selection.collapse(childToInsertAfter,
+                       childToInsertAfter.nodeValue ?
+                       childToInsertAfter.nodeValue.length : 0);
+
+
+    if (gMsgCompose.composeHTML) {
+      // It's really quite strange, but if we don't set
+      // the innerHTML of each element to be non-empty, then
+      // the nodes fail to be added to the compose window.
+      let root = editor.createElementWithDefaults("div");
+      root.id = "cloudAttachmentListRoot";
+      root.style.padding = "15px";
+      root.style.backgroundColor = "#D9EDFF";
+      root.innerHTML = " ";
+
+      let header = editor.createElementWithDefaults("div");
+      header.id = "cloudAttachmentListHeader";
+      header.style.marginBottom = "15px";
+      header.innerHTML = " ";
+      root.appendChild(header);
+
+      let list = editor.createElementWithDefaults("div");
+      list.id = "cloudAttachmentList";
+      list.style.backgroundColor = "#FFFFFF";
+      list.style.padding = "15px";
+      list.display = "inline-block";
+      list.innerHTML = " ";
+      root.appendChild(list);
+
+      let footer = editor.createElementWithDefaults("div");
+      let appLinkUrl = Services.prefs
+                               .getCharPref("mail.cloud_files.inserted_urls.footer.link");
+      let appname = this._generateLink(aDocument,
+                                       brandBundle.GetStringFromName("brandFullName"),
+                                       appLinkUrl);
+
+      let applink = this._getHTMLRepresentation(aDocument, appname);
+      let footerMessage = getComposeBundle().getFormattedString("cloudAttachmentListFooter", [applink], 1);
+
+      footer.innerHTML = footerMessage;
+      footer.style.color = "#444444";
+      footer.style.fontSize = "small";
+      footer.style.marginTop = "15px";
+      root.appendChild(footer);
+
+      editor.insertLineBreak();
+      editor.insertElementAtSelection(root, false);
+      editor.insertLineBreak();
+    }
+    else {
+      let header = editor.createElementWithDefaults("div");
+      header.id = "cloudAttachmentListHeader";
+      header.innerHTML = " ";
+      let list = editor.createElementWithDefaults("span");
+      list.id = "cloudAttachmentList";
+      list.innerHTML = " ";
+
+      editor.insertLineBreak();
+      editor.insertElementAtSelection(header, false);
+      editor.insertElementAtSelection(list, false);
+      editor.insertLineBreak();
+    }
+
+    // Restore the selection ranges.
+    for (let [,range] in Iterator(ranges))
+      selection.addRange(range);
+
+  },
+
+  /**
+   * Updates the count of how many attachments have been added
+   * in HTML emails.
+   *
+   * @aDocument the document that contains the cloudAttachmentListHeader node.
+   */
+  _updateAttachmentCount: function(aDocument) {
+    let header = aDocument.getElementById("cloudAttachmentListHeader");
+    if (!header)
+      return;
+
+    let count = PluralForm.get(this.cloudAttachments.length,
+                               getComposeBundle().getString("cloudAttachmentCountHeader"));
+
+    header.textContent = count.replace("#1", this.cloudAttachments.length);
+  },
+
+  /**
+   * Insert the information for a cloud attachment.
+   *
+   * @param aDocument the document to insert the item into
+   * @param aAttachment the nsIMsgAttachment to insert
+   * @param aProviderType the cloud storage provider
+   */
+  _insertItem: function(aDocument, aAttachment, aProvider) {
+    let list = aDocument.getElementById("cloudAttachmentList");
+    let node = aDocument.createElement("div");
+    node.className = "cloudAttachmentItem";
+    node.contentLocation = aAttachment.contentLocation;
+
+    if (gMsgCompose.composeHTML) {
+      node.style.border = "1px solid #CDCDCD";
+      node.style.borderRadius = "5px";
+      node.style.marginTop = "10px";
+      node.style.marginBottom = "10px";
+      node.style.padding = "15px";
+
+      let paperclip = aDocument.createElement("img");
+      paperclip.style.marginRight = "5px";
+      paperclip.style.cssFloat = "left";
+      paperclip.style.width = "24px";
+      paperclip.style.height = "24px";
+      paperclip.src = "chrome://messenger/content/cloudfile/attachment-24.png";
+      node.appendChild(paperclip);
+
+      let link = this._generateLink(aDocument, aAttachment.name,
+                                    aAttachment.contentLocation);
+      link.setAttribute("moz-do-not-send", "true");
+      node.appendChild(link);
+
+      let size = aDocument.createElement("span");
+      size.textContent = "(" + gMessenger.formatFileSize(aAttachment.size)
+                         + ")";
+      size.style.marginLeft = "5px";
+      size.style.fontSize = "small";
+      size.style.color = "grey";
+      node.appendChild(size);
+
+      let providerIdentity = aDocument.createElement("span");
+      providerIdentity.style.cssFloat = "right";
+
+      if (aProvider.iconClass) {
+        let providerIcon = aDocument.createElement("img");
+        providerIcon.src = aProvider.iconClass;
+        providerIcon.style.marginRight = "5px";
+        providerIdentity.appendChild(providerIcon);
+      }
+
+      if (aProvider.serviceURL) {
+        let providerLink = this._generateLink(aDocument, aProvider.displayName,
+                                              aProvider.serviceURL);
+        providerIdentity.appendChild(providerLink);
+      } else {
+        let providerName = aDocument.createElement("span");
+        providerName.textContent = aProvider.displayName;
+        providerIdentity.appendChild(providerName);
+      }
+
+      node.appendChild(providerIdentity);
+
+      let downloadUrl = this._generateLink(aDocument,
+                                           aAttachment.contentLocation,
+                                           aAttachment.contentLocation);
+      downloadUrl.style.fontSize = "small";
+      downloadUrl.style.display = "block";
+
+      node.appendChild(downloadUrl);
+    }
+    else {
+      node.textContent = getComposeBundle().getFormattedString(
+        "cloudAttachmentListItem",
+        [aAttachment.name, gMessenger.formatFileSize(aAttachment.size),
+         aProvider.displayName,
+         aAttachment.contentLocation]);
+    }
+
+    this._updateAttachmentCount(aDocument);
+    list.appendChild(node);
+  },
+
+  /**
+   * Event handler for when mail is sent.  For mail that is being sent
+   * (and not saved!), find any cloudAttachmentList* nodes that we've created,
+   * and strip their IDs out.  That way, if the receiving user replies by
+   * sending some BigFiles, we don't run into ID conflicts.
+   */
+  send: function(aEvent) {
+    const Ci = Components.interfaces;
+
+    let msgType = parseInt(aEvent.target.getAttribute("msgtype"));
+
+    if (msgType == Ci.nsIMsgCompDeliverMode.Now ||
+        msgType == Ci.nsIMsgCompDeliverMode.Later ||
+        msgType == Ci.nsIMsgCompDeliverMode.Background) {
+
+      const kIDs = ["cloudAttachmentList", "cloudAttachmentListRoot",
+                    "cloudAttachmentListHeader"];
+      let mailDoc = document.getElementById("content-frame").contentDocument;
+
+      for each (let [, id] in Iterator(kIDs)) {
+        let element = mailDoc.getElementById(id);
+        if (element)
+          element.removeAttribute("id");
+      }
+    }
+  },
+};
+
+document.documentElement.addEventListener("compose-window-init",
+  gCloudAttachmentLinkManager.init.bind(gCloudAttachmentLinkManager), false);
+document.documentElement.addEventListener("compose-window-close",
+  gCloudAttachmentLinkManager.uninit.bind(gCloudAttachmentLinkManager), false);
+document.documentElement.addEventListener("compose-send-message",
+  gCloudAttachmentLinkManager.send.bind(gCloudAttachmentLinkManager), false);
--- a/mail/components/compose/content/messengercompose.xul
+++ b/mail/components/compose/content/messengercompose.xul
@@ -83,18 +83,21 @@
   <stringbundle id="languageBundle"     src="chrome://global/locale/languageNames.properties"/>
   <stringbundle id="brandBundle"        src="chrome://branding/locale/brand.properties"/>
 </stringbundleset>
 
 <script type="application/javascript" src="chrome://global/content/printUtils.js"/>
 <script type="application/javascript" src="chrome://messenger/content/accountUtils.js"/>
 <script type="application/javascript" src="chrome://messenger/content/widgetglue.js"/>
 <script type="application/javascript" src="chrome://messenger/content/mailCore.js"/>
+<script type="application/javascript" src="chrome://communicator/content/contentAreaClick.js"/>
 <script type="application/javascript" src="chrome://editor/content/editor.js"/>
 <script type="application/javascript" src="chrome://messenger/content/messengercompose/MsgComposeCommands.js"/>
+<script type="application/javascript" src="chrome://messenger/content/messengercompose/bigFileObserver.js"/>
+<script type="application/javascript" src="chrome://messenger/content/messengercompose/cloudAttachmentLinkManager.js"/>
 
 <!-- drag and drop -->
 <script type="application/javascript" src="chrome://messenger/content/addressbook/abDragDrop.js"/>
 <script type="application/javascript" src="chrome://global/content/nsDragAndDrop.js"/>
 
 <!-- move needed functions into a single js file -->
 <script type="application/javascript" src="chrome://messenger/content/messengercompose/addressingWidgetOverlay.js"/>
 <script type="application/javascript" src="chrome://global/content/charsetOverlay.js"/>
@@ -128,16 +131,17 @@
   <commandset id="composerMenuItems"/>
   <commandset id="composerEditMenuItems"/> 
   <commandset id="composerStyleMenuItems"/>
   <commandset id="composerTableMenuItems"/>
   <commandset id="composerListMenuItems"/>
   <!-- File Menu -->
   <command id="cmd_new" oncommand="goDoCommand('cmd_newMessage')"/>
   <command id="cmd_attachFile" oncommand="goDoCommand('cmd_attachFile')"/>
+  <command id="cmd_attachCloud" oncommand="attachToCloud(event.target.cloudProvider); event.stopPropagation();"/>
   <command id="cmd_attachPage" oncommand="goDoCommand('cmd_attachPage')"/>
   <command id="cmd_attachVCard" checked="false" oncommand="ToggleAttachVCard(event.target)"/>
   <command id="cmd_close" oncommand="goDoCommand('cmd_close')"/>
   <command id="cmd_saveDefault" oncommand="goDoCommand('cmd_saveDefault')"/>
   <command id="cmd_saveAsFile" oncommand="goDoCommand('cmd_saveAsFile')"/>
   <command id="cmd_saveAsDraft" oncommand="goDoCommand('cmd_saveAsDraft')"/>
   <command id="cmd_saveAsTemplate" oncommand="goDoCommand('cmd_saveAsTemplate')"/>
   <command id="cmd_sendButton" oncommand="goDoCommand('cmd_sendButton')"/>
@@ -175,16 +179,20 @@
   <command id="minimizeWindow" label="&minimizeWindow.label;" oncommand="window.minimize();"/>
   <command id="zoomWindow" label="&zoomWindow.label;" oncommand="zoomWindow();"/>
   <command id="Tasks:Mail" oncommand="toMessengerWindow();"/>
   <command id="Tasks:AddressBook" oncommand="toAddressBook();"/>
 #endif
 
   <command id="cmd_CustomizeComposeToolbar"
            oncommand="CustomizeMailToolbar('compose-toolbox', 'CustomizeComposeToolbar')"/>
+
+  <command id="cmd_convertCloud" oncommand="convertSelectedToCloudAttachment(event.target.cloudProvider); event.stopPropagation();"/>
+  <command id="cmd_convertAttachment" oncommand="goDoCommand('cmd_convertAttachment')"/>
+  <command id="cmd_cancelUpload" oncommand="goDoCommand('cmd_cancelUpload')"/>
 </commandset>
 
 <broadcasterset id="composeBroadcasters">
   <broadcaster id="args" value="editorType=default"/>
   <broadcaster id="viewAddressPicker" autoCheck="false" type="checkbox" oncommand="toggleAddressPicker();"/>
 </broadcasterset>
 
 <keyset id="tasksKeys">
@@ -315,41 +323,62 @@
                 accesskey="&spellAddDictionaries.accesskey;"
                 oncommand="openDictionaryList();"/>
     </menupopup>
   </menu>
 
 </menupopup>
 
 <menupopup id="msgComposeAttachmentItemContext"
-           onpopupshowing="updateEditItems();">
+           onpopupshowing="updateAttachmentItems();">
   <menuitem label="&openAttachment.label;"
             accesskey="&openAttachment.accesskey;"
             command="cmd_openAttachment"/>
   <menuitem label="&removeAttachment.label;"
             accesskey="&removeAttachment.accesskey;"
             command="cmd_delete"/>
   <menuitem label="&renameAttachment.label;"
             accesskey="&renameAttachment.accesskey;"
             command="cmd_renameAttachment"/>
+  <menu label="&convertCloud.label;"
+        accesskey="&convertCloud.accesskey;"
+        command="cmd_convertCloud">
+    <menupopup onpopupshowing="addConvertCloudMenuItems(this, 'context_convertCloudSeparator', 'context_convertCloud');">
+      <menuitem id="context_convertAttachment"
+                type="radio" name="context_convertCloud"
+                label="&convertRegularAttachment.label;"
+                accesskey="&convertRegularAttachment.accesskey;"
+                command="cmd_convertAttachment"/>
+      <menuseparator id="context_convertCloudSeparator"/>
+    </menupopup>
+  </menu>
+  <menuitem id="context_cancelUpload"
+            label="&cancelUpload.label;"
+            accesskey="&cancelUpload.accesskey;"
+            command="cmd_cancelUpload"/>
   <menuseparator/>
   <menuitem label="&selectAll.label;"
             accesskey="&selectAll.accesskey;"
             command="cmd_selectAll"/>
 </menupopup>
 
 <menupopup id="msgComposeAttachmentListContext"
-           onpopupshowing="updateEditItems();">
+           onpopupshowing="updateAttachmentItems();">
   <menuitem label="&selectAll.label;"
             accesskey="&selectAll.accesskey;"
             command="cmd_selectAll"/>
   <menuseparator/>
   <menuitem label="&attachFile.label;"
             accesskey="&attachFile.accesskey;"
             command="cmd_attachFile"/>
+  <menu label="&attachCloud.label;"
+        accesskey="&attachCloud.accesskey;"
+        command="cmd_attachCloud">
+    <menupopup onpopupshowing="addAttachCloudMenuItems(this);"/>
+  </menu>
   <menuitem label="&attachPage.label;"
             accesskey="&attachPage.accesskey;"
             command="cmd_attachPage"/>
 </menupopup>
 
 <menupopup id="toolbar-context-menu"
            onpopupshowing="onViewToolbarsPopupShowing(event, 'compose-toolbox');">
   <menuseparator/>
@@ -395,19 +424,23 @@
                         key="key_newMessage2" oncommand="goOpenNewMessage();"/>
               <menuseparator/>
               <menuitem id="menu_NewContact" label="&newContact.label;"
                         accesskey="&newContact.accesskey;"
                         oncommand="window.openDialog('chrome://messenger/content/addressbook/abNewCardDialog.xul', '', 'chrome,modal,resizable=no,centerscreen');"/>
             </menupopup>
           </menu>
           <menu id="menu_Attach" label="&attachMenu.label;" accesskey="&attachMenu.accesskey;">
-            <menupopup id="menu_AttachPopup">
+            <menupopup id="menu_AttachPopup" onpopupshowing="updateAttachmentItems();">
               <menuitem label="&attachFileCmd.label;" accesskey="&attachFileCmd.accesskey;"
                         key="key_attachFile" command="cmd_attachFile"/>
+              <menu label="&attachCloudCmd.label;" accesskey="&attachCloudCmd.accesskey;"
+                    command="cmd_attachCloud">
+                <menupopup onpopupshowing="addAttachCloudMenuItems(this);"/>
+              </menu>
               <menuitem label="&attachPageCmd.label;" accesskey="&attachPageCmd.accesskey;" command="cmd_attachPage"/>
               <menuseparator/>
               <menuitem type="checkbox" label="&attachVCardCmd.label;" accesskey="&attachVCardCmd.accesskey;" command="cmd_attachVCard"/>
             </menupopup>
           </menu>
           <menuseparator/>
           <menuitem id="menu_SaveCmd" label="&saveCmd.label;" accesskey="&saveCmd.accesskey;"
                     key="key_save" command="cmd_saveDefault"/>
@@ -625,22 +658,33 @@
 
     <toolbarbutton class="toolbarbutton-1" type="menu-button"
                id="button-attach" label="&attachButton.label;" 
                tooltiptext="&attachButton.tooltip;" 
                command="cmd_attachFile"
                ondragover="nsDragAndDrop.dragOver(event, envelopeDragObserver);"
                ondragdrop="nsDragAndDrop.drop(event, envelopeDragObserver);"
                ondragexit="nsDragAndDrop.dragExit(event, envelopeDragObserver);">
-         <menupopup id="button-attachPopup">
-            <menuitem label="&attachFileCmd.label;" accesskey="&attachFileCmd.accesskey;" command="cmd_attachFile"/>
-            <menuitem label="&attachPageCmd.label;" accesskey="&attachPageCmd.accesskey;" command="cmd_attachPage"/>
-           <menuseparator/>
-           <menuitem type="checkbox" label="&attachVCardCmd.label;" accesskey="&attachVCardCmd.accesskey;" command="cmd_attachVCard"/>
-         </menupopup>
+      <menupopup id="button-attachPopup" onpopupshowing="updateAttachmentItems();">
+        <menuitem label="&attachFileCmd.label;"
+                  accesskey="&attachFileCmd.accesskey;"
+                  command="cmd_attachFile"/>
+        <menu label="&attachCloudCmd.label;"
+              accesskey="&attachCloudCmd.accesskey;"
+              command="cmd_attachCloud">
+          <menupopup onpopupshowing="addAttachCloudMenuItems(this);"/>
+        </menu>
+        <menuitem label="&attachPageCmd.label;"
+                  accesskey="&attachPageCmd.accesskey;"
+                  command="cmd_attachPage"/>
+        <menuseparator/>
+        <menuitem type="checkbox" label="&attachVCardCmd.label;"
+                  accesskey="&attachVCardCmd.accesskey;"
+                  command="cmd_attachVCard"/>
+      </menupopup>
     </toolbarbutton>
 
     <toolbarbutton class="toolbarbutton-1" type="menu-button"
                id="spellingButton" label="&spellingButton.label;" 
                tooltiptext="&spellingButton.tooltip;"
                command="cmd_spelling">
       <!-- this popup gets dynamically generated -->
       <menupopup id="languageMenuList" oncommand="ChangeLanguage(event);"
--- a/mail/components/compose/jar.mn
+++ b/mail/components/compose/jar.mn
@@ -1,11 +1,13 @@
 messenger.jar:
 % overlay chrome://editor/content/EdImageOverlay.xul chrome://messenger/content/messengercompose/mailComposeEditorOverlay.xul
 % overlay chrome://editor/content/EdImageOverlay.xul chrome://messenger/content/messengercompose/EdImageOverlayOverlay.xul
 % overlay chrome://editor/content/EdLinkProps.xul chrome://messenger/content/messengercompose/mailComposeEditorOverlay.xul
 *   content/messenger/messengercompose/messengercompose.xul        (content/messengercompose.xul)
     content/messenger/messengercompose/MsgComposeCommands.js       (content/MsgComposeCommands.js)
+    content/messenger/messengercompose/bigFileObserver.js          (content/bigFileObserver.js)
+    content/messenger/messengercompose/cloudAttachmentLinkManager.js (content/cloudAttachmentLinkManager.js)
     content/messenger/messengercompose/EdImageOverlayOverlay.xul   (content/EdImageOverlayOverlay.xul)
 *   content/messenger/messengercompose/addressingWidgetOverlay.js  (content/addressingWidgetOverlay.js)
 
 comm.jar:
 *+  content/editor/editorOverlay.xul                               (content/editorOverlay.xul) 
--- a/mail/components/preferences/applications.js
+++ b/mail/components/preferences/applications.js
@@ -71,16 +71,21 @@ const ICON_URL_APP      = "chrome://mess
 /*
 #endif
 */
 
 // For CSS. Can be one of "ask", "save", "plugin" or "feed". If absent, the icon URL
 // was set by us to a custom handler icon and CSS should not try to override it.
 const APP_ICON_ATTR_NAME = "appHandlerIcon";
 
+// CloudFile account tools used by gCloudFileTab.
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/cloudFileAccounts.js");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 //****************************************************************************//
 // Utilities
 
 function getDisplayNameForFile(aFile) {
 /*
 #ifdef XP_WIN
 */
   if (aFile instanceof Components.interfaces.nsILocalFileWin) {
@@ -435,16 +440,446 @@ HandlerInfoWrapper.prototype = {
       return "moz-icon://goat?size=" + aSize + "&contentType=" + this.type;
 
     // FIXME: consider returning some generic icon when we can't get a URL for
     // one (for example in the case of protocol schemes).  Filed as bug 395141.
     return null;
   }
 };
 
+var gApplicationsTabController = {
+  mInitialized: false,
+  // We default to displaying the Outgoing tab, which is the tab at index 1
+  // of the attachmentPrefs tabs.
+  mDefaultIndex: 1,
+
+  init: function() {
+    if (this.mInitialized)
+      return;
+
+    gApplicationsPane.init();
+
+    let tabbox = document.getElementById("attachmentPrefs");
+
+    // If BigFiles is disabled, hide the "Outgoing" tab, and the tab
+    // selectors, and bail out.
+    if (!Services.prefs.getBoolPref("mail.cloud_files.enabled")) {
+      // Default to the first tab, "Incoming"
+      tabbox.selectedIndex = 0;
+      // Hide the tab selector
+      let tabs = document.getElementById("attachmentPrefsTabs");
+      tabs.hidden = true;
+      this.mInitialized = true;
+      return;
+    }
+
+    gCloudFileTab.init();
+
+    let preference = document.getElementById("mail.preferences.applications.selectedTabIndex");
+    tabbox.selectedIndex = preference.value ? preference.value : this.mDefaultIndex;
+
+    this.mInitialized = true;
+  },
+
+  tabSelectionChanged: function() {
+    if (this.mInitialized)
+      document.getElementById("mail.preferences.applications.selectedTabIndex")
+              .valueFromPreferences = document.getElementById("attachmentPrefs")
+              .selectedIndex;
+  },
+
+}
+
+var gCloudFileController = {
+  commands: {
+    cmd_addCloudfileAccount: {
+      isEnabled: function() {
+        return true;
+      },
+      doCommand: function() {
+        gCloudFileTab.addCloudFileAccount();
+      },
+    },
+
+    cmd_removeCloudfileAccount: {
+      isEnabled: function() {
+        let listbox = document.getElementById("cloudFileView");
+        return listbox.selectedCount > 0;
+      },
+      doCommand: function() {
+        gCloudFileTab.removeCloudFileAccount();
+      },
+    },
+
+    cmd_reauthCloudfileAccount: {
+      isEnabled: function() {
+        return true;
+      },
+      doCommand: function() {
+        gCloudFileTab.authSelected();
+      },
+    },
+
+  },
+
+  supportsCommand: function(aCommand) {
+    return (aCommand in this.commands);
+  },
+
+  isCommandEnabled: function(aCommand) {
+    if (!this.supportsCommand(aCommand))
+      return false;
+    return this.commands[aCommand].isEnabled();
+  },
+
+  doCommand: function(aCommand) {
+    if (!this.supportsCommand(aCommand))
+      return;
+
+    let cmd = this.commands[aCommand];
+
+    if (!cmd.isEnabled())
+      return;
+
+    cmd.doCommand();
+  },
+  onEvent: function(event) {},
+}
+
+function CommandUpdate_CloudFile() {
+  goUpdateCommand("cmd_removeCloudfileAccount");
+  goUpdateCommand("cmd_addCloudfileAccount");
+}
+
+var gCloudFileTab = {
+  _initialized: false,
+  _list: null,
+  _settings: null,
+  _settingsDeck: null,
+  _tabpanel: null,
+  _accountCache: {},
+  _settingsPanelWrap: null,
+  _defaultPanel: null,
+  _loadingPanel: null,
+  _authErrorPanel: null,
+
+  get _strings() {
+    return Services.strings
+                   .createBundle("chrome://messenger/locale/preferences/applications.properties");
+  },
+
+  init: function() {
+    if (this._initialized)
+      return;
+
+    this._list = document.getElementById("cloudFileView");
+    this._settingsDeck = document.getElementById("cloudFileSettingsDeck");
+    this._defaultPanel = document.getElementById("cloudFileDefaultPanel");
+    this._settingsPanelWrap = document.getElementById("cloudFileSettingsWrapper");
+    this._loadingPanel = document.getElementById("cloudFileLoadingPanel");
+    this._authErrorPanel = document.getElementById("cloudFileAuthErrorPanel");
+
+    top.controllers.appendController(gCloudFileController);
+
+    this.rebuildView();
+
+    if (this._list.itemCount > 0)
+      this._list.selectedIndex = 0;
+
+    window.addEventListener("unload", this, false);
+    CommandUpdate_CloudFile();
+
+    this._initialized = true;
+  },
+
+  destroy: function CFT_destroy() {
+    // Remove any controllers or observers here.
+    top.controllers.removeController(gCloudFileController);
+    window.removeEventListener("unload", this, false);
+  },
+
+  makeRichListItemForAccount: function CFT_makeRichListItemForAccount(aAccount) {
+    let rli = document.createElement("richlistitem");
+    rli.value = aAccount.accountKey;
+    rli.setAttribute("value", aAccount.accountKey);
+    rli.setAttribute("class", "cloudfileAccount");
+    rli.setAttribute("state", "waiting-to-connect");
+
+    if (aAccount.iconClass)
+      rli.style.listStyleImage = "url('" + aAccount.iconClass + "')";
+
+    let displayName = cloudFileAccounts.getDisplayName(aAccount.accountKey);
+    // Quick and ugly - accountKey:displayName for now
+    let status = document.createElement("image");
+    status.setAttribute("class", "typeIcon");
+
+    rli.appendChild(status);
+    let descr = document.createElement("label");
+    descr.setAttribute("value", displayName);
+    rli.appendChild(descr);
+
+    // Set the state of the richlistitem, if applicable
+    if (aAccount.accountKey in this._accountCache) {
+      let result = this._accountCache[aAccount.accountKey].result;
+      this._mapResultToState(rli, result);
+      this._accountCache[aAccount.accountKey].listItem = rli;
+    }
+
+    return rli;
+  },
+
+  clearEntries: function CFT_clearEntries() {
+    // Clear the list of entries.
+    while (this._list.childNodes.length > 0)
+      this._list.removeChild(this._list.lastChild);
+  },
+
+  rebuildView: function CFT_rebuildView() {
+    this.clearEntries();
+    let accounts = cloudFileAccounts.accounts;
+
+    // Sort the accounts by displayName.
+    function sortAccounts(a, b) {
+      let aName = cloudFileAccounts.getDisplayName(a.accountKey)
+                                   .toLowerCase();
+      let bName = cloudFileAccounts.getDisplayName(b.accountKey)
+                                   .toLowerCase();
+
+      if (aName < bName)
+        return -1;
+      if (aName > bName)
+        return 1;
+      return 0;
+    }
+
+    accounts.sort(sortAccounts);
+
+    for (let [, account] in Iterator(accounts)) {
+      let rli = this.makeRichListItemForAccount(account);
+      this._list.appendChild(rli);
+      if (!(account.accountKey in this._accountCache))
+        this.requestUserInfoForItem(rli, false);
+    }
+  },
+
+  requestUserInfoForItem: function CFT_requestUserInfoForItem(aItem, aWithUI) {
+    let Cr = Components.results;
+    let accountKey = aItem.value;
+    let account = cloudFileAccounts.getAccount(accountKey);
+
+    let observer = {
+      onStopRequest: function(aRequest, aContext, aStatusCode) {
+        gCloudFileTab._accountCache[accountKey].result = aStatusCode;
+        gCloudFileTab.onUserInfoRequestDone(accountKey);
+      },
+      onStartRequest: function(aRequest, aContext) {
+        aItem.setAttribute("state", "connecting");
+      },
+    };
+
+    let accountInfo = {account: account,
+                       listItem: aItem,
+                       result: Cr.NS_ERROR_NOT_AVAILABLE}
+
+    this._accountCache[accountKey] = accountInfo;
+
+    this._settingsDeck.selectedPanel = this._loadingPanel;
+    account.refreshUserInfo(aWithUI, observer);
+  },
+
+  onUserInfoRequestDone: function CFT_onUserInfoRequestDone(aAccountKey) {
+    this.updateRichListItem(aAccountKey);
+
+    if (this._list.selectedItem &&
+        this._list.selectedItem.value == aAccountKey)
+      this._showAccountInfo(aAccountKey);
+  },
+
+  updateRichListItem: function CFT_updateRichListItem(aAccountKey) {
+    let accountInfo = this._accountCache[aAccountKey];
+    if (!accountInfo)
+      return;
+
+    let item = accountInfo.listItem;
+    let result = accountInfo.result;
+    this._mapResultToState(item, result);
+  },
+
+  _mapResultToState: function CFT__mapResultToState(aItem, aResult) {
+    let Cr = Components.results;
+    let Ci = Components.interfaces;
+    let itemState = "no-connection";
+
+    if (aResult == Cr.NS_OK)
+      itemState = "connected";
+    else if (aResult == Ci.nsIMsgCloudFileProvider.authErr)
+      itemState = "auth-error";
+    else if (aResult == Cr.NS_ERROR_NOT_AVAILABLE)
+      itemState = "no-connection";
+    // TODO: What other states are there?
+
+    aItem.setAttribute("state", itemState);
+  },
+
+
+  onSelectionChanged: function CFT_onSelectionChanged() {
+    // Get the selected item
+    let selection = this._list.selectedItem;
+    if (!selection)
+      return;
+
+    // The selection tells us the key.  We need the actual
+    // provider here.
+    let accountKey = selection.value;
+    this._showAccountInfo(accountKey);
+  },
+
+  _showAccountInfo: function CFT__showAccountInfo(aAccountKey) {
+    let Ci = Components.interfaces;
+    let Cr = Components.results;
+    let account = this._accountCache[aAccountKey].account;
+    let result = this._accountCache[aAccountKey].result;
+
+    if (result == Cr.NS_ERROR_NOT_AVAILABLE)
+      this._settingsDeck.selectedPanel = this._loadingPanel;
+    else if (result == Cr.NS_OK) {
+      this._settingsDeck.selectedPanel = this._settingsPanelWrap;
+      this._showAccountManagement(account);
+    }
+    else if (result == Ci.nsIMsgCloudFileProvider.authErr) {
+      this._settingsDeck.selectedPanel = this._authErrorPanel;
+    }
+    else {
+      // TODO Handle this case...
+      dump("\n\nSome other error...\n\n");
+    }
+  },
+
+  _showAccountManagement: function CFT__showAccountManagement(aProvider) {
+    let iframe = document.createElement('iframe');
+
+    iframe.setAttribute("src", aProvider.managementURL);
+    iframe.setAttribute("flex", "1");
+
+    // If we have a past iframe, we replace it.  Else, append
+    // to the wrapper.
+    if (this._settings && this._settings.parentNode)
+      this._settings.parentNode.removeChild(this._settings);
+
+    this._settingsPanelWrap.appendChild(iframe);
+    this._settings = iframe;
+
+    // When the iframe loads, populate it with the provider.
+    this._settings.contentWindow
+                  .addEventListener("load", function(e) {
+
+      iframe.contentWindow.removeEventListener("load",
+                                               arguments.callee,
+                                               false);
+      try {
+        iframe.contentWindow
+              .wrappedJSObject
+              .onLoadProvider(aProvider);
+      } catch(e) {
+        Components.utils.reportError(e);
+      }
+    }, false);
+
+    // When the iframe (or any subcontent) fires the DOMContentLoaded event,
+    // attach the _onClickLink handler to any anchor elements that we can find.
+    this._settings.contentWindow
+                  .addEventListener("DOMContentLoaded", function(e) {
+
+      iframe.contentWindow.removeEventListener("DOMContentLoaded",
+                                               arguments.callee,
+                                               false);
+
+      let doc = e.originalTarget;
+      let links = doc.getElementsByTagName("a");
+
+      for (let [, link] in Iterator(links))
+        link.addEventListener("click", gCloudFileTab._onClickLink);
+
+    }, false);
+
+    CommandUpdate_CloudFile();
+  },
+
+  _onClickLink: function CFT__onClickLink(aEvent) {
+    aEvent.preventDefault();
+    let href = aEvent.target.getAttribute("href");
+    openLinkExternally(href);
+  },
+
+  authSelected: function CFT_authSelected() {
+    let item = this._list.selectedItem;
+
+    if (!item)
+      return;
+
+    this.requestUserInfoForItem(item, true);
+  },
+
+  addCloudFileAccount: function CFT_addCloudFileAccount() {
+    let accountKey = cloudFileAccounts.addAccountDialog();
+    if (accountKey)
+      this.rebuildView();
+
+    let newItem = this._list.querySelector("richlistitem[value='" + accountKey + "']");
+    this._list.selectItem(newItem);
+  },
+
+  removeCloudFileAccount: function CFT_removeCloudFileAccount() {
+    // Get the selected account key
+    let selection = this._list.selectedItem;
+    if (!selection)
+      return;
+
+    let accountKey = selection.value;
+    let accountName = cloudFileAccounts.getDisplayName(accountKey);
+    // Does the user really want to remove this account?
+    let confirmMessage = this._strings
+                             .formatStringFromName("dialog_removeAccount",
+                                                   [accountName], 1);
+
+    if (confirm(confirmMessage)) {
+      this._list.clearSelection();
+      cloudFileAccounts.removeAccount(accountKey);
+      this.rebuildView();
+      this._settingsDeck.selectedPanel = this._defaultPanel;
+      delete this._accountCache[accountKey];
+      // For some reason, the focus event isn't fired, so I think
+      // we have to update the buttons manually...
+      CommandUpdate_CloudFile();
+    }
+  },
+
+  handleEvent: function CFT_handleEvent(aEvent) {
+    if (aEvent.type == "unload")
+      this.destroy();
+  },
+
+  readThreshold: function CFT_readThreshold() {
+    let pref = document.getElementById("mail.compose.big_attachments.threshold_kb");
+    return pref.value / 1024;
+  },
+
+  writeThreshold: function CFT_writeThreshold() {
+    let threshold = document.getElementById("cloudFileThreshold");
+    let intValue = parseInt(threshold.value, 10);
+    return isNaN(intValue) ? 0 : intValue * 1024;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Components.interfaces
+                                                   .nsIObserver,
+                                         Components.interfaces
+                                                   .nsISupportsWeakReference]),
+
+}
+
 //****************************************************************************//
 // Prefpane Controller
 
 var gApplicationsPane = {
   // The set of types the app knows how to handle.  A hash of HandlerInfoWrapper
   // objects, indexed by type.
   _handledTypes: {},
 
@@ -913,16 +1348,20 @@ var gApplicationsPane = {
   },
 
   /**
    * Rebuild the actions menu for the selected entry.  Gets called by
    * the richlistitem constructor when an entry in the list gets selected.
    */
   rebuildActionsMenu: function() {
     var typeItem = this._list.selectedItem;
+
+    if (!typeItem)
+      return;
+
     var handlerInfo = this._handledTypes[typeItem.type];
     var menu =
       document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
     var menuPopup = menu.menupopup;
 
     // Clear out existing items.
     while (menuPopup.hasChildNodes())
       menuPopup.removeChild(menuPopup.lastChild);
--- a/mail/components/preferences/applications.xul
+++ b/mail/components/preferences/applications.xul
@@ -46,19 +46,24 @@
   %brandDTD;
   %applicationsDTD;
 ]>
 
 <overlay id="ApplicationsPaneOverlay"
          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <prefpane id="paneApplications"
-            onpaneload="gApplicationsPane.init();"
+            onpaneload="gApplicationsTabController.init();"
             flex="1">
 
+    <preferences id="applicationsPreferences">
+      <preference id="mail.preferences.applications.selectedTabIndex"
+                  name="mail.preferences.applications.selectedTabIndex" type="int"/>
+    </preferences>
+
     <preferences id="downloadDirPreferences">
       <preference id="browser.download.useDownloadDir"
                   name="browser.download.useDownloadDir"
                   type="bool"/>
       <preference id="browser.download.folderList"
                   name="browser.download.folderList"
                   type="int"/>
       <preference id="browser.download.downloadDir"
@@ -68,89 +73,189 @@
                   name="browser.download.dir"
                   type="file"
                   onchange="gDownloadDirSection.readDownloadDirPref();"/>
       <preference id="pref.downloads.disable_button.edit_actions"
                   name="pref.downloads.disable_button.edit_actions"
                   type="bool"/>
     </preferences>
 
+    <preferences id="cloudFilePreferences">
+      <preference id="mail.compose.big_attachments.notify"
+                  name="mail.compose.big_attachments.notify"
+                  type="bool"/>
+      <preference id="mail.compose.big_attachments.threshold_kb"
+                  name="mail.compose.big_attachments.threshold_kb"
+                  type="int"/>
+    </preferences>
+
     <script type="application/javascript"
             src="chrome://messenger/content/preferences/applications.js"/>
 
     <commandset id="appPaneCommandSet">
       <command id="cmd_delete"
                oncommand="gApplicationsPane.onDelete();"/>
     </commandset>
 
+    <commandset id="cloudFileCommandSet">
+      <commandset id="cloudFileCommandSetUpdate"
+                  commandupdater="true"
+                  events="focus"
+                  oncommandupdate="CommandUpdate_CloudFile()"/>
+      <command id="cmd_addCloudfileAccount"
+               oncommand="goDoCommand('cmd_addCloudfileAccount');"/>
+      <command id="cmd_removeCloudfileAccount"
+               oncommand="goDoCommand('cmd_removeCloudfileAccount');"/>
+      <command id="cmd_reauthCloudfileAccount"
+               oncommand="goDoCommand('cmd_reauthCloudfileAccount');"/>
+
+    </commandset>
+
     <keyset id="appPaneKeyset">
       <key keycode="VK_BACK" modifiers="any" command="cmd_delete"/>
       <key keycode="VK_DELETE" modifiers="any" command="cmd_delete"/>
     </keyset>
 
     <keyset>
       <key key="&focusSearch1.key;" modifiers="accel"
            oncommand="gApplicationsPane.focusFilterBox();"/>
       <key key="&focusSearch2.key;" modifiers="accel"
            oncommand="gApplicationsPane.focusFilterBox();"/>
     </keyset>
 
-    <hbox>
-      <textbox id="filter" flex="1"
-               type="search"
-               placeholder="&filter.placeholder;"
-               aria-controls="handlersView"
-               oncommand="gApplicationsPane.rebuildView();"/>
-    </hbox>
+
+    <tabbox id="attachmentPrefs" flex="1" onselect="gApplicationsTabController.tabSelectionChanged();">
+      <tabs id="attachmentPrefsTabs">
+        <tab label="&attachments.incoming.label;"/>
+        <tab label="&attachments.outgoing.label;"/>
+      </tabs>
 
-    <separator class="thin"/>
+      <tabpanels flex="1">
+        <tabpanel orient="vertical">
+          <hbox>
+            <textbox id="filter" flex="1"
+                     type="search"
+                     placeholder="&filter.placeholder;"
+                     aria-controls="handlersView"
+                     oncommand="gApplicationsPane.rebuildView();"/>
+          </hbox>
+
+          <separator class="thin"/>
 
-    <richlistbox id="handlersView" orient="vertical" persist="lastSelectedType"
-                 preference="pref.downloads.disable_button.edit_actions"
-                 onselect="gApplicationsPane.onSelectionChanged();">
-      <listheader equalsize="always" style="border: 0; padding: 0; -moz-appearance: none;">
-        <treecol id="typeColumn" label="&typeColumn.label;" value="type"
-                 accesskey="&typeColumn.accesskey;" persist="sortDirection"
-                 flex="1" onclick="gApplicationsPane.sort(event);"
-                 sortDirection="ascending" sort="typeDescription"/>
-        <treecol id="actionColumn" label="&actionColumn2.label;" value="action"
-                 accesskey="&actionColumn2.accesskey;" persist="sortDirection"
-                 flex="1" onclick="gApplicationsPane.sort(event);"/>
-      </listheader>
-    </richlistbox>
+          <richlistbox id="handlersView" orient="vertical" persist="lastSelectedType"
+                       preference="pref.downloads.disable_button.edit_actions"
+                       onselect="gApplicationsPane.onSelectionChanged();">
+            <listheader equalsize="always" style="border: 0; padding: 0; -moz-appearance: none;">
+              <treecol id="typeColumn" label="&typeColumn.label;" value="type"
+                       accesskey="&typeColumn.accesskey;" persist="sortDirection"
+                       flex="1" onclick="gApplicationsPane.sort(event);"
+                       sortDirection="ascending" sort="typeDescription"/>
+              <treecol id="actionColumn" label="&actionColumn2.label;" value="action"
+                       accesskey="&actionColumn2.accesskey;" persist="sortDirection"
+                       flex="1" onclick="gApplicationsPane.sort(event);"/>
+            </listheader>
+          </richlistbox>
 
-    <separator class="thin"/>
+          <separator class="thin"/>
 
-    <script type="application/javascript"
-            src="chrome://messenger/content/preferences/downloads.js"/>
+          <script type="application/javascript"
+                  src="chrome://messenger/content/preferences/downloads.js"/>
 
-    <vbox align="start">
-      <radiogroup id="saveWhere" flex="1"
-                  preference="browser.download.useDownloadDir"
-                  onsyncfrompreference="return gDownloadDirSection.onReadUseDownloadDir();">
-      <hbox id="saveToRow">
-        <radio id="saveTo" value="true"
-               label="&saveTo.label;"
-               accesskey="&saveTo.accesskey;"
-               aria-labelledby="saveTo downloadFolder"/>
-        <filefield id="downloadFolder" flex="1"
-                   preference="browser.download.folderList"
-                   preference-editable="true"
-                   aria-labelledby="saveTo"
-                   onsyncfrompreference="return gDownloadDirSection.readDownloadDirPref();"
-                   onsynctopreference="return gDownloadDirSection.writeFolderList();"/>
-        <button id="chooseFolder" oncommand="gDownloadDirSection.chooseFolder();"
+          <vbox align="start">
+            <radiogroup id="saveWhere" flex="1"
+                        preference="browser.download.useDownloadDir"
+                        onsyncfrompreference="return gDownloadDirSection.onReadUseDownloadDir();">
+            <hbox id="saveToRow">
+              <radio id="saveTo" value="true"
+                     label="&saveTo.label;"
+                     accesskey="&saveTo.accesskey;"
+                     aria-labelledby="saveTo downloadFolder"/>
+              <filefield id="downloadFolder" flex="1"
+                         preference="browser.download.folderList"
+                         preference-editable="true"
+                         aria-labelledby="saveTo"
+                         onsyncfrompreference="return gDownloadDirSection.readDownloadDirPref();"
+                         onsynctopreference="return gDownloadDirSection.writeFolderList();"/>
+              <button id="chooseFolder" oncommand="gDownloadDirSection.chooseFolder();"
 #ifdef XP_MACOSX
-                accesskey="&chooseFolderMac.accesskey;"
-                label="&chooseFolderMac.label;"
+                      accesskey="&chooseFolderMac.accesskey;"
+                      label="&chooseFolderMac.label;"
 #else
-                accesskey="&chooseFolderWin.accesskey;"
-                label="&chooseFolderWin.label;"
+                      accesskey="&chooseFolderWin.accesskey;"
+                      label="&chooseFolderWin.label;"
 #endif
-                preference="browser.download.folderList"
-                onsynctopreference="return gDownloadDirSection.writeFolderList();"/>
-      </hbox>
-      <radio id="alwaysAsk" value="false"
-             label="&alwaysAsk.label;" accesskey="&alwaysAsk.accesskey;"/>
-      </radiogroup>
-    </vbox>
+                      preference="browser.download.folderList"
+                      onsynctopreference="return gDownloadDirSection.writeFolderList();"/>
+            </hbox>
+            <radio id="alwaysAsk" value="false"
+                   label="&alwaysAsk.label;" accesskey="&alwaysAsk.accesskey;"/>
+            </radiogroup>
+          </vbox>
+        </tabpanel>
+
+        <tabpanel orient="vertical">
+          <vbox flex="1">
+            <hbox id="cloudFileToggleAndThreshold" align="center">
+              <checkbox label="&enableCloudFileAccountOffer.label;"
+                        preference="mail.compose.big_attachments.notify"/>
+              <textbox id="cloudFileThreshold" type="number" increment="1"
+                       maxlength="4" size="1"
+                       preference="mail.compose.big_attachments.threshold_kb"
+                       onsyncfrompreference="return gCloudFileTab.readThreshold();"
+                       onsynctopreference="return gCloudFileTab.writeThreshold();"/>
+              <label control="cloudFileThreshold"
+                     value="&enableCloudFileAccountOffer.mb;"/>
+            </hbox>
+            <hbox flex="1">
+              <vbox flex="1" id="provider-listing">
+                <richlistbox id="cloudFileView" orient="vertical" flex="1"
+                             seltype="single"
+                             onselect="gCloudFileTab.onSelectionChanged();">
+                </richlistbox>
+                <separator class="thin"/>
+                <hbox>
+                  <button id="addCloudFileAccount" flex="1" label="&addCloudFileAccount.label;"
+                          accesskey="&addCloudFileAccount.accesskey;"
+                          command="cmd_addCloudfileAccount"/>
+                  <button id="removeCloudFileAccount" flex="1"
+                          label="&removeCloudFileAccount.label;"
+                          accesskey="&removeCloudFileAccount.accesskey;"
+                          command="cmd_removeCloudfileAccount"/>
+                </hbox>
+              </vbox>
+              <vbox flex="1">
+                <deck id="cloudFileSettingsDeck" flex="1">
+                  <vbox id="cloudFileDefaultPanel" flex="1">
+                    <description>&addCloudFileAccount.description;</description>
+                  </vbox>
+                  <vbox id="cloudFileSettingsWrapper" flex="1">
+                  </vbox>
+                  <vbox id="cloudFileLoadingPanel" flex="1">
+                    <hbox>
+                      <spring flex="1"/>
+                        <image id="cloudFileLoadingSpinner" />
+                      <spring flex="1"/>
+                    </hbox>
+                  </vbox>
+                  <vbox id="cloudFileAuthErrorPanel" flex="1">
+                    <spacer flex="1"/>
+                    <hbox>
+                      <spring flex="1"/>
+                        <image id="authImage"/>
+                      <spring flex="1"/>
+                    </hbox>
+                    <description id="authMessage">&authRequired.description;</description>
+                    <hbox>
+                      <spring flex="1"/>
+                        <button command="cmd_reauthCloudfileAccount" label="&authRequired.button.label;" accesskey="&authRequired.button.accesskey;"/>
+                      <spring flex="1"/>
+                     </hbox>
+                    <spacer flex="1"/>
+                  </vbox>
+                </deck>
+              </vbox>
+            </hbox>
+          </vbox>
+        </tabpanel>
+      </tabpanels>
+    </tabbox>
   </prefpane>
 </overlay>
--- a/mail/components/preferences/preferences.xul
+++ b/mail/components/preferences/preferences.xul
@@ -87,16 +87,19 @@
 #else
             style="&prefWindow.styleGNOME;">
 #endif
 #endif
   <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/>
   <stringbundle id="bundlePreferences"
                 src="chrome://messenger/locale/preferences/preferences.properties"/>
 
+  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
+  <script type="application/javascript" src="chrome://communicator/content/contentAreaClick.js"/>
+
   <prefpane id="paneGeneral" label="&paneGeneral.title;"
             src="chrome://messenger/content/preferences/general.xul"/>
   <prefpane id="paneDisplay" label="&paneDisplay.title;"
             src="chrome://messenger/content/preferences/display.xul"/>
   <prefpane id="paneCompose" label="&paneComposition.title;"
             src="chrome://messenger/content/preferences/compose.xul"/>
   <prefpane id="paneSecurity" label="&paneSecurity.title;"
             src="chrome://messenger/content/preferences/security.xul"/>
--- a/mail/installer/package-manifest.in
+++ b/mail/installer/package-manifest.in
@@ -137,16 +137,18 @@
 @BINPATH@/defaults/profile/mimeTypes.rdf
 
 @BINPATH@/isp/*
 
 @BINPATH@/components/components.manifest
 @BINPATH@/components/aboutRedirector.js
 @BINPATH@/components/activity.xpt
 @BINPATH@/components/activityComponents.manifest
+@BINPATH@/components/cloudFileComponents.manifest
+@BINPATH@/components/cloudfile.xpt
 @BINPATH@/components/addrbook.xpt
 @BINPATH@/components/fts3tok.xpt
 ; interfaces.manifest doesn't get packaged because it is dynamically
 ; re-created at packaging time when linking the xpts that will actually
 ; go into the package, so the test related interfaces aren't included.
 @BINPATH@/components/jetpack.xpt
 @BINPATH@/components/mime.xpt
 @BINPATH@/components/steel.xpt
@@ -162,16 +164,19 @@
 @BINPATH@/components/import.xpt
 @BINPATH@/components/mailview.xpt
 @BINPATH@/components/mailprofilemigration.xpt
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/nsActivity.js
 @BINPATH@/components/nsActivityManager.js
 @BINPATH@/components/nsActivityManagerUI.js
+@BINPATH@/components/nsYouSendIt.js
+@BINPATH@/components/nsDropbox.js
+@BINPATH@/components/cloudFileAccounts.js
 @BINPATH@/components/nsAddrbook.manifest
 @BINPATH@/components/nsMailNewsCommandLineHandler.js
 @BINPATH@/components/services-crypto-component.xpt
 #ifndef XP_OS2
 @BINPATH@/components/shellservice.xpt
 #endif
 @BINPATH@/components/xpcom_base.xpt
 @BINPATH@/components/xpcom_system.xpt
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/cloudfile/Dropbox/management.dtd
@@ -0,0 +1,4 @@
+<!-- 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/.  -->
+<!ENTITY dropboxMgmt.viewSettings "View my account settings on dropbox.com">
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/cloudfile/YouSendIt/management.dtd
@@ -0,0 +1,4 @@
+<!-- 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/.  -->
+<!ENTITY youSendItMgmt.viewSettings "View my account settings on yousendit.com">
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/cloudfile/YouSendIt/settings.dtd
@@ -0,0 +1,5 @@
+<!-- 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/.  -->
+<!ENTITY youSendItSettings.username "Username:">
+<!ENTITY youSendItSettings.needAnAccount "Need an account?">
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/cloudfile/addAccountDialog.dtd
@@ -0,0 +1,12 @@
+<!-- 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/.  -->
+<!ENTITY addAccountDialog.title   "Set up Filelink">
+<!ENTITY addAccountDialog.menuTitle "Select an online storage service">
+<!ENTITY addAccountDialog.style "width: 40em; min-height: 20em;">
+<!ENTITY addAccountDialog.accountName.label "Account Name:">
+<!ENTITY addAccountDialog.noAccountText "We're sorry, but the current version of &brandShortName; only allows one account from each online storage service.">
+<!ENTITY addAccountDialog.createAccountText "This feature allows you to send large attachments using one of several online storage services. You can either connect to an existing account, or sign up for a new account.">
+<!ENTITY addAccountDialog.authorizing "Checking authorization...">
+<!ENTITY addAccountDialog.error "An error occurred while setting up the account!">
+<!ENTITY addAccountDialog.acceptButton.label "Set up Account">
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/cloudfile/management.dtd
@@ -0,0 +1,8 @@
+<!-- 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/.  -->
+<!ENTITY cloudfileMgmt.privacyPolicy "Privacy Policy">
+<!ENTITY cloudfileMgmt.termsOfService "Terms of Service">
+<!ENTITY cloudfileMgmt.usedSpace "Used Space:">
+<!ENTITY cloudfileMgmt.unusedSpace "Unused Space:">
+<!ENTITY cloudfileMgmt.upgradeOffer "Get more space">
--- a/mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
+++ b/mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
@@ -267,16 +267,20 @@ 12600=Unable to authenticate to SMTP ser
 ## Strings use for the save message dialog shown when the user close a message compose window
 saveDlogTitle=Save Message
 saveDlogMessage=Message has not been sent. Do you want to save the message in the Drafts folder?
 
 ## generics string
 defaultSubject=(no subject)
 chooseFileToAttach=Attach File(s)
 
+# LOCALIZATION NOTE (chooseFileToAttachViaCloud): %1$S is the cloud
+# provider to save the file to.
+chooseFileToAttachViaCloud=Attach File(s) via %1$S
+
 ##
 windowTitlePrefix=Write:
 
 ## Strings used by the empty subject dialog
 subjectEmptyTitle=Subject Reminder
 subjectEmptyMessage=Your message doesn't have a subject.
 sendWithEmptySubjectButton=&Send Without Subject
 cancelSendingButton=&Cancel Sending
@@ -359,24 +363,102 @@ attachmentReminderMsg=Did you forget to 
 # LOCALIZATION NOTE (attachmentReminderKeywordsMsgs): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/Localization_and_Plurals
 # #1 number of keywords
 attachmentReminderKeywordsMsgs=Found an attachment keyword:;Found #1 attachment keywords:
 attachmentReminderOptionsMsg=Attachment reminder words can be configured in your preferences
 attachmentReminderYesIForgot=Oh, I did!
 attachmentReminderFalseAlarm=No, Send Now
 
+# LOCALIZATION NOTE (bigFileDescription): Semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/Localization_and_Plurals
+# #1 number of big attached files
+bigFileDescription=This is a large file. It might be better to use Filelink instead.;These are large files. It might be better to use Filelink instead.
+bigFileShare.label=Link
+bigFileShare.accesskey=l
+bigFileAttach.label=Ignore
+bigFileAttach.accesskey=i
+
+bigFileChooseAccount.title=Choose Account
+bigFileChooseAccount.text=Choose a cloud account to upload the attachment to
+
+bigFileHideNotification.title=Don't Upload My Files
+bigFileHideNotification.text=You won't be notified if you attach more big files to this message.
+bigFileHideNotification.check=Never notify me of this again.
+
+# LOCALIZATION NOTE(cloudFileUploadingTooltip): Do not translate the string
+# %S. %S is the display name for the cloud account the attachment is being
+# uploaded to.
+cloudFileUploadingTooltip=Uploading to %S…
+# LOCALIZATION NOTE(cloudFileUploadedTooltip): Do not translate the string
+# %S. %S is the display name for the cloud account the attachment was uploaded
+# to.
+cloudFileUploadedTooltip=Uploaded to %S
+cloudFileUploadingNotification=Your file is being linked. It will appear in the body of the message when it's done.;Your files are being linked. They will appear in the body of the message when they're done.
+cloudFileUploadingCancel.label=Cancel
+cloudFileUploadingCancel.accesskey=c
+
 ## LOCALIZATION NOTE(smtpEnterPasswordPrompt): Do not translate the
 ## word $S. Place the word $S where the host name should appear.
 smtpEnterPasswordPrompt=Enter your password for %S:
 ## LOCALIZATION NOTE(smtpEnterPasswordPromptWithUsername): Do not translate the
 ## words %1$S and %2$S. Place the word %1$S where the host name should appear,
 ## and %2$S where the user name should appear.
 smtpEnterPasswordPromptWithUsername=Enter your password for %2$S on %1$S:
 smtpEnterPasswordPromptTitle=SMTP Server Password Required
 
 # LOCALIZATION NOTE (removeAttachmentMsgs): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/Localization_and_Plurals
 removeAttachmentMsgs=Remove Attachment;Remove Attachments
 
 ## LOCALIZATION NOTE(errorSavingMsg): Do not translate the word %S. It
 ## will be replaced with the name of the folder the message is being saved to.
 errorSavingMsg=There was an error saving the message to %S. Retry?
+
+errorCloudFileAuth.title=Authentication Error
+## LOCALIZATION NOTE(errorCloudFileAuth.message):
+## %1$S is the name of the online storage service that authentication failed against.
+errorCloudFileAuth.message=Unable to authenticate to %1$S.
+errorCloudFileUpload.title=Upload Error
+## LOCALIZATION NOTE(errorCloudFileUpload.message):
+## %1$S is the name of the online storage service that uploading failed against.
+## %2$S is the name of the file that failed to upload.
+errorCloudFileUpload.message=Unable to upload %2$S to %1$S.
+errorCloudFileQuota.title=Quota Error
+## LOCALIZATION NOTE(errorCloudFileQuota.message):
+## %1$S is the name of the online storage service being uploaded to.
+## %2$S is the name of the file that could not be uploaded due to exceeding the storage limit.
+errorCloudFileQuota.message=Uploading %2$S to %1$S would exceed your space quota.
+errorCloudFileLimit.title=File Size Error
+## LOCALIZATION NOTE(errorCloudFileLimit.message):
+## %1$S is the name of the online storage service being uploaded to.
+## %2$S is the name of the file that could not be uploaded due to size restrictions.
+errorCloudFileLimit.message=%2$S exceeds the maximum size for %1$S.
+errorCloudFileOther.title=Unknown Error
+## LOCALIZATION NOTE(errorCloudFileOther.message):
+## %1$S is the name of the online storage service that cannot be communicated with.
+errorCloudFileOther.message=An unknown error occurred when communicating with %1$S.
+errorCloudFileDeletion.title=Deletion Error
+## LOCALIZATION NOTE(errorCloudFileDeletion.message):
+## %1$S is the name of the online storage service that the file is to be deleted from.
+## %2$S is the name of the file that failed to be deleted.
+errorCloudFileDeletion.message=There was a problem deleting %2$S from %1$S.
+
+errorCloudFileUpgrade.label=Upgrade
+
+## LOCALIZATION NOTE(cloudAttachmentCountHeader): A line of text describing how
+## many uploaded files have been appended to this message.  Emphasis should be
+## on sharing as opposed to attaching. This item is used as a header to a list,
+## hence the colon.  This header is only displayed in HTML emails.
+## Using PluralForm (so don't replace the #1).
+cloudAttachmentCountHeader=I've linked #1 file to this email:;I've linked #1 files to this email:
+
+## LOCALIZATION NOTE(cloudAttachmentListFooter): %1$S is a link, whose text
+## contents are the brandFullName of this application.
+cloudAttachmentListFooter=%1$S makes it easy to share large files over email.
+
+## LOCALIZATION NOTE(cloudAttachmentListItem): A line of text describing a cloud
+## attachment, to be inserted into the message body. Do not translate the words
+## %1$S, %2$S, %3$S, or %4$S. %1$S is the attachment name, %2$S is its size,
+## %3$S is the name of the cloud storage service, and %4$S is the link to the
+## attachment.
+cloudAttachmentListItem=* %1$S (%2$S) hosted on %3$S: %4$S
+
--- a/mail/locales/en-US/chrome/messenger/messengercompose/messengercompose.dtd
+++ b/mail/locales/en-US/chrome/messenger/messengercompose/messengercompose.dtd
@@ -12,16 +12,18 @@
 <!ENTITY newMessage.accesskey "M">
 <!ENTITY newContact.label "Address Book Contact…">
 <!ENTITY newContact.accesskey "C">
 <!ENTITY attachMenu.label "Attach">
 <!ENTITY attachMenu.accesskey "t">
 <!ENTITY attachFileCmd.label "File(s)…">
 <!ENTITY attachFileCmd.accesskey "F">
 <!ENTITY attachFileCmd.key "A">
+<!ENTITY attachCloudCmd.label "Filelink">
+<!ENTITY attachCloudCmd.accesskey "i">
 <!ENTITY attachPageCmd.label "Web Page…">
 <!ENTITY attachPageCmd.accesskey "W">
 <!--LOCALIZATION NOTE attachVCardCmd.label Don't translate the term 'vCard' -->
 <!ENTITY attachVCardCmd.label "Personal Card (vCard)">
 <!ENTITY attachVCardCmd.accesskey "P">
 <!ENTITY closeCmd.label "Close">
 <!ENTITY closeCmd.key "W">
 <!ENTITY closeCmd.accesskey "c">
@@ -265,16 +267,24 @@
 <!ENTITY removeAttachment.label "Remove Attachment">
 <!ENTITY removeAttachment.accesskey "M">
 <!ENTITY renameAttachment.label "Rename…">
 <!ENTITY renameAttachment.accesskey "R">
 <!ENTITY selectAll.label "Select All">
 <!ENTITY selectAll.accesskey "A">
 <!ENTITY attachFile.label "Attach File(s)…">
 <!ENTITY attachFile.accesskey "F">
+<!ENTITY attachCloud.label "Filelink…">
+<!ENTITY attachCloud.accesskey "i">
+<!ENTITY convertCloud.label "Convert to…">
+<!ENTITY convertCloud.accesskey "C">
+<!ENTITY cancelUpload.label "Cancel Upload">
+<!ENTITY cancelUpload.accesskey "n">
+<!ENTITY convertRegularAttachment.label "Regular Attachment">
+<!ENTITY convertRegularAttachment.accesskey "A">
 <!ENTITY attachPage.label "Attach Web Page…">
 <!ENTITY attachPage.accesskey "W">
 
 <!-- Spell checker context menu items -->
 <!ENTITY spellAddDictionaries.label "Add Dictionaries…">
 <!ENTITY spellAddDictionaries.accesskey "A">
 
 <!-- Title for the address picker panel -->
--- a/mail/locales/en-US/chrome/messenger/preferences/applications.dtd
+++ b/mail/locales/en-US/chrome/messenger/preferences/applications.dtd
@@ -12,8 +12,23 @@
 <!ENTITY saveTo.label                 "Save files to">
 <!ENTITY saveTo.accesskey             "S">
 <!ENTITY alwaysAsk.label              "Always ask me where to save files">
 <!ENTITY alwaysAsk.accesskey          "A">
 <!ENTITY chooseFolderWin.label        "Browse…">
 <!ENTITY chooseFolderWin.accesskey    "B">
 <!ENTITY chooseFolderMac.label        "Choose…">
 <!ENTITY chooseFolderMac.accesskey    "C">
+
+<!ENTITY attachments.incoming.label   "Incoming">
+<!ENTITY attachments.outgoing.label   "Outgoing">
+
+<!ENTITY addCloudFileAccount.label        "Add">
+<!ENTITY addCloudFileAccount.accesskey    "A">
+<!ENTITY addCloudFileAccount.description  "Add a new Filelink storage service">
+<!ENTITY removeCloudFileAccount.label     "Remove">
+<!ENTITY removeCloudFileAccount.accesskey "R">
+<!ENTITY authRequired.description         "Your authorization is required in order to see the settings for this storage service.">
+<!ENTITY authRequired.button.label        "Authorize">
+<!ENTITY authRequired.button.accesskey    "U">
+
+<!ENTITY enableCloudFileAccountOffer.label "Offer to share for files larger than">
+<!ENTITY enableCloudFileAccountOffer.mb "MB">
--- a/mail/locales/jar.mn
+++ b/mail/locales/jar.mn
@@ -116,16 +116,21 @@
   locale/@AB_CD@/messenger/addressbook/abCardViewOverlay.dtd            (%chrome/messenger/addressbook/abCardViewOverlay.dtd)
   locale/@AB_CD@/messenger/addressbook/abResultsPaneOverlay.dtd         (%chrome/messenger/addressbook/abResultsPaneOverlay.dtd)
   locale/@AB_CD@/messenger/addressbook/abMailListDialog.dtd             (%chrome/messenger/addressbook/abMailListDialog.dtd)
   locale/@AB_CD@/messenger/addressbook/addressBook.properties           (%chrome/messenger/addressbook/addressBook.properties)
   locale/@AB_CD@/messenger/addressbook/ldapAutoCompErrs.properties      (%chrome/messenger/addressbook/ldapAutoCompErrs.properties)
   locale/@AB_CD@/messenger/addressbook/pref-directory.dtd               (%chrome/messenger/addressbook/pref-directory.dtd)
   locale/@AB_CD@/messenger/addressbook/pref-directory-add.dtd           (%chrome/messenger/addressbook/pref-directory-add.dtd)
   locale/@AB_CD@/messenger/addressbook/replicationProgress.properties   (%chrome/messenger/addressbook/replicationProgress.properties)
+  locale/@AB_CD@/messenger/cloudfile/addAccountDialog.dtd               (%chrome/messenger/cloudfile/addAccountDialog.dtd)
+  locale/@AB_CD@/messenger/cloudfile/management.dtd                     (%chrome/messenger/cloudfile/management.dtd)
+  locale/@AB_CD@/messenger/cloudfile/Dropbox/management.dtd             (%chrome/messenger/cloudfile/Dropbox/management.dtd)
+  locale/@AB_CD@/messenger/cloudfile/YouSendIt/management.dtd           (%chrome/messenger/cloudfile/YouSendIt/management.dtd)
+  locale/@AB_CD@/messenger/cloudfile/YouSendIt/settings.dtd             (%chrome/messenger/cloudfile/YouSendIt/settings.dtd)
   locale/@AB_CD@/messenger/messengercompose/messengercompose.dtd        (%chrome/messenger/messengercompose/messengercompose.dtd)
   locale/@AB_CD@/messenger/messengercompose/addressingWidgetOverlay.dtd (%chrome/messenger/messengercompose/addressingWidgetOverlay.dtd)
   locale/@AB_CD@/messenger/messengercompose/askSendFormat.dtd           (%chrome/messenger/messengercompose/askSendFormat.dtd)
   locale/@AB_CD@/messenger/messengercompose/askSendFormat.properties    (%chrome/messenger/messengercompose/askSendFormat.properties)
   locale/@AB_CD@/messenger/messengercompose/sendProgress.dtd            (%chrome/messenger/messengercompose/sendProgress.dtd)
   locale/@AB_CD@/messenger/messengercompose/sendProgress.properties     (%chrome/messenger/messengercompose/sendProgress.properties)
   locale/@AB_CD@/messenger/messengercompose/composeMsgs.properties      (%chrome/messenger/messengercompose/composeMsgs.properties)
   locale/@AB_CD@/messenger/messengercompose/mailComposeEditorOverlay.dtd (%chrome/messenger/messengercompose/mailComposeEditorOverlay.dtd)
@@ -136,16 +141,17 @@
   locale/@AB_CD@/messenger/preferences/sendoptions.dtd                  (%chrome/messenger/preferences/sendoptions.dtd)
   locale/@AB_CD@/messenger/preferences/security.dtd                     (%chrome/messenger/preferences/security.dtd)
   locale/@AB_CD@/messenger/preferences/junkLog.dtd                      (%chrome/messenger/preferences/junkLog.dtd)
   locale/@AB_CD@/messenger/preferences/advanced.dtd                     (%chrome/messenger/preferences/advanced.dtd)
   locale/@AB_CD@/messenger/preferences/attachmentReminder.dtd           (%chrome/messenger/preferences/attachmentReminder.dtd)
   locale/@AB_CD@/messenger/preferences/receipts.dtd                     (%chrome/messenger/preferences/receipts.dtd)
   locale/@AB_CD@/messenger/preferences/connection.dtd                   (%chrome/messenger/preferences/connection.dtd)
   locale/@AB_CD@/messenger/preferences/applications.dtd                 (%chrome/messenger/preferences/applications.dtd)
+  locale/@AB_CD@/messenger/preferences/applications.properties          (%chrome/messenger/preferences/applications.properties)
   locale/@AB_CD@/messenger/preferences/applicationManager.dtd           (%chrome/messenger/preferences/applicationManager.dtd)
   locale/@AB_CD@/messenger/preferences/applicationManager.properties    (%chrome/messenger/preferences/applicationManager.properties)
   locale/@AB_CD@/messenger/preferences/fonts.dtd                        (%chrome/messenger/preferences/fonts.dtd)
   locale/@AB_CD@/messenger/preferences/offline.dtd                      (%chrome/messenger/preferences/offline.dtd)
   locale/@AB_CD@/messenger/preferences/notifications.dtd                (%chrome/messenger/preferences/notifications.dtd)
   locale/@AB_CD@/messenger/preferences/preferences.properties           (%chrome/messenger/preferences/preferences.properties)
   locale/@AB_CD@/messenger/preferences/cookies.dtd                      (%chrome/messenger/preferences/cookies.dtd)
   locale/@AB_CD@/messenger/preferences/permissions.dtd                  (%chrome/messenger/preferences/permissions.dtd)
@@ -180,9 +186,9 @@
 % locale messenger-region @AB_CD@ %locale/@AB_CD@/messenger-region/
   locale/@AB_CD@/messenger-region/region.properties                     (%chrome/messenger-region/region.properties)
 % locale mozldap @AB_CD@ %locale/@AB_CD@/mozldap/
   locale/@AB_CD@/mozldap/ldap.properties                                (%chrome/mozldap/ldap.properties)
 % locale communicator @AB_CD@ %locale/@AB_CD@/communicator/
   locale/@AB_CD@/communicator/utilityOverlay.dtd                        (%chrome/communicator/utilityOverlay.dtd)
 % locale testpilot @AB_CD@ %locale/@AB_CD@/feedback/
   locale/@AB_CD@/feedback/main.dtd                       (%feedback/main.dtd)
-  locale/@AB_CD@/feedback/main.properties                (%feedback/main.properties)
\ No newline at end of file
+  locale/@AB_CD@/feedback/main.properties                (%feedback/main.properties)
--- a/mail/themes/gnomestripe/jar.mn
+++ b/mail/themes/gnomestripe/jar.mn
@@ -8,16 +8,17 @@ classic.jar:
   skin/classic/messenger/featureConfigurators/folder-columns.png    (mail/featureConfigurators/folder-columns.png)
   skin/classic/messenger/featureConfigurators/toolbars.png    (mail/featureConfigurators/toolbars.png)
   skin/classic/messenger/primaryToolbar.css                   (mail/primaryToolbar.css)
   skin/classic/messenger/aboutSupport.css                     (../qute/mail/aboutSupport.css)
   skin/classic/messenger/accountCentral.css                   (mail/accountCentral.css)
   skin/classic/messenger/accountCreation.css                  (mail/accountCreation.css)
   skin/classic/messenger/accountManage.css                    (mail/accountManage.css)
   skin/classic/messenger/accountWizard.css                    (mail/accountWizard.css)
+  skin/classic/messenger/browserRequest.css                   (mail/browserRequest.css)
   skin/classic/messenger/section_collapsed.png                (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                 (mail/section_expanded.png)
   skin/classic/messenger/messageHeader.css                    (mail/messageHeader.css)
   skin/classic/messenger/messageBody.css                      (mail/messageBody.css)
   skin/classic/messenger/webSearch.css                        (mail/webSearch.css)
   skin/classic/messenger/messageQuotes.css                    (mail/messageQuotes.css)
   skin/classic/messenger/messenger.css                        (mail/messenger.css)
   skin/classic/messenger/attachmentList.css                   (mail/attachmentList.css)
@@ -94,16 +95,17 @@ classic.jar:
   skin/classic/messenger/preferences/composition.png          (mail/preferences/composition.png)
   skin/classic/messenger/preferences/security.png             (mail/preferences/security.png)
   skin/classic/messenger/preferences/attachments.png          (mail/preferences/attachments.png)
   skin/classic/messenger/preferences/applications.css         (mail/preferences/applications.css)
   skin/classic/messenger/preferences/advanced.png             (mail/preferences/advanced.png)
   skin/classic/messenger/preferences/background.png           (mail/preferences/background.png)
   skin/classic/messenger/preferences/hover.png                (mail/preferences/hover.png)
   skin/classic/messenger/preferences/selected.png             (mail/preferences/selected.png)
+  skin/classic/messenger/preferences/auth-error.png           (mail/preferences/auth-error.png)
   skin/classic/messenger/smime/msgCompSMIMEOverlay.css        (mail/smime/msgCompSMIMEOverlay.css)
   skin/classic/messenger/smime/msgHdrViewSMIMEOverlay.css     (mail/smime/msgHdrViewSMIMEOverlay.css)
   skin/classic/messenger/smime/msgReadSMIMEOverlay.css        (mail/smime/msgReadSMIMEOverlay.css)
   skin/classic/messenger/smime/msgReadSecurityInfo.css        (mail/smime/msgReadSecurityInfo.css)
   skin/classic/messenger/smime/msgCompSecurityInfo.css        (mail/smime/msgCompSecurityInfo.css)
   skin/classic/messenger/smime/certFetchingStatus.css         (mail/smime/certFetchingStatus.css)
   skin/classic/messenger/smime/icons/hdrCryptoNotOk.png       (mail/smime/hdrCryptoNotOk.png)
   skin/classic/messenger/smime/icons/hdrCryptoOk.png          (mail/smime/hdrCryptoOk.png)
@@ -206,16 +208,18 @@ classic.jar:
   skin/classic/messenger/icons/arrow/arrow-down.png           (mail/icons/arrow/arrow-down.png)
   skin/classic/messenger/icons/arrow/arrow-left-dim.png       (mail/icons/arrow/arrow-left-dim.png)
   skin/classic/messenger/icons/arrow/arrow-right-dim.png      (mail/icons/arrow/arrow-right-dim.png)
   skin/classic/messenger/icons/arrow/arrow-up-dim.png         (mail/icons/arrow/arrow-up-dim.png)
   skin/classic/messenger/icons/arrow/arrow-down-dim.png       (mail/icons/arrow/arrow-down-dim.png)
   skin/classic/messenger/icons/arrow/foldercycler-arrow-left.png        (mail/icons/arrow/foldercycler-arrow-left.png)
   skin/classic/messenger/icons/arrow/foldercycler-arrow-right.png       (mail/icons/arrow/foldercycler-arrow-right.png)
   skin/classic/messenger/icons/search-favorite.png            (mail/icons/search-favorite.png)
+  skin/classic/messenger/icons/connecting.png                 (mail/icons/connecting.png)
+  skin/classic/messenger/icons/loading.png                    (mail/icons/loading.png)
   skin/classic/messenger/tagbg.png                            (mail/tagbg.png)
 % skin editor classic/1.0 %skin/classic/editor/
   skin/classic/editor/editor.css                              (editor/editor.css)
   skin/classic/editor/EditorDialog.css                        (editor/EditorDialog.css)
   skin/classic/editor/icons/img-align-bottom.gif              (editor/img-align-bottom.gif)
   skin/classic/editor/icons/img-align-left.gif                (editor/img-align-left.gif)
   skin/classic/editor/icons/img-align-middle.gif              (editor/img-align-middle.gif)
   skin/classic/editor/icons/img-align-right.gif               (editor/img-align-right.gif)
@@ -223,8 +227,10 @@ classic.jar:
   skin/classic/messenger/newmailaccount/accountProvisioner.css          (mail/newmailaccount/accountProvisioner.css)
   skin/classic/messenger/newmailaccount/search.gif                      (mail/newmailaccount/search.gif)
   skin/classic/messenger/newmailaccount/spinner.gif                     (mail/newmailaccount/spinner.gif)
   skin/classic/messenger/newmailaccount/success-addons.png              (mail/newmailaccount/success-addons.png)
   skin/classic/messenger/newmailaccount/success-border.png              (mail/newmailaccount/success-border.png)
   skin/classic/messenger/newmailaccount/success-compose.png             (mail/newmailaccount/success-compose.png)
   skin/classic/messenger/newmailaccount/success-signature.png           (mail/newmailaccount/success-signature.png)
   skin/classic/messenger/newmailaccount/search.png                      (mail/newmailaccount/search.png)
+  skin/classic/messenger/icons/dropbox.png                    (mail/icons/dropbox.png)
+  skin/classic/messenger/icons/yousendit.png                  (mail/icons/yousendit.png)
new file mode 100644
--- /dev/null
+++ b/mail/themes/gnomestripe/mail/browserRequest.css
@@ -0,0 +1,65 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is oauthorizer.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shane Caraveo <shanec@mozillamessaging.com>
+ *   Florian Quze <florian@instantbird.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#security-button {
+  width: 20px;
+  padding-right: 5px;
+  background-repeat: no-repeat;
+}
+
+#security-button[level="high"],
+#security-button[level="low"] {
+  background-image: url("chrome://messenger/skin/icons/secure.png");
+}
+
+#security-button[level="broken"] {
+  background-image: url("chrome://messenger/skin/icons/insecure.png");
+}
+
+#header {
+  border-bottom: 1px solid black;
+  overflow: hidden;
+}
+
+#addressbox {
+  font-weight: bold;
+  font-size: normal;
+  -moz-appearance: textfield;
+  overflow: hidden;
+  margin: 0px 5px;
+  font-weight: normal;
+}
--- a/mail/themes/gnomestripe/mail/compose/messengercompose.css
+++ b/mail/themes/gnomestripe/mail/compose/messengercompose.css
@@ -718,8 +718,15 @@ toolbarbutton.formatting-button {
   /* splitter grip area */
   width: 5px;
   /* make only the splitter border visible */
   -moz-margin-end: -5px;
   /* because of the negative margin needed to make the splitter visible */
   position: relative;
   z-index: 10;
 }
+
+menu[command="cmd_attachCloud"] .menu-iconic-left,
+menu[command="cmd_convertCloud"] .menu-iconic-left {
+  /* Ensure that the provider icons are visible even if the Gnome theme says
+     menus shouldn't have icons. */
+  visibility: visible;
+}
--- a/mail/themes/gnomestripe/mail/preferences/applications.css
+++ b/mail/themes/gnomestripe/mail/preferences/applications.css
@@ -48,22 +48,22 @@
   -moz-margin-end: 0;
 }
 
 .typeIcon,
 .actionIcon {
   margin: 0px 3px;
 }
 
-richlistitem label {
+#handlersView richlistitem label {
   -moz-margin-start: 1px;
   margin-top: 2px;
 }
 
-richlistitem {
+#handlersView richlistitem {
   min-height: 25px;
 }
 
 richlistitem[appHandlerIcon="ask"],
 menuitem[appHandlerIcon="ask"] {
   list-style-image: url("chrome://messenger/skin/preferences/alwaysAsk.png");
 }
 
@@ -92,11 +92,52 @@ menuitem[appHandlerIcon="plugin"] {
   -moz-padding-start: 3px;
 }
 
 .shortDetails {
   text-align: right;
   color: GrayText;
 }
 
-richlistbox:focus .shortDetails.selected {
+#handlersView richlistbox:focus .shortDetails.selected {
   color: inherit;
 }
+
+/**
+ * Used by the outgoing attachment manager
+ */
+richlistitem.cloudfileAccount[state="connecting"],
+richlistitem.cloudfileAccount[state="waiting-to-connect"] {
+  list-style-image: url("chrome://global/skin/icons/loading_16.png") !important;
+}
+
+richlistitem.cloudfileAccount[state="auth-error"],
+richlistitem.cloudfileAccount[state="no-connection"] {
+  list-style-image: url("moz-icon://stock/gtk-dialog-error?size=menu")!important;
+}
+
+.cloudfileAccount .typeIcon {
+  max-height: 16px;
+  max-width: 16px;
+  margin-top: 3px;
+  margin-bottom: 3px;
+}
+
+#authMessage {
+  text-align: center;
+  padding: 0px 80px;
+}
+
+#authImage {
+  list-style-image: url("chrome://messenger/skin/preferences/auth-error.png");
+  width: 200px;
+  height: 200px;
+}
+
+#cloudFileToggleAndThreshold {
+  margin-bottom: 15px;
+}
+
+#cloudFileLoadingSpinner {
+  width: 16px;
+  height: 16px;
+  list-style-image: url("chrome://global/skin/icons/loading_16.png");
+}
--- a/mail/themes/gnomestripe/mail/preferences/preferences.css
+++ b/mail/themes/gnomestripe/mail/preferences/preferences.css
@@ -99,8 +99,178 @@ radio[pane=paneAdvanced] {
   border: 1px solid ThreeDShadow;
 }
 
 #quotaPercentageBar > .progress-bar {
   -moz-appearance: none;
   background-color: #88AAFF;
   opacity: .5;
 }
+
+/* Attachments */
+
+body {
+  font: message-box;
+}
+
+a {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+/* Setting margins so we can start lining things up */
+#accountNameBox label, #accountName, #accountType, #provider-settings {
+  margin: 0;
+}
+
+#providerForm #username, #provider-signup {
+  width: 250px;
+}
+
+#providerForm label{
+  width: 150px;
+  text-align: right;
+//  padding-right: 3px; ???
+}
+
+#provider-signup, #accountType {
+  margin-left: 150px;
+}
+
+#providerForm label {
+  display: inline-block;
+}
+
+#provider-signup {
+  display: block;
+}
+
+/* Making sure there is some spacing between elements */
+
+#createAccountText {
+  margin-bottom: 20px;
+  padding: 0px 15px;
+}
+
+#accountType {
+  margin-bottom: 5px;
+}
+
+/* I am a hack */
+#accountType {
+  margin-right: 165px;
+}
+
+#messages #authorizing image,
+#messages #error image {
+  max-width: 16px;
+  max-height: 16px;
+}
+
+#provider-header {
+  border-bottom: 1px solid black;
+  margin-bottom: 20px;
+  height: 50px;
+}
+
+#provider-name {
+  padding: 0;
+  margin: 0;
+  margin-right: 120px;
+}
+
+#provider-name h1 {
+  font-size: large;
+}
+
+#provider-name h2 {
+  font: message-box;
+}
+
+#provider-terms {
+  float: right;
+  width: 110px;
+}
+
+#provider-terms a {
+  font-size: small;
+  display: block;
+  margin-bottom: 5px;
+  text-align: right;
+}
+
+#provider-listing {
+  max-width: 150px;
+}
+
+#provider-space {
+  text-align: right;
+  margin-right: 160px;
+  margin-top: 50px;
+  height: 100px;
+}
+
+#upgrade {
+  margin-top: 1em;
+}
+
+#provider-space-visuals {
+  width: 150px;
+  height: 150px;
+  float: right;
+}
+
+#provider-account-settings {
+/*  margin-top: 100px; */
+  text-align: right;
+}
+
+.space-swatch {
+  display: inline-block;
+  width: 1em;
+  height: 1em;
+  border: 1px solid rgba(0,0,0,0.5);
+  border-radius: 2px;
+
+  vertical-align: middle;
+  -moz-margin-start: 0.5ch;
+  -moz-box-sizing: border-box;
+}
+
+/* Main pane padding */
+
+#provider-management {
+  margin: 0 10px;
+}
+
+#provider-name h1 {
+  margin: 0;
+}
+
+/* Loading panel */
+#cloudFileLoadingPanel, #cloudFileDefaultPanel {
+  text-align: center;
+  padding-top: 150px;
+}
+
+#provider-settings .indented {
+  margin-left: 150px;
+}
+
+/* Flexing */
+#provider-management {
+  height: 310px; /* As far as I can see, the height and width values needs to be hardcoded :( */
+  width: 420px;
+  display: -moz-box;
+  -moz-box-orient: vertical;
+}
+
+#provider-header {
+  -moz-box-flex: 0;
+}
+
+#provider-spacebox {
+  -moz-box-flex: 1;
+}
+
+#provider-account-settings {
+  -moz-box-flex: 0;
+}
--- a/mail/themes/pinstripe/jar.mn
+++ b/mail/themes/pinstripe/jar.mn
@@ -11,16 +11,17 @@ classic.jar:
   skin/classic/messenger/dialogs.css                             (mail/dialogs.css)
   skin/classic/messenger/messenger.css                           (mail/messenger.css)
   skin/classic/messenger/primaryToolbar.css                      (mail/primaryToolbar.css)
   skin/classic/messenger/aboutSupport.css                        (../qute/mail/aboutSupport.css)
   skin/classic/messenger/accountCentral.css                      (mail/accountCentral.css)
   skin/classic/messenger/accountCreation.css                     (mail/accountCreation.css)
   skin/classic/messenger/accountManage.css                       (mail/accountManage.css)
   skin/classic/messenger/accountWizard.css                       (mail/accountWizard.css)
+  skin/classic/messenger/browserRequest.css                      (mail/browserRequest.css)
   skin/classic/messenger/section_collapsed.png                   (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                    (mail/section_expanded.png)
   skin/classic/messenger/messageHeader.css                       (mail/messageHeader.css)
   skin/classic/messenger/messageWindow.css                       (mail/messageWindow.css)
   skin/classic/messenger/messageBody.css                         (mail/messageBody.css)
   skin/classic/messenger/webSearch.css                          (mail/webSearch.css)
   skin/classic/messenger/attachmentList.css                      (mail/attachmentList.css)
   skin/classic/messenger/msgSelectOffline.css                    (mail/msgSelectOffline.css)
@@ -111,16 +112,17 @@ classic.jar:
 % skin messenger-newsblog classic/1.0 %skin/classic/messenger-newsblog/
   skin/classic/messenger-newsblog/feed-subscriptions.css         (mail/newsblog/feed-subscriptions.css)
   skin/classic/messenger/preferences/alwaysAsk.png               (mail/preferences/alwaysAsk.png)
   skin/classic/messenger/preferences/application.png             (mail/preferences/application.png)
   skin/classic/messenger/preferences/saveFile.png                (mail/preferences/saveFile.png)
   skin/classic/messenger/preferences/preferences.css             (mail/preferences/preferences.css)
   skin/classic/messenger/preferences/applications.css            (mail/preferences/applications.css)
   skin/classic/messenger/preferences/mail-options.png            (mail/preferences/mail-options.png)
+  skin/classic/messenger/preferences/auth-error.png              (mail/preferences/auth-error.png)
   skin/classic/messenger/smime/msgCompSMIMEOverlay.css           (mail/smime/msgCompSMIMEOverlay.css)
   skin/classic/messenger/smime/msgHdrViewSMIMEOverlay.css        (mail/smime/msgHdrViewSMIMEOverlay.css)
   skin/classic/messenger/smime/msgReadSMIMEOverlay.css           (mail/smime/msgReadSMIMEOverlay.css)
   skin/classic/messenger/smime/msgReadSecurityInfo.css           (mail/smime/msgReadSecurityInfo.css)
   skin/classic/messenger/smime/msgCompSecurityInfo.css           (mail/smime/msgCompSecurityInfo.css)
   skin/classic/messenger/smime/certFetchingStatus.css            (mail/smime/certFetchingStatus.css)
   skin/classic/messenger/smime/icons/sbSignOk.png                (mail/smime/sbSignOk.png)
   skin/classic/messenger/smime/icons/sbSignUnknown.png           (mail/smime/sbSignUnknown.png)
@@ -220,16 +222,18 @@ classic.jar:
   skin/classic/messenger/icons/timeline.png                      (mail/icons/timeline.png)
   skin/classic/messenger/icons/timeline-inverted.png             (mail/icons/timeline-inverted.png)
   skin/classic/messenger/icons/empty-search-results.png          (mail/icons/empty-search-results.png)
   skin/classic/messenger/icons/black_pin.png                     (mail/icons/black_pin.png)
   skin/classic/messenger/icons/red_pin.png                       (mail/icons/red_pin.png)
   skin/classic/messenger/icons/tag1616.png                       (mail/icons/tag1616.png)
   skin/classic/messenger/icons/search-tab.png                    (mail/icons/search-tab.png)
   skin/classic/messenger/icons/message-header-toolbar.png             (mail/icons/message-header-toolbar.png)
+  skin/classic/messenger/icons/connecting.png                         (mail/icons/connecting.png)
+  skin/classic/messenger/icons/loading.png                            (mail/icons/loading.png)
   skin/classic/messenger/tabs/alltabs-box-bkgnd-icon.png              (mail/tabs/alltabs-box-bkgnd-icon.png)
   skin/classic/messenger/tabs/alltabs-box-overflow-bkgnd-animate.png  (mail/tabs/alltabs-box-overflow-bkgnd-animate.png)
   skin/classic/messenger/tabs/newtab.png                              (mail/tabs/newtab.png)
   skin/classic/messenger/tabs/tab-arrow-end.png                       (mail/tabs/tab-arrow-end.png)
   skin/classic/messenger/tabs/tab-arrow-start.png                     (mail/tabs/tab-arrow-start.png)
   skin/classic/messenger/tabs/tab-bkgnd.png                           (mail/tabs/tab-bkgnd.png)
   skin/classic/messenger/tabs/tabDragIndicator.png                    (mail/tabs/tabDragIndicator.png)
   skin/classic/messenger/tabs/tabbrowser-tabs-bkgnd.png               (mail/tabs/tabbrowser-tabs-bkgnd.png)
@@ -293,9 +297,10 @@ classic.jar:
   skin/classic/messenger/newmailaccount/accountProvisioner.css          (mail/newmailaccount/accountProvisioner.css)
   skin/classic/messenger/newmailaccount/search.gif                      (mail/newmailaccount/search.gif)
   skin/classic/messenger/newmailaccount/spinner.gif                     (mail/newmailaccount/spinner.gif)
   skin/classic/messenger/newmailaccount/success-addons.png              (mail/newmailaccount/success-addons.png)
   skin/classic/messenger/newmailaccount/success-border.png              (mail/newmailaccount/success-border.png)
   skin/classic/messenger/newmailaccount/success-compose.png             (mail/newmailaccount/success-compose.png)
   skin/classic/messenger/newmailaccount/success-signature.png           (mail/newmailaccount/success-signature.png)
   skin/classic/messenger/newmailaccount/search.png                      (mail/newmailaccount/search.png)
-
+  skin/classic/messenger/icons/dropbox.png                    (mail/icons/dropbox.png)
+  skin/classic/messenger/icons/yousendit.png                  (mail/icons/yousendit.png)
new file mode 100644
--- /dev/null
+++ b/mail/themes/pinstripe/mail/browserRequest.css
@@ -0,0 +1,75 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is oauthorizer.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shane Caraveo <shanec@mozillamessaging.com>
+ *   Florian Quze <florian@instantbird.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#security-button {
+  width: 20px;
+  padding-right: 5px;
+  background-repeat: no-repeat;
+}
+
+#security-button[level="high"],
+#security-button[level="low"] {
+  background-image: url("chrome://messenger/skin/icons/secure.png");
+}
+
+#security-button[level="broken"] {
+  background-image: url("chrome://messenger/skin/icons/insecure.png");
+}
+/*
+#header {
+  overflow: hidden;
+  padding: 5px;
+  border-bottom: 1px solid black;
+  font-weight: bold;
+  font-size: 1.2em;
+}
+*/
+
+
+#header {
+    border-bottom: 1px solid rgb(105, 105, 105);
+    overflow: hidden;
+}
+
+#addressbox {
+    font-weight: bold;
+    font-size: normal;
+    -moz-appearance: textfield;
+    overflow: hidden;
+    margin: 5px 5px;
+    font-weight: normal;
+}
--- a/mail/themes/pinstripe/mail/compose/messengercompose.css
+++ b/mail/themes/pinstripe/mail/compose/messengercompose.css
@@ -1004,9 +1004,8 @@ blockquote[type=cite] > blockquote {
    color: green !important;
    border-color: green !important;
 }
 
 blockquote[type=cite] > blockquote > blockquote {
    color: maroon !important;
    border-color: maroon !important;
 }
-
--- a/mail/themes/pinstripe/mail/preferences/applications.css
+++ b/mail/themes/pinstripe/mail/preferences/applications.css
@@ -88,8 +88,54 @@ menuitem[appHandlerIcon="plugin"] {
 .shortDetails {
   text-align: right;
   color: GrayText;
 }
 
 richlistbox:focus .shortDetails.selected {
   color: inherit;
 }
+
+
+richlistitem.cloudfileAccount {
+  padding: 4px;
+}
+
+richlistitem.cloudfileAccount description{
+  padding-left: 3px;
+}
+
+/**
+ * Used by the outgoing attachment manager
+ */
+richlistitem.cloudfileAccount[state="connecting"],
+richlistitem.cloudfileAccount[state="waiting-to-connect"] {
+  list-style-image: url("chrome://global/skin/icons/loading_16.png") !important;
+}
+
+richlistitem.cloudfileAccount[state="auth-error"],
+richlistitem.cloudfileAccount[state="no-connection"] {
+  list-style-image: url("chrome://global/skin/icons/error-16.png") !important;
+}
+
+.cloudfileAccount .typeIcon {
+  max-height: 16px;
+  max-width: 16px;
+  margin-top: 3px;
+  margin-bottom: 3px;
+}
+
+#authMessage {
+  text-align: center;
+  padding: 0px 80px;
+}
+
+#authImage {
+  list-style-image: url("chrome://messenger/skin/preferences/auth-error.png");
+  width: 200px;
+  height: 200px;
+}
+
+#cloudFileLoadingSpinner {
+  width: 16px;
+  height: 16px;
+  list-style-image: url("chrome://global/skin/icons/loading_16.png");
+}
--- a/mail/themes/pinstripe/mail/preferences/preferences.css
+++ b/mail/themes/pinstripe/mail/preferences/preferences.css
@@ -171,8 +171,183 @@ tabpanels {
   border: 1px solid #8B8B8B;
 }
 
 #quotaPercentageBar > .progress-bar {
   -moz-appearance: none;
   background-color: #88AAFF;
   opacity: .5;
 }
+
+/* Attachments */
+
+body {
+  font-size: 12px;
+}
+
+a {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+/* Setting margins so we can start lining things up */
+#accountNameBox label, #accountName, #accountType, #provider-settings {
+  margin: 0;
+}
+
+#providerForm #username, #provider-signup {
+  width: 250px;
+}
+
+#providerForm label{
+  width: 75px;
+  text-align: right;
+//  padding-right: 3px; ???
+}
+
+#provider-signup, #accountType {
+  margin-left: 75px;
+}
+
+#providerForm label {
+  display: inline-block;
+}
+
+#provider-signup {
+  display: block;
+}
+
+/* Making sure there is some spacing between elements */
+
+#createAccountText {
+  margin-bottom: 20px;
+  padding: 0px 15px;
+}
+
+#accountType {
+  margin-bottom: 5px;
+}
+
+/* I am a hack */
+#accountType {
+  margin-right: 80px;
+}
+
+#messages #authorizing image,
+#messages #error image {
+  max-width: 16px;
+  max-height: 16px;
+}
+
+#provider-header {
+  border-bottom: 1px solid #b1b1b1;
+  margin-bottom: 20px;
+  height: 50px;
+}
+
+#provider-name {
+  padding: 0;
+  margin: 0;
+  margin-right: 120px;
+}
+
+#provider-name h1 {
+  font-size: large;
+}
+
+#provider-name h2 {
+  font-size: 12px;
+}
+
+#provider-terms {
+  float: right;
+  width: 110px;
+}
+
+#provider-terms a {
+  font-size: small;
+  display: block;
+  margin-bottom: 5px;
+  text-align: right;
+}
+
+#provider-listing {
+  max-width: 150px;
+}
+
+#provider-space {
+  text-align: right;
+  margin-right: 160px;
+  margin-top: 50px;
+  height: 100px;
+}
+
+#upgrade {
+  margin-top: 1em;
+}
+
+#provider-space-visuals {
+  width: 150px;
+  height: 150px;
+  float: right;
+}
+
+#provider-account-settings {
+  text-align: right;
+}
+
+#createAccountTitle {
+  font-size: 12px;
+  font-weight: bold;
+  padding-left: 15px;
+  padding-right: 15px;
+}
+
+.space-swatch {
+  display: inline-block;
+  width: 1em;
+  height: 1em;
+  border: 1px solid rgba(0,0,0,0.5);
+  border-radius: 2px;
+  vertical-align: middle;
+  -moz-margin-start: 0.5ch;
+  -moz-box-sizing: border-box;
+}
+
+/* Main pane padding */
+
+#provider-management {
+  margin: 0 10px;
+}
+
+#provider-name h1 {
+  margin: 0;
+}
+
+/* Loading panel */
+#cloudFileLoadingPanel, #cloudFileDefaultPanel {
+  text-align: center;
+  padding-top: 150px;
+}
+
+#provider-settings .indented {
+  margin-left: 75px;
+}
+
+/* Flexing */
+#provider-management {
+  height: 390px; /* As far as I can see, the height and width values needs to be hardcoded :( */
+  width: 280px;
+  display: -moz-box;
+  -moz-box-orient: vertical;
+}
+
+#provider-header {
+  -moz-box-flex: 0;
+}
+
+#provider-spacebox {
+  -moz-box-flex: 1;
+}
+
+#provider-account-settings {
+  -moz-box-flex: 0;
+}
--- a/mail/themes/qute/jar.mn
+++ b/mail/themes/qute/jar.mn
@@ -48,16 +48,17 @@ classic.jar:
   skin/classic/messenger/featureConfigurators/folder-columns.png  (mail/featureConfigurators/folder-columns.png)
   skin/classic/messenger/featureConfigurators/toolbars.png    (mail/featureConfigurators/toolbars.png)
   skin/classic/messenger/primaryToolbar.css                   (mail/primaryToolbar.css)
   skin/classic/messenger/aboutSupport.css                     (mail/aboutSupport.css)
   skin/classic/messenger/accountCentral.css                   (mail/accountCentral.css)
   skin/classic/messenger/accountCreation.css                  (mail/accountCreation.css)
   skin/classic/messenger/accountManage.css                    (mail/accountManage.css)
   skin/classic/messenger/accountWizard.css                    (mail/accountWizard.css)
+  skin/classic/messenger/browserRequest.css                   (mail/browserRequest.css)
   skin/classic/messenger/section_collapsed.png                (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                 (mail/section_expanded.png)
   skin/classic/messenger/messageHeader.css                    (mail/messageHeader.css)
   skin/classic/messenger/messageBody.css                      (mail/messageBody.css)
   skin/classic/messenger/webSearch.css                        (mail/webSearch.css)
   skin/classic/messenger/messageQuotes.css                    (mail/messageQuotes.css)
   skin/classic/messenger/messenger.css                        (mail/messenger.css)
   skin/classic/messenger/attachmentList.css                   (mail/attachmentList.css)
@@ -129,16 +130,17 @@ classic.jar:
   skin/classic/messenger/preferences/security.png             (mail/preferences/security.png)
   skin/classic/messenger/preferences/attachments.png          (mail/preferences/attachments.png)
   skin/classic/messenger/preferences/applications.css         (mail/preferences/applications.css)
   skin/classic/messenger/preferences/saveFile.png             (mail/preferences/saveFile.png)
   skin/classic/messenger/preferences/advanced.png             (mail/preferences/advanced.png)
   skin/classic/messenger/preferences/background.png           (mail/preferences/background.png)
   skin/classic/messenger/preferences/hover.png                (mail/preferences/hover.png)
   skin/classic/messenger/preferences/selected.png             (mail/preferences/selected.png)
+  skin/classic/messenger/preferences/auth-error.png           (mail/preferences/auth-error.png)
   skin/classic/messenger/smime/msgCompSMIMEOverlay.css        (mail/smime/msgCompSMIMEOverlay.css)
   skin/classic/messenger/smime/msgHdrViewSMIMEOverlay.css     (mail/smime/msgHdrViewSMIMEOverlay.css)
   skin/classic/messenger/smime/msgReadSMIMEOverlay.css        (mail/smime/msgReadSMIMEOverlay.css)
   skin/classic/messenger/smime/msgReadSecurityInfo.css        (mail/smime/msgReadSecurityInfo.css)
   skin/classic/messenger/smime/msgCompSecurityInfo.css        (mail/smime/msgCompSecurityInfo.css)
   skin/classic/messenger/smime/certFetchingStatus.css         (mail/smime/certFetchingStatus.css)
   skin/classic/messenger/smime/icons/hdrCryptoNotOk.png       (mail/smime/hdrCryptoNotOk.png)
   skin/classic/messenger/smime/icons/hdrCryptoOk.png          (mail/smime/hdrCryptoOk.png)
@@ -198,16 +200,17 @@ classic.jar:
   skin/classic/messenger/icons/error.png                      (mail/icons/error.png)
   skin/classic/messenger/icons/cancel.png                     (mail/icons/cancel.png)
   skin/classic/messenger/icons/tab-arrow-left.png             (mail/icons/tab-arrow-left.png)
   skin/classic/messenger/icons/tab-arrow-left-inverted.png    (mail/icons/tab-arrow-left-inverted.png)
   skin/classic/messenger/icons/mainwindow-dropdown-arrow.png  (mail/icons/mainwindow-dropdown-arrow.png)
   skin/classic/messenger/icons/mainwindow-dropdown-arrow-inverted.png  (mail/icons/mainwindow-dropdown-arrow-inverted.png)
   skin/classic/messenger/icons/tabDragIndicator.png           (mail/icons/tabDragIndicator.png)
   skin/classic/messenger/icons/tab.png                        (mail/icons/tab.png)
+  skin/classic/messenger/icons/connecting.png                 (mail/icons/connecting.png)
   skin/classic/messenger/icons/loading.png                    (mail/icons/loading.png)
   skin/classic/messenger/accountcentral/read-messages.png     (mail/accountcentral/read-messages.png)
   skin/classic/messenger/accountcentral/write-message.png     (mail/accountcentral/write-message.png)
   skin/classic/messenger/accountcentral/create-account.png    (mail/accountcentral/create-account.png)
   skin/classic/messenger/accountcentral/account-settings.png  (mail/accountcentral/account-settings.png)
   skin/classic/messenger/accountcentral/search-messages.png   (mail/accountcentral/search-messages.png)
   skin/classic/messenger/accountcentral/manage-filters.png    (mail/accountcentral/manage-filters.png)
   skin/classic/messenger/accountcentral/offline-settings.png  (mail/accountcentral/offline-settings.png)
@@ -242,16 +245,18 @@ classic.jar:
   skin/classic/messenger/newmailaccount/accountProvisioner.css          (mail/newmailaccount/accountProvisioner.css)
   skin/classic/messenger/newmailaccount/search.gif                      (mail/newmailaccount/search.gif)
   skin/classic/messenger/newmailaccount/spinner.gif                     (mail/newmailaccount/spinner.gif)
   skin/classic/messenger/newmailaccount/success-addons.png              (mail/newmailaccount/success-addons.png)
   skin/classic/messenger/newmailaccount/success-border.png              (mail/newmailaccount/success-border.png)
   skin/classic/messenger/newmailaccount/success-compose.png             (mail/newmailaccount/success-compose.png)
   skin/classic/messenger/newmailaccount/success-signature.png           (mail/newmailaccount/success-signature.png)
   skin/classic/messenger/newmailaccount/search.png                      (mail/newmailaccount/search.png)
+ skin/classic/messenger/icons/dropbox.png                    (mail/icons/dropbox.png)
+ skin/classic/messenger/icons/yousendit.png                  (mail/icons/yousendit.png)
 #ifdef XP_WIN
 % skin communicator classic/1.0 %skin/classic/aero/communicator/ os=WINNT osversion>=6
   skin/classic/aero/communicator/communicator.css                       (mail/communicator.css)
   skin/classic/aero/communicator/smileys.css                            (mail/smileys.css)
   skin/classic/aero/communicator/icons/smileys/smiley-smile.png         (mail/icons/smiley-smile-aero.png)
   skin/classic/aero/communicator/icons/smileys/smiley-frown.png         (mail/icons/smiley-frown-aero.png)
   skin/classic/aero/communicator/icons/smileys/smiley-wink.png          (mail/icons/smiley-wink-aero.png)
   skin/classic/aero/communicator/icons/smileys/smiley-tongue-out.png    (mail/icons/smiley-tongue-out-aero.png)
@@ -357,16 +362,17 @@ classic.jar:
   skin/classic/aero/messenger/preferences/security.png             (mail/preferences/security-aero.png)
   skin/classic/aero/messenger/preferences/attachments.png          (mail/preferences/attachments-aero.png)
   skin/classic/aero/messenger/preferences/applications.css         (mail/preferences/applications.css)
   skin/classic/aero/messenger/preferences/saveFile.png             (mail/preferences/saveFile.png)
   skin/classic/aero/messenger/preferences/advanced.png             (mail/preferences/advanced-aero.png)
   skin/classic/aero/messenger/preferences/background.png           (mail/preferences/background.png)
   skin/classic/aero/messenger/preferences/hover.png                (mail/preferences/hover.png)
   skin/classic/aero/messenger/preferences/selected.png             (mail/preferences/selected.png)
+  skin/classic/aero/messenger/preferences/auth-error.png           (mail/preferences/auth-error.png)
   skin/classic/aero/messenger/smime/msgCompSMIMEOverlay.css        (mail/smime/msgCompSMIMEOverlay.css)
   skin/classic/aero/messenger/smime/msgHdrViewSMIMEOverlay.css     (mail/smime/msgHdrViewSMIMEOverlay.css)
   skin/classic/aero/messenger/smime/msgReadSMIMEOverlay.css        (mail/smime/msgReadSMIMEOverlay.css)
   skin/classic/aero/messenger/smime/msgReadSecurityInfo.css        (mail/smime/msgReadSecurityInfo.css)
   skin/classic/aero/messenger/smime/msgCompSecurityInfo.css        (mail/smime/msgCompSecurityInfo.css)
   skin/classic/aero/messenger/smime/certFetchingStatus.css         (mail/smime/certFetchingStatus.css)
   skin/classic/aero/messenger/smime/icons/hdrCryptoNotOk.png       (mail/smime/hdrCryptoNotOk-aero.png)
   skin/classic/aero/messenger/smime/icons/hdrCryptoOk.png          (mail/smime/hdrCryptoOk-aero.png)
@@ -455,25 +461,27 @@ classic.jar:
   skin/classic/aero/messenger/icons/arrow/arrow-right-dim.png      (mail/icons/arrow/arrow-right-dim.png)
   skin/classic/aero/messenger/icons/arrow/arrow-up-dim.png         (mail/icons/arrow/arrow-up-dim.png)
   skin/classic/aero/messenger/icons/arrow/arrow-down-dim.png       (mail/icons/arrow/arrow-down-dim.png)
   skin/classic/aero/messenger/icons/arrow/foldercycler-arrow-left.png        (mail/icons/arrow/foldercycler-arrow-left.png)
   skin/classic/aero/messenger/icons/arrow/foldercycler-arrow-right.png       (mail/icons/arrow/foldercycler-arrow-right.png)
   skin/classic/aero/messenger/icons/search-favorite.png            (mail/icons/search-favorite.png)
   skin/classic/aero/messenger/icons/xp-pin-grey.png                (mail/icons/xp-pin-grey.png)
   skin/classic/aero/messenger/icons/xp-pin-red.png                 (mail/icons/xp-pin-red.png)
+  skin/classic/aero/messenger/icons/connecting.png                 (mail/icons/connecting.png)
   skin/classic/aero/messenger/icons/loading.png                    (mail/icons/loading.png)
   skin/classic/aero/messenger/tagbg.png                            (mail/tagbg.png)
   skin/classic/aero/messenger/icons/download.png                   (mail/icons/download.png)
 % skin messenger-newsblog classic/1.0 %skin/classic/aero/messenger-newsblog/ os=WINNT osversion>=6
   skin/classic/aero/messenger-newsblog/feed-subscriptions.css      (mail/newsblog/feed-subscriptions.css)
   skin/classic/aero/messenger-newsblog/icons/rss-feed.png          (mail/newsblog/rss-feed-aero.png)
   skin/classic/aero/messenger-newsblog/icons/server-rss.png        (mail/newsblog/server-rss-aero.png)
   skin/classic/aero/messenger/newmailaccount/accountProvisioner.css          (mail/newmailaccount/accountProvisioner-aero.css)
   skin/classic/aero/messenger/newmailaccount/search.gif                      (mail/newmailaccount/search.gif)
   skin/classic/aero/messenger/newmailaccount/spinner.gif                     (mail/newmailaccount/spinner.gif)
   skin/classic/aero/messenger/newmailaccount/success-addons.png              (mail/newmailaccount/success-addons.png)
   skin/classic/aero/messenger/newmailaccount/success-border.png              (mail/newmailaccount/success-border.png)
   skin/classic/aero/messenger/newmailaccount/success-compose.png             (mail/newmailaccount/success-compose.png)
   skin/classic/aero/messenger/newmailaccount/success-signature.png           (mail/newmailaccount/success-signature.png)
   skin/classic/aero/messenger/newmailaccount/search.png                      (mail/newmailaccount/search.png)
-
+  skin/classic/aero/messenger/icons/dropbox.png                    (mail/icons/dropbox.png)
+  skin/classic/aero/messenger/icons/yousendit.png                  (mail/icons/yousendit.png)
 #endif
new file mode 100644
--- /dev/null
+++ b/mail/themes/qute/mail/browserRequest.css
@@ -0,0 +1,65 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is oauthorizer.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shane Caraveo <shanec@mozillamessaging.com>
+ *   Florian Quze <florian@instantbird.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#security-button {
+  width: 20px;
+  padding-right: 5px;
+  background-repeat: no-repeat;
+}
+
+#security-button[level="high"],
+#security-button[level="low"] {
+  background-image: url("chrome://messenger/skin/icons/secure.png");
+}
+
+#security-button[level="broken"] {
+  background-image: url("chrome://messenger/skin/icons/insecure.png");
+}
+
+#header {
+  overflow: hidden;
+  border-bottom: 1px solid black;
+}
+
+#addressbox {
+  font-weight: bold;
+  font-size: normal;
+  -moz-appearance: textfield;
+  overflow: hidden;
+  margin: 0px 5px;
+  font-weight: normal;
+}
--- a/mail/themes/qute/mail/preferences/applications.css
+++ b/mail/themes/qute/mail/preferences/applications.css
@@ -93,8 +93,46 @@ menuitem[appHandlerIcon="plugin"] {
 .shortDetails {
   text-align: right;
   color: GrayText;
 }
 
 richlistbox:focus .shortDetails.selected {
   color: inherit;
 }
+
+/**
+ * Used by the outgoing attachment manager
+ */
+
+richlistitem.cloudfileAccount[state="connecting"],
+richlistitem.cloudfileAccount[state="waiting-to-connect"] {
+  list-style-image: url("chrome://global/skin/icons/loading_16.png") !important;
+}
+
+richlistitem.cloudfileAccount[state="auth-error"],
+richlistitem.cloudfileAccount[state="no-connection"] {
+  list-style-image: url("chrome://global/skin/icons/error-16.png") !important;
+}
+
+.cloudfileAccount .typeIcon {
+  max-height: 16px;
+  max-width: 16px;
+  margin-top: 3px;
+  margin-bottom: 3px;
+}
+
+#authMessage {
+  text-align: center;
+  padding: 0px 80px;
+}
+
+#authImage {
+  list-style-image: url("chrome://messenger/skin/preferences/auth-error.png");
+  width: 200px;
+  height: 200px;
+}
+
+#cloudFileLoadingSpinner {
+  width: 16px;
+  height: 16px;
+  list-style-image: url("chrome://global/skin/icons/loading_16.png");
+}
--- a/mail/themes/qute/mail/preferences/preferences.css
+++ b/mail/themes/qute/mail/preferences/preferences.css
@@ -129,8 +129,169 @@ radio[pane=paneAdvanced] {
   border: 1px solid ThreeDShadow;
 }
 
 #quotaPercentageBar > .progress-bar {
   -moz-appearance: none;
   background-color: #88AAFF;
   opacity: .5;
 }
+
+/* Attachments */
+
+body {
+  font: message-box;
+}
+
+a {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+/* Setting margins so we can start lining things up */
+#accountNameBox label, #accountName, #accountType, #provider-settings {
+  margin: 0;
+}
+
+#providerForm #username, #provider-signup {
+  width: 250px;
+}
+
+#providerForm label{
+  width: 150px;
+  text-align: right;
+//  padding-right: 3px; ???
+}
+
+#provider-signup, #accountType {
+  margin-left: 150px;
+}
+
+#providerForm label {
+  display: inline-block;
+}
+
+#provider-signup {
+  display: block;
+}
+
+/* Making sure there is some spacing between elements */
+
+#createAccountText {
+  margin-bottom: 20px;
+  padding: 0px 15px;
+}
+
+#accountType {
+  margin-bottom: 5px;
+}
+
+/* I am a hack */
+#accountType {
+  margin-right: 62px;
+}
+
+#messages #authorizing image,
+#messages #error image {
+  max-width: 16px;
+  max-height: 16px;
+}
+
+#provider-header {
+  border-bottom: 1px solid #898c95;
+  margin-bottom: 20px;
+  padding-bottom: 20px;
+}
+
+#provider-name {
+  padding: 0;
+  margin: 0;
+  margin-right: 120px;
+}
+
+#provider-name h1 {
+  font-size: large;
+}
+
+#provider-name h2 {
+  font: message-box;
+}
+
+#provider-terms {
+  float: right;
+  width: 110px;
+}
+
+#provider-terms a {
+  display: block;
+  margin-bottom: 5px;
+  text-align: right;
+}
+
+#provider-listing {
+  max-width: 150px;
+}
+
+#provider-space {
+  text-align: right;
+  margin-right: 170px;
+  height: 100px;
+}
+
+#upgrade {
+  margin-top: 1em;
+}
+
+#provider-space-visuals {
+  float: right;
+}
+
+#provider-account-settings {
+  text-align: right;
+}
+
+.space-swatch {
+  display: inline-block;
+  width: 1em;
+  height: 1em;
+  border: 1px solid rgba(0,0,0,0.5);
+  border-radius: 2px;
+
+  vertical-align: middle;
+  -moz-margin-start: 0.5ch;
+  -moz-box-sizing: border-box;
+}
+
+/* Main pane padding */
+
+#provider-management {
+  margin: 0 10px;
+}
+
+#provider-name h1 {
+  margin: 0;
+}
+
+/* Loading panel */
+#cloudFileLoadingPanel, #cloudFileDefaultPanel {
+  text-align: center;
+  padding-top: 150px;
+}
+
+/* Flexing */
+#provider-management {
+  height: 270px; /* As far as I can see, the height and width values needs to be hardcoded :( */
+  width: 320px;
+  display: -moz-box;
+  -moz-box-orient: vertical;
+}
+
+#provider-header {
+  -moz-box-flex: 0;
+}
+
+#provider-spacebox {
+  -moz-box-flex: 1;
+}
+
+#provider-account-settings {
+  -moz-box-flex: 0;
+}