~ubuntu-branches/ubuntu/maverick/ubufox/maverick-proposed

« back to all changes in this revision

Viewing changes to content/pluginInstallerService.js

  • Committer: Bazaar Package Importer
  • Author(s): Chris Coulson
  • Date: 2011-04-09 00:08:51 UTC
  • mto: (1.1.23)
  • mto: This revision was merged to the branch mainline in revision 46.
  • Revision ID: james.westby@ubuntu.com-20110409000851-upuxdwy0ugr3r0l2
Tags: upstream-0.9
ImportĀ upstreamĀ versionĀ 0.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
 *
37
37
 * ***** END LICENSE BLOCK ***** */
38
38
 
39
 
var PluginXPIInstallService = {
40
 
  
41
 
  init: function () 
42
 
  {
43
 
  },
44
 
 
45
 
  pluginPidArray: null,
46
 
 
47
 
  startPluginInstallation: function (aPluginXPIUrlsArray,
48
 
                                     aPluginHashes,
49
 
                                     aPluginPidArray) {
50
 
     this.pluginPidArray = aPluginPidArray;
51
 
 
52
 
     var xpiManager = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
53
 
                                .createInstance(Components.interfaces.nsIXPInstallManager);
54
 
     xpiManager.initManagerWithHashes(aPluginXPIUrlsArray, aPluginHashes,
55
 
                                      aPluginXPIUrlsArray.length, this);
56
 
  },
57
 
 
58
 
  // XPI progress listener stuff
59
 
  onStateChange: function (aIndex, aState, aValue)
60
 
  {
61
 
    // get the pid to return to the wizard
62
 
    var pid = this.pluginPidArray[aIndex];
63
 
    var errorMsg;
64
 
 
65
 
    if (aState == Components.interfaces.nsIXPIProgressDialog.INSTALL_DONE) {
66
 
      if (aValue != 0) {
67
 
        var xpinstallStrings = document.getElementById("xpinstallStrings");
68
 
        try {
69
 
          errorMsg = xpinstallStrings.getString("error" + aValue);
70
 
        }
71
 
        catch (e) {
72
 
          errorMsg = xpinstallStrings.getFormattedString("unknown.error", [aValue]);
73
 
        }
74
 
      }
75
 
    }
76
 
 
77
 
    gPluginInstaller.pluginXPIInstallationProgress(pid, aState, errorMsg);
78
 
 
79
 
  },
80
 
 
81
 
  onProgress: function (aIndex, aValue, aMaxValue)
82
 
  {
83
 
    // get the pid to return to the wizard
84
 
    var pid = this.pluginPidArray[aIndex];
85
 
 
86
 
    gPluginInstaller.pluginXPIInstallationProgressMeter(pid, aValue, aMaxValue);
 
39
Components.utils.import("resource://gre/modules/AddonManager.jsm");
 
40
 
 
41
const DOWNLOAD_STARTED = 0;
 
42
const DOWNLOAD_FINISHED = 1;
 
43
const INSTALL_STARTED = 2;
 
44
const INSTALL_FINISHED = 3;
 
45
const INSTALLS_COMPLETE = 4;
 
46
 
 
47
function getLocalizedError(key)
 
48
{
 
49
  return document.getElementById("xpinstallStrings").getString(key);
 
50
}
 
51
 
 
52
function binaryToHex(input)
 
53
{
 
54
  return [('0' + input.charCodeAt(i).toString(16)).slice(-2)
 
55
          for (i in input)].join('');
 
56
}
 
57
 
 
58
function verifyHash(aFile, aHash)
 
59
{
 
60
  try {
 
61
    var [, method, hash] = /^([A-Za-z0-9]+):(.*)$/.exec(aHash);
 
62
 
 
63
    var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
 
64
      createInstance(Components.interfaces.nsIFileInputStream);
 
65
    fis.init(aFile, -1, -1, 0);
 
66
 
 
67
    var hasher = Components.classes['@mozilla.org/security/hash;1'].
 
68
      createInstance(Components.interfaces.nsICryptoHash);
 
69
    hasher.initWithString(method);
 
70
    hasher.updateFromStream(fis, -1);
 
71
    dlhash = binaryToHex(hasher.finish(false));
 
72
    return dlhash == hash;
 
73
  }
 
74
  catch (e) {
 
75
    Components.utils.reportError(e);
 
76
    return false;
 
77
  }
 
78
}
 
79
 
 
80
function InstallerObserver(aPlugin)
 
81
{
 
82
  this._plugin = aPlugin;
 
83
  this._init();
 
84
}
 
85
 
 
86
InstallerObserver.prototype = {
 
87
  _init: function()
 
88
  {
 
89
    try {
 
90
      var ios = Components.classes["@mozilla.org/network/io-service;1"].
 
91
        getService(Components.interfaces.nsIIOService);
 
92
      var uri = ios.newURI(this._plugin.InstallerLocation, null, null);
 
93
      uri.QueryInterface(Components.interfaces.nsIURL);
 
94
 
 
95
      // Use a local filename appropriate for the OS
 
96
      var leafName = uri.fileName;
 
97
      var os = Components.classes["@mozilla.org/xre/app-info;1"]
 
98
                         .getService(Components.interfaces.nsIXULRuntime)
 
99
                         .OS;
 
100
      if (os == "WINNT" && leafName.indexOf(".") < 0)
 
101
        leafName += ".exe";
 
102
 
 
103
      var dirs = Components.classes["@mozilla.org/file/directory_service;1"].
 
104
        getService(Components.interfaces.nsIProperties);
 
105
 
 
106
      var resultFile = dirs.get("TmpD", Components.interfaces.nsIFile);
 
107
      resultFile.append(leafName);
 
108
      resultFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE,
 
109
                              0770);
 
110
 
 
111
      var channel = ios.newChannelFromURI(uri);
 
112
      this._downloader =
 
113
        Components.classes["@mozilla.org/network/downloader;1"].
 
114
          createInstance(Components.interfaces.nsIDownloader);
 
115
      this._downloader.init(this, resultFile);
 
116
      channel.notificationCallbacks = this;
 
117
 
 
118
      this._fireNotification(DOWNLOAD_STARTED, null);
 
119
 
 
120
      channel.asyncOpen(this._downloader, null);
 
121
    }
 
122
    catch (e) {
 
123
      Components.utils.reportError(e);
 
124
      this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-228"));
 
125
      if (resultFile && resultFile.exists())
 
126
        resultfile.remove(false);
 
127
    }
 
128
  },
 
129
 
 
130
  /**
 
131
   * Inform the gPluginInstaller about what's going on.
 
132
   */
 
133
  _fireNotification: function(aStatus, aErrorMsg)
 
134
  {
 
135
    gPluginInstaller.pluginXPIInstallationProgress(this._plugin.pid,
 
136
                                                   aStatus, aErrorMsg);
 
137
 
 
138
    if (aStatus == INSTALL_FINISHED) {
 
139
      --PluginInstallService._installersPending;
 
140
      PluginInstallService._fireFinishedNotification();
 
141
    }
 
142
  },
 
143
 
 
144
  QueryInterface: function(iid)
 
145
  {
 
146
    if (iid.equals(Components.interfaces.nsISupports) ||
 
147
        iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
 
148
        iid.equals(Components.interfaces.nsIDownloadObserver) ||
 
149
        iid.equals(Components.interfaces.nsIProgressEventSink))
 
150
      return this;
 
151
 
 
152
    throw Components.results.NS_ERROR_NO_INTERFACE;
 
153
  },
 
154
 
 
155
  getInterface: function(iid)
 
156
  {
 
157
    if (iid.equals(Components.interfaces.nsIProgressEventSink))
 
158
      return this;
 
159
 
 
160
    return null;
 
161
  },
 
162
 
 
163
  onDownloadComplete: function(downloader, request, ctxt, status, result)
 
164
  {
 
165
    if (!Components.isSuccessCode(status)) {
 
166
      // xpinstall error 228 is "Download Error"
 
167
      this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-228"));
 
168
      result.remove(false);
 
169
      return;
 
170
    }
 
171
 
 
172
    this._fireNotification(DOWNLOAD_FINISHED);
 
173
 
 
174
    if (this._plugin.InstallerHash &&
 
175
        !verifyHash(result, this._plugin.InstallerHash)) {
 
176
      // xpinstall error 261 is "Invalid file hash..."
 
177
      this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-261"));
 
178
      result.remove(false);
 
179
      return;
 
180
    }
 
181
 
 
182
    this._fireNotification(INSTALL_STARTED);
 
183
 
 
184
    result.QueryInterface(Components.interfaces.nsILocalFile);
 
185
    try {
 
186
      // Make sure the file is executable
 
187
      result.permissions = 0770;
 
188
      var process = Components.classes["@mozilla.org/process/util;1"]
 
189
                              .createInstance(Components.interfaces.nsIProcess);
 
190
      process.init(result);
 
191
      var self = this;
 
192
      process.runAsync([], 0, {
 
193
        observe: function(subject, topic, data) {
 
194
          if (topic != "process-finished") {
 
195
            Components.utils.reportError("Failed to launch installer");
 
196
            self._fireNotification(INSTALL_FINISHED,
 
197
                                   getLocalizedError("error-207"));
 
198
          }
 
199
          else if (process.exitValue != 0) {
 
200
            Components.utils.reportError("Installer returned exit code " + process.exitValue);
 
201
            self._fireNotification(INSTALL_FINISHED,
 
202
                                   getLocalizedError("error-203"));
 
203
          }
 
204
          else {
 
205
            self._fireNotification(INSTALL_FINISHED, null);
 
206
          }
 
207
          result.remove(false);
 
208
        }
 
209
      });
 
210
    }
 
211
    catch (e) {
 
212
      Components.utils.reportError(e);
 
213
      this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-207"));
 
214
      result.remove(false);
 
215
    }
 
216
  },
 
217
 
 
218
  onProgress: function(aRequest, aContext, aProgress, aProgressMax)
 
219
  {
 
220
    gPluginInstaller.pluginInstallationProgressMeter(this._plugin.pid,
 
221
                                                     aProgress,
 
222
                                                     aProgressMax);
 
223
  },
 
224
 
 
225
  onStatus: function(aRequest, aContext, aStatus, aStatusArg)
 
226
  {
 
227
    /* pass */
 
228
  }
 
229
};
 
230
 
 
231
var PluginInstallService = {
 
232
 
 
233
  /**
 
234
   * Start installation of installers and XPI plugins.
 
235
   * @param aInstallerPlugins An array of objects which should have the
 
236
   *                          properties "pid", "InstallerLocation",
 
237
   *                          and "InstallerHash"
 
238
   * @param aXPIPlugins       An array of objects which should have the
 
239
   *                          properties "pid", "XPILocation",
 
240
   *                          and "XPIHash"
 
241
   */
 
242
  startPluginInstallation: function (aInstallerPlugins,
 
243
                                     aXPIPlugins)
 
244
  {
 
245
    this._xpiPlugins = aXPIPlugins;
 
246
    this._xpisPending = aXPIPlugins.length;
 
247
 
 
248
    aXPIPlugins.forEach(function(plugin) {
 
249
      AddonManager.getInstallForURL(plugin.XPILocation, function(install) {
 
250
        install.addListener(PluginInstallService);
 
251
        install.install();
 
252
      }, "application/x-xpinstall", plugin.XPIHash);
 
253
    });
 
254
 
 
255
    // InstallerObserver may finish immediately so we must initialise the
 
256
    // installers after setting the number of installers and xpis pending
 
257
    this._installersPending = aInstallerPlugins.length;
 
258
    this._installerPlugins = [new InstallerObserver(plugin)
 
259
                              for each (plugin in aInstallerPlugins)];
 
260
  },
 
261
 
 
262
  _fireFinishedNotification: function()
 
263
  {
 
264
    if (this._installersPending == 0 && this._xpisPending == 0)
 
265
      gPluginInstaller.pluginXPIInstallationProgress(null, INSTALLS_COMPLETE, null);
 
266
  },
 
267
 
 
268
  getPidForInstall: function(install) {
 
269
    for (let i = 0; i < this._xpiPlugins.length; i++) {
 
270
      if (install.sourceURI.spec == this._xpiPlugins[i].XPILocation)
 
271
        return this._xpiPlugins[i].pid;
 
272
    }
 
273
    return -1;
 
274
  },
 
275
 
 
276
  // InstallListener interface
 
277
  onDownloadStarted: function(install) {
 
278
    var pid = this.getPidForInstall(install);
 
279
    gPluginInstaller.pluginXPIInstallationProgress(pid, DOWNLOAD_STARTED, null);
 
280
  },
 
281
 
 
282
  onDownloadProgress: function(install) {
 
283
    var pid = this.getPidForInstall(install);
 
284
    gPluginInstaller.pluginXPIInstallationProgressMeter(pid, install.progress,
 
285
                                                     install.maxProgress);
 
286
  },
 
287
 
 
288
  onDownloadEnded: function(install) {
 
289
    var pid = this.getPidForInstall(install);
 
290
    gPluginInstaller.pluginXPIInstallationProgress(pid, DOWNLOAD_FINISHED, null);
 
291
  },
 
292
 
 
293
  onDownloadFailed: function(install) {
 
294
    var pid = this.getPidForInstall(install);
 
295
    switch (install.error) {
 
296
    case AddonManager.ERROR_NETWORK_FAILURE:
 
297
      var errorMsg = getLocalizedError("error-228");
 
298
      break;
 
299
    case AddonManager.ERROR_INCORRECT_HASH:
 
300
      var errorMsg = getLocalizedError("error-261");
 
301
      break;
 
302
    case AddonManager.ERROR_CORRUPT_FILE:
 
303
      var errorMsg = getLocalizedError("error-207");
 
304
      break;
 
305
    }
 
306
    gPluginInstaller.pluginXPIInstallationProgress(pid, INSTALL_FINISHED, errorMsg);
 
307
 
 
308
    this._xpisPending--;
 
309
    this._fireFinishedNotification();
 
310
  },
 
311
 
 
312
  onInstallStarted: function(install) {
 
313
    var pid = this.getPidForInstall(install);
 
314
    gPluginInstaller.pluginXPIInstallationProgress(pid, INSTALL_STARTED, null);
 
315
  },
 
316
 
 
317
  onInstallEnded: function(install, addon) {
 
318
    var pid = this.getPidForInstall(install);
 
319
    gPluginInstaller.pluginXPIInstallationProgress(pid, INSTALL_FINISHED, null);
 
320
 
 
321
    this._xpisPending--;
 
322
    this._fireFinishedNotification();
 
323
  },
 
324
 
 
325
  onInstallFailed: function(install) {
 
326
    var pid = this.getPidForInstall(install);
 
327
    gPluginInstaller.pluginXPIInstallationProgress(pid, INSTALL_FINISHED,
 
328
                                                   getLocalizedError("error-203"));
 
329
 
 
330
    this._xpisPending--;
 
331
    this._fireFinishedNotification();
87
332
  }
88
333
}
89
334
 
95
340
  mAptPidArray: null,
96
341
  mRunning: false,
97
342
 
98
 
  install: function(aAptUrlArray,
99
 
                    aAptPidArray,
 
343
  install: function(aAptPlugins,
100
344
                    aAptInstallerService) {
101
345
 
102
346
    this.mAptInstallerService = aAptInstallerService;
103
 
    this.mAptUrlArray = aAptUrlArray;
104
 
    this.mAptPidArray = aAptPidArray;
 
347
    this.mAptPlugins = aAptPlugins;
105
348
    this.mRunning = true;
106
349
 
107
350
    //    var thread = Components.classes["@mozilla.org/thread;1"]
112
355
 
113
356
  run: function()
114
357
  {
115
 
    for (var i = 0; i < this.mAptUrlArray.length; i++) {
116
 
      var aptUrl = this.mAptUrlArray[i];
117
 
      var aptPid = this.mAptPidArray[i];
 
358
    for (var i = 0; i < this.mAptPlugins.length; i++) {
 
359
      var aptUrl = this.mAptPlugins[i].XPILocation;
 
360
      var aptPid = this.mAptPlugins[i].pid;
118
361
      this.mAptInstallerService.onNotifyStart(aptUrl, aptPid);
119
362
 
120
363
      var executable =
125
368
 
126
369
      if(!executable.exists() || !executable.isExecutable()) {
127
370
        window.alert('Unexpected error!');
128
 
        this.mAptInstallerService.onNotifyResult(aptUrl, aptPid, -1 );
 
371
        this.mAptInstallerService.onNotifyResult(aptUrl, aptPid, -1);
129
372
        continue;
130
373
      }
131
374
 
181
424
 
182
425
  pluginPidArray: null,
183
426
 
184
 
  startPluginInstallation: function (aPluginAptUrlsArray,
185
 
                                     aPluginPidArray) {
186
 
    AptInstaller.install(aPluginAptUrlsArray, aPluginPidArray, this);
 
427
  startPluginInstallation: function (aAptPlugins) {
 
428
    AptInstaller.install(aAptPlugins, this);
187
429
  },
188
430
 
189
431
  onNotifyStart: function (aptUrl, aptPid) {