[evolution-data-server/gnome-3-10] Support migrating merged GConf trees.



commit 1d8cafea6d7527739726a6d2c81934516279e2d1
Author: Matthew Barnes <mbarnes redhat com>
Date:   Mon Oct 28 13:44:00 2013 -0400

    Support migrating merged GConf trees.
    
    Check for and parse a ~/.gconf/%gconf-tree.xml file during migration.
    This is mainly for Debian, which I think accidentally merged some users'
    GConf trees at some point.
    
    This also adds a command-line program (evolution-scan-gconf-tree-xml),
    which parses an arbitrary %gconf-tree.xml file as a recovery measure.
    
    (cherry picked from commit fd3ad46d19a02c4ea73117c9ebee7913ca388890)

 services/evolution-source-registry/Makefile.am     |   32 +++-
 .../evolution-scan-gconf-tree-xml.c                |   70 +++++
 .../evolution-source-registry-migrate-sources.c    |  269 ++++++++++++++++++-
 3 files changed, 355 insertions(+), 16 deletions(-)
---
diff --git a/services/evolution-source-registry/Makefile.am b/services/evolution-source-registry/Makefile.am
index 88f32f1..fc28b66 100644
--- a/services/evolution-source-registry/Makefile.am
+++ b/services/evolution-source-registry/Makefile.am
@@ -40,7 +40,10 @@ EXTRA_DIST = \
        $(service_in_files) \
        $(NULL)
 
-libexec_PROGRAMS = evolution-source-registry
+libexec_PROGRAMS = \
+       evolution-source-registry \
+       evolution-scan-gconf-tree-xml \
+       $(NULL)
 
 evolution_source_registry_CPPFLAGS = \
        $(AM_CPPFLAGS) \
@@ -74,6 +77,33 @@ evolution_source_registry_LDADD = \
        $(SOUP_LIBS) \
        $(NULL)
 
+evolution_scan_gconf_tree_xml_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       -I$(top_srcdir) \
+       -I$(top_builddir) \
+       -DG_LOG_DOMAIN=\"evolution-scan-gconf-tree-xml\" \
+       -DLOCALEDIR=\"$(localedir)\" \
+       $(E_DATA_SERVER_CFLAGS) \
+       $(LIBSECRET_CFLAGS) \
+       $(CAMEL_CFLAGS) \
+       $(SOUP_CFLAGS) \
+       $(NULL)
+
+evolution_scan_gconf_tree_xml_SOURCES = \
+       evolution-scan-gconf-tree-xml.c \
+       evolution-source-registry-migrate-sources.c \
+       $(NULL)
+
+evolution_scan_gconf_tree_xml_LDADD = \
+       $(top_builddir)/libebackend/libebackend-1.2.la \
+       $(top_builddir)/libedataserver/libedataserver-1.2.la \
+       $(top_builddir)/camel/libcamel-1.2.la \
+       $(E_DATA_SERVER_LIBS) \
+       $(LIBSECRET_LIBS) \
+       $(CAMEL_LIBS) \
+       $(SOUP_LIBS) \
+       $(NULL)
+
 evolution-source-registry-resource.h: evolution-source-registry-resource.xml $(builtin_sources)
        $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) \
        $(srcdir)/evolution-source-registry-resource.xml \
diff --git a/services/evolution-source-registry/evolution-scan-gconf-tree-xml.c 
b/services/evolution-source-registry/evolution-scan-gconf-tree-xml.c
new file mode 100644
index 0000000..123c0b1
--- /dev/null
+++ b/services/evolution-source-registry/evolution-scan-gconf-tree-xml.c
@@ -0,0 +1,70 @@
+/*
+ * evolution-scan-gconf-tree-xml.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 <config.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include <glib.h>
+
+#define PROGRAM_SUMMARY \
+       "Extracts Evolution accounts from a merged GConf tree file."
+
+/* Forward Declarations */
+gboolean       evolution_source_registry_migrate_gconf_tree_xml
+                                               (const gchar *filename,
+                                                GError **error);
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       GOptionContext *context;
+       GError *error = NULL;
+
+       setlocale (LC_ALL, "");
+       bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+       context = g_option_context_new ("/path/to/%gconf-tree.xml");
+       g_option_context_set_summary (context, PROGRAM_SUMMARY);
+       g_option_context_parse (context, &argc, &argv, &error);
+
+       if (error != NULL) {
+               g_printerr ("%s\n", error->message);
+               exit (1);
+       }
+
+       if (argc != 2) {
+               g_print (
+                       "Usage: %s /path/to/%%gconf-tree.xml\n\n",
+                       g_get_prgname ());
+               exit (0);
+       }
+
+       evolution_source_registry_migrate_gconf_tree_xml (argv[1], &error);
+
+       if (error != NULL) {
+               g_printerr ("%s\n", error->message);
+               exit (1);
+       }
+
+       return 0;
+}
+
diff --git a/services/evolution-source-registry/evolution-source-registry-migrate-sources.c 
b/services/evolution-source-registry/evolution-source-registry-migrate-sources.c
index 9653ba8..c207ee7 100644
--- a/services/evolution-source-registry/evolution-source-registry-migrate-sources.c
+++ b/services/evolution-source-registry/evolution-source-registry-migrate-sources.c
@@ -165,7 +165,10 @@ static const SecretSchema e_passwords_schema = {
 };
 
 /* Forward Declarations */
