Bug 677687 - Broken migration of "namespace" IMAP setting
[platform/upstream/evolution-data-server.git] / services / evolution-source-registry / evolution-source-registry-migrate-sources.c
1 /*
2  * migrate-from-gconf.c
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
16  *
17  */
18
19 #include <errno.h>
20 #include <string.h>
21 #include <glib/gstdio.h>
22 #include <camel/camel.h>
23 #include <libsoup/soup.h>
24 #include <gnome-keyring.h>
25
26 #include <libebackend/libebackend.h>
27
28 /* These constants are collected from various e-source-*.h files
29  * throughout evolution-data-server and known extension packages. */
30 #define E_SOURCE_GROUP_NAME                     "Data Source"
31 #define E_SOURCE_EXTENSION_CONTACTS_BACKEND     "Contacts Backend"
32 #define E_SOURCE_EXTENSION_LDAP_BACKEND         "LDAP Backend"
33 #define E_SOURCE_EXTENSION_LOCAL_BACKEND        "Local Backend"
34 #define E_SOURCE_EXTENSION_VCF_BACKEND          "VCF Backend"
35 #define E_SOURCE_EXTENSION_WEATHER_BACKEND      "Weather Backend"
36 #define E_SOURCE_EXTENSION_WEBDAV_BACKEND       "WebDAV Backend"
37
38 /* These constants are copied from e-source-password.c. */
39 #define KEYRING_ITEM_ATTRIBUTE_NAME             "e-source-uid"
40 #define KEYRING_ITEM_DISPLAY_FORMAT             "Evolution Data Source %s"
41
42 typedef struct _ParseData ParseData;
43
44 typedef void            (*PropertyFunc)         (ParseData *parse_data,
45                                                  const gchar *property_name,
46                                                  const gchar *property_value);
47
48 typedef enum {
49         PARSE_TYPE_MAIL,
50         PARSE_TYPE_ADDRESSBOOK,
51         PARSE_TYPE_CALENDAR,
52         PARSE_TYPE_TASKS,
53         PARSE_TYPE_MEMOS
54 } ParseType;
55
56 typedef enum {
57         PARSE_STATE_INITIAL,
58
59         PARSE_STATE_IN_GCONF,                   /* GConf XML */
60         PARSE_STATE_IN_ACCOUNTS_ENTRY,          /* GConf XML */
61         PARSE_STATE_IN_ACCOUNTS_VALUE,          /* GConf XML */
62         PARSE_STATE_IN_SIGNATURES_ENTRY,        /* GConf XML */
63         PARSE_STATE_IN_SIGNATURES_VALUE,        /* GConf XML */
64         PARSE_STATE_IN_SOURCES_ENTRY,           /* GConf XML */
65         PARSE_STATE_IN_SOURCES_VALUE,           /* GConf XML */
66
67         PARSE_STATE_IN_ACCOUNT,                 /* EAccount XML */
68         PARSE_STATE_IN_IDENTITY,                /* EAccount XML */
69         PARSE_STATE_IN_IDENTITY_NAME,           /* EAccount XML */
70         PARSE_STATE_IN_IDENTITY_ADDR_SPEC,      /* EAccount XML */
71         PARSE_STATE_IN_IDENTITY_REPLY_TO,       /* EAccount XML */
72         PARSE_STATE_IN_IDENTITY_ORGANIZATION,   /* EAccount XML */
73         PARSE_STATE_IN_IDENTITY_SIGNATURE,      /* EAccount XML */
74         PARSE_STATE_IN_MAIL_SOURCE,             /* EAccount XML */
75         PARSE_STATE_IN_MAIL_SOURCE_URL,         /* EAccount XML */
76         PARSE_STATE_IN_MAIL_TRANSPORT,          /* EAccount XML */
77         PARSE_STATE_IN_MAIL_TRANSPORT_URL,      /* EAccount XML */
78         PARSE_STATE_IN_AUTO_CC,                 /* EAccount XML */
79         PARSE_STATE_IN_AUTO_CC_RECIPIENTS,      /* EAccount XML */
80         PARSE_STATE_IN_AUTO_BCC,                /* EAccount XML */
81         PARSE_STATE_IN_AUTO_BCC_RECIPIENTS,     /* EAccount XML */
82         PARSE_STATE_IN_DRAFTS_FOLDER,           /* EAccount XML */
83         PARSE_STATE_IN_SENT_FOLDER,             /* EAccount XML */
84         PARSE_STATE_IN_RECEIPT_POLICY,          /* EAccount XML */
85         PARSE_STATE_IN_PGP,                     /* EAccount XML */
86         PARSE_STATE_IN_PGP_KEY_ID,              /* EAccount XML */
87         PARSE_STATE_IN_SMIME,                   /* EAccount XML */
88         PARSE_STATE_IN_SMIME_SIGN_KEY_ID,       /* EAccount XML */
89         PARSE_STATE_IN_SMIME_ENCRYPT_KEY_ID,    /* EAccount XML */
90
91         PARSE_STATE_IN_SIGNATURE,               /* ESignature XML */
92         PARSE_STATE_IN_FILENAME,                /* ESignature XML */
93
94         PARSE_STATE_IN_GROUP,                   /* ESource XML */
95         PARSE_STATE_IN_SOURCE,                  /* ESource XML */
96         PARSE_STATE_IN_PROPERTIES               /* ESource XML */
97 } ParseState;
98
99 struct _ParseData {
100         ParseType type;
101         ParseState state;
102
103         /* Whether to skip writing a file
104          * for this account information. */
105         gboolean skip;
106
107         /* Set by <account>, <source> and <signature> tags. */
108         GFile *file;
109         GKeyFile *key_file;
110
111         /* Set by <account>/<source> tags. */
112         gboolean auto_bcc;
113         gboolean auto_cc;
114
115         /* Set by <identity> tags. */
116         GFile *identity_file;
117         GKeyFile *identity_key_file;
118
119         /* Set by <transport> tags. */
120         GFile *transport_file;
121         GKeyFile *transport_key_file;
122
123         /* Set by <account> tags. */
124         GFile *collection_file;
125         GKeyFile *collection_key_file;
126
127         /* Set by <signature> tags. */
128         GFile *signature_file;
129         gboolean is_script;
130
131         /* Set by <group> tags. */
132         gchar *base_uri;
133
134         /* Set by <source> tags. */
135         gchar *mangled_uri;
136         SoupURI *soup_uri;
137         PropertyFunc property_func;
138 };
139
140 static GnomeKeyringPasswordSchema schema = {
141         GNOME_KEYRING_ITEM_GENERIC_SECRET,
142         {
143                 { KEYRING_ITEM_ATTRIBUTE_NAME,
144                   GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
145                 { NULL, 0 }
146         }
147 };
148
149 /* Forward Declarations */
150 void evolution_source_registry_migrate_sources (void);
151
152 static ParseData *
153 parse_data_new (ParseType parse_type)
154 {
155         ParseData *parse_data;
156
157         parse_data = g_slice_new0 (ParseData);
158         parse_data->type = parse_type;
159         parse_data->state = PARSE_STATE_INITIAL;
160
161         return parse_data;
162 }
163
164 static void
165 parse_data_free (ParseData *parse_data)
166 {
167         /* Normally the allocated data in ParseData is freed and the
168          * pointers are cleared before we get here.  But if an error
169          * occurred we may leave data behind.  This cleans it up. */
170
171         if (parse_data->file != NULL)
172                 g_object_unref (parse_data->file);
173
174         if (parse_data->key_file != NULL)
175                 g_key_file_free (parse_data->key_file);
176
177         if (parse_data->identity_file != NULL)
178                 g_object_unref (parse_data->identity_file);
179
180         if (parse_data->identity_key_file != NULL)
181                 g_key_file_free (parse_data->identity_key_file);
182
183         if (parse_data->transport_file != NULL)
184                 g_object_unref (parse_data->transport_file);
185
186         if (parse_data->transport_key_file != NULL)
187                 g_key_file_free (parse_data->transport_key_file);
188
189         if (parse_data->collection_file != NULL)
190                 g_object_unref (parse_data->collection_file);
191
192         if (parse_data->collection_key_file != NULL)
193                 g_key_file_free (parse_data->collection_key_file);
194
195         if (parse_data->signature_file != NULL)
196                 g_object_unref (parse_data->signature_file);
197
198         g_free (parse_data->base_uri);
199         g_free (parse_data->mangled_uri);
200
201         if (parse_data->soup_uri != NULL)
202                 soup_uri_free (parse_data->soup_uri);
203
204         g_slice_free (ParseData, parse_data);
205 }
206
207 static gboolean
208 is_true (const gchar *string)
209 {
210         return  (g_ascii_strcasecmp (string, "1") == 0) ||
211                 (g_ascii_strcasecmp (string, "true") == 0);
212 }
213
214 static gboolean
215 is_false (const gchar *string)
216 {
217         return  (g_ascii_strcasecmp (string, "0") == 0) ||
218                 (g_ascii_strcasecmp (string, "false") == 0);
219 }
220
221 static gboolean
222 base_uri_is_groupware (const gchar *base_uri)
223 {
224         /* Well-known scheme names from various groupware packages. */
225
226         /* We use a limited string comparsion here because the
227          * base_uri string may be 'scheme://' or just 'scheme'. */
228
229         g_return_val_if_fail (base_uri != NULL, FALSE);
230
231         if (g_ascii_strncasecmp (base_uri, "ews", 3) == 0)
232                 return TRUE;
233
234         if (g_ascii_strncasecmp (base_uri, "exchange", 8) == 0)
235                 return TRUE;
236
237         if (g_ascii_strncasecmp (base_uri, "groupwise", 9) == 0)
238                 return TRUE;
239
240         if (g_ascii_strncasecmp (base_uri, "kolab", 5) == 0)
241                 return TRUE;
242
243         if (g_ascii_strncasecmp (base_uri, "mapi", 4) == 0)
244                 return TRUE;
245
246         return FALSE;
247 }
248
249 static void
250 migrate_keyring_entry (const gchar *uid,
251                        const gchar *user,
252                        const gchar *server,
253                        const gchar *protocol)
254 {
255         GnomeKeyringAttributeList *attributes;
256         GList *found_list = NULL;
257         gchar *display_name;
258
259         /* This is a best-effort routine, so we don't really care about
260          * errors.  We leave the old keyring entry in place since it may
261          * be reused for address book or calendar migration. */
262
263         display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
264
265         attributes = gnome_keyring_attribute_list_new ();
266
267         gnome_keyring_attribute_list_append_string (
268                 attributes, "application", "Evolution");
269         if (user != NULL)
270                 gnome_keyring_attribute_list_append_string (
271                         attributes, "user", user);
272         if (server != NULL)
273                 gnome_keyring_attribute_list_append_string (
274                         attributes, "server", server);
275         if (protocol != NULL)
276                 gnome_keyring_attribute_list_append_string (
277                         attributes, "protocol", protocol);
278
279         gnome_keyring_find_items_sync (
280                 GNOME_KEYRING_ITEM_NETWORK_PASSWORD, attributes, &found_list);
281
282         /* Pick the first match we find. */
283         if (found_list != NULL) {
284                 GnomeKeyringFound *found = found_list->data;
285
286                 /* Sanity check. */
287                 g_return_if_fail (found->secret != NULL);
288
289                 gnome_keyring_store_password_sync (
290                         &schema, GNOME_KEYRING_DEFAULT, display_name,
291                         found->secret, KEYRING_ITEM_ATTRIBUTE_NAME, uid, NULL);
292         }
293
294         gnome_keyring_attribute_list_free (attributes);
295         gnome_keyring_found_list_free (found_list);
296
297         g_free (display_name);
298 }
299
300 static gboolean
301 migrate_parse_commit_changes (ParseType parse_type,
302                               GFile *file,
303                               GKeyFile *key_file,
304                               const gchar *mangled_uri,
305                               GError **error)
306 {
307         const gchar *data_dir;
308         const gchar *cache_dir;
309         const gchar *component;
310         gchar *old_directory;
311         gchar *new_directory;
312         gchar *contents;
313         gchar *uid;
314         gsize length;
315         gboolean success;
316         gboolean old_directory_exists;
317         gboolean new_directory_exists;
318
319         data_dir = e_get_user_data_dir ();
320         cache_dir = e_get_user_cache_dir ();
321
322         uid = e_server_side_source_uid_from_file (file, error);
323
324         if (uid == NULL)
325                 return FALSE;
326
327         g_print ("  * Source: %s\n", uid);
328
329         g_print ("    Writing key file...\n");
330
331         /* Save the key file contents to disk. */
332         contents = g_key_file_to_data (key_file, &length, NULL);
333         success = g_file_replace_contents (
334                 file, contents, length, NULL, FALSE,
335                 G_FILE_CREATE_PRIVATE, NULL, NULL, error);
336         g_free (contents);
337
338         if (!success)
339                 goto exit;
340
341         /* Rename the source's local cache directory from its mangled
342          * URI to its UID.  The key file's basename contains the UID.
343          * All source types but "local" should have cache directories. */
344
345         /* Mail cache directories already use UIDs. */
346         switch (parse_type) {
347                 case PARSE_TYPE_ADDRESSBOOK:
348                         component = "addressbook";
349                         break;
350                 case PARSE_TYPE_CALENDAR:
351                         component = "calendar";
352                         break;
353                 case PARSE_TYPE_TASKS:
354                         component = "tasks";
355                         break;
356                 case PARSE_TYPE_MEMOS:
357                         component = "memos";
358                         break;
359                 default:
360                         goto exit;
361         }
362
363         g_assert (mangled_uri != NULL);
364
365         old_directory = g_build_filename (
366                 cache_dir, component, mangled_uri, NULL);
367
368         new_directory = g_build_filename (
369                 cache_dir, component, uid, NULL);
370
371         old_directory_exists = g_file_test (old_directory, G_FILE_TEST_EXISTS);
372         new_directory_exists = g_file_test (new_directory, G_FILE_TEST_EXISTS);
373
374         g_print (
375                 "    Checking for old cache dir '%s'... %s\n",
376                 old_directory,
377                 old_directory_exists ?  "found" : "not found");
378
379         if (old_directory_exists) {
380                 g_print (
381                         "    Checking for new cache dir '%s'... %s\n",
382                         new_directory,
383                         new_directory_exists ? "found" : "not found");
384
385                 if (new_directory_exists)
386                         g_print ("    Skipping cache directory rename.\n");
387                 else {
388                         g_print ("    Renaming old cache directory...\n");
389                         if (g_rename (old_directory, new_directory) < 0) {
390                                 g_set_error (
391                                         error, G_FILE_ERROR,
392                                         g_file_error_from_errno (errno),
393                                         "%s", g_strerror (errno));
394                                 success = FALSE;
395                         }
396                 }
397         }
398
399         g_free (old_directory);
400         g_free (new_directory);
401
402         if (!success)
403                 goto exit;
404
405         /* Rename the source's local data directory from its mangled
406          * URI to its UID.  The key file's basename contains the UID.
407          * Only "local" sources have local data directores. */
408
409         old_directory = g_build_filename (
410                 data_dir, component, mangled_uri, NULL);
411
412         new_directory = g_build_filename (
413                 data_dir, component, uid, NULL);
414
415         old_directory_exists = g_file_test (old_directory, G_FILE_TEST_EXISTS);
416         new_directory_exists = g_file_test (new_directory, G_FILE_TEST_EXISTS);
417
418         g_print (
419                 "    Checking for old data dir '%s'... %s\n",
420                 old_directory,
421                 old_directory_exists ?  "found" : "not found");
422
423         if (old_directory_exists) {
424                 g_print (
425                         "    Checking for new data dir '%s'... %s\n",
426                         new_directory,
427                         new_directory_exists ? "found" : "not found");
428
429                 if (new_directory_exists)
430                         g_print ("    Skipping data directory rename.\n");
431                 else {
432                         g_print ("    Renaming old data directory...\n");
433                         if (g_rename (old_directory, new_directory) < 0) {
434                                 g_set_error (
435                                         error, G_FILE_ERROR,
436                                         g_file_error_from_errno (errno),
437                                         "%s", g_strerror (errno));
438                                 success = FALSE;
439                         }
440                 }
441         }
442
443         g_free (old_directory);
444         g_free (new_directory);
445
446 exit:
447         g_free (uid);
448
449         return success;
450 }
451
452 static void
453 migrate_setup_collection (ParseData *parse_data,
454                           CamelURL *url)
455 {
456         gchar *collection_uid;
457         gchar *display_name;
458         gboolean enabled;
459
460         g_return_if_fail (parse_data->key_file != NULL);
461         g_return_if_fail (parse_data->identity_key_file != NULL);
462         g_return_if_fail (parse_data->transport_key_file != NULL);
463
464         parse_data->collection_file = e_server_side_source_new_user_file (NULL);
465         parse_data->collection_key_file = g_key_file_new ();
466
467         collection_uid = e_server_side_source_uid_from_file (
468                 parse_data->collection_file, NULL);
469
470         /* Copy the display name from the mail account source. */
471
472         display_name = g_key_file_get_string (
473                 parse_data->key_file,
474                 E_SOURCE_GROUP_NAME, "DisplayName", NULL);
475
476         g_key_file_set_string (
477                 parse_data->collection_key_file,
478                 E_SOURCE_GROUP_NAME, "DisplayName", display_name);
479
480         /* Copy the enabled state from the mail account source. */
481
482         enabled = g_key_file_get_boolean (
483                 parse_data->key_file,
484                 E_SOURCE_GROUP_NAME, "Enabled", NULL);
485
486         g_key_file_set_boolean (
487                 parse_data->collection_key_file,
488                 E_SOURCE_GROUP_NAME, "Enabled", enabled);
489
490         /* Collection sources are always top-level sources. */
491
492         g_key_file_set_string (
493                 parse_data->collection_key_file,
494                 E_SOURCE_GROUP_NAME, "Parent", "");
495
496         /* Collection backend name should match the CamelURL protocol. */
497
498         g_key_file_set_string (
499                 parse_data->collection_key_file,
500                 E_SOURCE_EXTENSION_COLLECTION,
501                 "BackendName", url->protocol);
502
503         g_key_file_set_boolean (
504                 parse_data->collection_key_file,
505                 E_SOURCE_EXTENSION_COLLECTION,
506                 "CalendarEnabled", TRUE);
507
508         g_key_file_set_boolean (
509                 parse_data->collection_key_file,
510                 E_SOURCE_EXTENSION_COLLECTION,
511                 "ContactsEnabled", TRUE);
512
513         g_key_file_set_boolean (
514                 parse_data->collection_key_file,
515                 E_SOURCE_EXTENSION_COLLECTION,
516                 "MailEnabled", TRUE);
517
518         /* Enable all mail sources since we set "MailEnabled=true" above. */
519
520         g_key_file_set_boolean (
521                 parse_data->key_file,
522                 E_SOURCE_GROUP_NAME, "Enabled", TRUE);
523
524         g_key_file_set_boolean (
525                 parse_data->identity_key_file,
526                 E_SOURCE_GROUP_NAME, "Enabled", TRUE);
527
528         g_key_file_set_boolean (
529                 parse_data->transport_key_file,
530                 E_SOURCE_GROUP_NAME, "Enabled", TRUE);
531
532         /* The other mail sources are children of the collection source. */
533
534         g_key_file_set_string (
535                 parse_data->key_file,
536                 E_SOURCE_GROUP_NAME,
537                 "Parent", collection_uid);
538
539         g_key_file_set_string (
540                 parse_data->identity_key_file,
541                 E_SOURCE_GROUP_NAME,
542                 "Parent", collection_uid);
543
544         g_key_file_set_string (
545                 parse_data->transport_key_file,
546                 E_SOURCE_GROUP_NAME,
547                 "Parent", collection_uid);
548
549         /* The collection identity has to be determined case-by-case.
550          * Some are based on user name, some are based on email address. */
551
552         if (g_strcmp0 (url->protocol, "ews") == 0)
553                 g_key_file_set_string (
554                         parse_data->collection_key_file,
555                         E_SOURCE_EXTENSION_COLLECTION,
556                         "Identity", url->user);
557
558         g_free (collection_uid);
559         g_free (display_name);
560 }
561
562 static void
563 migrate_parse_account (ParseData *parse_data,
564                        const gchar *element_name,
565                        const gchar **attribute_names,
566                        const gchar **attribute_values,
567                        GError **error)
568 {
569         const gchar *uid;
570         const gchar *name;
571         gchar *identity_uid;
572         gchar *transport_uid;
573         gboolean enabled;
574         gboolean success;
575
576         success = g_markup_collect_attributes (
577                 element_name,
578                 attribute_names,
579                 attribute_values,
580                 error,
581                 G_MARKUP_COLLECT_STRING,
582                 "uid", &uid,
583                 G_MARKUP_COLLECT_STRING,
584                 "name", &name,
585                 G_MARKUP_COLLECT_BOOLEAN,
586                 "enabled", &enabled,
587                 G_MARKUP_COLLECT_INVALID);
588
589         if (!success)
590                 return;
591
592         parse_data->file = e_server_side_source_new_user_file (uid);
593
594         /* If the file already exists, skip this source.  It may be that we
595          * already migrated it, in which case we don't want to overwrite it. */
596         if (g_file_query_exists (parse_data->file, NULL))
597                 return;
598
599         parse_data->key_file = g_key_file_new ();
600
601         parse_data->identity_file = e_server_side_source_new_user_file (NULL);
602         parse_data->identity_key_file = g_key_file_new ();
603
604         parse_data->transport_file = e_server_side_source_new_user_file (NULL);
605         parse_data->transport_key_file = g_key_file_new ();
606
607         identity_uid = e_server_side_source_uid_from_file (
608                 parse_data->identity_file, NULL);
609
610         transport_uid = e_server_side_source_uid_from_file (
611                 parse_data->transport_file, NULL);
612
613         g_key_file_set_string (
614                 parse_data->key_file,
615                 E_SOURCE_GROUP_NAME,
616                 "DisplayName", name);
617
618         g_key_file_set_boolean (
619                 parse_data->key_file,
620                 E_SOURCE_GROUP_NAME,
621                 "Enabled", enabled);
622
623         /* Mail account source references the identity source. */
624         g_key_file_set_string (
625                 parse_data->key_file,
626                 E_SOURCE_EXTENSION_MAIL_ACCOUNT,
627                 "IdentityUid", identity_uid);
628
629         /* Mail account source references the transport source. */
630         g_key_file_set_string (
631                 parse_data->identity_key_file,
632                 E_SOURCE_EXTENSION_MAIL_SUBMISSION,
633                 "TransportUid", transport_uid);
634
635         /* Identity source gets the same display name. */
636         g_key_file_set_string (
637                 parse_data->identity_key_file,
638                 E_SOURCE_GROUP_NAME,
639                 "DisplayName", name);
640
641         /* Identity source is a child of the mail account. */
642         g_key_file_set_string (
643                 parse_data->identity_key_file,
644                 E_SOURCE_GROUP_NAME,
645                 "Parent", uid);
646
647         /* Transport source gets the same display name. */
648         g_key_file_set_string (
649                 parse_data->transport_key_file,
650                 E_SOURCE_GROUP_NAME,
651                 "DisplayName", name);
652
653         /* Transport source is a child of the mail account. */
654         g_key_file_set_string (
655                 parse_data->transport_key_file,
656                 E_SOURCE_GROUP_NAME,
657                 "Parent", uid);
658
659         g_free (identity_uid);
660         g_free (transport_uid);
661 }
662
663 static void
664 migrate_parse_pgp (ParseData *parse_data,
665                    const gchar *element_name,
666                    const gchar **attribute_names,
667                    const gchar **attribute_values,
668                    GError **error)
669 {
670         const gchar *hash_algo;
671         gboolean always_sign;
672         gboolean always_trust;
673         gboolean encrypt_to_self;
674         gboolean no_imip_sign;
675         gboolean success;
676
677         success = g_markup_collect_attributes (
678                 element_name,
679                 attribute_names,
680                 attribute_values,
681                 error,
682                 G_MARKUP_COLLECT_BOOLEAN,
683                 "always-sign", &always_sign,
684                 G_MARKUP_COLLECT_BOOLEAN,
685                 "always-trust", &always_trust,
686                 G_MARKUP_COLLECT_BOOLEAN,
687                 "encrypt-to-self", &encrypt_to_self,
688                 G_MARKUP_COLLECT_BOOLEAN,
689                 "no-imip-sign", &no_imip_sign,
690                 G_MARKUP_COLLECT_STRING |
691                 G_MARKUP_COLLECT_OPTIONAL,
692                 "hash-algo", &hash_algo,
693                 G_MARKUP_COLLECT_INVALID);
694
695         if (!success)
696                 return;
697
698         g_key_file_set_boolean (
699                 parse_data->identity_key_file,
700                 E_SOURCE_EXTENSION_OPENPGP,
701                 "AlwaysSign", always_sign);
702
703         g_key_file_set_boolean (
704                 parse_data->identity_key_file,
705                 E_SOURCE_EXTENSION_OPENPGP,
706                 "AlwaysTrust", always_trust);
707
708         g_key_file_set_boolean (
709                 parse_data->identity_key_file,
710                 E_SOURCE_EXTENSION_OPENPGP,
711                 "EncryptToSelf", encrypt_to_self);
712
713         if (hash_algo != NULL && *hash_algo != '\0')
714                 g_key_file_set_string (
715                         parse_data->identity_key_file,
716                         E_SOURCE_EXTENSION_OPENPGP,
717                         "SigningAlgorithm", hash_algo);
718
719         /* XXX Don't know why this is under the <pgp>
720          *     element, it applies to S/MIME as well.
721          *     Also note we're inverting the setting. */
722         g_key_file_set_boolean (
723                 parse_data->identity_key_file,
724                 E_SOURCE_EXTENSION_MAIL_COMPOSITION,
725                 "SignImip", !no_imip_sign);
726 }
727
728 static void
729 migrate_parse_recipients (ParseData *parse_data,
730                           const gchar *key,
731                           const gchar *recipients)
732 {
733         CamelAddress *address;
734         CamelInternetAddress *inet_address;
735         gchar **string_list;
736         gint ii, length;
737         gsize index = 0;
738
739         if (recipients == NULL || *recipients == '\0')
740                 return;
741
742         inet_address = camel_internet_address_new ();
743         address = CAMEL_ADDRESS (inet_address);
744
745         if (camel_address_decode (address, recipients) == -1)
746                 goto exit;
747
748         length = camel_address_length (address);
749         string_list = g_new0 (gchar *, length + 1);
750
751         for (ii = 0; ii < length; ii++) {
752                 const gchar *name, *addr;
753
754                 if (!camel_internet_address_get (
755                         inet_address, ii, &name, &addr))
756                         continue;
757
758                 string_list[index++] =
759                         camel_internet_address_format_address (name, addr);
760         }
761
762         g_key_file_set_string_list (
763                 parse_data->identity_key_file,
764                 E_SOURCE_EXTENSION_MAIL_COMPOSITION, key,
765                 (const gchar *const *) string_list, index);
766
767         g_strfreev (string_list);
768
769 exit:
770         g_object_unref (inet_address);
771 }
772
773 static void
774 migrate_parse_smime (ParseData *parse_data,
775                      const gchar *element_name,
776                      const gchar **attribute_names,
777                      const gchar **attribute_values,
778                      GError **error)
779 {
780         const gchar *hash_algo;
781         gboolean encrypt_default;
782         gboolean encrypt_to_self;
783         gboolean sign_default;
784         gboolean success;
785
786         success = g_markup_collect_attributes (
787                 element_name,
788                 attribute_names,
789                 attribute_values,
790                 error,
791                 G_MARKUP_COLLECT_BOOLEAN,
792                 "encrypt-default", &encrypt_default,
793                 G_MARKUP_COLLECT_BOOLEAN,
794                 "encrypt-to-self", &encrypt_to_self,
795                 G_MARKUP_COLLECT_STRING |
796                 G_MARKUP_COLLECT_OPTIONAL,
797                 "hash-algo", &hash_algo,
798                 G_MARKUP_COLLECT_BOOLEAN,
799                 "sign-default", &sign_default,
800                 G_MARKUP_COLLECT_INVALID);
801
802         if (!success)
803                 return;
804
805         g_key_file_set_boolean (
806                 parse_data->identity_key_file,
807                 E_SOURCE_EXTENSION_SMIME,
808                 "EncryptByDefault", encrypt_default);
809
810         g_key_file_set_boolean (
811                 parse_data->identity_key_file,
812                 E_SOURCE_EXTENSION_SMIME,
813                 "EncryptToSelf", encrypt_to_self);
814
815         if (hash_algo != NULL && *hash_algo != '\0')
816                 g_key_file_set_string (
817                         parse_data->identity_key_file,
818                         E_SOURCE_EXTENSION_SMIME,
819                         "SigningAlgorithm", hash_algo);
820
821         g_key_file_set_boolean (
822                 parse_data->identity_key_file,
823                 E_SOURCE_EXTENSION_SMIME,
824                 "SignByDefault", sign_default);
825 }
826
827 static void
828 migrate_parse_mail_source (ParseData *parse_data,
829                            const gchar *element_name,
830                            const gchar **attribute_names,
831                            const gchar **attribute_values,
832                            GError **error)
833 {
834         const gchar *auto_check_timeout;
835         glong interval_minutes = 0;
836         gboolean auto_check;
837         gboolean success;
838
839         /* Disregard "keep-on-server" and "save-passwd" attributes. */
840         success = g_markup_collect_attributes (
841                 element_name,
842                 attribute_names,
843                 attribute_values,
844                 error,
845                 G_MARKUP_COLLECT_BOOLEAN,
846                 "auto-check", &auto_check,
847                 G_MARKUP_COLLECT_STRING,
848                 "auto-check-timeout", &auto_check_timeout,
849                 G_MARKUP_COLLECT_BOOLEAN |
850                 G_MARKUP_COLLECT_OPTIONAL,
851                 "keep-on-server", NULL,
852                 G_MARKUP_COLLECT_BOOLEAN |
853                 G_MARKUP_COLLECT_OPTIONAL,
854                 "save-passwd", NULL,
855                 G_MARKUP_COLLECT_INVALID);
856
857         if (!success)
858                 return;
859
860         if (auto_check_timeout != NULL)
861                 interval_minutes = strtol (auto_check_timeout, NULL, 10);
862
863         g_key_file_set_boolean (
864                 parse_data->key_file,
865                 E_SOURCE_EXTENSION_REFRESH,
866                 "Enabled", auto_check);
867
868         if (interval_minutes > 0)
869                 g_key_file_set_integer (
870                         parse_data->key_file,
871                         E_SOURCE_EXTENSION_REFRESH,
872                         "IntervalMinutes", interval_minutes);
873 }
874
875 static void
876 migrate_parse_url_rename_params (CamelURL *url)
877 {
878         /* This list includes known URL parameters from built-in providers
879          * in Camel, as well as from evolution-exchange/groupwise/mapi/ews.
880          * Add more as needed. */
881         static struct {
882                 const gchar *url_parameter;
883                 const gchar *property_name;
884         } camel_url_conversion[] = {
885                 { "account_uid",                "account-uid" },
886                 { "ad_auth",                    "gc-auth-method" },
887                 { "ad_browse",                  "gc-allow-browse" },
888                 { "ad_expand_groups",           "gc-expand-groups" },
889                 { "ad_limit",                   "gc-results-limit" },
890                 { "ad_server",                  "gc-server-name" },
891                 { "all_headers",                "fetch-headers" },
892                 { "basic_headers",              "fetch-headers" },
893                 { "cachedconn"                  "concurrent-connections" },
894                 { "check_all",                  "check-all" },
895                 { "check_lsub",                 "check-subscribed" },
896                 { "command",                    "shell-command" },
897                 { "delete_after",               "delete-after-days" },
898                 { "delete_expunged",            "delete-expunged" },
899                 { "disable_extensions",         "disable-extensions" },
900                 { "dotfolders",                 "use-dot-folders" },
901                 { "filter",                     "filter-inbox" },
902                 { "filter_junk",                "filter-junk" },
903                 { "filter_junk_inbox",          "filter-junk-inbox" },
904                 { "folder_hierarchy_relative",  "folder-hierarchy-relative" },
905                 { "imap_custom_headers",        "fetch-headers-extra" },
906                 { "keep_on_server",             "keep-on-server" },
907                 { "oab_offline",                "oab-offline" },
908                 { "oal_selected",               "oal-selected" },
909                 { "offline_sync",               "stay-synchronized" },
910                 { "override_namespace",         "use-namespace" },
911                 { "owa_path",                   "owa-path" },
912                 { "owa_url",                    "owa-url" },
913                 { "password_exp_warn_period",   "password-exp-warn-period" },
914                 { "real_junk_path",             "real-junk-path" },
915                 { "real_trash_path",            "real-trash-path" },
916                 { "show_short_notation",        "short-folder-names" },
917                 { "soap_port",                  "soap-port" },
918                 { "ssl",                        "security-method" },
919                 { "sync_offline",               "stay-synchronized" },
920                 { "use_command",                "use-shell-command" },
921                 { "use_idle",                   "use-idle" },
922                 { "use_lsub",                   "use-subscriptions" },
923                 { "use_qresync",                "use-qresync" },
924                 { "use_ssl",                    "security-method" },
925                 { "xstatus",                    "use-xstatus-headers" }
926         };
927
928         const gchar *param;
929         const gchar *use_param;
930         gint ii;
931
932         for (ii = 0; ii < G_N_ELEMENTS (camel_url_conversion); ii++) {
933                 const gchar *key;
934                 gpointer value;
935
936                 key = camel_url_conversion[ii].url_parameter;
937                 value = g_datalist_get_data (&url->params, key);
938
939                 if (value == NULL)
940                         continue;
941
942                 g_datalist_remove_no_notify (&url->params, key);
943
944                 key = camel_url_conversion[ii].property_name;
945
946                 /* Deal with a few special enum cases where
947                  * the parameter value also needs renamed. */
948
949                 if (strcmp (key, "all_headers") == 0) {
950                         GEnumClass *enum_class;
951                         GEnumValue *enum_value;
952
953                         enum_class = g_type_class_ref (
954                                 CAMEL_TYPE_FETCH_HEADERS_TYPE);
955                         enum_value = g_enum_get_value (
956                                 enum_class, CAMEL_FETCH_HEADERS_ALL);
957                         if (enum_value != NULL) {
958                                 g_free (value);
959                                 value = g_strdup (enum_value->value_nick);
960                         } else
961                                 g_warn_if_reached ();
962                         g_type_class_unref (enum_class);
963                 }
964
965                 if (strcmp (key, "basic_headers") == 0) {
966                         GEnumClass *enum_class;
967                         GEnumValue *enum_value;
968
969                         enum_class = g_type_class_ref (
970                                 CAMEL_TYPE_FETCH_HEADERS_TYPE);
971                         enum_value = g_enum_get_value (
972                                 enum_class, CAMEL_FETCH_HEADERS_BASIC);
973                         if (enum_value != NULL) {
974                                 g_free (value);
975                                 value = g_strdup (enum_value->value_nick);
976                         } else
977                                 g_warn_if_reached ();
978                         g_type_class_unref (enum_class);
979                 }
980
981                 if (strcmp (key, "imap_custom_headers") == 0)
982                         g_strdelimit (value, " ", ',');
983
984                 if (strcmp (key, "security-method") == 0) {
985                         CamelNetworkSecurityMethod method;
986                         GEnumClass *enum_class;
987                         GEnumValue *enum_value;
988
989                         if (strcmp (value, "always") == 0)
990                                 method = CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT;
991                         else if (strcmp (value, "1") == 0)
992                                 method = CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT;
993                         else if (strcmp (value, "when-possible") == 0)
994                                 method = CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT;
995                         else
996                                 method = CAMEL_NETWORK_SECURITY_METHOD_NONE;
997
998                         enum_class = g_type_class_ref (
999                                 CAMEL_TYPE_NETWORK_SECURITY_METHOD);
1000                         enum_value = g_enum_get_value (enum_class, method);
1001                         if (enum_value != NULL) {
1002                                 g_free (value);
1003                                 value = g_strdup (enum_value->value_nick);
1004                         } else
1005                                 g_warn_if_reached ();
1006                         g_type_class_unref (enum_class);
1007                 }
1008
1009                 g_datalist_set_data_full (&url->params, key, value, g_free);
1010         }
1011
1012         /* A few more adjustments...
1013          *
1014          * These are all CAMEL_PROVIDER_CONF_CHECKSPIN settings.  The spin
1015          * button value is bound to "param" and the checkbox state is bound
1016          * to "use-param".  The "use-param" settings are new.  If "param"
1017          * exists but no "use-param", then set "use-param" to "true". */
1018
1019         param = g_datalist_get_data (&url->params, "gc-results-limit");
1020         use_param = g_datalist_get_data (&url->params, "use-gc-results-limit");
1021         if (param != NULL && *param != '\0' && use_param == NULL) {
1022                 g_datalist_set_data_full (
1023                         &url->params, "use-gc-results-limit",
1024                         g_strdup ("true"), (GDestroyNotify) g_free);
1025         }
1026
1027         param = g_datalist_get_data (&url->params, "kerberos");
1028         if (g_strcmp0 (param, "required") == 0) {
1029                 g_datalist_set_data_full (
1030                         &url->params, "kerberos",
1031                         g_strdup ("true"), (GDestroyNotify) g_free);
1032         }
1033
1034         param = g_datalist_get_data (
1035                 &url->params, "password-exp-warn-period");
1036         use_param = g_datalist_get_data (
1037                 &url->params, "use-password-exp-warn-period");
1038         if (param != NULL && *param != '\0' && use_param == NULL) {
1039                 g_datalist_set_data_full (
1040                         &url->params, "use-password-exp-warn-period",
1041                         g_strdup ("true"), (GDestroyNotify) g_free);
1042         }
1043
1044         param = g_datalist_get_data (&url->params, "real-junk-path");
1045         use_param = g_datalist_get_data (&url->params, "use-real-junk-path");
1046         if (param != NULL && *param != '\0' && use_param == NULL) {
1047                 g_datalist_set_data_full (
1048                         &url->params, "use-real-junk-path",
1049                         g_strdup ("true"), (GDestroyNotify) g_free);
1050         }
1051
1052         param = g_datalist_get_data (&url->params, "real-trash-path");
1053         use_param = g_datalist_get_data (&url->params, "use-real-trash-path");
1054         if (param != NULL && *param != '\0' && use_param == NULL) {
1055                 g_datalist_set_data_full (
1056                         &url->params, "use-real-trash-path",
1057                         g_strdup ("true"), (GDestroyNotify) g_free);
1058         }
1059
1060         /* Remove an empty "namespace" parameter (if present) to avoid
1061          * it being converted to "true" in migrate_parse_url_foreach(). */
1062         param = g_datalist_get_data (&url->params, "namespace");
1063         if (param != NULL && *param == '\0')
1064                 g_datalist_remove_data (&url->params, "namespace");
1065 }
1066
1067 static void
1068 migrate_parse_url_foreach (GQuark key_id,
1069                            const gchar *value,
1070                            gpointer user_data)
1071 {
1072         const gchar *param_name;
1073         const gchar *key;
1074
1075         struct {
1076                 GKeyFile *key_file;
1077                 const gchar *group_name;
1078         } *foreach_data = user_data;
1079
1080         g_return_if_fail (value != NULL);
1081
1082         param_name = g_quark_to_string (key_id);
1083         key = e_source_parameter_to_key (param_name);
1084
1085         /* If the value is empty, then the mere
1086          * presence of the parameter implies TRUE. */
1087         if (*value == '\0')
1088                 value = "true";
1089
1090         g_key_file_set_string (
1091                 foreach_data->key_file,
1092                 foreach_data->group_name,
1093                 key, value);
1094 }
1095
1096 static void
1097 migrate_parse_url (ParseData *parse_data,
1098                    GKeyFile *key_file,
1099                    GFile *file,
1100                    const gchar *group_name,
1101                    const gchar *url_string,
1102                    GError **error)
1103 {
1104         CamelURL *url;
1105         GKeyFile *backend_key_file;
1106         GFile *backend_file;
1107         const gchar *value;
1108         gboolean setup_collection;
1109         gchar *uid;
1110
1111         struct {
1112                 GKeyFile *key_file;
1113                 const gchar *group_name;
1114         } foreach_data;
1115
1116         url = camel_url_new (url_string, error);
1117         if (url == NULL || url->protocol == NULL)
1118                 return;
1119
1120         /* Rename URL params as necessary to match
1121          * their ESourceExtension property names. */
1122         migrate_parse_url_rename_params (url);
1123
1124         setup_collection =
1125                 (key_file == parse_data->key_file) &&
1126                 base_uri_is_groupware (url->protocol);
1127
1128         if (setup_collection)
1129                 migrate_setup_collection (parse_data, url);
1130
1131         /* Store backend settings in the collection GKeyFile, if one is
1132          * defined.  Otherwise store them in the GKeyFile we were passed.
1133          * Same goes for the keyring entry, which uses the GFile. */
1134         if (parse_data->collection_key_file != NULL) {
1135                 backend_key_file = parse_data->collection_key_file;
1136                 backend_file = parse_data->collection_file;
1137         } else {
1138                 backend_key_file = key_file;
1139                 backend_file = file;
1140         }
1141
1142         /* This is not a backend setting. */
1143         g_key_file_set_string (
1144                 key_file, group_name,
1145                 "BackendName", url->protocol);
1146
1147         /* Set authentication details. */
1148
1149         if (url->host != NULL)
1150                 g_key_file_set_string (
1151                         backend_key_file,
1152                         E_SOURCE_EXTENSION_AUTHENTICATION,
1153                         "Host", url->host);
1154
1155         if (url->authmech != NULL)
1156                 g_key_file_set_string (
1157                         backend_key_file,
1158                         E_SOURCE_EXTENSION_AUTHENTICATION,
1159                         "Method", url->authmech);
1160
1161         if (url->port > 0)
1162                 g_key_file_set_integer (
1163                         backend_key_file,
1164                         E_SOURCE_EXTENSION_AUTHENTICATION,
1165                         "Port", url->port);
1166
1167         if (url->user != NULL)
1168                 g_key_file_set_string (
1169                         backend_key_file,
1170                         E_SOURCE_EXTENSION_AUTHENTICATION,
1171                         "User", url->user);
1172
1173         /* Pick out particular URL parameters we know about. */
1174
1175         /* If set, this should be "true" or "false",
1176          * but we'll just write it like it's a string. */
1177         value = g_datalist_get_data (&url->params, "stay-synchronized");
1178         if (value != NULL)
1179                 g_key_file_set_string (
1180                         backend_key_file,
1181                         E_SOURCE_EXTENSION_OFFLINE,
1182                         "StaySynchronized", value);
1183         g_datalist_set_data (&url->params, "stay-synchronized", NULL);
1184
1185         value = g_datalist_get_data (&url->params, "security-method");
1186         if (value != NULL)
1187                 g_key_file_set_string (
1188                         backend_key_file,
1189                         E_SOURCE_EXTENSION_SECURITY,
1190                         "Method", value);
1191         g_datalist_set_data (&url->params, "security-method", NULL);
1192
1193         /* If we see a "goa-account-id" parameter, skip the entire
1194          * account and let the online-accounts module recreate it. */
1195         value = g_datalist_get_data (&url->params, "goa-account-id");
1196         if (value != NULL && *value != '\0')
1197                 parse_data->skip = TRUE;
1198
1199         /* The rest of the URL parameters go in the backend group. */
1200
1201         group_name = e_source_camel_get_extension_name (url->protocol);
1202
1203         foreach_data.key_file = backend_key_file;
1204         foreach_data.group_name = group_name;
1205
1206         g_datalist_foreach (
1207                 &url->params, (GDataForeachFunc)
1208                 migrate_parse_url_foreach, &foreach_data);
1209
1210         /* Local providers store their "path" as the url->path */
1211         if (g_strcmp0 (url->protocol, "mh") == 0 ||
1212             g_strcmp0 (url->protocol, "mbox") == 0 ||
1213             g_strcmp0 (url->protocol, "maildir") == 0 ||
1214             g_strcmp0 (url->protocol, "spool") == 0 ||
1215             g_strcmp0 (url->protocol, "spooldir") == 0)
1216                 g_key_file_set_string (
1217                         backend_key_file,
1218                         group_name,
1219                         "Path", url->path);
1220
1221         uid = e_server_side_source_uid_from_file (backend_file, error);
1222
1223         if (uid != NULL) {
1224                 migrate_keyring_entry (
1225                         uid, url->user, url->host, url->protocol);
1226                 g_free (uid);
1227         }
1228
1229         camel_url_free (url);
1230 }
1231
1232 static void
1233 migrate_parse_account_xml_start_element (GMarkupParseContext *context,
1234                                          const gchar *element_name,
1235                                          const gchar **attribute_names,
1236                                          const gchar **attribute_values,
1237                                          gpointer user_data,
1238                                          GError **error)
1239 {
1240         ParseData *parse_data = user_data;
1241
1242         if (g_strcmp0 (element_name, "account") == 0) {
1243                 if (parse_data->state != PARSE_STATE_IN_ACCOUNTS_VALUE)
1244                         goto invalid_content;
1245
1246                 parse_data->state = PARSE_STATE_IN_ACCOUNT;
1247
1248                 migrate_parse_account (
1249                         parse_data,
1250                         element_name,
1251                         attribute_names,
1252                         attribute_values,
1253                         error);
1254
1255                 return;
1256         }
1257
1258         if (g_strcmp0 (element_name, "addr-spec") == 0) {
1259                 if (parse_data->state != PARSE_STATE_IN_IDENTITY)
1260                         goto invalid_content;
1261
1262                 parse_data->state = PARSE_STATE_IN_IDENTITY_ADDR_SPEC;
1263
1264                 return;
1265         }
1266
1267         if (g_strcmp0 (element_name, "auto-bcc") == 0) {
1268                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1269                         goto invalid_content;
1270
1271                 parse_data->state = PARSE_STATE_IN_AUTO_BCC;
1272
1273                 g_markup_collect_attributes (
1274                         element_name,
1275                         attribute_names,
1276                         attribute_values,
1277                         error,
1278                         G_MARKUP_COLLECT_BOOLEAN,
1279                         "always", &parse_data->auto_bcc,
1280                         G_MARKUP_COLLECT_INVALID);
1281
1282                 return;
1283         }
1284
1285         if (g_strcmp0 (element_name, "auto-cc") == 0) {
1286                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1287                         goto invalid_content;
1288
1289                 parse_data->state = PARSE_STATE_IN_AUTO_CC;
1290
1291                 g_markup_collect_attributes (
1292                         element_name,
1293                         attribute_names,
1294                         attribute_values,
1295                         error,
1296                         G_MARKUP_COLLECT_BOOLEAN,
1297                         "always", &parse_data->auto_cc,
1298                         G_MARKUP_COLLECT_INVALID);
1299
1300                 return;
1301         }
1302
1303         if (g_strcmp0 (element_name, "drafts-folder") == 0) {
1304                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1305                         goto invalid_content;
1306
1307                 parse_data->state = PARSE_STATE_IN_DRAFTS_FOLDER;
1308
1309                 return;
1310         }
1311
1312         if (g_strcmp0 (element_name, "encrypt-key-id") == 0) {
1313                 if (parse_data->state != PARSE_STATE_IN_SMIME)
1314                         goto invalid_content;
1315
1316                 parse_data->state = PARSE_STATE_IN_SMIME_ENCRYPT_KEY_ID;
1317
1318                 return;
1319         }
1320
1321         if (g_strcmp0 (element_name, "identity") == 0) {
1322                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1323                         goto invalid_content;
1324
1325                 parse_data->state = PARSE_STATE_IN_IDENTITY;
1326
1327                 return;
1328         }
1329
1330         if (g_strcmp0 (element_name, "key-id") == 0) {
1331                 if (parse_data->state != PARSE_STATE_IN_PGP)
1332                         goto invalid_content;
1333
1334                 parse_data->state = PARSE_STATE_IN_PGP_KEY_ID;
1335
1336                 return;
1337         }
1338
1339         if (g_strcmp0 (element_name, "name") == 0) {
1340                 if (parse_data->state != PARSE_STATE_IN_IDENTITY)
1341                         goto invalid_content;
1342
1343                 parse_data->state = PARSE_STATE_IN_IDENTITY_NAME;
1344
1345                 return;
1346         }
1347
1348         if (g_strcmp0 (element_name, "reply-to") == 0) {
1349                 if (parse_data->state != PARSE_STATE_IN_IDENTITY)
1350                         goto invalid_content;
1351
1352                 parse_data->state = PARSE_STATE_IN_IDENTITY_REPLY_TO;
1353
1354                 return;
1355         }
1356
1357         if (g_strcmp0 (element_name, "organization") == 0) {
1358                 if (parse_data->state != PARSE_STATE_IN_IDENTITY)
1359                         goto invalid_content;
1360
1361                 parse_data->state = PARSE_STATE_IN_IDENTITY_ORGANIZATION;
1362
1363                 return;
1364         }
1365
1366         if (g_strcmp0 (element_name, "pgp") == 0) {
1367                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1368                         goto invalid_content;
1369
1370                 parse_data->state = PARSE_STATE_IN_PGP;
1371
1372                 migrate_parse_pgp (
1373                         parse_data,
1374                         element_name,
1375                         attribute_names,
1376                         attribute_values,
1377                         error);
1378
1379                 return;
1380         }
1381
1382         if (g_strcmp0 (element_name, "receipt-policy") == 0) {
1383                 const gchar *policy;
1384                 gboolean success;
1385
1386                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1387                         goto invalid_content;
1388
1389                 parse_data->state = PARSE_STATE_IN_RECEIPT_POLICY;
1390
1391                 success = g_markup_collect_attributes (
1392                         element_name,
1393                         attribute_names,
1394                         attribute_values,
1395                         error,
1396                         G_MARKUP_COLLECT_STRING,
1397                         "policy", &policy,
1398                         G_MARKUP_COLLECT_INVALID);
1399
1400                 /* The new enum strings match the old ones. */
1401                 if (success && policy != NULL)
1402                         g_key_file_set_string (
1403                                 parse_data->key_file,
1404                                 E_SOURCE_EXTENSION_MDN,
1405                                 "ResponsePolicy", policy);
1406
1407                 return;
1408         }
1409
1410         if (g_strcmp0 (element_name, "recipients") == 0) {
1411                 if (parse_data->state == PARSE_STATE_IN_AUTO_BCC) {
1412                         parse_data->state = PARSE_STATE_IN_AUTO_BCC_RECIPIENTS;
1413                         return;
1414                 }
1415
1416                 if (parse_data->state == PARSE_STATE_IN_AUTO_CC) {
1417                         parse_data->state = PARSE_STATE_IN_AUTO_CC_RECIPIENTS;
1418                         return;
1419                 }
1420
1421                 goto invalid_content;
1422         }
1423
1424         if (g_strcmp0 (element_name, "sent-folder") == 0) {
1425                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1426                         goto invalid_content;
1427
1428                 parse_data->state = PARSE_STATE_IN_SENT_FOLDER;
1429
1430                 return;
1431         }
1432
1433         if (g_strcmp0 (element_name, "signature") == 0) {
1434                 const gchar *uid;
1435                 gboolean success;
1436
1437                 if (parse_data->state != PARSE_STATE_IN_IDENTITY)
1438                         goto invalid_content;
1439
1440                 parse_data->state = PARSE_STATE_IN_IDENTITY_SIGNATURE;
1441
1442                 success = g_markup_collect_attributes (
1443                         element_name,
1444                         attribute_names,
1445                         attribute_values,
1446                         error,
1447                         G_MARKUP_COLLECT_STRING,
1448                         "uid", &uid,
1449                         G_MARKUP_COLLECT_INVALID);
1450
1451                 if (success && uid != NULL)
1452                         g_key_file_set_string (
1453                                 parse_data->identity_key_file,
1454                                 E_SOURCE_EXTENSION_MAIL_IDENTITY,
1455                                 "SignatureUid", uid);
1456
1457                 return;
1458         }
1459
1460         if (g_strcmp0 (element_name, "sign-key-id") == 0) {
1461                 if (parse_data->state != PARSE_STATE_IN_SMIME)
1462                         goto invalid_content;
1463
1464                 parse_data->state = PARSE_STATE_IN_SMIME_SIGN_KEY_ID;
1465
1466                 return;
1467         }
1468
1469         if (g_strcmp0 (element_name, "smime") == 0) {
1470                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1471                         goto invalid_content;
1472
1473                 parse_data->state = PARSE_STATE_IN_SMIME;
1474
1475                 migrate_parse_smime (
1476                         parse_data,
1477                         element_name,
1478                         attribute_names,
1479                         attribute_values,
1480                         error);
1481
1482                 return;
1483         }
1484
1485         if (g_strcmp0 (element_name, "source") == 0) {
1486                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1487                         goto invalid_content;
1488
1489                 parse_data->state = PARSE_STATE_IN_MAIL_SOURCE;
1490
1491                 migrate_parse_mail_source (
1492                         parse_data,
1493                         element_name,
1494                         attribute_names,
1495                         attribute_values,
1496                         error);
1497
1498                 return;
1499         }
1500
1501         if (g_strcmp0 (element_name, "transport") == 0) {
1502                 if (parse_data->state != PARSE_STATE_IN_ACCOUNT)
1503                         goto invalid_content;
1504
1505                 parse_data->state = PARSE_STATE_IN_MAIL_TRANSPORT;
1506
1507                 return;
1508         }
1509
1510         if (g_strcmp0 (element_name, "url") == 0) {
1511                 if (parse_data->state == PARSE_STATE_IN_MAIL_SOURCE) {
1512                         parse_data->state = PARSE_STATE_IN_MAIL_SOURCE_URL;
1513                         return;
1514                 }
1515
1516                 if (parse_data->state == PARSE_STATE_IN_MAIL_TRANSPORT) {
1517                         parse_data->state = PARSE_STATE_IN_MAIL_TRANSPORT_URL;
1518                         return;
1519                 }
1520
1521                 goto invalid_content;
1522         }
1523
1524         g_set_error (
1525                 error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1526                 "Unknown element <%s>", element_name);
1527
1528         return;
1529
1530 invalid_content:
1531
1532         g_set_error (
1533                 error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1534                 "Element <%s> at unexpected location", element_name);
1535 }
1536
1537 static void
1538 migrate_parse_account_xml_end_element (GMarkupParseContext *context,
1539                                        const gchar *element_name,
1540                                        gpointer user_data,
1541                                        GError **error)
1542 {
1543         ParseData *parse_data = user_data;
1544
1545         if (g_strcmp0 (element_name, "account") == 0) {
1546                 if (parse_data->state == PARSE_STATE_IN_ACCOUNT) {
1547                         parse_data->state = PARSE_STATE_IN_ACCOUNTS_VALUE;
1548
1549                         /* Clean up <account> tag data. */
1550
1551                         /* The key file will be NULL if we decided to skip it.
1552                          * e.g. A file with the same UID may already exist. */
1553                         if (parse_data->key_file != NULL) {
1554                                 GError *local_error = NULL;
1555
1556                                 if (!parse_data->skip)
1557                                         migrate_parse_commit_changes (
1558                                                 parse_data->type,
1559                                                 parse_data->file,
1560                                                 parse_data->key_file,
1561                                                 NULL, &local_error);
1562
1563                                 if (local_error != NULL) {
1564                                         g_printerr (
1565                                                 "  FAILED: %s\n",
1566                                                 local_error->message);
1567                                         g_error_free (local_error);
1568                                 }
1569
1570                                 g_key_file_free (parse_data->key_file);
1571                                 parse_data->key_file = NULL;
1572                         }
1573
1574                         /* Same deal for the identity key file. */
1575                         if (parse_data->identity_key_file != NULL) {
1576                                 GError *local_error = NULL;
1577
1578                                 if (!parse_data->skip)
1579                                         migrate_parse_commit_changes (
1580                                                 parse_data->type,
1581                                                 parse_data->identity_file,
1582                                                 parse_data->identity_key_file,
1583                                                 NULL, &local_error);
1584
1585                                 if (local_error != NULL) {
1586                                         g_printerr (
1587                                                 "  FAILED: %s\n",
1588                                                 local_error->message);
1589                                         g_error_free (local_error);
1590                                 }
1591
1592                                 g_key_file_free (parse_data->identity_key_file);
1593                                 parse_data->identity_key_file = NULL;
1594                         }
1595
1596                         /* Same deal for the transport key file. */
1597                         if (parse_data->transport_key_file != NULL) {
1598                                 GError *local_error = NULL;
1599
1600                                 if (!parse_data->skip)
1601                                         migrate_parse_commit_changes (
1602                                                 parse_data->type,
1603                                                 parse_data->transport_file,
1604                                                 parse_data->transport_key_file,
1605                                                 NULL, &local_error);
1606
1607                                 if (local_error != NULL) {
1608                                         g_printerr (
1609                                                 "  FAILED: %s\n",
1610                                                 local_error->message);
1611                                         g_error_free (local_error);
1612                                 }
1613
1614                                 g_key_file_free (parse_data->transport_key_file);
1615                                 parse_data->transport_key_file = NULL;
1616                         }
1617
1618                         /* The collection key file is optional anyway. */
1619                         if (parse_data->collection_key_file != NULL) {
1620                                 GError *local_error = NULL;
1621
1622                                 if (!parse_data->skip)
1623                                         migrate_parse_commit_changes (
1624                                                 parse_data->type,
1625                                                 parse_data->collection_file,
1626                                                 parse_data->collection_key_file,
1627                                                 NULL, &local_error);
1628
1629                                 if (local_error != NULL) {
1630                                         g_printerr (
1631                                                 "  FAILED: %s\n",
1632                                                 local_error->message);
1633                                         g_error_free (local_error);
1634                                 }
1635
1636                                 g_key_file_free (parse_data->collection_key_file);
1637                                 parse_data->collection_key_file = NULL;
1638                         }
1639
1640                         if (parse_data->file != NULL) {
1641                                 g_object_unref (parse_data->file);
1642                                 parse_data->file = NULL;
1643                         }
1644
1645                         if (parse_data->identity_file != NULL) {
1646                                 g_object_unref (parse_data->identity_file);
1647                                 parse_data->identity_file = NULL;
1648                         }
1649
1650                         if (parse_data->transport_file != NULL) {
1651                                 g_object_unref (parse_data->transport_file);
1652                                 parse_data->transport_file = NULL;
1653                         }
1654
1655                         if (parse_data->collection_file != NULL) {
1656                                 g_object_unref (parse_data->collection_file);
1657                                 parse_data->collection_file = NULL;
1658                         }
1659
1660                         parse_data->skip = FALSE;
1661                 }
1662                 return;
1663         }
1664
1665         if (g_strcmp0 (element_name, "addr-spec") == 0) {
1666                 if (parse_data->state == PARSE_STATE_IN_IDENTITY_ADDR_SPEC)
1667                         parse_data->state = PARSE_STATE_IN_IDENTITY;
1668                 return;
1669         }
1670
1671         if (g_strcmp0 (element_name, "auto-bcc") == 0) {
1672                 if (parse_data->state == PARSE_STATE_IN_AUTO_BCC)
1673                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1674                 return;
1675         }
1676
1677         if (g_strcmp0 (element_name, "auto-cc") == 0) {
1678                 if (parse_data->state == PARSE_STATE_IN_AUTO_CC)
1679                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1680                 return;
1681         }
1682
1683         if (g_strcmp0 (element_name, "drafts-folder") == 0) {
1684                 if (parse_data->state == PARSE_STATE_IN_DRAFTS_FOLDER)
1685                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1686                 return;
1687         }
1688
1689         if (g_strcmp0 (element_name, "encrypt-key-id") == 0) {
1690                 if (parse_data->state == PARSE_STATE_IN_SMIME_ENCRYPT_KEY_ID)
1691                         parse_data->state = PARSE_STATE_IN_SMIME;
1692                 return;
1693         }
1694
1695         if (g_strcmp0 (element_name, "identity") == 0) {
1696                 if (parse_data->state == PARSE_STATE_IN_IDENTITY)
1697                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1698                 return;
1699         }
1700
1701         if (g_strcmp0 (element_name, "key-id") == 0) {
1702                 if (parse_data->state == PARSE_STATE_IN_PGP_KEY_ID)
1703                         parse_data->state = PARSE_STATE_IN_PGP;
1704                 return;
1705         }
1706
1707         if (g_strcmp0 (element_name, "name") == 0) {
1708                 if (parse_data->state == PARSE_STATE_IN_IDENTITY_NAME)
1709                         parse_data->state = PARSE_STATE_IN_IDENTITY;
1710                 return;
1711         }
1712
1713         if (g_strcmp0 (element_name, "organization") == 0) {
1714                 if (parse_data->state == PARSE_STATE_IN_IDENTITY_ORGANIZATION)
1715                         parse_data->state = PARSE_STATE_IN_IDENTITY;
1716                 return;
1717         }
1718
1719         if (g_strcmp0 (element_name, "pgp") == 0) {
1720                 if (parse_data->state == PARSE_STATE_IN_PGP)
1721                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1722                 return;
1723         }
1724
1725         if (g_strcmp0 (element_name, "receipt-policy") == 0) {
1726                 if (parse_data->state == PARSE_STATE_IN_RECEIPT_POLICY)
1727                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1728                 return;
1729         }
1730
1731         if (g_strcmp0 (element_name, "recipients") == 0) {
1732                 if (parse_data->state == PARSE_STATE_IN_AUTO_BCC_RECIPIENTS)
1733                         parse_data->state = PARSE_STATE_IN_AUTO_BCC;
1734                 if (parse_data->state == PARSE_STATE_IN_AUTO_CC_RECIPIENTS)
1735                         parse_data->state = PARSE_STATE_IN_AUTO_CC;
1736                 return;
1737         }
1738
1739         if (g_strcmp0 (element_name, "reply-to") == 0) {
1740                 if (parse_data->state == PARSE_STATE_IN_IDENTITY_REPLY_TO)
1741                         parse_data->state = PARSE_STATE_IN_IDENTITY;
1742                 return;
1743         }
1744
1745         if (g_strcmp0 (element_name, "sent-folder") == 0) {
1746                 if (parse_data->state == PARSE_STATE_IN_SENT_FOLDER)
1747                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1748                 return;
1749         }
1750
1751         if (g_strcmp0 (element_name, "signature") == 0) {
1752                 if (parse_data->state == PARSE_STATE_IN_IDENTITY_SIGNATURE)
1753                         parse_data->state = PARSE_STATE_IN_IDENTITY;
1754                 return;
1755         }
1756
1757         if (g_strcmp0 (element_name, "sign-key-id") == 0) {
1758                 if (parse_data->state == PARSE_STATE_IN_SMIME_SIGN_KEY_ID)
1759                         parse_data->state = PARSE_STATE_IN_SMIME;
1760                 return;
1761         }
1762
1763         if (g_strcmp0 (element_name, "smime") == 0) {
1764                 if (parse_data->state == PARSE_STATE_IN_SMIME)
1765                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1766                 return;
1767         }
1768
1769         if (g_strcmp0 (element_name, "source") == 0) {
1770                 if (parse_data->state == PARSE_STATE_IN_MAIL_SOURCE)
1771                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1772                 return;
1773         }
1774
1775         if (g_strcmp0 (element_name, "transport") == 0) {
1776                 if (parse_data->state == PARSE_STATE_IN_MAIL_TRANSPORT)
1777                         parse_data->state = PARSE_STATE_IN_ACCOUNT;
1778                 return;
1779         }
1780
1781         if (g_strcmp0 (element_name, "url") == 0) {
1782                 if (parse_data->state == PARSE_STATE_IN_MAIL_SOURCE_URL)
1783                         parse_data->state = PARSE_STATE_IN_MAIL_SOURCE;
1784                 if (parse_data->state == PARSE_STATE_IN_MAIL_TRANSPORT_URL)
1785                         parse_data->state = PARSE_STATE_IN_MAIL_TRANSPORT;
1786                 return;
1787         }
1788 }
1789
1790 static void
1791 migrate_parse_account_xml_text (GMarkupParseContext *context,
1792                                 const gchar *text,
1793                                 gsize text_len,
1794                                 gpointer user_data,
1795                                 GError **error)
1796 {
1797         ParseData *parse_data = user_data;
1798
1799         switch (parse_data->state) {
1800                 case PARSE_STATE_IN_AUTO_BCC_RECIPIENTS:
1801                         /* Disregard the recipient list if
1802                          * we're not going to auto-BCC them. */
1803                         if (parse_data->auto_bcc)
1804                                 migrate_parse_recipients (
1805                                         parse_data, "Bcc", text);
1806                         break;
1807
1808                 case PARSE_STATE_IN_AUTO_CC_RECIPIENTS:
1809                         /* Disregard the recipient list if
1810                          * we're not going to auto-CC them. */
1811                         if (parse_data->auto_cc)
1812                                 migrate_parse_recipients (
1813                                         parse_data, "Cc", text);
1814                         break;
1815
1816                 case PARSE_STATE_IN_DRAFTS_FOLDER:
1817                         g_key_file_set_string (
1818                                 parse_data->identity_key_file,
1819                                 E_SOURCE_EXTENSION_MAIL_COMPOSITION,
1820                                 "DraftsFolder", text);
1821                         break;
1822
1823                 case PARSE_STATE_IN_SMIME_ENCRYPT_KEY_ID:
1824                         g_key_file_set_string (
1825                                 parse_data->identity_key_file,
1826                                 E_SOURCE_EXTENSION_SMIME,
1827                                 "EncryptionCertificate", text);
1828                         break;
1829
1830                 case PARSE_STATE_IN_IDENTITY_ADDR_SPEC:
1831                         g_key_file_set_string (
1832                                 parse_data->identity_key_file,
1833                                 E_SOURCE_EXTENSION_MAIL_IDENTITY,
1834                                 "Address", text);
1835                         break;
1836
1837                 case PARSE_STATE_IN_IDENTITY_NAME:
1838                         g_key_file_set_string (
1839                                 parse_data->identity_key_file,
1840                                 E_SOURCE_EXTENSION_MAIL_IDENTITY,
1841                                 "Name", text);
1842                         break;
1843
1844                 case PARSE_STATE_IN_IDENTITY_ORGANIZATION:
1845                         g_key_file_set_string (
1846                                 parse_data->identity_key_file,
1847                                 E_SOURCE_EXTENSION_MAIL_IDENTITY,
1848                                 "Organization", text);
1849                         break;
1850
1851                 case PARSE_STATE_IN_IDENTITY_REPLY_TO:
1852                         g_key_file_set_string (
1853                                 parse_data->identity_key_file,
1854                                 E_SOURCE_EXTENSION_MAIL_IDENTITY,
1855                                 "ReplyTo", text);
1856                         break;
1857
1858                 case PARSE_STATE_IN_MAIL_SOURCE_URL:
1859                         migrate_parse_url (
1860                                 parse_data,
1861                                 parse_data->key_file,
1862                                 parse_data->file,
1863                                 E_SOURCE_EXTENSION_MAIL_ACCOUNT,
1864                                 text, error);
1865                         break;
1866
1867                 case PARSE_STATE_IN_MAIL_TRANSPORT_URL:
1868                         migrate_parse_url (
1869                                 parse_data,
1870                                 parse_data->transport_key_file,
1871                                 parse_data->transport_file,
1872                                 E_SOURCE_EXTENSION_MAIL_TRANSPORT,
1873                                 text, error);
1874                         break;
1875
1876                 case PARSE_STATE_IN_PGP_KEY_ID:
1877                         g_key_file_set_string (
1878                                 parse_data->identity_key_file,
1879                                 E_SOURCE_EXTENSION_OPENPGP,
1880                                 "KeyId", text);
1881                         break;
1882
1883                 case PARSE_STATE_IN_SENT_FOLDER:
1884                         g_key_file_set_string (
1885                                 parse_data->identity_key_file,
1886                                 E_SOURCE_EXTENSION_MAIL_SUBMISSION,
1887                                 "SentFolder", text);
1888                         break;
1889
1890                 case PARSE_STATE_IN_SMIME_SIGN_KEY_ID:
1891                         g_key_file_set_string (
1892                                 parse_data->identity_key_file,
1893                                 E_SOURCE_EXTENSION_SMIME,
1894                                 "SigningCertificate", text);
1895                         break;
1896
1897                 default:
1898                         break;
1899         }
1900 }
1901
1902 static GMarkupParser account_xml_parser = {
1903         migrate_parse_account_xml_start_element,
1904         migrate_parse_account_xml_end_element,
1905         migrate_parse_account_xml_text,
1906         NULL,  /* passthrough */
1907         NULL   /* error */
1908 };
1909
1910 static void
1911 migrate_parse_signature (ParseData *parse_data,
1912                          const gchar *element_name,
1913                          const gchar **attribute_names,
1914                          const gchar **attribute_values,
1915                          GError **error)
1916 {
1917         const gchar *uid;
1918         const gchar *name;
1919         const gchar *format;
1920         const gchar *config_dir;
1921         gchar *directory;
1922         gchar *absolute_path;
1923         gboolean autogenerated;
1924         gboolean success;
1925
1926         success = g_markup_collect_attributes (
1927                 element_name,
1928                 attribute_names,
1929                 attribute_values,
1930                 error,
1931                 G_MARKUP_COLLECT_STRING,
1932                 "uid", &uid,
1933                 G_MARKUP_COLLECT_STRING,
1934                 "name", &name,
1935                 G_MARKUP_COLLECT_BOOLEAN,
1936                 "auto", &autogenerated,
1937                 G_MARKUP_COLLECT_STRING |
1938                 G_MARKUP_COLLECT_OPTIONAL,
1939                 "format", &format,
1940                 G_MARKUP_COLLECT_INVALID);
1941
1942         if (!success)
1943                 return;
1944
1945         /* Skip the "autogenerated" signature. */
1946         if (autogenerated)
1947                 return;
1948
1949         parse_data->file = e_server_side_source_new_user_file (uid);
1950
1951         config_dir = e_get_user_config_dir ();
1952         directory = g_build_filename (config_dir, "signatures", NULL);
1953         absolute_path = g_build_filename (directory, uid, NULL);
1954         parse_data->signature_file = g_file_new_for_path (absolute_path);
1955         g_mkdir_with_parents (directory, 0700);
1956         g_free (absolute_path);
1957         g_free (directory);
1958
1959         /* If the file already exists, skip this source.  It may be that we
1960          * already migrated it, in which case we don't want to overwrite it. */
1961         if (g_file_query_exists (parse_data->file, NULL))
1962                 return;
1963
1964         parse_data->key_file = g_key_file_new ();
1965
1966         g_key_file_set_string (
1967                 parse_data->key_file,
1968                 E_SOURCE_GROUP_NAME,
1969                 "DisplayName", name);
1970
1971         g_key_file_set_string (
1972                 parse_data->key_file,
1973                 E_SOURCE_EXTENSION_MAIL_SIGNATURE,
1974                 "MimeType", format);
1975 }
1976
1977 static void
1978 migrate_parse_signature_xml_start_element (GMarkupParseContext *context,
1979                                            const gchar *element_name,
1980                                            const gchar **attribute_names,
1981                                            const gchar **attribute_values,
1982                                            gpointer user_data,
1983                                            GError **error)
1984 {
1985         ParseData *parse_data = user_data;
1986
1987         if (g_strcmp0 (element_name, "filename") == 0) {
1988                 if (parse_data->state != PARSE_STATE_IN_SIGNATURE)
1989                         goto invalid_content;
1990
1991                 parse_data->state = PARSE_STATE_IN_FILENAME;
1992
1993                 g_markup_collect_attributes (
1994                         element_name,
1995                         attribute_names,
1996                         attribute_values,
1997                         error,
1998                         G_MARKUP_COLLECT_BOOLEAN |
1999                         G_MARKUP_COLLECT_OPTIONAL,
2000                         "script", &parse_data->is_script,
2001                         G_MARKUP_COLLECT_INVALID);
2002
2003                 return;
2004         }
2005
2006         if (g_strcmp0 (element_name, "signature") == 0) {
2007                 if (parse_data->state != PARSE_STATE_IN_SIGNATURES_VALUE)
2008                         goto invalid_content;
2009
2010                 parse_data->state = PARSE_STATE_IN_SIGNATURE;
2011
2012                 migrate_parse_signature (
2013                         parse_data,
2014                         element_name,
2015                         attribute_names,
2016                         attribute_values,
2017                         error);
2018
2019                 return;
2020         }
2021
2022         g_set_error (
2023                 error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
2024                 "Unknown element <%s>", element_name);
2025
2026         return;
2027
2028 invalid_content:
2029
2030         g_set_error (
2031                 error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
2032                 "Element <%s> at unexpected location", element_name);
2033 }
2034
2035 static void
2036 migrate_parse_signature_xml_end_element (GMarkupParseContext *context,
2037                                          const gchar *element_name,
2038                                          gpointer user_data,
2039                                          GError **error)
2040 {
2041         ParseData *parse_data = user_data;
2042
2043         if (g_strcmp0 (element_name, "filename") == 0) {
2044                 if (parse_data->state == PARSE_STATE_IN_FILENAME) {
2045                         parse_data->state = PARSE_STATE_IN_SIGNATURE;
2046
2047                         return;
2048                 }
2049         }
2050
2051         if (g_strcmp0 (element_name, "signature") == 0) {
2052                 if (parse_data->state == PARSE_STATE_IN_SIGNATURE) {
2053                         parse_data->state = PARSE_STATE_IN_SIGNATURES_VALUE;
2054
2055                         /* Clean up <signature> tag data. */
2056
2057                         /* The key file will be NULL if we decided to skip it.
2058                          * e.g. A file with the same UID may already exist. */
2059                         if (parse_data->key_file != NULL) {
2060                                 GError *local_error = NULL;
2061
2062                                 if (!parse_data->skip)
2063                                         migrate_parse_commit_changes (
2064                                                 parse_data->type,
2065                                                 parse_data->file,
2066                                                 parse_data->key_file,
2067                                                 NULL, &local_error);
2068
2069                                 if (local_error != NULL) {
2070                                         g_printerr (
2071                                                 "  FAILED: %s\n",
2072                                                 local_error->message);
2073                                         g_error_free (local_error);
2074                                 }
2075
2076                                 g_key_file_free (parse_data->key_file);
2077                                 parse_data->key_file = NULL;
2078                         }
2079
2080                         if (parse_data->file != NULL) {
2081                                 g_object_unref (parse_data->file);
2082                                 parse_data->file = NULL;
2083                         }
2084
2085                         parse_data->skip = FALSE;
2086
2087                         return;
2088                 }
2089         }
2090 }
2091
2092 static void
2093 migrate_parse_signature_xml_text (GMarkupParseContext *context,
2094                                   const gchar *text,
2095                                   gsize text_len,
2096                                   gpointer user_data,
2097                                   GError **error)
2098 {
2099         ParseData *parse_data = user_data;
2100
2101         if (parse_data->state == PARSE_STATE_IN_FILENAME) {
2102                 GFile *old_signature_file;
2103                 GFile *new_signature_file;
2104                 const gchar *data_dir;
2105                 gchar *absolute_path;
2106
2107                 /* Note we're moving the signature files
2108                  * from $XDG_DATA_HOME to $XDG_CONFIG_HOME. */
2109                 data_dir = e_get_user_data_dir ();
2110
2111                 /* Text should be either an absolute file name
2112                  * or a base file name with no path components. */
2113                 if (g_path_is_absolute (text))
2114                         absolute_path = g_strdup (text);
2115                 else
2116                         absolute_path = g_build_filename (
2117                                 data_dir, "signatures", text, NULL);
2118
2119                 old_signature_file = g_file_new_for_path (absolute_path);
2120                 new_signature_file = parse_data->signature_file;
2121                 parse_data->signature_file = NULL;
2122
2123                 /* If the signature is a script, we symlink to it.
2124                  * Otherwise we move and rename the regular file. */
2125                 if (parse_data->is_script)
2126                         g_file_make_symbolic_link (
2127                                 new_signature_file,
2128                                 absolute_path, NULL, error);
2129                 else
2130                         g_file_move (
2131                                 old_signature_file,
2132                                 new_signature_file,
2133                                 G_FILE_COPY_NONE,
2134                                 NULL, NULL, NULL, error);
2135
2136                 g_object_unref (old_signature_file);
2137                 g_object_unref (new_signature_file);
2138                 g_free (absolute_path);
2139         }
2140 }
2141
2142 static GMarkupParser signature_xml_parser = {
2143         migrate_parse_signature_xml_start_element,
2144         migrate_parse_signature_xml_end_element,
2145         migrate_parse_signature_xml_text,
2146         NULL,  /* passthrough */
2147         NULL   /* error */
2148 };
2149
2150 static void
2151 migrate_parse_local_calendar_property (ParseData *parse_data,
2152                                        const gchar *property_name,
2153                                        const gchar *property_value)
2154 {
2155         if (g_strcmp0 (property_name, "custom-file") == 0) {
2156                 gchar *uri;
2157
2158                 /* Property value is a local filename.  Convert it to a
2159                  * "file://" URI.
2160                  *
2161                  * Note: The key is named "CustomFile" instead of, say,
2162                  * "CustomURI" because the corresponding ESourceExtension
2163                  * property is a GFile.  The fact that ESource saves GFile
2164                  * properties as URI strings is an implementation detail. */
2165                 uri = g_filename_to_uri (property_value, NULL, NULL);
2166                 if (uri != NULL) {
2167                         g_key_file_set_string (
2168                                 parse_data->key_file,
2169                                 E_SOURCE_EXTENSION_LOCAL_BACKEND,
2170                                 "CustomFile", uri);
2171                         g_free (uri);
2172                 }
2173         }
2174 }
2175
2176 static void
2177 migrate_parse_local_source (ParseData *parse_data)
2178 {
2179         if (parse_data->type != PARSE_TYPE_ADDRESSBOOK)
2180                 parse_data->property_func =
2181                         migrate_parse_local_calendar_property;
2182
2183         /* Local ADDRESS BOOK Backend has no special properties to parse. */
2184 }
2185
2186 static void
2187 migrate_parse_caldav_property (ParseData *parse_data,
2188                                const gchar *property_name,
2189                                const gchar *property_value)
2190 {
2191         if (g_strcmp0 (property_name, "autoschedule") == 0) {
2192                 g_key_file_set_boolean (
2193                         parse_data->key_file,
2194                         E_SOURCE_EXTENSION_WEBDAV_BACKEND,
2195                         "CalendarAutoSchedule",
2196                         is_true (property_value));
2197
2198         } else if (g_strcmp0 (property_name, "usermail") == 0) {
2199                 g_key_file_set_string (
2200                         parse_data->key_file,
2201                         E_SOURCE_EXTENSION_WEBDAV_BACKEND,
2202                         "EmailAddress", property_value);
2203         }
2204 }
2205
2206 static void
2207 migrate_parse_caldav_source (ParseData *parse_data)
2208 {
2209         if (parse_data->soup_uri->host != NULL)
2210                 g_key_file_set_string (
2211                         parse_data->key_file,
2212                         E_SOURCE_EXTENSION_AUTHENTICATION,
2213                         "Host", parse_data->soup_uri->host);
2214
2215         /* We may override this later if we see an "ssl" property. */
2216         if (parse_data->soup_uri->port == 0)
2217                 parse_data->soup_uri->port = 80;
2218
2219         g_key_file_set_integer (
2220                 parse_data->key_file,
2221                 E_SOURCE_EXTENSION_AUTHENTICATION,
2222                 "Port", parse_data->soup_uri->port);
2223
2224         if (parse_data->soup_uri->user != NULL)
2225                 g_key_file_set_string (
2226                         parse_data->key_file,
2227                         E_SOURCE_EXTENSION_AUTHENTICATION,
2228                         "User", parse_data->soup_uri->user);
2229
2230         if (parse_data->soup_uri->path != NULL)
2231                 g_key_file_set_string (
2232                         parse_data->key_file,
2233                         E_SOURCE_EXTENSION_WEBDAV_BACKEND,
2234                         "ResourcePath", parse_data->soup_uri->path);
2235
2236         parse_data->property_func = migrate_parse_caldav_property;
2237 }
2238
2239 static void
2240 migrate_parse_google_calendar_property (ParseData *parse_data,
2241                                         const gchar *property_name,
2242                                         const gchar *property_value)
2243 {
2244         if (g_strcmp0 (property_name, "username") == 0) {
2245                 gchar *path;
2246
2247                 g_key_file_set_string (
2248                         parse_data->key_file,
2249                         E_SOURCE_EXTENSION_AUTHENTICATION,
2250                         "User", property_value);
2251
2252                 path = g_strdup_printf (
2253                         "/calendar/dav/%s/events", property_value);
2254
2255                 g_key_file_set_string (
2256                         parse_data->key_file,
2257                         E_SOURCE_EXTENSION_WEBDAV_BACKEND,
2258                         "ResourcePath", path);
2259
2260                 g_free (path);
2261         }
2262 }
2263
2264 static void
2265 migrate_parse_google_contacts_property (ParseData *parse_data,
2266                                         const gchar *property_name,
2267                                         const gchar *property_value)
2268 {
2269         if (g_strcmp0 (property_name, "refresh-interval") == 0) {
2270                 guint64 interval_seconds;
2271
2272                 interval_seconds =
2273                         g_ascii_strtoull (property_value, NULL, 10);
2274
2275                 if (interval_seconds >= 60) {
2276                         g_key_file_set_boolean (
2277                                 parse_data->key_file,
2278                                 E_SOURCE_EXTENSION_REFRESH,
2279                                 "Enabled", TRUE);
2280                         g_key_file_set_uint64 (
2281                                 parse_data->key_file,
2282                                 E_SOURCE_EXTENSION_REFRESH,
2283                                 "IntervalMinutes",
2284                                 interval_seconds / 60);
2285                 }
2286
2287         } else if (g_strcmp0 (property_name, "username") == 0) {
2288                 g_key_file_set_string (
2289                         parse_data->key_file,
2290                         E_SOURCE_EXTENSION_AUTHENTICATION,
2291                         "User", property_value);
2292         }
2293 }
2294
2295 static void
2296 migrate_parse_google_source (ParseData *parse_data)
2297 {
2298         if (parse_data->type == PARSE_TYPE_ADDRESSBOOK)
2299                 parse_data->property_func =
2300                         migrate_parse_google_contacts_property;
2301
2302         else {
2303                 g_key_file_set_string (
2304                         parse_data->key_file,
2305                         E_SOURCE_EXTENSION_AUTHENTICATION,
2306                         "Host", "www.google.com");
2307
2308                 g_key_file_set_integer (
2309                         parse_data->key_file,
2310                         E_SOURCE_EXTENSION_AUTHENTICATION,
2311                         "Port", 443);
2312
2313                 g_key_file_set_string (
2314                         parse_data->key_file,
2315                         E_SOURCE_EXTENSION_SECURITY,
2316                         "Method", "tls");
2317
2318                 parse_data->property_func =
2319                         migrate_parse_google_calendar_property;
2320         }
2321 }
2322
2323 static void
2324 migrate_parse_ldap_property (ParseData *parse_data,
2325                              const gchar *property_name,
2326                              const gchar *property_value)
2327 {
2328         if (g_strcmp0 (property_name, "can-browse") == 0) {
2329                 g_key_file_set_boolean (
2330                         parse_data->key_file,
2331                         E_SOURCE_EXTENSION_LDAP_BACKEND,
2332                         "CanBrowse",
2333                         is_true (property_value));
2334
2335         /* This is an integer value, but we can use the string as is. */
2336         } else if (g_strcmp0 (property_name, "limit") == 0) {
2337                 g_key_file_set_string (
2338                         parse_data->key_file,
2339                         E_SOURCE_EXTENSION_LDAP_BACKEND,
2340                         "Limit", property_value);
2341         }
2342 }
2343
2344 static void
2345 migrate_parse_ldap_source (ParseData *parse_data)
2346 {
2347         if (parse_data->soup_uri->host != NULL)
2348                 g_key_file_set_string (
2349                         parse_data->key_file,
2350                         E_SOURCE_EXTENSION_AUTHENTICATION,
2351                         "Host", parse_data->soup_uri->host);
2352
2353         if (parse_data->soup_uri->port != 0)
2354                 g_key_file_set_integer (
2355                         parse_data->key_file,
2356                         E_SOURCE_EXTENSION_AUTHENTICATION,
2357                         "Port", parse_data->soup_uri->port);
2358
2359         if (parse_data->soup_uri->user != NULL)
2360                 g_key_file_set_string (
2361                         parse_data->key_file,
2362                         E_SOURCE_EXTENSION_AUTHENTICATION,
2363                         "User", parse_data->soup_uri->user);
2364
2365         /* Skip the leading slash on the URI path to get the RootDn. */
2366         if (parse_data->soup_uri->path != NULL)
2367                 g_key_file_set_string (
2368                         parse_data->key_file,
2369                         E_SOURCE_EXTENSION_LDAP_BACKEND,
2370                         "RootDn", parse_data->soup_uri->path + 1);
2371
2372         if (g_strcmp0 (parse_data->soup_uri->query, "?sub?") == 0)
2373                 g_key_file_set_string (
2374                         parse_data->key_file,
2375                         E_SOURCE_EXTENSION_LDAP_BACKEND,
2376                         "Scope", "subtree");
2377
2378         if (g_strcmp0 (parse_data->soup_uri->query, "?one?") == 0)
2379                 g_key_file_set_string (
2380                         parse_data->key_file,
2381                         E_SOURCE_EXTENSION_LDAP_BACKEND,
2382                         "Scope", "onelevel");
2383
2384         parse_data->property_func = migrate_parse_ldap_property;
2385 }
2386
2387 static void
2388 migrate_parse_vcf_source (ParseData *parse_data)
2389 {
2390         if (parse_data->soup_uri->path != NULL)
2391                 g_key_file_set_string (
2392                         parse_data->key_file,
2393                         E_SOURCE_EXTENSION_VCF_BACKEND,
2394                         "Path", parse_data->soup_uri->path);
2395
2396         /* VCF Backend has no special properties to parse. */
2397 }
2398
2399 static void
2400 migrate_parse_weather_property (ParseData *parse_data,
2401                                 const gchar *property_name,
2402                                 const gchar *property_value)
2403 {
2404         /* XXX Temperature property was replaced by units... I think. */
2405         if (g_strcmp0 (property_name, "temperature") == 0) {
2406                 gboolean metric;
2407
2408                 metric = (g_strcmp0 (property_value, "fahrenheit") != 0);
2409
2410                 g_key_file_set_string (
2411                         parse_data->key_file,
2412                         E_SOURCE_EXTENSION_WEATHER_BACKEND,
2413                         "Units", metric ? "metric" : "imperial");
2414
2415         } else if (g_strcmp0 (property_name, "units") == 0) {
2416                 gboolean metric;
2417
2418                 metric = (g_strcmp0 (property_value, "metric") == 0);
2419
2420                 g_key_file_set_string (
2421                         parse_data->key_file,
2422                         E_SOURCE_EXTENSION_WEATHER_BACKEND,
2423                         "Units", metric ? "metric" : "imperial");
2424         }
2425 }
2426
2427 static void
2428 migrate_parse_weather_source (ParseData *parse_data)
2429 {
2430         /* Oh man, we actually try to shove a weather location into
2431          * a URI!  The station code winds up as the host component,
2432          * and the location name winds up as the path component. */
2433         if (parse_data->soup_uri->host != NULL) {
2434                 gchar *location;
2435
2436                 if (parse_data->soup_uri->path != NULL)
2437                         location = g_strconcat (
2438                                 parse_data->soup_uri->host,
2439                                 parse_data->soup_uri->path, NULL);
2440                 else
2441                         location = g_strdup (parse_data->soup_uri->host);
2442
2443                 g_key_file_set_string (
2444                         parse_data->key_file,
2445                         E_SOURCE_EXTENSION_WEATHER_BACKEND,
2446                         "Location", location);
2447
2448                 g_free (location);
2449         }
2450
2451
2452         parse_data->property_func = migrate_parse_weather_property;
2453 }
2454
2455 static void
2456 migrate_parse_webcal_source (ParseData *parse_data)
2457 {
2458         if (parse_data->soup_uri->host != NULL)
2459                 g_key_file_set_string (
2460                         parse_data->key_file,
2461                         E_SOURCE_EXTENSION_AUTHENTICATION,
2462                         "Host", parse_data->soup_uri->host);
2463
2464         /* We may override this later if we see an "ssl" property. */
2465         if (parse_data->soup_uri->port == 0)
2466                 parse_data->soup_uri->port = 80;
2467
2468         g_key_file_set_integer (
2469                 parse_data->key_file,
2470                 E_SOURCE_EXTENSION_AUTHENTICATION,
2471                 "Port", parse_data->soup_uri->port);
2472
2473         if (parse_data->soup_uri->user != NULL)
2474                 g_key_file_set_string (
2475                         parse_data->key_file,
2476                         E_SOURCE_EXTENSION_AUTHENTICATION,
2477                         "User", parse_data->soup_uri->user);
2478
2479         if (parse_data->soup_uri->path != NULL)
2480                 g_key_file_set_string (
2481                         parse_data->key_file,
2482                         E_SOURCE_EXTENSION_WEBDAV_BACKEND,
2483                         "ResourcePath", parse_data->soup_uri->path);
2484
2485         /* Webcal Backend has no special properties to parse. */
2486 }
2487
2488 static void
2489 migrate_parse_webdav_property (ParseData *parse_data,
2490                                const gchar *property_name,
2491                                const gchar *property_value)
2492 {
2493         if (g_strcmp0 (property_name, "avoid_ifmatch") == 0) {
2494                 g_key_file_set_boolean (
2495                         parse_data->key_file,
2496                         E_SOURCE_EXTENSION_WEBDAV_BACKEND,
2497                         "AvoidIfmatch",
2498                         is_true (property_value));
2499         }
2500 }
2501
2502 static void
2503 migrate_parse_webdav_source (ParseData *parse_data)
2504 {
2505         if (parse_data->soup_uri->host != NULL)
2506                 g_key_file_set_string (
2507                         parse_data->key_file,
2508                         E_SOURCE_EXTENSION_AUTHENTICATION,
2509                         "Host", parse_data->soup_uri->host);
2510
2511         if (parse_data->soup_uri->port != 0)
2512                 g_key_file_set_integer (
2513                         parse_data->key_file,
2514                         E_SOURCE_EXTENSION_AUTHENTICATION,
2515                         "Port", parse_data->soup_uri->port);
2516
2517         if (parse_data->soup_uri->user != NULL)
2518                 g_key_file_set_string (
2519                         parse_data->key_file,
2520                         E_SOURCE_EXTENSION_AUTHENTICATION,
2521                         "User", parse_data->soup_uri->user);
2522
2523         if (parse_data->soup_uri->path != NULL)
2524                 g_key_file_set_string (
2525                         parse_data->key_file,
2526                         E_SOURCE_EXTENSION_WEBDAV_BACKEND,
2527                         "ResourcePath", parse_data->soup_uri->path);
2528
2529         parse_data->property_func = migrate_parse_webdav_property;
2530 }
2531
2532 static void
2533 migrate_parse_group (ParseData *parse_data,
2534                      const gchar *element_name,
2535                      const gchar **attribute_names,
2536                      const gchar **attribute_values,
2537                      GError **error)
2538 {
2539         const gchar *base_uri;
2540         gboolean success;
2541
2542         success = g_markup_collect_attributes (
2543                 element_name,
2544                 attribute_names,
2545                 attribute_values,
2546                 error,
2547                 G_MARKUP_COLLECT_STRING,
2548                 "uid", NULL,
2549                 G_MARKUP_COLLECT_STRING,
2550                 "name", NULL,
2551                 G_MARKUP_COLLECT_STRING,
2552                 "base_uri", &base_uri,
2553                 G_MARKUP_COLLECT_BOOLEAN |
2554                 G_MARKUP_COLLECT_OPTIONAL,
2555                 "readonly", NULL,
2556                 G_MARKUP_COLLECT_INVALID);
2557
2558         if (!success)
2559                 return;
2560
2561         /* Convert "file://" schemes to "local:". */
2562         if (g_strcmp0 (base_uri, "file://") == 0)
2563                 base_uri = "local:";
2564
2565         parse_data->base_uri = g_strdup (base_uri);
2566 }
2567
2568 static void
2569 migrate_parse_source (ParseData *parse_data,
2570                       const gchar *element_name,
2571                       const gchar **attribute_names,
2572                       const gchar **attribute_values,
2573                       GError **error)
2574 {
2575         const gchar *uid;
2576         const gchar *name;
2577         const gchar *color_spec;
2578         const gchar *group_name;
2579         const gchar *absolute_uri;
2580         const gchar *relative_uri;
2581         gchar *backend_name;
2582         gchar *parent_name;
2583         gchar *uri_string;
2584         gchar *cp;
2585         gboolean success;
2586         gboolean is_google_calendar;
2587
2588         success = g_markup_collect_attributes (
2589                 element_name,
2590                 attribute_names,
2591                 attribute_values,
2592                 error,
2593                 G_MARKUP_COLLECT_STRING,
2594                 "uid", &uid,
2595                 G_MARKUP_COLLECT_STRING,
2596                 "name", &name,
2597                 G_MARKUP_COLLECT_STRING |
2598                 G_MARKUP_COLLECT_OPTIONAL,
2599                 "color_spec", &color_spec,
2600                 G_MARKUP_COLLECT_STRING,
2601                 "relative_uri", &relative_uri,
2602                 G_MARKUP_COLLECT_STRING |
2603                 G_MARKUP_COLLECT_OPTIONAL,
2604                 "uri", &absolute_uri,
2605                 G_MARKUP_COLLECT_INVALID);
2606
2607         if (!success)
2608                 return;
2609
2610         /* Don't try and migrate the "system" sources, as
2611          * we'll defer to the built-in "system-*" key files. */
2612         if (g_strcmp0 (relative_uri, "system") == 0)
2613                 return;
2614
2615         /* Also skip any sources with a "contacts://" base URI, which
2616          * should just be "Birthdays & Anniversaries".  We'll reset to
2617          * the built-in key file. */
2618         if (g_strcmp0 (parse_data->base_uri, "contacts://") == 0)
2619                 return;
2620
2621         /* Also skip any sources for groupware extensions, as these are
2622          * no longer saved to disk.  We do have a mechanism in place for
2623          * remembering the UIDs of memory-only sources so that cached
2624          * data can be reused, but let's not bother with it here.  Let
2625          * the sources be set up fresh. */
2626         if (base_uri_is_groupware (parse_data->base_uri))
2627                 return;
2628
2629         switch (parse_data->type) {
2630                 case PARSE_TYPE_ADDRESSBOOK:
2631                         group_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
2632                         break;
2633                 case PARSE_TYPE_CALENDAR:
2634                         group_name = E_SOURCE_EXTENSION_CALENDAR;
2635                         break;
2636                 case PARSE_TYPE_TASKS:
2637                         group_name = E_SOURCE_EXTENSION_TASK_LIST;
2638                         break;
2639                 case PARSE_TYPE_MEMOS:
2640                         group_name = E_SOURCE_EXTENSION_MEMO_LIST;
2641                         break;
2642                 default:
2643                         g_return_if_reached ();
2644         }
2645
2646         parse_data->file = e_server_side_source_new_user_file (uid);
2647
2648         /* If the file already exists, skip this source.  It may be that we
2649          * already migrated it, in which case we don't want to overwrite it. */
2650         if (g_file_query_exists (parse_data->file, NULL))
2651                 return;
2652
2653         parse_data->key_file = g_key_file_new ();
2654
2655         /* Trim ':' or '://' off the base_uri to get the backend name. */
2656         backend_name = g_strdup (parse_data->base_uri);
2657         if ((cp = strchr (backend_name, ':')) != NULL)
2658                 *cp = '\0';
2659
2660         /* The parent name is generally the backend name + "-stub". */
2661         parent_name = g_strdup_printf ("%s-stub", backend_name);
2662
2663         g_key_file_set_string (
2664                 parse_data->key_file,
2665                 E_SOURCE_GROUP_NAME,
2666                 "DisplayName", name);
2667
2668         g_key_file_set_string (
2669                 parse_data->key_file,
2670                 E_SOURCE_GROUP_NAME,
2671                 "Parent", parent_name);
2672
2673         if (color_spec != NULL)
2674                 g_key_file_set_string (
2675                         parse_data->key_file, group_name,
2676                         "Color", color_spec);
2677
2678         is_google_calendar =
2679                 (parse_data->type == PARSE_TYPE_CALENDAR) &&
2680                 (g_strcmp0 (parse_data->base_uri, "google://") == 0);
2681
2682         /* For Google Calendar sources we override the backend name. */
2683         if (is_google_calendar)
2684                 g_key_file_set_string (
2685                         parse_data->key_file, group_name,
2686                         "BackendName", "caldav");
2687         else
2688                 g_key_file_set_string (
2689                         parse_data->key_file, group_name,
2690                         "BackendName", backend_name);
2691
2692         g_free (backend_name);
2693         g_free (parent_name);
2694
2695         /* Prefer absolute URIs over relative URIs.  All these
2696          * other strange rules are for backward-compatibility. */
2697         if (absolute_uri != NULL)
2698                 uri_string = g_strdup (absolute_uri);
2699         else if (g_str_has_suffix (parse_data->base_uri, "/"))
2700                 uri_string = g_strconcat (
2701                         parse_data->base_uri, relative_uri, NULL);
2702         else if (g_strcmp0 (parse_data->base_uri, "local:") == 0)
2703                 uri_string = g_strconcat (
2704                         parse_data->base_uri, relative_uri, NULL);
2705         else
2706                 uri_string = g_strconcat (
2707                         parse_data->base_uri, "/", relative_uri, NULL);
2708
2709         parse_data->soup_uri = soup_uri_new (uri_string);
2710
2711         /* Mangle the URI to not contain invalid characters.  We'll need
2712          * this later to rename the source's cache and data directories. */
2713         parse_data->mangled_uri = g_strdelimit (uri_string, ":/", '_');
2714
2715         /* g_strdelimit() modifies the input string in place, so ParseData
2716          * now owns 'uri_string'.  Clear the pointer to emphasize that. */
2717         uri_string = NULL;
2718
2719         if (parse_data->soup_uri == NULL) {
2720                 g_warning (
2721                         "  Failed to parse source URI: %s",
2722                         (absolute_uri != NULL) ? absolute_uri : relative_uri);
2723                 g_key_file_free (parse_data->key_file);
2724                 parse_data->key_file = NULL;
2725                 return;
2726         }
2727
2728         if (g_strcmp0 (parse_data->base_uri, "local:") == 0)
2729                 migrate_parse_local_source (parse_data);
2730
2731         else if (g_strcmp0 (parse_data->base_uri, "caldav://") == 0)
2732                 migrate_parse_caldav_source (parse_data);
2733
2734         else if (g_strcmp0 (parse_data->base_uri, "google://") == 0)
2735                 migrate_parse_google_source (parse_data);
2736
2737         else if (g_strcmp0 (parse_data->base_uri, "ldap://") == 0)
2738                 migrate_parse_ldap_source (parse_data);
2739
2740         else if (g_strcmp0 (parse_data->base_uri, "vcf://") == 0)
2741                 migrate_parse_vcf_source (parse_data);
2742
2743         else if (g_strcmp0 (parse_data->base_uri, "weather://") == 0)
2744                 migrate_parse_weather_source (parse_data);
2745
2746         else if (g_strcmp0 (parse_data->base_uri, "webcal://") == 0)
2747                 migrate_parse_webcal_source (parse_data);
2748
2749         else if (g_strcmp0 (parse_data->base_uri, "webdav://") == 0)
2750                 migrate_parse_webdav_source (parse_data);
2751
2752         migrate_keyring_entry (
2753                 uid,
2754                 parse_data->soup_uri->user,
2755                 parse_data->soup_uri->host,
2756                 parse_data->soup_uri->scheme);
2757 }
2758
2759 static void
2760 migrate_parse_property (ParseData *parse_data,
2761                         const gchar *element_name,
2762                         const gchar **attribute_names,
2763                         const gchar **attribute_values,
2764                         GError **error)
2765 {
2766         const gchar *property_name;
2767         const gchar *property_value;
2768         gboolean success;
2769
2770         success = g_markup_collect_attributes (
2771                 element_name,
2772                 attribute_names,
2773                 attribute_values,
2774                 error,
2775                 G_MARKUP_COLLECT_STRING,
2776                 "name", &property_name,
2777                 G_MARKUP_COLLECT_STRING,
2778                 "value", &property_value,
2779                 G_MARKUP_COLLECT_INVALID);
2780
2781         if (!success)
2782                 return;
2783
2784         if (g_strcmp0 (property_name, "alarm") == 0) {
2785                 g_key_file_set_boolean (
2786                         parse_data->key_file,
2787                         E_SOURCE_EXTENSION_ALARMS,
2788                         "IncludeMe",
2789                         is_true (property_value));
2790
2791         } else if (g_strcmp0 (property_name, "auth") == 0) {
2792                 if (is_true (property_value))
2793                         g_key_file_set_string (
2794                                 parse_data->key_file,
2795                                 E_SOURCE_EXTENSION_AUTHENTICATION,
2796                                 "Method", "plain/password");
2797                 else if (is_false (property_value))
2798                         g_key_file_set_string (
2799                                 parse_data->key_file,
2800                                 E_SOURCE_EXTENSION_AUTHENTICATION,
2801                                 "Method", "none");
2802                 else
2803                         g_key_file_set_string (
2804                                 parse_data->key_file,
2805                                 E_SOURCE_EXTENSION_AUTHENTICATION,
2806                                 "Method", property_value);
2807
2808         } else if (g_strcmp0 (property_name, "completion") == 0) {
2809                 g_key_file_set_boolean (
2810                         parse_data->key_file,
2811                         E_SOURCE_EXTENSION_AUTOCOMPLETE,
2812                         "IncludeMe",
2813                         is_true (property_value));
2814
2815         /* If we see a "goa-account-id" property, skip the entire
2816          * source and let the online-accounts module recreate it. */
2817         } else if (g_strcmp0 (property_name, "goa-account-id") == 0) {
2818                 parse_data->skip = TRUE;
2819
2820         } else if (g_strcmp0 (property_name, "last-notified") == 0) {
2821                 g_key_file_set_string (
2822                         parse_data->key_file,
2823                         E_SOURCE_EXTENSION_ALARMS,
2824                         "LastNotified", property_value);
2825
2826         } else if (g_strcmp0 (property_name, "offline_sync") == 0) {
2827                 g_key_file_set_boolean (
2828                         parse_data->key_file,
2829                         E_SOURCE_EXTENSION_OFFLINE,
2830                         "StaySynchronized",
2831                         is_true (property_value));
2832
2833         } else if (g_strcmp0 (property_name, "refresh") == 0) {
2834                 g_key_file_set_boolean (
2835                         parse_data->key_file,
2836                         E_SOURCE_EXTENSION_REFRESH,
2837                         "Enabled", TRUE);
2838                 g_key_file_set_string (
2839                         parse_data->key_file,
2840                         E_SOURCE_EXTENSION_REFRESH,
2841                         "IntervalMinutes", property_value);
2842
2843         } else if (g_strcmp0 (property_name, "remember_password") == 0) {
2844                 g_key_file_set_boolean (
2845                         parse_data->key_file,
2846                         E_SOURCE_EXTENSION_AUTHENTICATION,
2847                         "RememberPassword",
2848                         is_true (property_value));
2849
2850         } else if (g_strcmp0 (property_name, "ssl") == 0) {
2851                 if (is_true (property_value))
2852                         g_key_file_set_string (
2853                                 parse_data->key_file,
2854                                 E_SOURCE_EXTENSION_SECURITY,
2855                                 "Method", "tls");
2856                 else if (is_false (property_value))
2857                         g_key_file_set_string (
2858                                 parse_data->key_file,
2859                                 E_SOURCE_EXTENSION_SECURITY,
2860                                 "Method", "none");
2861
2862                 /* These next two are LDAP-specific. */
2863                 else if (g_strcmp0 (property_value, "always") == 0)
2864                         g_key_file_set_string (
2865                                 parse_data->key_file,
2866                                 E_SOURCE_EXTENSION_SECURITY,
2867                                 "Method", "starttls");
2868                 else if (g_strcmp0 (property_value, "whenever_possible") == 0)
2869                         g_key_file_set_string (
2870                                 parse_data->key_file,
2871                                 E_SOURCE_EXTENSION_SECURITY,
2872                                 "Method", "ldaps");
2873
2874                 /* For WebDAV-based backends we set the port to 80
2875                  * (http://) by default.  If we see that and we're
2876                  * using a secure connection, bump the port to 443
2877                  * (https://). */
2878                 if (parse_data->soup_uri->port == 80)
2879                         if (is_true (property_value))
2880                                 g_key_file_set_integer (
2881                                         parse_data->key_file,
2882                                         E_SOURCE_EXTENSION_AUTHENTICATION,
2883                                         "Port", 443);
2884
2885         } else if (g_strcmp0 (property_name, "use_ssl") == 0) {
2886                 g_key_file_set_string (
2887                         parse_data->key_file,
2888                         E_SOURCE_EXTENSION_SECURITY,
2889                         "Method",
2890                         is_true (property_value) ?
2891                         "tls" : "none");
2892
2893                 /* For WebDAV-based backends we set the port to 80
2894                  * (http://) by default.  If we see that and we're
2895                  * using a secure connection, bump the port to 443
2896                  * (https://). */
2897                 if (parse_data->soup_uri->port == 80)
2898                         if (is_true (property_value))
2899                                 g_key_file_set_integer (
2900                                         parse_data->key_file,
2901                                         E_SOURCE_EXTENSION_AUTHENTICATION,
2902                                         "Port", 443);
2903
2904         } else if (g_strcmp0 (property_name, "use-in-contacts-calendar") == 0) {
2905                 g_key_file_set_boolean (
2906                         parse_data->key_file,
2907                         E_SOURCE_EXTENSION_CONTACTS_BACKEND,
2908                         "IncludeMe",
2909                         is_true (property_value));
2910
2911         } else if (parse_data->property_func != NULL) {
2912                 parse_data->property_func (
2913                         parse_data, property_name, property_value);
2914         }
2915 }
2916
2917 static void
2918 migrate_parse_source_xml_start_element (GMarkupParseContext *context,
2919                                         const gchar *element_name,
2920                                         const gchar **attribute_names,
2921                                         const gchar **attribute_values,
2922                                         gpointer user_data,
2923                                         GError **error)
2924 {
2925         ParseData *parse_data = user_data;
2926
2927         if (g_strcmp0 (element_name, "group") == 0) {
2928                 if (parse_data->state != PARSE_STATE_IN_SOURCES_VALUE)
2929                         goto invalid_content;
2930
2931                 parse_data->state = PARSE_STATE_IN_GROUP;
2932
2933                 migrate_parse_group (
2934                         parse_data,
2935                         element_name,
2936                         attribute_names,
2937                         attribute_values,
2938                         error);
2939
2940                 return;
2941         }
2942
2943         if (g_strcmp0 (element_name, "source") == 0) {
2944                 if (parse_data->state != PARSE_STATE_IN_GROUP)
2945                         goto invalid_content;
2946
2947                 parse_data->state = PARSE_STATE_IN_SOURCE;
2948
2949                 migrate_parse_source (
2950                         parse_data,
2951                         element_name,
2952                         attribute_names,
2953                         attribute_values,
2954                         error);
2955
2956                 return;
2957         }
2958
2959         if (g_strcmp0 (element_name, "properties") == 0) {
2960                 /* Disregard group properties, we're only
2961                  * interested in source properties. */
2962                 if (parse_data->state == PARSE_STATE_IN_GROUP)
2963                         return;
2964
2965                 if (parse_data->state != PARSE_STATE_IN_SOURCE)
2966                         goto invalid_content;
2967
2968                 parse_data->state = PARSE_STATE_IN_PROPERTIES;
2969
2970                 return;
2971         }
2972
2973         if (g_strcmp0 (element_name, "property") == 0) {
2974                 /* Disregard group properties, we're only
2975                  * interested in source properties. */
2976                 if (parse_data->state == PARSE_STATE_IN_GROUP)
2977                         return;
2978
2979                 if (parse_data->state != PARSE_STATE_IN_PROPERTIES)
2980                         goto invalid_content;
2981
2982                 /* The key file will be NULL if we decided to skip it.
2983                  * e.g. A file with the same UID may already exist. */
2984                 if (parse_data->key_file != NULL)
2985                         migrate_parse_property (
2986                                 parse_data,
2987                                 element_name,
2988                                 attribute_names,
2989                                 attribute_values,
2990                                 error);
2991
2992                 return;
2993         }
2994
2995         g_set_error (
2996                 error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
2997                 "Unknown element <%s>", element_name);
2998
2999         return;
3000
3001 invalid_content:
3002
3003         g_set_error (
3004                 error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
3005                 "Element <%s> at unexpected location", element_name);
3006 }
3007
3008 static void
3009 migrate_parse_source_xml_end_element (GMarkupParseContext *context,
3010                                       const gchar *element_name,
3011                                       gpointer user_data,
3012                                       GError **error)
3013 {
3014         ParseData *parse_data = user_data;
3015
3016         if (g_strcmp0 (element_name, "group") == 0) {
3017                 if (parse_data->state == PARSE_STATE_IN_GROUP) {
3018                         parse_data->state = PARSE_STATE_IN_SOURCES_VALUE;
3019
3020                         /* Clean up <group> tag data. */
3021
3022                         g_free (parse_data->base_uri);
3023                         parse_data->base_uri = NULL;
3024                 }
3025                 return;
3026         }
3027
3028         if (g_strcmp0 (element_name, "source") == 0) {
3029                 if (parse_data->state == PARSE_STATE_IN_SOURCE) {
3030                         parse_data->state = PARSE_STATE_IN_GROUP;
3031
3032                         /* Clean up <source> tag data. */
3033
3034                         /* The key file will be NULL if we decided to skip it.
3035                          * e.g. A file with the same UID may already exist. */
3036                         if (parse_data->key_file != NULL) {
3037                                 GError *local_error = NULL;
3038
3039                                 if (!parse_data->skip)
3040                                         migrate_parse_commit_changes (
3041                                                 parse_data->type,
3042                                                 parse_data->file,
3043                                                 parse_data->key_file,
3044                                                 parse_data->mangled_uri,
3045                                                 &local_error);
3046
3047                                 if (local_error != NULL) {
3048                                         g_printerr (
3049                                                 "  FAILED: %s\n",
3050                                                 local_error->message);
3051                                         g_error_free (local_error);
3052                                 }
3053
3054                                 g_key_file_free (parse_data->key_file);
3055                                 parse_data->key_file = NULL;
3056                         }
3057
3058                         if (parse_data->file != NULL) {
3059                                 g_object_unref (parse_data->file);
3060                                 parse_data->file = NULL;
3061                         }
3062
3063                         g_free (parse_data->mangled_uri);
3064                         parse_data->mangled_uri = NULL;
3065
3066                         if (parse_data->soup_uri != NULL) {
3067                                 soup_uri_free (parse_data->soup_uri);
3068                                 parse_data->soup_uri = NULL;
3069                         }
3070
3071                         parse_data->property_func = NULL;
3072
3073                         parse_data->skip = FALSE;
3074                 }
3075                 return;
3076         }
3077
3078         if (g_strcmp0 (element_name, "properties") == 0) {
3079                 if (parse_data->state == PARSE_STATE_IN_PROPERTIES)
3080                         parse_data->state = PARSE_STATE_IN_SOURCE;
3081                 return;
3082         }
3083 }
3084
3085 static GMarkupParser source_xml_parser = {
3086         migrate_parse_source_xml_start_element,
3087         migrate_parse_source_xml_end_element,
3088         NULL,  /* text */
3089         NULL,  /* passthrough */
3090         NULL   /* error */
3091 };
3092
3093 static void
3094 migrate_parse_gconf_xml_start_element (GMarkupParseContext *context,
3095                                        const gchar *element_name,
3096                                        const gchar **attribute_names,
3097                                        const gchar **attribute_values,
3098                                        gpointer user_data,
3099                                        GError **error)
3100 {
3101         ParseData *parse_data = user_data;
3102
3103         if (g_strcmp0 (element_name, "gconf") == 0) {
3104                 if (parse_data->state != PARSE_STATE_INITIAL)
3105                         goto invalid_content;
3106
3107                 parse_data->state = PARSE_STATE_IN_GCONF;
3108
3109                 return;
3110         }
3111
3112         if (g_strcmp0 (element_name, "entry") == 0) {
3113                 const gchar *name;
3114                 gboolean success;
3115
3116                 if (parse_data->state != PARSE_STATE_IN_GCONF)
3117                         goto invalid_content;
3118
3119                 success = g_markup_collect_attributes (
3120                         element_name,
3121                         attribute_names,
3122                         attribute_values,
3123                         error,
3124                         G_MARKUP_COLLECT_STRING,
3125                         "name", &name,
3126                         G_MARKUP_COLLECT_STRING,
3127                         "mtime", NULL,
3128                         G_MARKUP_COLLECT_STRING |
3129                         G_MARKUP_COLLECT_OPTIONAL,
3130                         "type", NULL,
3131                         G_MARKUP_COLLECT_STRING |
3132                         G_MARKUP_COLLECT_OPTIONAL,
3133                         "ltype", NULL,
3134                         G_MARKUP_COLLECT_STRING |
3135                         G_MARKUP_COLLECT_OPTIONAL,
3136                         "schema", NULL,
3137                         G_MARKUP_COLLECT_STRING |
3138                         G_MARKUP_COLLECT_OPTIONAL,
3139                         "value", NULL,
3140                         G_MARKUP_COLLECT_INVALID);
3141
3142                 if (success && g_strcmp0 (name, "accounts") == 0)
3143                         parse_data->state = PARSE_STATE_IN_ACCOUNTS_ENTRY;
3144
3145                 if (success && g_strcmp0 (name, "signatues") == 0)
3146                         parse_data->state = PARSE_STATE_IN_SIGNATURES_ENTRY;
3147
3148                 if (success && g_strcmp0 (name, "sources") == 0)
3149                         parse_data->state = PARSE_STATE_IN_SOURCES_ENTRY;
3150
3151                 return;
3152         }
3153
3154         if (g_strcmp0 (element_name, "li") == 0)
3155                 return;
3156
3157         if (g_strcmp0 (element_name, "stringvalue") == 0) {
3158                 if (parse_data->state == PARSE_STATE_IN_ACCOUNTS_ENTRY)
3159                         parse_data->state = PARSE_STATE_IN_ACCOUNTS_VALUE;
3160
3161                 if (parse_data->state == PARSE_STATE_IN_SIGNATURES_ENTRY)
3162                         parse_data->state = PARSE_STATE_IN_SIGNATURES_VALUE;
3163
3164                 if (parse_data->state == PARSE_STATE_IN_SOURCES_ENTRY)
3165                         parse_data->state = PARSE_STATE_IN_SOURCES_VALUE;
3166
3167                 return;
3168         }
3169
3170         g_set_error (
3171                 error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
3172                 "Unknown element <%s>", element_name);
3173
3174         return;
3175
3176 invalid_content:
3177
3178         g_set_error (
3179                 error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
3180                 "Element <%s> at unexpected location", element_name);
3181 }
3182
3183 static void
3184 migrate_parse_gconf_xml_end_element (GMarkupParseContext *context,
3185                                      const gchar *element_name,
3186                                      gpointer user_data,
3187                                      GError **error)
3188 {
3189         ParseData *parse_data = user_data;
3190
3191         if (g_strcmp0 (element_name, "gconf") == 0) {
3192                 if (parse_data->state == PARSE_STATE_IN_GCONF)
3193                         parse_data->state = PARSE_STATE_INITIAL;
3194
3195                 return;
3196         }
3197
3198         if (g_strcmp0 (element_name, "entry") == 0) {
3199                 if (parse_data->state == PARSE_STATE_IN_ACCOUNTS_ENTRY)
3200                         parse_data->state = PARSE_STATE_IN_GCONF;
3201
3202                 if (parse_data->state == PARSE_STATE_IN_SIGNATURES_ENTRY)
3203                         parse_data->state = PARSE_STATE_IN_GCONF;
3204
3205                 if (parse_data->state == PARSE_STATE_IN_SOURCES_ENTRY)
3206                         parse_data->state = PARSE_STATE_IN_GCONF;
3207
3208                 return;
3209         }
3210
3211         if (g_strcmp0 (element_name, "stringvalue") == 0) {
3212                 if (parse_data->state == PARSE_STATE_IN_ACCOUNTS_VALUE)
3213                         parse_data->state = PARSE_STATE_IN_ACCOUNTS_ENTRY;
3214
3215                 if (parse_data->state == PARSE_STATE_IN_SIGNATURES_VALUE)
3216                         parse_data->state = PARSE_STATE_IN_SIGNATURES_ENTRY;
3217
3218                 if (parse_data->state == PARSE_STATE_IN_SOURCES_VALUE)
3219                         parse_data->state = PARSE_STATE_IN_SOURCES_ENTRY;
3220
3221                 return;
3222         }
3223 }
3224
3225 static void
3226 migrate_parse_gconf_xml_text (GMarkupParseContext *context,
3227                               const gchar *text,
3228                               gsize length,
3229                               gpointer user_data,
3230                               GError **error)
3231 {
3232         ParseData *parse_data = user_data;
3233
3234         /* The account and signature data is encoded XML stuffed into
3235          * GConf XML (yuck!).  Fortunately GMarkupParseContext decodes
3236          * the XML for us, so we just have to feed it to a nested
3237          * GMarkupParseContext. */
3238
3239         switch (parse_data->state) {
3240                 case PARSE_STATE_IN_ACCOUNTS_VALUE:
3241                         context = g_markup_parse_context_new (
3242                                 &account_xml_parser, 0, parse_data, NULL);
3243                         break;
3244
3245                 case PARSE_STATE_IN_SIGNATURES_VALUE:
3246                         context = g_markup_parse_context_new (
3247                                 &signature_xml_parser, 0, parse_data, NULL);
3248                         break;
3249
3250                 case PARSE_STATE_IN_SOURCES_VALUE:
3251                         context = g_markup_parse_context_new (
3252                                 &source_xml_parser, 0, parse_data, NULL);
3253                         break;
3254
3255                 default:
3256                         return;
3257         }
3258
3259         if (g_markup_parse_context_parse (context, text, length, error))
3260                 g_markup_parse_context_end_parse (context, error);
3261
3262         g_markup_parse_context_free (context);
3263 }
3264
3265 static GMarkupParser gconf_xml_parser = {
3266         migrate_parse_gconf_xml_start_element,
3267         migrate_parse_gconf_xml_end_element,
3268         migrate_parse_gconf_xml_text,
3269         NULL,  /* passthrough */
3270         NULL   /* error */
3271 };
3272
3273 static gboolean
3274 migrate_parse_gconf_xml (ParseType parse_type,
3275                          const gchar *contents,
3276                          gsize length,
3277                          GError **error)
3278 {
3279         GMarkupParseContext *context;
3280         ParseData *parse_data;
3281         gboolean success = FALSE;
3282
3283         parse_data = parse_data_new (parse_type);
3284
3285         context = g_markup_parse_context_new (
3286                 &gconf_xml_parser, 0, parse_data,
3287                 (GDestroyNotify) parse_data_free);
3288
3289         if (g_markup_parse_context_parse (context, contents, length, error))
3290                 if (g_markup_parse_context_end_parse (context, error))
3291                         success = TRUE;
3292
3293         g_markup_parse_context_free (context);
3294
3295         return success;
3296 }
3297
3298 static void
3299 migrate_remove_gconf_xml (const gchar *gconf_key,
3300                           const gchar *gconf_xml)
3301 {
3302         /* Remove the GConf string list so the user is not haunted by
3303          * old data sources being resurrected from leftover GConf data.
3304          * Also delete the %gconf.xml file itself.  If gconfd is running
3305          * then it will just recreate the file from memory when it exits
3306          * (which is why we invoke gconftool-2), otherwise the file will
3307          * stay deleted. */
3308
3309         gchar *path_to_program;
3310
3311         path_to_program = g_find_program_in_path ("gconftool-2");
3312
3313         if (path_to_program != NULL) {
3314                 gchar *command_line;
3315                 GError *error = NULL;
3316
3317                 command_line = g_strjoin (
3318                         " ",
3319                         path_to_program,
3320                         "--set",
3321                         "--type=list",
3322                         "--list-type=string",
3323                         gconf_key, "[]", NULL);
3324
3325                 /* We don't really care if the command worked or not,
3326                  * just check that the program got spawned successfully. */
3327                 if (!g_spawn_command_line_async (command_line, &error)) {
3328                         g_printerr (
3329                                 "Failed to spawn '%s': %s\n",
3330                                 path_to_program, error->message);
3331                         g_error_free (error);
3332                 }
3333
3334                 g_free (path_to_program);
3335                 g_free (command_line);
3336         }
3337
3338         if (g_file_test (gconf_xml, G_FILE_TEST_IS_REGULAR)) {
3339                 if (g_remove (gconf_xml) == -1) {
3340                         g_printerr (
3341                                 "Failed to remove '%s': %s\n",
3342                                 gconf_xml, g_strerror (errno));
3343                 }
3344         }
3345 }
3346
3347 void
3348 evolution_source_registry_migrate_sources (void)
3349 {
3350         gchar *base_dir;
3351         gchar *contents;
3352         gchar *gconf_xml;
3353         gsize length;
3354         const gchar *gconf_key;
3355         GError *error = NULL;
3356
3357         base_dir = g_build_filename (
3358                 g_get_home_dir (), ".gconf", "apps", "evolution", NULL);
3359
3360         /* ------------------------------------------------------------------*/
3361
3362         g_print ("Migrating mail accounts from GConf...\n");
3363
3364         gconf_xml = g_build_filename (
3365                 base_dir, "mail", "%gconf.xml", NULL);
3366         g_file_get_contents (gconf_xml, &contents, &length, &error);
3367
3368         if (error == NULL) {
3369                 migrate_parse_gconf_xml (
3370                         PARSE_TYPE_MAIL,
3371                         contents, length, &error);
3372                 g_free (contents);
3373         }
3374
3375         if (error == NULL) {
3376                 gconf_key = "/apps/evolution/mail/accounts";
3377                 migrate_remove_gconf_xml (gconf_key, gconf_xml);
3378         } else {
3379                 g_printerr ("  FAILED: %s\n", error->message);
3380                 g_clear_error (&error);
3381         }
3382
3383         g_free (gconf_xml);
3384
3385         /* ------------------------------------------------------------------*/
3386
3387         g_print ("Migrating addressbook sources from GConf...\n");
3388
3389         gconf_xml = g_build_filename (
3390                 base_dir, "addressbook", "%gconf.xml", NULL);
3391         g_file_get_contents (gconf_xml, &contents, &length, &error);
3392
3393         if (error == NULL) {
3394                 migrate_parse_gconf_xml (
3395                         PARSE_TYPE_ADDRESSBOOK,
3396                         contents, length, &error);
3397                 g_free (contents);
3398         }
3399
3400         if (error == NULL) {
3401                 gconf_key = "/apps/evolution/addressbook/sources";
3402                 migrate_remove_gconf_xml (gconf_key, gconf_xml);
3403         } else {
3404                 g_printerr ("  FAILED: %s\n", error->message);
3405                 g_clear_error (&error);
3406         }
3407
3408         g_free (gconf_xml);
3409
3410         /* ------------------------------------------------------------------*/
3411
3412         g_print ("Migrating calendar sources from GConf...\n");
3413
3414         gconf_xml = g_build_filename (
3415                 base_dir, "calendar", "%gconf.xml", NULL);
3416         g_file_get_contents (gconf_xml, &contents, &length, &error);
3417
3418         if (error == NULL) {
3419                 migrate_parse_gconf_xml (
3420                         PARSE_TYPE_CALENDAR,
3421                         contents, length, &error);
3422                 g_free (contents);
3423         }
3424
3425         if (error == NULL) {
3426                 gconf_key = "/apps/evolution/calendar/sources";
3427                 migrate_remove_gconf_xml (gconf_key, gconf_xml);
3428         } else {
3429                 g_printerr ("  FAILED: %s\n", error->message);
3430                 g_clear_error (&error);
3431         }
3432
3433         g_free (gconf_xml);
3434
3435         /* ------------------------------------------------------------------*/
3436
3437         g_print ("Migrating task list sources from GConf...\n");
3438
3439         gconf_xml = g_build_filename (
3440                 base_dir, "tasks", "%gconf.xml", NULL);
3441         g_file_get_contents (gconf_xml, &contents, &length, &error);
3442
3443         if (error == NULL) {
3444                 migrate_parse_gconf_xml (
3445                         PARSE_TYPE_TASKS,
3446                         contents, length, &error);
3447                 g_free (contents);
3448         }
3449
3450         if (error == NULL) {
3451                 gconf_key = "/apps/evolution/tasks/sources";
3452                 migrate_remove_gconf_xml (gconf_key, gconf_xml);
3453         } else {
3454                 g_printerr ("  FAILED: %s\n", error->message);
3455                 g_clear_error (&error);
3456         }
3457
3458         g_free (gconf_xml);
3459
3460         /* ------------------------------------------------------------------*/
3461
3462         g_print ("Migrating memo list sources from GConf...\n");
3463
3464         gconf_xml = g_build_filename (
3465                 base_dir, "memos", "%gconf.xml", NULL);
3466         g_file_get_contents (gconf_xml, &contents, &length, &error);
3467
3468         if (error == NULL) {
3469                 migrate_parse_gconf_xml (
3470                         PARSE_TYPE_MEMOS,
3471                         contents, length, &error);
3472                 g_free (contents);
3473         }
3474
3475         if (error == NULL) {
3476                 gconf_key = "/apps/evolution/memos/sources";
3477                 migrate_remove_gconf_xml (gconf_key, gconf_xml);
3478         } else {
3479                 g_printerr ("  FAILED: %s\n", error->message);
3480                 g_clear_error (&error);
3481         }
3482
3483         g_free (gconf_xml);
3484
3485         /* ------------------------------------------------------------------*/
3486
3487         g_free (base_dir);
3488 }