1 /*
2 * evolution-backup-tool.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <glib/gi18n.h>
29 #include <glib/gstdio.h>
30 #include <gtk/gtk.h>
31
32 #include <libedataserver/libedataserver.h>
33
34 #ifdef G_OS_WIN32
35 #ifdef DATADIR
36 #undef DATADIR
37 #endif
38 #include <windows.h>
39 #include <conio.h>
40 #ifndef PROCESS_DEP_ENABLE
41 #define PROCESS_DEP_ENABLE 0x00000001
42 #endif
43 #ifndef PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
44 #define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION 0x00000002
45 #endif
46 #endif
47
48 #include "e-util/e-util-private.h"
49 #include "e-util/e-util.h"
50
51 #define EVOUSERDATADIR_MAGIC "#EVO_USERDATADIR#"
52
53 #define EVOLUTION "evolution"
54 #define EVOLUTION_DIR "$DATADIR/"
55 #define EVOLUTION_DIR_FILE EVOLUTION ".dir"
56 #define DBUS_SOURCE_REGISTRY_SERVICE_FILE "$DBUSDATADIR/org.gnome.evolution.dataserver.Sources.service"
57
58 #define ANCIENT_GCONF_DUMP_FILE "backup-restore-gconf.xml"
59
60 #define DCONF_DUMP_FILE_EDS "backup-restore-dconf-eds.ini"
61 #define DCONF_DUMP_FILE_EVO "backup-restore-dconf-evo.ini"
62
63 #define DCONF_PATH_EDS "/org/gnome/evolution-data-server/"
64 #define DCONF_PATH_EVO "/org/gnome/evolution/"
65
66 #define KEY_FILE_GROUP "Evolution Backup"
67
68 static gboolean backup_op = FALSE;
69 static gchar *bk_file = NULL;
70 static gboolean restore_op = FALSE;
71 static gchar *res_file = NULL;
72 static gboolean check_op = FALSE;
73 static gchar *chk_file = NULL;
74 static gboolean restart_arg = FALSE;
75 static gboolean gui_arg = FALSE;
76 static gchar **opt_remaining = NULL;
77 static gint result = 0;
78 static GtkWidget *progress_dialog;
79 static GtkWidget *pbar;
80 static gchar *txt = NULL;
81
82 static GOptionEntry options[] = {
83 { "backup", '\0', 0, G_OPTION_ARG_NONE, &backup_op,
84 N_("Back up Evolution directory"), NULL },
85 { "restore", '\0', 0, G_OPTION_ARG_NONE, &restore_op,
86 N_("Restore Evolution directory"), NULL },
87 { "check", '\0', 0, G_OPTION_ARG_NONE, &check_op,
88 N_("Check Evolution Back up"), NULL },
89 { "restart", '\0', 0, G_OPTION_ARG_NONE, &restart_arg,
90 N_("Restart Evolution"), NULL },
91 { "gui", '\0', 0, G_OPTION_ARG_NONE, &gui_arg,
92 N_("With Graphical User Interface"), NULL },
93 { G_OPTION_REMAINING, '\0', 0,
94 G_OPTION_ARG_STRING_ARRAY, &opt_remaining },
95 { NULL }
96 };
97
98 #define d(x)
99
100 #define print_and_run(x) \
101 G_STMT_START { g_message ("%s", x); system (x); } G_STMT_END
102
103 static gboolean check (const gchar *filename, gboolean *is_new_format);
104
105 static GString *
106 replace_string (const gchar *text,
107 const gchar *find,
108 const gchar *replace)
109 {
110 const gchar *p, *next;
111 GString *str;
112 gint find_len;
113
114 g_return_val_if_fail (text != NULL, NULL);
115 g_return_val_if_fail (find != NULL, NULL);
116 g_return_val_if_fail (*find, NULL);
117
118 find_len = strlen (find);
119 str = g_string_new ("");
120
121 p = text;
122 while (next = strstr (p, find), next) {
123 if (p < next)
124 g_string_append_len (str, p, next - p);
125
126 if (replace && *replace)
127 g_string_append (str, replace);
128
129 p = next + find_len;
130 }
131
132 g_string_append (str, p);
133
134 return str;
135 }
136
137 static const gchar *
138 strip_home_dir (const gchar *dir)
139 {
140 const gchar *home_dir, *res;
141
142 g_return_val_if_fail (dir != NULL, NULL);
143
144 home_dir = g_get_home_dir ();
145 g_return_val_if_fail (home_dir != NULL, dir);
146 g_return_val_if_fail (*home_dir != '\0', dir);
147
148 res = dir;
149 if (g_str_has_prefix (res, home_dir))
150 res += strlen (home_dir);
151
152 if (*res == G_DIR_SEPARATOR)
153 res++;
154
155 return res;
156 }
157
158 static GString *
159 replace_variables (const gchar *str,
160 gboolean remove_dir_sep)
161 {
162 GString *res = NULL, *use;
163 const gchar *strip_datadir, *strip_configdir;
164
165 g_return_val_if_fail (str != NULL, NULL);
166
167 strip_datadir = strip_home_dir (e_get_user_data_dir ());
168 strip_configdir = strip_home_dir (e_get_user_config_dir ());
169
170 #define repl(_find, _replace) \
171 use = replace_string (res ? res->str : str, _find, _replace); \
172 g_return_val_if_fail (use != NULL, NULL); \
173 if (res) \
174 g_string_free (res, TRUE); \
175 res = use;
176
177 repl ("$HOME", g_get_home_dir ());
178 repl ("$TMP", g_get_tmp_dir ());
179 repl ("$DATADIR", e_get_user_data_dir ());
180 repl ("$CONFIGDIR", e_get_user_config_dir ());
181 repl ("$STRIPDATADIR", strip_datadir);
182 repl ("$STRIPCONFIGDIR", strip_configdir);
183 repl ("$DBUSDATADIR", DBUS_SERVICES_DIR);
184
185 #undef repl
186
187 g_return_val_if_fail (res != NULL, NULL);
188
189 if (remove_dir_sep) {
190 /* remove trailing dir separator */
191 while (res->len > 0 && res->str[res->len - 1] == G_DIR_SEPARATOR) {
192 g_string_truncate (res, res->len - 1);
193 }
194 }
195
196 return res;
197 }
198
199 static void
200 replace_in_file (const gchar *filename,
201 const gchar *find,
202 const gchar *replace)
203 {
204 gchar *content = NULL;
205 GError *error = NULL;
206 GString *filenamestr = NULL;
207
208 g_return_if_fail (filename != NULL);
209 g_return_if_fail (find != NULL);
210 g_return_if_fail (*find);
211 g_return_if_fail (replace != NULL);
212
213 if (strstr (filename, "$")) {
214 filenamestr = replace_variables (filename, TRUE);
215
216 if (!filenamestr) {
217 g_warning (
218 "%s: Replace variables in '%s' failed!",
219 G_STRFUNC, filename);
220 return;
221 }
222
223 filename = filenamestr->str;
224 }
225
226 if (g_file_get_contents (filename, &content, NULL, &error)) {
227 GString *str = replace_string (content, find, replace);
228
229 if (str) {
230 if (!g_file_set_contents (filename, str->str, -1, &error) && error) {
231 g_warning (
232 "%s: cannot write file content, "
233 "error: %s", G_STRFUNC, error->message);
234 g_error_free (error);
235 }
236
237 g_string_free (str, TRUE);
238 } else {
239 g_warning (
240 "%s: Replace of '%s' to '%s' failed!",
241 G_STRFUNC, find, replace);
242 }
243
244 g_free (content);
245 } else if (error) {
246 g_warning (
247 "%s: Cannot read file content, error: %s",
248 G_STRFUNC, error->message);
249 g_error_free (error);
250 }
251
252 if (filenamestr)
253 g_string_free (filenamestr, TRUE);
254 }
255
256 static void
257 run_cmd (const gchar *cmd)
258 {
259 if (!cmd)
260 return;
261
262 if (strstr (cmd, "$") != NULL) {
263 /* read the doc for g_get_home_dir to know why replacing it here */
264 GString *str = replace_variables (cmd, FALSE);
265
266 if (str) {
267 print_and_run (str->str);
ignoring return value of 'system', declared with attribute warn_unused_result
(emitted by gcc)
268 g_string_free (str, TRUE);
269 }
270 } else
271 print_and_run (cmd);
ignoring return value of 'system', declared with attribute warn_unused_result
(emitted by gcc)
272 }
273
274 static void
275 run_evolution_no_wait (void)
276 {
277 g_spawn_command_line_async (EVOLUTION, NULL);
278 }
279
280 static void
281 write_dir_file (void)
282 {
283 GString *content, *filename;
284 GError *error = NULL;
285
286 filename = replace_variables ("$HOME/" EVOLUTION_DIR_FILE, TRUE);
287 g_return_if_fail (filename != NULL);
288
289 content = replace_variables (
290 "[" KEY_FILE_GROUP "]\n"
291 "Version=" VERSION "\n"
292 "UserDataDir=$STRIPDATADIR\n"
293 "UserConfigDir=$STRIPCONFIGDIR\n"
294 , TRUE);
295 g_return_if_fail (content != NULL);
296
297 g_file_set_contents (filename->str, content->str, content->len, &error);
298
299 if (error) {
300 g_warning ("Failed to write file '%s': %s\n", filename->str, error->message);
301 g_error_free (error);
302 }
303
304 g_string_free (filename, TRUE);
305 g_string_free (content, TRUE);
306 }
307
308 static void
309 backup (const gchar *filename,
310 GCancellable *cancellable)
311 {
312 gchar *command;
313 gchar *quotedfname;
314
315 g_return_if_fail (filename && *filename);
316 quotedfname = g_shell_quote (filename);
317
318 if (g_cancellable_is_cancelled (cancellable))
319 return;
320
321 txt = _("Shutting down Evolution");
322 /* FIXME Will the versioned setting always work? */
323 run_cmd (EVOLUTION " --quit");
324
325 run_cmd ("rm $DATADIR/.running");
326
327 if (g_cancellable_is_cancelled (cancellable))
328 return;
329
330 txt = _("Backing Evolution accounts and settings");
331 run_cmd ("dconf dump " DCONF_PATH_EDS " >" EVOLUTION_DIR DCONF_DUMP_FILE_EDS);
332 run_cmd ("dconf dump " DCONF_PATH_EVO " >" EVOLUTION_DIR DCONF_DUMP_FILE_EVO);
333
334 replace_in_file (
335 EVOLUTION_DIR DCONF_DUMP_FILE_EDS,
336 e_get_user_data_dir (), EVOUSERDATADIR_MAGIC);
337
338 replace_in_file (
339 EVOLUTION_DIR DCONF_DUMP_FILE_EVO,
340 e_get_user_data_dir (), EVOUSERDATADIR_MAGIC);
341
342 write_dir_file ();
343
344 if (g_cancellable_is_cancelled (cancellable))
345 return;
346
347 txt = _("Backing Evolution data (Mails, Contacts, Calendar, Tasks, Memos)");
348
349 /* FIXME stay on this file system ,other options?" */
350 /* FIXME compression type?" */
351 /* FIXME date/time stamp?" */
352 /* FIXME backup location?" */
353 command = g_strdup_printf (
354 "cd $HOME && tar chf - $STRIPDATADIR "
355 "$STRIPCONFIGDIR " EVOLUTION_DIR_FILE " | "
356 "gzip > %s", quotedfname);
357 run_cmd (command);
358 g_free (command);
359 g_free (quotedfname);
360
361 run_cmd ("rm $HOME/" EVOLUTION_DIR_FILE);
362
363 txt = _("Back up complete");
364
365 if (restart_arg) {
366
367 if (g_cancellable_is_cancelled (cancellable))
368 return;
369
370 txt = _("Restarting Evolution");
371 run_evolution_no_wait ();
372 }
373
374 }
375
376 static void
377 extract_backup_data (const gchar *filename,
378 gchar **restored_version,
379 gchar **data_dir,
380 gchar **config_dir)
381 {
382 GKeyFile *key_file;
383 GError *error = NULL;
384
385 g_return_if_fail (filename != NULL);
386 g_return_if_fail (data_dir != NULL);
387 g_return_if_fail (config_dir != NULL);
388
389 key_file = g_key_file_new ();
390 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error);
391
392 if (error) {
393 g_warning ("Failed to read '%s': %s", filename, error->message);
394 g_error_free (error);
395
396 /* This is the current format as of Evolution 3.6. */
397 } else if (g_key_file_has_group (key_file, KEY_FILE_GROUP)) {
398 gchar *tmp;
399
400 tmp = g_key_file_get_value (
401 key_file, KEY_FILE_GROUP, "Version", NULL);
402 if (tmp != NULL)
403 *restored_version = g_strstrip (g_strdup (tmp));
404 g_free (tmp);
405
406 tmp = g_key_file_get_value (
407 key_file, KEY_FILE_GROUP, "UserDataDir", NULL);
408 if (tmp != NULL)
409 *data_dir = g_shell_quote (tmp);
410 g_free (tmp);
411
412 tmp = g_key_file_get_value (
413 key_file, KEY_FILE_GROUP, "UserConfigDir", NULL);
414 if (tmp != NULL)
415 *config_dir = g_shell_quote (tmp);
416 g_free (tmp);
417
418 /* This is the legacy format with no version information. */
419 } else if (g_key_file_has_group (key_file, "dirs")) {
420 gchar *tmp;
421
422 tmp = g_key_file_get_value (key_file, "dirs", "data", NULL);
423 if (tmp)
424 *data_dir = g_shell_quote (tmp);
425 g_free (tmp);
426
427 tmp = g_key_file_get_value (key_file, "dirs", "config", NULL);
428 if (tmp)
429 *config_dir = g_shell_quote (tmp);
430 g_free (tmp);
431 }
432
433 g_key_file_free (key_file);
434 }
435
436 static gint
437 get_dir_level (const gchar *dir)
438 {
439 gint res = 0, i;
440
441 g_return_val_if_fail (dir != NULL, -1);
442
443 for (i = 0; dir[i]; i++) {
444 if (dir[i] == '/' || dir[i] == '\\')
445 res++;
446 }
447
448 if (i > 0)
449 res++;
450
451 return res;
452 }
453
454 static gchar *
455 get_source_manager_reload_command (void)
456 {
457 GString *tmp;
458 gchar *command;
459
460 tmp = replace_variables (DBUS_SOURCE_REGISTRY_SERVICE_FILE, TRUE);
461 if (tmp) {
462 GKeyFile *key_file;
463 gchar *str = NULL;
464
465 key_file = g_key_file_new ();
466 if (g_key_file_load_from_file (key_file, tmp->str, G_KEY_FILE_NONE, NULL)) {
467 str = g_key_file_get_string (key_file, "D-BUS Service", "Name", NULL);
468 }
469 g_key_file_free (key_file);
470
471 if (str && *str) {
472 g_string_assign (tmp, str);
473 } else {
474 g_string_free (tmp, TRUE);
475 tmp = NULL;
476 }
477
478 g_free (str);
479 }
480
481 if (!tmp)
482 tmp = g_string_new ("org.gnome.evolution.dataserver.Sources0");
483
484 command = g_strdup_printf ("gdbus call --session --dest %s "
485 "--object-path /org/gnome/evolution/dataserver/SourceManager "
486 "--method org.gnome.evolution.dataserver.SourceManager.Reload",
487 tmp->str);
488
489 g_string_free (tmp, TRUE);
490
491 return command;
492 }
493
494 static void
495 restore (const gchar *filename,
496 GCancellable *cancellable)
497 {
498 gchar *command;
499 gchar *quotedfname;
500 gboolean is_new_format = FALSE;
501
502 g_return_if_fail (filename && *filename);
503
504 if (!check (filename, &is_new_format)) {
505 g_message ("Cannot restore from an incorrect archive '%s'.", filename);
506 goto end;
507 }
508
509 quotedfname = g_shell_quote (filename);
510
511 if (g_cancellable_is_cancelled (cancellable))
512 return;
513
514 /* FIXME Will the versioned setting always work? */
515 txt = _("Shutting down Evolution");
516 run_cmd (EVOLUTION " --quit");
517
518 if (g_cancellable_is_cancelled (cancellable))
519 return;
520
521 txt = _("Back up current Evolution data");
522 run_cmd ("mv $DATADIR $DATADIR_old");
523 run_cmd ("mv $CONFIGDIR $CONFIGDIR_old");
524
525 if (g_cancellable_is_cancelled (cancellable))
526 return;
527
528 txt = _("Extracting files from back up");
529
530 if (is_new_format) {
531 GString *dir_fn;
532 gchar *data_dir = NULL;
533 gchar *config_dir = NULL;
534 gchar *restored_version = NULL;
535
536 command = g_strdup_printf (
537 "cd $TMP && tar xzf %s "
538 EVOLUTION_DIR_FILE, quotedfname);
539 run_cmd (command);
540 g_free (command);
541
542 dir_fn = replace_variables ("$TMP" G_DIR_SEPARATOR_S EVOLUTION_DIR_FILE, TRUE);
543 if (!dir_fn) {
544 g_warning ("Failed to create evolution's dir filename");
545 goto end;
546 }
547
548 /* data_dir and config_dir are quoted inside extract_backup_data */
549 extract_backup_data (
550 dir_fn->str,
551 &restored_version,
552 &data_dir,
553 &config_dir);
554
555 g_unlink (dir_fn->str);
556 g_string_free (dir_fn, TRUE);
557
558 if (!data_dir || !config_dir) {
559 g_warning (
560 "Failed to get old data_dir (%p)/"
561 "config_dir (%p)", data_dir, config_dir);
562 g_free (data_dir);
563 g_free (config_dir);
564 goto end;
565 }
566
567 g_mkdir_with_parents (e_get_user_data_dir (), 0700);
568 g_mkdir_with_parents (e_get_user_config_dir (), 0700);
569
570 command = g_strdup_printf (
571 "cd $DATADIR && tar xzf %s %s --strip-components=%d",
572 quotedfname, data_dir, get_dir_level (data_dir));
573 run_cmd (command);
574 g_free (command);
575
576 command = g_strdup_printf (
577 "cd $CONFIGDIR && tar xzf %s %s --strip-components=%d",
578 quotedfname, config_dir, get_dir_level (config_dir));
579 run_cmd (command);
580 g_free (command);
581
582 /* If the back file had version information, set the last
583 * used version in GSettings before restarting Evolution. */
584 if (restored_version != NULL && *restored_version != '\0') {
585 GSettings *settings;
586
587 settings = g_settings_new ("org.gnome.evolution");
588 g_settings_set_string (
589 settings, "version", restored_version);
590 g_object_unref (settings);
591 }
592
593 g_free (data_dir);
594 g_free (config_dir);
595 g_free (restored_version);
596 } else {
597 run_cmd ("mv $HOME/.evolution $HOME/.evolution_old");
598
599 command = g_strdup_printf (
600 "cd $HOME && gzip -cd %s | tar xf -", quotedfname);
601 run_cmd (command);
602 g_free (command);
603 }
604
605 g_free (quotedfname);
606
607 if (g_cancellable_is_cancelled (cancellable))
608 return;
609
610 txt = _("Loading Evolution settings");
611
612 if (is_new_format) {
613 /* new format has it in DATADIR... */
614 GString *file = replace_variables (EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE, TRUE);
615 if (file && g_file_test (file->str, G_FILE_TEST_EXISTS)) {
616 /* ancient backup */
617 replace_in_file (
618 EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE,
619 EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
620 run_cmd ("gconftool-2 --load " EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE);
621
622 /* give a chance to GConf to save what was loaded into a disk */
623 g_usleep (G_USEC_PER_SEC * 5);
624
625 /* do not forget to convert GConf keys into GSettings */
626 run_cmd ("gsettings-data-convert");
627 run_cmd ("rm " EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE);
628 } else {
629 replace_in_file (
630 EVOLUTION_DIR DCONF_DUMP_FILE_EDS,
631 EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
632 run_cmd ("cat " EVOLUTION_DIR DCONF_DUMP_FILE_EDS " | dconf load " DCONF_PATH_EDS);
633 run_cmd ("rm " EVOLUTION_DIR DCONF_DUMP_FILE_EDS);
634
635 replace_in_file (
636 EVOLUTION_DIR DCONF_DUMP_FILE_EVO,
637 EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
638 run_cmd ("cat " EVOLUTION_DIR DCONF_DUMP_FILE_EVO " | dconf load " DCONF_PATH_EVO);
639 run_cmd ("rm " EVOLUTION_DIR DCONF_DUMP_FILE_EVO);
640 }
641
642 g_string_free (file, TRUE);
643 } else {
644 gchar *gconf_dump_file;
645
646 /* ... old format in ~/.evolution */
647 gconf_dump_file = g_build_filename (
648 "$HOME", ".evolution", ANCIENT_GCONF_DUMP_FILE, NULL);
649
650 replace_in_file (
651 gconf_dump_file,
652 EVOUSERDATADIR_MAGIC,
653 e_get_user_data_dir ());
654
655 command = g_strconcat (
656 "gconftool-2 --load ", gconf_dump_file, NULL);
657 run_cmd (command);
658 g_free (command);
659
660 /* give a chance to GConf to save what was loaded into a disk */
661 g_usleep (G_USEC_PER_SEC * 5);
662
663 /* do not forget to convert GConf keys into GSettings */
664 run_cmd ("gsettings-data-convert");
665
666 command = g_strconcat ("rm ", gconf_dump_file, NULL);
667 run_cmd (command);
668 g_free (command);
669
670 g_free (gconf_dump_file);
671 }
672
673 if (g_cancellable_is_cancelled (cancellable))
674 return;
675
676 txt = _("Removing temporary back up files");
677 run_cmd ("rm -rf $DATADIR_old");
678 run_cmd ("rm -rf $CONFIGDIR_old");
679 run_cmd ("rm $DATADIR/.running");
680
681 if (!is_new_format)
682 run_cmd ("rm -rf $HOME/.evolution_old");
683
684 if (g_cancellable_is_cancelled (cancellable))
685 return;
686
687 txt = _("Reloading registry service");
688
689 /* wait few seconds, till changes settle */
690 g_usleep (G_USEC_PER_SEC * 5);
691
692 command = get_source_manager_reload_command ();
693 /* This runs migration routines on the newly-restored data. */
694 run_cmd (command);
695 g_free (command);
696
697 end:
698 if (restart_arg) {
699 if (g_cancellable_is_cancelled (cancellable))
700 return;
701
702 txt = _("Restarting Evolution");
703
704 /* wait 5 seconds before restarting evolution, thus any
705 changes being done are updated in source registry too */
706 g_usleep (G_USEC_PER_SEC * 5);
707
708 run_evolution_no_wait ();
709 }
710 }
711
712 static gboolean
713 check (const gchar *filename,
714 gboolean *is_new_format)
715 {
716 gchar *command;
717 gchar *quotedfname;
718 gboolean is_new = TRUE;
719
720 g_return_val_if_fail (filename && *filename, FALSE);
721 quotedfname = g_shell_quote (filename);
722
723 if (is_new_format)
724 *is_new_format = FALSE;
725
726 command = g_strdup_printf ("tar ztf %s 1>/dev/null", quotedfname);
727 result = system (command);
728 g_free (command);
729
730 g_message ("First result %d", result);
731 if (result) {
732 g_free (quotedfname);
733 return FALSE;
734 }
735
736 command = g_strdup_printf (
737 "tar ztf %s | grep -e \"%s$\"",
738 quotedfname, EVOLUTION_DIR_FILE);
739 result = system (command);
740 g_free (command);
741
742 if (result) {
743 command = g_strdup_printf (
744 "tar ztf %s | grep -e \"^\\.evolution/$\"",
745 quotedfname);
746 result = system (command);
747 g_free (command);
748 is_new = FALSE;
749 }
750
751 g_message ("Second result %d", result);
752 if (result) {
753 g_free (quotedfname);
754 return FALSE;
755 }
756
757 if (is_new) {
758 if (is_new_format)
759 *is_new_format = TRUE;
760 g_free (quotedfname);
761 return TRUE;
762 }
763
764 command = g_strdup_printf (
765 "tar ztf %s | grep -e \"^\\.evolution/%s$\"",
766 quotedfname, ANCIENT_GCONF_DUMP_FILE);
767 result = system (command);
768 g_free (command);
769
770 if (result != 0) {
771 /* maybe it's an ancient backup */
772 command = g_strdup_printf (
773 "tar ztf %s | grep -e \"^\\.evolution/%s$\"",
774 quotedfname, ANCIENT_GCONF_DUMP_FILE);
775 result = system (command);
776 g_free (command);
777 }
778
779 g_free (quotedfname);
780
781 g_message ("Third result %d", result);
782
783 return result == 0;
784 }
785
786 static gboolean
787 pbar_update (GCancellable *cancellable)
788 {
789 gtk_progress_bar_pulse ((GtkProgressBar *) pbar);
790 gtk_progress_bar_set_text ((GtkProgressBar *) pbar, txt);
791
792 /* Return TRUE to reschedule the timeout. */
793 return !g_cancellable_is_cancelled (cancellable);
794 }
795
796 static gboolean
797 finish_job (gpointer user_data)
798 {
799 gtk_main_quit ();
800
801 return FALSE;
802 }
803
804 static gboolean
805 start_job (GIOSchedulerJob *job,
806 GCancellable *cancellable,
807 gpointer user_data)
808 {
809 if (backup_op)
810 backup (bk_file, cancellable);
811 else if (restore_op)
812 restore (res_file, cancellable);
813 else if (check_op)
814 check (chk_file, NULL); /* not cancellable */
815
816 g_io_scheduler_job_send_to_mainloop_async (
817 job, finish_job, NULL, (GDestroyNotify) NULL);
818
819 return FALSE;
820 }
821
822 static void
823 dlg_response (GtkWidget *dlg,
824 gint response,
825 GCancellable *cancellable)
826 {
827 /* We will cancel only backup/restore
828 * operations and not the check operation. */
829 g_cancellable_cancel (cancellable);
830
831 /* If the response is not of delete_event then destroy the event. */
832 if (response != GTK_RESPONSE_NONE)
833 gtk_widget_destroy (dlg);
834
835 /* We will kill just the tar operation. Rest of
836 * them will be just a second of microseconds.*/
837 run_cmd ("pkill tar");
838
839 if (bk_file && backup_op && response == GTK_RESPONSE_REJECT) {
840 /* Backup was canceled, delete the
841 * backup file as it is not needed now. */
842 gchar *cmd, *filename;
843
844 g_message ("Back up canceled, removing partial back up file.");
845
846 filename = g_shell_quote (bk_file);
847 cmd = g_strconcat ("rm ", filename, NULL);
848
849 run_cmd (cmd);
850
851 g_free (cmd);
852 g_free (filename);
853 }
854
855 gtk_main_quit ();
856 }
857
858 gint
859 main (gint argc,
860 gchar **argv)
861 {
862 GCancellable *cancellable;
863 gchar *file = NULL, *oper = NULL;
864 const gchar *title = NULL;
865 gint ii;
866 GError *error = NULL;
867
868 #ifdef G_OS_WIN32
869 /* Reduce risks */
870 {
871 typedef BOOL (WINAPI *t_SetDllDirectoryA) (LPCSTR lpPathName);
872 t_SetDllDirectoryA p_SetDllDirectoryA;
873
874 p_SetDllDirectoryA = GetProcAddress (
875 GetModuleHandle ("kernel32.dll"),
876 "SetDllDirectoryA");
877
878 if (p_SetDllDirectoryA != NULL)
879 p_SetDllDirectoryA ("");
880 }
881 #ifndef _WIN64
882 {
883 typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags);
884 t_SetProcessDEPPolicy p_SetProcessDEPPolicy;
885
886 p_SetProcessDEPPolicy = GetProcAddress (
887 GetModuleHandle ("kernel32.dll"),
888 "SetProcessDEPPolicy");
889
890 if (p_SetProcessDEPPolicy)
891 p_SetProcessDEPPolicy (
892 PROCESS_DEP_ENABLE |
893 PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
894 }
895 #endif
896 #endif
897
898 bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
899 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
900 textdomain (GETTEXT_PACKAGE);
901
902 gtk_init_with_args (
903 &argc, &argv, NULL, options, GETTEXT_PACKAGE, &error);
904
905 if (error != NULL) {
906 g_printerr ("%s\n", error->message);
907 g_error_free (error);
908 exit (EXIT_FAILURE);
909 }
910
911 if (opt_remaining != NULL) {
912 for (ii = 0; ii < g_strv_length (opt_remaining); ii++) {
913 if (backup_op) {
914 title = _("Evolution Back Up");
915 oper = _("Backing up to the folder %s");
916 bk_file = g_strdup ((gchar *) opt_remaining[ii]);
917 file = bk_file;
918 } else if (restore_op) {
919 title = _("Evolution Restore");
920 oper = _("Restoring from the folder %s");
921 res_file = g_strdup ((gchar *) opt_remaining[ii]);
922 file = res_file;
923 } else if (check_op) {
924 d (g_message ("Checking %s", (gchar *) opt_remaining[ii]));
925 chk_file = g_strdup ((gchar *) opt_remaining[ii]);
926 }
927 }
928 }
929
930 cancellable = g_cancellable_new ();
931
932 if (gui_arg && !check_op) {
933 GtkWidget *widget, *container;
934 GtkWidget *action_area;
935 GtkWidget *content_area;
936 const gchar *txt, *txt2;
937 gchar *str = NULL;
938 gchar *markup;
939
940 gtk_window_set_default_icon_name ("evolution");
941
942 /* Backup / Restore only can have GUI.
943 * We should restrict the rest. */
944 progress_dialog = gtk_dialog_new_with_buttons (
945 title, NULL,
946 GTK_DIALOG_MODAL,
947 GTK_STOCK_CANCEL,
948 GTK_RESPONSE_REJECT,
949 NULL);
950
951 gtk_container_set_border_width (
952 GTK_CONTAINER (progress_dialog), 12);
953
954 action_area = gtk_dialog_get_action_area (
955 GTK_DIALOG (progress_dialog));
956 content_area = gtk_dialog_get_content_area (
957 GTK_DIALOG (progress_dialog));
958
959 /* Override GtkDialog defaults */
960 gtk_box_set_spacing (GTK_BOX (content_area), 12);
961 gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
962 gtk_box_set_spacing (GTK_BOX (action_area), 12);
963 gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
964
965 if (oper && file)
966 str = g_strdup_printf (oper, file);
967
968 container = gtk_grid_new ();
969 gtk_grid_set_column_spacing (GTK_GRID (container), 6);
970 gtk_grid_set_row_spacing (GTK_GRID (container), 0);
971 gtk_widget_show (container);
972
973 gtk_box_pack_start (
974 GTK_BOX (content_area), container, FALSE, TRUE, 0);
975
976 widget = gtk_image_new_from_stock (
977 GTK_STOCK_COPY, GTK_ICON_SIZE_DIALOG);
978 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
979 gtk_widget_show (widget);
980
981 gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 3);
982 g_object_set (G_OBJECT (widget),
983 "halign", GTK_ALIGN_FILL,
984 "valign", GTK_ALIGN_FILL,
985 "vexpand", TRUE,
986 NULL);
987
988 if (backup_op) {
989 txt = _("Backing up Evolution Data");
990 txt2 = _("Please wait while Evolution is backing up your data.");
991 } else if (restore_op) {
992 txt = _("Restoring Evolution Data");
993 txt2 = _("Please wait while Evolution is restoring your data.");
994 } else {
995 g_return_val_if_reached (EXIT_FAILURE);
996 }
997
998 markup = g_markup_printf_escaped ("<b><big>%s</big></b>", txt);
999 widget = gtk_label_new (markup);
1000 gtk_label_set_line_wrap (GTK_LABEL (widget), FALSE);
1001 gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
1002 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
1003 gtk_widget_show (widget);
1004 g_free (markup);
1005
1006 gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1);
1007 g_object_set (G_OBJECT (widget),
1008 "halign", GTK_ALIGN_FILL,
1009 "hexpand", TRUE,
1010 "valign", GTK_ALIGN_FILL,
1011 NULL);
1012
1013 markup = g_strconcat (
1014 txt2, " ", _("This may take a while depending "
1015 "on the amount of data in your account."), NULL);
1016 widget = gtk_label_new (markup);
1017 gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
1018 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
1019 gtk_widget_show (widget);
1020 g_free (markup);
1021
1022 gtk_grid_attach (GTK_GRID (container), widget, 1, 1, 1, 1);
1023 g_object_set (G_OBJECT (widget),
1024 "halign", GTK_ALIGN_FILL,
1025 "hexpand", TRUE,
1026 "valign", GTK_ALIGN_FILL,
1027 NULL);
1028
1029 pbar = gtk_progress_bar_new ();
1030
1031 if (str != NULL) {
1032 markup = g_markup_printf_escaped ("<i>%s</i>", str);
1033 widget = gtk_label_new (markup);
1034 gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
1035 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
1036 g_free (markup);
1037 g_free (str);
1038
1039 gtk_grid_attach (GTK_GRID (container), widget, 1, 2, 1, 1);
1040 g_object_set (G_OBJECT (widget),
1041 "halign", GTK_ALIGN_FILL,
1042 "hexpand", TRUE,
1043 "valign", GTK_ALIGN_FILL,
1044 NULL);
1045
1046 gtk_grid_attach (GTK_GRID (container), pbar, 1, 3, 1, 1);
1047 } else
1048 gtk_grid_attach (GTK_GRID (container), pbar, 1, 2, 1, 1);
1049
1050 g_object_set (G_OBJECT (pbar),
1051 "halign", GTK_ALIGN_FILL,
1052 "hexpand", TRUE,
1053 "valign", GTK_ALIGN_FILL,
1054 NULL);
1055
1056 g_signal_connect (
1057 progress_dialog, "response",
1058 G_CALLBACK (dlg_response), cancellable);
1059 gtk_widget_show_all (progress_dialog);
1060
1061 } else if (check_op) {
1062 /* For sanity we don't need gui */
1063 check (chk_file, NULL);
1064 exit (result == 0 ? 0 : 1);
1065 }
1066
1067 if (gui_arg)
1068 g_timeout_add_full (
1069 G_PRIORITY_DEFAULT, 50,
1070 (GSourceFunc) pbar_update,
1071 g_object_ref (cancellable),
1072 (GDestroyNotify) g_object_unref);
1073
1074 g_io_scheduler_push_job (
1075 start_job, NULL,
1076 (GDestroyNotify) NULL,
1077 G_PRIORITY_DEFAULT, cancellable);
1078
1079 gtk_main ();
1080
1081 g_object_unref (cancellable);
1082
1083 return result;
1084 }