~ubuntu-branches/ubuntu/precise/ubufox/precise

« back to all changes in this revision

Viewing changes to content/pluginInstallerService.js

  • Committer: Package Import Robot
  • Author(s): Chris Coulson
  • Date: 2012-01-03 12:54:26 UTC
  • mfrom: (1.1.28)
  • Revision ID: package-import@ubuntu.com-20120103125426-6huvc70js97j10i4
Tags: 1.5-0ubuntu1
* New upstream release
  - Update for Firefox 12.0a1 compatibility
  - Rewrite the plugin installer code to natively support aptdaemon
    (instead of apturl).
    + Note, this only affects plugin installation - search still happens
      using the online PFS.
  - Simplify the update restart notification code
  - Set the update restart notification reminder period to 15 minutes
* Drop dependency on apturl, and add dependency on aptdaemon and
  libglib2.0-0 (we're using GDBus via ctypes)
  - update debian/control

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* ***** BEGIN LICENSE BLOCK *****
2
 
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3
 
 *
4
 
 * The contents of this file are subject to the Mozilla Public License Version
5
 
 * 1.1 (the "License"); you may not use this file except in compliance with
6
 
 * the License. You may obtain a copy of the License at
7
 
 * http://www.mozilla.org/MPL/
8
 
 *
9
 
 * Software distributed under the License is distributed on an "AS IS" basis,
10
 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
 
 * for the specific language governing rights and limitations under the
12
 
 * License.
13
 
 *
14
 
 * The Original Code is Plugin Finder Service.
15
 
 *
16
 
 * The Initial Developer of the Original Code is
17
 
 * IBM Corporation.
18
 
 * Portions created by the IBM Corporation are Copyright (C) 2004
19
 
 * IBM Corporation. All Rights Reserved.
20
 
 *
21
 
 * Contributor(s):
22
 
 *   Doron Rosenberg <doronr@us.ibm.com>
23
 
 *   Alexander Sack <asac@jwsdot.com> - Canonical Ltd.
24
 
 *
25
 
 * Alternatively, the contents of this file may be used under the terms of
26
 
 * either the GNU General Public License Version 2 or later (the "GPL"), or
27
 
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
 
 * in which case the provisions of the GPL or the LGPL are applicable instead
29
 
 * of those above. If you wish to allow use of your version of this file only
30
 
 * under the terms of either the GPL or the LGPL, and not to allow others to
31
 
 * use your version of this file under the terms of the MPL, indicate your
32
 
 * decision by deleting the provisions above and replace them with the notice
33
 
 * and other provisions required by the GPL or the LGPL. If you do not delete
34
 
 * the provisions above, a recipient may use your version of this file under
35
 
 * the terms of any one of the MPL, the GPL or the LGPL.
36
 
 *
37
 
 * ***** END LICENSE BLOCK ***** */
38
 
 
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();
332
 
  }
333
 
}
334
 
 
335
 
 
336
 
var AptInstaller = {
337
 
 
338
 
  mAptInstallerService: null,
339
 
  mAptUrlArray: null,
340
 
  mAptPidArray: null,
341
 
  mRunning: false,
342
 
 
343
 
  install: function(aAptPlugins,
344
 
                    aAptInstallerService) {
345
 
 
346
 
    this.mAptInstallerService = aAptInstallerService;
347
 
    this.mAptPlugins = aAptPlugins;
348
 
    this.mRunning = true;
349
 
 
350
 
    //    var thread = Components.classes["@mozilla.org/thread;1"]
351
 
    //      .createInstance(Components.interfaces.nsIThread);
352
 
    //    thread.init(this, 0, nsIThread.PRIORITY_NORMAL, nsIThread.SCOPE_LOCAL, nsIThread.STATE_UNJOINABLE);
353
 
    this.run();
354
 
  },
355
 
 
356
 
  run: function()
357
 
  {
358
 
    for (var i = 0; i < this.mAptPlugins.length; i++) {
359
 
      var aptUrl = this.mAptPlugins[i].XPILocation;
360
 
      var aptPid = this.mAptPlugins[i].pid;
361
 
      this.mAptInstallerService.onNotifyStart(aptUrl, aptPid);
362
 
 
363
 
      var executable =
364
 
        Components.classes['@mozilla.org/file/local;1']
365
 
       .createInstance(Components.interfaces.nsILocalFile);
366
 
 
367
 
      executable.initWithPath("/usr/bin/apturl");
368
 
 
369
 
      if(!executable.exists() || !executable.isExecutable()) {
370
 
        window.alert('Unexpected error!');
371
 
        this.mAptInstallerService.onNotifyResult(aptUrl, aptPid, -1);
372
 
        continue;
373
 
      }
374
 
 
375
 
      var procUtil =
376
 
        Components.classes['@mozilla.org/process/util;1']
377
 
        .createInstance(Components.interfaces.nsIProcess);
378
 
 
379
 
      var nsFile = executable.QueryInterface(Components.interfaces.nsIFile);
380
 
 
381
 
      procUtil.init(executable);
382
 
 
383
 
      var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
384
 
                                .getService(Components.interfaces.nsIPrefBranch);
385
 
 
386
 
      var proxyType = prefBranch.getIntPref("network.proxy.type");
387
 
      var proxyHost = prefBranch.getCharPref("network.proxy.http");
388
 
      var proxyPort = prefBranch.getIntPref("network.proxy.http_port");
389
 
 
390
 
      var httpProxy = "";
391
 
      if(proxyType > 0 && proxyHost != null && proxyHost.length > 0)
392
 
      {
393
 
        httpProxy = proxyHost;
394
 
        if(proxyPort > 0)
395
 
        {
396
 
          httpProxy = httpProxy + ":" + proxyPort;
397
 
        }
398
 
      }
399
 
 
400
 
      var args = new Array();
401
 
      if(httpProxy.length > 0)
402
 
      {
403
 
        args = new Array("--http-proxy", httpProxy, aptUrl);
404
 
      } else {
405
 
        args = new Array(aptUrl);
406
 
      }
407
 
      procUtil.run(true, args, args.length);
408
 
      res = procUtil.exitValue;
409
 
 
410
 
      this.mAptInstallerService.onNotifyResult(aptUrl, aptPid, res);
411
 
    }
412
 
 
413
 
    this.mAptInstallerService.onNotifyResult(null, null, -1 );
414
 
    mRunning = false;
415
 
    return true;
416
 
  }
417
 
}
418
 
 
419
 
var PluginAPTInstallService = {
420
 
  
421
 
  init: function () 
422
 
  {
423
 
  },
424
 
 
425
 
  pluginPidArray: null,
426
 
 
427
 
  startPluginInstallation: function (aAptPlugins) {
428
 
    AptInstaller.install(aAptPlugins, this);
429
 
  },
430
 
 
431
 
  onNotifyStart: function (aptUrl, aptPid) {
432
 
    gPluginInstaller.pluginXPIInstallationProgress(aptPid, 6, null);
433
 
  },
434
 
 
435
 
  onNotifyResult: function (aptUrl, aptPid, result) {
436
 
    if(result > 0) {
437
 
      gPluginInstaller.pluginXPIInstallationProgress(aptPid, 7, "Apt Install Failed or Cancelled");
438
 
    } else if (result == 0) {
439
 
      gPluginInstaller.pluginXPIInstallationProgress(aptPid, 7, null);
440
 
    } else {
441
 
      gPluginInstaller.pluginXPIInstallationProgress(null, 8, null);
442
 
    }
443
 
  }
444
 
}