1
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is ubufox.
17
* The Initial Developer of the Original Code is
19
* Portions created by the Initial Developer are Copyright (C) 2011
20
* the Initial Developer. All Rights Reserved.
23
* Chris Coulson <chris.coulson@canonical.com>
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.
37
* ***** END LICENSE BLOCK ***** */
39
const Cc = Components.classes;
40
const Cu = Components.utils;
41
const Ci = Components.interfaces;
43
const nsITimer = Ci.nsITimer;
45
var EXPORTED_SYMBOLS = [ ];
47
Cu.import("resource://ubufox/PluginFinder.jsm");
48
Cu.import("resource://ubufox/libs/gio.jsm");
49
Cu.import("resource://ubufox/libs/gobject.jsm");
50
Cu.import("resource://ubufox/libs/glib.jsm");
51
Cu.import("resource://gre/modules/Services.jsm");
52
Cu.import("resource://gre/modules/ctypes.jsm");
54
["LOG", "WARN", "ERROR"].forEach(function(aName) {
55
this.__defineGetter__(aName, function() {
56
Components.utils.import("resource://gre/modules/AddonLogging.jsm");
58
LogManager.getLogger("ubufox.plugininstaller.apt", this);
63
function AptInstallContext(aListener, aPkgName) {
64
this.listener = aListener;
65
this.pkgname = aPkgName;
68
this.transaction = null;
69
this.error_code = null;
70
this.error_details = null;
73
AptInstallContext.prototype = {
74
destroy: function AIC_destroy() {
75
if (this.proxy && !this.proxy.isNull()) {
76
gobject.g_object_unref(this.proxy);
80
if (this.transaction && !this.transaction.isNull()) {
81
gobject.GSignalHandlerDisconnect(this.transaction, this.transactionSigID);
82
gobject.g_object_unref(this.transaction);
83
this.transaction = null;
88
var PluginInstallerApt = {
89
install: function PIA_install(aPluginInfo, aListener) {
90
let pkgname = Services.io.newURI(aPluginInfo.location, null, null)
91
.path.replace(/^([a-zA-Z0-9\-]*).*/, "$1");
92
LOG("Starting apt install for " + pkgname);
94
var ctxt = new AptInstallContext(aListener, pkgname);
96
gio.GDbusProxyNewForBus(gio.G_BUS_TYPE_SYSTEM, gio.G_DBUS_PROXY_FLAGS_NONE,
97
null, "org.debian.apt", "/org/debian/apt",
98
"org.debian.apt", null, function(aObject, aResult) {
99
PluginInstallerApt.proxyNewCb(aObject, aResult, ctxt);
103
proxyNewCb: function PIA_proxyNewCb(aObject, aResult, aCtxt) {
104
LOG("Got aptdaemon proxy for " + aCtxt.pkgname);
105
let error = new glib.GError.ptr;
106
aCtxt.proxy = gio.g_dbus_proxy_new_for_bus_finish(aResult, error.address());
107
if (aCtxt.proxy.isNull() || !error.isNull()) {
108
ERROR("Failed to get GDBusProxy for aptdaemon: " +
109
error.contents.message.readString());
110
this.fail(this.getString("plugininstaller.apt.error.unexpected"), aCtxt);
114
let builder = new glib.GVariantBuilder;
115
glib.g_variant_builder_init(builder.address(), "a*");
116
glib.g_variant_builder_add(builder.address(), "s",
117
ctypes.char.array()(aCtxt.pkgname).address());
118
let inner = glib.g_variant_builder_end(builder.address());
119
glib.g_variant_builder_clear(builder.address());
121
glib.g_variant_builder_init(builder.address(), "r");
122
glib.g_variant_builder_add_value(builder.address(), inner);
123
let params = glib.g_variant_builder_end(builder.address());
124
glib.g_variant_builder_clear(builder.address());
126
gio.GDbusProxyCall(aCtxt.proxy, "InstallPackages", params,
127
gio.G_DBUS_CALL_FLAGS_NONE, -1, null, function(aObject,
129
PluginInstallerApt.installPkgsCb(aObject, aResult, aCtxt);
133
installPkgsCb: function PIA_installPkgsCb(aObject, aResult, aCtxt) {
134
LOG("Got response to InstallPackages() for " + aCtxt.pkgname);
135
let error = new glib.GError.ptr;
136
let res = gio.g_dbus_proxy_call_finish(ctypes.cast(aObject, gio.GDBusProxy.ptr),
137
aResult, error.address());
138
if (res.isNull() || !error.isNull()) {
139
ERROR("InstallPackages() method failed: " + error.contents.message.readString());
140
this.fail(this.getString("plugininstaller.apt.error.unexpected"), aCtxt);
144
let path = new ctypes.char.ptr;
145
glib.g_variant_get(res, "(s)", path.address());
147
gio.GDbusProxyNewForBus(gio.G_BUS_TYPE_SYSTEM, gio.G_DBUS_PROXY_FLAGS_NONE,
148
null, "org.debian.apt", path.readString(),
149
"org.debian.apt.transaction", null,
150
function(aObject, aResult) {
151
PluginInstallerApt.transactionNewCb(aObject, aResult, aCtxt);
154
glib.g_variant_unref(res);
157
transactionNewCb: function PIA_transactionNewCb(aObject, aResult, aCtxt) {
158
LOG("Got transaction proxy for " + aCtxt.pkgname);
159
let error = new glib.GError.ptr;
160
aCtxt.transaction = gio.g_dbus_proxy_new_for_bus_finish(aResult, error.address());
161
if (aCtxt.transaction.isNull() || !error.isNull()) {
162
ERROR("Failed to get GDBusProxy for transaction: " +
163
error.contents.message.readString());
164
this.fail(this.getString("plugininstaller.apt.error.unexpected"), aCtxt);
168
aCtxt.transactionSigID = gobject.GSignalConnect(aCtxt.transaction, "g-signal",
169
function(aProxy, aSender,
172
PluginInstallerApt.transactionSignalCb(aProxy, aSender, aSignal,
174
}, ctypes.void_t, [gio.GDBusProxy.ptr, glib.gchar.ptr, glib.gchar.ptr,
175
glib.GVariant.ptr, glib.gpointer]);
177
gio.GDbusProxyCall(aCtxt.transaction, "Run", null,
178
gio.G_DBUS_CALL_FLAGS_NONE, -1, null, function(aObject,
180
PluginInstallerApt.runCb(aObject, aResult, aCtxt);
184
transactionSignalCb: function PIA_transactionSignalCb(aProxy, aSender,
185
aSignal, aParams, aCtxt) {
186
let signal = aSignal.readString();
187
LOG("Got signal " + signal + " for " + aCtxt.pkgname);
188
if (signal == "PropertyChanged") {
189
let namePtr = new ctypes.char.ptr;
190
let variant = new glib.GVariant.ptr;
191
glib.g_variant_get(aParams, "(sv)", namePtr.address(), variant.address());
193
// FIXME: Desperately need some sanity checking in here
195
let name = namePtr.readString();
196
LOG("Property - " + name);
197
if (name == "Progress") {
198
if (!aCtxt.running) {
202
let progress = glib.g_variant_get_int32(variant);
203
LOG("Progress = " + progress.toString());
204
aCtxt.listener.onProgressChanged(progress);
206
} else if (name == "ExitState") {
207
let exitState = glib.g_variant_get_string(variant, null).readString();
208
LOG("ExitState = " + exitState);
209
if (exitState == "exit-success") {
212
this.fail(this.getErrorMessage(aCtxt), aCtxt);
214
} else if (name == "Status") {
215
let status = glib.g_variant_get_string(variant, null).readString();
216
LOG("Status = " + status);
217
if (status == "status-running") {
218
aCtxt.running = true;
219
aCtxt.listener.onInstallStarted();
221
} else if (name == "Error") {
222
aCtxt.error_code = new ctypes.char.ptr;
223
aCtxt.error_details = new ctypes.char.ptr;
224
glib.g_variant_get(variant, "(ss)", aCtxt.error_code.address(),
225
aCtxt.error_details.address());
226
ERROR("Error: " + aCtxt.error_code.readString() + " (" +
227
aCtxt.error_details.readString() + ")");
232
runCb: function PIA_runCb(aObject, aResult, aCtxt) {
233
LOG("Got response to Run() for " + aCtxt.pkgname);
234
let error = new glib.GError.ptr;
235
let res = gio.g_dbus_proxy_call_finish(ctypes.cast(aObject,
237
aResult, error.address());
238
if (!error.isNull()) {
239
ERROR("Error running transaction: " + error.contents.message.readString()
240
+ " (domain: " + error.contents.domain + ", code: "
241
+ error.contents.code + ")");
243
if (aCtxt.error_code) {
244
msg = this.getErrorMessage(aCtxt);
245
} else if (error.contents.domain == gio.G_IO_ERROR &&
246
error.contents.code == gio.G_IO_ERROR_DBUS_ERROR) {
247
let rerr = gio.g_dbus_error_get_remote_error(error).readString();
248
if (rerr == "org.freedesktop.PolicyKit.Error.NotAuthorized") {
249
msg = this.getString("plugininstaller.apt.error.unauthorized");
254
msg = this.getString("plugininstaller.apt.error.unexpected");
257
this.fail(msg, aCtxt);
261
finish: function PIA_finish(aCtxt) {
262
aCtxt.listener.onInstallFinished();
266
fail: function PIA_fail(aError, aCtxt) {
267
aCtxt.listener.onInstallFailed(aError);
271
getErrorMessage: function PIA_getErrorMessage(aCtxt) {
273
switch (aCtxt.error_code.readString()) {
274
case "error-package-download-failed":
275
msg = this.getString("plugininstaller.apt.error.download_failed");
278
case "error-dep-resolution-failed":
279
msg = this.getString("plugininstaller.apt.error.dependencies");
282
case "error-no-lock":
283
msg = this.getString("plugininstaller.apt.error.lock");
286
case "error-no-package":
287
msg = this.getString("plugininstaller.apt.error.does_not_exist");
290
case "error-package-already-installed":
291
msg = this.getString("plugininstaller.apt.error.already_installed");
294
case "error-package-manager-failed":
295
msg = this.getString("plugininstaller.apt.error.failed_install");
298
case "error-cache-broken":
299
msg = this.getString("plugininstaller.apt.error.broken_cache");
302
case "error-package-unauthenticated":
303
msg = this.getString("plugininstaller.apt.error.unauthenticated");
306
case "error-incomplete-install":
307
msg = this.getString("plugininstaller.apt.error.incomplete_install");
310
case "error-unreadable-package-file":
311
msg = this.getString("plugininstaller.apt.error.open_failed");
314
case "error-invalid-package-file":
315
msg = this.getString("plugininstaller.apt.error.policy_violation");
319
msg = this.getString("plugininstaller.apt.error.unexpected");
326
getString: function PIA_getString(aName) {
327
if (!this._strbundle) {
329
Services.strings.createBundle("chrome://ubufox/locale/plugins.properties");
332
return this._strbundle.GetStringFromName(aName);
336
var PluginInstallerIface = {
339
install: function PII_install(aPluginInfo, aListener) {
340
return PluginInstallerApt.install(aPluginInfo, aListener);
343
shutdown: function PII_shutdown() {
344
PluginFinder.unregisterInstallHandler(this);
348
PluginFinder.registerInstallHandler(PluginInstallerIface);