-void evolution_source_registry_migrate_sources (void);
+void           evolution_source_registry_migrate_sources (void);
+gboolean       evolution_source_registry_migrate_gconf_tree_xml
+                                               (const gchar *filename,
+                                                GError **error);
 
 static ParseData *
 parse_data_new (ParseType parse_type)
@@ -186,6 +189,8 @@ parse_data_free (ParseData *parse_data)
         * pointers are cleared before we get here.  But if an error
         * occurred we may leave data behind.  This cleans it up. */
 
+       g_return_if_fail (parse_data != NULL);
+
        if (parse_data->file != NULL)
                g_object_unref (parse_data->file);
 
@@ -3187,6 +3192,10 @@ migrate_parse_gconf_xml_start_element (GMarkupParseContext *context,
 {
        ParseData *parse_data = user_data;
 
+       /* Only seen in merged XML files. */
+       if (g_strcmp0 (element_name, "dir") == 0)
+               return;
+
        if (g_strcmp0 (element_name, "gconf") == 0) {
                if (parse_data->state != PARSE_STATE_INITIAL)
                        goto invalid_content;
@@ -3385,8 +3394,156 @@ migrate_parse_gconf_xml (ParseType parse_type,
        return success;
 }
 
+static gboolean
+migrate_parse_gconf_tree_xml_in_evolution (GQueue *dir_stack)
+{
+       if (g_strcmp0 (g_queue_peek_nth (dir_stack, 0), "apps") != 0)
+               return FALSE;
+
+       if (g_strcmp0 (g_queue_peek_nth (dir_stack, 1), "evolution") != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
 static void
-migrate_remove_gconf_xml (const gchar *gconf_key,
+migrate_parse_gconf_tree_xml_start_element (GMarkupParseContext *context,
+                                            const gchar *element_name,
+                                            const gchar **attribute_names,
+                                            const gchar **attribute_values,
+                                            gpointer user_data,
+                                            GError **error)
+{
+       GQueue *dir_stack = user_data;
+
+       if (g_strcmp0 (element_name, "dir") == 0) {
+               ParseData *parse_data = NULL;
+               gchar *dir_name = NULL;
+
+               g_markup_collect_attributes (
+                       element_name,
+                       attribute_names,
+                       attribute_values,
+                       error,
+                       G_MARKUP_COLLECT_STRDUP,
+                       "name", &dir_name,
+                       G_MARKUP_COLLECT_INVALID);
+
+               if (dir_name != NULL) {
+                       /* Takes ownership of the string. */
+                       g_queue_push_tail (dir_stack, dir_name);
+                       dir_name = NULL;
+               }
+
+               /* Push a sub-parser to handle the <entry> tag. */
+
+               if (migrate_parse_gconf_tree_xml_in_evolution (dir_stack))
+                       dir_name = g_queue_peek_tail (dir_stack);
+
+               if (g_strcmp0 (dir_name, "mail") == 0)
+                       parse_data = parse_data_new (PARSE_TYPE_MAIL);
+
+               if (g_strcmp0 (dir_name, "addressbook") == 0)
+                       parse_data = parse_data_new (PARSE_TYPE_ADDRESSBOOK);
+
+               if (g_strcmp0 (dir_name, "calendar") == 0)
+                       parse_data = parse_data_new (PARSE_TYPE_CALENDAR);
+
+               if (g_strcmp0 (dir_name, "tasks") == 0)
+                       parse_data = parse_data_new (PARSE_TYPE_TASKS);
+
+               if (g_strcmp0 (dir_name, "memos") == 0)
+                       parse_data = parse_data_new (PARSE_TYPE_MEMOS);
+
+               if (parse_data != NULL) {
+                       /* Pretend like we saw a <gconf> tag. */
+                       parse_data->state = PARSE_STATE_IN_GCONF;
+
+                       g_markup_parse_context_push (
+                               context, &gconf_xml_parser, parse_data);
+               }
+       }
+}
+
+static void
+migrate_parse_gconf_tree_xml_end_element (GMarkupParseContext *context,
+                                          const gchar *element_name,
+                                          gpointer user_data,
+                                          GError **error)
+{
+       GQueue *dir_stack = user_data;
+
+       if (g_strcmp0 (element_name, "dir") == 0) {
+               gboolean pop_parse_context = FALSE;
+
+               /* Figure out if we need to pop the parse context. */
+
+               if (migrate_parse_gconf_tree_xml_in_evolution (dir_stack)) {
+                       const gchar *dir_name;
+
+                       dir_name = g_queue_peek_tail (dir_stack);
+
+                       if (g_strcmp0 (dir_name, "mail") == 0)
+                               pop_parse_context = TRUE;
+
+                       if (g_strcmp0 (dir_name, "addressbook") == 0)
+                               pop_parse_context = TRUE;
+
+                       if (g_strcmp0 (dir_name, "calendar") == 0)
+                               pop_parse_context = TRUE;
+
+                       if (g_strcmp0 (dir_name, "tasks") == 0)
+                               pop_parse_context = TRUE;
+
+                       if (g_strcmp0 (dir_name, "memos") == 0)
+                               pop_parse_context = TRUE;
+               }
+
+               if (pop_parse_context) {
+                       ParseData *parse_data;
+
+                       parse_data = g_markup_parse_context_pop (context);
+                       parse_data_free (parse_data);
+               }
+
+               g_free (g_queue_pop_tail (dir_stack));
+       }
+}
+
+static GMarkupParser gconf_tree_xml_parser = {
+       migrate_parse_gconf_tree_xml_start_element,
+       migrate_parse_gconf_tree_xml_end_element,
+       NULL,  /* text */
+       NULL,  /* passthrough */
+       NULL   /* error */
+};
+
+static gboolean
+migrate_parse_gconf_tree_xml (const gchar *contents,
+                              gsize length,
+                              GError **error)
+{
+       GMarkupParseContext *context;
+       GQueue dir_stack = G_QUEUE_INIT;
+       gboolean success = FALSE;
+
+       context = g_markup_parse_context_new (
+               &gconf_tree_xml_parser, 0,
+               &dir_stack, (GDestroyNotify) NULL);
+
+       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);
+
+       g_warn_if_fail (g_queue_is_empty (&dir_stack));
+
+       return success;
+}
+
+static void
+migrate_remove_gconf_key (const gchar *gconf_key,
                           const gchar *gconf_xml)
 {
        /* Remove the GConf string list so the user is not haunted by
@@ -3425,11 +3582,14 @@ migrate_remove_gconf_xml (const gchar *gconf_key,
                g_free (command_line);
        }
 
-       if (g_file_test (gconf_xml, G_FILE_TEST_IS_REGULAR)) {
-               if (g_remove (gconf_xml) == -1) {
-                       g_printerr (
-                               "Failed to remove '%s': %s\n",
-                               gconf_xml, g_strerror (errno));
+       /* This will be NULL when parsing a merged XML tree. */
+       if (gconf_xml != NULL) {
+               if (g_file_test (gconf_xml, G_FILE_TEST_IS_REGULAR)) {
+                       if (g_remove (gconf_xml) == -1) {
+                               g_printerr (
+                                       "Failed to remove '%s': %s\n",
+                                       gconf_xml, g_strerror (errno));
+                       }
                }
        }
 }
@@ -3443,8 +3603,45 @@ migrate_handle_error (const GError *error)
                g_printerr ("  FAILED: %s\n", error->message);
 }
 
-void
-evolution_source_registry_migrate_sources (void)
+static void
+migrate_merged_gconf_tree (const gchar *gconf_tree_xml)
+{
+       gchar *contents;
+       gsize length;
+       GError *error = NULL;
+
+       g_file_get_contents (gconf_tree_xml, &contents, &length, &error);
+
+       if (error == NULL) {
+               migrate_parse_gconf_tree_xml (contents, length, &error);
+               g_free (contents);
+       }
+
+       if (error == NULL) {
+               const gchar *gconf_key;
+
+               gconf_key = "/apps/evolution/mail/accounts";
+               migrate_remove_gconf_key (gconf_key, NULL);
+
+               gconf_key = "/apps/evolution/addressbook/sources";
+               migrate_remove_gconf_key (gconf_key, NULL);
+
+               gconf_key = "/apps/evolution/calendar/sources";
+               migrate_remove_gconf_key (gconf_key, NULL);
+
+               gconf_key = "/apps/evolution/tasks/sources";
+               migrate_remove_gconf_key (gconf_key, NULL);
+
+               gconf_key = "/apps/evolution/memos/sources";
+               migrate_remove_gconf_key (gconf_key, NULL);
+       } else {
+               migrate_handle_error (error);
+               g_clear_error (&error);
+       }
+}
+
+static void
+migrate_normal_gconf_tree (const gchar *gconf_base_dir)
 {
        gchar *base_dir;
        gchar *contents;
@@ -3454,7 +3651,7 @@ evolution_source_registry_migrate_sources (void)
        GError *error = NULL;
 
        base_dir = g_build_filename (
-               g_get_home_dir (), ".gconf", "apps", "evolution", NULL);
+               gconf_base_dir, "apps", "evolution", NULL);
 
        /* ------------------------------------------------------------------*/
 
@@ -3473,7 +3670,7 @@ evolution_source_registry_migrate_sources (void)
 
        if (error == NULL) {
                gconf_key = "/apps/evolution/mail/accounts";
-               migrate_remove_gconf_xml (gconf_key, gconf_xml);
+               migrate_remove_gconf_key (gconf_key, gconf_xml);
        } else {
                migrate_handle_error (error);
                g_clear_error (&error);
@@ -3498,7 +3695,7 @@ evolution_source_registry_migrate_sources (void)
 
        if (error == NULL) {
                gconf_key = "/apps/evolution/addressbook/sources";
-               migrate_remove_gconf_xml (gconf_key, gconf_xml);
+               migrate_remove_gconf_key (gconf_key, gconf_xml);
        } else {
                migrate_handle_error (error);
                g_clear_error (&error);
@@ -3523,7 +3720,7 @@ evolution_source_registry_migrate_sources (void)
 
        if (error == NULL) {
                gconf_key = "/apps/evolution/calendar/sources";
-               migrate_remove_gconf_xml (gconf_key, gconf_xml);
+               migrate_remove_gconf_key (gconf_key, gconf_xml);
        } else {
                migrate_handle_error (error);
                g_clear_error (&error);
@@ -3548,7 +3745,7 @@ evolution_source_registry_migrate_sources (void)
 
        if (error == NULL) {
                gconf_key = "/apps/evolution/tasks/sources";
-               migrate_remove_gconf_xml (gconf_key, gconf_xml);
+               migrate_remove_gconf_key (gconf_key, gconf_xml);
        } else {
                migrate_handle_error (error);
                g_clear_error (&error);
@@ -3573,7 +3770,7 @@ evolution_source_registry_migrate_sources (void)
 
        if (error == NULL) {
                gconf_key = "/apps/evolution/memos/sources";
-               migrate_remove_gconf_xml (gconf_key, gconf_xml);
+               migrate_remove_gconf_key (gconf_key, gconf_xml);
        } else {
                migrate_handle_error (error);
                g_clear_error (&error);
@@ -3585,3 +3782,45 @@ evolution_source_registry_migrate_sources (void)
 
        g_free (base_dir);
 }
+
+void
+evolution_source_registry_migrate_sources (void)
+{
+       gchar *gconf_base_dir;
+       gchar *gconf_tree_xml;
+
+       gconf_base_dir =
+               g_build_filename (g_get_home_dir (), ".gconf", NULL);
+       gconf_tree_xml =
+               g_build_filename (gconf_base_dir, "%gconf-tree.xml", NULL);
+
+       /* Handle a merged GConf tree file if present (mainly for
+        * Debian), otherwise assume a normal GConf directory tree. */
+       if (g_file_test (gconf_tree_xml, G_FILE_TEST_IS_REGULAR))
+               migrate_merged_gconf_tree (gconf_tree_xml);
+       else
+               migrate_normal_gconf_tree (gconf_base_dir);
+
+       g_free (gconf_base_dir);
+       g_free (gconf_tree_xml);
+}
+
+gboolean
+evolution_source_registry_migrate_gconf_tree_xml (const gchar *filename,
+                                                  GError **error)
+{
+       gchar *contents;
+       gsize length;
+       gboolean success = FALSE;
+
+       /* Extracts account info from an arbitrary merged XML file. */
+
+       if (g_file_get_contents (filename, &contents, &length, error)) {
+               success = migrate_parse_gconf_tree_xml (
+                       contents, length, error);
+               g_free (contents);
+       }
+
+       return success;
+}
+


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