1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 this.EXPORTED_SYMBOLS = [ "ZipUtils" ];
7 const Cc = Components.classes;
8 const Ci = Components.interfaces;
9 const Cr = Components.results;
10 const Cu = Components.utils;
12 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
13 Cu.import("resource://gre/modules/Services.jsm");
15 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
16 "resource://gre/modules/FileUtils.jsm");
17 XPCOMUtils.defineLazyModuleGetter(this, "OS",
18 "resource://gre/modules/osfile.jsm");
19 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
20 "resource://gre/modules/Promise.jsm");
21 XPCOMUtils.defineLazyModuleGetter(this, "Task",
22 "resource://gre/modules/Task.jsm");
25 // The maximum amount of file data to buffer at a time during file extraction
26 const EXTRACTION_BUFFER = 1024 * 512;
30 * Asynchronously writes data from an nsIInputStream to an OS.File instance.
31 * The source stream and OS.File are closed regardless of whether the operation
33 * Returns a promise that will be resolved when complete.
36 * The name of the file being extracted for logging purposes.
38 * The source nsIInputStream.
40 * The open OS.File instance to write to.
42 function saveStreamAsync(aPath, aStream, aFile) {
43 let deferred = Promise.defer();
45 // Read the input stream on a background thread
46 let sts = Cc["@mozilla.org/network/stream-transport-service;1"].
47 getService(Ci.nsIStreamTransportService);
48 let transport = sts.createInputTransport(aStream, -1, -1, true);
49 let input = transport.openInputStream(0, 0, 0)
50 .QueryInterface(Ci.nsIAsyncInputStream);
51 let source = Cc["@mozilla.org/binaryinputstream;1"].
52 createInstance(Ci.nsIBinaryInputStream);
53 source.setInputStream(input);
55 let data = new Uint8Array(EXTRACTION_BUFFER);
57 function readFailed(error) {
62 logger.error("Failed to close JAR stream for " + aPath);
65 aFile.close().then(function() {
66 deferred.reject(error);
68 logger.error("Failed to close file for " + aPath);
69 deferred.reject(error);
75 let count = Math.min(source.available(), data.byteLength);
76 source.readArrayBuffer(count, data.buffer);
78 aFile.write(data, { bytes: count }).then(function() {
79 input.asyncWait(readData, 0, 0, Services.tm.currentThread);
82 catch (e if e.result == Cr.NS_BASE_STREAM_CLOSED) {
83 deferred.resolve(aFile.close());
90 input.asyncWait(readData, 0, 0, Services.tm.currentThread);
92 return deferred.promise;
99 * Asynchronously extracts files from a ZIP file into a directory.
100 * Returns a promise that will be resolved when the extraction is complete.
103 * The source ZIP file that contains the add-on.
105 * The nsIFile to extract to.
107 extractFilesAsync: function ZipUtils_extractFilesAsync(aZipFile, aDir) {
108 let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
109 createInstance(Ci.nsIZipReader);
112 zipReader.open(aZipFile);
115 return Promise.reject(e);
118 return Task.spawn(function() {
119 // Get all of the entries in the zip and sort them so we create directories
121 let entries = zipReader.findEntries(null);
123 while (entries.hasMore())
124 names.push(entries.getNext());
127 for (let name of names) {
128 let entryName = name;
129 let zipentry = zipReader.getEntry(name);
130 let path = OS.Path.join(aDir.path, ...name.split("/"));
132 if (zipentry.isDirectory) {
134 yield OS.File.makeDir(path);
137 dump("extractFilesAsync: failed to create directory " + path + "\n");
142 let options = { unixMode: zipentry.permissions | FileUtils.PERMS_FILE };
144 let file = yield OS.File.open(path, { truncate: true }, options);
145 if (zipentry.realSize == 0)
148 yield saveStreamAsync(path, zipReader.getInputStream(entryName), file);
151 dump("extractFilesAsync: failed to extract file " + path + "\n");
158 }).then(null, (e) => {
165 * Extracts files from a ZIP file into a directory.
168 * The source ZIP file that contains the add-on.
170 * The nsIFile to extract to.
172 extractFiles: function ZipUtils_extractFiles(aZipFile, aDir) {
173 function getTargetFile(aDir, entry) {
174 let target = aDir.clone();
175 entry.split("/").forEach(function(aPart) {
176 target.append(aPart);
181 let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
182 createInstance(Ci.nsIZipReader);
183 zipReader.open(aZipFile);
186 // create directories first
187 let entries = zipReader.findEntries("*/");
188 while (entries.hasMore()) {
189 let entryName = entries.getNext();
190 let target = getTargetFile(aDir, entryName);
191 if (!target.exists()) {
193 target.create(Ci.nsIFile.DIRECTORY_TYPE,
194 FileUtils.PERMS_DIRECTORY);
197 dump("extractFiles: failed to create target directory for extraction file = " + target.path + "\n");
202 entries = zipReader.findEntries(null);
203 while (entries.hasMore()) {
204 let entryName = entries.getNext();
205 let target = getTargetFile(aDir, entryName);
209 zipReader.extract(entryName, target);
211 target.permissions |= FileUtils.PERMS_FILE;
214 dump("Failed to set permissions " + aPermissions.toString(8) + " on " + target.path + "\n");