No issues found
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 |
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, µ);
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 }