[evolution-data-server/account-mgmt: 33/39] Migrate addressbook XML data to key files.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/account-mgmt: 33/39] Migrate addressbook XML data to key files.
- Date: Thu, 27 Oct 2011 02:51:36 +0000 (UTC)
commit edb77d554f08214f2d51dc3c4d3b542cd0a6330c
Author: Matthew Barnes <mbarnes redhat com>
Date: Fri Mar 4 14:06:01 2011 -0500
Migrate addressbook XML data to key files.
The migration code reads raw %gconf.xml files, generates equivalent key
files from the XML data, and renames data and cache directories from the
source's URI to its UID.
services/evolution-addressbook-factory/Makefile.am | 5 +
...evolution-addressbook-factory-migrate-sources.c | 1093 ++++++++++++++++++++
.../evolution-addressbook-factory.c | 6 +
3 files changed, 1104 insertions(+), 0 deletions(-)
---
diff --git a/services/evolution-addressbook-factory/Makefile.am b/services/evolution-addressbook-factory/Makefile.am
index ff2416f..6f94962 100644
--- a/services/evolution-addressbook-factory/Makefile.am
+++ b/services/evolution-addressbook-factory/Makefile.am
@@ -19,13 +19,16 @@ evolution_addressbook_factory_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_builddir)/addressbook \
$(EVOLUTION_ADDRESSBOOK_CFLAGS) \
+ $(GNOME_KEYRING_CFLAGS) \
$(FACTORY_GTK_CFLAGS) \
+ $(SOUP_CFLAGS) \
$(GOA_CFLAGS) \
$(NULL)
evolution_addressbook_factory_SOURCES = \
evolution-addressbook-factory.c \
evolution-addressbook-factory-migrate-basedir.c \
+ evolution-addressbook-factory-migrate-sources.c \
$(NULL)
evolution_addressbook_factory_LDADD = \
@@ -33,7 +36,9 @@ evolution_addressbook_factory_LDADD = \
$(top_builddir)/libebackend/libebackend-1.2.la \
$(top_builddir)/libedataserver/libedataserver-1.2.la \
$(EVOLUTION_ADDRESSBOOK_LIBS) \
+ $(GNOME_KEYRING_LIBS) \
$(FACTORY_GTK_LIBS) \
+ $(SOUP_LIBS) \
$(GOA_LIBS) \
$(NULL)
diff --git a/services/evolution-addressbook-factory/evolution-addressbook-factory-migrate-sources.c b/services/evolution-addressbook-factory/evolution-addressbook-factory-migrate-sources.c
new file mode 100644
index 0000000..3fa129b
--- /dev/null
+++ b/services/evolution-addressbook-factory/evolution-addressbook-factory-migrate-sources.c
@@ -0,0 +1,1093 @@
+/*
+ * e-data-book-migrate-sources.c
+ *
+ * This program 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <glib/gstdio.h>
+#include <libsoup/soup.h>
+#include <gnome-keyring.h>
+
+#include <libebook/e-source-address-book.h>
+#include <libedataserver/e-data-server-util.h>
+
+/* These constants are collected from various e-source-*.h files
+ * throughout evolution-data-server and known extension packages. */
+#define E_SOURCE_GROUP_NAME "Data Source"
+#define E_SOURCE_EXTENSION_ADDRESS_BOOK "Address Book"
+#define E_SOURCE_EXTENSION_AUTHENTICATION "Authentication"
+#define E_SOURCE_EXTENSION_AUTOCOMPLETE "Autocomplete"
+#define E_SOURCE_EXTENSION_OFFLINE "Offline"
+#define E_SOURCE_EXTENSION_REFRESH "Refresh"
+#define E_SOURCE_EXTENSION_SECURITY "Security"
+#define E_SOURCE_EXTENSION_CONTACTS_BACKEND "Contacts Backend"
+#define E_SOURCE_EXTENSION_LDAP_BACKEND "LDAP Backend"
+#define E_SOURCE_EXTENSION_VCF_BACKEND "VCF Backend"
+#define E_SOURCE_EXTENSION_WEBDAV_BACKEND "WebDAV Backend"
+
+/* These constants are copied from e-source-password.c. */
+#define KEYRING_ITEM_ATTRIBUTE_NAME "e-source-uid"
+#define KEYRING_ITEM_DISPLAY_FORMAT "Evolution Data Source %s"
+
+typedef struct _ParseData ParseData;
+
+typedef void (*PropertyFunc) (ParseData *parse_data,
+ const gchar *property_name,
+ const gchar *property_value);
+
+typedef enum {
+ PARSE_STATE_INITIAL,
+
+ PARSE_STATE_IN_GCONF, /* GConf XML */
+ PARSE_STATE_IN_SOURCES_ENTRY, /* GConf XML */
+ PARSE_STATE_IN_SOURCES_VALUE, /* GConf XML */
+
+ PARSE_STATE_IN_GROUP, /* ESource XML */
+ PARSE_STATE_IN_SOURCE, /* ESource XML */
+ PARSE_STATE_IN_PROPERTIES /* ESource XML */
+} ParseState;
+
+struct _ParseData {
+ ParseState state;
+
+ /* Set by <group> tags. */
+ gchar *base_uri;
+ gboolean writable_hint;
+
+ /* Set by <source> tags. */
+ gchar *filename;
+ gchar *mangled_uri;
+ GKeyFile *key_file;
+ SoupURI *soup_uri;
+ PropertyFunc property_func;
+};
+
+static GnomeKeyringPasswordSchema schema = {
+ GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ {
+ { KEYRING_ITEM_ATTRIBUTE_NAME,
+ GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { NULL, 0 }
+ }
+};
+
+/* Forward Declarations */
+void evolution_addressbook_factory_migrate_sources (void);
+
+static ParseData *
+parse_data_new (void)
+{
+ ParseData *parse_data;
+
+ parse_data = g_slice_new0 (ParseData);
+ parse_data->state = PARSE_STATE_INITIAL;
+
+ return parse_data;
+}
+
+static void
+parse_data_free (ParseData *parse_data)
+{
+ /* Normally the allocated data in ParseData is freed and the
+ * pointers are cleared before we get here. But if an error
+ * occurred we may leave data behind. This cleans it up. */
+
+ g_free (parse_data->base_uri);
+ g_free (parse_data->filename);
+ g_free (parse_data->mangled_uri);
+
+ if (parse_data->key_file != NULL)
+ g_key_file_free (parse_data->key_file);
+
+ if (parse_data->soup_uri != NULL)
+ soup_uri_free (parse_data->soup_uri);
+
+ g_slice_free (ParseData, parse_data);
+}
+
+static gboolean
+is_true (const gchar *string)
+{
+ return (g_ascii_strcasecmp (string, "1") == 0) ||
+ (g_ascii_strcasecmp (string, "true") == 0);
+}
+
+static void
+migrate_keyring_entry (ParseData *parse_data)
+{
+ GnomeKeyringAttributeList *attributes;
+ GList *found_list = NULL;
+ gchar *display_name;
+ gchar *uid;
+
+ /* This is a best-effort routine, so we don't really care about
+ * errors. We leave the old keyring entry in place since it may
+ * be reused for calendar migration. */
+
+ uid = g_path_get_basename (parse_data->filename);
+ display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
+
+ attributes = gnome_keyring_attribute_list_new ();
+
+ gnome_keyring_attribute_list_append_string (
+ attributes, "application", "Evolution");
+ if (parse_data->soup_uri->user != NULL)
+ gnome_keyring_attribute_list_append_string (
+ attributes, "user", parse_data->soup_uri->user);
+ if (parse_data->soup_uri->host != NULL)
+ gnome_keyring_attribute_list_append_string (
+ attributes, "server", parse_data->soup_uri->host);
+ if (parse_data->soup_uri->scheme != NULL)
+ gnome_keyring_attribute_list_append_string (
+ attributes, "protocol", parse_data->soup_uri->scheme);
+
+ gnome_keyring_find_items_sync (
+ GNOME_KEYRING_ITEM_NETWORK_PASSWORD, attributes, &found_list);
+
+ /* Pick the first match we find. */
+ if (found_list != NULL) {
+ GnomeKeyringFound *found = found_list->data;
+
+ /* Sanity check. */
+ g_return_if_fail (found->secret != NULL);
+
+ gnome_keyring_store_password_sync (
+ &schema, GNOME_KEYRING_DEFAULT, display_name,
+ found->secret, KEYRING_ITEM_ATTRIBUTE_NAME, uid, NULL);
+ }
+
+ gnome_keyring_attribute_list_free (attributes);
+ gnome_keyring_found_list_free (found_list);
+
+ g_free (display_name);
+ g_free (uid);
+}
+
+static gboolean
+migrate_parse_commit_changes (ParseData *parse_data,
+ GError **error)
+{
+ const gchar *data_dir;
+ const gchar *cache_dir;
+ gchar *old_directory;
+ gchar *new_directory;
+ gchar *contents;
+ gchar *uid;
+ gsize length;
+ gboolean success;
+ gboolean old_directory_exists;
+ gboolean new_directory_exists;
+
+ data_dir = e_get_user_data_dir ();
+ cache_dir = e_get_user_cache_dir ();
+
+ uid = g_path_get_basename (parse_data->filename);
+
+ g_print (" * Source: %s\n", uid);
+
+ g_print (" Writing key file...\n");
+
+ /* Save the key file contents to disk. */
+ contents = g_key_file_to_data (
+ parse_data->key_file, &length, NULL);
+ success = g_file_set_contents (
+ parse_data->filename, contents, length, error);
+ g_free (contents);
+
+ if (!success)
+ goto exit;
+
+ /* Rename the source's cache directory from its mangled URI
+ * to its UID. The key file's basename is also the UID. All
+ * source types but "local" should have cache directories. */
+
+ old_directory = g_build_filename (
+ cache_dir, "addressbook", parse_data->mangled_uri, NULL);
+
+ new_directory = g_build_filename (
+ cache_dir, "addressbook", uid, NULL);
+
+ old_directory_exists = g_file_test (old_directory, G_FILE_TEST_EXISTS);
+ new_directory_exists = g_file_test (new_directory, G_FILE_TEST_EXISTS);
+
+ g_print (
+ " Checking for old cache dir '%s'... %s\n",
+ old_directory,
+ old_directory_exists ? "found" : "not found");
+
+ if (old_directory_exists) {
+ g_print (
+ " Checking for new cache dir '%s'... %s\n",
+ new_directory,
+ new_directory_exists ? "found" : "not found");
+
+ if (new_directory_exists)
+ g_print (" Skipping cache directory rename.\n");
+ else {
+ g_print (" Renaming old cache directory...\n");
+ if (g_rename (old_directory, new_directory) < 0) {
+ g_set_error (
+ error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "%s", g_strerror (errno));
+ success = FALSE;
+ }
+ }
+ }
+
+ g_free (old_directory);
+ g_free (new_directory);
+
+ if (!success)
+ goto exit;
+
+ /* Rename the source's local data directory from its mangled
+ * URI to its UID. The key file's basename is also the UID.
+ * Only "local" sources have local data directores. */
+
+ old_directory = g_build_filename (
+ data_dir, "addressbook", parse_data->mangled_uri, NULL);
+
+ new_directory = g_build_filename (
+ data_dir, "addressbook", uid, NULL);
+
+ old_directory_exists = g_file_test (old_directory, G_FILE_TEST_EXISTS);
+ new_directory_exists = g_file_test (new_directory, G_FILE_TEST_EXISTS);
+
+ g_print (
+ " Checking for old data dir '%s'... %s\n",
+ old_directory,
+ old_directory_exists ? "found" : "not found");
+
+ if (old_directory_exists) {
+ g_print (
+ " Checking for new data dir '%s'... %s\n",
+ new_directory,
+ new_directory_exists ? "found" : "not found");
+
+ if (new_directory_exists)
+ g_print (" Skipping data directory rename.\n");
+ else {
+ g_print (" Renaming old data directory...\n");
+ if (g_rename (old_directory, new_directory) < 0) {
+ g_set_error (
+ error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "%s", g_strerror (errno));
+ success = FALSE;
+ }
+ }
+ }
+
+ g_free (old_directory);
+ g_free (new_directory);
+
+exit:
+ g_free (uid);
+
+ return success;
+}
+
+static void
+migrate_parse_local_source (ParseData *parse_data)
+{
+ /* Local Backend has no special properties to parse. */
+}
+
+static void
+migrate_parse_google_property (ParseData *parse_data,
+ const gchar *property_name,
+ const gchar *property_value)
+{
+ if (g_strcmp0 (property_name, "refresh-interval") == 0) {
+ guint64 interval_seconds;
+
+ interval_seconds =
+ g_ascii_strtoull (property_value, NULL, 10);
+
+ if (interval_seconds >= 60)
+ g_key_file_set_uint64 (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_REFRESH,
+ "IntervalMinutes",
+ interval_seconds / 60);
+
+ } else if (g_strcmp0 (property_name, "ssl") == 0) {
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "Method",
+ is_true (property_value) ?
+ "tls" : "none");
+
+ } else if (g_strcmp0 (property_name, "username") == 0) {
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "User", property_value);
+ }
+}
+
+static void
+migrate_parse_google_source (ParseData *parse_data)
+{
+ parse_data->property_func = migrate_parse_google_property;
+}
+
+static void
+migrate_parse_ldap_property (ParseData *parse_data,
+ const gchar *property_name,
+ const gchar *property_value)
+{
+ if (g_strcmp0 (property_name, "can-browse") == 0) {
+ g_key_file_set_boolean (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_LDAP_BACKEND,
+ "CanBrowse",
+ is_true (property_value));
+
+ /* This is an integer value, but we can use the string as is. */
+ } else if (g_strcmp0 (property_name, "limit") == 0) {
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_LDAP_BACKEND,
+ "Limit", property_value);
+
+ } else if (g_strcmp0 (property_name, "ssl") == 0) {
+ if (g_strcmp0 (property_value, "always") == 0)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_SECURITY,
+ "Method", "starttls");
+ else if (g_strcmp0 (property_value, "whenever_possible") == 0)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_SECURITY,
+ "Method", "ldaps");
+ }
+}
+
+static void
+migrate_parse_ldap_source (ParseData *parse_data)
+{
+ if (parse_data->soup_uri->host != NULL)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "Host", parse_data->soup_uri->host);
+
+ if (parse_data->soup_uri->port != 0)
+ g_key_file_set_integer (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "Port", parse_data->soup_uri->port);
+
+ if (parse_data->soup_uri->user != NULL)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "User", parse_data->soup_uri->user);
+
+ /* Skip the leading slash on the URI path to get the RootDn. */
+ if (parse_data->soup_uri->path != NULL)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_LDAP_BACKEND,
+ "RootDn", parse_data->soup_uri->path + 1);
+
+ if (g_strcmp0 (parse_data->soup_uri->query, "?sub?") == 0)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_LDAP_BACKEND,
+ "Scope", "subtree");
+
+ if (g_strcmp0 (parse_data->soup_uri->query, "?one?") == 0)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_LDAP_BACKEND,
+ "Scope", "onelevel");
+
+ parse_data->property_func = migrate_parse_ldap_property;
+}
+
+static void
+migrate_parse_vcf_source (ParseData *parse_data)
+{
+ if (parse_data->soup_uri->path != NULL)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_VCF_BACKEND,
+ "Path", parse_data->soup_uri->path);
+
+ /* VCF Backend has no special properties to parse. */
+}
+
+static void
+migrate_parse_webdav_property (ParseData *parse_data,
+ const gchar *property_name,
+ const gchar *property_value)
+{
+ if (g_strcmp0 (property_name, "avoid_ifmatch") == 0) {
+ g_key_file_set_boolean (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_WEBDAV_BACKEND,
+ "AvoidIfmatch",
+ is_true (property_value));
+ }
+}
+
+static void
+migrate_parse_webdav_source (ParseData *parse_data)
+{
+ if (parse_data->soup_uri->host != NULL)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "Host", parse_data->soup_uri->host);
+
+ if (parse_data->soup_uri->port != 0)
+ g_key_file_set_integer (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "Port", parse_data->soup_uri->port);
+
+ if (parse_data->soup_uri->user != NULL)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "User", parse_data->soup_uri->user);
+
+ if (parse_data->soup_uri->path != NULL)
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_WEBDAV_BACKEND,
+ "Path", parse_data->soup_uri->path);
+
+ parse_data->property_func = migrate_parse_webdav_property;
+}
+
+static void
+migrate_parse_group (ParseData *parse_data,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ const gchar *base_uri;
+ gboolean readonly;
+ gboolean success;
+
+ success = g_markup_collect_attributes (
+ element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING,
+ "uid", NULL,
+ G_MARKUP_COLLECT_STRING,
+ "name", NULL,
+ G_MARKUP_COLLECT_STRING,
+ "base_uri", &base_uri,
+ G_MARKUP_COLLECT_BOOLEAN,
+ "readonly", &readonly,
+ G_MARKUP_COLLECT_INVALID);
+
+ if (!success)
+ return;
+
+ /* Convert "file://" schemes to "local:". */
+ if (g_strcmp0 (base_uri, "file://") == 0)
+ base_uri = "local:";
+
+ parse_data->base_uri = g_strdup (base_uri);
+ parse_data->writable_hint = !readonly;
+}
+
+static void
+migrate_parse_source (ParseData *parse_data,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ const gchar *uid;
+ const gchar *name;
+ const gchar *config_dir;
+ const gchar *absolute_uri;
+ const gchar *relative_uri;
+ gchar *directory;
+ gchar *parent, *cp;
+ gchar *uri_string;
+ gboolean success;
+
+ success = g_markup_collect_attributes (
+ element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING,
+ "uid", &uid,
+ G_MARKUP_COLLECT_STRING,
+ "name", &name,
+ G_MARKUP_COLLECT_STRING,
+ "relative_uri", &relative_uri,
+ G_MARKUP_COLLECT_STRING |
+ G_MARKUP_COLLECT_OPTIONAL,
+ "uri", &absolute_uri,
+ G_MARKUP_COLLECT_INVALID);
+
+ if (!success)
+ return;
+
+ /* Don't try and migrate the "system" source, we'll reset to
+ * the built-in key file. The "system" source is a special case
+ * that combines address book, calendar, task list and memo list
+ * groups into one file, so it breaks the migration logic here.
+ * Means the user may lose a custom color. Not a big deal. */
+ if (g_strcmp0 (relative_uri, "system") == 0)
+ return;
+
+ config_dir = e_get_user_config_dir ();
+ directory = g_build_filename (config_dir, "sources", NULL);
+ g_mkdir_with_parents (directory, 0700);
+
+ parse_data->filename = g_build_filename (directory, uid, NULL);
+
+ g_free (directory);
+
+ /* If the file already exists, skip this source. It may be that we
+ * already migrated it, in which case we don't want to overwrite it. */
+ if (g_file_test (parse_data->filename, G_FILE_TEST_EXISTS))
+ return;
+
+ parse_data->key_file = g_key_file_new ();
+
+ /* Trim ':' or '://' off the base_uri to get the parent name. */
+ parent = g_strdup (parse_data->base_uri);
+ if ((cp = strchr (parent, ':')) != NULL)
+ *cp = '\0';
+
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_GROUP_NAME,
+ "DisplayName", name);
+
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_GROUP_NAME,
+ "Parent", parent);
+
+ g_key_file_set_boolean (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_ADDRESS_BOOK,
+ "WritableHint", parse_data->writable_hint);
+
+ g_free (parent);
+
+ /* Prefer absolute URIs over relative URIs. All these
+ * other strange rules are for backward-compatibility. */
+ if (absolute_uri != NULL)
+ uri_string = g_strdup (absolute_uri);
+ else if (g_str_has_suffix (parse_data->base_uri, "/"))
+ uri_string = g_strconcat (
+ parse_data->base_uri, relative_uri, NULL);
+ else if (g_strcmp0 (parse_data->base_uri, "local:") == 0)
+ uri_string = g_strconcat (
+ parse_data->base_uri, relative_uri, NULL);
+ else
+ uri_string = g_strconcat (
+ parse_data->base_uri, "/", relative_uri, NULL);
+
+ parse_data->soup_uri = soup_uri_new (uri_string);
+
+ /* Mangle the URI to not contain invalid characters. We'll need
+ * this later to rename the source's cache and data directories. */
+ parse_data->mangled_uri = g_strdelimit (uri_string, ":/", '_');
+
+ /* g_strdelimit() modifies the input string in place, so ParseData
+ * now owns 'uri_string'. Clear the pointer to emphasize that. */
+ uri_string = NULL;
+
+ if (parse_data->soup_uri == NULL) {
+ g_warning (
+ " Failed to parse source URI: %s",
+ (absolute_uri != NULL) ? absolute_uri : relative_uri);
+ g_key_file_free (parse_data->key_file);
+ parse_data->key_file = NULL;
+ return;
+ }
+
+ if (g_strcmp0 (parse_data->base_uri, "local:") == 0)
+ migrate_parse_local_source (parse_data);
+
+ else if (g_strcmp0 (parse_data->base_uri, "google://") == 0)
+ migrate_parse_google_source (parse_data);
+
+ else if (g_strcmp0 (parse_data->base_uri, "ldap://") == 0)
+ migrate_parse_ldap_source (parse_data);
+
+ else if (g_strcmp0 (parse_data->base_uri, "vcf://") == 0)
+ migrate_parse_vcf_source (parse_data);
+
+ else if (g_strcmp0 (parse_data->base_uri, "webdav://") == 0)
+ migrate_parse_webdav_source (parse_data);
+
+ migrate_keyring_entry (parse_data);
+}
+
+static void
+migrate_parse_property (ParseData *parse_data,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ const gchar *property_name;
+ const gchar *property_value;
+ gboolean success;
+
+ success = g_markup_collect_attributes (
+ element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING,
+ "name", &property_name,
+ G_MARKUP_COLLECT_STRING,
+ "value", &property_value,
+ G_MARKUP_COLLECT_INVALID);
+
+ if (!success)
+ return;
+
+ if (g_strcmp0 (property_name, "auth") == 0) {
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "Method", property_value);
+
+ } else if (g_strcmp0 (property_name, "completion") == 0) {
+ g_key_file_set_boolean (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTOCOMPLETE,
+ "IncludeMe",
+ is_true (property_value));
+
+ } else if (g_strcmp0 (property_name, "offline_sync") == 0) {
+ g_key_file_set_boolean (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_OFFLINE,
+ "StaySynchronized",
+ is_true (property_value));
+
+ } else if (g_strcmp0 (property_name, "remember_password") == 0) {
+ g_key_file_set_boolean (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_AUTHENTICATION,
+ "RememberPassword",
+ is_true (property_value));
+
+ } else if (g_strcmp0 (property_name, "use_ssl") == 0) {
+ g_key_file_set_string (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_SECURITY,
+ "Method",
+ is_true (property_value) ?
+ "tls" : "none");
+
+ } else if (g_strcmp0 (property_name, "use-in-contacts-calendar") == 0) {
+ g_key_file_set_boolean (
+ parse_data->key_file,
+ E_SOURCE_EXTENSION_CONTACTS_BACKEND,
+ "IncludeMe",
+ is_true (property_value));
+
+ } else if (parse_data->property_func != NULL) {
+ parse_data->property_func (
+ parse_data, property_name, property_value);
+ }
+}
+
+static void
+migrate_parse_source_xml_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = user_data;
+
+ if (g_strcmp0 (element_name, "group") == 0) {
+ if (parse_data->state != PARSE_STATE_IN_SOURCES_VALUE)
+ goto invalid_content;
+
+ parse_data->state = PARSE_STATE_IN_GROUP;
+
+ migrate_parse_group (
+ parse_data,
+ element_name,
+ attribute_names,
+ attribute_values,
+ error);
+
+ return;
+ }
+
+ if (g_strcmp0 (element_name, "source") == 0) {
+ if (parse_data->state != PARSE_STATE_IN_GROUP)
+ goto invalid_content;
+
+ parse_data->state = PARSE_STATE_IN_SOURCE;
+
+ migrate_parse_source (
+ parse_data,
+ element_name,
+ attribute_names,
+ attribute_values,
+ error);
+
+ return;
+ }
+
+ if (g_strcmp0 (element_name, "properties") == 0) {
+ /* Disregard group properties, we're only
+ * interested in source properties. */
+ if (parse_data->state == PARSE_STATE_IN_GROUP)
+ return;
+
+ if (parse_data->state != PARSE_STATE_IN_SOURCE)
+ goto invalid_content;
+
+ parse_data->state = PARSE_STATE_IN_PROPERTIES;
+
+ return;
+ }
+
+ if (g_strcmp0 (element_name, "property") == 0) {
+ /* Disregard group properties, we're only
+ * interested in source properties. */
+ if (parse_data->state == PARSE_STATE_IN_GROUP)
+ return;
+
+ if (parse_data->state != PARSE_STATE_IN_PROPERTIES)
+ goto invalid_content;
+
+ /* The key file will be NULL if we decided to skip it.
+ * e.g. A file with the same UID may already exist. */
+ if (parse_data->key_file != NULL)
+ migrate_parse_property (
+ parse_data,
+ element_name,
+ attribute_names,
+ attribute_values,
+ error);
+
+ return;
+ }
+
+ g_set_error (
+ error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s>", element_name);
+
+ return;
+
+invalid_content:
+
+ g_set_error (
+ error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Element <%s> at unexpected location", element_name);
+}
+
+static void
+migrate_parse_source_xml_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = user_data;
+
+ if (g_strcmp0 (element_name, "group") == 0) {
+ if (parse_data->state == PARSE_STATE_IN_GROUP) {
+ parse_data->state = PARSE_STATE_IN_SOURCES_VALUE;
+
+ /* Clean up <group> tag data. */
+
+ g_free (parse_data->base_uri);
+ parse_data->base_uri = NULL;
+
+ return;
+ }
+ }
+
+ if (g_strcmp0 (element_name, "source") == 0) {
+ if (parse_data->state == PARSE_STATE_IN_SOURCE) {
+ parse_data->state = PARSE_STATE_IN_GROUP;
+
+ /* Clean up <source> tag data. */
+
+ /* The key file will be NULL if we decided to skip it.
+ * e.g. A file with the same UID may already exist. */
+ if (parse_data->key_file != NULL) {
+ GError *local_error = NULL;
+
+ migrate_parse_commit_changes (
+ parse_data, &local_error);
+
+ if (local_error != NULL) {
+ g_printerr (
+ " FAILED: %s\n",
+ local_error->message);
+ g_error_free (local_error);
+ }
+
+ g_key_file_free (parse_data->key_file);
+ parse_data->key_file = NULL;
+ }
+
+ g_free (parse_data->filename);
+ parse_data->filename = NULL;
+
+ g_free (parse_data->mangled_uri);
+ parse_data->mangled_uri = NULL;
+
+ if (parse_data->soup_uri != NULL) {
+ soup_uri_free (parse_data->soup_uri);
+ parse_data->soup_uri = NULL;
+ }
+
+ parse_data->property_func = NULL;
+
+ return;
+ }
+ }
+
+ if (g_strcmp0 (element_name, "properties") == 0) {
+ if (parse_data->state == PARSE_STATE_IN_PROPERTIES) {
+ parse_data->state = PARSE_STATE_IN_SOURCE;
+ return;
+ }
+ }
+}
+
+static GMarkupParser source_xml_parser = {
+ migrate_parse_source_xml_start_element,
+ migrate_parse_source_xml_end_element,
+ NULL, /* text */
+ NULL, /* passthrough */
+ NULL /* error */
+};
+
+static void
+migrate_parse_gconf_xml_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = user_data;
+
+ if (g_strcmp0 (element_name, "gconf") == 0) {
+ if (parse_data->state != PARSE_STATE_INITIAL)
+ goto invalid_content;
+
+ parse_data->state = PARSE_STATE_IN_GCONF;
+
+ return;
+ }
+
+ if (g_strcmp0 (element_name, "entry") == 0) {
+ const gchar *name;
+ gboolean success;
+
+ if (parse_data->state != PARSE_STATE_IN_GCONF)
+ goto invalid_content;
+
+ success = g_markup_collect_attributes (
+ element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING,
+ "name", &name,
+ G_MARKUP_COLLECT_STRING,
+ "mtime", NULL,
+ G_MARKUP_COLLECT_STRING |
+ G_MARKUP_COLLECT_OPTIONAL,
+ "type", NULL,
+ G_MARKUP_COLLECT_STRING |
+ G_MARKUP_COLLECT_OPTIONAL,
+ "ltype", NULL,
+ G_MARKUP_COLLECT_STRING |
+ G_MARKUP_COLLECT_OPTIONAL,
+ "schema", NULL,
+ G_MARKUP_COLLECT_INVALID);
+
+ if (success && g_strcmp0 (name, "sources") == 0)
+ parse_data->state = PARSE_STATE_IN_SOURCES_ENTRY;
+
+ return;
+ }
+
+ if (g_strcmp0 (element_name, "li") == 0) {
+ if (parse_data->state != PARSE_STATE_IN_SOURCES_ENTRY)
+ goto invalid_content;
+
+ return;
+ }
+
+ if (g_strcmp0 (element_name, "stringvalue") == 0) {
+ if (parse_data->state != PARSE_STATE_IN_SOURCES_ENTRY)
+ goto invalid_content;
+
+ parse_data->state = PARSE_STATE_IN_SOURCES_VALUE;
+
+ return;
+ }
+
+ g_set_error (
+ error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element <%s>", element_name);
+
+ return;
+
+invalid_content:
+
+ g_set_error (
+ error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Element <%s> at unexpected location", element_name);
+}
+
+static void
+migrate_parse_gconf_xml_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = user_data;
+
+ if (g_strcmp0 (element_name, "gconf") == 0) {
+ if (parse_data->state == PARSE_STATE_IN_GCONF) {
+ parse_data->state = PARSE_STATE_INITIAL;
+ return;
+ }
+ }
+
+ if (g_strcmp0 (element_name, "entry") == 0) {
+ if (parse_data->state == PARSE_STATE_IN_SOURCES_ENTRY) {
+ parse_data->state = PARSE_STATE_IN_GCONF;
+ return;
+ }
+ }
+
+ if (g_strcmp0 (element_name, "stringvalue") == 0) {
+ if (parse_data->state == PARSE_STATE_IN_SOURCES_VALUE) {
+ parse_data->state = PARSE_STATE_IN_SOURCES_ENTRY;
+ return;
+ }
+ }
+}
+
+static void
+migrate_parse_gconf_xml_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize length,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = user_data;
+
+ if (parse_data->state != PARSE_STATE_IN_SOURCES_VALUE)
+ return;
+
+ /* The source data is encoded XML stuffed into GConf XML (yuck!).
+ * Fortunately GMarkupParseContext decodes the XML for us, so we
+ * just have to feed it to a nested GMarkupParseContext. */
+
+ context = g_markup_parse_context_new (
+ &source_xml_parser, 0, parse_data, NULL);
+
+ if (g_markup_parse_context_parse (context, text, length, error))
+ g_markup_parse_context_end_parse (context, error);
+
+ g_markup_parse_context_free (context);
+}
+
+static GMarkupParser gconf_xml_parser = {
+ migrate_parse_gconf_xml_start_element,
+ migrate_parse_gconf_xml_end_element,
+ migrate_parse_gconf_xml_text,
+ NULL, /* passthrough */
+ NULL /* error */
+};
+
+static gboolean
+migrate_parse_gconf_xml (const gchar *contents,
+ gsize length,
+ GError **error)
+{
+ GMarkupParseContext *context;
+ ParseData *parse_data;
+ gboolean success = FALSE;
+
+ parse_data = parse_data_new ();
+
+ context = g_markup_parse_context_new (
+ &gconf_xml_parser, 0, parse_data,
+ (GDestroyNotify) parse_data_free);
+
+ if (g_markup_parse_context_parse (context, contents, length, error))
+ if (g_markup_parse_context_end_parse (context, error))
+ success = TRUE;
+
+ g_markup_parse_context_free (context);
+
+ return success;
+}
+
+void
+evolution_addressbook_factory_migrate_sources (void)
+{
+ gchar *base_dir;
+ gchar *filename;
+ gchar *contents;
+ gsize length;
+ GError *error = NULL;
+
+ base_dir = g_build_filename (
+ g_get_home_dir (), ".gconf", "apps", "evolution", NULL);
+
+ g_print ("Migrating addressbook sources from GConf...\n");
+
+ filename = g_build_filename (
+ base_dir, "addressbook", "%gconf.xml", NULL);
+ g_file_get_contents (filename, &contents, &length, &error);
+ g_free (filename);
+
+ if (error == NULL) {
+ migrate_parse_gconf_xml (contents, length, &error);
+ g_free (contents);
+ }
+
+ if (error != NULL) {
+ g_printerr (" FAILED: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ g_free (base_dir);
+}
diff --git a/services/evolution-addressbook-factory/evolution-addressbook-factory.c b/services/evolution-addressbook-factory/evolution-addressbook-factory.c
index 4577e8c..7b36c59 100644
--- a/services/evolution-addressbook-factory/evolution-addressbook-factory.c
+++ b/services/evolution-addressbook-factory/evolution-addressbook-factory.c
@@ -50,6 +50,7 @@ static GOptionEntry entries[] = {
/* Forward Declarations */
void evolution_addressbook_factory_migrate_basedir (void);
+void evolution_addressbook_factory_migrate_sources (void);
gint
main (gint argc,
@@ -114,6 +115,11 @@ main (gint argc,
/* Migrate user data from ~/.evolution to XDG base directories. */
evolution_addressbook_factory_migrate_basedir ();
+ /* Migrate ESource data from GConf XML blobs to key files.
+ * Do this AFTER XDG base directory migration since the key
+ * files are saved according to XDG base directory settings. */
+ evolution_addressbook_factory_migrate_sources ();
+
server = e_data_book_factory_new (NULL, &error);
if (error != NULL) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]