No issues found
1 /*
2 * evolution-backup-restore.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 #include <config.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #ifdef HAVE_SYS_WAIT_H
23 # include <sys/wait.h>
24 #endif
25 #include <stdlib.h>
26
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 #include <glib/gstdio.h>
30
31 #include <libebackend/libebackend.h>
32
33 #include <mail/e-mail-config-assistant.h>
34 #include <libevolution-utils/e-alert-dialog.h>
35 #include <e-util/e-util.h>
36 #include <e-util/e-dialog-utils.h>
37 #include <shell/e-shell-utils.h>
38 #include <shell/e-shell-window.h>
39
40 #include "e-mail-config-restore-page.h"
41 #include "e-mail-config-restore-ready-page.h"
42
43 #ifdef G_OS_WIN32
44 #ifdef localtime_r
45 #undef localtime_r
46 #endif
47 /* The localtime() in Microsoft's C library *is* thread-safe */
48 #define localtime_r(timep, result) \
49 (localtime (timep) ? memcpy ( \
50 (result), localtime (timep), sizeof (*(result))) : 0)
51 #endif
52
53 typedef EExtension EvolutionBackupRestoreAssistant;
54 typedef EExtensionClass EvolutionBackupRestoreAssistantClass;
55
56 typedef EExtension EvolutionBackupRestoreMenuItems;
57 typedef EExtensionClass EvolutionBackupRestoreMenuItemsClass;
58
59 /* Module Entry Points */
60 void e_module_load (GTypeModule *type_module);
61 void e_module_unload (GTypeModule *type_module);
62
63 /* Forward Declarations */
64 GType evolution_backup_restore_assistant_get_type (void);
65 GType evolution_backup_restore_menu_items_get_type (void);
66
67 static const gchar *ui =
68 "<ui>"
69 " <menubar name='main-menu'>"
70 " <menu action='file-menu'>"
71 " <placeholder name='file-actions'>"
72 " <menuitem action='settings-backup'/>"
73 " <menuitem action='settings-restore'/>"
74 " </placeholder>"
75 " </menu>"
76 " </menubar>"
77 "</ui>";
78
79 G_DEFINE_DYNAMIC_TYPE (
80 EvolutionBackupRestoreAssistant,
81 evolution_backup_restore_assistant,
82 E_TYPE_EXTENSION)
83
84 G_DEFINE_DYNAMIC_TYPE (
85 EvolutionBackupRestoreMenuItems,
86 evolution_backup_restore_menu_items,
87 E_TYPE_EXTENSION)
88
89 enum {
90 BR_OK = 1 << 0,
91 BR_START = 1 << 1
92 };
93
94 static void
95 backup (const gchar *filename,
96 gboolean restart)
97 {
98 if (restart)
99 execl (
100 EVOLUTION_TOOLSDIR "/evolution-backup",
101 "evolution-backup",
102 "--gui",
103 "--backup",
104 "--restart",
105 filename,
106 NULL);
107 else
108 execl (
109 EVOLUTION_TOOLSDIR "/evolution-backup",
110 "evolution-backup",
111 "--gui",
112 "--backup",
113 filename,
114 NULL);
115 }
116
117 static void
118 restore (const gchar *filename,
119 gboolean restart)
120 {
121 if (restart)
122 execl (
123 EVOLUTION_TOOLSDIR "/evolution-backup",
124 "evolution-backup",
125 "--gui",
126 "--restore",
127 "--restart",
128 filename,
129 NULL);
130 else
131 execl (
132 EVOLUTION_TOOLSDIR "/evolution-backup",
133 "evolution-backup",
134 "--gui",
135 "--restore",
136 filename,
137 NULL);
138 }
139
140 static guint32
141 dialog_prompt_user (GtkWindow *parent,
142 const gchar *string,
143 const gchar *tag,
144 ...)
145 {
146 GtkWidget *dialog;
147 GtkWidget *check = NULL;
148 GtkWidget *container;
149 va_list ap;
150 gint button;
151 guint32 mask = 0;
152 EAlert *alert = NULL;
153
154 va_start (ap, tag);
155 alert = e_alert_new_valist (tag, ap);
156 va_end (ap);
157
158 dialog = e_alert_dialog_new (parent, alert);
159 g_object_unref (alert);
160
161 container = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
162
163 check = gtk_check_button_new_with_mnemonic (string);
164 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE);
165 gtk_box_pack_start (GTK_BOX (container), check, FALSE, FALSE, 0);
166 gtk_widget_show (check);
167
168 button = gtk_dialog_run (GTK_DIALOG (dialog));
169
170 if (button == GTK_RESPONSE_YES)
171 mask |= BR_OK;
172 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)))
173 mask |= BR_START;
174
175 gtk_widget_destroy (dialog);
176
177 return mask;
178 }
179
180 static void
181 set_local_only (GtkFileChooser *file_chooser)
182 {
183 /* XXX Has to be a local file, since the backup utility
184 * takes a filename argument, not a URI. */
185 gtk_file_chooser_set_local_only (file_chooser, TRUE);
186 }
187
188 static gchar *
189 suggest_file_name (void)
190 {
191 time_t t;
192 struct tm tm;
193
194 t = time (NULL);
195 localtime_r (&t, &tm);
196
197 return g_strdup_printf (
198 "evolution-backup-%04d%02d%02d.tar.gz",
199 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
200 }
201
202 static void
203 action_settings_backup_cb (GtkAction *action,
204 EShellWindow *shell_window)
205 {
206 GFile *file;
207 GFile *parent;
208 GFileInfo *file_info;
209 const gchar *attribute;
210 GError *error = NULL;
211 gchar *suggest;
212
213 suggest = suggest_file_name ();
214
215 file = e_shell_run_save_dialog (
216 e_shell_window_get_shell (shell_window),
217 _("Select name of the Evolution backup file"),
218 suggest, "*.tar.gz", (GtkCallback)
219 set_local_only, NULL);
220
221 g_free (suggest);
222
223 if (file == NULL)
224 return;
225
226 /* Make sure the parent directory can be written to. */
227
228 parent = g_file_get_parent (file);
229 attribute = G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE;
230
231 /* XXX The query operation blocks the main loop but we
232 * know it's a local file, so let it slide for now. */
233 file_info = g_file_query_info (
234 parent, attribute, G_FILE_QUERY_INFO_NONE, NULL, &error);
235
236 g_object_unref (parent);
237
238 if (error != NULL) {
239 g_warning ("%s", error->message);
240 g_error_free (error);
241 return;
242 }
243
244 if (g_file_info_get_attribute_boolean (file_info, attribute)) {
245 guint32 mask;
246 gchar *path;
247
248 mask = dialog_prompt_user (
249 GTK_WINDOW (shell_window),
250 _("_Restart Evolution after backup"),
251 "org.gnome.backup-restore:backup-confirm", NULL);
252 if (mask & BR_OK) {
253 path = g_file_get_path (file);
254 backup (path, (mask & BR_START) ? TRUE: FALSE);
255 g_free (path);
256 }
257 } else {
258 e_alert_run_dialog_for_args (
259 GTK_WINDOW (shell_window),
260 "org.gnome.backup-restore:insufficient-permissions",
261 NULL);
262 }
263
264 g_object_unref (file_info);
265 g_object_unref (file);
266 }
267
268 static void
269 action_settings_restore_cb (GtkAction *action,
270 EShellWindow *shell_window)
271 {
272 GFile *file;
273 gchar *path;
274
275 file = e_shell_run_open_dialog (
276 e_shell_window_get_shell (shell_window),
277 _("Select name of the Evolution backup file to restore"),
278 (GtkCallback) set_local_only, NULL);
279
280 if (file == NULL)
281 return;
282
283 path = g_file_get_path (file);
284
285 if (evolution_backup_restore_validate_backup_file (path)) {
286 guint32 mask;
287
288 mask = dialog_prompt_user (
289 GTK_WINDOW (shell_window),
290 _("_Restart Evolution after restore"),
291 "org.gnome.backup-restore:restore-confirm", NULL);
292 if (mask & BR_OK)
293 restore (path, mask & BR_START);
294 } else {
295 e_alert_run_dialog_for_args (
296 GTK_WINDOW (shell_window),
297 "org.gnome.backup-restore:invalid-backup", NULL);
298 }
299
300 g_object_unref (file);
301 g_free (path);
302 }
303
304 static GtkActionEntry entries[] = {
305
306 { "settings-backup",
307 NULL,
308 N_("_Back up Evolution Data..."),
309 NULL,
310 N_("Back up Evolution data and settings to an archive file"),
311 G_CALLBACK (action_settings_backup_cb) },
312
313 { "settings-restore",
314 NULL,
315 N_("R_estore Evolution Data..."),
316 NULL,
317 N_("Restore Evolution data and settings from an archive file"),
318 G_CALLBACK (action_settings_restore_cb) }
319 };
320
321 static gboolean
322 evolution_backup_restore_filename_to_visible (GBinding *binding,
323 const GValue *source_value,
324 GValue *target_value,
325 gpointer unused)
326 {
327 const gchar *filename;
328 gboolean visible;
329
330 filename = g_value_get_string (source_value);
331 visible = (filename != NULL && *filename != '\0');
332 g_value_set_boolean (target_value, visible);
333
334 return TRUE;
335 }
336
337 static void
338 evolution_backup_restore_prepare_cb (GtkAssistant *assistant,
339 GtkWidget *page,
340 EMailConfigRestorePage *restore_page)
341 {
342 const gchar *filename;
343
344 /* If we've landed on the EMailConfigRestoreReadyPage, that
345 * means the user has chosen a valid backup file to restore
346 * so start the "evolution-backup" tool immediately. */
347
348 filename = e_mail_config_restore_page_get_filename (restore_page);
349
350 if (E_IS_MAIL_CONFIG_RESTORE_READY_PAGE (page))
351 restore (filename, TRUE);
352 }
353
354 static void
355 evolution_backup_restore_assistant_constructed (GObject *object)
356 {
357 EExtension *extension;
358 EExtensible *extensible;
359 EMailConfigAssistant *assistant;
360 const gchar *type_name;
361
362 extension = E_EXTENSION (object);
363 extensible = e_extension_get_extensible (extension);
364
365 /* Chain up to parent's constructed() method. */
366 G_OBJECT_CLASS (evolution_backup_restore_assistant_parent_class)->
367 constructed (object);
368
369 assistant = E_MAIL_CONFIG_ASSISTANT (extensible);
370
371 /* XXX We only want to add the EMailConfigRestorePage to an
372 * EStartupAssistant instance, not a normal EMailConfigAssistant.
373 * But EStartupAssistant is defined in the "startup-wizard" module
374 * and we can't access its GType without knowing its type name, so
375 * just hard-code the type name. */
376 type_name = G_OBJECT_TYPE_NAME (assistant);
377 if (g_strcmp0 (type_name, "EStartupAssistant") == 0) {
378 EMailConfigPage *restore_page;
379 EMailConfigPage *ready_page;
380
381 restore_page = e_mail_config_restore_page_new ();
382 e_mail_config_assistant_add_page (assistant, restore_page);
383
384 ready_page = e_mail_config_restore_ready_page_new ();
385 e_mail_config_assistant_add_page (assistant, ready_page);
386
387 g_object_bind_property_full (
388 restore_page, "filename",
389 ready_page, "visible",
390 G_BINDING_SYNC_CREATE,
391 evolution_backup_restore_filename_to_visible,
392 NULL,
393 NULL, (GDestroyNotify) NULL);
394
395 g_signal_connect (
396 assistant, "prepare",
397 G_CALLBACK (evolution_backup_restore_prepare_cb),
398 restore_page);
399 }
400 }
401
402 static void
403 evolution_backup_restore_assistant_class_init (EExtensionClass *class)
404 {
405 GObjectClass *object_class;
406
407 object_class = G_OBJECT_CLASS (class);
408 object_class->constructed = evolution_backup_restore_assistant_constructed;
409
410 class->extensible_type = E_TYPE_MAIL_CONFIG_ASSISTANT;
411 }
412
413 static void
414 evolution_backup_restore_assistant_class_finalize (EExtensionClass *class)
415 {
416 }
417
418 static void
419 evolution_backup_restore_assistant_init (EExtension *extension)
420 {
421 }
422
423 static void
424 evolution_backup_restore_menu_items_constructed (GObject *object)
425 {
426 EExtension *extension;
427 EExtensible *extensible;
428 EShellWindow *shell_window;
429 GtkActionGroup *action_group;
430 GtkUIManager *ui_manager;
431 GError *error = NULL;
432
433 extension = E_EXTENSION (object);
434 extensible = e_extension_get_extensible (extension);
435
436 /* Chain up to parent's constructed() method. */
437 G_OBJECT_CLASS (evolution_backup_restore_menu_items_parent_class)->
438 constructed (object);
439
440 shell_window = E_SHELL_WINDOW (extensible);
441 action_group = e_shell_window_get_action_group (shell_window, "shell");
442
443 /* Add actions to the "shell" action group. */
444 gtk_action_group_add_actions (
445 action_group, entries,
446 G_N_ELEMENTS (entries), shell_window);
447
448 /* Because we are loading from a hard-coded string, there is
449 * no chance of I/O errors. Failure here implies a malformed
450 * UI definition. Full stop. */
451 ui_manager = e_shell_window_get_ui_manager (shell_window);
452 gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
453 if (error != NULL)
454 g_error ("%s", error->message);
455 }
456
457 static void
458 evolution_backup_restore_menu_items_class_init (EExtensionClass *class)
459 {
460 GObjectClass *object_class;
461 EExtensionClass *extension_class;
462
463 object_class = G_OBJECT_CLASS (class);
464 object_class->constructed = evolution_backup_restore_menu_items_constructed;
465
466 extension_class = E_EXTENSION_CLASS (class);
467 extension_class->extensible_type = E_TYPE_SHELL_WINDOW;
468 }
469
470 static void
471 evolution_backup_restore_menu_items_class_finalize (EExtensionClass *class)
472 {
473 }
474
475 static void
476 evolution_backup_restore_menu_items_init (EExtension *extension)
477 {
478 }
479
480 G_MODULE_EXPORT void
481 e_module_load (GTypeModule *type_module)
482 {
483 evolution_backup_restore_assistant_register_type (type_module);
484 evolution_backup_restore_menu_items_register_type (type_module);
485
486 e_mail_config_restore_page_type_register (type_module);
487 e_mail_config_restore_ready_page_type_register (type_module);
488 }
489
490 G_MODULE_EXPORT void
491 e_module_unload (GTypeModule *type_module)
492 {
493 }