[gimp] app: new "gex" format (GIMP Extension).



commit 406279e4ef2579d381e78614a83d23a7cca2ea8b
Author: Jehan <jehan girinstud io>
Date:   Fri Apr 12 18:27:38 2019 +0200

    app: new "gex" format (GIMP Extension).
    
    File extension (.gex) may still change if anything better is proposed.
    This format is currently just a compressed archive containing the
    extension data (plug-in, brushes or whatever) and the metadata file.
    
    For now, opening such file will simply install it as a new extension,
    keeping all file permissions and structure. Of course in the future, it
    will have to trigger a confirmation dialog.
    
    Currently the compression used is zip, which is just a first draft. This
    is not a decisive choice as well. We could use some tarball compressed
    in whatever other compression algorithm. I use libarchive as a new
    dependency to support unarchiving as it seems to be a common library
    (and since it is already used by AppStream-glib anyway, this doesn't add
    any actual dependency, just make it direct).

 app/Makefile.am               |   1 +
 app/file-data/Makefile.am     |   4 +
 app/file-data/file-data-gex.c | 515 ++++++++++++++++++++++++++++++++++++++++++
 app/file-data/file-data-gex.h |  32 +++
 app/file-data/file-data.c     |  61 +++++
 configure.ac                  |  10 +-
 6 files changed, 619 insertions(+), 4 deletions(-)
---
diff --git a/app/Makefile.am b/app/Makefile.am
index c6774f0492..bd8dae85d3 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -189,6 +189,7 @@ gimpconsoleldadd = \
        $(GEXIV2_LIBS)                                          \
        $(Z_LIBS)                                               \
        $(JSON_C_LIBS)                                          \
+       $(LIBARCHIVE_LIBS)                                      \
        $(LIBMYPAINT_LIBS)                                      \
        $(LIBBACKTRACE_LIBS)                                    \
        $(LIBUNWIND_LIBS)                                       \
diff --git a/app/file-data/Makefile.am b/app/file-data/Makefile.am
index ba0fd577f2..6b683c918a 100644
--- a/app/file-data/Makefile.am
+++ b/app/file-data/Makefile.am
@@ -6,9 +6,11 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)                         \
        -I$(top_builddir)/app                   \
        -I$(top_srcdir)/app                     \
+       $(APPSTREAM_GLIB_CFLAGS)                \
        $(CAIRO_CFLAGS)                         \
        $(GEGL_CFLAGS)                          \
        $(GDK_PIXBUF_CFLAGS)                    \
+       $(LIBARCHIVE_CFLAGS)                    \
        -I$(includedir)
 
 noinst_LIBRARIES = libappfile-data.a
@@ -18,6 +20,8 @@ libappfile_data_a_SOURCES = \
        file-data.h     \
        file-data-gbr.c \
        file-data-gbr.h \
+       file-data-gex.c \
+       file-data-gex.h \
        file-data-gih.c \
        file-data-gih.h \
        file-data-pat.c \
