[evolution-data-server] Allow to load modules from custom prefixes



commit 4aff145a850b13232cae9958e1f409ae6e99f0cf
Author: Milan Crha <mcrha redhat com>
Date:   Thu Feb 25 08:02:36 2021 +0100

    Allow to load modules from custom prefixes
    
    This change loads modules not only from the install prefix,
    but tries to read it also from the ~/.local/share/evolution/modules
    and from the directories listed in the EDS_EXTRA_PREFIXES, which
    is a list of paths separated by colon (':').

 src/camel/camel-provider.c                         | 100 +++++++++++------
 src/camel/camel.c                                  | 123 +++++++++++++++++++++
 src/camel/camel.h                                  |   4 +
 src/libebackend/e-dbus-server.c                    |   2 +-
 src/libedataserver/e-data-server-util.c            |  35 ++++++
 src/libedataserver/e-data-server-util.h            |   3 +
 src/libedataserver/e-module.c                      |  58 ++++++++++
 src/libedataserver/e-module.h                      |   3 +
 src/libedataserver/e-source-credentials-provider.c |   2 +-
 src/libedataserverui/CMakeLists.txt                |   1 +
 src/libedataserverui/libedataserverui-private.c    |   2 +-
 11 files changed, 298 insertions(+), 35 deletions(-)
---
diff --git a/src/camel/camel-provider.c b/src/camel/camel-provider.c
index 8e4dad45a..915c65f87 100644
--- a/src/camel/camel-provider.c
+++ b/src/camel/camel-provider.c
@@ -37,6 +37,7 @@
 #include "camel-string-utils.h"
 #include "camel-vee-store.h"
 #include "camel-win32.h"
+#include "camel.h"
 
 /* table of CamelProviderModule's */
 static GHashTable *module_table;
@@ -182,45 +183,22 @@ provider_setup (gpointer param)
        return NULL;
 }
 
-/**
- * camel_provider_init:
- *
- * Initialize the Camel provider system by reading in the .urls
- * files in the provider directory and creating a hash table mapping
- * URLs to module names.
- *
- * A .urls file has the same initial prefix as the shared library it
- * correspond to, and consists of a series of lines containing the URL
- * protocols that that library handles.
- *
- * TODO: This should be pathed?
- * TODO: This should be plugin-d?
- **/
-void
-camel_provider_init (void)
+static void
+camel_provider_traverse_directory (const gchar *provider_dir,
+                                  gboolean warn_failure)
 {
        GDir *dir;
        const gchar *entry;
        gchar *p, *name, buf[80];
-       static gint loaded = 0;
-       const gchar *provider_dir;
-
-       provider_dir = g_getenv (EDS_CAMEL_PROVIDER_DIR);
-       if (!provider_dir)
-               provider_dir = CAMEL_PROVIDERDIR;
-
-       g_once (&setup_once, provider_setup, NULL);
-
-       if (loaded)
-               return;
-
-       loaded = 1;
 
        dir = g_dir_open (provider_dir, 0, NULL);
        if (!dir) {
-               g_warning (
-                       "Could not open camel provider directory (%s): %s",
-                       provider_dir, g_strerror (errno));
+               if (warn_failure) {
+                       g_warning (
+                               "Could not open camel provider directory (%s): %s",
+                               provider_dir, g_strerror (errno));
+               }
+
                return;
        }
 
@@ -273,6 +251,64 @@ camel_provider_init (void)
        g_dir_close (dir);
 }
 
