toolkit/mozapps/plugins/content/pluginInstallerService.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 Components.utils.import("resource://gre/modules/AddonManager.jsm");
michael@0 6
michael@0 7 const DOWNLOAD_STARTED = 0;
michael@0 8 const DOWNLOAD_FINISHED = 1;
michael@0 9 const INSTALL_STARTED = 2;
michael@0 10 const INSTALL_FINISHED = 3;
michael@0 11 const INSTALLS_COMPLETE = 4;
michael@0 12
michael@0 13 function getLocalizedError(key)
michael@0 14 {
michael@0 15 return document.getElementById("xpinstallStrings").getString(key);
michael@0 16 }
michael@0 17
michael@0 18 function binaryToHex(input)
michael@0 19 {
michael@0 20 return [('0' + input.charCodeAt(i).toString(16)).slice(-2)
michael@0 21 for (i in input)].join('');
michael@0 22 }
michael@0 23
michael@0 24 function verifyHash(aFile, aHash)
michael@0 25 {
michael@0 26 try {
michael@0 27 var [, method, hash] = /^([A-Za-z0-9]+):(.*)$/.exec(aHash);
michael@0 28
michael@0 29 var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
michael@0 30 createInstance(Components.interfaces.nsIFileInputStream);
michael@0 31 fis.init(aFile, -1, -1, 0);
michael@0 32
michael@0 33 var hasher = Components.classes['@mozilla.org/security/hash;1'].
michael@0 34 createInstance(Components.interfaces.nsICryptoHash);
michael@0 35 hasher.initWithString(method);
michael@0 36 hasher.updateFromStream(fis, -1);
michael@0 37 dlhash = binaryToHex(hasher.finish(false));
michael@0 38 return dlhash == hash;
michael@0 39 }
michael@0 40 catch (e) {
michael@0 41 Components.utils.reportError(e);
michael@0 42 return false;
michael@0 43 }
michael@0 44 }
michael@0 45
michael@0 46 function InstallerObserver(aPlugin)
michael@0 47 {
michael@0 48 this._plugin = aPlugin;
michael@0 49 this._init();
michael@0 50 }
michael@0 51
michael@0 52 InstallerObserver.prototype = {
michael@0 53 _init: function()
michael@0 54 {
michael@0 55 try {
michael@0 56 var ios = Components.classes["@mozilla.org/network/io-service;1"].
michael@0 57 getService(Components.interfaces.nsIIOService);
michael@0 58 var uri = ios.newURI(this._plugin.InstallerLocation, null, null);
michael@0 59 uri.QueryInterface(Components.interfaces.nsIURL);
michael@0 60
michael@0 61 // Use a local filename appropriate for the OS
michael@0 62 var leafName = uri.fileName;
michael@0 63 var os = Components.classes["@mozilla.org/xre/app-info;1"]
michael@0 64 .getService(Components.interfaces.nsIXULRuntime)
michael@0 65 .OS;
michael@0 66 if (os == "WINNT" && leafName.indexOf(".") < 0)
michael@0 67 leafName += ".exe";
michael@0 68
michael@0 69 var dirs = Components.classes["@mozilla.org/file/directory_service;1"].
michael@0 70 getService(Components.interfaces.nsIProperties);
michael@0 71
michael@0 72 var resultFile = dirs.get("TmpD", Components.interfaces.nsIFile);
michael@0 73 resultFile.append(leafName);
michael@0 74 resultFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE,
michael@0 75 0770);
michael@0 76
michael@0 77 var channel = ios.newChannelFromURI(uri);
michael@0 78 this._downloader =
michael@0 79 Components.classes["@mozilla.org/network/downloader;1"].
michael@0 80 createInstance(Components.interfaces.nsIDownloader);
michael@0 81 this._downloader.init(this, resultFile);
michael@0 82 channel.notificationCallbacks = this;
michael@0 83
michael@0 84 this._fireNotification(DOWNLOAD_STARTED, null);
michael@0 85
michael@0 86 channel.asyncOpen(this._downloader, null);
michael@0 87 }
michael@0 88 catch (e) {
michael@0 89 Components.utils.reportError(e);
michael@0 90 this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-228"));
michael@0 91 if (resultFile && resultFile.exists())
michael@0 92 resultfile.remove(false);
michael@0 93 }
michael@0 94 },
michael@0 95
michael@0 96 /**
michael@0 97 * Inform the gPluginInstaller about what's going on.
michael@0 98 */
michael@0 99 _fireNotification: function(aStatus, aErrorMsg)
michael@0 100 {
michael@0 101 gPluginInstaller.pluginInstallationProgress(this._plugin.pid,
michael@0 102 aStatus, aErrorMsg);
michael@0 103
michael@0 104 if (aStatus == INSTALL_FINISHED) {
michael@0 105 --PluginInstallService._installersPending;
michael@0 106 PluginInstallService._fireFinishedNotification();
michael@0 107 }
michael@0 108 },
michael@0 109
michael@0 110 QueryInterface: function(iid)
michael@0 111 {
michael@0 112 if (iid.equals(Components.interfaces.nsISupports) ||
michael@0 113 iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
michael@0 114 iid.equals(Components.interfaces.nsIDownloadObserver) ||
michael@0 115 iid.equals(Components.interfaces.nsIProgressEventSink))
michael@0 116 return this;
michael@0 117
michael@0 118 throw Components.results.NS_ERROR_NO_INTERFACE;
michael@0 119 },
michael@0 120
michael@0 121 getInterface: function(iid)
michael@0 122 {
michael@0 123 if (iid.equals(Components.interfaces.nsIProgressEventSink))
michael@0 124 return this;
michael@0 125
michael@0 126 return null;
michael@0 127 },
michael@0 128
michael@0 129 onDownloadComplete: function(downloader, request, ctxt, status, result)
michael@0 130 {
michael@0 131 if (!Components.isSuccessCode(status)) {
michael@0 132 // xpinstall error 228 is "Download Error"
michael@0 133 this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-228"));
michael@0 134 result.remove(false);
michael@0 135 return;
michael@0 136 }
michael@0 137
michael@0 138 this._fireNotification(DOWNLOAD_FINISHED);
michael@0 139
michael@0 140 if (this._plugin.InstallerHash &&
michael@0 141 !verifyHash(result, this._plugin.InstallerHash)) {
michael@0 142 // xpinstall error 261 is "Invalid file hash..."
michael@0 143 this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-261"));
michael@0 144 result.remove(false);
michael@0 145 return;
michael@0 146 }
michael@0 147
michael@0 148 this._fireNotification(INSTALL_STARTED);
michael@0 149
michael@0 150 result.QueryInterface(Components.interfaces.nsILocalFile);
michael@0 151 try {
michael@0 152 // Make sure the file is executable
michael@0 153 result.permissions = 0770;
michael@0 154 var process = Components.classes["@mozilla.org/process/util;1"]
michael@0 155 .createInstance(Components.interfaces.nsIProcess);
michael@0 156 process.init(result);
michael@0 157 var self = this;
michael@0 158 process.runAsync([], 0, {
michael@0 159 observe: function(subject, topic, data) {
michael@0 160 if (topic != "process-finished") {
michael@0 161 Components.utils.reportError("Failed to launch installer");
michael@0 162 self._fireNotification(INSTALL_FINISHED,
michael@0 163 getLocalizedError("error-207"));
michael@0 164 }
michael@0 165 else if (process.exitValue != 0) {
michael@0 166 Components.utils.reportError("Installer returned exit code " + process.exitValue);
michael@0 167 self._fireNotification(INSTALL_FINISHED,
michael@0 168 getLocalizedError("error-203"));
michael@0 169 }
michael@0 170 else {
michael@0 171 self._fireNotification(INSTALL_FINISHED, null);
michael@0 172 }
michael@0 173 result.remove(false);
michael@0 174 }
michael@0 175 });
michael@0 176 }
michael@0 177 catch (e) {
michael@0 178 Components.utils.reportError(e);
michael@0 179 this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-207"));
michael@0 180 result.remove(false);
michael@0 181 }
michael@0 182 },
michael@0 183
michael@0 184 onProgress: function(aRequest, aContext, aProgress, aProgressMax)
michael@0 185 {
michael@0 186 gPluginInstaller.pluginInstallationProgressMeter(this._plugin.pid,
michael@0 187 aProgress,
michael@0 188 aProgressMax);
michael@0 189 },
michael@0 190
michael@0 191 onStatus: function(aRequest, aContext, aStatus, aStatusArg)
michael@0 192 {
michael@0 193 /* pass */
michael@0 194 }
michael@0 195 };
michael@0 196
michael@0 197 var PluginInstallService = {
michael@0 198
michael@0 199 /**
michael@0 200 * Start installation of installers and XPI plugins.
michael@0 201 * @param aInstallerPlugins An array of objects which should have the
michael@0 202 * properties "pid", "InstallerLocation",
michael@0 203 * and "InstallerHash"
michael@0 204 * @param aXPIPlugins An array of objects which should have the
michael@0 205 * properties "pid", "XPILocation",
michael@0 206 * and "XPIHash"
michael@0 207 */
michael@0 208 startPluginInstallation: function (aInstallerPlugins,
michael@0 209 aXPIPlugins)
michael@0 210 {
michael@0 211 this._xpiPlugins = aXPIPlugins;
michael@0 212 this._xpisPending = aXPIPlugins.length;
michael@0 213
michael@0 214 aXPIPlugins.forEach(function(plugin) {
michael@0 215 AddonManager.getInstallForURL(plugin.XPILocation, function(install) {
michael@0 216 install.addListener(PluginInstallService);
michael@0 217 install.install();
michael@0 218 }, "application/x-xpinstall", plugin.XPIHash);
michael@0 219 });
michael@0 220
michael@0 221 // InstallerObserver may finish immediately so we must initialise the
michael@0 222 // installers after setting the number of installers and xpis pending
michael@0 223 this._installersPending = aInstallerPlugins.length;
michael@0 224 this._installerPlugins = [new InstallerObserver(plugin)
michael@0 225 for each (plugin in aInstallerPlugins)];
michael@0 226 },
michael@0 227
michael@0 228 _fireFinishedNotification: function()
michael@0 229 {
michael@0 230 if (this._installersPending == 0 && this._xpisPending == 0)
michael@0 231 gPluginInstaller.pluginInstallationProgress(null, INSTALLS_COMPLETE, null);
michael@0 232 },
michael@0 233
michael@0 234 getPidForInstall: function(install) {
michael@0 235 for (let i = 0; i < this._xpiPlugins.length; i++) {
michael@0 236 if (install.sourceURI.spec == this._xpiPlugins[i].XPILocation)
michael@0 237 return this._xpiPlugins[i].pid;
michael@0 238 }
michael@0 239 return -1;
michael@0 240 },
michael@0 241
michael@0 242 // InstallListener interface
michael@0 243 onDownloadStarted: function(install) {
michael@0 244 var pid = this.getPidForInstall(install);
michael@0 245 gPluginInstaller.pluginInstallationProgress(pid, DOWNLOAD_STARTED, null);
michael@0 246 },
michael@0 247
michael@0 248 onDownloadProgress: function(install) {
michael@0 249 var pid = this.getPidForInstall(install);
michael@0 250 gPluginInstaller.pluginInstallationProgressMeter(pid, install.progress,
michael@0 251 install.maxProgress);
michael@0 252 },
michael@0 253
michael@0 254 onDownloadEnded: function(install) {
michael@0 255 var pid = this.getPidForInstall(install);
michael@0 256 gPluginInstaller.pluginInstallationProgress(pid, DOWNLOAD_FINISHED, null);
michael@0 257 },
michael@0 258
michael@0 259 onDownloadFailed: function(install) {
michael@0 260 var pid = this.getPidForInstall(install);
michael@0 261 switch (install.error) {
michael@0 262 case AddonManager.ERROR_NETWORK_FAILURE:
michael@0 263 var errorMsg = getLocalizedError("error-228");
michael@0 264 break;
michael@0 265 case AddonManager.ERROR_INCORRECT_HASH:
michael@0 266 var errorMsg = getLocalizedError("error-261");
michael@0 267 break;
michael@0 268 case AddonManager.ERROR_CORRUPT_FILE:
michael@0 269 var errorMsg = getLocalizedError("error-207");
michael@0 270 break;
michael@0 271 }
michael@0 272 gPluginInstaller.pluginInstallationProgress(pid, INSTALL_FINISHED, errorMsg);
michael@0 273
michael@0 274 this._xpisPending--;
michael@0 275 this._fireFinishedNotification();
michael@0 276 },
michael@0 277
michael@0 278 onInstallStarted: function(install) {
michael@0 279 var pid = this.getPidForInstall(install);
michael@0 280 gPluginInstaller.pluginInstallationProgress(pid, INSTALL_STARTED, null);
michael@0 281 },
michael@0 282
michael@0 283 onInstallEnded: function(install, addon) {
michael@0 284 var pid = this.getPidForInstall(install);
michael@0 285 gPluginInstaller.pluginInstallationProgress(pid, INSTALL_FINISHED, null);
michael@0 286
michael@0 287 this._xpisPending--;
michael@0 288 this._fireFinishedNotification();
michael@0 289 },
michael@0 290
michael@0 291 onInstallFailed: function(install) {
michael@0 292 var pid = this.getPidForInstall(install);
michael@0 293 gPluginInstaller.pluginInstallationProgress(pid, INSTALL_FINISHED,
michael@0 294 getLocalizedError("error-203"));
michael@0 295
michael@0 296 this._xpisPending--;
michael@0 297 this._fireFinishedNotification();
michael@0 298 }
michael@0 299 }

mercurial