aboutsummaryrefslogtreecommitdiffstats
path: root/mail/upgrade-mailer.c
diff options
context:
space:
mode:
Diffstat (limited to 'mail/upgrade-mailer.c')
-rw-r--r--mail/upgrade-mailer.c1169
1 files changed, 1169 insertions, 0 deletions
diff --git a/mail/upgrade-mailer.c b/mail/upgrade-mailer.c
new file mode 100644
index 0000000000..130194ca6a
--- /dev/null
+++ b/mail/upgrade-mailer.c
@@ -0,0 +1,1169 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <bonobo.h>
+#include <bonobo-conf/bonobo-config-database.h>
+#include <gal/util/e-xml-utils.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <camel/camel-file-utils.h>
+
+struct _storeinfo {
+ char *base_url;
+ char *namespace;
+ char *encoded_namespace;
+ char dir_sep;
+ GPtrArray *folders;
+};
+
+
+
+static char
+find_dir_sep (const char *lsub_response)
+{
+ register const unsigned char *inptr;
+ const unsigned char *inend;
+
+ inptr = (const unsigned char *) lsub_response;
+ inend = inptr + strlen (inptr);
+
+ if (strncmp (inptr, "* LSUB (", 8))
+ return '\0';
+
+ inptr += 8;
+ while (inptr < inend && *inptr != ')')
+ inptr++;
+
+ if (inptr >= inend)
+ return '\0';
+
+ inptr++;
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ return '\0';
+
+ if (*inptr == '\"')
+ inptr++;
+
+ return inptr < inend ? *inptr : '\0';
+}
+
+static void
+si_free (struct _storeinfo *si)
+{
+ int i;
+
+ g_free (si->base_url);
+ g_free (si->namespace);
+ g_free (si->encoded_namespace);
+ if (si->folders) {
+ for (i = 0; i < si->folders->len; i++)
+ g_free (si->folders->pdata[i]);
+ g_ptr_array_free (si->folders, TRUE);
+ }
+ g_free (si);
+}
+
+static unsigned char tohex[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static char *
+hex_encode (const char *in, size_t len)
+{
+ const unsigned char *inend = in + len;
+ unsigned char *inptr, *outptr;
+ char *outbuf;
+
+ outptr = outbuf = g_malloc ((len * 3) + 1);
+
+ inptr = (unsigned char *) in;
+ while (inptr < inend) {
+ if (*inptr > 127 || isspace ((int) *inptr)) {
+ *outptr++ = '%';
+ *outptr++ = tohex[(*inptr >> 4) & 0xf];
+ *outptr++ = tohex[*inptr & 0xf];
+ inptr++;
+ } else
+ *outptr++ = *inptr++;
+ }
+
+ *outptr = '\0';
+
+ return outbuf;
+}
+
+#define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
+
+static char *
+hex_decode (const char *in, size_t len)
+{
+ const unsigned char *inend = in + len;
+ unsigned char *inptr, *outptr;
+ char *outbuf;
+
+ outptr = outbuf = g_malloc (len + 1);
+
+ inptr = (unsigned char *) in;
+ while (inptr < inend) {
+ if (*inptr == '%') {
+ if (isxdigit ((int) inptr[1]) && isxdigit ((int) inptr[2])) {
+ *outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]);
+ inptr += 3;
+ } else
+ *outptr++ = *inptr++;
+ } else
+ *outptr++ = *inptr++;
+ }
+
+ *outptr = '\0';
+
+ return outbuf;
+}
+
+static char *
+parse_lsub (const char *lsub, char *dir_sep)
+{
+ const unsigned char *inptr = (const unsigned char *) lsub;
+ const unsigned char *inend;
+ int inlen, quoted = 0;
+
+ inend = inptr + strlen (inptr);
+ if (strncmp (inptr, "* LSUB (", 8))
+ return NULL;
+
+ inptr += 8;
+ while (inptr < inend && *inptr != ')')
+ inptr++;
+
+ if (inptr >= inend)
+ return NULL;
+
+ inptr++;
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ return NULL;
+
+ /* skip over the dir sep */
+ if (*inptr == '\"')
+ inptr++;
+
+ *dir_sep = (char) *inptr++;
+ if (*inptr == '\"')
+ inptr++;
+
+ if (inptr >= inend)
+ return NULL;
+
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ return NULL;
+
+ if (*inptr == '\"') {
+ inptr++;
+ quoted = 1;
+ } else
+ quoted = 0;
+
+ inlen = strlen (inptr) - quoted;
+
+ return g_strndup (inptr, inlen);
+}
+
+static void
+cache_upgrade (struct _storeinfo *si, const char *folder_name)
+{
+ const char *old_folder_name = folder_name;
+ char *oldpath, *newpath, *p;
+ struct dirent *dent;
+ DIR *dir = NULL;
+
+ if (si->namespace && strcmp ("INBOX", folder_name)) {
+ if (!strncmp (old_folder_name, si->namespace, strlen (si->namespace))) {
+ old_folder_name += strlen (si->namespace);
+ if (*old_folder_name == si->dir_sep)
+ old_folder_name++;
+ }
+ }
+
+ oldpath = g_strdup_printf ("%s/evolution/mail/imap/%s/%s", getenv ("HOME"),
+ si->base_url + 7, old_folder_name);
+
+ newpath = g_strdup_printf ("%s/evolution/mail/imap/%s/folders/%s",
+ getenv ("HOME"), si->base_url + 7, folder_name);
+
+ if (!strcmp (folder_name, "folders"))
+ goto special_case_folders;
+
+ if (si->dir_sep != '/') {
+ p = newpath + strlen (newpath) - strlen (folder_name) - 1;
+ while (*p) {
+ if (*p == si->dir_sep)
+ *p = '/';
+ p++;
+ }
+ }
+
+ /* make sure all parent directories exist */
+ if ((p = strrchr (newpath, '/'))) {
+ *p = '\0';
+ camel_mkdir (newpath, 0755);
+ *p = '/';
+ }
+
+ if (rename (oldpath, newpath) == -1) {
+ fprintf (stderr, "Failed to upgrade cache for imap folder %s/%s: %s\n",
+ si->base_url, folder_name, g_strerror (errno));
+ }
+
+ g_free (oldpath);
+ g_free (newpath);
+
+ return;
+
+ special_case_folders:
+
+ /* the user had a toplevel folder named "folders" */
+ if (camel_mkdir (newpath, 0755) == -1) {
+ /* we don't bother to check EEXIST because well, if
+ folders/folders exists then we're pretty much
+ fucked */
+ goto exception;
+ }
+
+ if (!(dir = opendir (oldpath)))
+ goto exception;
+
+ while ((dent = readdir (dir))) {
+ char *old_path, *new_path;
+
+ if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, ".."))
+ continue;
+
+ old_path = g_strdup_printf ("%s/%s", oldpath, dent->d_name);
+ new_path = g_strdup_printf ("%s/%s", newpath, dent->d_name);
+
+ /* make sure all parent directories exist */
+ if ((p = strrchr (new_path, '/'))) {
+ *p = '\0';
+ camel_mkdir (new_path, 0755);
+ *p = '/';
+ }
+
+ if (rename (old_path, new_path) == -1) {
+ g_free (old_path);
+ g_free (new_path);
+ goto exception;
+ }
+
+ g_free (old_path);
+ g_free (new_path);
+ }
+
+ closedir (dir);
+
+ g_free (oldpath);
+ g_free (newpath);
+
+ return;
+
+ exception:
+
+ fprintf (stderr, "Failed to upgrade cache for imap folder %s/%s: %s\n",
+ si->base_url, folder_name, g_strerror (errno));
+
+ if (dir)
+ closedir (dir);
+
+ g_free (oldpath);
+ g_free (newpath);
+}
+
+static int
+foldercmp (const void *f1, const void *f2)
+{
+ const char **folder1 = (const char **) f1;
+ const char **folder2 = (const char **) f2;
+
+ return strcmp (*folder1, *folder2);
+}
+
+static void
+cache_upgrade_and_free (gpointer key, gpointer val, gpointer user_data)
+{
+ struct _storeinfo *si = val;
+ GPtrArray *folders;
+ char *path = NULL;
+ char dir_sep;
+ int i;
+
+ if (si->folders) {
+ path = g_strdup_printf ("%s/evolution/mail/imap/%s/folders",
+ getenv ("HOME"), si->base_url + 7);
+
+ if (mkdir (path, 0755) == -1 && errno != EEXIST) {
+ fprintf (stderr, "Failed to create directory %s: %s", path, g_strerror (errno));
+ goto exception;
+ }
+
+ g_free (path);
+ folders = g_ptr_array_new ();
+ for (i = 0; i < si->folders->len; i++) {
+ if ((path = parse_lsub (si->folders->pdata[i], &dir_sep))) {
+ g_ptr_array_add (folders, path);
+ }
+ }
+
+ /* sort the folders so that parents get created before
+ their children */
+ qsort (folders->pdata, folders->len, sizeof (void *), foldercmp);
+
+ for (i = 0; i < folders->len; i++) {
+ cache_upgrade (si, folders->pdata[i]);
+ g_free (folders->pdata[i]);
+ }
+ }
+
+ si_free (si);
+
+ return;
+
+ exception:
+
+ fprintf (stderr, "Could not upgrade imap cache for %s: %s\n",
+ si->base_url + 7, g_strerror (errno));
+
+ g_free (path);
+
+ si_free (si);
+}
+
+static char *
+get_base_url (const char *protocol, const char *uri)
+{
+ unsigned char *base_url, *p;
+
+ p = (unsigned char *) uri + strlen (protocol) + 1;
+ if (!strncmp (p, "//", 2))
+ p += 2;
+
+ base_url = p;
+ p = strchr (p, '/');
+ base_url = g_strdup_printf ("%s://%.*s", protocol, p ? (int) (p - base_url) : (int) strlen (base_url), base_url);
+
+ return base_url;
+}
+
+static char *
+imap_namespace (const char *uri)
+{
+ unsigned char *name, *p;
+
+ if ((name = strstr (uri, ";namespace=\"")) == NULL)
+ return NULL;
+
+ name += strlen (";namespace=\"");
+ p = name;
+ while (*p && *p != '\"')
+ p++;
+
+ return g_strndup (name, p - name);
+}
+
+static char *
+find_folder (GPtrArray *folders, const char *folder, char *dir_sep)
+{
+ const unsigned char *inptr, *inend;
+ int inlen, len, diff, i;
+ int quoted;
+
+ len = strlen (folder);
+
+ for (i = 0; i < folders->len; i++) {
+ inptr = folders->pdata[i];
+ inend = inptr + strlen (inptr);
+ if (strncmp (inptr, "* LSUB (", 8))
+ continue;
+
+ inptr += 8;
+ while (inptr < inend && *inptr != ')')
+ inptr++;
+
+ if (inptr >= inend)
+ continue;
+
+ inptr++;
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ continue;
+
+ /* skip over the dir sep */
+ if (*inptr == '\"')
+ inptr++;
+
+ *dir_sep = *inptr++;
+ if (*inptr == '\"')
+ inptr++;
+
+ if (inptr >= inend)
+ continue;
+
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ continue;
+
+ if (*inptr == '\"') {
+ inptr++;
+ quoted = 1;
+ } else
+ quoted = 0;
+
+ inlen = strlen (inptr) - quoted;
+ if (len > inlen)
+ continue;
+
+ diff = inlen - len;
+ if (!strncmp (inptr + diff, folder, len))
+ return hex_encode (inptr, inlen);
+ }
+
+ *dir_sep = '\0';
+
+ return NULL;
+}
+
+static char *
+imap_url_upgrade (GHashTable *imap_sources, const char *uri)
+{
+ struct _storeinfo *si;
+ unsigned char *base_url, *folder, *p, *new = NULL;
+ char dir_sep;
+
+ base_url = get_base_url ("imap", uri);
+
+ fprintf (stderr, "checking for %s... ", base_url);
+ if (!(si = g_hash_table_lookup (imap_sources, base_url))) {
+ fprintf (stderr, "not found.\n");
+ g_warning ("Unknown imap account: %s", base_url);
+ g_free (base_url);
+ return NULL;
+ }
+
+ fprintf (stderr, "found.\n");
+ p = (unsigned char *) uri + strlen (base_url) + 1;
+ if (!strcmp (p, "INBOX")) {
+ new = g_strdup_printf ("%s/INBOX", base_url);
+ g_free (base_url);
+ return new;
+ }
+
+ p = hex_decode (p, strlen (p));
+
+ fprintf (stderr, "checking for folder %s on %s... ", p, base_url);
+ folder = si->folders ? find_folder (si->folders, p, &dir_sep) : NULL;
+ if (folder == NULL) {
+ fprintf (stderr, "not found.\n");
+ folder = p;
+ if (si->namespace) {
+ if (!si->dir_sep) {
+ fprintf (stderr, "checking for directory separator in namespace param... ");
+ if (*si->namespace == '/') {
+ dir_sep = '/';
+ } else {
+ p = si->namespace;
+ while (*p && !ispunct ((int) *p))
+ p++;
+
+ dir_sep = (char) *p;
+ }
+ } else {
+ dir_sep = si->dir_sep;
+ }
+
+ if (dir_sep) {
+ fprintf (stderr, "found: '%c'\n", dir_sep);
+ p = folder;
+ folder = hex_encode (folder, strlen (folder));
+ new = g_strdup_printf ("%s/%s%c%s", base_url, si->encoded_namespace, dir_sep, folder);
+ g_free (folder);
+ folder = p;
+
+ p = new + strlen (base_url) + 1;
+ while (*p) {
+ if (*p == dir_sep)
+ *p = '/';
+ p++;
+ }
+ } else {
+ fprintf (stderr, "not found.");
+ g_warning ("Cannot update settings for imap folder %s: unknown directory separator", uri);
+ }
+ } else {
+ g_warning ("Cannot update settings for imap folder %s: unknown namespace", uri);
+ }
+
+ g_free (base_url);
+ g_free (folder);
+
+ return new;
+ } else
+ g_free (p);
+
+ fprintf (stderr, "found.\n");
+ new = g_strdup_printf ("%s/%s", base_url, folder);
+ g_free (folder);
+
+ if (!si->dir_sep)
+ si->dir_sep = dir_sep;
+
+ if (dir_sep) {
+ p = new + strlen (base_url) + 1;
+ while (*p) {
+ if (*p == dir_sep)
+ *p = '/';
+ p++;
+ }
+ }
+
+ g_free (base_url);
+
+ return new;
+}
+
+static char *
+exchange_url_upgrade (const char *uri)
+{
+ unsigned char *base_url, *folder;
+ char *url;
+
+ base_url = get_base_url ("exchange", uri);
+ folder = (unsigned char *) uri + strlen (base_url) + 1;
+
+ if (strncmp (folder, "exchange/", 9))
+ return g_strdup (uri);
+
+ folder += 9;
+ while (*folder && *folder != '/')
+ folder++;
+ if (*folder == '/')
+ folder++;
+
+ folder = hex_decode (folder, strlen (folder));
+ url = g_strdup_printf ("%s/personal/%s", base_url, folder);
+ g_free (base_url);
+ g_free (folder);
+
+ return url;
+}
+
+static int
+mailer_upgrade_account_info (Bonobo_ConfigDatabase db, const char *key, int num, GHashTable *imap_sources)
+{
+ char *path, *uri, *new;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ path = g_strdup_printf ("/Mail/Accounts/account_%s_folder_uri_%d", key, i);
+ uri = bonobo_config_get_string (db, path, NULL);
+ if (uri) {
+ if (!strncmp (uri, "imap:", 5)) {
+ new = imap_url_upgrade (imap_sources, uri);
+ if (new) {
+ bonobo_config_set_string (db, path, new, NULL);
+ g_free (new);
+ }
+ } else if (!strncmp (uri, "exchange:", 9)) {
+ new = exchange_url_upgrade (uri);
+ bonobo_config_set_string (db, path, new, NULL);
+ g_free (new);
+ }
+ }
+
+ g_free (uri);
+ g_free (path);
+ }
+
+ return 0;
+}
+
+static int
+mailer_upgrade_xml_file (GHashTable *imap_sources, const char *filename)
+{
+ unsigned char *buffer, *inptr, *start, *uri, *new;
+ ssize_t nread = 0, nwritten, n;
+ gboolean url_need_upgrade;
+ struct stat st;
+ size_t len;
+ char *bak;
+ int fd;
+
+ bak = g_strdup_printf ("%s.bak-1.0", filename);
+ if (stat (bak, &st) != -1) {
+ /* seems we have already converted this file? */
+ fprintf (stderr, "\n%s already exists, assuming %s has already been upgraded\n", bak, filename);
+ g_free (bak);
+ return 0;
+ }
+
+ if (stat (filename, &st) == -1 || (fd = open (filename, O_RDONLY)) == -1) {
+ /* file doesn't exist? I guess nothing to upgrade here */
+ fprintf (stderr, "\nCould not open %s: %s\n", filename, strerror (errno));
+ g_free (bak);
+ return 0;
+ }
+
+ start = buffer = g_malloc (st.st_size + 1);
+ do {
+ do {
+ n = read (fd, buffer + nread, st.st_size - nread);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nread += n;
+ } while (n != -1 && nread < st.st_size);
+ buffer[nread] = '\0';
+
+ if (nread < st.st_size) {
+ /* failed to load the entire file? */
+ fprintf (stderr, "\nFailed to load %s: %s\n", filename, strerror (errno));
+ g_free (buffer);
+ g_free (bak);
+ close (fd);
+ return -1;
+ }
+
+ close (fd);
+
+ inptr = buffer;
+ url_need_upgrade = FALSE;
+ do {
+ inptr = strstr (inptr, "uri=\"");
+ if (inptr) {
+ inptr += 5;
+ url_need_upgrade = !strncmp (inptr, "imap:", 5) || !strncmp (inptr, "exchange:", 9);
+ }
+ } while (inptr && !url_need_upgrade);
+
+ if (inptr == NULL) {
+ /* no imap urls in this xml file, so no need to "upgrade" it */
+ fprintf (stdout, "\nNo updates required for %s\n", filename);
+ g_free (buffer);
+ g_free (bak);
+ return 0;
+ }
+
+ if (rename (filename, bak) == -1) {
+ /* failed to backup xml file */
+ fprintf (stderr, "\nFailed to create backup file %s: %s\n", bak, strerror (errno));
+ g_free (buffer);
+ g_free (bak);
+ return -1;
+ }
+
+ if ((fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) {
+ /* failed to create new xml file */
+ fprintf (stderr, "\nFailed to create new %s: %s\n", filename, strerror (errno));
+ rename (bak, filename);
+ g_free (buffer);
+ g_free (bak);
+ return -1;
+ }
+
+ while (inptr != NULL) {
+ len = inptr - start;
+ nwritten = 0;
+ do {
+ do {
+ n = write (fd, start + nwritten, len - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (n != -1 && nwritten < len);
+
+ if (nwritten < len)
+ goto exception;
+
+ start = inptr;
+ while (*start && *start != '"')
+ start++;
+
+ uri = g_strndup (inptr, start - inptr);
+ if (!strncmp (uri, "imap:", 5)) {
+ if ((new = imap_url_upgrade (imap_sources, uri)) == NULL) {
+ new = uri;
+ uri = NULL;
+ }
+ } else if (!strncmp (uri, "exchange:", 9)) {
+ new = exchange_url_upgrade (uri);
+ } else {
+ new = uri;
+ uri = NULL;
+ }
+ g_free (uri);
+
+ nwritten = 0;
+ len = strlen (new);
+ do {
+ do {
+ n = write (fd, new + nwritten, len - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (n != -1 && nwritten < len);
+
+ g_free (new);
+
+ if (nwritten < len)
+ goto exception;
+
+ inptr = start;
+ url_need_upgrade = FALSE;
+ do {
+ inptr = strstr (inptr, "uri=\"");
+ if (inptr) {
+ inptr += 5;
+ url_need_upgrade = !strncmp (inptr, "imap:", 5) || !strncmp (inptr, "exchange:", 9);
+ }
+ } while (inptr && !url_need_upgrade);
+ }
+
+ nwritten = 0;
+ len = strlen (start);
+ do {
+ do {
+ n = write (fd, start + nwritten, len - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (n != -1 && nwritten < len);
+
+ if (nwritten < len)
+ goto exception;
+
+ if (fsync (fd) == -1)
+ goto exception;
+
+ close (fd);
+ g_free (buffer);
+
+ fprintf (stdout, "\nSuccessfully upgraded %s\nPrevious settings saved in %s\n\n", filename, bak);
+
+ g_free (bak);
+
+ return 0;
+
+ exception:
+
+ fprintf (stderr, "\nFailed to save updated settings to %s: %s\n\n", filename, strerror (errno));
+
+ close (fd);
+ g_free (buffer);
+ unlink (filename);
+ rename (bak, filename);
+ g_free (bak);
+
+ return -1;
+}
+
+static char *
+shortcuts_upgrade_uri (GHashTable *accounts, GHashTable *imap_sources, const char *account, const char *folder)
+{
+ char *url, *name, *decoded, *new = NULL;
+ struct _storeinfo *si;
+ int type;
+
+ type = GPOINTER_TO_INT ((si = g_hash_table_lookup (accounts, account)));
+ if (type == 1) {
+ /* exchange */
+ decoded = hex_decode (folder, strlen (folder));
+ name = g_strdup_printf ("personal/%s", decoded);
+ g_free (decoded);
+
+ return name;
+ } else {
+ /* imap */
+ url = g_strdup_printf ("%s/%s", si->base_url, folder);
+ new = imap_url_upgrade (imap_sources, url);
+ g_free (url);
+
+ if (new) {
+ name = new + strlen (si->base_url) + 1;
+ name = hex_decode (name, strlen (name));
+ g_free (new);
+
+ return name;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+shortcuts_upgrade_xml_file (GHashTable *accounts, GHashTable *imap_sources, const char *filename)
+{
+ char *bak, *uri, *account, *folder, *new, *new_uri, *type;
+ struct stat st;
+ xmlDoc *doc;
+ xmlNode *group, *item;
+ int account_len;
+ gboolean changed = FALSE;
+
+ bak = g_strdup_printf ("%s.bak-1.0", filename);
+ if (stat (bak, &st) != -1) {
+ /* seems we have already converted this file? */
+ fprintf (stderr, "\n%s already exists, assuming %s has already been upgraded\n", bak, filename);
+ g_free (bak);
+ return 0;
+ }
+
+ if (stat (filename, &st) == -1) {
+ /* file doesn't exist? I guess nothing to upgrade here */
+ fprintf (stderr, "\nCould not open %s: %s\n", filename, strerror (errno));
+ g_free (bak);
+ return 0;
+ }
+
+ doc = xmlParseFile (filename);
+ if (!doc || !doc->xmlRootNode) {
+ /* failed to load/parse the file? */
+ fprintf (stderr, "\nFailed to load %s\n", filename);
+ g_free (bak);
+ return -1;
+ }
+
+ for (group = doc->xmlRootNode->xmlChildrenNode; group; group = group->next) {
+ for (item = group->xmlChildrenNode; item; item = item->next) {
+ /* Fix IMAP/Exchange URIs */
+ uri = xmlNodeGetContent (item);
+ if (!strncmp (uri, "evolution:/", 11)) {
+ if (!strcmp (uri, "evolution:/local/Inbox")) {
+ xmlNodeSetContent (item, "default:mail");
+ changed = TRUE;
+ } else if (!strcmp (uri, "evolution:/local/Calendar")) {
+ xmlNodeSetContent (item, "default:calendar");
+ changed = TRUE;
+ } else if (!strcmp (uri, "evolution:/local/Contacts")) {
+ xmlNodeSetContent (item, "default:contacts");
+ changed = TRUE;
+ } else if (!strcmp (uri, "evolution:/local/Tasks")) {
+ xmlNodeSetContent (item, "default:tasks");
+ changed = TRUE;
+ } else {
+ account_len = strcspn (uri + 11, "/");
+ account = g_strndup (uri + 11, account_len);
+ if (g_hash_table_lookup (accounts, account)) {
+ folder = uri + 11 + account_len;
+ if (*folder)
+ folder++;
+ new = shortcuts_upgrade_uri (accounts, imap_sources, account, folder);
+ new_uri = g_strdup_printf ("evolution:/%s/%s", account, new);
+ xmlNodeSetContent (item, new_uri);
+ changed = TRUE;
+ g_free (new_uri);
+ }
+ g_free (account);
+ }
+ }
+ xmlFree (uri);
+
+ /* Fix LDAP shortcuts */
+ type = xmlGetProp (item, "type");
+ if (type) {
+ if (!strcmp (type, "ldap-contacts")) {
+ xmlSetProp (item, "type", "contacts/ldap");
+ changed = TRUE;
+ }
+ xmlFree (type);
+ }
+ }
+ }
+
+ if (!changed) {
+ fprintf (stdout, "\nNo updates required for %s\n", filename);
+ xmlFreeDoc (doc);
+ g_free (bak);
+ return 0;
+ }
+
+ if (rename (filename, bak) == -1) {
+ /* failed to backup xml file */
+ fprintf (stderr, "\nFailed to create backup file %s: %s\n", bak, strerror (errno));
+ xmlFreeDoc (doc);
+ g_free (bak);
+ return -1;
+ }
+
+ if (e_xml_save_file (filename, doc) == -1) {
+ fprintf (stderr, "\nFailed to save updated settings to %s: %s\n\n", filename, strerror (errno));
+ xmlFreeDoc (doc);
+ unlink (filename);
+ rename (bak, filename);
+ g_free (bak);
+ return -1;
+ }
+
+ fprintf (stdout, "\nSuccessfully upgraded %s\nPrevious settings saved in %s\n\n", filename, bak);
+
+ xmlFreeDoc (doc);
+ g_free (bak);
+
+ return 0;
+}
+
+
+static int
+mailer_upgrade (Bonobo_ConfigDatabase db)
+{
+ GHashTable *imap_sources, *accounts;
+ char *path, *uri;
+ char *account, *transport;
+ int num, i;
+
+ if ((num = bonobo_config_get_long_with_default (db, "/Mail/Accounts/num", 0, NULL)) == 0) {
+ /* nothing to upgrade */
+ return 0;
+ }
+
+ accounts = g_hash_table_new (g_str_hash, g_str_equal);
+ imap_sources = g_hash_table_new (g_str_hash, g_str_equal);
+ for (i = 0; i < num; i++) {
+ struct _storeinfo *si;
+ struct stat st;
+ char *string;
+ guint32 tmp;
+ FILE *fp;
+ int j;
+
+ path = g_strdup_printf ("/Mail/Accounts/source_url_%d", i);
+ uri = bonobo_config_get_string (db, path, NULL);
+ g_free (path);
+ if (uri && !strncmp (uri, "imap:", 5)) {
+ path = g_strdup_printf ("/Mail/Accounts/account_name_%d", i);
+ account = bonobo_config_get_string (db, path, NULL);
+ g_free (path);
+
+ si = g_new (struct _storeinfo, 1);
+ si->base_url = get_base_url ("imap", uri);
+ si->namespace = imap_namespace (uri);
+ si->encoded_namespace = NULL;
+ si->dir_sep = '\0';
+ si->folders = NULL;
+
+ path = si->base_url + 7;
+
+ path = g_strdup_printf ("%s/evolution/mail/imap/%s/storeinfo", getenv ("HOME"), path);
+ if (stat (path, &st) != -1 && (fp = fopen (path, "r")) != NULL) {
+ camel_file_util_decode_uint32 (fp, &tmp);
+ camel_file_util_decode_uint32 (fp, &tmp);
+
+ j = 0;
+ si->folders = g_ptr_array_new ();
+ while (camel_file_util_decode_string (fp, &string) != -1) {
+ if (j++ > 0) {
+ g_ptr_array_add (si->folders, string);
+ } else {
+ if (!si->namespace)
+ si->namespace = string;
+ else
+ g_free (string);
+
+ camel_file_util_decode_uint32 (fp, &tmp);
+ si->dir_sep = (char) tmp & 0xff;
+ }
+ }
+
+ fclose (fp);
+ }
+ g_free (path);
+
+ if (si->folders && si->folders->len > 0)
+ si->dir_sep = find_dir_sep (si->folders->pdata[0]);
+
+ if (si->namespace) {
+ /* strip trailing dir_sep from namespace if it's there */
+ j = strlen (si->namespace) - 1;
+ if (si->namespace[j] == si->dir_sep)
+ si->namespace[j] = '\0';
+
+ /* set the encoded version of the namespace */
+ si->encoded_namespace = g_strdup (si->namespace);
+ for (j = 0; j < strlen (si->encoded_namespace); j++) {
+ if (si->encoded_namespace[j] == '/')
+ si->encoded_namespace[j] = '.';
+ }
+ }
+
+ g_hash_table_insert (imap_sources, si->base_url, si);
+
+ if (account)
+ g_hash_table_insert (accounts, account, si);
+ } else if (uri && !strncmp (uri, "exchange:", 9)) {
+ /* Upgrade transport uri */
+ path = g_strdup_printf ("/Mail/Accounts/transport_url_%d", i);
+ transport = bonobo_config_get_string (db, path, NULL);
+ if (transport && !strncmp (transport, "exchanget:", 10))
+ bonobo_config_set_string (db, path, uri, NULL);
+ g_free (transport);
+ g_free (path);
+
+ path = g_strdup_printf ("/Mail/Accounts/account_name_%d", i);
+ account = bonobo_config_get_string (db, path, NULL);
+ g_free (path);
+
+ if (account)
+ g_hash_table_insert (accounts, account, GINT_TO_POINTER (1));
+ }
+
+ g_free (uri);
+ }
+
+ if (g_hash_table_size (accounts) == 0) {
+ /* user doesn't have any imap/exchange accounts - nothing to upgrade */
+ g_hash_table_destroy (imap_sources);
+ return 0;
+ }
+
+ /* upgrade user's account info (bug #29135) */
+ mailer_upgrade_account_info (db, "drafts", num, imap_sources);
+ mailer_upgrade_account_info (db, "sent", num, imap_sources);
+
+ /* upgrade user's filters/vfolders (bug #24451) */
+ path = g_strdup_printf ("%s/evolution/filters.xml", getenv ("HOME"));
+ mailer_upgrade_xml_file (imap_sources, path);
+ g_free (path);
+
+ path = g_strdup_printf ("%s/evolution/vfolders.xml", getenv ("HOME"));
+ mailer_upgrade_xml_file (imap_sources, path);
+ g_free (path);
+
+ /* upgrade user's shortcuts (there's no bug # for this one) */
+ path = g_strdup_printf ("%s/evolution/shortcuts.xml", getenv ("HOME"));
+ shortcuts_upgrade_xml_file (accounts, imap_sources, path);
+ g_free (path);
+
+ g_hash_table_foreach (imap_sources, cache_upgrade_and_free, NULL);
+ g_hash_table_destroy (imap_sources);
+#if 0
+ path = g_strdup_printf ("%s/evolution/mail/imap", getenv ("HOME"));
+ bak = g_strdup_printf ("%s.bak-1.0", path);
+
+ if (rename (path, bak) == -1)
+ fprintf (stderr, "\nFailed to backup Evolution 1.0's IMAP cache: %s\n", strerror (errno));
+
+ g_free (path);
+ g_free (bak);
+#endif
+
+ return 0;
+}
+
+static Bonobo_ConfigDatabase
+get_config_db (void)
+{
+ Bonobo_ConfigDatabase db;
+ CORBA_Environment ev;
+
+ CORBA_exception_init (&ev);
+
+ db = bonobo_get_object ("wombat:", "Bonobo/ConfigDatabase", &ev);
+ if (BONOBO_EX (&ev) || db == CORBA_OBJECT_NIL) {
+ fprintf (stderr, "get_config_db(): Could not get the config database object '%s'",
+ bonobo_exception_get_text (&ev));
+ db = CORBA_OBJECT_NIL;
+ }
+
+ CORBA_exception_free (&ev);
+
+ return db;
+}
+
+static int
+upgrade (void)
+{
+ Bonobo_ConfigDatabase db;
+ CORBA_Environment ev;
+
+ if ((db = get_config_db ()) == CORBA_OBJECT_NIL)
+ g_error ("Could not get config db");
+
+ mailer_upgrade (db);
+
+ CORBA_exception_init (&ev);
+ Bonobo_ConfigDatabase_sync (db, &ev);
+
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+int main (int argc, char **argv)
+{
+ CORBA_ORB orb;
+
+ gnome_init ("evolution-upgrade", "1.0", argc, argv);
+
+ if ((orb = oaf_init (argc, argv)) == NULL)
+ g_error ("Cannot init oaf");
+
+ if (bonobo_init (orb, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL) == FALSE)
+ g_error ("Cannot init bonobo");
+
+ gtk_idle_add ((GtkFunction) upgrade, NULL);
+
+ bonobo_main ();
+
+ return 0;
+}