+/**
+ * camel_provider_init:
+ *
+ * Initialize the Camel provider system by reading in the .urls
+ * files in the provider directory and creating a hash table mapping
+ * URLs to module names.
+ *
+ * A .urls file has the same initial prefix as the shared library it
+ * correspond to, and consists of a series of lines containing the URL
+ * protocols that that library handles.
+ *
+ * TODO: This should be pathed?
+ * TODO: This should be plugin-d?
+ **/
+void
+camel_provider_init (void)
+{
+       static gint loaded = 0;
+       const gchar *provider_dir;
+       gboolean is_default = FALSE;
+
+       provider_dir = g_getenv (EDS_CAMEL_PROVIDER_DIR);
+       if (!provider_dir) {
+               provider_dir = CAMEL_PROVIDERDIR;
+               is_default = TRUE;
+       }
+
+       g_once (&setup_once, provider_setup, NULL);
+
+       if (loaded)
+               return;
+
+       loaded = 1;
+
+       if (is_default) {
+               GPtrArray *variants;
+
+               variants = camel_util_get_directory_variants (provider_dir, E_DATA_SERVER_PREFIX, TRUE);
+
+               if (variants) {
+                       guint ii;
+
+                       for (ii = 0; ii < variants->len; ii++) {
+                               const gchar *path = g_ptr_array_index (variants, ii);
+
+                               if (path && *path)
+                                       camel_provider_traverse_directory (path, g_strcmp0 (provider_dir, 
path) == 0);
+                       }
+
+                       g_ptr_array_unref (variants);
+               } else {
+                       camel_provider_traverse_directory (provider_dir, TRUE);
+               }
+       } else {
+               camel_provider_traverse_directory (provider_dir, TRUE);
+       }
+}
+
 /**
  * camel_provider_load:
  * @path: the path to a shared library
diff --git a/src/camel/camel.c b/src/camel/camel.c
index d2babff18..e10f9e463 100644
--- a/src/camel/camel.c
+++ b/src/camel/camel.c
@@ -392,3 +392,126 @@ camel_binding_bind_property_with_closures (gpointer source,
 
        return binding;
 }
+
+static gint
+sort_paths_by_index (gconstpointer aa,
+                    gconstpointer bb,
+                    gpointer user_data)
+{
+       GHashTable *paths_hash = user_data;
+       const gchar *path1 = *((gchar **) aa);
+       const gchar *path2 = *((gchar **) bb);
+       gint val1, val2;
+
+       val1 = GPOINTER_TO_INT (g_hash_table_lookup (paths_hash, path1));
+       val2 = GPOINTER_TO_INT (g_hash_table_lookup (paths_hash, path2));
+
+       return val1 - val2;
+}
+
+/**
+ * camel_util_get_directory_variants:
+ * @main_path: the main path to work with
+ * @replace_prefix: path prefix to replace
+ * @with_modules_dir: whether to add also the modules directory
+ *
+ * The @main_path is a directory, which will be always used. It
+ * should have as its prefix the @replace_prefix, otherwise
+ * the function returns only the @main_path in the paths array.
+ *
+ * When there's exported an environment variable EDS_EXTRA_PREFIXES,
+ * it is used as a list of alternative prefixes where to look for
+ * the @main_path (rest after the @replace_prefix).
+ *
+ * When the @with_modules_dir is %TRUE, there's also added
+ * g_get_user_data_dir() + "evolution/modules/", aka
+ * ~/.local/share/evolution/modules/, into the resulting array.
+ *
+ * Returns: (element-type utf8) (transfer container): a %GPtrArray
+ *    with paths to use, including the @main_path. Free it with
+ *    g_ptr_array_unref(), when no longer needed.
+ *
+ * Since: 3.40
+ **/
+GPtrArray *
+camel_util_get_directory_variants (const gchar *main_path,
+                                  const gchar *replace_prefix,
+                                  gboolean with_modules_dir)
+{
+       GPtrArray *paths;
+       GHashTable *paths_hash;
+       GHashTableIter iter;
+       gpointer key;
+       gint index = 0;
+
+       g_return_val_if_fail (main_path && *main_path, NULL);
+       g_return_val_if_fail (replace_prefix && *replace_prefix, NULL);
+
+       paths_hash = g_hash_table_new (g_str_hash, g_str_equal);
+       g_hash_table_insert (paths_hash, g_strdup (main_path), GINT_TO_POINTER (index++));
+
+       if (g_str_has_prefix (main_path, replace_prefix)) {
+               const gchar *add_path;
+               guint len = strlen (replace_prefix);
+
+               if (replace_prefix[len - 1] == G_DIR_SEPARATOR)
+                       len--;
+
+               add_path = main_path + len;
+
+               if (add_path[0] == G_DIR_SEPARATOR) {
+                       const gchar *env = g_getenv ("EDS_EXTRA_PREFIXES");
+
+                       /* Skip the directory separator */
+                       add_path++;
+
+                       if (env) {
+                               gchar **strv;
+                               guint ii;
+
+                               strv = g_strsplit (env,
+                               #ifdef G_OS_WIN32
+                                       ";",
+                               #else
+                                       ":",
+                               #endif
+                                       -1);
+
+                               for (ii = 0; strv && strv[ii]; ii++) {
+                                       const gchar *prefix = strv[ii];
+
+                                       if (*prefix) {
+                                               gchar *path = g_build_filename (prefix, add_path, NULL);
+
+                                               if (!path || g_hash_table_contains (paths_hash, path))
+                                                       g_free (path);
+                                               else
+                                                       g_hash_table_insert (paths_hash, path, 
GINT_TO_POINTER (index++));
+                                       }
+                               }
+
+                               g_strfreev (strv);
+                       }
+
+                       if (with_modules_dir) {
+                               gchar *path = g_build_filename (g_get_user_data_dir (), "evolution", 
"modules", add_path, NULL);
+
+                               if (!path || g_hash_table_contains (paths_hash, path))
+                                       g_free (path);
+                               else
+                                       g_hash_table_insert (paths_hash, path, GINT_TO_POINTER (index++));
+                       }
+               }
+       }
+
+       paths = g_ptr_array_new_with_free_func (g_free);
+       g_hash_table_iter_init (&iter, paths_hash);
+
+       while (g_hash_table_iter_next (&iter, &key, NULL)) {
+               g_ptr_array_add (paths, key);
+       }
+
+       g_ptr_array_sort_with_data (paths, sort_paths_by_index, paths_hash);
+
+       return paths;
+}
diff --git a/src/camel/camel.h b/src/camel/camel.h
index 3ceba1bdb..6733085be 100644
--- a/src/camel/camel.h
+++ b/src/camel/camel.h
@@ -176,6 +176,10 @@ GBinding * camel_binding_bind_property_with_closures
                                                 GBindingFlags flags,
                                                 GClosure *transform_to,
                                                 GClosure *transform_from);
