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