diff --git a/app/file-data/file-data-gex.c b/app/file-data/file-data-gex.c
new file mode 100644
index 0000000000..c10850d34c
--- /dev/null
+++ b/app/file-data/file-data-gex.c
@@ -0,0 +1,515 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * Copyright (C) 2019 Jehan
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <appstream-glib.h>
+#include <archive.h>
+#include <archive_entry.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+#include <glib.h>
+#include <zlib.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "core/core-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpbrush.h"
+#include "core/gimpbrush-load.h"
+#include "core/gimpbrush-private.h"
+#include "core/gimpdrawable.h"
+#include "core/gimpextension-error.h"
+#include "core/gimpimage.h"
+#include "core/gimplayer-new.h"
+#include "core/gimpparamspecs.h"
+#include "core/gimptempbuf.h"
+
+#include "pdb/gimpprocedure.h"
+
+#include "file-data-gex.h"
+
+#include "gimp-intl.h"
+
+
+/*  local function prototypes  */
+
+
+typedef struct
+{
+  GInputStream  *input;
+  void          *buffer;
+} GexReadData;
+
+static int        file_gex_open_callback  (struct archive  *a,
+                                           void            *client_data);
+static la_ssize_t file_gex_read_callback  (struct archive  *a,
+                                           void            *client_data,
+                                           const void     **buffer);
+static int        file_gex_close_callback (struct archive  *a,
+                                           void            *client_data);
+
+static gboolean   file_gex_validate_path  (const gchar     *path,
+                                           const gchar     *file_name,
+                                           gboolean         first,
+                                           gchar          **plugin_id,
+                                           GError         **error);
+static gboolean   file_gex_validate       (GFile           *file,
+                                           AsApp          **appstream,
+                                           GError         **error);
+static gboolean   file_gex_decompress     (GFile           *file,
+                                           gchar           *plugin_id,
+                                           GError         **error);
+
+static int
+file_gex_open_callback (struct archive  *a,
+                        void            *client_data)
+{
+  /* File already opened when we start with libarchive. */
+  GexReadData *data = client_data;
+
+  data->buffer = g_malloc0 (2048);
+
+  return ARCHIVE_OK;
+}
+
+static la_ssize_t
+file_gex_read_callback (struct archive  *a,
+                        void            *client_data,
+                        const void     **buffer)
+{
+  GexReadData *data  = client_data;
+  GError      *error = NULL;
+  gssize       read_count;
+
+  read_count = g_input_stream_read (data->input, data->buffer, 2048, NULL, &error);
+
+  if (read_count == -1)
+    {
+      archive_set_error (a, 0, "%s: %s", G_STRFUNC, error->message);
+      g_clear_error (&error);
+
+      return ARCHIVE_FATAL;
+    }
+
+  *buffer = data->buffer;
+
+  return read_count;
+}
+
+static int
+file_gex_close_callback (struct archive  *a,
+                         void            *client_data)
+{
+  /* Input allocated outside, let's also unref it outside. */
+  GexReadData *data = client_data;
+
+  g_free (data->buffer);
+
+  return ARCHIVE_OK;
+}
+
+static gboolean
+file_gex_validate_path (const gchar  *path,
+                        const gchar  *file_name,
+                        gboolean      first,
+                        gchar       **plugin_id,
+                        GError      **error)
+{
+  gchar    *dirname = g_path_get_dirname (path);
+  gboolean  valid   = TRUE;
+
+  if (g_path_is_absolute (path) || g_strcmp0 (dirname, "/") == 0)
+    {
+      *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                            _("Absolute path are forbidden in GIMP extension '%s': %s"),
+                            file_name, path);
+      return FALSE;
+    }
+
+  if (g_strcmp0 (dirname, ".") == 0)
+    {
+      if (first)
+        {
+          *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                _("File not allowed in root of GIMP extension '%s': %s"),
+                                file_name, path);
+          valid = FALSE;
+        }
+      else
+        {
+          if (*plugin_id)
+            {
+              if (g_strcmp0 (path, *plugin_id) != 0)
+                {
+                  *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                        _("File not in GIMP extension '%s' folder id '%s': %s"),
+                                        file_name, *plugin_id, path);
+                  valid = FALSE;
+                }
+            }
+          else
+            {
+              *plugin_id = g_strdup (path);
+            }
+          }
+    }
+  else
+    {
+      valid = file_gex_validate_path (dirname, file_name, FALSE, plugin_id, error);
+    }
+
+  g_free (dirname);
+
+  return valid;
+}
+
+/**
+ * file_gex_validate:
+ * @file:
+ * @appstream:
+ * @error:
+ *
+ * Validate the extension file with the following tests:
+ * - No absolute path allowed.
+ * - All files must be in a single folder which determines the extension
+ *   ID.
+ * - This folder must contain the AppStream metadata file which must be
+ *   valid AppStream XML format.
+ * - The extension ID resulting from the AppStream parsing must
+ *   correspond to the extension ID resulting from the top folder.
+ *
+ * Returns: TRUE on success and allocates @appstream, FALSE otherwise
+ *          with @error set.
+ */
+static gboolean
+file_gex_validate (GFile   *file,
+                   AsApp  **appstream,
+                   GError **error)
+{
+  GInputStream *input;
+  gboolean      success = FALSE;
+
+  g_return_val_if_fail (error != NULL && *error == NULL, FALSE);
+  g_return_val_if_fail (appstream != NULL && *appstream == NULL, FALSE);
+
+  input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+  if (input)
+    {
+      struct archive        *a;
+      struct archive_entry *entry;
+      int                   r;
+      GexReadData           user_data;
+
+      user_data.input = input;
+      if ((a = archive_read_new ()))
+        {
+          archive_read_support_format_zip (a);
+
+          r = archive_read_open (a, &user_data, file_gex_open_callback,
+                                 file_gex_read_callback, file_gex_close_callback);
+          if (r == ARCHIVE_OK)
+            {
+              gchar  *appdata_path = NULL;
+              GBytes *appdata      = NULL;
+              gchar  *plugin_id    = NULL;
+
+              while (archive_read_next_header (a, &entry) == ARCHIVE_OK &&
+                     file_gex_validate_path (archive_entry_pathname (entry),
+                                             gimp_file_get_utf8_name (file),
+                                             TRUE, &plugin_id, error))
+                {
+                  if (plugin_id && ! appdata_path)
+                    appdata_path = g_strdup_printf ("%s/%s.metainfo.xml", plugin_id, plugin_id);
+
+                  if (appdata_path)
+                    {
+                      if (g_strcmp0 (appdata_path, archive_entry_pathname (entry)) == 0)
+                        {
+                          const void *buffer;
+                          GString    *appstring = g_string_new ("");
+                          off_t       offset;
+                          size_t      size;
+
+                          while (TRUE)
+                            {
+                              r = archive_read_data_block (a, &buffer, &size, &offset);
+
+                              if (r == ARCHIVE_FATAL)
+                                {
+                                  *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                                        _("Fatal error when uncompressing GIMP extension 
'%s': %s"),
+                                                        gimp_file_get_utf8_name (file),
+                                                        archive_error_string (a));
+                                  break;
+                                }
+                              else if (r == ARCHIVE_EOF)
+                                {
+                                  appdata = g_string_free_to_bytes (appstring);
+                                  break;
+                                }
+
+                              appstring = g_string_append_len (appstring, (const gchar *) buffer, size);
+                            }
+                          continue;
+                        }
+                    }
+                  archive_read_data_skip (a);
+                }
+
+              if (! (*error))
+                {
+                  if (appdata)
+                    {
+                      *appstream = as_app_new ();
+
+                      if (! as_app_parse_data (*appstream, appdata,
+                                               AS_APP_PARSE_FLAG_USE_HEURISTICS,
+                                               error))
+                        {
+                          g_clear_object (appstream);
+                        }
+                      else if (g_strcmp0 (as_app_get_id (*appstream), plugin_id) != 0)
+                        {
+                          *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                                _("GIMP extension '%s' directory (%s) different from 
AppStream id: %s"),
+                                                gimp_file_get_utf8_name (file),
+                                                plugin_id, as_app_get_id (*appstream));
+                          g_clear_object (appstream);
+                        }
+                    }
+                  else
+                    {
+                      *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                            _("GIMP extension '%s' requires an AppStream file: %s"),
+                                            gimp_file_get_utf8_name (file),
+                                            appdata_path);
+                    }
+                }
+              if (appdata_path)
+                g_free (appdata_path);
+              if (appdata)
+                g_bytes_unref (appdata);
+              if (plugin_id)
+                g_free (plugin_id);
+            }
+          else
+            {
+              *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                    _("Invalid GIMP extension '%s': %s"),
+                                    gimp_file_get_utf8_name (file),
+                                    archive_error_string (a));
+            }
+
+          archive_read_close (a);
+          archive_read_free (a);
+          if (! *error)
+            success = TRUE;
+        }
+      else
+        {
+          *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                "%s: archive_read_new() failed.", G_STRFUNC);
+        }
+
+      g_object_unref (input);
+    }
+  else
+    {
+      g_prefix_error (error, _("Could not open '%s' for reading: "),
+                      gimp_file_get_utf8_name (file));
+    }
+
+  return success;
+}
+
+static gboolean
+file_gex_decompress (GFile   *file,
+                     gchar   *plugin_id,
+                     GError **error)
+{
+  GInputStream *input;
+  GFile        *ext_dir = gimp_directory_file ("extensions", NULL);
+  gboolean      success = FALSE;
+
+  g_return_val_if_fail (error != NULL && *error == NULL, FALSE);
+  g_return_val_if_fail (plugin_id != NULL, FALSE);
+
+  input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+  if (input)
+    {
+      struct archive       *a;
+      struct archive       *ext;
+      struct archive_entry *entry;
+      int                   r;
+      GexReadData           user_data;
+      const void           *buffer;
+
+      user_data.input = input;
+      if ((a = archive_read_new ()))
+        {
+          archive_read_support_format_zip (a);
+
+          ext = archive_write_disk_new ();
+          archive_write_disk_set_options (ext,
+                                          ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM |
+                                          ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS |
+                                          ARCHIVE_EXTRACT_SECURE_NODOTDOT |
+                                          ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_NO_OVERWRITE);
+          archive_write_disk_set_standard_lookup (ext);
+
+          r = archive_read_open (a, &user_data, file_gex_open_callback,
+                                 file_gex_read_callback, file_gex_close_callback);
+          if (r == ARCHIVE_OK)
+            {
+              while (archive_read_next_header (a, &entry) == ARCHIVE_OK &&
+                     /* Re-validate just in case the archive got swapped
+                      * between validation and decompression. */
+                     file_gex_validate_path (archive_entry_pathname (entry),
+                                             gimp_file_get_utf8_name (file),
+                                             TRUE, &plugin_id, error))
+                {
+                  gchar  *path;
+                  size_t  size;
+                  off_t   offset;
+
+                  path = g_build_filename (g_file_get_path (ext_dir), archive_entry_pathname (entry), NULL);
+
+                  archive_entry_set_pathname (entry, path);
+                  g_free (path);
+
+                  r = archive_write_header (ext, entry);
+                  if (r < ARCHIVE_OK)
+                    {
+                      *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                            _("Fatal error when uncompressing GIMP extension '%s': %s"),
+                                            gimp_file_get_utf8_name (file),
+                                            archive_error_string (ext));
+                      break;
+                    }
+
+                  while (TRUE)
+                    {
+                      r = archive_read_data_block (a, &buffer, &size, &offset);
+                      if (r == ARCHIVE_FATAL)
+                        {
+                          *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                                _("Fatal error when uncompressing GIMP extension '%s': %s"),
+                                                gimp_file_get_utf8_name (file),
+                                                archive_error_string (a));
+                          break;
+                        }
+                      else if (r == ARCHIVE_EOF)
+                        break;
+
+                      r = archive_write_data_block (ext, buffer, size, offset);
+                      if (r < ARCHIVE_OK)
+                        {
+                          *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                                _("Fatal error when uncompressing GIMP extension '%s': %s"),
+                                                gimp_file_get_utf8_name (file),
+                                                archive_error_string (ext));
+                          break;
+                        }
+                    }
+                  if (*error)
+                    break;
+
+                  r = archive_write_finish_entry (ext);
+                  if (r < ARCHIVE_OK)
+                    {
+                      *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                            _("Fatal error when uncompressing GIMP extension '%s': %s"),
+                                            gimp_file_get_utf8_name (file),
+                                            archive_error_string (ext));
+                      break;
+                    }
+                }
+            }
+          else
+            {
+              *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                    _("Invalid GIMP extension '%s': %s"),
+                                    gimp_file_get_utf8_name (file),
+                                    archive_error_string (a));
+            }
+
+          archive_read_close (a);
+          archive_read_free (a);
+          archive_write_close(ext);
+          archive_write_free(ext);
+
+          if (! *error)
+            success = TRUE;
+        }
+      else
+        {
+          *error = g_error_new (GIMP_EXTENSION_ERROR, GIMP_EXTENSION_FAILED,
+                                "%s: archive_read_new() failed.", G_STRFUNC);
+        }
+
+      g_object_unref (input);
+    }
+  else
+    {
+      g_prefix_error (error, _("Could not open '%s' for reading: "),
+                      gimp_file_get_utf8_name (file));
+    }
+
+  g_object_unref (ext_dir);
+
+  return success;
+}
+
+/*  public functions  */
+
+GimpValueArray *
+file_gex_load_invoker (GimpProcedure         *procedure,
+                       Gimp                  *gimp,
+                       GimpContext           *context,
+                       GimpProgress          *progress,
+                       const GimpValueArray  *args,
+                       GError               **error)
+{
+  GimpValueArray *return_vals;
+  const gchar    *uri;
+  GFile          *file;
+  gboolean        success   = FALSE;
+  AsApp          *appdata   = NULL;
+
+  gimp_set_busy (gimp);
+
+  uri  = g_value_get_string (gimp_value_array_index (args, 1));
+  file = g_file_new_for_uri (uri);
+
+  if (file_gex_validate (file, &appdata, error))
+    success = file_gex_decompress (file, (gchar *) as_app_get_id (appdata), error);
+
+  g_object_unref (file);
+
+  return_vals = gimp_procedure_get_return_values (procedure, success,
+                                                  error ? *error : NULL);
+  gimp_unset_busy (gimp);
+
+  return return_vals;
+}
diff --git a/app/file-data/file-data-gex.h b/app/file-data/file-data-gex.h
new file mode 100644
index 0000000000..dfddc00f91
--- /dev/null
+++ b/app/file-data/file-data-gex.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * Copyright (C) 2019 Jehan
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FILE_DATA_GEX_H__
+#define __FILE_DATA_GEX_H__
+
+
+GimpValueArray * file_gex_load_invoker (GimpProcedure         *procedure,
+                                        Gimp                  *gimp,
+                                        GimpContext           *context,
+                                        GimpProgress          *progress,
+                                        const GimpValueArray  *args,
+                                        GError               **error);
+
+
+#endif /* __FILE_DATA_GEX_H__ */
+
diff --git a/app/file-data/file-data.c b/app/file-data/file-data.c
index 411d2cadba..88c0047b08 100644
--- a/app/file-data/file-data.c
+++ b/app/file-data/file-data.c
@@ -32,6 +32,7 @@
 
 #include "file-data.h"
 #include "file-data-gbr.h"
