From d8a98e5467ca59a8203229fc10c9e5ca16158312 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Wed, 24 Aug 2011 13:08:34 -0400 Subject: [PATCH] extensionSystem: Add install-from-HTTP capability This adds a new DBus method: InstallExtensionRemote(uuid : s, url : s) Pass it the UUID of an extension and the URL of a manifest file: the same as a metadata.json, but with a special key, '__installer', which is an HTTP location that points to an zip file containing the extension. The Shell will download and use it to install the extension. In the future, the manifest file may be used to automatically detect and install updates. https://bugzilla.gnome.org/show_bug.cgi?id=654770 --- js/ui/extensionSystem.js | 83 ++++++++++++++++++++++++++++++++++++++++ js/ui/shellDBus.js | 8 ++++ 2 files changed, 91 insertions(+) diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js index 6bd6b9177..8907a8ee8 100644 --- a/js/ui/extensionSystem.js +++ b/js/ui/extensionSystem.js @@ -7,6 +7,7 @@ const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const St = imports.gi.St; const Shell = imports.gi.Shell; +const Soup = imports.gi.Soup; const Config = imports.misc.config; @@ -26,6 +27,15 @@ const ExtensionType = { PER_USER: 2 }; +const _httpSession = new Soup.SessionAsync(); + +// The unfortunate state of gjs, gobject-introspection and libsoup +// means that I have to do a hack to add a feature. +// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context. + +if (Soup.Session.prototype.add_feature != null) + Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault()); + // Maps uuid -> metadata object const extensionMeta = {}; // Maps uuid -> importer object (extension directory tree) @@ -81,6 +91,79 @@ function versionCheck(required, current) { return false; } +function installExtensionFromManifestURL(uuid, url) { + _httpSession.queue_message( + Soup.Message.new('GET', url), + function(session, message) { + if (message.status_code != Soup.KnownStatusCode.OK) { + logExtensionError(uuid, 'downloading manifest: ' + message.status_code.toString()); + return; + } + + let manifest; + try { + manifest = JSON.parse(message.response_body.data); + } catch (e) { + logExtensionError(uuid, 'parsing: ' + e.toString()); + return; + } + + if (uuid != manifest['uuid']) { + logExtensionError(uuid, 'manifest: manifest uuids do not match'); + return; + } + + installExtensionFromManifest(manifest, meta); + }); +} + +function installExtensionFromManifest(manifest, meta) { + let uuid = manifest['uuid']; + let name = manifest['name']; + + let url = manifest['__installer']; + _httpSession.queue_message(Soup.Message.new('GET', url), + function(session, message) { + gotExtensionZipFile(session, message, uuid); + }); +} + +function gotExtensionZipFile(session, message, uuid) { + if (message.status_code != Soup.KnownStatusCode.OK) { + logExtensionError(uuid, 'downloading extension: ' + message.status_code); + return; + } + + // FIXME: use a GFile mkstemp-type method once one exists + let fd, tmpzip; + try { + [fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip'); + } catch (e) { + logExtensionError(uuid, 'tempfile: ' + e.toString()); + return; + } + + let stream = new Gio.UnixOutputStream({ fd: fd }); + let dir = userExtensionsDir.get_child(uuid); + Shell.write_soup_message_to_stream(stream, message); + stream.close(null); + let [success, pid] = GLib.spawn_async(null, + ['unzip', '-uod', dir.get_path(), '--', tmpzip], + null, + GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, + null); + + if (!success) { + logExtensionError(uuid, 'extract: could not extract'); + return; + } + + GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) { + GLib.spawn_close_pid(pid); + loadExtension(dir, true, ExtensionType.PER_USER); + }); +} + function disableExtension(uuid) { let meta = extensionMeta[uuid]; if (!meta) diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js index ae679ad52..07f2881b9 100644 --- a/js/ui/shellDBus.js +++ b/js/ui/shellDBus.js @@ -43,6 +43,10 @@ const GnomeShellIface = { { name: 'DisableExtension', inSignature: 's', outSignature: '' + }, + { name: 'InstallRemoteExtension', + inSignature: 's', + outSignature: '' } ], signals: [], @@ -166,6 +170,10 @@ GnomeShell.prototype = { global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions); }, + InstallRemoteExtension: function(uuid, url) { + ExtensionSystem.installExtensionFromManifestURL(uuid, url); + }, + get OverviewActive() { return Main.overview.visible; },