+GPtrArray *    camel_util_get_directory_variants
+                                               (const gchar *main_path,
+                                                const gchar *replace_prefix,
+                                                gboolean with_modules_dir);
 
 G_END_DECLS
 
diff --git a/src/libebackend/e-dbus-server.c b/src/libebackend/e-dbus-server.c
index cf265772b..3ce8cd2b9 100644
--- a/src/libebackend/e-dbus-server.c
+++ b/src/libebackend/e-dbus-server.c
@@ -702,7 +702,7 @@ e_dbus_server_load_modules (EDBusServer *server)
 
        G_LOCK (loaded_modules);
 
-       list = e_module_load_all_in_directory (class->module_directory);
+       list = e_module_load_all_in_directory_and_prefixes (class->module_directory, E_DATA_SERVER_PREFIX);
        for (link = list; link; link = g_list_next (link)) {
                EModule *module = link->data;
 
diff --git a/src/libedataserver/e-data-server-util.c b/src/libedataserver/e-data-server-util.c
index 401fb3388..eb762120f 100644
--- a/src/libedataserver/e-data-server-util.c
+++ b/src/libedataserver/e-data-server-util.c
@@ -3408,3 +3408,38 @@ e_util_source_compare_for_sort (ESource *source_a,
 
        return e_source_compare_by_display_name (source_a, source_b);
 }
+
+/**
+ * e_util_get_directory_variants:
+ * @main_path: the main path to work with
+ * @replace_prefix: path prefix to replace
+ * @with_modules_dir: whether to add also the modules directory
+ *
+ * The @main_path is a directory, which will be always used. It
+ * should have as its prefix the @replace_prefix, otherwise
+ * the function returns only the @main_path in the paths array.
+ *
+ * When there's exported an environment variable EDS_EXTRA_PREFIXES,
+ * it is used as a list of alternative prefixes where to look for
+ * the @main_path (rest after the @replace_prefix).
+ *
+ * When the @with_modules_dir is %TRUE, there's also added
+ * g_get_user_data_dir() + "evolution/modules/", aka
+ * ~/.local/share/evolution/modules/, into the resulting array.
+ *
+ * Returns: (element-type utf8) (transfer container): a %GPtrArray
+ *    with paths to use, including the @main_path. Free it with
+ *    g_ptr_array_unref(), when no longer needed.
+ *
+ * Since: 3.40
+ **/
+GPtrArray *
+e_util_get_directory_variants (const gchar *main_path,
+                              const gchar *replace_prefix,
+                              gboolean with_modules_dir)
+{
+       g_return_val_if_fail (main_path && *main_path, NULL);
+       g_return_val_if_fail (replace_prefix && *replace_prefix, NULL);
+
+       return camel_util_get_directory_variants (main_path, replace_prefix, with_modules_dir);
+}
diff --git a/src/libedataserver/e-data-server-util.h b/src/libedataserver/e-data-server-util.h
index 526d8a542..797760012 100644
--- a/src/libedataserver/e-data-server-util.h
+++ b/src/libedataserver/e-data-server-util.h
@@ -295,6 +295,9 @@ gboolean    e_util_can_use_collection_as_credential_source
                                                 struct _ESource *child_source);
 gint           e_util_source_compare_for_sort  (struct _ESource *source_a,
                                                 struct _ESource *source_b);
