[gnome-shell] extensions-tool: Add 'install' command



commit a429fdbd081722f079b0452df37abc97f396ee5f
Author: Florian Müllner <fmuellner gnome org>
Date:   Tue Sep 4 01:41:55 2018 +0200

    extensions-tool: Add 'install' command
    
    The ability to install unaudited extensions directly from a zip file
    can be useful for testing and code review, so implement a corresponding
    command that complements the previously added 'pack' command.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/issues/1234

 src/extensions-tool/command-install.c              | 207 +++++++++++++++++++++
 src/extensions-tool/command-pack.c                 |  24 +--
 src/extensions-tool/commands.h                     |   1 +
 src/extensions-tool/common.h                       |   3 +
 .../completion/bash/gnome-extensions               |   9 +-
 src/extensions-tool/main.c                         |  28 +++
 src/extensions-tool/man/gnome-extensions.txt       |  18 ++
 src/extensions-tool/meson-src.build                |   1 +
 src/extensions-tool/meson.build                    |   1 +
 9 files changed, 268 insertions(+), 24 deletions(-)
---
diff --git a/src/extensions-tool/command-install.c b/src/extensions-tool/command-install.c
new file mode 100644
index 0000000000..58f62bb866
--- /dev/null
+++ b/src/extensions-tool/command-install.c
@@ -0,0 +1,207 @@
+/* command-install.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <gnome-autoar/gnome-autoar.h>
+#include <json-glib/json-glib.h>
+
+#include "commands.h"
+#include "common.h"
+#include "config.h"
+
+static JsonObject *
+load_metadata (GFile   *dir,
+               GError **error)
+{
+  g_autoptr (JsonParser) parser = NULL;
+  g_autoptr (GInputStream) stream = NULL;
+  g_autoptr (GFile) file = NULL;
+
+  file = g_file_get_child (dir, "metadata.json");
+  stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
+  if (stream == NULL)
+    return NULL;
+
+  parser = json_parser_new_immutable ();
+  if (!json_parser_load_from_stream (parser, stream, NULL, error))
+    return NULL;
+
+  return json_node_dup_object (json_parser_get_root (parser));
+}
+
+static void
+on_error (AutoarExtractor *extractor,
+          GError          *error,
+          gpointer         data)
+{
+  *((GError **)data) = g_error_copy (error);
+}
+
+static GFile *
+on_decide_destination (AutoarExtractor *extractor,
+                       GFile           *dest,
+                       GList           *files,
+                       gpointer         data)
+{
+  g_autofree char *dest_path = NULL;
+  GFile *new_dest;
+  int copy = 1;
+
+  dest_path = g_file_get_path (dest);
+  new_dest = g_object_ref (dest);
+
+  while (g_file_query_exists (new_dest, NULL))
+    {
+      g_autofree char *new_path = g_strdup_printf ("%s (%d)", dest_path, copy);
+
+      g_object_unref (new_dest);
+      new_dest = g_file_new_for_path (new_path);
+
+      copy++;
+    }
+
+  *((GFile **)data) = g_object_ref (new_dest);
+
+  return new_dest;
+}
+
+static int
+install_extension (const char *bundle,
+                   gboolean    force)
+{
+  g_autoptr (AutoarExtractor) extractor = NULL;
+  g_autoptr (JsonObject) metadata = NULL;
+  g_autoptr (GFile) cachedir = NULL;
+  g_autoptr (GFile) tmpdir = NULL;
+  g_autoptr (GFile) src = NULL;
+  g_autoptr (GFile) dst = NULL;
+  g_autoptr (GFile) dstdir = NULL;
+  g_autoptr (GError) error = NULL;
+  g_autofree char *cwd = NULL;
+  const char *uuid;
+
+  cwd = g_get_current_dir ();
+  src = g_file_new_for_commandline_arg_and_cwd (bundle, cwd);
+  cachedir = g_file_new_for_path (g_get_user_cache_dir ());
+
+  extractor = autoar_extractor_new (src, cachedir);
+
+  g_signal_connect (extractor, "error", G_CALLBACK (on_error), &error);
+  g_signal_connect (extractor, "decide-destination", G_CALLBACK (on_decide_destination), &tmpdir);
+
+  autoar_extractor_start (extractor, NULL);
+
+  if (error != NULL)
+    goto err;
+
+  metadata = load_metadata (tmpdir, &error);
+  if (metadata == NULL)
+    goto err;
+
+  dstdir = g_file_new_build_filename (g_get_user_data_dir (),
+                                      "gnome-shell", "extensions", NULL);
+
+  if (!g_file_make_directory_with_parents (dstdir, NULL, &error))
+    goto err;
+
+  uuid = json_object_get_string_member (metadata, "uuid");
+  dst = g_file_get_child (dstdir, uuid);
+
+  if (g_file_query_exists (dst, NULL))
+    {
+      if (!force)
+        {
+          g_set_error (&error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                       "%s exists and --force was not specified", uuid);
+          goto err;
+        }
+      else if (!file_delete_recursively (dst, &error))
+        {
+          goto err;
+        }
+    }
+
+  if (!g_file_move (tmpdir, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, &error))
+    goto err;
+
+  return 0;
+
+err:
+  if (error != NULL)
+    g_printerr ("%s\n", error->message);
+
+  if (tmpdir != NULL)
+    file_delete_recursively (tmpdir, NULL);
+
+  return 2;
+}
+
+int
+handle_install (int argc, char *argv[], gboolean do_help)
+{
+  g_autoptr (GOptionContext) context = NULL;
+  g_autoptr (GError) error = NULL;
+  g_auto (GStrv) filenames = NULL;
+  gboolean force = FALSE;
+  GOptionEntry entries[] = {
+    { .long_name = "force", .short_name = 'f',
+      .arg = G_OPTION_ARG_NONE, .arg_data = &force,
+      .description = _("Overwrite an existing extension") },
+    { .long_name = G_OPTION_REMAINING,
+      .arg_description =_("EXTENSION_BUNDLE"),
+      .arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = &filenames },
+    { NULL }
+  };
+
+  g_set_prgname ("gnome-extensions install");
+
+  context = g_option_context_new (NULL);
+  g_option_context_set_help_enabled (context, FALSE);
+  g_option_context_set_summary (context, _("Install an extension bundle"));
+  g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+
+  if (do_help)
+    {
+      show_help (context, NULL);
+      return 0;
+    }
+
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      show_help (context, error->message);
+      return 1;
+    }
+
+  if (filenames == NULL)
+    {
+      show_help (context, _("No extension bundle specified"));
+      return 1;
+    }
+
+  if (g_strv_length (filenames) > 1)
+    {
+      show_help (context, _("More than one extension bundle specified"));
+      return 1;
+    }
+
+  return install_extension (*filenames, force);
+}
diff --git a/src/extensions-tool/command-pack.c b/src/extensions-tool/command-pack.c
index 28c39adb7c..d98e8fa4fb 100644
--- a/src/extensions-tool/command-pack.c
+++ b/src/extensions-tool/command-pack.c
@@ -38,28 +38,6 @@ typedef struct _ExtensionPack {
 static void extension_pack_free (ExtensionPack *);
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (ExtensionPack, extension_pack_free);
 
-static void
-delete_recursively (GFile *file)
-{
-  g_autoptr (GFileEnumerator) file_enum = NULL;
-  GFile *child;
-
-  file_enum = g_file_enumerate_children (file, NULL, 0, NULL, NULL);
-  if (file_enum)
-    while (TRUE)
-      {
-        if (!g_file_enumerator_iterate (file_enum, NULL, &child, NULL, NULL))
-          return;
-
-        if (child == NULL)
-          break;
-
-        delete_recursively (child);
-      }
-
-  g_file_delete (file, NULL, NULL);
-}
-
 static ExtensionPack *
 extension_pack_new (const char *srcdir)
 {
@@ -74,7 +52,7 @@ static void
 extension_pack_free (ExtensionPack *pack)
 {
   if (pack->tmpdir)
-    delete_recursively (pack->tmpdir);
+    file_delete_recursively (pack->tmpdir, NULL);
 
   g_clear_pointer (&pack->files, g_hash_table_destroy);
   g_clear_pointer (&pack->metadata, json_object_unref);
diff --git a/src/extensions-tool/commands.h b/src/extensions-tool/commands.h
index 677998fc71..247a14434c 100644
--- a/src/extensions-tool/commands.h
+++ b/src/extensions-tool/commands.h
@@ -30,5 +30,6 @@ int handle_list       (int argc, char *argv[], gboolean do_help);
 int handle_info       (int argc, char *argv[], gboolean do_help);
 int handle_create     (int argc, char *argv[], gboolean do_help);
 int handle_pack       (int argc, char *argv[], gboolean do_help);
+int handle_install    (int argc, char *argv[], gboolean do_help);
 
 G_END_DECLS
diff --git a/src/extensions-tool/common.h b/src/extensions-tool/common.h
index 849c9caab5..860813a07b 100644
--- a/src/extensions-tool/common.h
+++ b/src/extensions-tool/common.h
@@ -54,4 +54,7 @@ void print_extension_info (GVariantDict  *info,
 GDBusProxy *get_shell_proxy (GError **error);
 GSettings  *get_shell_settings (void);
 
+gboolean file_delete_recursively (GFile   *file,
+                                  GError **error);
+
 G_END_DECLS
diff --git a/src/extensions-tool/completion/bash/gnome-extensions 
b/src/extensions-tool/completion/bash/gnome-extensions
index bc4816afae..73b5942812 100644
--- a/src/extensions-tool/completion/bash/gnome-extensions
+++ b/src/extensions-tool/completion/bash/gnome-extensions
@@ -5,7 +5,7 @@
 ################################################################################
 
 __gnome_extensions() {
-  local commands="version enable disable info show list create pack"
+  local commands="version enable disable info install show list create pack"
   local COMMAND=${COMP_WORDS[1]}
 
   _init_completion -s || return
@@ -54,6 +54,13 @@ __gnome_extensions() {
           ;;
       esac
       ;;
+    install)
+      if [[ $cur != -* ]]
+      then
+          _filedir zip
+          return 0
+      fi
+      ;;
   esac
 
   # Stop if we are currently waiting for an option value
diff --git a/src/extensions-tool/main.c b/src/extensions-tool/main.c
index 6a59079fb5..467f498050 100644
--- a/src/extensions-tool/main.c
+++ b/src/extensions-tool/main.c
@@ -126,6 +126,31 @@ print_extension_info (GVariantDict  *info,
   g_print ("  %s: %s\n", _("State"), extension_state_to_string (state));
 }
 
+gboolean
+file_delete_recursively (GFile   *file,
+                         GError **error)
+{
+  g_autoptr (GFileEnumerator) file_enum = NULL;
+  GFile *child;
+
+  file_enum = g_file_enumerate_children (file, NULL, 0, NULL, NULL);
+  if (file_enum)
+    while (TRUE)
+      {
+        if (!g_file_enumerator_iterate (file_enum, NULL, &child, NULL, error))
+          return FALSE;
+
+        if (child == NULL)
+          break;
+
+        if (!file_delete_recursively (child, error))
+          return FALSE;
+      }
+
+  return g_file_delete (file, NULL, error);
+}
+
+
 static int
 handle_version (int argc, char *argv[], gboolean do_help)
 {
@@ -163,6 +188,7 @@ usage (void)
   g_printerr ("  show      %s\n", _("Show extension info"));
   g_printerr ("  create    %s\n", _("Create extension"));
   g_printerr ("  pack      %s\n", _("Package extension"));
+  g_printerr ("  install   %s\n", _("Install extension bundle"));
   g_printerr ("\n");
   g_printerr (_("Use %s to get detailed help.\n"), "“gnome-extensions help COMMAND”");
 }
@@ -230,6 +256,8 @@ main (int argc, char *argv[])
     return handle_create (argc, argv, do_help);
   else if (g_str_equal (command, "pack"))
     return handle_pack (argc, argv, do_help);
+  else if (g_str_equal (command, "install"))
+    return handle_install (argc, argv, do_help);
   else
     usage ();
 
diff --git a/src/extensions-tool/man/gnome-extensions.txt b/src/extensions-tool/man/gnome-extensions.txt
index c29016d9d6..c362e4d0af 100644
--- a/src/extensions-tool/man/gnome-extensions.txt
+++ b/src/extensions-tool/man/gnome-extensions.txt
@@ -29,6 +29,8 @@ SYNOPSIS
 
 *gnome-extensions* pack ['OPTION'...]
 
+*gnome-extensions* install ['OPTION'...] 'PACK'
+
 DESCRIPTION
 -----------
 *gnome-extensions* is a utility that makes some common GNOME extensions
@@ -147,6 +149,22 @@ the current directory otherwise.
   *--out-dir*='DIRECTORY':::
     The directory where the pack should be created
 
+*install* ['OPTION'...] 'PACK'::
+Installs an extension from the bundle 'PACK'.
++
+The command unpacks the extension files and moves them to
+the expected location in the user's *$HOME*, so that it
+will be loaded in the next session.
++
+It is mainly intended for testing, not as a replacement for
+GNOME Software or the extension website. As extensions have
+privileged access to the user's session, it is advised to
+never load extensions from untrusted sources without carefully
+reviewing their content.
++
+.Options
+  *--force*:::
+    Override an existing extension
 
 EXIT STATUS
 -----------
diff --git a/src/extensions-tool/meson-src.build b/src/extensions-tool/meson-src.build
index 60834f6d36..7af5365e4e 100644
--- a/src/extensions-tool/meson-src.build
+++ b/src/extensions-tool/meson-src.build
@@ -4,6 +4,7 @@ sources = [
   'command-disable.c',
   'command-enable.c',
   'command-info.c',
+  'command-install.c',
   'command-list.c',
   'command-pack.c',
   'common.h',
diff --git a/src/extensions-tool/meson.build b/src/extensions-tool/meson.build
index 2528dfd52c..3902274901 100644
--- a/src/extensions-tool/meson.build
+++ b/src/extensions-tool/meson.build
@@ -13,6 +13,7 @@ sources = [
   'command-disable.c',
   'command-enable.c',
   'command-info.c',
+  'command-install.c',
   'command-list.c',
   'command-pack.c',
   'main.c'


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]