/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Authors: Jeffrey Stedfast * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include struct _storeinfo { char *base_url; char *namespace; char dir_sep; GByteArray *junk; }; static void * e_memmem (const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { register unsigned char *h, *n, *hc, *nc; unsigned char *he, *ne; if (haystacklen < needlelen) { return NULL; } else if (needlelen == 0) { return (void *) haystack; } h = (unsigned char *) haystack; he = (unsigned char *) haystack + haystacklen - needlelen; n = (unsigned char *) needle; ne = (unsigned char *) needle + needlelen; while (h < he) { if (*h == *n) { for (hc = h + 1, nc = n + 1; nc < ne; hc++, nc++) if (*hc != *nc) break; if (nc == ne) return (void *) h; } h++; } return NULL; } static char find_dir_sep (GByteArray *buf) { register unsigned char *inptr; unsigned char *inend; inend = buf->data + buf->len; if (!(inptr = e_memmem (buf->data, buf->len, "* LSUB (", 8))) return '\0'; inptr += 9; 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 (gpointer key, gpointer val, gpointer user_data) { struct _storeinfo *si = val; g_free (si->base_url); g_free (si->namespace); if (si->junk) g_byte_array_free (si->junk, TRUE); g_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) : 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 * imap_url_upgrade (GHashTable *imap_sources, const char *uri) { struct _storeinfo *si; unsigned char *base_url, *folder, *p, *new = NULL; unsigned char dir_sep; int len; 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; } folder = g_strdup_printf ("%s\"", p); len = strlen (folder); fprintf (stderr, "checking for folder %s on %s... ", p, base_url); p = e_memmem (si->junk->data, si->junk->len, folder, len); g_free (folder); if (p == NULL) { fprintf (stderr, "not found.\n"); if (si->namespace) { if (!si->dir_sep) { fprintf (stderr, "checking for directory separator in namespace param... "); folder = p; if (*si->namespace == '/') { dir_sep = '/'; } else { p = si->namespace; while (*p && !ispunct ((int) *p)) p++; dir_sep = *p; } } else dir_sep = si->dir_sep; if (dir_sep) { fprintf (stderr, "found: '%c'\n", dir_sep); if (si->namespace[strlen (si->namespace) - 1] == dir_sep) new = g_strdup_printf ("%s/%s%s", base_url, si->namespace, folder); else new = g_strdup_printf ("%s/%s%c%s", base_url, si->namespace, dir_sep, folder); 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); return new; } fprintf (stderr, "found.\n"); len--; folder = p; while (*folder != '\"') { folder--; len++; } new = g_strdup_printf ("%s/%.*s", base_url, len - 1, folder + 1); if (si->dir_sep) { p = new + strlen (base_url) + 1; while (*p) { if (*p == si->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)) { folder += 9; while (*folder && *folder != '/') folder++; if (*folder == '/') folder++; } url = g_strdup_printf ("%s/personal/%s", base_url, folder); g_free (base_url); 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, " 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, " 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", filename, bak); g_free (bak); return 0; exception: fprintf (stderr, "\nFailed to save updated settings to %s: %s\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) { struct _storeinfo *si; char *url, *new; int type; type = GPOINTER_TO_INT ((si = g_hash_table_lookup (accounts, account))); if (type == 1) { /* exchange */ return g_strdup_printf ("personal/%s", folder); } else { /* imap */ url = g_strdup_printf ("%s/%s", si->base_url, folder); new = imap_url_upgrade (imap_sources, url); g_free (url); if (new) { url = g_strdup (new + strlen (si->base_url) + 1); g_free (new); return url; } } return NULL; } static int shortcuts_upgrade_xml_file (GHashTable *accounts, GHashTable *imap_sources, const char *filename) { unsigned char *buffer, *inptr, *start, *folder, *new, *account = NULL; 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 { g_free (account); inptr = strstr (inptr, ">evolution:"); if (inptr) { inptr += 11; account = inptr; while (*inptr && *inptr != '/') inptr++; account = g_strndup (account, inptr - account); inptr++; url_need_upgrade = GPOINTER_TO_INT (g_hash_table_lookup (accounts, account)); } } 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++; folder = g_strndup (inptr, start - inptr); new = shortcuts_upgrade_uri (accounts, imap_sources, account, folder); g_free (account); account = NULL; g_free (folder); 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 { g_free (account); inptr = strstr (inptr, ">evolution:"); if (inptr) { inptr += 11; account = inptr; while (*inptr && *inptr != '/') inptr++; account = g_strndup (account, inptr - account); inptr++; url_need_upgrade = GPOINTER_TO_INT (g_hash_table_lookup (accounts, account)); } } 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", filename, bak); g_free (bak); return 0; exception: fprintf (stderr, "\nFailed to save updated settings to %s: %s\n", filename, strerror (errno)); close (fd); g_free (buffer); unlink (filename); rename (bak, filename); g_free (bak); return -1; } static int mailer_upgrade (Bonobo_ConfigDatabase db) { GHashTable *imap_sources, *accounts; char *path, *uri, *bak; char *account; 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; int fd; 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->dir_sep = '\0'; si->junk = NULL; path = si->base_url + 7; path = g_strdup_printf ("%s/evolution/mail/imap/%s/storeinfo", getenv ("HOME"), path); if (stat (path, &st) != -1 && (fd = open (path, O_RDONLY)) != -1) { ssize_t nread = 0, n; si->junk = g_byte_array_new (); g_byte_array_set_size (si->junk, st.st_size); do { do { n = read (fd, si->junk->data + nread, st.st_size - nread); } while (n == -1 && errno == EINTR); if (n > 0) nread += n; } while (n != -1 && nread < st.st_size); close (fd); } g_free (path); if (si->junk) si->dir_sep = find_dir_sep (si->junk); 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)) { 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 (imap_sources) == 0) { /* user doesn't have any imap 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, si_free, NULL); g_hash_table_destroy (imap_sources); 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); 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; if ((db = get_config_db ()) == CORBA_OBJECT_NIL) g_error ("Could not get config db"); mailer_upgrade (db); 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; }