[evolution-data-server] Bug 773156 - Allow system-wide ESource configurations (Autoconfig)
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug 773156 - Allow system-wide ESource configurations (Autoconfig)
- Date: Mon, 5 Dec 2016 14:27:08 +0000 (UTC)
commit df37ebfd31509abcb92a45c24f973816b9b32b5f
Author: Iago López Galeiras <iago kinvolk io>
Date: Mon Dec 5 15:26:19 2016 +0100
Bug 773156 - Allow system-wide ESource configurations (Autoconfig)
src/libedataserver/CMakeLists.txt | 2 +
src/libedataserver/e-source-autoconfig.c | 235 ++++++++
src/libedataserver/e-source-autoconfig.h | 92 +++
src/libedataserver/e-source.c | 2 +
src/libedataserver/libedataserver.h | 1 +
.../evolution-source-registry/CMakeLists.txt | 1 +
.../evolution-source-registry-autoconfig.c | 604 ++++++++++++++++++++
.../evolution-source-registry.c | 11 +
8 files changed, 948 insertions(+), 0 deletions(-)
---
diff --git a/src/libedataserver/CMakeLists.txt b/src/libedataserver/CMakeLists.txt
index b5addf6..7762523 100644
--- a/src/libedataserver/CMakeLists.txt
+++ b/src/libedataserver/CMakeLists.txt
@@ -76,6 +76,7 @@ set(SOURCES
e-source-alarms.c
e-source-authentication.c
e-source-autocomplete.c
+ e-source-autoconfig.c
e-source-backend.c
e-source-calendar.c
e-source-camel.c
@@ -153,6 +154,7 @@ set(HEADERS
e-source-alarms.h
e-source-authentication.h
e-source-autocomplete.h
+ e-source-autoconfig.h
e-source-backend.h
e-source-calendar.h
e-source-camel.h
diff --git a/src/libedataserver/e-source-autoconfig.c b/src/libedataserver/e-source-autoconfig.c
new file mode 100644
index 0000000..6135bde
--- /dev/null
+++ b/src/libedataserver/e-source-autoconfig.c
@@ -0,0 +1,235 @@
+/*
+ * e-source-autoconfig.c
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * SECTION: e-source-autoconfig
+ * @include: libedataserver/libedataserver.h
+ * @short_description: #ESource extension for autoconfig settings
+ *
+ * The #ESourceAutoconfig extension keeps a mapping between user-specific
+ * sources and system-wide ones.
+ *
+ * Access the extension as follows:
+ *
+ * |[
+ * #include <libedataserver/libedataserver.h>
+ *
+ * ESourceAutoconfig *extension;
+ *
+ * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTOCONFIG);
+ * ]|
+ **/
+
+#include "e-source-autoconfig.h"
+
+#define E_SOURCE_AUTOCONFIG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_SOURCE_AUTOCONFIG, ESourceAutoconfigPrivate))
+
+struct _ESourceAutoconfigPrivate {
+ gchar *revision;
+};
+
+enum {
+ PROP_0,
+ PROP_REVISION
+};
+
+G_DEFINE_TYPE (
+ ESourceAutoconfig,
+ e_source_autoconfig,
+ E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_autoconfig_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_REVISION:
+ g_value_take_string (
+ value,
+ e_source_autoconfig_dup_revision (
+ E_SOURCE_AUTOCONFIG (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_autoconfig_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_REVISION:
+ e_source_autoconfig_set_revision (
+ E_SOURCE_AUTOCONFIG (object),
+ g_value_get_string (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_autoconfig_finalize (GObject *object)
+{
+ ESourceAutoconfigPrivate *priv;
+
+ priv = E_SOURCE_AUTOCONFIG_GET_PRIVATE (object);
+
+ g_free (priv->revision);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_source_autoconfig_parent_class)->finalize (object);
+}
+
+static void
+e_source_autoconfig_class_init (ESourceAutoconfigClass *class)
+{
+ GObjectClass *object_class;
+ ESourceExtensionClass *extension_class;
+
+ g_type_class_add_private (class, sizeof (ESourceAutoconfigPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = source_autoconfig_set_property;
+ object_class->get_property = source_autoconfig_get_property;
+ object_class->finalize = source_autoconfig_finalize;
+
+ extension_class = E_SOURCE_EXTENSION_CLASS (class);
+ extension_class->name = E_SOURCE_EXTENSION_AUTOCONFIG;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_REVISION,
+ g_param_spec_string (
+ "revision",
+ "Revision",
+ "Identifier to map a particular version of a system-wide source to a user-specific
source",
+ "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS |
+ E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_autoconfig_init (ESourceAutoconfig *extension)
+{
+ extension->priv = E_SOURCE_AUTOCONFIG_GET_PRIVATE (extension);
+}
+
+/**
+ * e_source_autoconfig_get_revision:
+ * @extension: an #ESourceAutoconfig
+ *
+ * Returns the revision of a data source. This maps a particular version of a
+ * system-wide source to a user-specific source.
+ *
+ * If doesn't match, the system-wide source will be copied to the user-specific
+ * evolution config directory, preserving the already present fields that are
+ * not defined by the system-wide source.
+ *
+ * If it matches, no copying is done.
+ *
+ * Returns: revision of the data source
+ *
+ * Since: 3.24
+ **/
+const gchar *
+e_source_autoconfig_get_revision (ESourceAutoconfig *extension)
+{
+ g_return_val_if_fail (E_IS_SOURCE_AUTOCONFIG (extension), NULL);
+
+ return extension->priv->revision;
+}
+
+/**
+ * e_source_autoconfig_dup_revision:
+ * @extension: an #ESourceAutoconfig
+ *
+ * Thread-safe variation of e_source_autoconfig_get_revision().
+ * Use this function when accessing @extension from multiple threads.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: (transfer full): a newly-allocated copy of #ESourceAutoconfig:revision
+ *
+ * Since: 3.24
+ **/
+gchar *
+e_source_autoconfig_dup_revision (ESourceAutoconfig *extension)
+{
+ const gchar *protected;
+ gchar *duplicate;
+
+ g_return_val_if_fail (E_IS_SOURCE_AUTOCONFIG (extension), NULL);
+
+ e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
+
+ protected = e_source_autoconfig_get_revision (extension);
+ duplicate = g_strdup (protected);
+
+ e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+
+ return duplicate;
+}
+
+/**
+ * e_source_autoconfig_set_revision:
+ * @extension: an #ESourceAutoconfig
+ * @revision: a revision
+ *
+ * Sets the revision used to map a particular version of a system-wide source
+ * to a user-specific source.
+ *
+ * If doesn't match, the system-wide source will be copied to the user-specific
+ * evolution config directory, preserving the already present fields that are
+ * not defined by the system-wide source.
+ *
+ * If it matches, no copying is done.
+ *
+ * The internal copy of @revision is automatically stripped of leading and
+ * trailing whitespace.
+ *
+ * Since: 3.24
+ **/
+void
+e_source_autoconfig_set_revision (ESourceAutoconfig *extension,
+ const gchar *revision)
+{
+ g_return_if_fail (E_IS_SOURCE_AUTOCONFIG (extension));
+
+ e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
+
+ if (g_strcmp0 (extension->priv->revision, revision) == 0) {
+ e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+ return;
+ }
+
+ g_free (extension->priv->revision);
+ extension->priv->revision = e_util_strdup_strip (revision);
+
+ e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+
+ g_object_notify (G_OBJECT (extension), "revision");
+}
diff --git a/src/libedataserver/e-source-autoconfig.h b/src/libedataserver/e-source-autoconfig.h
new file mode 100644
index 0000000..fdca48c
--- /dev/null
+++ b/src/libedataserver/e-source-autoconfig.h
@@ -0,0 +1,92 @@
+/*
+ * e-source-autoconfig.h
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_SOURCE_AUTOCONFIG_H
+#define E_SOURCE_AUTOCONFIG_H
+
+#include <libedataserver/e-source-extension.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_AUTOCONFIG \
+ (e_source_autoconfig_get_type ())
+#define E_SOURCE_AUTOCONFIG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SOURCE_AUTOCONFIG, ESourceAutoconfig))
+#define E_SOURCE_AUTOCONFIG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SOURCE_AUTOCONFIG, ESourceAutoconfigClass))
+#define E_IS_SOURCE_AUTOCONFIG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SOURCE_AUTOCONFIG))
+#define E_IS_SOURCE_AUTOCONFIG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SOURCE_AUTOCONFIG))
+#define E_SOURCE_AUTOCONFIG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SOURCE_AUTOCONFIG, ESourceAutoconfigClass))
+
+/**
+ * E_SOURCE_EXTENSION_AUTOCONFIG:
+ *
+ * Pass this extension name to e_source_get_extension() to access
+ * #ESourceAutoconfig. This is also used as a group name in key files.
+ *
+ * Since: 3.24
+ **/
+#define E_SOURCE_EXTENSION_AUTOCONFIG "Autoconfig"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceAutoconfig ESourceAutoconfig;
+typedef struct _ESourceAutoconfigClass ESourceAutoconfigClass;
+typedef struct _ESourceAutoconfigPrivate ESourceAutoconfigPrivate;
+
+/**
+ * ESourceAutoconfig:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.24
+ **/
+struct _ESourceAutoconfig {
+ /*< private >*/
+ ESourceExtension parent;
+ ESourceAutoconfigPrivate *priv;
+};
+
+struct _ESourceAutoconfigClass {
+ ESourceExtensionClass parent_class;
+};
+
+GType e_source_autoconfig_get_type
+ (void) G_GNUC_CONST;
+const gchar * e_source_autoconfig_get_revision
+ (ESourceAutoconfig *extension);
+gchar * e_source_autoconfig_dup_revision
+ (ESourceAutoconfig *extension);
+void e_source_autoconfig_set_revision
+ (ESourceAutoconfig *extension,
+ const gchar *revision);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_AUTOCONFIG_H */
diff --git a/src/libedataserver/e-source.c b/src/libedataserver/e-source.c
index 85ab910..51b2bfc 100644
--- a/src/libedataserver/e-source.c
+++ b/src/libedataserver/e-source.c
@@ -84,6 +84,7 @@
#include "e-source-alarms.h"
#include "e-source-authentication.h"
#include "e-source-autocomplete.h"
+#include "e-source-autoconfig.h"
#include "e-source-calendar.h"
#include "e-source-camel.h"
#include "e-source-collection.h"
@@ -2371,6 +2372,7 @@ e_source_class_init (ESourceClass *class)
g_type_ensure (E_TYPE_SOURCE_ALARMS);
g_type_ensure (E_TYPE_SOURCE_AUTHENTICATION);
g_type_ensure (E_TYPE_SOURCE_AUTOCOMPLETE);
+ g_type_ensure (E_TYPE_SOURCE_AUTOCONFIG);
g_type_ensure (E_TYPE_SOURCE_CALENDAR);
g_type_ensure (E_TYPE_SOURCE_COLLECTION);
g_type_ensure (E_TYPE_SOURCE_CONTACTS);
diff --git a/src/libedataserver/libedataserver.h b/src/libedataserver/libedataserver.h
index 42d4f14..47b6304 100644
--- a/src/libedataserver/libedataserver.h
+++ b/src/libedataserver/libedataserver.h
@@ -48,6 +48,7 @@
#include <libedataserver/e-source-alarms.h>
#include <libedataserver/e-source-authentication.h>
#include <libedataserver/e-source-autocomplete.h>
+#include <libedataserver/e-source-autoconfig.h>
#include <libedataserver/e-source-backend.h>
#include <libedataserver/e-source-calendar.h>
#include <libedataserver/e-source-camel.h>
diff --git a/src/services/evolution-source-registry/CMakeLists.txt
b/src/services/evolution-source-registry/CMakeLists.txt
index 03259b6..b5bffc1 100644
--- a/src/services/evolution-source-registry/CMakeLists.txt
+++ b/src/services/evolution-source-registry/CMakeLists.txt
@@ -60,6 +60,7 @@ set(DEPENDENCIES
set(SOURCES
evolution-source-registry.c
+ evolution-source-registry-autoconfig.c
evolution-source-registry-migrate-basedir.c
evolution-source-registry-migrate-proxies.c
evolution-source-registry-migrate-sources.c
diff --git a/src/services/evolution-source-registry/evolution-source-registry-autoconfig.c
b/src/services/evolution-source-registry/evolution-source-registry-autoconfig.c
new file mode 100644
index 0000000..de9461b
--- /dev/null
+++ b/src/services/evolution-source-registry/evolution-source-registry-autoconfig.c
@@ -0,0 +1,604 @@
+/*
+ * evolution-source-registry-autoconfig.c
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evolution-data-server-config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <glib/gstdio.h>
+
+#include <libebackend/libebackend.h>
+
+typedef struct _MergeSourceData {
+ gchar *path;
+ GKeyFile *key_file;
+} MergeSourceData;
+
+typedef struct _EnvVarMap {
+ const gchar *from;
+ const gchar *to;
+} EnvVarMap;
+
+typedef void (*MergeSourcePopulateHashtableFunc)(GHashTable *source,
+ GKeyFile *key_file,
+ const gchar *basename,
+ const gchar *filename);
+
+/* Forward Declarations */
+gboolean evolution_source_registry_merge_autoconfig_sources
+ (ESourceRegistryServer *server,
+ GError **error);
+
+static void
+evolution_source_registry_free_merge_source_data (gpointer mem)
+{
+ MergeSourceData *source_data = (MergeSourceData *) mem;
+
+ if (source_data == NULL)
+ return;
+
+ g_free (source_data->path);
+ g_key_file_unref (source_data->key_file);
+ g_free (source_data);
+}
+
+static void
+populate_hashtable_autoconfig (GHashTable *sources,
+ GKeyFile *key_file,
+ const gchar *basename,
+ const gchar *filename)
+{
+ MergeSourceData *data;
+ GError *local_error = NULL;
+ gchar *uid, *val;
+
+ val = g_key_file_get_value (
+ key_file,
+ E_SOURCE_EXTENSION_AUTOCONFIG,
+ "Revision",
+ &local_error);
+ if (val == NULL) {
+ e_source_registry_debug_print (
+ "Autoconfig: Failed to read '%s': %s.\n",
+ filename,
+ local_error->message);
+ g_error_free (local_error);
+ return;
+ }
+ g_free (val);
+
+ uid = g_strndup (basename, strlen (basename) - 7);
+
+ data = g_new0 (MergeSourceData, 1);
+ data->key_file = g_key_file_ref (key_file);
+ data->path = g_strdup (filename);
+
+ g_hash_table_insert (sources, uid, data);
+ e_source_registry_debug_print (
+ "Autoconfig: Found autoconfig source '%s'.\n",
+ filename);
+}
+
+static void
+populate_hashtable_home (GHashTable *sources,
+ GKeyFile *key_file,
+ const gchar *basename,
+ const gchar *filename)
+{
+ MergeSourceData *data;
+ gchar *uid;
+
+ if (!g_key_file_has_group (key_file, E_SOURCE_EXTENSION_AUTOCONFIG))
+ return;
+
+ uid = g_strndup (basename, strlen (basename) - 7);
+
+ data = g_new0 (MergeSourceData, 1);
+ data->key_file = g_key_file_ref (key_file);
+ data->path = g_strdup (filename);
+ g_hash_table_insert (sources, uid, data);
+}
+
+static gboolean
+evolution_source_registry_read_directory (const gchar *path,
+ GHashTable *sources,
+ MergeSourcePopulateHashtableFunc func,
+ GError **error)
+{
+ GDir *dir;
+ const gchar *basename;
+
+ dir = g_dir_open (path, 0, error);
+ if (dir == NULL) {
+ return FALSE;
+ }
+
+ while ((basename = g_dir_read_name (dir)) != NULL) {
+ GKeyFile *key_file;
+ gchar *filename;
+
+ if (!g_str_has_suffix (basename, ".source"))
+ continue;
+
+ filename = g_build_filename (path, basename, NULL);
+
+ key_file = g_key_file_new ();
+ if (!g_key_file_load_from_file (
+ key_file,
+ filename,
+ G_KEY_FILE_NONE,
+ error)) {
+ g_prefix_error (
+ error,
+ "Failed to load key file '%s': ",
+ filename);
+ g_free (filename);
+ g_dir_close (dir);
+ g_key_file_unref (key_file);
+ return FALSE;
+ }
+
+ func (sources, key_file, basename, filename);
+ g_free (filename);
+ g_key_file_unref (key_file);
+ }
+
+ g_dir_close (dir);
+
+ return TRUE;
+}
+
+static void
+evolution_source_registry_clean_orphans (GHashTable *autoconfig_sources,
+ GHashTable *home_sources)
+{
+ GList *keys;
+ GList *index;
+ keys = g_hash_table_get_keys (home_sources);
+
+ for (index = keys; index != NULL; index = g_list_next (index)) {
+ gchar *key = index->data;
+ if (!g_hash_table_contains (autoconfig_sources, key)) {
+ MergeSourceData *data = g_hash_table_lookup (home_sources, key);
+ if (data != NULL) {
+ /* if we fail to remove it, keep going */
+ if (g_unlink (data->path) == -1) {
+ e_source_registry_debug_print (
+ "Autoconfig: Error cleaning orphan source
'%s': %s.\n",
+ data->path,
+ g_strerror (errno));
+ }
+ e_source_registry_debug_print (
+ "Autoconfig: Removed orphan autoconfig source
'%s'.\n",
+ data->path);
+
+ g_hash_table_remove (home_sources, data->path);
+ }
+ }
+ }
+
+ g_list_free (keys);
+}
+
+static gboolean
+evolution_source_registry_replace_env_vars_eval_cb (const GMatchInfo *match_info,
+ GString *result,
+ gpointer user_data)
+{
+ gchar *var_name, *source_path;
+ const gchar *val;
+ gint ii;
+ EnvVarMap map[] = {
+ { "USER", g_get_user_name () },
+ { "REALNAME", g_get_real_name () },
+ { "HOST", g_get_host_name () }
+ };
+
+ var_name = g_match_info_fetch (match_info, 1);
+ source_path = user_data;
+
+ for (ii = 0; ii < G_N_ELEMENTS (map); ++ii) {
+ if (g_strcmp0 (var_name, map[ii].from) == 0) {
+ g_string_append (result, map[ii].to);
+ goto exit;
+ }
+ }
+
+ val = g_getenv (var_name);
+
+ if (val != NULL) {
+ g_string_append (result, val);
+ } else {
+ /* env var will be replaced by an empty string */
+ e_source_registry_debug_print (
+ "Autoconfig: Variable '${%s}' not found, used in '%s'.\n",
+ var_name,
+ source_path);
+ }
+
+ exit:
+ g_free (var_name);
+
+ return FALSE;
+}
+
+static gchar *
+evolution_source_registry_replace_env_vars (const gchar *old, MergeSourceData *source)
+{
+ GRegex *regex;
+ gchar *new;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (old != NULL, NULL);
+
+ regex = g_regex_new ("\\$\\{(\\w+)\\}", 0, 0, NULL);
+
+ g_return_val_if_fail (regex != NULL, g_strdup (old));
+
+ new = g_regex_replace_eval (
+ regex,
+ old,
+ -1,
+ 0,
+ 0,
+ evolution_source_registry_replace_env_vars_eval_cb,
+ source->path,
+ &local_error);
+
+ g_regex_unref (regex);
+
+ if (new == NULL) {
+ e_source_registry_debug_print (
+ "Autoconfig: Replacing variables failed: %s.\n",
+ local_error ? local_error->message : "Unknown error");;
+ g_error_free (local_error);
+ return g_strdup (old);
+ }
+
+ return new;
+}
+
+static void
+evolution_source_registry_copy_source (MergeSourceData *target,
+ MergeSourceData *source)
+{
+ gchar **groups = NULL;
+ gsize ngroups;
+ gint ii;
+
+ groups = g_key_file_get_groups (source->key_file, &ngroups);
+
+ for (ii = 0; ii < ngroups; ii++) {
+ gsize nkeys;
+ gint jj;
+ gchar **keys;
+
+ keys = g_key_file_get_keys (
+ source->key_file,
+ groups[ii],
+ &nkeys,
+ NULL);
+
+ for (jj = 0; jj < nkeys; jj++) {
+ gchar *new_val;
+ gchar *val = g_key_file_get_value (
+ source->key_file,
+ groups[ii],
+ keys[jj],
+ NULL);
+
+ new_val = evolution_source_registry_replace_env_vars (val, source);
+ g_free (val);
+
+ g_key_file_set_value (
+ target->key_file,
+ groups[ii],
+ keys[jj],
+ new_val);
+ g_free (new_val);
+ }
+ g_strfreev (keys);
+ }
+
+ g_strfreev (groups);
+}
+
+static gboolean
+evolution_source_registry_merge_source (GHashTable *home_sources,
+ const gchar *key,
+ MergeSourceData *autoconfig_key_file,
+ GList **key_files_to_copy,
+ GError **error)
+{
+ GKeyFile *new_keyfile;
+ MergeSourceData *home_key_file;
+ MergeSourceData *new_data;
+ gboolean skip_copy;
+ gchar *autoconfig_revision, *home_revision;
+
+ g_return_val_if_fail (key_files_to_copy != NULL, FALSE);
+
+ home_key_file = g_hash_table_lookup (home_sources, key);
+
+ autoconfig_revision = g_key_file_get_value (
+ autoconfig_key_file->key_file,
+ E_SOURCE_EXTENSION_AUTOCONFIG,
+ "Revision",
+ error);
+ if (autoconfig_revision == NULL) {
+ g_prefix_error (
+ error,
+ "Failed to get revision of key file '%s': ",
+ autoconfig_key_file->path);
+ return FALSE;
+ }
+
+ home_revision = g_key_file_get_value (
+ home_key_file->key_file,
+ E_SOURCE_EXTENSION_AUTOCONFIG,
+ "Revision",
+ error);
+ if (home_revision == NULL) {
+ g_prefix_error (
+ error,
+ "Failed to get revision of key file '%s': ",
+ home_key_file->path);
+ g_free (autoconfig_revision);
+ return FALSE;
+ }
+
+ skip_copy = g_strcmp0 (autoconfig_revision, home_revision) == 0;
+
+ if (skip_copy) {
+ e_source_registry_debug_print (
+ "Autoconfig: Revisions of '%s' and '%s' are the same ('%s'). Skipping
update.\n",
+ home_key_file->path,
+ autoconfig_key_file->path,
+ home_revision);
+ g_free (autoconfig_revision);
+ g_free (home_revision);
+ return TRUE;
+ }
+
+ e_source_registry_debug_print (
+ "Autoconfig: '%s' (Revision '%s') will be updated from '%s' (Revision
'%s').\n",
+ home_key_file->path,
+ home_revision,
+ autoconfig_key_file->path,
+ autoconfig_revision);
+
+ g_free (autoconfig_revision);
+ g_free (home_revision);
+
+ new_keyfile = g_key_file_new ();
+
+ if (!g_key_file_load_from_file (
+ new_keyfile,
+ home_key_file->path,
+ G_KEY_FILE_NONE,
+ error)) {
+ g_prefix_error (
+ error,
+ "Failed to load key file '%s': ",
+ home_key_file->path);
+ g_key_file_unref (new_keyfile);
+ return FALSE;
+ }
+
+ new_data = g_new0 (MergeSourceData, 1);
+ new_data->path = g_strdup (home_key_file->path);
+ new_data->key_file = new_keyfile;
+
+ evolution_source_registry_copy_source (new_data, autoconfig_key_file);
+
+ *key_files_to_copy = g_list_prepend (*key_files_to_copy, new_data);
+
+ return TRUE;
+}
+
+static void
+evolution_source_registry_generate_source_from_autoconfig (const gchar *key,
+ MergeSourceData *autoconfig_key_file,
+ GList **key_files_to_copy)
+{
+ GKeyFile *new_keyfile;
+ MergeSourceData *new_data;
+ gchar *dest_source_filename, *dest_source_path;
+
+ g_return_if_fail (key_files_to_copy != NULL);
+
+ dest_source_filename = g_strdup_printf ("%s.source", key);
+ dest_source_path = g_build_filename (
+ e_server_side_source_get_user_dir (),
+ dest_source_filename,
+ NULL);
+
+ g_free (dest_source_filename);
+
+ /* If we're here it means there was no file in home sources with an
+ * Autoconfig section that corresponds with autoconfig_key_file.
+ * In the unlikely event there's an existing file in home sources with
+ * the same name as autoconfig_key file, let's not include it in the
+ * list of files to copy to avoid data loss. */
+ if (g_file_test (dest_source_path, G_FILE_TEST_EXISTS)) {
+ e_source_registry_debug_print (
+ "Autoconfig: Skipping '%s' due to an existing '%s' without '%s' section.\n",
+ autoconfig_key_file->path, dest_source_path, E_SOURCE_EXTENSION_AUTOCONFIG);
+ g_free (dest_source_path);
+ return;
+ }
+
+ new_keyfile = g_key_file_new ();
+
+ new_data = g_new0 (MergeSourceData, 1);
+ new_data->path = dest_source_path;
+ new_data->key_file = new_keyfile;
+
+ evolution_source_registry_copy_source (new_data, autoconfig_key_file);
+
+ *key_files_to_copy = g_list_prepend (*key_files_to_copy, new_data);
+
+ e_source_registry_debug_print (
+ "Autoconfig: New autoconfig source '%s'. It will be copied to '%s'.\n",
+ autoconfig_key_file->path,
+ new_data->path);
+}
+
+static GList *
+evolution_source_registry_merge_sources (GHashTable *autoconfig_sources,
+ GHashTable *home_sources)
+{
+ GHashTableIter iter;
+ GList *key_files_to_copy = NULL;
+ gpointer key, value;
+
+ evolution_source_registry_clean_orphans (autoconfig_sources, home_sources);
+
+ g_hash_table_iter_init (&iter, autoconfig_sources);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MergeSourceData *autoconfig_key_file;
+
+ autoconfig_key_file = (MergeSourceData *)value;
+ if (g_hash_table_contains (home_sources, key)) {
+ GError *local_error = NULL;
+
+ if (!evolution_source_registry_merge_source (
+ home_sources,
+ key,
+ autoconfig_key_file,
+ &key_files_to_copy,
+ &local_error)) {
+ e_source_registry_debug_print (
+ "Autoconfig: evolution_source_registry_merge_source() failed:
%s.\n",
+ local_error ? local_error->message : "Unknown error");
+ g_clear_error (&local_error);
+ continue;
+ }
+ } else {
+ evolution_source_registry_generate_source_from_autoconfig (
+ key,
+ autoconfig_key_file,
+ &key_files_to_copy);
+ }
+ }
+
+ return key_files_to_copy;
+}
+
+static gboolean
+evolution_source_registry_write_key_file (MergeSourceData *key_file_data,
+ GError **error)
+{
+ return g_key_file_save_to_file (key_file_data->key_file, key_file_data->path, error);
+}
+
+static gboolean
+evolution_source_registry_write_key_files (GList *list,
+ GError **error)
+{
+ GList *index;
+
+ for (index = list; index != NULL; index = g_list_next (index)) {
+ MergeSourceData *data;
+
+ data = index->data;
+
+ if (data == NULL)
+ continue;
+
+ if (!evolution_source_registry_write_key_file (data, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+evolution_source_registry_merge_autoconfig_sources (ESourceRegistryServer *server,
+ GError **error)
+{
+ GHashTable *home_sources = NULL, *autoconfig_sources = NULL;
+ GList *key_files_to_copy = NULL;
+ GError *local_error = NULL;
+ gboolean success = FALSE;
+ const gchar * const *config_dirs;
+ gint ii;
+
+ autoconfig_sources = g_hash_table_new_full (
+ g_str_hash,
+ g_str_equal,
+ g_free,
+ evolution_source_registry_free_merge_source_data);
+
+ config_dirs = g_get_system_config_dirs ();
+ for (ii = 0; config_dirs[ii]; ii++) {
+ gchar *path = g_build_filename (
+ config_dirs[ii],
+ "evolution-data-server",
+ "autoconfig",
+ NULL);
+ success = evolution_source_registry_read_directory (
+ path,
+ autoconfig_sources,
+ populate_hashtable_autoconfig,
+ &local_error);
+
+ g_free (path);
+
+ if (!success) {
+ if (local_error != NULL &&
+ g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
+ g_clear_error (&local_error);
+ continue;
+ }
+
+ goto exit;
+ }
+ }
+
+ home_sources = g_hash_table_new_full (
+ g_str_hash,
+ g_str_equal,
+ g_free,
+ evolution_source_registry_free_merge_source_data);
+
+ success = evolution_source_registry_read_directory (
+ e_server_side_source_get_user_dir (),
+ home_sources,
+ populate_hashtable_home,
+ &local_error);
+
+ if (!success)
+ goto exit;
+
+ key_files_to_copy = evolution_source_registry_merge_sources (autoconfig_sources, home_sources);
+
+ success = evolution_source_registry_write_key_files (key_files_to_copy, error);
+
+ exit:
+ if (autoconfig_sources != NULL)
+ g_hash_table_unref (autoconfig_sources);
+ if (home_sources != NULL)
+ g_hash_table_unref (home_sources);
+ if (key_files_to_copy != NULL)
+ g_list_free_full (key_files_to_copy, evolution_source_registry_free_merge_source_data);
+
+ if (local_error != NULL)
+ g_propagate_error (error, local_error);
+
+ return success;
+}
diff --git a/src/services/evolution-source-registry/evolution-source-registry.c
b/src/services/evolution-source-registry/evolution-source-registry.c
index cfd021e..dbdb234 100644
--- a/src/services/evolution-source-registry/evolution-source-registry.c
+++ b/src/services/evolution-source-registry/evolution-source-registry.c
@@ -51,6 +51,9 @@ gboolean evolution_source_registry_migrate_imap_to_imapx
const gchar *uid);
void evolution_source_registry_migrate_proxies
(ESourceRegistryServer *server);
+gboolean evolution_source_registry_merge_autoconfig_sources
+ (ESourceRegistryServer *server,
+ GError **error);
static void
evolution_source_registry_load_error (ESourceRegistryServer *server,
@@ -129,6 +132,14 @@ evolution_source_registry_load_sources (ESourceRegistryServer *server,
{
GError *error = NULL;
+ if (!evolution_source_registry_merge_autoconfig_sources (server, &error)) {
+ e_source_registry_debug_print (
+ "Autoconfig: evolution_source_registry_merge_autoconfig_sources() failed: %s",
+ error ? error->message : "Unknown error");
+ }
+
+ g_clear_error (&error);
+
/* Failure here is fatal. Don't even try to keep going. */
evolution_source_registry_load_all (server, &error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]