1 /*
2 * e-mail-store-utils.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "evolution-config.h"
19
20 #include "e-mail-folder-utils.h"
21 #include "e-mail-utils.h"
22
23 #include "e-mail-store-utils.h"
24
25 #include <glib/gi18n-lib.h>
26
27 typedef struct _AsyncContext AsyncContext;
28
29 struct _AsyncContext {
30 gchar *full_name;
31 };
32
33 static void
async_context_free(AsyncContext * context)34 async_context_free (AsyncContext *context)
35 {
36 g_free (context->full_name);
37
38 g_slice_free (AsyncContext, context);
39 }
40
41 gboolean
e_mail_store_create_folder_sync(CamelStore * store,const gchar * full_name,GCancellable * cancellable,GError ** error)42 e_mail_store_create_folder_sync (CamelStore *store,
43 const gchar *full_name,
44 GCancellable *cancellable,
45 GError **error)
46 {
47 CamelFolderInfo *folder_info;
48 gchar *copied_full_name;
49 gchar *display_name;
50 const gchar *parent;
51 gboolean success = TRUE;
52
53 g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
54 g_return_val_if_fail (full_name != NULL, FALSE);
55
56 copied_full_name = g_strdup (full_name);
57 display_name = strrchr (copied_full_name, '/');
58 if (display_name == NULL) {
59 display_name = copied_full_name;
60 parent = "";
61 } else {
62 *display_name++ = '\0';
63 parent = copied_full_name;
64 }
65
66 folder_info = camel_store_create_folder_sync (
67 store, parent, display_name, cancellable, error);
68
69 g_free (copied_full_name);
70
71 if (folder_info == NULL)
72 return FALSE;
73
74 if (CAMEL_IS_SUBSCRIBABLE (store))
75 success = camel_subscribable_subscribe_folder_sync (
76 CAMEL_SUBSCRIBABLE (store),
77 full_name, cancellable, error);
78
79 camel_folder_info_free (folder_info);
80
81 return success;
82 }
83
84 /* Helper for e_mail_store_create_folder() */
85 static void
mail_store_create_folder_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)86 mail_store_create_folder_thread (GSimpleAsyncResult *simple,
87 GObject *source_object,
88 GCancellable *cancellable)
89 {
90 AsyncContext *context;
91 GError *local_error = NULL;
92
93 context = g_simple_async_result_get_op_res_gpointer (simple);
94
95 e_mail_store_create_folder_sync (
96 CAMEL_STORE (source_object),
97 context->full_name,
98 cancellable, &local_error);
99
100 if (local_error != NULL)
101 g_simple_async_result_take_error (simple, local_error);
102 }
103
104 void
e_mail_store_create_folder(CamelStore * store,const gchar * full_name,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)105 e_mail_store_create_folder (CamelStore *store,
106 const gchar *full_name,
107 gint io_priority,
108 GCancellable *cancellable,
109 GAsyncReadyCallback callback,
110 gpointer user_data)
111 {
112 GSimpleAsyncResult *simple;
113 AsyncContext *context;
114
115 g_return_if_fail (CAMEL_IS_STORE (store));
116 g_return_if_fail (full_name != NULL);
117
118 context = g_slice_new0 (AsyncContext);
119 context->full_name = g_strdup (full_name);
120
121 simple = g_simple_async_result_new (
122 G_OBJECT (store), callback, user_data,
123 e_mail_store_create_folder);
124
125 g_simple_async_result_set_check_cancellable (simple, cancellable);
126
127 g_simple_async_result_set_op_res_gpointer (
128 simple, context, (GDestroyNotify) async_context_free);
129
130 g_simple_async_result_run_in_thread (
131 simple, mail_store_create_folder_thread,
132 io_priority, cancellable);
133
134 g_object_unref (simple);
135 }
136
137 gboolean
e_mail_store_create_folder_finish(CamelStore * store,GAsyncResult * result,GError ** error)138 e_mail_store_create_folder_finish (CamelStore *store,
139 GAsyncResult *result,
140 GError **error)
141 {
142 GSimpleAsyncResult *simple;
143
144 g_return_val_if_fail (
145 g_simple_async_result_is_valid (
146 result, G_OBJECT (store),
147 e_mail_store_create_folder), FALSE);
148
149 simple = G_SIMPLE_ASYNC_RESULT (result);
150
151 /* Assume success unless a GError is set. */
152 return !g_simple_async_result_propagate_error (simple, error);
153 }
154
155 /* Helper for e_mail_store_go_offline() */
156 static void
mail_store_go_offline_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)157 mail_store_go_offline_thread (GSimpleAsyncResult *simple,
158 GObject *source_object,
159 GCancellable *cancellable)
160 {
161 GError *local_error = NULL;
162
163 e_mail_store_go_offline_sync (
164 CAMEL_STORE (source_object), cancellable, &local_error);
165
166 if (local_error != NULL)
167 g_simple_async_result_take_error (simple, local_error);
168 }
169
170 gboolean
e_mail_store_go_offline_sync(CamelStore * store,GCancellable * cancellable,GError ** error)171 e_mail_store_go_offline_sync (CamelStore *store,
172 GCancellable *cancellable,
173 GError **error)
174 {
175 CamelService *service;
176 const gchar *display_name;
177 gboolean success = TRUE;
178
179 g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
180
181 service = CAMEL_SERVICE (store);
182
183 display_name = camel_service_get_display_name (service);
184 if (display_name == NULL || *display_name == '\0')
185 display_name = G_OBJECT_TYPE_NAME (service);
186
187 camel_operation_push_message (
188 cancellable, _("Disconnecting from “%s”"), display_name);
189
190 if (CAMEL_IS_OFFLINE_STORE (store)) {
191 success = camel_offline_store_set_online_sync (
192 CAMEL_OFFLINE_STORE (store),
193 FALSE, cancellable, error);
194
195 } else {
196 success = camel_service_disconnect_sync (
197 service, TRUE, cancellable, error);
198 }
199
200 camel_operation_pop_message (cancellable);
201
202 return success;
203 }
204
205 void
e_mail_store_go_offline(CamelStore * store,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)206 e_mail_store_go_offline (CamelStore *store,
207 gint io_priority,
208 GCancellable *cancellable,
209 GAsyncReadyCallback callback,
210 gpointer user_data)
211 {
212 GSimpleAsyncResult *simple;
213
214 g_return_if_fail (CAMEL_IS_STORE (store));
215
216 simple = g_simple_async_result_new (
217 G_OBJECT (store), callback,
218 user_data, e_mail_store_go_offline);
219
220 g_simple_async_result_set_check_cancellable (simple, cancellable);
221
222 g_simple_async_result_run_in_thread (
223 simple, mail_store_go_offline_thread,
224 io_priority, cancellable);
225
226 g_object_unref (simple);
227 }
228
229 gboolean
e_mail_store_go_offline_finish(CamelStore * store,GAsyncResult * result,GError ** error)230 e_mail_store_go_offline_finish (CamelStore *store,
231 GAsyncResult *result,
232 GError **error)
233 {
234 GSimpleAsyncResult *simple;
235
236 g_return_val_if_fail (
237 g_simple_async_result_is_valid (
238 result, G_OBJECT (store), e_mail_store_go_offline), FALSE);
239
240 simple = G_SIMPLE_ASYNC_RESULT (result);
241
242 /* Assume success unless a GError is set. */
243 return !g_simple_async_result_propagate_error (simple, error);
244 }
245
246 gboolean
e_mail_store_go_online_sync(CamelStore * store,GCancellable * cancellable,GError ** error)247 e_mail_store_go_online_sync (CamelStore *store,
248 GCancellable *cancellable,
249 GError **error)
250 {
251 CamelService *service;
252 const gchar *display_name;
253 gboolean success = TRUE;
254
255 g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
256
257 service = CAMEL_SERVICE (store);
258
259 display_name = camel_service_get_display_name (service);
260 if (display_name == NULL || *display_name == '\0')
261 display_name = G_OBJECT_TYPE_NAME (service);
262
263 camel_operation_push_message (
264 cancellable, _("Reconnecting to “%s”"), display_name);
265
266 if (CAMEL_IS_OFFLINE_STORE (store))
267 success = camel_offline_store_set_online_sync (
268 CAMEL_OFFLINE_STORE (store),
269 TRUE, cancellable, error);
270
271 camel_operation_pop_message (cancellable);
272
273 return success;
274 }
275
276 /* Helper for e_mail_store_go_online() */
277 static void
mail_store_go_online_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)278 mail_store_go_online_thread (GSimpleAsyncResult *simple,
279 GObject *source_object,
280 GCancellable *cancellable)
281 {
282 GError *local_error = NULL;
283
284 e_mail_store_go_online_sync (
285 CAMEL_STORE (source_object), cancellable, &local_error);
286
287 if (local_error != NULL)
288 g_simple_async_result_take_error (simple, local_error);
289 }
290
291 void
e_mail_store_go_online(CamelStore * store,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)292 e_mail_store_go_online (CamelStore *store,
293 gint io_priority,
294 GCancellable *cancellable,
295 GAsyncReadyCallback callback,
296 gpointer user_data)
297 {
298 GSimpleAsyncResult *simple;
299
300 g_return_if_fail (CAMEL_IS_STORE (store));
301
302 simple = g_simple_async_result_new (
303 G_OBJECT (store), callback,
304 user_data, e_mail_store_go_online);
305
306 g_simple_async_result_set_check_cancellable (simple, cancellable);
307
308 g_simple_async_result_run_in_thread (
309 simple, mail_store_go_online_thread,
310 io_priority, cancellable);
311
312 g_object_unref (simple);
313 }
314
315 gboolean
e_mail_store_go_online_finish(CamelStore * store,GAsyncResult * result,GError ** error)316 e_mail_store_go_online_finish (CamelStore *store,
317 GAsyncResult *result,
318 GError **error)
319 {
320 GSimpleAsyncResult *simple;
321
322 g_return_val_if_fail (
323 g_simple_async_result_is_valid (
324 result, G_OBJECT (store), e_mail_store_go_online), FALSE);
325
326 simple = G_SIMPLE_ASYNC_RESULT (result);
327
328 /* Assume success unless a GError is set. */
329 return !g_simple_async_result_propagate_error (simple, error);
330 }
331
332 /* Helper for e_mail_store_prepare_for_offline() */
333 static void
mail_store_prepare_for_offline_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)334 mail_store_prepare_for_offline_thread (GSimpleAsyncResult *simple,
335 GObject *source_object,
336 GCancellable *cancellable)
337 {
338 CamelService *service;
339 const gchar *display_name;
340 GError *local_error = NULL;
341
342 service = CAMEL_SERVICE (source_object);
343
344 display_name = camel_service_get_display_name (service);
345 if (display_name == NULL || *display_name == '\0')
346 display_name = G_OBJECT_TYPE_NAME (service);
347
348 camel_operation_push_message (
349 cancellable, _("Preparing account “%s” for offline"),
350 display_name);
351
352 if (CAMEL_IS_OFFLINE_STORE (service))
353 camel_offline_store_prepare_for_offline_sync (
354 CAMEL_OFFLINE_STORE (service),
355 cancellable, &local_error);
356
357 if (local_error != NULL)
358 g_simple_async_result_take_error (simple, local_error);
359
360 camel_operation_pop_message (cancellable);
361 }
362
363 void
e_mail_store_prepare_for_offline(CamelStore * store,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)364 e_mail_store_prepare_for_offline (CamelStore *store,
365 gint io_priority,
366 GCancellable *cancellable,
367 GAsyncReadyCallback callback,
368 gpointer user_data)
369 {
370 GSimpleAsyncResult *simple;
371
372 g_return_if_fail (CAMEL_IS_STORE (store));
373
374 simple = g_simple_async_result_new (
375 G_OBJECT (store), callback, user_data,
376 e_mail_store_prepare_for_offline);
377
378 g_simple_async_result_set_check_cancellable (simple, cancellable);
379
380 g_simple_async_result_run_in_thread (
381 simple, mail_store_prepare_for_offline_thread,
382 io_priority, cancellable);
383
384 g_object_unref (simple);
385 }
386
387 gboolean
e_mail_store_prepare_for_offline_finish(CamelStore * store,GAsyncResult * result,GError ** error)388 e_mail_store_prepare_for_offline_finish (CamelStore *store,
389 GAsyncResult *result,
390 GError **error)
391 {
392 GSimpleAsyncResult *simple;
393
394 g_return_val_if_fail (
395 g_simple_async_result_is_valid (
396 result, G_OBJECT (store),
397 e_mail_store_prepare_for_offline), FALSE);
398
399 simple = G_SIMPLE_ASYNC_RESULT (result);
400
401 /* Assume success unless a GError is set. */
402 return !g_simple_async_result_propagate_error (simple, error);
403 }
404
405 static gboolean
mail_store_save_setup_key(CamelStore * store,ESource * source,const gchar * extension_name,const gchar * property_name,const gchar * type_id,const gchar * value)406 mail_store_save_setup_key (CamelStore *store,
407 ESource *source,
408 const gchar *extension_name,
409 const gchar *property_name,
410 const gchar *type_id,
411 const gchar *value)
412 {
413 gpointer extension;
414 GObjectClass *klass;
415
416 g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
417 if (source)
418 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
419 g_return_val_if_fail (extension_name != NULL, FALSE);
420 g_return_val_if_fail (property_name != NULL, FALSE);
421 g_return_val_if_fail (value != NULL, FALSE);
422
423 if (!source)
424 return FALSE;
425
426 extension = e_source_get_extension (source, extension_name);
427 if (!extension) {
428 g_warning ("%s: Cannot find extension '%s'", G_STRFUNC, extension_name);
429 return FALSE;
430 }
431
432 klass = G_OBJECT_GET_CLASS (extension);
433 g_return_val_if_fail (klass != NULL, FALSE);
434
435 if (!g_object_class_find_property (klass, property_name)) {
436 g_warning ("%s: Extension '%s' doesn't have property '%s'", G_STRFUNC, extension_name, property_name);
437 return FALSE;
438 }
439
440 if (!type_id || g_str_equal (type_id, "s")) {
441 g_object_set (extension, property_name, value, NULL);
442 } else if (g_str_equal (type_id, "b")) {
443 gboolean val;
444
445 val = g_strcmp0 (value, "false") != 0 && g_strcmp0 (value, "0") != 0;
446
447 g_object_set (extension, property_name, val, NULL);
448 } else if (g_str_equal (type_id, "i")) {
449 gint val;
450
451 val = (gint) g_ascii_strtoll (value, NULL, 10);
452
453 g_object_set (extension, property_name, val, NULL);
454 } else if (g_str_equal (type_id, "f")) {
455 gchar *folder_uri;
456
457 folder_uri = e_mail_folder_uri_build (store, value);
458 g_object_set (extension, property_name, folder_uri, NULL);
459 g_free (folder_uri);
460 } else {
461 g_warning ("%s: Unknown type identifier '%s' provided", G_STRFUNC, type_id);
462 return FALSE;
463 }
464
465 return TRUE;
466 }
467
468 gboolean
e_mail_store_save_initial_setup_sync(CamelStore * store,GHashTable * save_setup,ESource * collection_source,ESource * account_source,ESource * submission_source,ESource * transport_source,gboolean write_sources,GCancellable * cancellable,GError ** error)469 e_mail_store_save_initial_setup_sync (CamelStore *store,
470 GHashTable *save_setup,
471 ESource *collection_source,
472 ESource *account_source,
473 ESource *submission_source,
474 ESource *transport_source,
475 gboolean write_sources,
476 GCancellable *cancellable,
477 GError **error)
478 {
479 gboolean collection_changed = FALSE;
480 gboolean account_changed = FALSE;
481 gboolean submission_changed = FALSE;
482 gboolean transport_changed = FALSE;
483 gboolean success = TRUE;
484 GHashTableIter iter;
485 gpointer key, value;
486
487 g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
488 g_return_val_if_fail (save_setup != NULL, FALSE);
489 g_return_val_if_fail (E_IS_SOURCE (account_source), FALSE);
490
491 if (!g_hash_table_size (save_setup))
492 return TRUE;
493
494 /* The key name consists of up to four parts: Source:Extension:Property[:Type]
495 Source can be 'Collection', 'Account', 'Submission', 'Transport', 'Backend'
496 Extension is any extension name; it's up to the key creator to make sure
497 the extension belongs to that particular Source.
498 Property is a property name in the Extension.
499 Type is an optional letter describing the type of the value; if not set, then
500 string is used. Available values are: 'b' for boolean, 'i' for integer,
501 's' for string, 'f' for folder full path.
502 All the part values are case sensitive.
503 */
504
505 g_hash_table_iter_init (&iter, save_setup);
506 while (g_hash_table_iter_next (&iter, &key, &value)) {
507 gchar **keys;
508
509 keys = g_strsplit (key, ":", -1);
510 if (g_strv_length (keys) < 3 || g_strv_length (keys) > 4) {
511 g_warning ("%s: Incorrect store setup key, expects 3 or 4 parts, but %d given in '%s'",
512 G_STRFUNC, g_strv_length (keys), (const gchar *) key);
513 } else if (g_str_equal (keys[0], "Collection")) {
514 if (mail_store_save_setup_key (store, collection_source, keys[1], keys[2], keys[3], value))
515 collection_changed = TRUE;
516 } else if (g_str_equal (keys[0], "Account")) {
517 if (mail_store_save_setup_key (store, account_source, keys[1], keys[2], keys[3], value))
518 account_changed = TRUE;
519 } else if (g_str_equal (keys[0], "Submission")) {
520 if (mail_store_save_setup_key (store, submission_source, keys[1], keys[2], keys[3], value))
521 submission_changed = TRUE;
522 } else if (g_str_equal (keys[0], "Transport")) {
523 if (mail_store_save_setup_key (store, transport_source, keys[1], keys[2], keys[3], value))
524 transport_changed = TRUE;
525 } else if (g_str_equal (keys[0], "Backend")) {
526 ESource *backend_source = NULL;
527
528 if (collection_source && e_source_has_extension (collection_source, keys[1]))
529 backend_source = collection_source;
530 else if (account_source && e_source_has_extension (account_source, keys[1]))
531 backend_source = account_source;
532
533 if (mail_store_save_setup_key (store, backend_source, keys[1], keys[2], keys[3], value))
534 transport_changed = TRUE;
535 } else {
536 g_warning ("%s: Unknown source name '%s' given in '%s'", G_STRFUNC, keys[0], (const gchar *) key);
537 }
538
539 g_strfreev (keys);
540 }
541
542 if (write_sources) {
543 if (transport_changed && success && e_source_get_writable (transport_source))
544 success = e_source_write_sync (transport_source, cancellable, error);
545 if (submission_changed && success && e_source_get_writable (submission_source))
546 success = e_source_write_sync (submission_source, cancellable, error);
547 if (account_changed && success && e_source_get_writable (account_source))
548 success = e_source_write_sync (account_source, cancellable, error);
549 if (collection_changed && success && e_source_get_writable (collection_source))
550 success = e_source_write_sync (collection_source, cancellable, error);
551 }
552
553 return success;
554 }
555