+#include "file-data-gex.h"
 #include "file-data-gih.h"
 #include "file-data-pat.h"
 
@@ -494,6 +495,66 @@ file_data_init (Gimp *gimp)
 
   gimp_plug_in_manager_add_procedure (gimp->plug_in_manager, proc);
   g_object_unref (procedure);
+
+  /*  file-gex-load  */
+  file = g_file_new_for_path ("file-gex-load");
+  procedure = gimp_plug_in_procedure_new (GIMP_PLUGIN, file);
+  g_object_unref (file);
+
+  procedure->proc_type    = GIMP_INTERNAL;
+  procedure->marshal_func = file_gex_load_invoker;
+
+  proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+  proc->menu_label = g_strdup (N_("GIMP extension"));
+  gimp_plug_in_procedure_set_icon (proc, GIMP_ICON_TYPE_ICON_NAME,
+                                   (const guint8 *) "gimp-plugin",
+                                   strlen ("gimp-plugin") + 1);
+  gimp_plug_in_procedure_set_file_proc (proc, "gex", "",
+                                        "20, string, GIMP");
+  gimp_plug_in_procedure_set_generic_file_proc (proc, TRUE);
+  gimp_plug_in_procedure_set_mime_types (proc, "image/gimp-x-gex");
+  gimp_plug_in_procedure_set_handles_uri (proc);
+
+  gimp_object_set_static_name (GIMP_OBJECT (procedure), "file-gex-load");
+  gimp_procedure_set_static_strings (procedure,
+                                     "file-gex-load",
+                                     "Loads GIMP extension",
+                                     "Loads GIMP extension",
+                                     "Jehan", "Jehan", "2019",
+                                     NULL);
+
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_int32 ("dummy-param",
+                                                      "Dummy Param",
+                                                      "Dummy parameter",
+                                                      G_MININT32, G_MAXINT32, 0,
+                                                      GIMP_PARAM_READWRITE));
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_string ("uri",
+                                                       "URI",
+                                                       "The URI of the file "
+                                                       "to load",
+                                                       TRUE, FALSE, TRUE,
+                                                       NULL,
+                                                       GIMP_PARAM_READWRITE));
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_string ("raw-uri",
+                                                       "Raw URI",
+                                                       "The URI of the file "
+                                                       "to load",
+                                                       TRUE, FALSE, TRUE,
+                                                       NULL,
+                                                       GIMP_PARAM_READWRITE));
+
+  gimp_procedure_add_return_value (procedure,
+                                   gimp_param_spec_string ("extension-id",
+                                                           "ID of installed extension",
+                                                           "Identifier of the newly installed extension",
+                                                           FALSE, TRUE, FALSE, NULL,
+                                                           GIMP_PARAM_READWRITE));
+
+  gimp_plug_in_manager_add_procedure (gimp->plug_in_manager, proc);
+  g_object_unref (procedure);
 }
 
 void
diff --git a/configure.ac b/configure.ac
index 5eddda0658..4cb7e8b00a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1584,13 +1584,15 @@ PKG_CHECK_MODULES(LZMA, liblzma >= liblzma_required_version,,
                  [add_deps_error([liblzma >= liblzma_required_version])])
 
 
-##########################
-# Check for appstream-glib
-##########################
+#############################
+# Check for extension support
+#############################
 
 PKG_CHECK_MODULES(APPSTREAM_GLIB, appstream-glib >= appstream_glib_required_version,,
-                 [add_deps_error([appstream-glib >= appstream_glib_required_version])])
+                  [add_deps_error([appstream-glib >= appstream_glib_required_version])])
 
+PKG_CHECK_MODULES(LIBARCHIVE, libarchive,,
+                  [add_deps_error([libarchive])])
 
 ###############################
 # Check for Ghostscript library


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