+GPtrArray *    e_util_get_directory_variants   (const gchar *main_path,
+                                                const gchar *replace_prefix,
+                                                gboolean with_modules_dir);
 
 G_END_DECLS
 
diff --git a/src/libedataserver/e-module.c b/src/libedataserver/e-module.c
index 690156807..0ef8270e2 100644
--- a/src/libedataserver/e-module.c
+++ b/src/libedataserver/e-module.c
@@ -25,6 +25,7 @@
 
 #include <glib.h>
 
+#include "e-data-server-util.h"
 #include "e-module.h"
 
 /* This is the symbol we call when loading a module. */
@@ -337,3 +338,60 @@ e_module_load_file (const gchar *filename)
 
        return module;
 }
+
+/**
+ * e_module_load_all_in_directory_and_prefixes:
+ * @dirname: pathname for a directory containing modules to load
+ * @dirprefix: (nullable): prefix of @dirname, which can be replaced by custom prefixes, or %NULL
+ *
+ * Loads all the modules in the specified directory into memory and the other
+ * custom prefixes returned by e_util_get_directory_variants().  If
+ * you want to unload them (enabling on-demand loading) you must call
+ * g_type_module_unuse() on all the modules.  Free the returned list
+ * with g_list_free().
+ *
+ * When @dirprefix is %NULL, or not a prefix of @dirname, behaves
+ * the same as e_module_load_all_in_directory().
+ *
+ * Returns: (element-type EModule) (transfer container): a list of #EModules loaded
+ *    from @dirname and any extra prefix directory.
+ *
+ * Since: 3.40
+ **/
+GList *
+e_module_load_all_in_directory_and_prefixes (const gchar *dirname,
+                                            const gchar *dirprefix)
+{
+       GList *list = NULL;
+       GPtrArray *variants;
+       guint ii;
+
+       g_return_val_if_fail (dirname != NULL, NULL);
+
+       if (!g_module_supported ())
+               return NULL;
+
+       if (!dirprefix || !*dirprefix || !g_str_has_prefix (dirname, dirprefix))
+               return e_module_load_all_in_directory (dirname);
+
+       variants = e_util_get_directory_variants (dirname, dirprefix, TRUE);
+       if (!variants)
+               return e_module_load_all_in_directory (dirname);
+
+       for (ii = 0; ii < variants->len; ii++) {
+               const gchar *path = g_ptr_array_index (variants, ii);
+
+               if (path && *path) {
+                       GList *modules;
+
+                       modules = e_module_load_all_in_directory (path);
+
+                       if (modules)
+                               list = g_list_concat (list, modules);
+               }
+       }
+
+       g_ptr_array_unref (variants);
+
+       return list;
+}
diff --git a/src/libedataserver/e-module.h b/src/libedataserver/e-module.h
index 5e69dd831..4774d556b 100644
--- a/src/libedataserver/e-module.h
+++ b/src/libedataserver/e-module.h
@@ -73,6 +73,9 @@ EModule *     e_module_new                    (const gchar *filename);
 const gchar *  e_module_get_filename           (EModule *module);
 EModule *      e_module_load_file              (const gchar *filename);
 GList *                e_module_load_all_in_directory  (const gchar *dirname);
