Bug 749856 - Part 5: save message attachments, r=philikon
authorVicamo Yang <vyang@mozilla.com>
Mon, 04 Jun 2012 13:04:58 +0800
changeset 99787 abcc96067a0f5366477ef94ed41fea5cf6c783b5
parent 99786 728c759ed6544089c251612e12ce8ed2fa34c68a
child 99788 a0785f8d3a75579e8a4fe44d82770fe7fa89f8f5
push id1116
push userlsblakk@mozilla.com
push dateMon, 16 Jul 2012 19:38:18 +0000
treeherdermozilla-beta@95f959a8b4dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersphilikon
bugs749856
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 749856 - Part 5: save message attachments, r=philikon
dom/mms/src/ril/MmsService.js
--- a/dom/mms/src/ril/MmsService.js
+++ b/dom/mms/src/ril/MmsService.js
@@ -4,24 +4,35 @@
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
 const RIL_MMSSERVICE_CONTRACTID = "@mozilla.org/mms/rilmmsservice;1";
 const RIL_MMSSERVICE_CID = Components.ID("{217ddd76-75db-4210-955d-8806cd8d87f9}");
 
 const DEBUG = false;
 
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kXpcomShutdownObserverTopic        = "xpcom-shutdown";
 
+// File modes for saving MMS attachments.
+const FILE_OPEN_MODE = FileUtils.MODE_CREATE
+                     | FileUtils.MODE_WRONLY
+                     | FileUtils.MODE_TRUNCATE;
+
+// Size of each segment in a nsIStorageStream. Must be a power of two.
+const STORAGE_STREAM_SEGMENT_SIZE = 4096;
+
 // HTTP status codes:
 // @see http://tools.ietf.org/html/rfc2616#page-39
 const HTTP_STATUS_OK = 200;
 
 XPCOMUtils.defineLazyServiceGetter(this, "gpps",
                                    "@mozilla.org/network/protocol-proxy-service;1",
                                    "nsIProtocolProxyService");
 
@@ -146,16 +157,89 @@ MmsService.prototype = {
       xhr.send();
     } catch (e) {
       debug("xhr error, can't send: " + e.message);
       releaseProxyFilterAndCallback(0, null);
     }
   },
 
   /**
+   * @param file
+   *        A nsIFile object indicating where to save the data.
+   * @param data
+   *        An array of raw octets.
+   * @param callback
+   *        Callback function when I/O is done.
+   *
+   * @return An nsIRequest representing the copy operation returned by
+   *         NetUtil.asyncCopy().
+   */
+  saveContentToFile: function saveContentToFile(file, data, callback) {
+    // Write to a StorageStream for NetUtil.asyncCopy()
+    let sstream = Cc["@mozilla.org/storagestream;1"]
+                  .createInstance(Ci.nsIStorageStream);
+    sstream.init(STORAGE_STREAM_SEGMENT_SIZE, data.length, null);
+    let bostream = Cc["@mozilla.org/binaryoutputstream;1"]
+                   .createInstance(Ci.nsIBinaryOutputStream);
+    bostream.setOutputStream(sstream.getOutputStream(0));
+    bostream.writeByteArray(data, data.length);
+    bostream.close();
+
+    // Write message body to file
+    let ofstream = FileUtils.openSafeFileOutputStream(file, FILE_OPEN_MODE);
+    return NetUtil.asyncCopy(sstream.newInputStream(0), ofstream, callback);
+  },
+
+  /**
+   * @param msg
+   *        A MMS message object.
+   * @param callback
+   *        A callback function that accepts one argument as retrieved message.
+   */
+  saveMessageContent: function saveMessageContent(msg, callback) {
+    function saveCallback(obj, counter, status) {
+      obj.saved = Components.isSuccessCode(status);
+      debug("saveMessageContent: " + obj.file.path + ", saved: " + obj.saved);
+
+      // The async copy callback may not be invoked in order, so we only
+      // callback after all of them were done.
+      counter.count++;
+      if (counter.count >= counter.max) {
+        if (callback) {
+          callback(msg);
+        }
+      }
+    }
+
+    let tid = msg.headers["x-mms-transaction-id"];
+    if (msg.parts) {
+      let counter = {max: msg.parts.length, count: 0};
+
+      msg.parts.forEach((function (part, index) {
+        part.file = FileUtils.getFile("ProfD", ["mms", tid, index], true);
+        if (!part.content) {
+          saveCallback(part, counter, Cr.NS_ERROR_NOT_AVAILABLE);
+        } else {
+          this.saveContentToFile(part.file, part.content,
+                                 saveCallback.bind(null, part, counter));
+        }
+      }).bind(this));
+    } else if (msg.content) {
+      msg.file = FileUtils.getFile("ProfD", ["mms", tid, "content"], true);
+      this.saveContentToFile(msg.file, msg.content,
+                             saveCallback.bind(null, msg, {max: 1, count: 0}));
+    } else {
+      // Nothing to save here.
+      if (callback) {
+        callback(msg);
+      }
+    }
+  },
+
+  /**
    * @param data
    *        A wrapped object containing raw PDU data.
    * @param options
    *        Additional options to be passed to corresponding PDU handler.
    *
    * @return true if incoming data parsed successfully and passed to PDU
    *         handler; false otherwise.
    */
@@ -222,19 +306,17 @@ MmsService.prototype = {
     }
 
     let status = msg.headers["x-mms-retrieve-status"];
     if ((status != null) && (status != MMS.MMS_PDU_ERROR_OK)) {
       callbackIfValid(status, msg);
       return;
     }
 
-    // FIXME: further processing
-
-    callbackIfValid(MMS.MMS_PDU_ERROR_OK, msg);
+    this.saveMessageContent(msg, callbackIfValid.bind(null, MMS.MMS_PDU_ERROR_OK));
   },
 
   /**
    * Update proxyInfo & MMSC from preferences.
    *
    * @param enabled
    *        Enable or disable MMS proxy.
    */