evolution-3.6.4/shell/e-shell-migrate.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-shell-migrate.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-shell-migrate.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  * e-shell-migrate.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  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 19  *
 20  */
 21 
 22 #ifdef HAVE_CONFIG_H
 23 #include <config.h>
 24 #endif
 25 
 26 #include "e-shell-migrate.h"
 27 
 28 #include <libedataserver/libedataserver.h>
 29 
 30 #include <errno.h>
 31 #include <string.h>
 32 #include <unistd.h>
 33 #include <glib/gi18n.h>
 34 #include <glib/gstdio.h>
 35 
 36 #include "libevolution-utils/e-alert-dialog.h"
 37 #include "e-util/e-file-utils.h"
 38 #include "e-util/e-util.h"
 39 
 40 #include "es-event.h"
 41 #include "evo-version.h"
 42 
 43 /******************** Begin XDG Base Directory Migration ********************/
 44 /* These are the known EShellBackend names as of Evolution 3.0 */
 45 static const gchar *shell_backend_names[] =
 46 	{ "addressbook", "calendar", "mail", "memos", "tasks", NULL };
 47 
 48 static gboolean
 49 shell_xdg_migrate_rename (const gchar *old_filename,
 50                           const gchar *new_filename)
 51 {
 52 	gboolean old_filename_is_dir;
 53 	gboolean old_filename_exists;
 54 	gboolean new_filename_exists;
 55 	gboolean success = TRUE;
 56 
 57 	old_filename_is_dir = g_file_test (old_filename, G_FILE_TEST_IS_DIR);
 58 	old_filename_exists = g_file_test (old_filename, G_FILE_TEST_EXISTS);
 59 	new_filename_exists = g_file_test (new_filename, G_FILE_TEST_EXISTS);
 60 
 61 	if (!old_filename_exists)
 62 		return TRUE;
 63 
 64 	g_print ("  mv %s %s\n", old_filename, new_filename);
 65 
 66 	/* It's safe to go ahead and move directories because rename ()
 67 	 * will fail if the new directory already exists with content.
 68 	 * With regular files we have to be careful not to overwrite
 69 	 * new files with old files. */
 70 	if (old_filename_is_dir || !new_filename_exists) {
 71 		if (g_rename (old_filename, new_filename) < 0) {
 72 			g_printerr ("  FAILED: %s\n", g_strerror (errno));
 73 			success = FALSE;
 74 		}
 75 	} else {
 76 		g_printerr ("  FAILED: Destination file already exists\n");
 77 		success = FALSE;
 78 	}
 79 
 80 	return success;
 81 }
 82 
 83 static gboolean
 84 shell_xdg_migrate_rmdir (const gchar *dirname)
 85 {
 86 	GDir *dir = NULL;
 87 	gboolean success = TRUE;
 88 
 89 	if (g_file_test (dirname, G_FILE_TEST_IS_DIR)) {
 90 		g_print ("  rmdir %s\n", dirname);
 91 		if (g_rmdir (dirname) < 0) {
 92 			g_printerr ("  FAILED: %s", g_strerror (errno));
 93 			if (errno == ENOTEMPTY) {
 94 				dir = g_dir_open (dirname, 0, NULL);
 95 				g_printerr (" (contents follows)");
 96 			}
 97 			g_printerr ("\n");
 98 			success = FALSE;
 99 		}
100 	}
101 
102 	/* List the directory's contents to aid debugging. */
103 	if (dir != NULL) {
104 		const gchar *basename;
105 
106 		/* Align the filenames beneath the error message. */
107 		while ((basename = g_dir_read_name (dir)) != NULL)
108 			g_print ("          %s\n", basename);
109 
110 		g_dir_close (dir);
111 	}
112 
113 	return success;
114 }
115 
116 static void
117 shell_xdg_migrate_process_corrections (GHashTable *corrections)
118 {
119 	GHashTableIter iter;
120 	gpointer old_filename;
121 	gpointer new_filename;
122 
123 	g_hash_table_iter_init (&iter, corrections);
124 
125 	while (g_hash_table_iter_next (&iter, &old_filename, &new_filename)) {
126 		gboolean is_directory;
127 
128 		is_directory = g_file_test (old_filename, G_FILE_TEST_IS_DIR);
129 
130 		/* If the old filename is a directory and the new filename
131 		 * is NULL, treat it as a request to remove the directory. */
132 		if (is_directory && new_filename == NULL)
133 			shell_xdg_migrate_rmdir (old_filename);
134 		else
135 			shell_xdg_migrate_rename (old_filename, new_filename);
136 
137 		g_hash_table_iter_remove (&iter);
138 	}
139 }
140 
141 static gboolean
142 shell_xdg_migrate_rename_files (const gchar *src_directory,
143                                 const gchar *dst_directory)
144 {
145 	GDir *dir;
146 	GHashTable *corrections;
147 	const gchar *basename;
148 	const gchar *home_dir;
149 	gchar *old_base_dir;
150 	gchar *new_base_dir;
151 
152 	dir = g_dir_open (src_directory, 0, NULL);
153 	if (dir == NULL)
154 		return FALSE;
155 
156 	/* This is to avoid renaming files which we're iterating over the
157 	 * directory.  POSIX says the outcome of that is unspecified. */
158 	corrections = g_hash_table_new_full (
159 		g_str_hash, g_str_equal,
160 		(GDestroyNotify) g_free,
161 		(GDestroyNotify) g_free);
162 
163 	g_mkdir_with_parents (dst_directory, 0700);
164 
165 	home_dir = g_get_home_dir ();
166 	old_base_dir = g_build_filename (home_dir, ".evolution", NULL);
167 	e_filename_make_safe (old_base_dir);
168 	new_base_dir = g_strdup (e_get_user_data_dir ());
169 	e_filename_make_safe (new_base_dir);
170 
171 	while ((basename = g_dir_read_name (dir)) != NULL) {
172 		GString *buffer;
173 		gchar *old_filename;
174 		gchar *new_filename;
175 		gchar *cp;
176 
177 		buffer = g_string_new (basename);
178 
179 		if ((cp = strstr (basename, old_base_dir)) != NULL) {
180 			g_string_erase (
181 				buffer, cp - basename,
182 				strlen (old_base_dir));
183 			g_string_insert (
184 				buffer, cp - basename, new_base_dir);
185 		}
186 
187 		old_filename = g_build_filename (
188 			src_directory, basename, NULL);
189 		new_filename = g_build_filename (
190 			dst_directory, buffer->str, NULL);
191 
192 		g_string_free (buffer, TRUE);
193 
194 		g_hash_table_insert (corrections, old_filename, new_filename);
195 	}
196 
197 	g_free (old_base_dir);
198 	g_free (new_base_dir);
199 
200 	g_dir_close (dir);
201 
202 	shell_xdg_migrate_process_corrections (corrections);
203 	g_hash_table_destroy (corrections);
204 
205 	/* It's tempting to want to remove the source directory here.
206 	 * Don't.  We might be iterating over the source directory's
207 	 * parent directory, and removing the source directory would
208 	 * screw up the iteration. */
209 
210 	return TRUE;
211 }
212 
213 static gboolean
214 shell_xdg_migrate_move_contents (const gchar *src_directory,
215                                  const gchar *dst_directory)
216 {
217 	GDir *dir;
218 	GHashTable *corrections;
219 	const gchar *basename;
220 
221 	dir = g_dir_open (src_directory, 0, NULL);
222 	if (dir == NULL)
223 		return FALSE;
224 
225 	/* This is to avoid renaming files which we're iterating over the
226 	 * directory.  POSIX says the outcome of that is unspecified. */
227 	corrections = g_hash_table_new_full (
228 		g_str_hash, g_str_equal,
229 		(GDestroyNotify) g_free,
230 		(GDestroyNotify) g_free);
231 
232 	g_mkdir_with_parents (dst_directory, 0700);
233 
234 	while ((basename = g_dir_read_name (dir)) != NULL) {
235 		gchar *old_filename;
236 		gchar *new_filename;
237 
238 		old_filename = g_build_filename (src_directory, basename, NULL);
239 		new_filename = g_build_filename (dst_directory, basename, NULL);
240 
241 		g_hash_table_insert (corrections, old_filename, new_filename);
242 	}
243 
244 	g_dir_close (dir);
245 
246 	shell_xdg_migrate_process_corrections (corrections);
247 	g_hash_table_destroy (corrections);
248 
249 	/* It's tempting to want to remove the source directory here.
250 	 * Don't.  We might be iterating over the source directory's
251 	 * parent directory, and removing the source directory would
252 	 * screw up the iteration. */
253 
254 	return TRUE;
255 }
256 
257 static void
258 shell_xdg_migrate_cache_dir (EShell *shell,
259                              const gchar *old_base_dir)
260 {
261 	const gchar *new_cache_dir;
262 	gchar *old_cache_dir;
263 	gchar *old_filename;
264 	gchar *new_filename;
265 
266 	old_cache_dir = g_build_filename (old_base_dir, "cache", NULL);
267 	new_cache_dir = e_get_user_cache_dir ();
268 
269 	g_print ("Migrating cached data\n");
270 
271 	g_mkdir_with_parents (new_cache_dir, 0700);
272 
273 	old_filename = g_build_filename (old_cache_dir, "http", NULL);
274 	new_filename = g_build_filename (new_cache_dir, "http", NULL);
275 	shell_xdg_migrate_rename (old_filename, new_filename);
276 	g_free (old_filename);
277 	g_free (new_filename);
278 
279 	old_filename = g_build_filename (old_cache_dir, "tmp", NULL);
280 	new_filename = g_build_filename (new_cache_dir, "tmp", NULL);
281 	shell_xdg_migrate_rename (old_filename, new_filename);
282 	g_free (old_filename);
283 	g_free (new_filename);
284 
285 	/* Try to remove the old cache directory.  Good chance this will
286 	 * fail on the first try, since E-D-S puts stuff here too. */
287 	shell_xdg_migrate_rmdir (old_cache_dir);
288 
289 	g_free (old_cache_dir);
290 }
291 
292 static void
293 shell_xdg_migrate_config_dir_common (EShell *shell,
294                                      const gchar *old_base_dir,
295                                      const gchar *backend_name)
296 {
297 	GDir *dir;
298 	const gchar *user_config_dir;
299 	gchar *old_config_dir;
300 	gchar *new_config_dir;
301 	gchar *old_filename;
302 	gchar *new_filename;
303 	gchar *dirname;
304 
305 	user_config_dir = e_get_user_config_dir ();
306 
307 	old_config_dir = g_build_filename (old_base_dir, backend_name, NULL);
308 	new_config_dir = g_build_filename (user_config_dir, backend_name, NULL);
309 
310 	g_mkdir_with_parents (new_config_dir, 0700);
311 
312 	old_filename = g_build_filename (old_config_dir, "views", NULL);
313 	new_filename = g_build_filename (new_config_dir, "views", NULL);
314 	shell_xdg_migrate_rename_files (old_filename, new_filename);
315 	g_free (old_filename);
316 	g_free (new_filename);
317 
318 	old_filename = g_build_filename (old_config_dir, "searches.xml", NULL);
319 	new_filename = g_build_filename (new_config_dir, "searches.xml", NULL);
320 	shell_xdg_migrate_rename (old_filename, new_filename);
321 	g_free (old_filename);
322 	g_free (new_filename);
323 
324 	/* This one only occurs in calendar and memos.
325 	 * For other backends this will just be a no-op. */
326 	old_filename = g_build_filename (
327 		old_config_dir, "config", "MemoPad", NULL);
328 	new_filename = g_build_filename (new_config_dir, "MemoPad", NULL);
329 	shell_xdg_migrate_rename (old_filename, new_filename);
330 	g_free (old_filename);
331 	g_free (new_filename);
332 
333 	/* This one only occurs in calendar and tasks.
334 	 * For other backends this will just be a no-op. */
335 	old_filename = g_build_filename (
336 		old_config_dir, "config", "TaskPad", NULL);
337 	new_filename = g_build_filename (new_config_dir, "TaskPad", NULL);
338 	shell_xdg_migrate_rename (old_filename, new_filename);
339 	g_free (old_filename);
340 	g_free (new_filename);
341 
342 	/* Subtle name change: config/state --> state.ini */
343 	old_filename = g_build_filename (old_config_dir, "config", "state", NULL);
344 	new_filename = g_build_filename (new_config_dir, "state.ini", NULL);
345 	shell_xdg_migrate_rename (old_filename, new_filename);
346 	g_free (old_filename);
347 	g_free (new_filename);
348 
349 	/* GIO had a bug for awhile where it would leave behind an empty
350 	 * temp file with the pattern .goutputstream-XXXXXX if an output
351 	 * stream operation was cancelled.  We've had several reports of
352 	 * these files in config directories, so remove any we find. */
353 	dirname = g_build_filename (old_config_dir, "config", NULL);
354 	dir = g_dir_open (dirname, 0, NULL);
355 	if (dir != NULL) {
356 		const gchar *basename;
357 
358 		while ((basename = g_dir_read_name (dir)) != NULL) {
359 			gchar *filename;
360 			struct stat st;
361 
362 			if (!g_str_has_prefix (basename, ".goutputstream"))
363 				continue;
364 
365 			filename = g_build_filename (dirname, basename, NULL);
366 
367 			/* Verify the file is indeed empty. */
368 			if (g_stat (filename, &st) == 0 && st.st_size == 0)
369 				g_unlink (filename);
370 
371 			g_free (filename);
372 		}
373 
374 		g_dir_close (dir);
375 	}
376 	g_free (dirname);
377 
378 	g_free (old_config_dir);
379 	g_free (new_config_dir);
380 }
381 
382 static void
383 shell_xdg_migrate_config_dir_mail (EShell *shell,
384                                    const gchar *old_base_dir)
385 {
386 	const gchar *user_config_dir;
387 	gchar *old_config_dir;
388 	gchar *new_config_dir;
389 	gchar *old_filename;
390 	gchar *new_filename;
391 
392 	user_config_dir = e_get_user_config_dir ();
393 
394 	old_config_dir = g_build_filename (old_base_dir, "mail", NULL);
395 	new_config_dir = g_build_filename (user_config_dir, "mail", NULL);
396 
397 	old_filename = g_build_filename (old_config_dir, "filters.xml", NULL);
398 	new_filename = g_build_filename (new_config_dir, "filters.xml", NULL);
399 	shell_xdg_migrate_rename (old_filename, new_filename);
400 	g_free (old_filename);
401 	g_free (new_filename);
402 
403 	old_filename = g_build_filename (old_config_dir, "vfolders.xml", NULL);
404 	new_filename = g_build_filename (new_config_dir, "vfolders.xml", NULL);
405 	shell_xdg_migrate_rename (old_filename, new_filename);
406 	g_free (old_filename);
407 	g_free (new_filename);
408 
409 	/* I hate this file.  GtkHtml uses style properties for fonts. */
410 	old_filename = g_build_filename (
411 		old_config_dir, "config", "gtkrc-mail-fonts", NULL);
412 	new_filename = g_build_filename (
413 		new_config_dir, "gtkrc-mail-fonts", NULL);
414 	shell_xdg_migrate_rename (old_filename, new_filename);
415 	g_free (old_filename);
416 	g_free (new_filename);
417 
418 	/* This file is no longer used.  Try removing it. */
419 	old_filename = g_build_filename (
420 		old_config_dir, "config",
421 		"folder-tree-expand-state.xml", NULL);
422 	g_unlink (old_filename);
423 	g_free (old_filename);
424 
425 	/* Everything else in the "config" directory just should be
426 	 * per-folder ETree files recording the expanded state of mail
427 	 * threads.  Rename this directory to "folders". */
428 	old_filename = g_build_filename (old_config_dir, "config", NULL);
429 	new_filename = g_build_filename (new_config_dir, "folders", NULL);
430 	shell_xdg_migrate_rename_files (old_filename, new_filename);
431 	g_free (old_filename);
432 	g_free (new_filename);
433 
434 	g_free (old_config_dir);
435 	g_free (new_config_dir);
436 }
437 
438 static void
439 shell_xdg_migrate_dir_cleanup (EShell *shell,
440                                       const gchar *old_base_dir,
441                                       const gchar *backend_name,
442                                       const gchar *dir_name)
443 {
444 	gchar *dirname;
445 
446 	dirname = g_build_filename (
447 		old_base_dir, backend_name, dir_name, NULL);
448 
449 	shell_xdg_migrate_rmdir (dirname);
450 
451 	g_free (dirname);
452 }
453 
454 static void
455 shell_xdg_migrate_config_dir (EShell *shell,
456                               const gchar *old_base_dir)
457 {
458 	const gchar *old_config_dir;
459 	const gchar *new_config_dir;
460 	gchar *old_filename;
461 	gchar *new_filename;
462 	gint ii;
463 
464 	g_print ("Migrating config data\n");
465 
466 	/* Some files are common to all shell backends. */
467 	for (ii = 0; shell_backend_names[ii] != NULL; ii++)
468 		shell_xdg_migrate_config_dir_common (
469 			shell, old_base_dir, shell_backend_names[ii]);
470 
471 	/* Handle backend-specific files. */
472 	shell_xdg_migrate_config_dir_mail (shell, old_base_dir);
473 
474 	/* Remove leftover config directories. */
475 	for (ii = 0; shell_backend_names[ii] != NULL; ii++) {
476 		shell_xdg_migrate_dir_cleanup (
477 			shell, old_base_dir, shell_backend_names[ii], "config");
478 		shell_xdg_migrate_dir_cleanup (
479 			shell, old_base_dir, shell_backend_names[ii], "views");
480 	}
481 
482 	/*** Miscellaneous configuration files. ***/
483 
484 	old_config_dir = old_base_dir;
485 	new_config_dir = e_get_user_config_dir ();
486 
487 	/* Subtle name change: datetime-formats --> datetime-formats.ini */
488 	old_filename = g_build_filename (old_config_dir, "datetime-formats", NULL);
489 	new_filename = g_build_filename (new_config_dir, "datetime-formats.ini", NULL);
490 	shell_xdg_migrate_rename (old_filename, new_filename);
491 	g_free (old_filename);
492 	g_free (new_filename);
493 
494 	/* Subtle name change: printing --> printing.ini */
495 	old_filename = g_build_filename (old_config_dir, "printing", NULL);
496 	new_filename = g_build_filename (new_config_dir, "printing.ini", NULL);
497 	shell_xdg_migrate_rename (old_filename, new_filename);
498 	g_free (old_filename);
499 	g_free (new_filename);
500 }
501 
502 static void
503 shell_xdg_migrate_data_dir (EShell *shell,
504                             const gchar *old_base_dir)
505 {
506 	GDir *dir;
507 	GHashTable *corrections;
508 	const gchar *basename;
509 	const gchar *old_data_dir;
510 	const gchar *new_data_dir;
511 	gchar *src_directory;
512 	gchar *dst_directory;
513 
514 	g_print ("Migrating local user data\n");
515 
516 	old_data_dir = old_base_dir;
517 	new_data_dir = e_get_user_data_dir ();
518 
519 	/* The mail hierarchy is complex and Camel doesn't distinguish
520 	 * between user data files and disposable cache files, so just
521 	 * move everything to the data directory for now.  We'll sort
522 	 * it out sometime down the road. */
523 
524 	src_directory = g_build_filename (old_data_dir, "mail", NULL);
525 	dst_directory = g_build_filename (new_data_dir, "mail", NULL);
526 
527 	dir = g_dir_open (src_directory, 0, NULL);
528 	if (dir == NULL)
529 		goto skip_mail;
530 
531 	/* This is to avoid removing directories while we're iterating
532 	 * over the parent directory.  POSIX says the outcome of that
533 	 * is unspecified. */
534 	corrections = g_hash_table_new_full (
535 		g_str_hash, g_str_equal,
536 		(GDestroyNotify) g_free,
537 		(GDestroyNotify) g_free);
538 
539 	/* Iterate over the base CamelProvider directories. */
540 	while ((basename = g_dir_read_name (dir)) != NULL) {
541 		gchar *provider_src_directory;
542 		gchar *provider_dst_directory;
543 
544 		provider_src_directory =
545 			g_build_filename (src_directory, basename, NULL);
546 		provider_dst_directory =
547 			g_build_filename (dst_directory, basename, NULL);
548 
549 		if (!g_file_test (provider_src_directory, G_FILE_TEST_IS_DIR)) {
550 			g_free (provider_src_directory);
551 			g_free (provider_dst_directory);
552 			continue;
553 		}
554 
555 		shell_xdg_migrate_move_contents (
556 			provider_src_directory, provider_dst_directory);
557 
558 		g_hash_table_insert (corrections, provider_src_directory, NULL);
559 		g_free (provider_dst_directory);
560 	}
561 
562 	g_dir_close (dir);
563 
564 	/* Remove the old base CamelProvider directories. */
565 	shell_xdg_migrate_process_corrections (corrections);
566 	g_hash_table_destroy (corrections);
567 
568 skip_mail:
569 
570 	g_free (src_directory);
571 	g_free (dst_directory);
572 
573 	/* We don't want to move the source directory directly because the
574 	 * destination directory may already exist with content.  Instead
575 	 * we want to merge the content of the source directory into the
576 	 * destination directory.
577 	 *
578 	 * For example, given:
579 	 *
580 	 *    $(src_directory)/A   and   $(dst_directory)/B
581 	 *    $(src_directory)/C
582 	 *
583 	 * we want to end up with:
584 	 *
585 	 *    $(dst_directory)/A
586 	 *    $(dst_directory)/B
587 	 *    $(dst_directory)/C
588 	 *
589 	 * Any name collisions will be left in the source directory.
590 	 */
591 
592 	src_directory = g_build_filename (old_data_dir, "signatures", NULL);
593 	dst_directory = g_build_filename (new_data_dir, "signatures", NULL);
594 
595 	shell_xdg_migrate_move_contents (src_directory, dst_directory);
596 	shell_xdg_migrate_rmdir (src_directory);
597 
598 	g_free (src_directory);
599 	g_free (dst_directory);
600 
601 	/* Move all remaining regular files to the new data directory. */
602 
603 	dir = g_dir_open (old_data_dir, 0, NULL);
604 	if (dir == NULL)
605 		return;
606 
607 	/* This is to avoid renaming files while we're iterating over the
608 	 * directory.  POSIX says the outcome of that is unspecified. */
609 	corrections = g_hash_table_new_full (
610 		g_str_hash, g_str_equal,
611 		(GDestroyNotify) g_free,
612 		(GDestroyNotify) g_free);
613 
614 	while ((basename = g_dir_read_name (dir)) != NULL) {
615 		gchar *old_filename;
616 		gchar *new_filename;
617 
618 		old_filename = g_build_filename (old_data_dir, basename, NULL);
619 		new_filename = g_build_filename (new_data_dir, basename, NULL);
620 
621 		/* If we encounter a directory, try removing it.  This
622 		 * will only work if the directory is empty, so there's
623 		 * no risk of data loss. */
624 		if (g_file_test (old_filename, G_FILE_TEST_IS_DIR)) {
625 			shell_xdg_migrate_rmdir (old_filename);
626 			g_free (old_filename);
627 			g_free (new_filename);
628 			continue;
629 		}
630 
631 		g_hash_table_insert (corrections, old_filename, new_filename);
632 	}
633 
634 	g_dir_close (dir);
635 
636 	shell_xdg_migrate_process_corrections (corrections);
637 	g_hash_table_destroy (corrections);
638 }
639 
640 static void
641 shell_migrate_to_xdg_base_dirs (EShell *shell)
642 {
643 	const gchar *home_dir;
644 	gchar *old_base_dir;
645 
646 	g_return_if_fail (E_IS_SHELL (shell));
647 
648 	/* XXX This blocks, but it's all just local file
649 	 *     renames so it should be nearly instantaneous. */
650 
651 	home_dir = g_get_home_dir ();
652 	old_base_dir = g_build_filename (home_dir, ".evolution", NULL);
653 
654 	/* Is there even anything to migrate? */
655 	if (!g_file_test (old_base_dir, G_FILE_TEST_IS_DIR))
656 		goto exit;
657 
658 	shell_xdg_migrate_cache_dir (shell, old_base_dir);
659 	shell_xdg_migrate_config_dir (shell, old_base_dir);
660 	shell_xdg_migrate_data_dir (shell, old_base_dir);
661 
662 	/* Try to remove the old base directory.  Good chance this will
663 	 * fail on the first try, since Evolution puts stuff here too. */
664 	g_rmdir (old_base_dir);
665 
666 exit:
667 	g_free (old_base_dir);
668 }
669 
670 /********************* End XDG Base Directory Migration *********************/
671 
672 static gboolean
673 shell_migrate_attempt (EShell *shell,
674                        gint major,
675                        gint minor,
676                        gint micro)
677 {
678 	GtkWindow *parent;
679 	GList *backends;
680 	gboolean success = TRUE;
681 
682 	parent = e_shell_get_active_window (shell);
683 	backends = e_shell_get_shell_backends (shell);
684 
685 	/* New user accounts have nothing to migrate. */
686 	if (major == 0 && minor == 0 && micro == 0)
687 		return TRUE;
688 
689 	/* We only support migrating from version 2 now. */
690 	if (major < 2) {
691 		gchar *version;
692 		gint response;
693 
694 		version = g_strdup_printf ("%d.%d", major, minor);
695 		response = e_alert_run_dialog_for_args (
696 			parent, "shell:upgrade-version-too-old",
697 			version, NULL);
698 		g_free (version);
699 
700 		return (response == GTK_RESPONSE_OK);
701 	}
702 
703 	/* Ask each of the shell backends to migrate their own data.
704 	 * XXX If something fails the user may end up with only partially
705 	 *     migrated data.  Need transaction semantics here, but how? */
706 	while (success && backends != NULL) {
707 		EShellBackend *shell_backend = backends->data;
708 		GError *error = NULL;
709 
710 		success = e_shell_backend_migrate (
711 			shell_backend, major, minor, micro, &error);
712 
713 		if (error != NULL) {
714 			gint response;
715 
716 			response = e_alert_run_dialog_for_args (
717 				parent, "shell:upgrade-failed",
718 				error->message, NULL);
719 
720 			success = (response == GTK_RESPONSE_OK);
721 
722 			g_error_free (error);
723 		}
724 
725 		backends = g_list_next (backends);
726 	}
727 
728 	return success;
729 }
730 
731 static void
732 shell_migrate_get_version (EShell *shell,
733                            gint *major,
734                            gint *minor,
735                            gint *micro)
736 {
737 	GSettings *settings;
738 	gchar *string;
739 
740 	*major = 0;
741 	*minor = 0;
742 	*micro = 0;
743 
744 	settings = g_settings_new ("org.gnome.evolution");
745 	string = g_settings_get_string (settings, "version");
746 
747 	if (string != NULL) {
748 		/* Since 1.4.0 we've kept the version key in GSettings. */
749 		sscanf (string, "%d.%d.%d", major, minor, micro);
750 		g_free (string);
751 	}
752 
753 	g_object_unref (settings);
754 }
755 
756 static gboolean
757 shell_migrate_downgraded (gint previous_major,
758                           gint previous_minor,
759                           gint previous_micro)
760 {
761 	gboolean downgraded;
762 
763 	/* This could just be a single boolean expression,
764 	 * but I find this form easier to understand. */
765 
766 	if (previous_major == EVO_MAJOR_VERSION) {
767 		if (previous_minor == EVO_MINOR_VERSION) {
768 			downgraded = (previous_micro > EVO_MICRO_VERSION);
769 		} else {
770 			downgraded = (previous_minor > EVO_MINOR_VERSION);
771 		}
772 	} else {
773 		downgraded = (previous_major > EVO_MAJOR_VERSION);
774 	}
775 
776 	return downgraded;
777 }
778 
779 static void
780 change_dir_modes (const gchar *path)
781 {
782 	GDir *dir;
783 	GError *err = NULL;
784 	const gchar *file = NULL;
785 
786 	dir = g_dir_open (path, 0, &err);
787 	if (err) {
788 		g_warning ("Error opening directory %s: %s \n", path, err->message);
789 		g_clear_error (&err);
790 		return;
791 	}
792 
793 	while ((file = g_dir_read_name (dir))) {
794 		gchar *full_path = g_build_filename (path, file, NULL);
795 
796 		if (g_file_test (full_path, G_FILE_TEST_IS_DIR))
797 			change_dir_modes (full_path);
798 
799 		g_free (full_path);
800 	}
801 
802 	g_chmod (path, 0700);
803 	g_dir_close (dir);
804 }
805 
806 static void
807 fix_folder_permissions (const gchar *data_dir)
808 {
809 	struct stat sb;
810 
811 	if (g_stat (data_dir, &sb) == -1) {
812 		g_warning ("error stat: %s \n", data_dir);
813 		return;
814 	}
815 
816 	if (((guint32) sb.st_mode & 0777) != 0700)
817 		change_dir_modes (data_dir);
818 }
819 
820 static void
821 shell_migrate_save_current_version (void)
822 {
823 	GSettings *settings;
824 	gchar *version;
825 
826 	/* Save the version after the startup wizard has had a chance to
827 	 * run.  If the user chooses to restore data and settings from a
828 	 * backup, Evolution will restart and the restored data may need
829 	 * to be migrated.
830 	 *
831 	 * If we save the version before the restart, then Evolution will
832 	 * think it has already migrated data and settings to the current
833 	 * version and the restored data may not be handled properly.
834 	 *
835 	 * This implies an awareness of module behavior from within the
836 	 * application core, but practical considerations overrule here. */
837 
838 	settings = g_settings_new ("org.gnome.evolution");
839 
840 	version = g_strdup_printf (
841 		"%d.%d.%d",
842 		EVO_MAJOR_VERSION,
843 		EVO_MINOR_VERSION,
844 		EVO_MICRO_VERSION);
845 	g_settings_set_string (settings, "version", version);
846 	g_free (version);
847 
848 	g_object_unref (settings);
849 }
850 
851 static void
852 shell_migrate_ready_to_start_event_cb (EShell *shell)
853 {
854 	shell_migrate_save_current_version ();
855 }
856 
857 gboolean
858 e_shell_migrate_attempt (EShell *shell)
859 {
860 	ESEvent *ese;
861 	gint major, minor, micro;
862 
863 	g_return_val_if_fail (E_IS_SHELL (shell), FALSE);
864 
865 	shell_migrate_get_version (shell, &major, &minor, &micro);
866 
867 	/* Abort all migration if the user downgraded. */
868 	if (shell_migrate_downgraded (major, minor, micro))
869 		return TRUE;
870 
871 	/* Migrate to XDG Base Directories first, so shell backends
872 	 * don't have to deal with legacy data and cache directories. */
873 	shell_migrate_to_xdg_base_dirs (shell);
874 
875 	/* This sets the folder permissions to S_IRWXU if needed */
876 	if (major <= 2 && minor <= 30)
877 		fix_folder_permissions (e_get_user_data_dir ());
878 
879 	/* Attempt to run migration all the time and let the backend
880 	 * make the choice */
881 	if (!shell_migrate_attempt (shell, major, minor, micro))
882 		_exit (EXIT_SUCCESS);
883 
884 	/* We want our handler to run last, hence g_signal_connect_after(). */
885 	g_signal_connect_after (
886 		shell, "event::ready-to-start",
887 		G_CALLBACK (shell_migrate_ready_to_start_event_cb), NULL);
888 
889 	/** @Event: Shell attempted upgrade
890 	 * @Id: upgrade.done
891 	 * @Target: ESMenuTargetState
892 	 *
893 	 * This event is emitted whenever the shell successfully attempts
894 	 * an upgrade.
895 	 **/
896 	ese = es_event_peek ();
897 	e_event_emit (
898 		(EEvent *) ese, "upgrade.done",
899 		(EEventTarget *) es_event_target_new_upgrade (
900 		ese, EVO_MAJOR_VERSION, EVO_MINOR_VERSION, EVO_MICRO_VERSION));
901 
902 	return TRUE;
903 }
904 
905 GQuark
906 e_shell_migrate_error_quark (void)
907 {
908 	static GQuark quark = 0;
909 
910 	if (G_UNLIKELY (quark == 0))
911 		quark = g_quark_from_static_string (
912 			"e-shell-migrate-error-quark");
913 
914 	return quark;
915 }