+GList *                e_module_load_all_in_directory_and_prefixes
+                                               (const gchar *dirname,
+                                                const gchar *dirprefix);
 
 G_END_DECLS
 
diff --git a/src/libedataserver/e-source-credentials-provider.c 
b/src/libedataserver/e-source-credentials-provider.c
index 739d4fd94..b1ead1440 100644
--- a/src/libedataserver/e-source-credentials-provider.c
+++ b/src/libedataserver/e-source-credentials-provider.c
@@ -133,7 +133,7 @@ source_credentials_provider_constructed (GObject *object)
 
                modules_loaded = TRUE;
 
-               module_types = e_module_load_all_in_directory (E_DATA_SERVER_CREDENTIALMODULEDIR);
+               module_types = e_module_load_all_in_directory_and_prefixes 
(E_DATA_SERVER_CREDENTIALMODULEDIR, E_DATA_SERVER_PREFIX);
                g_list_free_full (module_types, (GDestroyNotify) g_type_module_unuse);
        }
 
diff --git a/src/libedataserverui/CMakeLists.txt b/src/libedataserverui/CMakeLists.txt
index 468730436..63f594fbe 100644
--- a/src/libedataserverui/CMakeLists.txt
+++ b/src/libedataserverui/CMakeLists.txt
@@ -51,6 +51,7 @@ set_target_properties(edataserverui PROPERTIES
 target_compile_definitions(edataserverui PRIVATE
        -DG_LOG_DOMAIN=\"e-data-server-ui\"
        -DLIBEDATASERVERUI_COMPILATION
+       -DE_DATA_SERVER_PREFIX=\"${CMAKE_INSTALL_PREFIX}\"
        -DE_DATA_SERVER_UIMODULEDIR=\"${uimoduledir}\"
 )
 
diff --git a/src/libedataserverui/libedataserverui-private.c b/src/libedataserverui/libedataserverui-private.c
index 83cb41459..847d34e31 100644
--- a/src/libedataserverui/libedataserverui-private.c
+++ b/src/libedataserverui/libedataserverui-private.c
@@ -43,7 +43,7 @@ _libedataserverui_load_modules (void)
 
                modules_loaded = TRUE;
 
-               module_types = e_module_load_all_in_directory (E_DATA_SERVER_UIMODULEDIR);
+               module_types = e_module_load_all_in_directory_and_prefixes (E_DATA_SERVER_UIMODULEDIR, 
E_DATA_SERVER_PREFIX);
                g_list_free_full (module_types, (GDestroyNotify) g_type_module_unuse);
        }
 }


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