1 /*
2 * camel-subscribable.c
3 *
4 * This library 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 library 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 Lesser 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 library. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "evolution-data-server-config.h"
19
20 #include <glib/gi18n-lib.h>
21
22 #include "camel-async-closure.h"
23 #include "camel-debug.h"
24 #include "camel-session.h"
25 #include "camel-vtrash-folder.h"
26
27 #include "camel-subscribable.h"
28
29 typedef struct _AsyncContext AsyncContext;
30 typedef struct _SignalClosure SignalClosure;
31
32 struct _AsyncContext {
33 gchar *folder_name;
34 };
35
36 struct _SignalClosure {
37 GWeakRef subscribable;
38 CamelFolderInfo *folder_info;
39 };
40
41 enum {
42 FOLDER_SUBSCRIBED,
43 FOLDER_UNSUBSCRIBED,
44 LAST_SIGNAL
45 };
46
47 static guint signals[LAST_SIGNAL];
48
G_DEFINE_INTERFACE(CamelSubscribable,camel_subscribable,CAMEL_TYPE_STORE)49 G_DEFINE_INTERFACE (CamelSubscribable, camel_subscribable, CAMEL_TYPE_STORE)
50
51 static void
52 async_context_free (AsyncContext *async_context)
53 {
54 g_free (async_context->folder_name);
55
56 g_slice_free (AsyncContext, async_context);
57 }
58
59 static void
signal_closure_free(SignalClosure * signal_closure)60 signal_closure_free (SignalClosure *signal_closure)
61 {
62 g_weak_ref_clear (&signal_closure->subscribable);
63
64 if (signal_closure->folder_info != NULL)
65 camel_folder_info_free (signal_closure->folder_info);
66
67 g_slice_free (SignalClosure, signal_closure);
68 }
69
70 static gboolean
subscribable_emit_folder_subscribed_cb(gpointer user_data)71 subscribable_emit_folder_subscribed_cb (gpointer user_data)
72 {
73 SignalClosure *signal_closure = user_data;
74 CamelSubscribable *subscribable;
75
76 subscribable = g_weak_ref_get (&signal_closure->subscribable);
77
78 if (subscribable != NULL) {
79 g_signal_emit (
80 subscribable,
81 signals[FOLDER_SUBSCRIBED], 0,
82 signal_closure->folder_info);
83 g_object_unref (subscribable);
84 }
85
86 return FALSE;
87 }
88
89 static gboolean
subscribable_emit_folder_unsubscribed_cb(gpointer user_data)90 subscribable_emit_folder_unsubscribed_cb (gpointer user_data)
91 {
92 SignalClosure *signal_closure = user_data;
93 CamelSubscribable *subscribable;
94
95 subscribable = g_weak_ref_get (&signal_closure->subscribable);
96
97 if (subscribable != NULL) {
98 g_signal_emit (
99 subscribable,
100 signals[FOLDER_UNSUBSCRIBED], 0,
101 signal_closure->folder_info);
102 g_object_unref (subscribable);
103 }
104
105 return FALSE;
106 }
107
108 static void
camel_subscribable_default_init(CamelSubscribableInterface * iface)109 camel_subscribable_default_init (CamelSubscribableInterface *iface)
110 {
111 signals[FOLDER_SUBSCRIBED] = g_signal_new (
112 "folder-subscribed",
113 G_OBJECT_CLASS_TYPE (iface),
114 G_SIGNAL_RUN_FIRST,
115 G_STRUCT_OFFSET (
116 CamelSubscribableInterface,
117 folder_subscribed),
118 NULL, NULL, NULL,
119 G_TYPE_NONE, 1,
120 CAMEL_TYPE_FOLDER_INFO);
121
122 signals[FOLDER_UNSUBSCRIBED] = g_signal_new (
123 "folder-unsubscribed",
124 G_OBJECT_CLASS_TYPE (iface),
125 G_SIGNAL_RUN_FIRST,
126 G_STRUCT_OFFSET (
127 CamelSubscribableInterface,
128 folder_unsubscribed),
129 NULL, NULL, NULL,
130 G_TYPE_NONE, 1,
131 CAMEL_TYPE_FOLDER_INFO);
132 }
133
134 /**
135 * camel_subscribable_folder_is_subscribed:
136 * @subscribable: a #CamelSubscribable
137 * @folder_name: full path of the folder
138 *
139 * Find out if a folder has been subscribed to.
140 *
141 * Returns: %TRUE if the folder has been subscribed to or %FALSE otherwise
142 *
143 * Since: 3.2
144 **/
145 gboolean
camel_subscribable_folder_is_subscribed(CamelSubscribable * subscribable,const gchar * folder_name)146 camel_subscribable_folder_is_subscribed (CamelSubscribable *subscribable,
147 const gchar *folder_name)
148 {
149 CamelSubscribableInterface *iface;
150
151 g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
152 g_return_val_if_fail (folder_name != NULL, FALSE);
153
154 iface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
155 g_return_val_if_fail (iface->folder_is_subscribed != NULL, FALSE);
156
157 return iface->folder_is_subscribed (subscribable, folder_name);
158 }
159
160 /**
161 * camel_subscribable_subscribe_folder_sync:
162 * @subscribable: a #CamelSubscribable
163 * @folder_name: full path of the folder
164 * @cancellable: optional #GCancellable object, or %NULL
165 * @error: return location for a #GError, or %NULL
166 *
167 * Subscribes to the folder described by @folder_name.
168 *
169 * Returns: %TRUE on success, %FALSE on error
170 *
171 * Since: 3.2
172 **/
173 gboolean
camel_subscribable_subscribe_folder_sync(CamelSubscribable * subscribable,const gchar * folder_name,GCancellable * cancellable,GError ** error)174 camel_subscribable_subscribe_folder_sync (CamelSubscribable *subscribable,
175 const gchar *folder_name,
176 GCancellable *cancellable,
177 GError **error)
178 {
179 CamelAsyncClosure *closure;
180 GAsyncResult *result;
181 gboolean success;
182
183 g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
184 g_return_val_if_fail (folder_name != NULL, FALSE);
185
186 closure = camel_async_closure_new ();
187
188 camel_subscribable_subscribe_folder (
189 subscribable, folder_name,
190 G_PRIORITY_DEFAULT, cancellable,
191 camel_async_closure_callback, closure);
192
193 result = camel_async_closure_wait (closure);
194
195 success = camel_subscribable_subscribe_folder_finish (
196 subscribable, result, error);
197
198 camel_async_closure_free (closure);
199
200 return success;
201 }
202
203 /* Helper for camel_subscribable_subscribe_folder() */
204 static void
subscribable_subscribe_folder_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)205 subscribable_subscribe_folder_thread (GTask *task,
206 gpointer source_object,
207 gpointer task_data,
208 GCancellable *cancellable)
209 {
210 CamelSubscribable *subscribable;
211 CamelSubscribableInterface *iface;
212 const gchar *folder_name;
213 const gchar *message;
214 gboolean success;
215 AsyncContext *async_context;
216 GError *local_error = NULL;
217
218 subscribable = CAMEL_SUBSCRIBABLE (source_object);
219 async_context = (AsyncContext *) task_data;
220
221 folder_name = async_context->folder_name;
222
223 iface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
224 g_return_if_fail (iface->subscribe_folder_sync != NULL);
225
226 /* Need to establish a connection before subscribing. */
227 camel_service_connect_sync (
228 CAMEL_SERVICE (subscribable), cancellable, &local_error);
229 if (local_error != NULL) {
230 g_task_return_error (task, local_error);
231 return;
232 }
233
234 message = _("Subscribing to folder “%s”");
235 camel_operation_push_message (cancellable, message, folder_name);
236
237 success = iface->subscribe_folder_sync (
238 subscribable, folder_name, cancellable, &local_error);
239 CAMEL_CHECK_LOCAL_GERROR (
240 subscribable, subscribe_folder_sync, success, local_error);
241
242 camel_operation_pop_message (cancellable);
243
244 if (local_error != NULL) {
245 g_task_return_error (task, local_error);
246 } else {
247 g_task_return_boolean (task, success);
248 }
249 }
250
251 /**
252 * camel_subscribable_subscribe_folder:
253 * @subscribable: a #CamelSubscribable
254 * @folder_name: full path of the folder
255 * @io_priority: the I/O priority of the request
256 * @cancellable: optional #GCancellable object, or %NULL
257 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
258 * @user_data: data to pass to the callback function
259 *
260 * Asynchronously subscribes to the folder described by @folder_name.
261 *
262 * When the operation is finished, @callback will be called. You can then
263 * call camel_subscribable_subscribe_folder_finish() to get the result of
264 * the operation.
265 *
266 * Since: 3.2
267 **/
268 void
camel_subscribable_subscribe_folder(CamelSubscribable * subscribable,const gchar * folder_name,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)269 camel_subscribable_subscribe_folder (CamelSubscribable *subscribable,
270 const gchar *folder_name,
271 gint io_priority,
272 GCancellable *cancellable,
273 GAsyncReadyCallback callback,
274 gpointer user_data)
275 {
276 GTask *task;
277 CamelService *service;
278 AsyncContext *async_context;
279
280 g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
281 g_return_if_fail (folder_name != NULL);
282
283 service = CAMEL_SERVICE (subscribable);
284
285 async_context = g_slice_new0 (AsyncContext);
286 async_context->folder_name = g_strdup (folder_name);
287
288 task = g_task_new (subscribable, cancellable, callback, user_data);
289 g_task_set_source_tag (task, camel_subscribable_subscribe_folder);
290 g_task_set_priority (task, io_priority);
291
292 g_task_set_task_data (
293 task, async_context,
294 (GDestroyNotify) async_context_free);
295
296 camel_service_queue_task (
297 service, task, subscribable_subscribe_folder_thread);
298
299 g_object_unref (task);
300 }
301
302 /**
303 * camel_subscribable_subscribe_folder_finish:
304 * @subscribable: a #CamelSubscribable
305 * @result: a #GAsyncResult
306 * @error: return location for a #GError, or %NULL
307 *
308 * Finishes the operation started with camel_subscribable_subscribe_folder().
309 *
310 * Returns: %TRUE on success, %FALSE on error
311 *
312 * Since: 3.2
313 **/
314 gboolean
camel_subscribable_subscribe_folder_finish(CamelSubscribable * subscribable,GAsyncResult * result,GError ** error)315 camel_subscribable_subscribe_folder_finish (CamelSubscribable *subscribable,
316 GAsyncResult *result,
317 GError **error)
318 {
319 g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
320 g_return_val_if_fail (g_task_is_valid (result, subscribable), FALSE);
321
322 g_return_val_if_fail (
323 g_async_result_is_tagged (
324 result, camel_subscribable_subscribe_folder), FALSE);
325
326 return g_task_propagate_boolean (G_TASK (result), error);
327 }
328
329 /**
330 * camel_subscribable_unsubscribe_folder_sync:
331 * @subscribable: a #CamelSubscribable
332 * @folder_name: full path of the folder
333 * @cancellable: optional #GCancellable object, or %NULL
334 * @error: return location for a #GError, or %NULL
335 *
336 * Unsubscribes from the folder described by @folder_name.
337 *
338 * Returns: %TRUE on success, %FALSE on error
339 *
340 * Since: 3.2
341 **/
342 gboolean
camel_subscribable_unsubscribe_folder_sync(CamelSubscribable * subscribable,const gchar * folder_name,GCancellable * cancellable,GError ** error)343 camel_subscribable_unsubscribe_folder_sync (CamelSubscribable *subscribable,
344 const gchar *folder_name,
345 GCancellable *cancellable,
346 GError **error)
347 {
348 CamelAsyncClosure *closure;
349 GAsyncResult *result;
350 gboolean success;
351
352 g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
353 g_return_val_if_fail (folder_name != NULL, FALSE);
354
355 closure = camel_async_closure_new ();
356
357 camel_subscribable_unsubscribe_folder (
358 subscribable, folder_name,
359 G_PRIORITY_DEFAULT, cancellable,
360 camel_async_closure_callback, closure);
361
362 result = camel_async_closure_wait (closure);
363
364 success = camel_subscribable_unsubscribe_folder_finish (
365 subscribable, result, error);
366
367 camel_async_closure_free (closure);
368
369 return success;
370 }
371
372 /* Helper for camel_subscribable_unsubscribe_folder() */
373 static void
subscribable_unsubscribe_folder_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)374 subscribable_unsubscribe_folder_thread (GTask *task,
375 gpointer source_object,
376 gpointer task_data,
377 GCancellable *cancellable)
378 {
379 CamelSubscribable *subscribable;
380 CamelSubscribableInterface *iface;
381 const gchar *folder_name;
382 const gchar *message;
383 gboolean success;
384 AsyncContext *async_context;
385 GError *local_error = NULL;
386
387 subscribable = CAMEL_SUBSCRIBABLE (source_object);
388 async_context = (AsyncContext *) task_data;
389
390 folder_name = async_context->folder_name;
391
392 iface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
393 g_return_if_fail (iface->unsubscribe_folder_sync != NULL);
394
395 /* Need to establish a connection before unsubscribing. */
396 camel_service_connect_sync (
397 CAMEL_SERVICE (subscribable), cancellable, &local_error);
398 if (local_error != NULL) {
399 g_task_return_error (task, local_error);
400 return;
401 }
402
403 message = _("Unsubscribing from folder “%s”");
404 camel_operation_push_message (cancellable, message, folder_name);
405
406 success = iface->unsubscribe_folder_sync (
407 subscribable, folder_name, cancellable, &local_error);
408 CAMEL_CHECK_LOCAL_GERROR (
409 subscribable, unsubscribe_folder_sync, success, local_error);
410
411 if (success)
412 camel_store_delete_cached_folder (CAMEL_STORE (subscribable), folder_name);
413
414 camel_operation_pop_message (cancellable);
415
416 if (local_error != NULL) {
417 g_task_return_error (task, local_error);
418 } else {
419 g_task_return_boolean (task, success);
420 }
421 }
422
423 /**
424 * camel_subscribable_unsubscribe_folder:
425 * @subscribable: a #CamelSubscribable
426 * @folder_name: full path of the folder
427 * @io_priority: the I/O priority of the request
428 * @cancellable: optional #GCancellable object, or %NULL
429 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
430 * @user_data: data to pass to the callback function
431 *
432 * Asynchronously unsubscribes from the folder described by @folder_name.
433 *
434 * When the operation is finished, @callback will be called. You can then
435 * call camel_subscribable_unsubscribe_folder_finish() to get the result of
436 * the operation.
437 *
438 * Since: 3.2
439 **/
440 void
camel_subscribable_unsubscribe_folder(CamelSubscribable * subscribable,const gchar * folder_name,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)441 camel_subscribable_unsubscribe_folder (CamelSubscribable *subscribable,
442 const gchar *folder_name,
443 gint io_priority,
444 GCancellable *cancellable,
445 GAsyncReadyCallback callback,
446 gpointer user_data)
447 {
448 GTask *task;
449 CamelService *service;
450 AsyncContext *async_context;
451
452 g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
453 g_return_if_fail (folder_name != NULL);
454
455 service = CAMEL_SERVICE (subscribable);
456
457 async_context = g_slice_new0 (AsyncContext);
458 async_context->folder_name = g_strdup (folder_name);
459
460 task = g_task_new (subscribable, cancellable, callback, user_data);
461 g_task_set_source_tag (task, camel_subscribable_unsubscribe_folder);
462 g_task_set_priority (task, io_priority);
463
464 g_task_set_task_data (
465 task, async_context,
466 (GDestroyNotify) async_context_free);
467
468 camel_service_queue_task (
469 service, task, subscribable_unsubscribe_folder_thread);
470
471 g_object_unref (task);
472 }
473
474 /**
475 * camel_subscribable_unsubscribe_folder_finish:
476 * @subscribable: a #CamelSubscribable
477 * @result: a #GAsyncResult
478 * @error: return location for a #GError, or %NULL
479 *
480 * Finishes the operation started with camel_subscribable_unsubscribe_folder().
481 *
482 * Returns: %TRUE on success, %FALSE on error
483 *
484 * Since: 3.2
485 **/
486 gboolean
camel_subscribable_unsubscribe_folder_finish(CamelSubscribable * subscribable,GAsyncResult * result,GError ** error)487 camel_subscribable_unsubscribe_folder_finish (CamelSubscribable *subscribable,
488 GAsyncResult *result,
489 GError **error)
490 {
491 g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
492 g_return_val_if_fail (g_task_is_valid (result, subscribable), FALSE);
493
494 g_return_val_if_fail (
495 g_async_result_is_tagged (
496 result, camel_subscribable_unsubscribe_folder), FALSE);
497
498 return g_task_propagate_boolean (G_TASK (result), error);
499 }
500
501 /**
502 * camel_subscribable_folder_subscribed:
503 * @subscribable: a #CamelSubscribable
504 * @folder_info: information about the subscribed folder
505 *
506 * Emits the #CamelSubscribable::folder-subscribed signal from an idle source
507 * on the main loop. The idle source's priority is #G_PRIORITY_HIGH_IDLE.
508 *
509 * This function is only intended for Camel providers.
510 *
511 * Since: 3.2
512 **/
513 void
camel_subscribable_folder_subscribed(CamelSubscribable * subscribable,CamelFolderInfo * folder_info)514 camel_subscribable_folder_subscribed (CamelSubscribable *subscribable,
515 CamelFolderInfo *folder_info)
516 {
517 CamelService *service;
518 CamelSession *session;
519 SignalClosure *signal_closure;
520
521 g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
522 g_return_if_fail (folder_info != NULL);
523
524 service = CAMEL_SERVICE (subscribable);
525 session = camel_service_ref_session (service);
526
527 if (!session)
528 return;
529
530 signal_closure = g_slice_new0 (SignalClosure);
531 g_weak_ref_init (&signal_closure->subscribable, subscribable);
532 signal_closure->folder_info = camel_folder_info_clone (folder_info);
533
534 /* Prioritize ahead of GTK+ redraws. */
535 camel_session_idle_add (
536 session, G_PRIORITY_HIGH_IDLE,
537 subscribable_emit_folder_subscribed_cb,
538 signal_closure,
539 (GDestroyNotify) signal_closure_free);
540
541 g_object_unref (session);
542 }
543
544 /**
545 * camel_subscribable_folder_unsubscribed:
546 * @subscribable: a #CamelSubscribable
547 * @folder_info: information about the unsubscribed folder
548 *
549 * Emits the #CamelSubscribable::folder-unsubscribed signal from an idle source
550 * on the main loop. The idle source's priority is #G_PRIORITY_HIGH_IDLE.
551 *
552 * This function is only intended for Camel providers.
553 *
554 * Since: 3.2
555 **/
556 void
camel_subscribable_folder_unsubscribed(CamelSubscribable * subscribable,CamelFolderInfo * folder_info)557 camel_subscribable_folder_unsubscribed (CamelSubscribable *subscribable,
558 CamelFolderInfo *folder_info)
559 {
560 CamelService *service;
561 CamelSession *session;
562 SignalClosure *signal_closure;
563
564 g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
565 g_return_if_fail (folder_info != NULL);
566
567 service = CAMEL_SERVICE (subscribable);
568 session = camel_service_ref_session (service);
569
570 if (!session)
571 return;
572
573 signal_closure = g_slice_new0 (SignalClosure);
574 g_weak_ref_init (&signal_closure->subscribable, subscribable);
575 signal_closure->folder_info = camel_folder_info_clone (folder_info);
576
577 /* Prioritize ahead of GTK+ redraws. */
578 camel_session_idle_add (
579 session, G_PRIORITY_HIGH_IDLE,
580 subscribable_emit_folder_unsubscribed_cb,
581 signal_closure,
582 (GDestroyNotify) signal_closure_free);
583
584 g_object_unref (session);
585 }
586
587