b73395e51212c6553c9431e42f93c13bda8a1c0f
[platform/upstream/evolution-data-server.git] / camel / camel-subscribable.c
1 /*
2  * camel-subscribable.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 "camel-subscribable.h"
20
21 #include <config.h>
22 #include <glib/gi18n-lib.h>
23
24 #include "camel-debug.h"
25 #include "camel-session.h"
26 #include "camel-vtrash-folder.h"
27
28 typedef struct _AsyncContext AsyncContext;
29 typedef struct _SignalData SignalData;
30
31 struct _AsyncContext {
32         gchar *folder_name;
33 };
34
35 struct _SignalData {
36         CamelSubscribable *subscribable;
37         CamelFolderInfo *folder_info;
38 };
39
40 enum {
41         FOLDER_SUBSCRIBED,
42         FOLDER_UNSUBSCRIBED,
43         LAST_SIGNAL
44 };
45
46 static guint signals[LAST_SIGNAL];
47
48 G_DEFINE_INTERFACE (CamelSubscribable, camel_subscribable, CAMEL_TYPE_STORE)
49
50 static void
51 async_context_free (AsyncContext *async_context)
52 {
53         g_free (async_context->folder_name);
54
55         g_slice_free (AsyncContext, async_context);
56 }
57
58 static void
59 signal_data_free (SignalData *signal_data)
60 {
61         if (signal_data->subscribable != NULL)
62                 g_object_unref (signal_data->subscribable);
63
64         if (signal_data->folder_info != NULL)
65                 camel_folder_info_free (signal_data->folder_info);
66
67         g_slice_free (SignalData, signal_data);
68 }
69
70 static gboolean
71 subscribable_emit_folder_subscribed_cb (gpointer user_data)
72 {
73         SignalData *signal_data = user_data;
74
75         g_signal_emit (
76                 signal_data->subscribable,
77                 signals[FOLDER_SUBSCRIBED], 0,
78                 signal_data->folder_info);
79
80         return FALSE;
81 }
82
83 static gboolean
84 subscribable_emit_folder_unsubscribed_cb (gpointer user_data)
85 {
86         SignalData *signal_data = user_data;
87
88         g_signal_emit (
89                 signal_data->subscribable,
90                 signals[FOLDER_UNSUBSCRIBED], 0,
91                 signal_data->folder_info);
92
93         return FALSE;
94 }
95
96 static void
97 subscribable_delete_cached_folder (CamelStore *store,
98                                    const gchar *folder_name)
99 {
100         CamelFolder *folder;
101         CamelVeeFolder *vfolder;
102
103         /* XXX Copied from camel-store.c.  Should this be public? */
104
105         if (store->folders == NULL)
106                 return;
107
108         folder = camel_object_bag_get (store->folders, folder_name);
109         if (folder == NULL)
110                 return;
111
112         if (store->flags & CAMEL_STORE_VTRASH) {
113                 folder_name = CAMEL_VTRASH_NAME;
114                 vfolder = camel_object_bag_get (store->folders, folder_name);
115                 if (vfolder != NULL) {
116                         camel_vee_folder_remove_folder (vfolder, folder, NULL);
117                         g_object_unref (vfolder);
118                 }
119         }
120
121         if (store->flags & CAMEL_STORE_VJUNK) {
122                 folder_name = CAMEL_VJUNK_NAME;
123                 vfolder = camel_object_bag_get (store->folders, folder_name);
124                 if (vfolder != NULL) {
125                         camel_vee_folder_remove_folder (vfolder, folder, NULL);
126                         g_object_unref (vfolder);
127                 }
128         }
129
130         camel_folder_delete (folder);
131
132         camel_object_bag_remove (store->folders, folder);
133         g_object_unref (folder);
134 }
135
136 static void
137 subscribable_subscribe_folder_thread (GSimpleAsyncResult *simple,
138                                       GObject *object,
139                                       GCancellable *cancellable)
140 {
141         AsyncContext *async_context;
142         GError *error = NULL;
143
144         async_context = g_simple_async_result_get_op_res_gpointer (simple);
145
146         camel_subscribable_subscribe_folder_sync (
147                 CAMEL_SUBSCRIBABLE (object),
148                 async_context->folder_name,
149                 cancellable, &error);
150
151         if (error != NULL)
152                 g_simple_async_result_take_error (simple, error);
153 }
154
155 static void
156 subscribable_subscribe_folder (CamelSubscribable *subscribable,
157                                const gchar *folder_name,
158                                gint io_priority,
159                                GCancellable *cancellable,
160                                GAsyncReadyCallback callback,
161                                gpointer user_data)
162 {
163         GSimpleAsyncResult *simple;
164         AsyncContext *async_context;
165
166         async_context = g_slice_new0 (AsyncContext);
167         async_context->folder_name = g_strdup (folder_name);
168
169         simple = g_simple_async_result_new (
170                 G_OBJECT (subscribable), callback,
171                 user_data, subscribable_subscribe_folder);
172
173         g_simple_async_result_set_check_cancellable (simple, cancellable);
174
175         g_simple_async_result_set_op_res_gpointer (
176                 simple, async_context, (GDestroyNotify) async_context_free);
177
178         g_simple_async_result_run_in_thread (
179                 simple, subscribable_subscribe_folder_thread,
180                 io_priority, cancellable);
181
182         g_object_unref (simple);
183 }
184
185 static gboolean
186 subscribable_subscribe_folder_finish (CamelSubscribable *subscribable,
187                                       GAsyncResult *result,
188                                       GError **error)
189 {
190         GSimpleAsyncResult *simple;
191
192         g_return_val_if_fail (
193                 g_simple_async_result_is_valid (
194                 result, G_OBJECT (subscribable),
195                 subscribable_subscribe_folder), FALSE);
196
197         simple = G_SIMPLE_ASYNC_RESULT (result);
198
199         /* Assume success unless a GError is set. */
200         return !g_simple_async_result_propagate_error (simple, error);
201 }
202
203 static void
204 subscribable_unsubscribe_folder_thread (GSimpleAsyncResult *simple,
205                                         GObject *object,
206                                         GCancellable *cancellable)
207 {
208         AsyncContext *async_context;
209         GError *error = NULL;
210
211         async_context = g_simple_async_result_get_op_res_gpointer (simple);
212
213         camel_subscribable_unsubscribe_folder_sync (
214                 CAMEL_SUBSCRIBABLE (object),
215                 async_context->folder_name,
216                 cancellable, &error);
217
218         if (error != NULL)
219                 g_simple_async_result_take_error (simple, error);
220 }
221
222 static void
223 subscribable_unsubscribe_folder (CamelSubscribable *subscribable,
224                                  const gchar *folder_name,
225                                  gint io_priority,
226                                  GCancellable *cancellable,
227                                  GAsyncReadyCallback callback,
228                                  gpointer user_data)
229 {
230         GSimpleAsyncResult *simple;
231         AsyncContext *async_context;
232
233         async_context = g_slice_new0 (AsyncContext);
234         async_context->folder_name = g_strdup (folder_name);
235
236         simple = g_simple_async_result_new (
237                 G_OBJECT (subscribable), callback,
238                 user_data, subscribable_unsubscribe_folder);
239
240         g_simple_async_result_set_check_cancellable (simple, cancellable);
241
242         g_simple_async_result_set_op_res_gpointer (
243                 simple, async_context, (GDestroyNotify) async_context_free);
244
245         g_simple_async_result_run_in_thread (
246                 simple, subscribable_unsubscribe_folder_thread,
247                 io_priority, cancellable);
248
249         g_object_unref (simple);
250 }
251
252 static gboolean
253 subscribable_unsubscribe_folder_finish (CamelSubscribable *subscribable,
254                                         GAsyncResult *result,
255                                         GError **error)
256 {
257         GSimpleAsyncResult *simple;
258
259         g_return_val_if_fail (
260                 g_simple_async_result_is_valid (
261                 result, G_OBJECT (subscribable),
262                 subscribable_unsubscribe_folder), FALSE);
263
264         simple = G_SIMPLE_ASYNC_RESULT (result);
265
266         /* Assume success unless a GError is set. */
267         return !g_simple_async_result_propagate_error (simple, error);
268 }
269
270 static void
271 camel_subscribable_default_init (CamelSubscribableInterface *interface)
272 {
273         interface->subscribe_folder = subscribable_subscribe_folder;
274         interface->subscribe_folder_finish = subscribable_subscribe_folder_finish;
275         interface->unsubscribe_folder = subscribable_unsubscribe_folder;
276         interface->unsubscribe_folder_finish = subscribable_unsubscribe_folder_finish;
277
278         signals[FOLDER_SUBSCRIBED] = g_signal_new (
279                 "folder-subscribed",
280                 G_OBJECT_CLASS_TYPE (interface),
281                 G_SIGNAL_RUN_FIRST,
282                 G_STRUCT_OFFSET (
283                         CamelSubscribableInterface,
284                         folder_subscribed),
285                 NULL, NULL,
286                 g_cclosure_marshal_VOID__POINTER,
287                 G_TYPE_NONE, 1,
288                 G_TYPE_POINTER);
289
290         signals[FOLDER_UNSUBSCRIBED] = g_signal_new (
291                 "folder-unsubscribed",
292                 G_OBJECT_CLASS_TYPE (interface),
293                 G_SIGNAL_RUN_FIRST,
294                 G_STRUCT_OFFSET (
295                         CamelSubscribableInterface,
296                         folder_unsubscribed),
297                 NULL, NULL,
298                 g_cclosure_marshal_VOID__POINTER,
299                 G_TYPE_NONE, 1,
300                 G_TYPE_POINTER);
301 }
302
303 /**
304  * camel_subscribable_folder_is_subscribed:
305  * @subscribable: a #CamelSubscribable
306  * @folder_name: full path of the folder
307  *
308  * Find out if a folder has been subscribed to.
309  *
310  * Returns: %TRUE if the folder has been subscribed to or %FALSE otherwise
311  *
312  * Since: 3.2
313  **/
314 gboolean
315 camel_subscribable_folder_is_subscribed (CamelSubscribable *subscribable,
316                                          const gchar *folder_name)
317 {
318         CamelSubscribableInterface *interface;
319         gboolean is_subscribed;
320
321         g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
322         g_return_val_if_fail (folder_name != NULL, FALSE);
323
324         interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
325         g_return_val_if_fail (interface->folder_is_subscribed != NULL, FALSE);
326
327         camel_store_lock (
328                 CAMEL_STORE (subscribable),
329                 CAMEL_STORE_FOLDER_LOCK);
330
331         is_subscribed = interface->folder_is_subscribed (
332                 subscribable, folder_name);
333
334         camel_store_unlock (
335                 CAMEL_STORE (subscribable),
336                 CAMEL_STORE_FOLDER_LOCK);
337
338         return is_subscribed;
339 }
340
341 /**
342  * camel_subscribable_subscribe_folder_sync:
343  * @subscribable: a #CamelSubscribable
344  * @folder_name: full path of the folder
345  * @cancellable: optional #GCancellable object, or %NULL
346  * @error: return location for a #GError, or %NULL
347  *
348  * Subscribes to the folder described by @folder_name.
349  *
350  * Returns: %TRUE on success, %FALSE on error
351  *
352  * Since: 3.2
353  **/
354 gboolean
355 camel_subscribable_subscribe_folder_sync (CamelSubscribable *subscribable,
356                                           const gchar *folder_name,
357                                           GCancellable *cancellable,
358                                           GError **error)
359 {
360         CamelSubscribableInterface *interface;
361         const gchar *message;
362         gboolean success;
363
364         g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
365         g_return_val_if_fail (folder_name != NULL, FALSE);
366
367         interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
368         g_return_val_if_fail (interface->subscribe_folder_sync != NULL, FALSE);
369
370         camel_store_lock (
371                 CAMEL_STORE (subscribable),
372                 CAMEL_STORE_FOLDER_LOCK);
373
374         /* Check for cancellation after locking. */
375         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
376                 success = FALSE;
377                 goto exit;
378         }
379
380         /* Need to establish a connection before subscribing. */
381         success = camel_service_connect_sync (
382                 CAMEL_SERVICE (subscribable), cancellable, error);
383         if (!success)
384                 goto exit;
385
386         message = _("Subscribing to folder '%s'");
387         camel_operation_push_message (cancellable, message, folder_name);
388
389         success = interface->subscribe_folder_sync (
390                 subscribable, folder_name, cancellable, error);
391         CAMEL_CHECK_GERROR (
392                 subscribable, subscribe_folder_sync, success, error);
393
394         camel_operation_pop_message (cancellable);
395
396 exit:
397         camel_store_unlock (
398                 CAMEL_STORE (subscribable),
399                 CAMEL_STORE_FOLDER_LOCK);
400
401         return success;
402 }
403
404 /**
405  * camel_subscribable_subscribe_folder:
406  * @subscribable: a #CamelSubscribable
407  * @folder_name: full path of the folder
408  * @io_priority: the I/O priority of the request
409  * @cancellable: optional #GCancellable object, or %NULL
410  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
411  * @user_data: data to pass to the callback function
412  *
413  * Asynchronously subscribes to the folder described by @folder_name.
414  *
415  * When the operation is finished, @callback will be called.  You can then
416  * call camel_subscribable_subscribe_folder_finish() to get the result of
417  * the operation.
418  *
419  * Since: 3.2
420  **/
421 void
422 camel_subscribable_subscribe_folder (CamelSubscribable *subscribable,
423                                      const gchar *folder_name,
424                                      gint io_priority,
425                                      GCancellable *cancellable,
426                                      GAsyncReadyCallback callback,
427                                      gpointer user_data)
428 {
429         CamelSubscribableInterface *interface;
430
431         g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
432         g_return_if_fail (folder_name != NULL);
433
434         interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
435         g_return_if_fail (interface->subscribe_folder != NULL);
436
437         interface->subscribe_folder (
438                 subscribable, folder_name, io_priority,
439                 cancellable, callback, user_data);
440 }
441
442 /**
443  * camel_subscribable_subscribe_folder_finish:
444  * @subscribable: a #CamelSubscribable
445  * @result: a #GAsyncResult
446  * @error: return location for a #GError, or %NULL
447  *
448  * Finishes the operation started with camel_subscribable_subscribe_folder().
449  *
450  * Returns: %TRUE on success, %FALSE on error
451  *
452  * Since: 3.2
453  **/
454 gboolean
455 camel_subscribable_subscribe_folder_finish (CamelSubscribable *subscribable,
456                                             GAsyncResult *result,
457                                             GError **error)
458 {
459         CamelSubscribableInterface *interface;
460
461         g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
462         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
463
464         interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
465         g_return_val_if_fail (
466                 interface->subscribe_folder_finish != NULL, FALSE);
467
468         return interface->subscribe_folder_finish (
469                 subscribable, result, error);
470 }
471
472 /**
473  * camel_subscribable_unsubscribe_folder_sync:
474  * @subscribable: a #CamelSubscribable
475  * @folder_name: full path of the folder
476  * @cancellable: optional #GCancellable object, or %NULL
477  * @error: return location for a #GError, or %NULL
478  *
479  * Unsubscribes from the folder described by @folder_name.
480  *
481  * Returns: %TRUE on success, %FALSE on error
482  *
483  * Since: 3.2
484  **/
485 gboolean
486 camel_subscribable_unsubscribe_folder_sync (CamelSubscribable *subscribable,
487                                             const gchar *folder_name,
488                                             GCancellable *cancellable,
489                                             GError **error)
490 {
491         CamelSubscribableInterface *interface;
492         const gchar *message;
493         gboolean success;
494
495         g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
496         g_return_val_if_fail (folder_name != NULL, FALSE);
497
498         interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
499         g_return_val_if_fail (
500                 interface->unsubscribe_folder_sync != NULL, FALSE);
501
502         camel_store_lock (
503                 CAMEL_STORE (subscribable),
504                 CAMEL_STORE_FOLDER_LOCK);
505
506         /* Check for cancellation after locking. */
507         if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
508                 success = FALSE;
509                 goto exit;
510         }
511
512         /* Need to establish a connection before unsubscribing. */
513         success = camel_service_connect_sync (
514                 CAMEL_SERVICE (subscribable), cancellable, error);
515         if (!success)
516                 goto exit;
517
518         message = _("Unsubscribing from folder '%s'");
519         camel_operation_push_message (cancellable, message, folder_name);
520
521         success = interface->unsubscribe_folder_sync (
522                 subscribable, folder_name, cancellable, error);
523         CAMEL_CHECK_GERROR (
524                 subscribable, unsubscribe_folder_sync, success, error);
525
526         if (success)
527                 subscribable_delete_cached_folder (
528                         CAMEL_STORE (subscribable), folder_name);
529
530         camel_operation_pop_message (cancellable);
531
532 exit:
533         camel_store_unlock (
534                 CAMEL_STORE (subscribable),
535                 CAMEL_STORE_FOLDER_LOCK);
536
537         return success;
538 }
539
540 /**
541  * camel_subscribable_unsubscribe_folder:
542  * @subscribable: a #CamelSubscribable
543  * @folder_name: full path of the folder
544  * @io_priority: the I/O priority of the request
545  * @cancellable: optional #GCancellable object, or %NULL
546  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
547  * @user_data: data to pass to the callback function
548  *
549  * Asynchronously unsubscribes from the folder described by @folder_name.
550  *
551  * When the operation is finished, @callback will be called.  You can then
552  * call camel_subscribable_unsubscribe_folder_finish() to get the result of
553  * the operation.
554  *
555  * Since: 3.2
556  **/
557 void
558 camel_subscribable_unsubscribe_folder (CamelSubscribable *subscribable,
559                                        const gchar *folder_name,
560                                        gint io_priority,
561                                        GCancellable *cancellable,
562                                        GAsyncReadyCallback callback,
563                                        gpointer user_data)
564 {
565         CamelSubscribableInterface *interface;
566
567         g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
568         g_return_if_fail (folder_name != NULL);
569
570         interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
571         g_return_if_fail (interface->unsubscribe_folder != NULL);
572
573         interface->unsubscribe_folder (
574                 subscribable, folder_name, io_priority,
575                 cancellable, callback, user_data);
576 }
577
578 /**
579  * camel_subscribable_unsubscribe_folder_finish:
580  * @subscribable: a #CamelSubscribable
581  * @result: a #GAsyncResult
582  * @error: return location for a #GError, or %NULL
583  *
584  * Finishes the operation started with camel_subscribable_unsubscribe_folder().
585  *
586  * Returns: %TRUE on success, %FALSE on error
587  *
588  * Since: 3.2
589  **/
590 gboolean
591 camel_subscribable_unsubscribe_folder_finish (CamelSubscribable *subscribable,
592                                               GAsyncResult *result,
593                                               GError **error)
594 {
595         CamelSubscribableInterface *interface;
596
597         g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
598         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
599
600         interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
601         g_return_val_if_fail (
602                 interface->unsubscribe_folder_finish != NULL, FALSE);
603
604         return interface->unsubscribe_folder_finish (
605                 subscribable, result, error);
606 }
607
608 /**
609  * camel_subscribable_folder_subscribed:
610  * @subscribable: a #CamelSubscribable
611  * @folder_info: information about the subscribed folder
612  *
613  * Emits the #CamelSubscribable::folder-subscribed signal from an idle source
614  * on the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT.
615  *
616  * This function is only intended for Camel providers.
617  *
618  * Since: 3.2
619  **/
620 void
621 camel_subscribable_folder_subscribed (CamelSubscribable *subscribable,
622                                       CamelFolderInfo *folder_info)
623 {
624         CamelService *service;
625         CamelSession *session;
626         SignalData *signal_data;
627
628         g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
629         g_return_if_fail (folder_info != NULL);
630
631         service = CAMEL_SERVICE (subscribable);
632         session = camel_service_get_session (service);
633
634         signal_data = g_slice_new0 (SignalData);
635         signal_data->subscribable = g_object_ref (subscribable);
636         signal_data->folder_info = camel_folder_info_clone (folder_info);
637
638         camel_session_idle_add (
639                 session, G_PRIORITY_DEFAULT,
640                 subscribable_emit_folder_subscribed_cb,
641                 signal_data, (GDestroyNotify) signal_data_free);
642 }
643
644 /**
645  * camel_subscribable_folder_unsubscribed:
646  * @subscribable: a #CamelSubscribable
647  * @folder_info: information about the unsubscribed folder
648  *
649  * Emits the #CamelSubscribable::folder-unsubscribed signal from an idle source
650  * on the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT.
651  *
652  * This function is only intended for Camel providers.
653  *
654  * Since: 3.2
655  **/
656 void
657 camel_subscribable_folder_unsubscribed (CamelSubscribable *subscribable,
658                                         CamelFolderInfo *folder_info)
659 {
660         CamelService *service;
661         CamelSession *session;
662         SignalData *signal_data;
663
664         g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
665         g_return_if_fail (folder_info != NULL);
666
667         service = CAMEL_SERVICE (subscribable);
668         session = camel_service_get_session (service);
669
670         signal_data = g_slice_new0 (SignalData);
671         signal_data->subscribable = g_object_ref (subscribable);
672         signal_data->folder_info = camel_folder_info_clone (folder_info);
673
674         camel_session_idle_add (
675                 session, G_PRIORITY_DEFAULT,
676                 subscribable_emit_folder_unsubscribed_cb,
677                 signal_data, (GDestroyNotify) signal_data_free);
678 }
679