1 /*
2 * e-cal-backend.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 /**
19 * SECTION: e-cal-backend
20 * @include: libedata-cal/libedata-cal.h
21 * @short_description: An abstract class for implementing calendar backends
22 *
23 * This is the main server facing API for interfacing with calendar backends,
24 * calendar backends must implement methods on this class.
25 **/
26
27 #include "evolution-data-server-config.h"
28
29 #include <glib/gi18n-lib.h>
30
31 #include "e-cal-backend.h"
32
33 #define NOTIFY_CHANGES_THRESHOLD 50
34 #define NOTIFY_CHANGES_TIMEOUT 333
35
36 typedef struct _AsyncContext AsyncContext;
37 typedef struct _DispatchNode DispatchNode;
38 typedef struct _SignalClosure SignalClosure;
39
40 struct _ECalBackendPrivate {
41 ESourceRegistry *registry;
42 EDataCal *data_cal;
43
44 gboolean opened;
45
46 /* The kind of components for this backend */
47 ICalComponentKind kind;
48
49 GMutex views_mutex;
50 GList *views;
51
52 GMutex property_lock;
53 GProxyResolver *proxy_resolver;
54 gchar *cache_dir;
55 gboolean writable;
56 GPtrArray *notify_changes; /* NotifyChangesData * */
57 guint changes_timeout_id;
58
59 ESource *authentication_source;
60 gulong auth_source_changed_handler_id;
61
62 GHashTable *zone_cache;
63 GMutex zone_cache_lock;
64
65 GMutex operation_lock;
66 GThreadPool *thread_pool;
67 GHashTable *operation_ids;
68 GQueue pending_operations;
69 guint32 next_operation_id;
70 GSimpleAsyncResult *blocked;
71 gboolean blocked_by_custom_op;
72 };
73
74 struct _AsyncContext {
75 /* Inputs */
76 gchar *uid;
77 gchar *rid;
78 gchar *alarm_uid;
79 gchar *calobj;
80 gchar *query;
81 gchar *tzid;
82 gchar *tzobject;
83 ECalObjModType mod;
84 time_t start;
85 time_t end;
86 GSList *compid_list;
87 GSList *string_list;
88 ECalOperationFlags opflags;
89
90 /* Outputs */
91 GQueue result_queue;
92
93 /* One of these should point to result_queue
94 * so any leftover resources can be released. */
95 GQueue *object_queue;
96 GQueue *string_queue;
97 };
98
99 struct _DispatchNode {
100 /* This is the dispatch function
101 * that invokes the class method. */
102 GSimpleAsyncThreadFunc dispatch_func;
103 gboolean blocking_operation;
104
105 GSimpleAsyncResult *simple;
106 GCancellable *cancellable;
107
108 GWeakRef *cal_backend_weak_ref;
109 ECalBackendCustomOpFunc custom_func;
110 gpointer custom_func_user_data;
111 GDestroyNotify custom_func_user_data_free;
112 };
113
114 struct _SignalClosure {
115 GWeakRef backend;
116 ICalTimezone *cached_zone;
117 };
118
119 enum {
120 PROP_0,
121 PROP_CACHE_DIR,
122 PROP_KIND,
123 PROP_PROXY_RESOLVER,
124 PROP_REGISTRY,
125 PROP_WRITABLE
126 };
127
128 enum {
129 CLOSED,
130 SHUTDOWN,
131 LAST_SIGNAL
132 };
133
134 static guint signals[LAST_SIGNAL];
135
136 /* Forward Declarations */
137 static void e_cal_backend_timezone_cache_init
138 (ETimezoneCacheInterface *iface);
139
G_DEFINE_TYPE_WITH_CODE(ECalBackend,e_cal_backend,E_TYPE_BACKEND,G_ADD_PRIVATE (ECalBackend)G_IMPLEMENT_INTERFACE (E_TYPE_TIMEZONE_CACHE,e_cal_backend_timezone_cache_init))140 G_DEFINE_TYPE_WITH_CODE (
141 ECalBackend,
142 e_cal_backend,
143 E_TYPE_BACKEND,
144 G_ADD_PRIVATE (ECalBackend)
145 G_IMPLEMENT_INTERFACE (
146 E_TYPE_TIMEZONE_CACHE,
147 e_cal_backend_timezone_cache_init))
148
149 static void
150 async_context_free (AsyncContext *async_context)
151 {
152 GQueue *queue;
153
154 g_free (async_context->uid);
155 g_free (async_context->rid);
156 g_free (async_context->alarm_uid);
157 g_free (async_context->calobj);
158 g_free (async_context->query);
159 g_free (async_context->tzid);
160 g_free (async_context->tzobject);
161
162 g_slist_free_full (
163 async_context->compid_list,
164 (GDestroyNotify) e_cal_component_id_free);
165
166 g_slist_free_full (
167 async_context->string_list,
168 (GDestroyNotify) g_free);
169
170 queue = async_context->object_queue;
171 while (queue != NULL && !g_queue_is_empty (queue))
172 g_object_unref (g_queue_pop_head (queue));
173
174 queue = async_context->string_queue;
175 while (queue != NULL && !g_queue_is_empty (queue))
176 g_free (g_queue_pop_head (queue));
177
178 g_slice_free (AsyncContext, async_context);
179 }
180
181 static void
dispatch_node_free(DispatchNode * dispatch_node)182 dispatch_node_free (DispatchNode *dispatch_node)
183 {
184 g_clear_object (&dispatch_node->simple);
185 g_clear_object (&dispatch_node->cancellable);
186
187 if (dispatch_node->custom_func_user_data_free)
188 dispatch_node->custom_func_user_data_free (dispatch_node->custom_func_user_data);
189
190 if (dispatch_node->cal_backend_weak_ref)
191 e_weak_ref_free (dispatch_node->cal_backend_weak_ref);
192
193 g_slice_free (DispatchNode, dispatch_node);
194 }
195
196 static void
signal_closure_free(SignalClosure * signal_closure)197 signal_closure_free (SignalClosure *signal_closure)
198 {
199 g_weak_ref_clear (&signal_closure->backend);
200 g_clear_object (&signal_closure->cached_zone);
201
202 g_slice_free (SignalClosure, signal_closure);
203 }
204
205 typedef enum {
206 NOTIFY_CHANGE_KIND_ADD,
207 NOTIFY_CHANGE_KIND_MODIFY,
208 NOTIFY_CHANGE_KIND_REMOVE
209 } NotifyChangeKind;
210
211 typedef struct _NotifyChangesData {
212 NotifyChangeKind kind;
213 ECalComponent *old_component;
214 ECalComponent *new_component;
215 ECalComponentId *id;
216 } NotifyChangesData;
217
218 static NotifyChangesData *
notify_changes_data_new(NotifyChangeKind kind,ECalComponent * old_component,ECalComponent * new_component,const ECalComponentId * id)219 notify_changes_data_new (NotifyChangeKind kind,
220 ECalComponent *old_component,
221 ECalComponent *new_component,
222 const ECalComponentId *id)
223 {
224 NotifyChangesData *ncd;
225
226 if (old_component)
227 g_return_val_if_fail (E_IS_CAL_COMPONENT (old_component), NULL);
228 if (new_component)
229 g_return_val_if_fail (E_IS_CAL_COMPONENT (new_component), NULL);
230
231 ncd = g_slice_new (NotifyChangesData);
232 ncd->kind = kind;
233 ncd->old_component = old_component ? g_object_ref (old_component) : NULL;
234 ncd->new_component = new_component ? g_object_ref (new_component) : NULL;
235 ncd->id = id ? e_cal_component_id_copy (id) : NULL;
236
237 return ncd;
238 }
239
240 static void
notify_changes_data_free(gpointer ptr)241 notify_changes_data_free (gpointer ptr)
242 {
243 NotifyChangesData *ncd = ptr;
244
245 if (ncd) {
246 g_clear_object (&ncd->old_component);
247 g_clear_object (&ncd->new_component);
248 e_cal_component_id_free (ncd->id);
249 g_slice_free (NotifyChangesData, ncd);
250 }
251 }
252
253 static void
match_view_and_notify_component(EDataCalView * view,ECalComponent * old_component,ECalComponent * new_component)254 match_view_and_notify_component (EDataCalView *view,
255 ECalComponent *old_component,
256 ECalComponent *new_component)
257 {
258 gboolean old_match = FALSE, new_match = FALSE;
259
260 if (old_component)
261 old_match = e_data_cal_view_component_matches (view, old_component);
262
263 new_match = e_data_cal_view_component_matches (view, new_component);
264
265 if (old_match && new_match)
266 e_data_cal_view_notify_components_modified_1 (view, new_component);
267 else if (new_match)
268 e_data_cal_view_notify_components_added_1 (view, new_component);
269 else if (old_match) {
270 ECalComponentId *id = e_cal_component_get_id (old_component);
271
272 e_data_cal_view_notify_objects_removed_1 (view, id);
273
274 e_cal_component_id_free (id);
275 }
276 }
277
278 static void
notify_changes_thread(ECalBackend * cal_backend,gpointer user_data,GCancellable * cancellable,GError ** error)279 notify_changes_thread (ECalBackend *cal_backend,
280 gpointer user_data,
281 GCancellable *cancellable,
282 GError **error)
283 {
284 GPtrArray *changes;
285 GList *views, *link;
286 guint ii;
287
288 g_mutex_lock (&cal_backend->priv->property_lock);
289
290 changes = cal_backend->priv->notify_changes;
291 cal_backend->priv->notify_changes = NULL;
292
293 g_mutex_unlock (&cal_backend->priv->property_lock);
294
295 if (!changes)
296 return;
297
298 views = e_cal_backend_list_views (cal_backend);
299
300 for (ii = 0; ii < changes->len; ii++) {
301 NotifyChangesData *ncd = g_ptr_array_index (changes, ii);
302
303 switch (ncd->kind) {
304 case NOTIFY_CHANGE_KIND_ADD:
305 for (link = views; link; link = g_list_next (link)) {
306 EDataCalView *view = E_DATA_CAL_VIEW (link->data);
307
308 if (e_data_cal_view_component_matches (view, ncd->new_component))
309 e_data_cal_view_notify_components_added_1 (view, ncd->new_component);
310 }
311 break;
312 case NOTIFY_CHANGE_KIND_MODIFY:
313 for (link = views; link; link = g_list_next (link)) {
314 EDataCalView *view = E_DATA_CAL_VIEW (link->data);
315
316 match_view_and_notify_component (view, ncd->old_component, ncd->new_component);
317 }
318 break;
319 case NOTIFY_CHANGE_KIND_REMOVE:
320 for (link = views; link; link = g_list_next (link)) {
321 EDataCalView *view = E_DATA_CAL_VIEW (link->data);
322
323 if (ncd->new_component)
324 match_view_and_notify_component (view, ncd->old_component, ncd->new_component);
325 else if (!ncd->old_component)
326 e_data_cal_view_notify_objects_removed_1 (view, ncd->id);
327 else if (e_data_cal_view_component_matches (view, ncd->old_component))
328 e_data_cal_view_notify_objects_removed_1 (view, ncd->id);
329 }
330 break;
331 }
332 }
333
334 g_list_free_full (views, g_object_unref);
335 g_ptr_array_unref (changes);
336 }
337
338 static void
schedule_notify_changes_thread_locked(ECalBackend * cal_backend)339 schedule_notify_changes_thread_locked (ECalBackend *cal_backend)
340 {
341 e_cal_backend_schedule_custom_operation (cal_backend, NULL,
342 notify_changes_thread, NULL, NULL);
343 }
344
345 static gboolean
notify_changes_timeout_cb(gpointer user_data)346 notify_changes_timeout_cb (gpointer user_data)
347 {
348 GWeakRef *weak_ref = user_data;
349 ECalBackend *backend;
350
351 if (g_source_is_destroyed (g_main_current_source ()))
352 return FALSE;
353
354 backend = g_weak_ref_get (weak_ref);
355 if (backend) {
356 g_mutex_lock (&backend->priv->property_lock);
357
358 if (g_source_get_id (g_main_current_source ()) == backend->priv->changes_timeout_id) {
359 backend->priv->changes_timeout_id = 0;
360 schedule_notify_changes_thread_locked (backend);
361 }
362
363 g_mutex_unlock (&backend->priv->property_lock);
364
365 g_object_unref (backend);
366 }
367
368 return FALSE;
369 }
370
371 static void
schedule_notify_changes(ECalBackend * backend,NotifyChangeKind kind,ECalComponent * old_component,ECalComponent * new_component,const ECalComponentId * id)372 schedule_notify_changes (ECalBackend *backend,
373 NotifyChangeKind kind,
374 ECalComponent *old_component,
375 ECalComponent *new_component,
376 const ECalComponentId *id)
377 {
378 NotifyChangesData *ncd;
379
380 ncd = notify_changes_data_new (kind, old_component, new_component, id);
381 g_return_if_fail (ncd != NULL);
382
383 g_mutex_lock (&backend->priv->property_lock);
384
385 if (!backend->priv->notify_changes)
386 backend->priv->notify_changes = g_ptr_array_new_full (NOTIFY_CHANGES_THRESHOLD, notify_changes_data_free);
387
388 g_ptr_array_add (backend->priv->notify_changes, ncd);
389
390 if (backend->priv->changes_timeout_id) {
391 if (backend->priv->notify_changes->len > NOTIFY_CHANGES_THRESHOLD) {
392 g_source_remove (backend->priv->changes_timeout_id);
393 backend->priv->changes_timeout_id = 0;
394
395 schedule_notify_changes_thread_locked (backend);
396 }
397 } else {
398 backend->priv->changes_timeout_id = e_named_timeout_add_full (G_PRIORITY_DEFAULT, NOTIFY_CHANGES_TIMEOUT,
399 notify_changes_timeout_cb, e_weak_ref_new (backend), (GDestroyNotify) e_weak_ref_free);
400 }
401
402 g_mutex_unlock (&backend->priv->property_lock);
403 }
404
405 static void
cal_backend_push_operation(ECalBackend * backend,GSimpleAsyncResult * simple,GCancellable * cancellable,gboolean blocking_operation,GSimpleAsyncThreadFunc dispatch_func)406 cal_backend_push_operation (ECalBackend *backend,
407 GSimpleAsyncResult *simple,
408 GCancellable *cancellable,
409 gboolean blocking_operation,
410 GSimpleAsyncThreadFunc dispatch_func)
411 {
412 DispatchNode *node;
413
414 g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
415 g_return_if_fail (dispatch_func != NULL);
416
417 g_mutex_lock (&backend->priv->operation_lock);
418
419 node = g_slice_new0 (DispatchNode);
420 node->dispatch_func = dispatch_func;
421 node->blocking_operation = blocking_operation;
422 node->simple = g_object_ref (simple);
423
424 if (G_IS_CANCELLABLE (cancellable))
425 node->cancellable = g_object_ref (cancellable);
426
427 g_queue_push_tail (&backend->priv->pending_operations, node);
428
429 g_mutex_unlock (&backend->priv->operation_lock);
430 }
431
432 static void cal_backend_unblock_operations (ECalBackend *backend, GSimpleAsyncResult *simple);
433
434 static void
cal_backend_dispatch_thread(DispatchNode * node)435 cal_backend_dispatch_thread (DispatchNode *node)
436 {
437 GCancellable *cancellable = node->cancellable;
438 GError *local_error = NULL;
439
440 if (node->custom_func) {
441 ECalBackend *cal_backend;
442
443 cal_backend = g_weak_ref_get (node->cal_backend_weak_ref);
444 if (cal_backend &&
445 !g_cancellable_is_cancelled (cancellable)) {
446 node->custom_func (cal_backend, node->custom_func_user_data, cancellable, &local_error);
447
448 if (local_error) {
449 if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
450 e_cal_backend_notify_error (cal_backend, local_error->message);
451
452 g_clear_error (&local_error);
453 }
454 }
455
456 if (cal_backend) {
457 cal_backend_unblock_operations (cal_backend, NULL);
458 e_util_unref_in_thread (cal_backend);
459 }
460 } else if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
461 g_simple_async_result_take_error (node->simple, local_error);
462 g_simple_async_result_complete_in_idle (node->simple);
463 } else {
464 GAsyncResult *result;
465 GObject *source_object;
466
467 result = G_ASYNC_RESULT (node->simple);
468 source_object = g_async_result_get_source_object (result);
469 node->dispatch_func (node->simple, source_object, cancellable);
470 g_object_unref (source_object);
471 }
472
473 dispatch_node_free (node);
474 }
475
476 static gboolean
cal_backend_dispatch_next_operation(ECalBackend * backend)477 cal_backend_dispatch_next_operation (ECalBackend *backend)
478 {
479 DispatchNode *node;
480
481 g_mutex_lock (&backend->priv->operation_lock);
482
483 /* We can't dispatch additional operations
484 * while a blocking operation is in progress. */
485 if (backend->priv->blocked != NULL ||
486 backend->priv->blocked_by_custom_op) {
487 g_mutex_unlock (&backend->priv->operation_lock);
488 return FALSE;
489 }
490
491 /* Pop the next DispatchNode off the queue. */
492 node = g_queue_pop_head (&backend->priv->pending_operations);
493 if (node == NULL) {
494 g_mutex_unlock (&backend->priv->operation_lock);
495 return FALSE;
496 }
497
498 /* If this a blocking operation, block any
499 * further dispatching until this finishes. */
500 if (node->blocking_operation) {
501 if (node->simple)
502 backend->priv->blocked = g_object_ref (node->simple);
503 else
504 backend->priv->blocked_by_custom_op = TRUE;
505 }
506
507 g_mutex_unlock (&backend->priv->operation_lock);
508
509 /* An error here merely indicates a thread could not be
510 * created, and so the node was queued. We don't care. */
511 g_thread_pool_push (backend->priv->thread_pool, node, NULL);
512
513 return TRUE;
514 }
515
516 static void
cal_backend_unblock_operations(ECalBackend * backend,GSimpleAsyncResult * simple)517 cal_backend_unblock_operations (ECalBackend *backend,
518 GSimpleAsyncResult *simple)
519 {
520 /* If the GSimpleAsyncResult was blocking the dispatch queue,
521 * unblock the dispatch queue. Then dispatch as many waiting
522 * operations as we can. */
523
524 g_mutex_lock (&backend->priv->operation_lock);
525 if (backend->priv->blocked == simple)
526 g_clear_object (&backend->priv->blocked);
527 backend->priv->blocked_by_custom_op = FALSE;
528 g_mutex_unlock (&backend->priv->operation_lock);
529
530 while (cal_backend_dispatch_next_operation (backend))
531 ;
532 }
533
534 static guint32
cal_backend_stash_operation(ECalBackend * backend,GSimpleAsyncResult * simple)535 cal_backend_stash_operation (ECalBackend *backend,
536 GSimpleAsyncResult *simple)
537 {
538 guint32 opid;
539
540 g_mutex_lock (&backend->priv->operation_lock);
541
542 if (backend->priv->next_operation_id == 0)
543 backend->priv->next_operation_id = 1;
544
545 opid = backend->priv->next_operation_id++;
546
547 g_hash_table_insert (
548 backend->priv->operation_ids,
549 GUINT_TO_POINTER (opid),
550 g_object_ref (simple));
551
552 g_mutex_unlock (&backend->priv->operation_lock);
553
554 return opid;
555 }
556
557 static GSimpleAsyncResult *
cal_backend_claim_operation(ECalBackend * backend,guint32 opid)558 cal_backend_claim_operation (ECalBackend *backend,
559 guint32 opid)
560 {
561 GSimpleAsyncResult *simple;
562
563 g_return_val_if_fail (opid > 0, NULL);
564
565 g_mutex_lock (&backend->priv->operation_lock);
566
567 simple = g_hash_table_lookup (
568 backend->priv->operation_ids,
569 GUINT_TO_POINTER (opid));
570
571 if (simple != NULL) {
572 /* Steal the hash table's reference. */
573 g_hash_table_steal (
574 backend->priv->operation_ids,
575 GUINT_TO_POINTER (opid));
576 }
577
578 g_mutex_unlock (&backend->priv->operation_lock);
579
580 return simple;
581 }
582
583 static void
cal_backend_set_default_cache_dir(ECalBackend * backend)584 cal_backend_set_default_cache_dir (ECalBackend *backend)
585 {
586 ESource *source;
587 ICalComponentKind kind;
588 const gchar *component_type;
589 const gchar *user_cache_dir;
590 const gchar *uid;
591 gchar *filename;
592
593 user_cache_dir = e_get_user_cache_dir ();
594
595 kind = e_cal_backend_get_kind (backend);
596 source = e_backend_get_source (E_BACKEND (backend));
597
598 uid = e_source_get_uid (source);
599 g_return_if_fail (uid != NULL);
600
601 switch (kind) {
602 case I_CAL_VEVENT_COMPONENT:
603 component_type = "calendar";
604 break;
605 case I_CAL_VTODO_COMPONENT:
606 component_type = "tasks";
607 break;
608 case I_CAL_VJOURNAL_COMPONENT:
609 component_type = "memos";
610 break;
611 default:
612 g_return_if_reached ();
613 }
614
615 filename = g_build_filename (
616 user_cache_dir, component_type, uid, NULL);
617 e_cal_backend_set_cache_dir (backend, filename);
618 g_free (filename);
619 }
620
621 static void
cal_backend_update_proxy_resolver(ECalBackend * backend)622 cal_backend_update_proxy_resolver (ECalBackend *backend)
623 {
624 GProxyResolver *proxy_resolver = NULL;
625 ESourceAuthentication *extension;
626 ESource *source = NULL;
627 gboolean notify = FALSE;
628 gchar *uid;
629
630 extension = e_source_get_extension (
631 backend->priv->authentication_source,
632 E_SOURCE_EXTENSION_AUTHENTICATION);
633
634 uid = e_source_authentication_dup_proxy_uid (extension);
635 if (uid != NULL) {
636 ESourceRegistry *registry;
637
638 registry = e_cal_backend_get_registry (backend);
639 source = e_source_registry_ref_source (registry, uid);
640 g_free (uid);
641 }
642
643 if (source != NULL) {
644 proxy_resolver = G_PROXY_RESOLVER (source);
645 if (!g_proxy_resolver_is_supported (proxy_resolver))
646 proxy_resolver = NULL;
647 }
648
649 g_mutex_lock (&backend->priv->property_lock);
650
651 /* Emitting a "notify" signal unnecessarily might have
652 * unwanted side effects like cancelling a SoupMessage.
653 * Only emit if we now have a different GProxyResolver. */
654
655 if (proxy_resolver != backend->priv->proxy_resolver) {
656 g_clear_object (&backend->priv->proxy_resolver);
657 backend->priv->proxy_resolver = proxy_resolver;
658
659 if (proxy_resolver != NULL)
660 g_object_ref (proxy_resolver);
661
662 notify = TRUE;
663 }
664
665 g_mutex_unlock (&backend->priv->property_lock);
666
667 if (notify)
668 g_object_notify (G_OBJECT (backend), "proxy-resolver");
669
670 g_clear_object (&source);
671 }
672
673 static void
cal_backend_auth_source_changed_cb(ESource * authentication_source,GWeakRef * backend_weak_ref)674 cal_backend_auth_source_changed_cb (ESource *authentication_source,
675 GWeakRef *backend_weak_ref)
676 {
677 ECalBackend *backend;
678
679 backend = g_weak_ref_get (backend_weak_ref);
680
681 if (backend != NULL) {
682 cal_backend_update_proxy_resolver (backend);
683 g_object_unref (backend);
684 }
685 }
686
687 static gchar *
cal_backend_get_backend_property(ECalBackend * backend,const gchar * prop_name)688 cal_backend_get_backend_property (ECalBackend *backend,
689 const gchar *prop_name)
690 {
691 gchar *prop_value = NULL;
692
693 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
694 g_return_val_if_fail (prop_name != NULL, NULL);
695
696 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
697 prop_value = g_strdup ("TRUE");
698
699 } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
700 prop_value = g_strdup ("FALSE");
701
702 } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_REVISION)) {
703 prop_value = g_strdup ("0");
704
705 } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
706 gboolean online;
707
708 online = e_backend_get_online (E_BACKEND (backend));
709 prop_value = g_strdup (online ? "TRUE" : "FALSE");
710
711 } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
712 gboolean readonly;
713
714 readonly = e_cal_backend_is_readonly (backend);
715 prop_value = g_strdup (readonly ? "TRUE" : "FALSE");
716
717 } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
718 prop_value = e_cal_backend_dup_cache_dir (backend);
719 }
720
721 return prop_value;
722 }
723
724 static gboolean
cal_backend_emit_timezone_added_idle_cb(gpointer user_data)725 cal_backend_emit_timezone_added_idle_cb (gpointer user_data)
726 {
727 SignalClosure *signal_closure = user_data;
728 ECalBackend *backend;
729
730 backend = g_weak_ref_get (&signal_closure->backend);
731
732 if (backend != NULL) {
733 g_signal_emit_by_name (
734 backend, "timezone-added",
735 signal_closure->cached_zone);
736 g_object_unref (backend);
737 }
738
739 return FALSE;
740 }
741
742 static void
cal_backend_set_kind(ECalBackend * backend,ICalComponentKind kind)743 cal_backend_set_kind (ECalBackend *backend,
744 ICalComponentKind kind)
745 {
746 backend->priv->kind = kind;
747 }
748
749 static void
cal_backend_set_registry(ECalBackend * backend,ESourceRegistry * registry)750 cal_backend_set_registry (ECalBackend *backend,
751 ESourceRegistry *registry)
752 {
753 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
754 g_return_if_fail (backend->priv->registry == NULL);
755
756 backend->priv->registry = g_object_ref (registry);
757 }
758
759 static void
cal_backend_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)760 cal_backend_set_property (GObject *object,
761 guint property_id,
762 const GValue *value,
763 GParamSpec *pspec)
764 {
765 switch (property_id) {
766 case PROP_CACHE_DIR:
767 e_cal_backend_set_cache_dir (
768 E_CAL_BACKEND (object),
769 g_value_get_string (value));
770 return;
771
772 case PROP_KIND:
773 cal_backend_set_kind (
774 E_CAL_BACKEND (object),
775 g_value_get_ulong (value));
776 return;
777
778 case PROP_REGISTRY:
779 cal_backend_set_registry (
780 E_CAL_BACKEND (object),
781 g_value_get_object (value));
782 return;
783
784 case PROP_WRITABLE:
785 e_cal_backend_set_writable (
786 E_CAL_BACKEND (object),
787 g_value_get_boolean (value));
788 return;
789 }
790
791 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
792 }
793
794 static void
cal_backend_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)795 cal_backend_get_property (GObject *object,
796 guint property_id,
797 GValue *value,
798 GParamSpec *pspec)
799 {
800 switch (property_id) {
801 case PROP_CACHE_DIR:
802 g_value_take_string (
803 value, e_cal_backend_dup_cache_dir (
804 E_CAL_BACKEND (object)));
805 return;
806
807 case PROP_KIND:
808 g_value_set_ulong (
809 value, e_cal_backend_get_kind (
810 E_CAL_BACKEND (object)));
811 return;
812
813 case PROP_PROXY_RESOLVER:
814 g_value_take_object (
815 value, e_cal_backend_ref_proxy_resolver (
816 E_CAL_BACKEND (object)));
817 return;
818
819 case PROP_REGISTRY:
820 g_value_set_object (
821 value, e_cal_backend_get_registry (
822 E_CAL_BACKEND (object)));
823 return;
824
825 case PROP_WRITABLE:
826 g_value_set_boolean (
827 value, e_cal_backend_get_writable (
828 E_CAL_BACKEND (object)));
829 return;
830 }
831
832 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
833 }
834
835 static void
cal_backend_dispose(GObject * object)836 cal_backend_dispose (GObject *object)
837 {
838 ECalBackendPrivate *priv;
839
840 priv = E_CAL_BACKEND (object)->priv;
841
842 if (priv->auth_source_changed_handler_id > 0) {
843 g_signal_handler_disconnect (
844 priv->authentication_source,
845 priv->auth_source_changed_handler_id);
846 priv->auth_source_changed_handler_id = 0;
847 }
848
849 g_mutex_lock (&priv->property_lock);
850 if (priv->changes_timeout_id) {
851 g_source_remove (priv->changes_timeout_id);
852 priv->changes_timeout_id = 0;
853 }
854 if (priv->notify_changes) {
855 g_ptr_array_unref (priv->notify_changes);
856 priv->notify_changes = NULL;
857 }
858 g_mutex_unlock (&priv->property_lock);
859
860 g_clear_object (&priv->registry);
861 g_clear_object (&priv->data_cal);
862 g_clear_object (&priv->proxy_resolver);
863 g_clear_object (&priv->authentication_source);
864
865 g_mutex_lock (&priv->views_mutex);
866 g_list_free_full (priv->views, g_object_unref);
867 priv->views = NULL;
868 g_mutex_unlock (&priv->views_mutex);
869
870 g_mutex_lock (&priv->operation_lock);
871 g_hash_table_remove_all (priv->operation_ids);
872
873 while (!g_queue_is_empty (&priv->pending_operations))
874 dispatch_node_free (g_queue_pop_head (&priv->pending_operations));
875
876 g_mutex_unlock (&priv->operation_lock);
877
878 g_clear_object (&priv->blocked);
879
880 /* Chain up to parent's dispose() method. */
881 G_OBJECT_CLASS (e_cal_backend_parent_class)->dispose (object);
882 }
883
884 static void
cal_backend_finalize(GObject * object)885 cal_backend_finalize (GObject *object)
886 {
887 ECalBackendPrivate *priv;
888
889 priv = E_CAL_BACKEND (object)->priv;
890
891 g_mutex_clear (&priv->views_mutex);
892 g_mutex_clear (&priv->property_lock);
893
894 if (priv->notify_changes) {
895 g_ptr_array_unref (priv->notify_changes);
896 priv->notify_changes = NULL;
897 }
898
899 g_free (priv->cache_dir);
900
901 g_hash_table_destroy (priv->zone_cache);
902 g_mutex_clear (&priv->zone_cache_lock);
903
904 g_warn_if_fail (g_queue_is_empty (&priv->pending_operations));
905 g_mutex_clear (&priv->operation_lock);
906 g_hash_table_destroy (priv->operation_ids);
907
908 /* Return immediately, do not wait. */
909 g_thread_pool_free (priv->thread_pool, TRUE, FALSE);
910
911 /* Chain up to parent's finalize() method. */
912 G_OBJECT_CLASS (e_cal_backend_parent_class)->finalize (object);
913 }
914
915 static void
cal_backend_constructed(GObject * object)916 cal_backend_constructed (GObject *object)
917 {
918 ECalBackend *backend;
919 ECalBackendClass *klass;
920 ESourceRegistry *registry;
921 ESource *source;
922 gint max_threads = -1;
923 gboolean exclusive = FALSE;
924
925 /* Chain up to parent's constructed() method. */
926 G_OBJECT_CLASS (e_cal_backend_parent_class)->constructed (object);
927
928 backend = E_CAL_BACKEND (object);
929 klass = E_CAL_BACKEND_GET_CLASS (backend);
930 g_return_if_fail (klass != NULL);
931
932 registry = e_cal_backend_get_registry (backend);
933 source = e_backend_get_source (E_BACKEND (backend));
934
935 /* If the backend specifies a serial dispatch queue, create
936 * a thread pool with one exclusive thread. The thread pool
937 * will serialize operations for us. */
938 if (klass->use_serial_dispatch_queue) {
939 max_threads = 1;
940 exclusive = TRUE;
941 }
942
943 /* XXX If creating an exclusive thread pool, technically there's
944 * a small chance of error here but we'll risk it since it's
945 * only for one exclusive thread. */
946 backend->priv->thread_pool = g_thread_pool_new (
947 (GFunc) cal_backend_dispatch_thread,
948 NULL, max_threads, exclusive, NULL);
949
950 /* Initialize the "cache-dir" property. */
951 cal_backend_set_default_cache_dir (backend);
952
953 /* Track the proxy resolver for this backend. */
954 backend->priv->authentication_source =
955 e_source_registry_find_extension (
956 registry, source, E_SOURCE_EXTENSION_AUTHENTICATION);
957 if (backend->priv->authentication_source != NULL) {
958 gulong handler_id;
959
960 handler_id = g_signal_connect_data (
961 backend->priv->authentication_source, "changed",
962 G_CALLBACK (cal_backend_auth_source_changed_cb),
963 e_weak_ref_new (backend),
964 (GClosureNotify) e_weak_ref_free, 0);
965 backend->priv->auth_source_changed_handler_id = handler_id;
966
967 cal_backend_update_proxy_resolver (backend);
968 }
969 }
970
971 static void
cal_backend_prepare_shutdown(EBackend * backend)972 cal_backend_prepare_shutdown (EBackend *backend)
973 {
974 GList *list, *l;
975
976 list = e_cal_backend_list_views (E_CAL_BACKEND (backend));
977
978 for (l = list; l != NULL; l = g_list_next (l)) {
979 EDataCalView *view = l->data;
980
981 e_cal_backend_remove_view (E_CAL_BACKEND (backend), view);
982 }
983
984 g_list_free_full (list, g_object_unref);
985
986 /* Chain up to parent's prepare_shutdown() method. */
987 E_BACKEND_CLASS (e_cal_backend_parent_class)->prepare_shutdown (backend);
988 }
989
990 static void
cal_backend_shutdown(ECalBackend * backend)991 cal_backend_shutdown (ECalBackend *backend)
992 {
993 ESource *source;
994
995 source = e_backend_get_source (E_BACKEND (backend));
996
997 e_source_registry_debug_print (
998 "The %s instance for \"%s\" is shutting down.\n",
999 G_OBJECT_TYPE_NAME (backend),
1000 e_source_get_display_name (source));
1001 }
1002
1003 /* Private function, not meant to be part of the public API */
1004 void _e_cal_backend_remove_cached_timezones (ECalBackend *cal_backend);
1005
1006 void
_e_cal_backend_remove_cached_timezones(ECalBackend * cal_backend)1007 _e_cal_backend_remove_cached_timezones (ECalBackend *cal_backend)
1008 {
1009 g_return_if_fail (E_IS_CAL_BACKEND (cal_backend));
1010
1011 g_mutex_lock (&cal_backend->priv->zone_cache_lock);
1012 g_hash_table_remove_all (cal_backend->priv->zone_cache);
1013 g_mutex_unlock (&cal_backend->priv->zone_cache_lock);
1014 }
1015
1016 static void
cal_backend_add_cached_timezone(ETimezoneCache * cache,ICalTimezone * zone)1017 cal_backend_add_cached_timezone (ETimezoneCache *cache,
1018 ICalTimezone *zone)
1019 {
1020 ECalBackendPrivate *priv;
1021 const gchar *tzid;
1022
1023 priv = E_CAL_BACKEND (cache)->priv;
1024
1025 /* XXX Apparently this function can sometimes return NULL.
1026 * I'm not sure when or why that happens, but we can't
1027 * cache the ICalTimezone if it has no tzid string. */
1028 tzid = i_cal_timezone_get_tzid (zone);
1029 if (tzid == NULL)
1030 return;
1031
1032 g_mutex_lock (&priv->zone_cache_lock);
1033
1034 /* Avoid replacing an existing cache entry. We don't want to
1035 * invalidate any ICalTimezone pointers that may have already
1036 * been returned through e_timezone_cache_get_timezone(). */
1037 if (!g_hash_table_contains (priv->zone_cache, tzid)) {
1038 GSource *idle_source;
1039 GMainContext *main_context;
1040 SignalClosure *signal_closure;
1041 ICalTimezone *cached_zone;
1042
1043 cached_zone = e_cal_util_copy_timezone (zone);
1044
1045 g_hash_table_insert (
1046 priv->zone_cache,
1047 g_strdup (tzid), cached_zone);
1048
1049 /* The closure's backend reference will keep the
1050 * internally cached ICalTimezone alive for the
1051 * duration of the idle callback. */
1052 signal_closure = g_slice_new0 (SignalClosure);
1053 g_weak_ref_init (&signal_closure->backend, cache);
1054 signal_closure->cached_zone = g_object_ref (cached_zone);
1055
1056 main_context = e_backend_ref_main_context (E_BACKEND (cache));
1057
1058 idle_source = g_idle_source_new ();
1059 g_source_set_callback (
1060 idle_source,
1061 cal_backend_emit_timezone_added_idle_cb,
1062 signal_closure,
1063 (GDestroyNotify) signal_closure_free);
1064 g_source_attach (idle_source, main_context);
1065 g_source_unref (idle_source);
1066
1067 g_main_context_unref (main_context);
1068 }
1069
1070 g_mutex_unlock (&priv->zone_cache_lock);
1071 }
1072
1073 static ICalTimezone *
cal_backend_get_cached_timezone(ETimezoneCache * cache,const gchar * tzid)1074 cal_backend_get_cached_timezone (ETimezoneCache *cache,
1075 const gchar *tzid)
1076 {
1077 ECalBackendPrivate *priv;
1078 ICalTimezone *zone = NULL;
1079 ICalTimezone *builtin_zone = NULL;
1080 ICalComponent *icomp, *clone;
1081 ICalProperty *prop;
1082 const gchar *builtin_tzid;
1083
1084 priv = E_CAL_BACKEND (cache)->priv;
1085
1086 if (g_str_equal (tzid, "UTC"))
1087 return i_cal_timezone_get_utc_timezone ();
1088
1089 g_mutex_lock (&priv->zone_cache_lock);
1090
1091 /* See if we already have it in the cache. */
1092 zone = g_hash_table_lookup (priv->zone_cache, tzid);
1093
1094 if (zone != NULL)
1095 goto exit;
1096
1097 /* Try to replace the original time zone with a more complete
1098 * and/or potentially updated built-in time zone. Note this also
1099 * applies to TZIDs which match built-in time zones exactly: they
1100 * are extracted via i_cal_timezone_get_builtin_timezone_from_tzid(). */
1101
1102 builtin_tzid = e_cal_match_tzid (tzid);
1103
1104 if (builtin_tzid != NULL)
1105 builtin_zone = i_cal_timezone_get_builtin_timezone_from_tzid (builtin_tzid);
1106
1107 if (builtin_zone == NULL)
1108 goto exit;
1109
1110 /* Use the built-in time zone *and* rename it. Likely the caller
1111 * is asking for a specific TZID because it has an event with such
1112 * a TZID. Returning an ICalTimezone with a different TZID would
1113 * lead to broken VCALENDARs in the caller. */
1114
1115 icomp = i_cal_timezone_get_component (builtin_zone);
1116 clone = i_cal_component_clone (icomp);
1117 g_object_unref (icomp);
1118 icomp = clone;
1119
1120 for (prop = i_cal_component_get_first_property (icomp, I_CAL_ANY_PROPERTY);
1121 prop;
1122 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_ANY_PROPERTY)) {
1123 if (i_cal_property_isa (prop) == I_CAL_TZID_PROPERTY) {
1124 i_cal_property_set_value_from_string (prop, tzid, "NO");
1125 g_object_unref (prop);
1126 break;
1127 }
1128 }
1129
1130 zone = i_cal_timezone_new ();
1131 if (i_cal_timezone_set_component (zone, icomp)) {
1132 tzid = i_cal_timezone_get_tzid (zone);
1133 g_hash_table_insert (priv->zone_cache, g_strdup (tzid), zone);
1134 } else {
1135 g_clear_object (&zone);
1136 }
1137 g_clear_object (&icomp);
1138
1139 exit:
1140 g_mutex_unlock (&priv->zone_cache_lock);
1141
1142 return zone;
1143 }
1144
1145 static GList *
cal_backend_list_cached_timezones(ETimezoneCache * cache)1146 cal_backend_list_cached_timezones (ETimezoneCache *cache)
1147 {
1148 ECalBackendPrivate *priv;
1149 GList *list;
1150
1151 priv = E_CAL_BACKEND (cache)->priv;
1152
1153 g_mutex_lock (&priv->zone_cache_lock);
1154
1155 list = g_hash_table_get_values (priv->zone_cache);
1156
1157 g_mutex_unlock (&priv->zone_cache_lock);
1158
1159 return list;
1160 }
1161
1162 static void
e_cal_backend_class_init(ECalBackendClass * class)1163 e_cal_backend_class_init (ECalBackendClass *class)
1164 {
1165 GObjectClass *object_class;
1166 EBackendClass *backend_class;
1167
1168 object_class = G_OBJECT_CLASS (class);
1169 object_class->set_property = cal_backend_set_property;
1170 object_class->get_property = cal_backend_get_property;
1171 object_class->dispose = cal_backend_dispose;
1172 object_class->finalize = cal_backend_finalize;
1173 object_class->constructed = cal_backend_constructed;
1174
1175 backend_class = E_BACKEND_CLASS (class);
1176 backend_class->prepare_shutdown = cal_backend_prepare_shutdown;
1177
1178 class->use_serial_dispatch_queue = TRUE;
1179 class->impl_get_backend_property = cal_backend_get_backend_property;
1180 class->shutdown = cal_backend_shutdown;
1181
1182 g_object_class_install_property (
1183 object_class,
1184 PROP_CACHE_DIR,
1185 g_param_spec_string (
1186 "cache-dir",
1187 "Cache Dir",
1188 "The backend's cache directory",
1189 NULL,
1190 G_PARAM_READWRITE |
1191 G_PARAM_EXPLICIT_NOTIFY |
1192 G_PARAM_STATIC_STRINGS));
1193
1194 g_object_class_install_property (
1195 object_class,
1196 PROP_KIND,
1197 g_param_spec_ulong (
1198 "kind",
1199 "Kind",
1200 "The kind of iCalendar components "
1201 "this backend manages",
1202 I_CAL_NO_COMPONENT,
1203 I_CAL_XLICMIMEPART_COMPONENT,
1204 I_CAL_NO_COMPONENT,
1205 G_PARAM_READWRITE |
1206 G_PARAM_CONSTRUCT_ONLY |
1207 G_PARAM_STATIC_STRINGS));
1208
1209 g_object_class_install_property (
1210 object_class,
1211 PROP_PROXY_RESOLVER,
1212 g_param_spec_object (
1213 "proxy-resolver",
1214 "Proxy Resolver",
1215 "The proxy resolver for this backend",
1216 G_TYPE_PROXY_RESOLVER,
1217 G_PARAM_READABLE |
1218 G_PARAM_STATIC_STRINGS));
1219
1220 g_object_class_install_property (
1221 object_class,
1222 PROP_REGISTRY,
1223 g_param_spec_object (
1224 "registry",
1225 "Registry",
1226 "Data source registry",
1227 E_TYPE_SOURCE_REGISTRY,
1228 G_PARAM_READWRITE |
1229 G_PARAM_CONSTRUCT_ONLY |
1230 G_PARAM_STATIC_STRINGS));
1231
1232 g_object_class_install_property (
1233 object_class,
1234 PROP_WRITABLE,
1235 g_param_spec_boolean (
1236 "writable",
1237 "Writable",
1238 "Whether the backend will accept changes",
1239 FALSE,
1240 G_PARAM_READWRITE |
1241 G_PARAM_EXPLICIT_NOTIFY |
1242 G_PARAM_STATIC_STRINGS));
1243
1244 /**
1245 * ECalBackend::closed:
1246 * @backend: the #ECalBackend which emitted the signal
1247 * @sender: the bus name that invoked the "close" method
1248 *
1249 * Emitted when a client destroys its #ECalClient for @backend
1250 *
1251 * Since: 3.10
1252 **/
1253 signals[CLOSED] = g_signal_new (
1254 "closed",
1255 G_OBJECT_CLASS_TYPE (object_class),
1256 G_SIGNAL_RUN_LAST,
1257 G_STRUCT_OFFSET (ECalBackendClass, closed),
1258 NULL, NULL, NULL,
1259 G_TYPE_NONE, 1,
1260 G_TYPE_STRING);
1261
1262 /**
1263 * ECalBackend::shutdown:
1264 * @backend: the #ECalBackend which emitted the signal
1265 *
1266 * Emitted when the last client destroys its #ECalClient for
1267 * @backend. This signals the @backend to begin final cleanup
1268 * tasks such as synchronizing data to permanent storage.
1269 *
1270 * Since: 3.10
1271 **/
1272 signals[SHUTDOWN] = g_signal_new (
1273 "shutdown",
1274 G_OBJECT_CLASS_TYPE (object_class),
1275 G_SIGNAL_RUN_LAST,
1276 G_STRUCT_OFFSET (ECalBackendClass, shutdown),
1277 NULL, NULL, NULL,
1278 G_TYPE_NONE, 0);
1279 }
1280
1281 static void
e_cal_backend_timezone_cache_init(ETimezoneCacheInterface * iface)1282 e_cal_backend_timezone_cache_init (ETimezoneCacheInterface *iface)
1283 {
1284 iface->tzcache_add_timezone = cal_backend_add_cached_timezone;
1285 iface->tzcache_get_timezone = cal_backend_get_cached_timezone;
1286 iface->tzcache_list_timezones = cal_backend_list_cached_timezones;
1287 }
1288
1289 static void
e_cal_backend_init(ECalBackend * backend)1290 e_cal_backend_init (ECalBackend *backend)
1291 {
1292 GHashTable *zone_cache;
1293
1294 zone_cache = g_hash_table_new_full (
1295 (GHashFunc) g_str_hash,
1296 (GEqualFunc) g_str_equal,
1297 (GDestroyNotify) g_free,
1298 (GDestroyNotify) g_object_unref);
1299
1300 backend->priv = e_cal_backend_get_instance_private (backend);
1301
1302 g_mutex_init (&backend->priv->views_mutex);
1303 g_mutex_init (&backend->priv->property_lock);
1304
1305 backend->priv->zone_cache = zone_cache;
1306 g_mutex_init (&backend->priv->zone_cache_lock);
1307
1308 g_mutex_init (&backend->priv->operation_lock);
1309
1310 backend->priv->operation_ids = g_hash_table_new_full (
1311 (GHashFunc) g_direct_hash,
1312 (GEqualFunc) g_direct_equal,
1313 (GDestroyNotify) NULL,
1314 (GDestroyNotify) g_object_unref);
1315 }
1316
1317 /**
1318 * e_cal_backend_get_kind:
1319 * @backend: an #ECalBackend
1320 *
1321 * Gets the kind of components the given backend stores.
1322 *
1323 * Returns: The kind of components for this backend.
1324 */
1325 ICalComponentKind
e_cal_backend_get_kind(ECalBackend * backend)1326 e_cal_backend_get_kind (ECalBackend *backend)
1327 {
1328 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), I_CAL_NO_COMPONENT);
1329
1330 return backend->priv->kind;
1331 }
1332
1333 /**
1334 * e_cal_backend_ref_data_cal:
1335 * @backend: an #ECalBackend
1336 *
1337 * Returns the #EDataCal for @backend. The #EDataCal is essentially
1338 * the glue between incoming D-Bus requests and @backend's native API.
1339 *
1340 * An #EDataCal should be set only once after @backend is first created.
1341 * If an #EDataCal has not yet been set, the function returns %NULL.
1342 *
1343 * The returned #EDataCal is referenced for thread-safety and must be
1344 * unreferenced with g_object_unref() when finished with it.
1345 *
1346 * Returns: (transfer full): an #EDataCal, or %NULL
1347 *
1348 * Since: 3.10
1349 **/
1350 EDataCal *
e_cal_backend_ref_data_cal(ECalBackend * backend)1351 e_cal_backend_ref_data_cal (ECalBackend *backend)
1352 {
1353 EDataCal *data_cal = NULL;
1354
1355 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1356
1357 if (backend->priv->data_cal != NULL)
1358 data_cal = g_object_ref (backend->priv->data_cal);
1359
1360 return data_cal;
1361 }
1362
1363 /**
1364 * e_cal_backend_set_data_cal:
1365 * @backend: an #ECalBackend
1366 * @data_cal: an #EDataCal
1367 *
1368 * Sets the #EDataCal for @backend. The #EDataCal is essentially the
1369 * glue between incoming D-Bus requests and @backend's native API.
1370 *
1371 * An #EDataCal should be set only once after @backend is first created.
1372 *
1373 * The @backend adds its own reference on the @data_cal.
1374 *
1375 * Since: 3.10
1376 **/
1377 void
e_cal_backend_set_data_cal(ECalBackend * backend,EDataCal * data_cal)1378 e_cal_backend_set_data_cal (ECalBackend *backend,
1379 EDataCal *data_cal)
1380 {
1381 g_return_if_fail (E_IS_CAL_BACKEND (backend));
1382 g_return_if_fail (E_IS_DATA_CAL (data_cal));
1383
1384 /* This should be set only once. Warn if not. */
1385 g_warn_if_fail (backend->priv->data_cal == NULL);
1386
1387 backend->priv->data_cal = g_object_ref (data_cal);
1388 }
1389
1390 /**
1391 * e_cal_backend_ref_proxy_resolver:
1392 * @backend: an #ECalBackend
1393 *
1394 * Returns the #GProxyResolver for @backend (if applicable), as indicated
1395 * by the #ESourceAuthentication:proxy-uid of @backend's #EBackend:source
1396 * or one of its ancestors.
1397 *
1398 * The returned #GProxyResolver is referenced for thread-safety and must
1399 * be unreferenced with g_object_unref() when finished with it.
1400 *
1401 * Returns: (transfer full) (nullable): a #GProxyResolver, or %NULL
1402 *
1403 * Since: 3.12
1404 **/
1405 GProxyResolver *
e_cal_backend_ref_proxy_resolver(ECalBackend * backend)1406 e_cal_backend_ref_proxy_resolver (ECalBackend *backend)
1407 {
1408 GProxyResolver *proxy_resolver = NULL;
1409
1410 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1411
1412 g_mutex_lock (&backend->priv->property_lock);
1413
1414 if (backend->priv->proxy_resolver != NULL)
1415 proxy_resolver = g_object_ref (backend->priv->proxy_resolver);
1416
1417 g_mutex_unlock (&backend->priv->property_lock);
1418
1419 return proxy_resolver;
1420 }
1421
1422 /**
1423 * e_cal_backend_get_registry:
1424 * @backend: an #ECalBackend
1425 *
1426 * Returns the data source registry to which #EBackend:source belongs.
1427 *
1428 * Returns: (transfer none): an #ESourceRegistry
1429 *
1430 * Since: 3.6
1431 **/
1432 ESourceRegistry *
e_cal_backend_get_registry(ECalBackend * backend)1433 e_cal_backend_get_registry (ECalBackend *backend)
1434 {
1435 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1436
1437 return backend->priv->registry;
1438 }
1439
1440 /**
1441 * e_cal_backend_get_writable:
1442 * @backend: an #ECalBackend
1443 *
1444 * Returns whether @backend will accept changes to its data content.
1445 *
1446 * Returns: whether @backend is writable
1447 *
1448 * Since: 3.8
1449 **/
1450 gboolean
e_cal_backend_get_writable(ECalBackend * backend)1451 e_cal_backend_get_writable (ECalBackend *backend)
1452 {
1453 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
1454
1455 return backend->priv->writable;
1456 }
1457
1458 /**
1459 * e_cal_backend_set_writable:
1460 * @backend: an #ECalBackend
1461 * @writable: whether @backend is writable
1462 *
1463 * Sets whether @backend will accept changes to its data content.
1464 *
1465 * Since: 3.8
1466 **/
1467 void
e_cal_backend_set_writable(ECalBackend * backend,gboolean writable)1468 e_cal_backend_set_writable (ECalBackend *backend,
1469 gboolean writable)
1470 {
1471 g_return_if_fail (E_IS_CAL_BACKEND (backend));
1472
1473 if (writable == backend->priv->writable)
1474 return;
1475
1476 backend->priv->writable = writable;
1477
1478 g_object_notify (G_OBJECT (backend), "writable");
1479 }
1480
1481 /**
1482 * e_cal_backend_is_opened:
1483 * @backend: an #ECalBackend
1484 *
1485 * Checks if @backend's storage has been opened (and
1486 * authenticated, if necessary) and the backend itself
1487 * is ready for accessing. This property is changed automatically
1488 * after the @backend is successfully opened.
1489 *
1490 * Returns: %TRUE if fully opened, %FALSE otherwise.
1491 *
1492 * Since: 3.2
1493 **/
1494 gboolean
e_cal_backend_is_opened(ECalBackend * backend)1495 e_cal_backend_is_opened (ECalBackend *backend)
1496 {
1497 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
1498
1499 return backend->priv->opened;
1500 }
1501
1502 /**
1503 * e_cal_backend_is_readonly:
1504 * @backend: an #ECalBackend
1505 *
1506 * Returns: Whether is backend read-only.
1507 *
1508 * Since: 3.2
1509 **/
1510 gboolean
e_cal_backend_is_readonly(ECalBackend * backend)1511 e_cal_backend_is_readonly (ECalBackend *backend)
1512 {
1513 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
1514
1515 return !e_cal_backend_get_writable (backend);
1516 }
1517
1518 /**
1519 * e_cal_backend_get_cache_dir:
1520 * @backend: an #ECalBackend
1521 *
1522 * Returns the cache directory path used by @backend.
1523 *
1524 * Returns: the cache directory path
1525 *
1526 * Since: 2.32
1527 **/
1528 const gchar *
e_cal_backend_get_cache_dir(ECalBackend * backend)1529 e_cal_backend_get_cache_dir (ECalBackend *backend)
1530 {
1531 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1532
1533 return backend->priv->cache_dir;
1534 }
1535
1536 /**
1537 * e_cal_backend_dup_cache_dir:
1538 * @backend: an #ECalBackend
1539 *
1540 * Thread-safe variation of e_cal_backend_get_cache_dir().
1541 * Use this function when accessing @backend from multiple threads.
1542 *
1543 * The returned string should be freed with g_free() when no longer needed.
1544 *
1545 * Returns: a newly-allocated copy of #ECalBackend:cache-dir
1546 *
1547 * Since: 3.10
1548 **/
1549 gchar *
e_cal_backend_dup_cache_dir(ECalBackend * backend)1550 e_cal_backend_dup_cache_dir (ECalBackend *backend)
1551 {
1552 const gchar *protected;
1553 gchar *duplicate;
1554
1555 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1556
1557 g_mutex_lock (&backend->priv->property_lock);
1558
1559 protected = e_cal_backend_get_cache_dir (backend);
1560 duplicate = g_strdup (protected);
1561
1562 g_mutex_unlock (&backend->priv->property_lock);
1563
1564 return duplicate;
1565 }
1566
1567 /**
1568 * e_cal_backend_set_cache_dir:
1569 * @backend: an #ECalBackend
1570 * @cache_dir: a local cache directory path
1571 *
1572 * Sets the cache directory path for use by @backend.
1573 *
1574 * Note that #ECalBackend is initialized with a default cache directory
1575 * path which should suffice for most cases. Backends should not override
1576 * the default path without good reason.
1577 *
1578 * Since: 2.32
1579 **/
1580 void
e_cal_backend_set_cache_dir(ECalBackend * backend,const gchar * cache_dir)1581 e_cal_backend_set_cache_dir (ECalBackend *backend,
1582 const gchar *cache_dir)
1583 {
1584 g_return_if_fail (E_IS_CAL_BACKEND (backend));
1585 g_return_if_fail (cache_dir != NULL);
1586
1587 g_mutex_lock (&backend->priv->property_lock);
1588
1589 if (g_strcmp0 (backend->priv->cache_dir, cache_dir) == 0) {
1590 g_mutex_unlock (&backend->priv->property_lock);
1591 return;
1592 }
1593
1594 g_free (backend->priv->cache_dir);
1595 backend->priv->cache_dir = g_strdup (cache_dir);
1596
1597 g_mutex_unlock (&backend->priv->property_lock);
1598
1599 g_object_notify (G_OBJECT (backend), "cache-dir");
1600 }
1601
1602 /**
1603 * e_cal_backend_create_cache_filename:
1604 * @backend: an #ECalBackend
1605 * @uid: a component UID
1606 * @filename: a filename to use; can be NULL
1607 * @fileindex: index of a file; used only when @filename is NULL
1608 *
1609 * Returns: a filename for an attachment in a local cache dir. Free returned
1610 * pointer with a g_free().
1611 *
1612 * Since: 3.4
1613 **/
1614 gchar *
e_cal_backend_create_cache_filename(ECalBackend * backend,const gchar * uid,const gchar * filename,gint fileindex)1615 e_cal_backend_create_cache_filename (ECalBackend *backend,
1616 const gchar *uid,
1617 const gchar *filename,
1618 gint fileindex)
1619 {
1620 const gchar *cache_dir;
1621
1622 g_return_val_if_fail (backend != NULL, NULL);
1623 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1624
1625 cache_dir = e_cal_backend_get_cache_dir (backend);
1626
1627 return e_filename_mkdir_encoded (cache_dir, uid, filename, fileindex);
1628 }
1629
1630 /**
1631 * e_cal_backend_get_backend_property:
1632 * @backend: an #ECalBackend
1633 * @prop_name: a backend property name
1634 *
1635 * Obtains the value of the backend property named @prop_name.
1636 * Freed the returned string with g_free() when finished with it.
1637 *
1638 * Returns: the value for @prop_name
1639 *
1640 * Since: 3.10
1641 **/
1642 gchar *
e_cal_backend_get_backend_property(ECalBackend * backend,const gchar * prop_name)1643 e_cal_backend_get_backend_property (ECalBackend *backend,
1644 const gchar *prop_name)
1645 {
1646 ECalBackendClass *class;
1647
1648 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1649 g_return_val_if_fail (prop_name != NULL, NULL);
1650
1651 class = E_CAL_BACKEND_GET_CLASS (backend);
1652 g_return_val_if_fail (class != NULL, NULL);
1653 g_return_val_if_fail (class->impl_get_backend_property != NULL, NULL);
1654
1655 return class->impl_get_backend_property (backend, prop_name);
1656 }
1657
1658 /**
1659 * e_cal_backend_add_view:
1660 * @backend: an #ECalBackend
1661 * @view: An #EDataCalView object.
1662 *
1663 * Adds a view to the list of live views being run by the given backend.
1664 * Doing so means that any listener on the view will get notified of any
1665 * change that affect the live view.
1666 *
1667 * Since: 3.2
1668 */
1669 void
e_cal_backend_add_view(ECalBackend * backend,EDataCalView * view)1670 e_cal_backend_add_view (ECalBackend *backend,
1671 EDataCalView *view)
1672 {
1673 g_return_if_fail (backend != NULL);
1674 g_return_if_fail (E_IS_CAL_BACKEND (backend));
1675
1676 g_mutex_lock (&backend->priv->views_mutex);
1677
1678 g_object_ref (view);
1679 backend->priv->views = g_list_append (backend->priv->views, view);
1680
1681 g_mutex_unlock (&backend->priv->views_mutex);
1682 }
1683
1684 /**
1685 * e_cal_backend_remove_view:
1686 * @backend: an #ECalBackend
1687 * @view: An #EDataCalView object, previously added with @ref e_cal_backend_add_view.
1688 *
1689 * Removes view from the list of live views for the backend.
1690 *
1691 * Since: 3.2
1692 **/
1693 void
e_cal_backend_remove_view(ECalBackend * backend,EDataCalView * view)1694 e_cal_backend_remove_view (ECalBackend *backend,
1695 EDataCalView *view)
1696 {
1697 GList *list, *link;
1698
1699 g_return_if_fail (backend != NULL);
1700 g_return_if_fail (E_IS_CAL_BACKEND (backend));
1701
1702 /* In case the view holds the last reference to backend */
1703 g_object_ref (backend);
1704
1705 g_mutex_lock (&backend->priv->views_mutex);
1706
1707 list = backend->priv->views;
1708
1709 link = g_list_find (list, view);
1710 if (link != NULL) {
1711 g_object_unref (view);
1712 list = g_list_delete_link (list, link);
1713 }
1714
1715 backend->priv->views = list;
1716
1717 g_mutex_unlock (&backend->priv->views_mutex);
1718
1719 g_object_unref (backend);
1720 }
1721
1722 /**
1723 * e_cal_backend_list_views:
1724 * @backend: an #ECalBackend
1725 *
1726 * Returns a list of #EDataCalView instances added with
1727 * e_cal_backend_add_view().
1728 *
1729 * The views returned in the list are referenced for thread-safety.
1730 * They must each be unreferenced with g_object_unref() when finished
1731 * with them. Free the returned list itself with g_list_free().
1732 *
1733 * An easy way to free the list properly in one step is as follows:
1734 *
1735 * |[
1736 * g_list_free_full (list, g_object_unref);
1737 * ]|
1738 *
1739 * Returns: (element-type EDataCalView) (transfer full): a list of cal views
1740 *
1741 * Since: 3.8
1742 **/
1743 GList *
e_cal_backend_list_views(ECalBackend * backend)1744 e_cal_backend_list_views (ECalBackend *backend)
1745 {
1746 GList *list;
1747
1748 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1749
1750 g_mutex_lock (&backend->priv->views_mutex);
1751
1752 /* XXX Use g_list_copy_deep() once we require GLib >= 2.34. */
1753 list = g_list_copy (backend->priv->views);
1754 g_list_foreach (list, (GFunc) g_object_ref, NULL);
1755
1756 g_mutex_unlock (&backend->priv->views_mutex);
1757
1758 return list;
1759 }
1760
1761 /**
1762 * ECalBackendForeachViewFunc:
1763 * @backend: an #ECalBackend
1764 * @view: an #EDataCalView
1765 * @user_data: user data for the function
1766 *
1767 * Callback function used by e_cal_backend_foreach_view().
1768 *
1769 * Returns: %TRUE, to continue, %FALSE to stop further processing.
1770 *
1771 * Since: 3.34
1772 **/
1773
1774 /**
1775 * e_cal_backend_foreach_view:
1776 * @backend: an #ECalBackend
1777 * @func: (scope call): an #ECalBackendForeachViewFunc function to call
1778 * @user_data: (closure func): user data to pass to @func
1779 *
1780 * Calls @func for each existing view (as returned by e_cal_backend_list_views()).
1781 * The @func can return %FALSE to stop early.
1782 *
1783 * Returns: whether the call had been stopped by @func
1784 *
1785 * Since: 3.34
1786 **/
1787 gboolean
e_cal_backend_foreach_view(ECalBackend * backend,ECalBackendForeachViewFunc func,gpointer user_data)1788 e_cal_backend_foreach_view (ECalBackend *backend,
1789 ECalBackendForeachViewFunc func,
1790 gpointer user_data)
1791 {
1792 GList *views, *link;
1793 gboolean stopped = FALSE;
1794
1795 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
1796 g_return_val_if_fail (func != NULL, FALSE);
1797
1798 views = e_cal_backend_list_views (backend);
1799
1800 for (link = views; link && !stopped; link = g_list_next (link)) {
1801 stopped = !func (backend, link->data, user_data);
1802 }
1803
1804 g_list_free_full (views, g_object_unref);
1805
1806 return stopped;
1807 }
1808
1809 struct NotifyProgressData {
1810 gboolean only_completed_views;
1811 gint percent;
1812 const gchar *message;
1813 };
1814
1815 static gboolean
ecb_notify_progress_cb(ECalBackend * backend,EDataCalView * view,gpointer user_data)1816 ecb_notify_progress_cb (ECalBackend *backend,
1817 EDataCalView *view,
1818 gpointer user_data)
1819 {
1820 struct NotifyProgressData *npd = user_data;
1821
1822 g_return_val_if_fail (E_IS_DATA_CAL_VIEW (view), FALSE);
1823 g_return_val_if_fail (npd != NULL, FALSE);
1824
1825 if (!npd->only_completed_views || e_data_cal_view_is_completed (view))
1826 e_data_cal_view_notify_progress (view, npd->percent, npd->message);
1827
1828 return TRUE;
1829 }
1830
1831 /**
1832 * e_cal_backend_foreach_view_notify_progress:
1833 * @backend: an #ECalBackend
1834 * @only_completed_views: whether notify in completed views only
1835 * @percent: percent complete
1836 * @message: (nullable): message describing the operation in progress, or %NULL
1837 *
1838 * Notifies each view of the @backend about progress. When @only_completed_views
1839 * is %TRUE, notifies only completed views.
1840 *
1841 * Since: 3.34
1842 **/
1843 void
e_cal_backend_foreach_view_notify_progress(ECalBackend * backend,gboolean only_completed_views,gint percent,const gchar * message)1844 e_cal_backend_foreach_view_notify_progress (ECalBackend *backend,
1845 gboolean only_completed_views,
1846 gint percent,
1847 const gchar *message)
1848 {
1849 struct NotifyProgressData npd;
1850
1851 g_return_if_fail (E_IS_CAL_BACKEND (backend));
1852
1853 npd.only_completed_views = only_completed_views;
1854 npd.percent = percent;
1855 npd.message = message;
1856
1857 e_cal_backend_foreach_view (backend, ecb_notify_progress_cb, &npd);
1858 }
1859
1860 /**
1861 * e_cal_backend_open_sync:
1862 * @backend: an #ECalBackend
1863 * @cancellable: optional #GCancellable object, or %NULL
1864 * @error: return location for a #GError, or %NULL
1865 *
1866 * "Opens" the @backend. Opening a backend is something of an outdated
1867 * concept, but the operation is hanging around for a little while longer.
1868 * This usually involves some custom initialization logic, and testing of
1869 * remote authentication if applicable.
1870 *
1871 * If an error occurs, the function will set @error and return %FALSE.
1872 *
1873 * Returns: %TRUE on success, %FALSE on failure
1874 *
1875 * Since: 3.10
1876 **/
1877 gboolean
e_cal_backend_open_sync(ECalBackend * backend,GCancellable * cancellable,GError ** error)1878 e_cal_backend_open_sync (ECalBackend *backend,
1879 GCancellable *cancellable,
1880 GError **error)
1881 {
1882 EAsyncClosure *closure;
1883 GAsyncResult *result;
1884 gboolean success;
1885
1886 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
1887
1888 closure = e_async_closure_new ();
1889
1890 e_cal_backend_open (
1891 backend, cancellable,
1892 e_async_closure_callback, closure);
1893
1894 result = e_async_closure_wait (closure);
1895
1896 success = e_cal_backend_open_finish (backend, result, error);
1897
1898 e_async_closure_free (closure);
1899
1900 return success;
1901 }
1902
1903 /* Helper for e_cal_backend_open() */
1904 static void
cal_backend_open_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1905 cal_backend_open_thread (GSimpleAsyncResult *simple,
1906 GObject *source_object,
1907 GCancellable *cancellable)
1908 {
1909 ECalBackend *backend;
1910 ECalBackendClass *class;
1911 EDataCal *data_cal;
1912
1913 backend = E_CAL_BACKEND (source_object);
1914
1915 class = E_CAL_BACKEND_GET_CLASS (backend);
1916 g_return_if_fail (class != NULL);
1917 g_return_if_fail (class->impl_open != NULL);
1918
1919 data_cal = e_cal_backend_ref_data_cal (backend);
1920 g_return_if_fail (data_cal != NULL);
1921
1922 if (e_cal_backend_is_opened (backend)) {
1923 g_simple_async_result_complete_in_idle (simple);
1924
1925 } else {
1926 guint32 opid;
1927
1928 opid = cal_backend_stash_operation (backend, simple);
1929
1930 e_backend_ensure_online_state_updated (E_BACKEND (backend), cancellable);
1931
1932 class->impl_open (backend, data_cal, opid, cancellable);
1933 }
1934
1935 g_object_unref (data_cal);
1936 }
1937
1938 /**
1939 * e_cal_backend_open:
1940 * @backend: an #ECalBackend
1941 * @cancellable: optional #GCancellable object, or %NULL
1942 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1943 * @user_data: data to pass to the callback function
1944 *
1945 * Asynchronously "opens" the @backend. Opening a backend is something of
1946 * an outdated concept, but the operation is hanging around for a little
1947 * while longer. This usually involves some custom initialization logic,
1948 * and testing of remote authentication if applicable.
1949 *
1950 * When the operation is finished, @callback will be called. You can then
1951 * call e_cal_backend_open_finish() to get the result of the operation.
1952 *
1953 * Since: 3.10
1954 **/
1955 void
e_cal_backend_open(ECalBackend * backend,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1956 e_cal_backend_open (ECalBackend *backend,
1957 GCancellable *cancellable,
1958 GAsyncReadyCallback callback,
1959 gpointer user_data)
1960 {
1961 GSimpleAsyncResult *simple;
1962
1963 g_return_if_fail (E_IS_CAL_BACKEND (backend));
1964
1965 simple = g_simple_async_result_new (
1966 G_OBJECT (backend), callback,
1967 user_data, e_cal_backend_open);
1968
1969 g_simple_async_result_set_check_cancellable (simple, cancellable);
1970
1971 cal_backend_push_operation (
1972 backend, simple, cancellable, TRUE,
1973 cal_backend_open_thread);
1974
1975 cal_backend_dispatch_next_operation (backend);
1976
1977 g_object_unref (simple);
1978 }
1979
1980 /**
1981 * e_cal_backend_open_finish:
1982 * @backend: an #ECalBackend
1983 * @result: a #GAsyncResult
1984 * @error: return location for a #GError, or %NULL
1985 *
1986 * Finishes the operation started with e_cal_backend_open().
1987 *
1988 * If an error occurred, the function will set @error and return %FALSE.
1989 *
1990 * Returns: %TRUE on success, %FALSE on failure
1991 *
1992 * Since: 3.10
1993 **/
1994 gboolean
e_cal_backend_open_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)1995 e_cal_backend_open_finish (ECalBackend *backend,
1996 GAsyncResult *result,
1997 GError **error)
1998 {
1999 GSimpleAsyncResult *simple;
2000
2001 g_return_val_if_fail (
2002 g_simple_async_result_is_valid (
2003 result, G_OBJECT (backend),
2004 e_cal_backend_open), FALSE);
2005
2006 simple = G_SIMPLE_ASYNC_RESULT (result);
2007
2008 cal_backend_unblock_operations (backend, simple);
2009
2010 if (g_simple_async_result_propagate_error (simple, error))
2011 return FALSE;
2012
2013 backend->priv->opened = TRUE;
2014
2015 return TRUE;
2016 }
2017
2018 /**
2019 * e_cal_backend_refresh_sync:
2020 * @backend: an #ECalBackend
2021 * @cancellable: optional #GCancellable object, or %NULL
2022 * @error: return location for a #GError, or %NULL
2023 *
2024 * Initiates a refresh for @backend, if the @backend supports refreshing.
2025 * The actual refresh operation completes on its own time. This function
2026 * merely initiates the operation.
2027 *
2028 * If an error occrs while initiating the refresh, the function will set
2029 * @error and return %FALSE. If the @backend does not support refreshing,
2030 * the function will set an %E_CLIENT_ERROR_NOT_SUPPORTED error and return
2031 * %FALSE.
2032 *
2033 * Returns: %TRUE on success, %FALSE on failure
2034 *
2035 * Since: 3.10
2036 **/
2037 gboolean
e_cal_backend_refresh_sync(ECalBackend * backend,GCancellable * cancellable,GError ** error)2038 e_cal_backend_refresh_sync (ECalBackend *backend,
2039 GCancellable *cancellable,
2040 GError **error)
2041 {
2042 EAsyncClosure *closure;
2043 GAsyncResult *result;
2044 gboolean success;
2045
2046 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
2047
2048 closure = e_async_closure_new ();
2049
2050 e_cal_backend_refresh (
2051 backend, cancellable,
2052 e_async_closure_callback, closure);
2053
2054 result = e_async_closure_wait (closure);
2055
2056 success = e_cal_backend_refresh_finish (backend, result, error);
2057
2058 e_async_closure_free (closure);
2059
2060 return success;
2061 }
2062
2063 /* Helper for e_cal_backend_refresh() */
2064 static void
cal_backend_refresh_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2065 cal_backend_refresh_thread (GSimpleAsyncResult *simple,
2066 GObject *source_object,
2067 GCancellable *cancellable)
2068 {
2069 ECalBackend *backend;
2070 ECalBackendClass *class;
2071 EDataCal *data_cal;
2072
2073 backend = E_CAL_BACKEND (source_object);
2074
2075 class = E_CAL_BACKEND_GET_CLASS (backend);
2076 g_return_if_fail (class != NULL);
2077
2078 data_cal = e_cal_backend_ref_data_cal (backend);
2079 g_return_if_fail (data_cal != NULL);
2080
2081 if (class->impl_refresh == NULL) {
2082 g_simple_async_result_set_error (
2083 simple, E_CLIENT_ERROR,
2084 E_CLIENT_ERROR_NOT_SUPPORTED,
2085 "%s", e_client_error_to_string (
2086 E_CLIENT_ERROR_NOT_SUPPORTED));
2087 g_simple_async_result_complete_in_idle (simple);
2088
2089 } else if (!e_cal_backend_is_opened (backend)) {
2090 g_simple_async_result_set_error (
2091 simple, E_CLIENT_ERROR,
2092 E_CLIENT_ERROR_NOT_OPENED,
2093 "%s", e_client_error_to_string (
2094 E_CLIENT_ERROR_NOT_OPENED));
2095 g_simple_async_result_complete_in_idle (simple);
2096
2097 } else {
2098 guint32 opid;
2099
2100 opid = cal_backend_stash_operation (backend, simple);
2101
2102 class->impl_refresh (backend, data_cal, opid, cancellable);
2103 }
2104
2105 g_object_unref (data_cal);
2106 }
2107
2108 /**
2109 * e_cal_backend_refresh:
2110 * @backend: an #ECalBackend
2111 * @cancellable: optional #GCancellable object, or %NULL
2112 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2113 * @user_data: data to pass to the callback function
2114 *
2115 * Asynchronously initiates a refresh for @backend, if the @backend supports
2116 * refreshing. The actual refresh operation completes on its own time. This
2117 * function, along with e_cal_backend_refresh_finish(), merely initiates the
2118 * operation.
2119 *
2120 * Once the refresh is initiated, @callback will be called. You can then
2121 * call e_cal_backend_refresh_finish() to get the result of the initiation.
2122 *
2123 * Since: 3.10
2124 **/
2125 void
e_cal_backend_refresh(ECalBackend * backend,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2126 e_cal_backend_refresh (ECalBackend *backend,
2127 GCancellable *cancellable,
2128 GAsyncReadyCallback callback,
2129 gpointer user_data)
2130 {
2131 GSimpleAsyncResult *simple;
2132
2133 g_return_if_fail (E_IS_CAL_BACKEND (backend));
2134
2135 simple = g_simple_async_result_new (
2136 G_OBJECT (backend), callback,
2137 user_data, e_cal_backend_refresh);
2138
2139 g_simple_async_result_set_check_cancellable (simple, cancellable);
2140
2141 cal_backend_push_operation (
2142 backend, simple, cancellable, FALSE,
2143 cal_backend_refresh_thread);
2144
2145 cal_backend_dispatch_next_operation (backend);
2146
2147 g_object_unref (simple);
2148 }
2149
2150 /**
2151 * e_cal_backend_refresh_finish:
2152 * @backend: an #ECalBackend
2153 * @result: a #GAsyncResult
2154 * @error: return location for a #GError, or %NULL
2155 *
2156 * Finishes the refresh initiation started with e_cal_backend_refresh().
2157 *
2158 * If an error occurred while initiating the refresh, the function will set
2159 * @error and return %FALSE. If the @backend does not support refreshing,
2160 * the function will set an %E_CLIENT_ERROR_NOT_SUPPORTED error and return
2161 * %FALSE.
2162 *
2163 * Returns: %TRUE on success, %FALSE on failure
2164 *
2165 * Since: 3.10
2166 **/
2167 gboolean
e_cal_backend_refresh_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)2168 e_cal_backend_refresh_finish (ECalBackend *backend,
2169 GAsyncResult *result,
2170 GError **error)
2171 {
2172 GSimpleAsyncResult *simple;
2173
2174 g_return_val_if_fail (
2175 g_simple_async_result_is_valid (
2176 result, G_OBJECT (backend),
2177 e_cal_backend_refresh), FALSE);
2178
2179 simple = G_SIMPLE_ASYNC_RESULT (result);
2180
2181 cal_backend_unblock_operations (backend, simple);
2182
2183 /* Assume success unless a GError is set. */
2184 return !g_simple_async_result_propagate_error (simple, error);
2185 }
2186
2187 /**
2188 * e_cal_backend_get_object_sync:
2189 * @backend: an #ECalBackend
2190 * @uid: a unique ID for an iCalendar object
2191 * @rid: a recurrence ID, or %NULL
2192 * @cancellable: optional #GCancellable object, or %NULL
2193 * @error: return location for a #GError, or %NULL
2194 *
2195 * Obtains an iCalendar string for an object identified by its @uid and,
2196 * optionally, @rid.
2197 *
2198 * The returned string should be freed with g_free() when finished with it.
2199 *
2200 * If an error occurs, the function will set @error and return %NULL.
2201 *
2202 * Returns: an #ECalComponent, or %NULL
2203 *
2204 * Since: 3.10
2205 **/
2206 gchar *
e_cal_backend_get_object_sync(ECalBackend * backend,const gchar * uid,const gchar * rid,GCancellable * cancellable,GError ** error)2207 e_cal_backend_get_object_sync (ECalBackend *backend,
2208 const gchar *uid,
2209 const gchar *rid,
2210 GCancellable *cancellable,
2211 GError **error)
2212 {
2213 EAsyncClosure *closure;
2214 GAsyncResult *result;
2215 gchar *calobj;
2216
2217 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
2218 g_return_val_if_fail (uid != NULL, NULL);
2219 /* rid can be NULL */
2220
2221 closure = e_async_closure_new ();
2222
2223 e_cal_backend_get_object (
2224 backend, uid, rid, cancellable,
2225 e_async_closure_callback, closure);
2226
2227 result = e_async_closure_wait (closure);
2228
2229 calobj = e_cal_backend_get_object_finish (backend, result, error);
2230
2231 e_async_closure_free (closure);
2232
2233 return calobj;
2234 }
2235
2236 /* Helper for e_cal_backend_get_object() */
2237 static void
cal_backend_get_object_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2238 cal_backend_get_object_thread (GSimpleAsyncResult *simple,
2239 GObject *source_object,
2240 GCancellable *cancellable)
2241 {
2242 ECalBackend *backend;
2243 ECalBackendClass *class;
2244 EDataCal *data_cal;
2245 AsyncContext *async_context;
2246
2247 backend = E_CAL_BACKEND (source_object);
2248
2249 class = E_CAL_BACKEND_GET_CLASS (backend);
2250 g_return_if_fail (class != NULL);
2251 g_return_if_fail (class->impl_get_object != NULL);
2252
2253 data_cal = e_cal_backend_ref_data_cal (backend);
2254 g_return_if_fail (data_cal != NULL);
2255
2256 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2257
2258 if (!e_cal_backend_is_opened (backend)) {
2259 g_simple_async_result_set_error (
2260 simple, E_CLIENT_ERROR,
2261 E_CLIENT_ERROR_NOT_OPENED,
2262 "%s", e_client_error_to_string (
2263 E_CLIENT_ERROR_NOT_OPENED));
2264 g_simple_async_result_complete_in_idle (simple);
2265
2266 } else {
2267 guint32 opid;
2268
2269 opid = cal_backend_stash_operation (backend, simple);
2270
2271 class->impl_get_object (
2272 backend, data_cal, opid, cancellable,
2273 async_context->uid,
2274 async_context->rid);
2275 }
2276
2277 g_object_unref (data_cal);
2278 }
2279
2280 /**
2281 * e_cal_backend_get_object:
2282 * @backend: an #ECalBackend
2283 * @uid: a unique ID for an iCalendar object
2284 * @rid: a recurrence ID, or %NULL
2285 * @cancellable: optional #GCancellable object, or %NULL
2286 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2287 * @user_data: data to pass to the callback function
2288 *
2289 * Asynchronously obtains an #ECalComponent by its @uid and, optionally, @rid.
2290 *
2291 * When the operation is finished, @callback will be called. You can then
2292 * call e_cal_backend_get_object_finish() to get the result of the operation.
2293 *
2294 * Since: 3.10
2295 **/
2296 void
e_cal_backend_get_object(ECalBackend * backend,const gchar * uid,const gchar * rid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2297 e_cal_backend_get_object (ECalBackend *backend,
2298 const gchar *uid,
2299 const gchar *rid,
2300 GCancellable *cancellable,
2301 GAsyncReadyCallback callback,
2302 gpointer user_data)
2303 {
2304 GSimpleAsyncResult *simple;
2305 AsyncContext *async_context;
2306
2307 g_return_if_fail (E_IS_CAL_BACKEND (backend));
2308 g_return_if_fail (uid != NULL);
2309 /* rid can be NULL */
2310
2311 async_context = g_slice_new0 (AsyncContext);
2312 async_context->uid = g_strdup (uid);
2313 async_context->rid = g_strdup (rid);
2314
2315 simple = g_simple_async_result_new (
2316 G_OBJECT (backend), callback, user_data,
2317 e_cal_backend_get_object);
2318
2319 g_simple_async_result_set_check_cancellable (simple, cancellable);
2320
2321 g_simple_async_result_set_op_res_gpointer (
2322 simple, async_context, (GDestroyNotify) async_context_free);
2323
2324 cal_backend_push_operation (
2325 backend, simple, cancellable, FALSE,
2326 cal_backend_get_object_thread);
2327
2328 cal_backend_dispatch_next_operation (backend);
2329
2330 g_object_unref (simple);
2331 }
2332
2333 /**
2334 * e_cal_backend_get_object_finish:
2335 * @backend: an #ECalBackend
2336 * @result: a #GAsyncResult
2337 * @error: return location for a #GError, or %NULL
2338 *
2339 * Finishes the operation started with e_cal_backend_get_object().
2340 *
2341 * The returned string is an iCalendar object describing either single component
2342 * or a vCalendar object, which includes also detached instances. It should be
2343 * freed when no longer needed.
2344 *
2345 * If an error occurs, the function will set @error and return %NULL.
2346 *
2347 * Returns: an #ECalComponent, or %NULL
2348 *
2349 * Since: 3.10
2350 **/
2351 gchar *
e_cal_backend_get_object_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)2352 e_cal_backend_get_object_finish (ECalBackend *backend,
2353 GAsyncResult *result,
2354 GError **error)
2355 {
2356 GSimpleAsyncResult *simple;
2357 AsyncContext *async_context;
2358 gchar *calobj;
2359
2360 g_return_val_if_fail (
2361 g_simple_async_result_is_valid (
2362 result, G_OBJECT (backend),
2363 e_cal_backend_get_object), FALSE);
2364
2365 simple = G_SIMPLE_ASYNC_RESULT (result);
2366 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2367
2368 cal_backend_unblock_operations (backend, simple);
2369
2370 if (g_simple_async_result_propagate_error (simple, error))
2371 return FALSE;
2372
2373 calobj = g_queue_pop_head (&async_context->result_queue);
2374
2375 g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
2376
2377 return calobj;
2378 }
2379
2380 /**
2381 * e_cal_backend_get_object_list_sync:
2382 * @backend: an #ECalBackend
2383 * @query: a search query in S-expression format
2384 * @out_objects: (element-type utf8): a #GQueue in which to deposit results
2385 * @cancellable: optional #GCancellable object, or %NULL
2386 * @error: return location for a #GError, or %NULL
2387 *
2388 * Obtains a set of iCalendar string instances which satisfy the criteria
2389 * specified in @query, and deposits them in @out_objects.
2390 *
2391 * The returned instances should be freed with g_free() when finished with them.
2392 *
2393 * If an error occurs, the function will set @error and return %FALSE.
2394 * Note that an empty result set does not necessarily imply an error.
2395 *
2396 * Returns: %TRUE on success, %FALSE on failure
2397 *
2398 * Since: 3.10
2399 **/
2400 gboolean
e_cal_backend_get_object_list_sync(ECalBackend * backend,const gchar * query,GQueue * out_objects,GCancellable * cancellable,GError ** error)2401 e_cal_backend_get_object_list_sync (ECalBackend *backend,
2402 const gchar *query,
2403 GQueue *out_objects,
2404 GCancellable *cancellable,
2405 GError **error)
2406 {
2407 EAsyncClosure *closure;
2408 GAsyncResult *result;
2409 gboolean success;
2410
2411 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
2412 g_return_val_if_fail (query != NULL, FALSE);
2413
2414 closure = e_async_closure_new ();
2415
2416 e_cal_backend_get_object_list (
2417 backend, query, cancellable,
2418 e_async_closure_callback, closure);
2419
2420 result = e_async_closure_wait (closure);
2421
2422 success = e_cal_backend_get_object_list_finish (
2423 backend, result, out_objects, error);
2424
2425 e_async_closure_free (closure);
2426
2427 return success;
2428 }
2429
2430 /* Helper for e_cal_backend_get_object_list() */
2431 static void
cal_backend_get_object_list_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2432 cal_backend_get_object_list_thread (GSimpleAsyncResult *simple,
2433 GObject *source_object,
2434 GCancellable *cancellable)
2435 {
2436 ECalBackend *backend;
2437 ECalBackendClass *class;
2438 EDataCal *data_cal;
2439 AsyncContext *async_context;
2440
2441 backend = E_CAL_BACKEND (source_object);
2442
2443 class = E_CAL_BACKEND_GET_CLASS (backend);
2444 g_return_if_fail (class != NULL);
2445 g_return_if_fail (class->impl_get_object_list != NULL);
2446
2447 data_cal = e_cal_backend_ref_data_cal (backend);
2448 g_return_if_fail (data_cal != NULL);
2449
2450 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2451
2452 if (!e_cal_backend_is_opened (backend)) {
2453 g_simple_async_result_set_error (
2454 simple, E_CLIENT_ERROR,
2455 E_CLIENT_ERROR_NOT_OPENED,
2456 "%s", e_client_error_to_string (
2457 E_CLIENT_ERROR_NOT_OPENED));
2458 g_simple_async_result_complete_in_idle (simple);
2459
2460 } else {
2461 guint32 opid;
2462
2463 opid = cal_backend_stash_operation (backend, simple);
2464
2465 class->impl_get_object_list (
2466 backend, data_cal, opid, cancellable,
2467 async_context->query);
2468 }
2469
2470 g_object_unref (data_cal);
2471 }
2472
2473 /**
2474 * e_cal_backend_get_object_list:
2475 * @backend: an #ECalBackend
2476 * @query: a search query in S-expression format
2477 * @cancellable: optional #GCancellable object, or %NULL
2478 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2479 * @user_data: data to pass to the callback function
2480 *
2481 * Asynchronously obtains a set of iCalendar instances which satisfy
2482 * the criteria specified in @query.
2483 *
2484 * When the operation in finished, @callback will be called. You can then
2485 * call e_cal_backend_get_object_list_finish() to get the result of the
2486 * operation.
2487 *
2488 * Since: 3.10
2489 **/
2490 void
e_cal_backend_get_object_list(ECalBackend * backend,const gchar * query,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2491 e_cal_backend_get_object_list (ECalBackend *backend,
2492 const gchar *query,
2493 GCancellable *cancellable,
2494 GAsyncReadyCallback callback,
2495 gpointer user_data)
2496 {
2497 GSimpleAsyncResult *simple;
2498 AsyncContext *async_context;
2499
2500 g_return_if_fail (E_IS_CAL_BACKEND (backend));
2501 g_return_if_fail (query != NULL);
2502
2503 async_context = g_slice_new0 (AsyncContext);
2504 async_context->query = g_strdup (query);
2505
2506 simple = g_simple_async_result_new (
2507 G_OBJECT (backend), callback, user_data,
2508 e_cal_backend_get_object_list);
2509
2510 g_simple_async_result_set_check_cancellable (simple, cancellable);
2511
2512 g_simple_async_result_set_op_res_gpointer (
2513 simple, async_context, (GDestroyNotify) async_context_free);
2514
2515 cal_backend_push_operation (
2516 backend, simple, cancellable, FALSE,
2517 cal_backend_get_object_list_thread);
2518
2519 cal_backend_dispatch_next_operation (backend);
2520
2521 g_object_unref (simple);
2522 }
2523
2524 /**
2525 * e_cal_backend_get_object_list_finish:
2526 * @backend: an #ECalBackend
2527 * @result: a #GAsyncResult
2528 * @out_objects: (element-type utf8): a #GQueue in which to deposit results
2529 * @error: return location for a #GError, or %NULL
2530 *
2531 * Finishes the operation started with e_cal_backend_get_object_list().
2532 *
2533 * The matching iCalendar instances are deposited in @out_objects.
2534 * The returned instances should be freed with g_free() when finished with them.
2535 *
2536 * If an error occurred, the function will set @error and return %FALSE.
2537 * Note that an empty result set does not necessarily imply an error.
2538 *
2539 * Returns: %TRUE on success, %FALSE on failure
2540 *
2541 * Since: 3.10
2542 **/
2543 gboolean
e_cal_backend_get_object_list_finish(ECalBackend * backend,GAsyncResult * result,GQueue * out_objects,GError ** error)2544 e_cal_backend_get_object_list_finish (ECalBackend *backend,
2545 GAsyncResult *result,
2546 GQueue *out_objects,
2547 GError **error)
2548 {
2549 GSimpleAsyncResult *simple;
2550 AsyncContext *async_context;
2551
2552 g_return_val_if_fail (
2553 g_simple_async_result_is_valid (
2554 result, G_OBJECT (backend),
2555 e_cal_backend_get_object_list), FALSE);
2556 g_return_val_if_fail (out_objects != NULL, FALSE);
2557
2558 simple = G_SIMPLE_ASYNC_RESULT (result);
2559 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2560
2561 cal_backend_unblock_operations (backend, simple);
2562
2563 if (g_simple_async_result_propagate_error (simple, error))
2564 return FALSE;
2565
2566 e_queue_transfer (&async_context->result_queue, out_objects);
2567
2568 return TRUE;
2569 }
2570
2571 /**
2572 * e_cal_backend_get_free_busy_sync:
2573 * @backend: an #ECalBackend
2574 * @start: start time
2575 * @end: end time
2576 * @users: (array zero-terminated=1): a %NULL-terminated array of user strings
2577 * @out_freebusy: (element-type utf8): iCalendar strings with overall returned Free/Busy data
2578 * @cancellable: optional #GCancellable object, or %NULL
2579 * @error: return location for a #GError, or %NULL
2580 *
2581 * Obtains a free/busy object for the list of @users in the time interval
2582 * between @start and @end.
2583 *
2584 * The free/busy results can be returned through the
2585 * e_data_cal_report_free_busy_data() function asynchronously. The out_freebusy
2586 * will contain all the returned data, possibly again, thus the client is
2587 * responsible for the data merge, if needed.
2588 *
2589 * If an error occurs, the function will set @error and return %FALSE.
2590 *
2591 * Returns: %TRUE on success, %FALSE on failure.
2592 *
2593 * Since: 3.10
2594 **/
2595 gboolean
e_cal_backend_get_free_busy_sync(ECalBackend * backend,time_t start,time_t end,const gchar * const * users,GSList ** out_freebusy,GCancellable * cancellable,GError ** error)2596 e_cal_backend_get_free_busy_sync (ECalBackend *backend,
2597 time_t start,
2598 time_t end,
2599 const gchar * const *users,
2600 GSList **out_freebusy,
2601 GCancellable *cancellable,
2602 GError **error)
2603 {
2604 EAsyncClosure *closure;
2605 GAsyncResult *result;
2606 gboolean success;
2607
2608 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
2609 g_return_val_if_fail (users != NULL, FALSE);
2610
2611 closure = e_async_closure_new ();
2612
2613 e_cal_backend_get_free_busy (
2614 backend, start, end, users, cancellable,
2615 e_async_closure_callback, closure);
2616
2617 result = e_async_closure_wait (closure);
2618
2619 success = e_cal_backend_get_free_busy_finish (
2620 backend, result, out_freebusy, error);
2621
2622 e_async_closure_free (closure);
2623
2624 return success;
2625 }
2626
2627 static void
cal_backend_get_free_busy_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2628 cal_backend_get_free_busy_thread (GSimpleAsyncResult *simple,
2629 GObject *source_object,
2630 GCancellable *cancellable)
2631 {
2632 ECalBackend *backend;
2633 ECalBackendClass *class;
2634 EDataCal *data_cal;
2635 AsyncContext *async_context;
2636
2637 backend = E_CAL_BACKEND (source_object);
2638
2639 class = E_CAL_BACKEND_GET_CLASS (backend);
2640 g_return_if_fail (class != NULL);
2641 g_return_if_fail (class->impl_get_free_busy != NULL);
2642
2643 data_cal = e_cal_backend_ref_data_cal (backend);
2644 g_return_if_fail (data_cal != NULL);
2645
2646 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2647
2648 if (!e_cal_backend_is_opened (backend)) {
2649 g_simple_async_result_set_error (
2650 simple, E_CLIENT_ERROR,
2651 E_CLIENT_ERROR_NOT_OPENED,
2652 "%s", e_client_error_to_string (
2653 E_CLIENT_ERROR_NOT_OPENED));
2654 g_simple_async_result_complete_in_idle (simple);
2655
2656 } else {
2657 guint32 opid;
2658
2659 opid = cal_backend_stash_operation (backend, simple);
2660
2661 class->impl_get_free_busy (
2662 backend, data_cal, opid, cancellable,
2663 async_context->string_list,
2664 async_context->start,
2665 async_context->end);
2666 }
2667
2668 g_object_unref (data_cal);
2669 }
2670
2671 /**
2672 * e_cal_backend_get_free_busy:
2673 * @backend: an #ECalBackend
2674 * @start: start time
2675 * @end: end time
2676 * @users: (array zero-terminated=1): a %NULL-terminated array of user strings
2677 * @cancellable: optional #GCancellable object, or %NULL
2678 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2679 * @user_data: data to pass to the callback function
2680 *
2681 * Asynchronously obtains a free/busy object for the list of @users in the
2682 * time interval between @start and @end.
2683 *
2684 * When the operation is finished, @callback will be called. You can
2685 * then call e_cal_backend_get_free_busy_finish() to get the result of
2686 * the operation.
2687 *
2688 * Since: 3.10
2689 **/
2690 void
e_cal_backend_get_free_busy(ECalBackend * backend,time_t start,time_t end,const gchar * const * users,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2691 e_cal_backend_get_free_busy (ECalBackend *backend,
2692 time_t start,
2693 time_t end,
2694 const gchar * const *users,
2695 GCancellable *cancellable,
2696 GAsyncReadyCallback callback,
2697 gpointer user_data)
2698 {
2699 GSimpleAsyncResult *simple;
2700 AsyncContext *async_context;
2701 GSList *list = NULL;
2702 gint ii;
2703
2704 g_return_if_fail (E_IS_CAL_BACKEND (backend));
2705 g_return_if_fail (start != -1 && end != -1);
2706 g_return_if_fail (start <= end);
2707 g_return_if_fail (users != NULL);
2708
2709 for (ii = 0; users[ii] != NULL; ii++)
2710 list = g_slist_prepend (list, g_strdup (users[ii]));
2711
2712 async_context = g_slice_new0 (AsyncContext);
2713 async_context->start = start;
2714 async_context->end = end;
2715 async_context->string_list = g_slist_reverse (list);
2716
2717 simple = g_simple_async_result_new (
2718 G_OBJECT (backend), callback, user_data,
2719 e_cal_backend_get_free_busy);
2720
2721 g_simple_async_result_set_check_cancellable (simple, cancellable);
2722
2723 g_simple_async_result_set_op_res_gpointer (
2724 simple, async_context, (GDestroyNotify) async_context_free);
2725
2726 cal_backend_push_operation (
2727 backend, simple, cancellable, FALSE,
2728 cal_backend_get_free_busy_thread);
2729
2730 cal_backend_dispatch_next_operation (backend);
2731
2732 g_object_unref (simple);
2733 }
2734
2735 /**
2736 * e_cal_backend_get_free_busy_finish:
2737 * @backend: an #ECalBackend
2738 * @result: a #GAsyncResult
2739 * @out_freebusy: (element-type utf8): iCalendar strings with overall returned Free/Busy data
2740 * @error: return location for a #GError, or %NULL
2741 *
2742 * Finishes the operation started with e_cal_backend_get_free_busy().
2743 *
2744 * The free/busy results can be returned through the
2745 * e_data_cal_report_free_busy_data() function asynchronously. The out_freebusy
2746 * will contain all the returned data, possibly again, thus the client is
2747 * responsible for the data merge, if needed.
2748 *
2749 * If an error occurred, the function will set @error and return %FALSE.
2750 *
2751 * Returns: %TRUE on success, %FALSE on failure
2752 *
2753 * Since: 3.10
2754 **/
2755 gboolean
e_cal_backend_get_free_busy_finish(ECalBackend * backend,GAsyncResult * result,GSList ** out_freebusy,GError ** error)2756 e_cal_backend_get_free_busy_finish (ECalBackend *backend,
2757 GAsyncResult *result,
2758 GSList **out_freebusy,
2759 GError **error)
2760 {
2761 GSimpleAsyncResult *simple;
2762 AsyncContext *async_context;
2763 GSList *ical_strings = NULL;
2764
2765 g_return_val_if_fail (
2766 g_simple_async_result_is_valid (
2767 result, G_OBJECT (backend),
2768 e_cal_backend_get_free_busy), FALSE);
2769
2770 simple = G_SIMPLE_ASYNC_RESULT (result);
2771 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2772
2773 cal_backend_unblock_operations (backend, simple);
2774
2775 /* Assume success unless a GError is set. */
2776 if (g_simple_async_result_propagate_error (simple, error))
2777 return FALSE;
2778
2779 while (!g_queue_is_empty (&async_context->result_queue)) {
2780 gchar *ical_freebusy;
2781
2782 ical_freebusy = g_queue_pop_head (&async_context->result_queue);
2783 if (out_freebusy)
2784 ical_strings = g_slist_prepend (ical_strings, ical_freebusy);
2785 else
2786 g_free (ical_freebusy);
2787 }
2788
2789 if (out_freebusy)
2790 *out_freebusy = g_slist_reverse (ical_strings);
2791
2792 return TRUE;
2793 }
2794
2795 /**
2796 * e_cal_backend_create_objects_sync:
2797 * @backend: an #ECalBackend
2798 * @calobjs: a %NULL-terminated array of iCalendar strings
2799 * @opflags: bit-or of #ECalOperationFlags
2800 * @out_uids: a #GQueue in which to deposit results
2801 * @cancellable: optional #GCancellable object, or %NULL
2802 * @error: return location for a #GError, or %NULL
2803 *
2804 * Creates one or more new iCalendar objects from @calobjs, and deposits
2805 * the unique ID string for each newly-created object in @out_uids.
2806 *
2807 * Free the returned ID strings with g_free() when finished with them.
2808 *
2809 * If an error occurs, the function will set @error and return %FALSE.
2810 *
2811 * Returns: %TRUE on success, %FALSE on failure
2812 *
2813 * Since: 3.10
2814 **/
2815 gboolean
e_cal_backend_create_objects_sync(ECalBackend * backend,const gchar * const * calobjs,ECalOperationFlags opflags,GQueue * out_uids,GCancellable * cancellable,GError ** error)2816 e_cal_backend_create_objects_sync (ECalBackend *backend,
2817 const gchar * const *calobjs,
2818 ECalOperationFlags opflags,
2819 GQueue *out_uids,
2820 GCancellable *cancellable,
2821 GError **error)
2822 {
2823 EAsyncClosure *closure;
2824 GAsyncResult *result;
2825 gboolean success;
2826
2827 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
2828 g_return_val_if_fail (calobjs != NULL, FALSE);
2829
2830 closure = e_async_closure_new ();
2831
2832 e_cal_backend_create_objects (
2833 backend, calobjs, opflags, cancellable,
2834 e_async_closure_callback, closure);
2835
2836 result = e_async_closure_wait (closure);
2837
2838 success = e_cal_backend_create_objects_finish (
2839 backend, result, out_uids, error);
2840
2841 e_async_closure_free (closure);
2842
2843 return success;
2844 }
2845
2846 /* Helper for e_cal_backend_create_objects() */
2847 static void
cal_backend_create_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2848 cal_backend_create_objects_thread (GSimpleAsyncResult *simple,
2849 GObject *source_object,
2850 GCancellable *cancellable)
2851 {
2852 ECalBackend *backend;
2853 ECalBackendClass *class;
2854 EDataCal *data_cal;
2855 AsyncContext *async_context;
2856
2857 backend = E_CAL_BACKEND (source_object);
2858
2859 class = E_CAL_BACKEND_GET_CLASS (backend);
2860 g_return_if_fail (class != NULL);
2861
2862 data_cal = e_cal_backend_ref_data_cal (backend);
2863 g_return_if_fail (data_cal != NULL);
2864
2865 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2866
2867 if (class->impl_create_objects == NULL) {
2868 g_simple_async_result_set_error (
2869 simple, E_CLIENT_ERROR,
2870 E_CLIENT_ERROR_NOT_SUPPORTED,
2871 "%s", e_client_error_to_string (
2872 E_CLIENT_ERROR_NOT_SUPPORTED));
2873 g_simple_async_result_complete_in_idle (simple);
2874
2875 } else if (!e_cal_backend_is_opened (backend)) {
2876 g_simple_async_result_set_error (
2877 simple, E_CLIENT_ERROR,
2878 E_CLIENT_ERROR_NOT_OPENED,
2879 "%s", e_client_error_to_string (
2880 E_CLIENT_ERROR_NOT_OPENED));
2881 g_simple_async_result_complete_in_idle (simple);
2882
2883 } else {
2884 guint32 opid;
2885
2886 opid = cal_backend_stash_operation (backend, simple);
2887
2888 class->impl_create_objects (
2889 backend, data_cal, opid, cancellable,
2890 async_context->string_list, async_context->opflags);
2891 }
2892
2893 g_object_unref (data_cal);
2894 }
2895
2896 /**
2897 * e_cal_backend_create_objects:
2898 * @backend: an #ECalBackend
2899 * @calobjs: a %NULL-terminated array of iCalendar strings
2900 * @opflags: bit-or of #ECalOperationFlags
2901 * @cancellable: optional #GCancellable object, or %NULL
2902 * @callback: a #GAsyncReadyCallback to call when the request is satisifed
2903 * @user_data: data to pass to the callback function
2904 *
2905 * Asynchronously creates one or more new iCalendar objects from @calobjs.
2906 *
2907 * When the operation is finished, @callback will be called. You can then
2908 * call e_cal_backend_create_objects_finish() to get the result of the
2909 * operation.
2910 *
2911 * Since: 3.10
2912 **/
2913 void
e_cal_backend_create_objects(ECalBackend * backend,const gchar * const * calobjs,ECalOperationFlags opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2914 e_cal_backend_create_objects (ECalBackend *backend,
2915 const gchar * const *calobjs,
2916 ECalOperationFlags opflags,
2917 GCancellable *cancellable,
2918 GAsyncReadyCallback callback,
2919 gpointer user_data)
2920 {
2921 GSimpleAsyncResult *simple;
2922 AsyncContext *async_context;
2923 GSList *list = NULL;
2924 gint ii;
2925
2926 g_return_if_fail (E_IS_CAL_BACKEND (backend));
2927 g_return_if_fail (calobjs != NULL);
2928
2929 for (ii = 0; calobjs[ii] != NULL; ii++)
2930 list = g_slist_prepend (list, g_strdup (calobjs[ii]));
2931
2932 async_context = g_slice_new0 (AsyncContext);
2933 async_context->string_list = g_slist_reverse (list);
2934 async_context->opflags = opflags;
2935
2936 simple = g_simple_async_result_new (
2937 G_OBJECT (backend), callback, user_data,
2938 e_cal_backend_create_objects);
2939
2940 g_simple_async_result_set_check_cancellable (simple, cancellable);
2941
2942 g_simple_async_result_set_op_res_gpointer (
2943 simple, async_context, (GDestroyNotify) async_context_free);
2944
2945 cal_backend_push_operation (
2946 backend, simple, cancellable, FALSE,
2947 cal_backend_create_objects_thread);
2948
2949 cal_backend_dispatch_next_operation (backend);
2950
2951 g_object_unref (simple);
2952 }
2953
2954 /**
2955 * e_cal_backend_create_objects_finish:
2956 * @backend: an #ECalBackend
2957 * @result: a #GAsyncResult
2958 * @out_uids: a #GQueue in which to deposit results
2959 * @error: return location for a #GError, or %NULL
2960 *
2961 * Finishes the operation started with e_cal_backend_create_objects().
2962 *
2963 * A unique ID string for each newly-created object is deposited in @out_uids.
2964 * Free the returned ID strings with g_free() when finished with them.
2965 *
2966 * If an error occurred, the function will set @error and return %FALSE.
2967 *
2968 * Returns: %TRUE on success, %FALSE on failure
2969 *
2970 * Since: 3.10
2971 **/
2972 gboolean
e_cal_backend_create_objects_finish(ECalBackend * backend,GAsyncResult * result,GQueue * out_uids,GError ** error)2973 e_cal_backend_create_objects_finish (ECalBackend *backend,
2974 GAsyncResult *result,
2975 GQueue *out_uids,
2976 GError **error)
2977 {
2978 GSimpleAsyncResult *simple;
2979 AsyncContext *async_context;
2980 GQueue *string_queue;
2981 GQueue *component_queue;
2982
2983 g_return_val_if_fail (
2984 g_simple_async_result_is_valid (
2985 result, G_OBJECT (backend),
2986 e_cal_backend_create_objects), FALSE);
2987 g_return_val_if_fail (out_uids != NULL, FALSE);
2988
2989 simple = G_SIMPLE_ASYNC_RESULT (result);
2990 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2991
2992 cal_backend_unblock_operations (backend, simple);
2993
2994 if (g_simple_async_result_propagate_error (simple, error))
2995 return FALSE;
2996
2997 /* XXX Packing GQueues in a GQueue is pretty awkward, but until
2998 * the backend methods can be updated I'm trying to make do
2999 * with a single data container for all kinds of results. */
3000
3001 string_queue = g_queue_pop_head (&async_context->result_queue);
3002 component_queue = g_queue_pop_head (&async_context->result_queue);
3003
3004 g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
3005
3006 g_return_val_if_fail (string_queue != NULL, FALSE);
3007 g_return_val_if_fail (component_queue != NULL, FALSE);
3008
3009 e_queue_transfer (string_queue, out_uids);
3010
3011 while (!g_queue_is_empty (component_queue)) {
3012 ECalComponent *component;
3013
3014 component = g_queue_pop_head (component_queue);
3015 e_cal_backend_notify_component_created (backend, component);
3016 g_object_unref (component);
3017 }
3018
3019 g_queue_free (string_queue);
3020 g_queue_free (component_queue);
3021
3022 return TRUE;
3023 }
3024
3025 /**
3026 * e_cal_backend_modify_objects_sync:
3027 * @backend: an #ECalBackend
3028 * @calobjs: a %NULL-terminated array of iCalendar strings
3029 * @mod: modification type for recurrences
3030 * @opflags: bit-or of #ECalOperationFlags
3031 * @cancellable: optional #GCancellable object, or %NULL
3032 * @error: return location for a #GError, or %NULL
3033 *
3034 * Modifies one or more iCalendar objects according to @calobjs and @mod.
3035 *
3036 * If an error occurs, the function will set @error and return %FALSE.
3037 *
3038 * Returns: %TRUE on success, %FALSE on failure
3039 *
3040 * Since: 3.10
3041 **/
3042 gboolean
e_cal_backend_modify_objects_sync(ECalBackend * backend,const gchar * const * calobjs,ECalObjModType mod,ECalOperationFlags opflags,GCancellable * cancellable,GError ** error)3043 e_cal_backend_modify_objects_sync (ECalBackend *backend,
3044 const gchar * const *calobjs,
3045 ECalObjModType mod,
3046 ECalOperationFlags opflags,
3047 GCancellable *cancellable,
3048 GError **error)
3049 {
3050 EAsyncClosure *closure;
3051 GAsyncResult *result;
3052 gboolean success;
3053
3054 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
3055 g_return_val_if_fail (calobjs != NULL, FALSE);
3056
3057 closure = e_async_closure_new ();
3058
3059 e_cal_backend_modify_objects (
3060 backend, calobjs, mod, opflags, cancellable,
3061 e_async_closure_callback, closure);
3062
3063 result = e_async_closure_wait (closure);
3064
3065 success = e_cal_backend_modify_objects_finish (
3066 backend, result, error);
3067
3068 e_async_closure_free (closure);
3069
3070 return success;
3071 }
3072
3073 /* Helper for e_cal_backend_modify_objects() */
3074 static void
cal_backend_modify_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3075 cal_backend_modify_objects_thread (GSimpleAsyncResult *simple,
3076 GObject *source_object,
3077 GCancellable *cancellable)
3078 {
3079 ECalBackend *backend;
3080 ECalBackendClass *class;
3081 EDataCal *data_cal;
3082 AsyncContext *async_context;
3083
3084 backend = E_CAL_BACKEND (source_object);
3085
3086 class = E_CAL_BACKEND_GET_CLASS (backend);
3087 g_return_if_fail (class != NULL);
3088
3089 data_cal = e_cal_backend_ref_data_cal (backend);
3090 g_return_if_fail (data_cal != NULL);
3091
3092 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3093
3094 if (class->impl_modify_objects == NULL) {
3095 g_simple_async_result_set_error (
3096 simple, E_CLIENT_ERROR,
3097 E_CLIENT_ERROR_NOT_SUPPORTED,
3098 "%s", e_client_error_to_string (
3099 E_CLIENT_ERROR_NOT_SUPPORTED));
3100 g_simple_async_result_complete_in_idle (simple);
3101
3102 } else if (!e_cal_backend_is_opened (backend)) {
3103 g_simple_async_result_set_error (
3104 simple, E_CLIENT_ERROR,
3105 E_CLIENT_ERROR_NOT_OPENED,
3106 "%s", e_client_error_to_string (
3107 E_CLIENT_ERROR_NOT_OPENED));
3108 g_simple_async_result_complete_in_idle (simple);
3109
3110 } else {
3111 guint32 opid;
3112
3113 opid = cal_backend_stash_operation (backend, simple);
3114
3115 class->impl_modify_objects (
3116 backend, data_cal, opid, cancellable,
3117 async_context->string_list,
3118 async_context->mod,
3119 async_context->opflags);
3120 }
3121
3122 g_object_unref (data_cal);
3123 }
3124
3125 /**
3126 * e_cal_backend_modify_objects:
3127 * @backend: an #ECalBackend
3128 * @calobjs: a %NULL-terminated array of iCalendar strings
3129 * @mod: modification type for recurrences
3130 * @opflags: bit-or of #ECalOperationFlags
3131 * @cancellable: optional #GCancellable object, or %NULL
3132 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3133 * @user_data: data to pass to the callback function
3134 *
3135 * Asynchronously modifies one or more iCalendar objects according to
3136 * @calobjs and @mod.
3137 *
3138 * When the operation is finished, @callback will be called. You can then
3139 * call e_cal_backend_modify_objects_finish() to get the result of the
3140 * operation.
3141 *
3142 * Since: 3.10
3143 **/
3144 void
e_cal_backend_modify_objects(ECalBackend * backend,const gchar * const * calobjs,ECalObjModType mod,ECalOperationFlags opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3145 e_cal_backend_modify_objects (ECalBackend *backend,
3146 const gchar * const *calobjs,
3147 ECalObjModType mod,
3148 ECalOperationFlags opflags,
3149 GCancellable *cancellable,
3150 GAsyncReadyCallback callback,
3151 gpointer user_data)
3152 {
3153 GSimpleAsyncResult *simple;
3154 AsyncContext *async_context;
3155 GSList *list = NULL;
3156 gint ii;
3157
3158 g_return_if_fail (E_IS_CAL_BACKEND (backend));
3159 g_return_if_fail (calobjs != NULL);
3160
3161 for (ii = 0; calobjs[ii] != NULL; ii++)
3162 list = g_slist_prepend (list, g_strdup (calobjs[ii]));
3163
3164 async_context = g_slice_new0 (AsyncContext);
3165 async_context->string_list = g_slist_reverse (list);
3166 async_context->mod = mod;
3167 async_context->opflags = opflags;
3168
3169 simple = g_simple_async_result_new (
3170 G_OBJECT (backend), callback, user_data,
3171 e_cal_backend_modify_objects);
3172
3173 g_simple_async_result_set_check_cancellable (simple, cancellable);
3174
3175 g_simple_async_result_set_op_res_gpointer (
3176 simple, async_context, (GDestroyNotify) async_context_free);
3177
3178 cal_backend_push_operation (
3179 backend, simple, cancellable, FALSE,
3180 cal_backend_modify_objects_thread);
3181
3182 cal_backend_dispatch_next_operation (backend);
3183
3184 g_object_unref (simple);
3185 }
3186
3187 /**
3188 * e_cal_backend_modify_objects_finish:
3189 * @backend: an #ECalBackend
3190 * @result: a #GAsyncResult
3191 * @error: return location for a #GError, or %NULL
3192 *
3193 * Finishes the operation started with e_cal_backend_modify_objects().
3194 *
3195 * If an error occurred, the function will set @error and return %FALSE.
3196 *
3197 * Returns: %TRUE on success, %FALSE on failure
3198 *
3199 * Since: 3.10
3200 **/
3201 gboolean
e_cal_backend_modify_objects_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)3202 e_cal_backend_modify_objects_finish (ECalBackend *backend,
3203 GAsyncResult *result,
3204 GError **error)
3205 {
3206 GSimpleAsyncResult *simple;
3207 AsyncContext *async_context;
3208 GQueue *old_component_queue;
3209 GQueue *new_component_queue;
3210 guint length, ii;
3211
3212 g_return_val_if_fail (
3213 g_simple_async_result_is_valid (
3214 result, G_OBJECT (backend),
3215 e_cal_backend_modify_objects), FALSE);
3216
3217 simple = G_SIMPLE_ASYNC_RESULT (result);
3218 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3219
3220 cal_backend_unblock_operations (backend, simple);
3221
3222 if (g_simple_async_result_propagate_error (simple, error))
3223 return FALSE;
3224
3225 /* XXX Packing GQueues in a GQueue is pretty awkward, but until
3226 * the backend methods can be updated I'm trying to make do
3227 * with a single data container for all kinds of results. */
3228
3229 old_component_queue = g_queue_pop_head (&async_context->result_queue);
3230 new_component_queue = g_queue_pop_head (&async_context->result_queue);
3231
3232 g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
3233
3234 g_return_val_if_fail (old_component_queue != NULL, FALSE);
3235 g_return_val_if_fail (new_component_queue != NULL, FALSE);
3236
3237 length = MIN (
3238 g_queue_get_length (old_component_queue),
3239 g_queue_get_length (new_component_queue));
3240
3241 for (ii = 0; ii < length; ii++) {
3242 ECalComponent *old_component;
3243 ECalComponent *new_component;
3244
3245 old_component = g_queue_pop_head (old_component_queue);
3246 new_component = g_queue_pop_head (new_component_queue);
3247
3248 e_cal_backend_notify_component_modified (
3249 backend, old_component, new_component);
3250
3251 g_clear_object (&old_component);
3252 g_clear_object (&new_component);
3253 }
3254
3255 g_warn_if_fail (g_queue_is_empty (old_component_queue));
3256 g_queue_free (old_component_queue);
3257
3258 g_warn_if_fail (g_queue_is_empty (new_component_queue));
3259 g_queue_free (new_component_queue);
3260
3261 return TRUE;
3262 }
3263
3264 /**
3265 * e_cal_backend_remove_objects_sync:
3266 * @backend: an #ECalBackend
3267 * @component_ids: (element-type ECalComponentId): a #GList of #ECalComponentId structs
3268 * @mod: modification type for recurrences
3269 * @opflags: bit-or of #ECalOperationFlags
3270 * @cancellable: optional #GCancellable object, or %NULL
3271 * @error: return location for a #GError, or %NULL
3272 *
3273 * Removes one or more iCalendar objects according to @component_ids and @mod.
3274 *
3275 * If an error occurs, the function will set @error and return %FALSE.
3276 *
3277 * Returns: %TRUE on success, %FALSE on failure
3278 *
3279 * Since: 3.10
3280 **/
3281 gboolean
e_cal_backend_remove_objects_sync(ECalBackend * backend,GList * component_ids,ECalObjModType mod,ECalOperationFlags opflags,GCancellable * cancellable,GError ** error)3282 e_cal_backend_remove_objects_sync (ECalBackend *backend,
3283 GList *component_ids,
3284 ECalObjModType mod,
3285 ECalOperationFlags opflags,
3286 GCancellable *cancellable,
3287 GError **error)
3288 {
3289 EAsyncClosure *closure;
3290 GAsyncResult *result;
3291 gboolean success;
3292
3293 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
3294 g_return_val_if_fail (component_ids != NULL, FALSE);
3295
3296 closure = e_async_closure_new ();
3297
3298 e_cal_backend_remove_objects (
3299 backend, component_ids, mod, opflags, cancellable,
3300 e_async_closure_callback, closure);
3301
3302 result = e_async_closure_wait (closure);
3303
3304 success = e_cal_backend_remove_objects_finish (
3305 backend, result, error);
3306
3307 e_async_closure_free (closure);
3308
3309 return success;
3310 }
3311
3312 /* Helper for e_cal_backend_remove_objects() */
3313 static void
cal_backend_remove_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3314 cal_backend_remove_objects_thread (GSimpleAsyncResult *simple,
3315 GObject *source_object,
3316 GCancellable *cancellable)
3317 {
3318 ECalBackend *backend;
3319 ECalBackendClass *class;
3320 EDataCal *data_cal;
3321 AsyncContext *async_context;
3322
3323 backend = E_CAL_BACKEND (source_object);
3324
3325 class = E_CAL_BACKEND_GET_CLASS (backend);
3326 g_return_if_fail (class != NULL);
3327 g_return_if_fail (class->impl_remove_objects != NULL);
3328
3329 data_cal = e_cal_backend_ref_data_cal (backend);
3330 g_return_if_fail (data_cal != NULL);
3331
3332 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3333
3334 if (!e_cal_backend_is_opened (backend)) {
3335 g_simple_async_result_set_error (
3336 simple, E_CLIENT_ERROR,
3337 E_CLIENT_ERROR_NOT_OPENED,
3338 "%s", e_client_error_to_string (
3339 E_CLIENT_ERROR_NOT_OPENED));
3340 g_simple_async_result_complete_in_idle (simple);
3341
3342 } else {
3343 guint32 opid;
3344
3345 opid = cal_backend_stash_operation (backend, simple);
3346
3347 class->impl_remove_objects (
3348 backend, data_cal, opid, cancellable,
3349 async_context->compid_list,
3350 async_context->mod,
3351 async_context->opflags);
3352 }
3353
3354 g_object_unref (data_cal);
3355 }
3356
3357 /**
3358 * e_cal_backend_remove_objects:
3359 * @backend: an #ECalBackend
3360 * @component_ids: (element-type ECalComponentId): a #GList of #ECalComponentId structs
3361 * @mod: modification type for recurrences
3362 * @opflags: bit-or of #ECalOperationFlags
3363 * @cancellable: optional #GCancellable object, or %NULL
3364 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3365 * @user_data: data to pass to the callback function
3366 *
3367 * Asynchronously removes one or more iCalendar objects according to
3368 * @component_ids and @mod.
3369 *
3370 * When the operation is finished, @callback will be called. You can then
3371 * call e_cal_backend_remove_objects_finish() to get the result of the
3372 * operation.
3373 *
3374 * Since: 3.10
3375 **/
3376 void
e_cal_backend_remove_objects(ECalBackend * backend,GList * component_ids,ECalObjModType mod,ECalOperationFlags opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3377 e_cal_backend_remove_objects (ECalBackend *backend,
3378 GList *component_ids,
3379 ECalObjModType mod,
3380 ECalOperationFlags opflags,
3381 GCancellable *cancellable,
3382 GAsyncReadyCallback callback,
3383 gpointer user_data)
3384 {
3385 GSimpleAsyncResult *simple;
3386 AsyncContext *async_context;
3387 GSList *list = NULL;
3388
3389 g_return_if_fail (E_IS_CAL_BACKEND (backend));
3390 g_return_if_fail (component_ids != NULL);
3391
3392 while (component_ids != NULL) {
3393 ECalComponentId *id = component_ids->data;
3394 list = g_slist_prepend (list, e_cal_component_id_copy (id));
3395 component_ids = g_list_next (component_ids);
3396 }
3397
3398 async_context = g_slice_new0 (AsyncContext);
3399 async_context->compid_list = g_slist_reverse (list);
3400 async_context->mod = mod;
3401 async_context->opflags = opflags;
3402
3403 simple = g_simple_async_result_new (
3404 G_OBJECT (backend), callback, user_data,
3405 e_cal_backend_remove_objects);
3406
3407 g_simple_async_result_set_check_cancellable (simple, cancellable);
3408
3409 g_simple_async_result_set_op_res_gpointer (
3410 simple, async_context, (GDestroyNotify) async_context_free);
3411
3412 cal_backend_push_operation (
3413 backend, simple, cancellable, FALSE,
3414 cal_backend_remove_objects_thread);
3415
3416 cal_backend_dispatch_next_operation (backend);
3417
3418 g_object_unref (simple);
3419 }
3420
3421 /**
3422 * e_cal_backend_remove_objects_finish:
3423 * @backend: an #ECalBackend
3424 * @result: a #GAsyncResult
3425 * @error: return location for a #GError, or %NULL
3426 *
3427 * Finishes the operation started with e_cal_backend_remove_objects().
3428 *
3429 * If an error occurred, the function will set @error and return %FALSE.
3430 *
3431 * Returns: %TRUE on success, %FALSE on failure
3432 *
3433 * Since: 3.10
3434 **/
3435 gboolean
e_cal_backend_remove_objects_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)3436 e_cal_backend_remove_objects_finish (ECalBackend *backend,
3437 GAsyncResult *result,
3438 GError **error)
3439 {
3440 GSimpleAsyncResult *simple;
3441 AsyncContext *async_context;
3442 GQueue *component_id_queue;
3443 GQueue *old_component_queue;
3444 GQueue *new_component_queue;
3445 guint length, ii;
3446
3447 g_return_val_if_fail (
3448 g_simple_async_result_is_valid (
3449 result, G_OBJECT (backend),
3450 e_cal_backend_remove_objects), FALSE);
3451
3452 simple = G_SIMPLE_ASYNC_RESULT (result);
3453 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3454
3455 cal_backend_unblock_operations (backend, simple);
3456
3457 if (g_simple_async_result_propagate_error (simple, error))
3458 return FALSE;
3459
3460 /* XXX Packing GQueues in a GQueue is pretty awkward, but until
3461 * the backend methods can be updated I'm trying to make do
3462 * with a single data container for all kinds of results. */
3463
3464 component_id_queue = g_queue_pop_head (&async_context->result_queue);
3465 old_component_queue = g_queue_pop_head (&async_context->result_queue);
3466 new_component_queue = g_queue_pop_head (&async_context->result_queue);
3467
3468 g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
3469
3470 g_return_val_if_fail (component_id_queue != NULL, FALSE);
3471 g_return_val_if_fail (old_component_queue != NULL, FALSE);
3472 /* new_component_queue may be NULL */
3473
3474 length = MIN (
3475 g_queue_get_length (component_id_queue),
3476 g_queue_get_length (old_component_queue));
3477
3478 for (ii = 0; ii < length; ii++) {
3479 ECalComponentId *component_id;
3480 ECalComponent *old_component;
3481 ECalComponent *new_component = NULL;
3482
3483 component_id = g_queue_pop_head (component_id_queue);
3484 old_component = g_queue_pop_head (old_component_queue);
3485 if (new_component_queue != NULL)
3486 new_component = g_queue_pop_head (new_component_queue);
3487
3488 e_cal_backend_notify_component_removed (
3489 backend, component_id, old_component, new_component);
3490
3491 e_cal_component_id_free (component_id);
3492 g_clear_object (&old_component);
3493 g_clear_object (&new_component);
3494 }
3495
3496 g_warn_if_fail (g_queue_is_empty (component_id_queue));
3497 g_queue_free (component_id_queue);
3498
3499 g_warn_if_fail (g_queue_is_empty (old_component_queue));
3500 g_queue_free (old_component_queue);
3501
3502 if (new_component_queue != NULL) {
3503 g_warn_if_fail (g_queue_is_empty (new_component_queue));
3504 g_queue_free (new_component_queue);
3505 }
3506
3507 return TRUE;
3508 }
3509
3510 /**
3511 * e_cal_backend_receive_objects_sync:
3512 * @backend: an #ECalBackend
3513 * @calobj: an iCalendar string
3514 * @opflags: bit-or of #ECalOperationFlags
3515 * @cancellable: optional #GCancellable object, or %NULL
3516 * @error: return location for a #GError, or %NULL
3517 *
3518 * Receives the set of iCalendar objects specified by @calobj. This is used
3519 * for iTIP confirmation and cancellation messages for scheduled meetings.
3520 *
3521 * If an error occurs, the function will set @error and return %FALSE.
3522 *
3523 * Returns: %TRUE on success, %FALSE on failure
3524 *
3525 * Since: 3.10
3526 **/
3527 gboolean
e_cal_backend_receive_objects_sync(ECalBackend * backend,const gchar * calobj,ECalOperationFlags opflags,GCancellable * cancellable,GError ** error)3528 e_cal_backend_receive_objects_sync (ECalBackend *backend,
3529 const gchar *calobj,
3530 ECalOperationFlags opflags,
3531 GCancellable *cancellable,
3532 GError **error)
3533 {
3534 EAsyncClosure *closure;
3535 GAsyncResult *result;
3536 gboolean success;
3537
3538 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
3539 g_return_val_if_fail (calobj != NULL, FALSE);
3540
3541 closure = e_async_closure_new ();
3542
3543 e_cal_backend_receive_objects (
3544 backend, calobj, opflags, cancellable,
3545 e_async_closure_callback, closure);
3546
3547 result = e_async_closure_wait (closure);
3548
3549 success = e_cal_backend_receive_objects_finish (
3550 backend, result, error);
3551
3552 e_async_closure_free (closure);
3553
3554 return success;
3555 }
3556
3557 /* Helper for e_cal_backend_receive_objects() */
3558 static void
cal_backend_receive_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3559 cal_backend_receive_objects_thread (GSimpleAsyncResult *simple,
3560 GObject *source_object,
3561 GCancellable *cancellable)
3562 {
3563 ECalBackend *backend;
3564 ECalBackendClass *class;
3565 EDataCal *data_cal;
3566 AsyncContext *async_context;
3567
3568 backend = E_CAL_BACKEND (source_object);
3569
3570 class = E_CAL_BACKEND_GET_CLASS (backend);
3571 g_return_if_fail (class != NULL);
3572 g_return_if_fail (class->impl_receive_objects != NULL);
3573
3574 data_cal = e_cal_backend_ref_data_cal (backend);
3575 g_return_if_fail (data_cal != NULL);
3576
3577 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3578
3579 if (!e_cal_backend_is_opened (backend)) {
3580 g_simple_async_result_set_error (
3581 simple, E_CLIENT_ERROR,
3582 E_CLIENT_ERROR_NOT_OPENED,
3583 "%s", e_client_error_to_string (
3584 E_CLIENT_ERROR_NOT_OPENED));
3585 g_simple_async_result_complete_in_idle (simple);
3586
3587 } else {
3588 guint32 opid;
3589
3590 opid = cal_backend_stash_operation (backend, simple);
3591
3592 class->impl_receive_objects (
3593 backend, data_cal, opid, cancellable,
3594 async_context->calobj,
3595 async_context->opflags);
3596 }
3597
3598 g_object_unref (data_cal);
3599 }
3600
3601 /**
3602 * e_cal_backend_receive_objects:
3603 * @backend: an #ECalBackend
3604 * @calobj: an iCalendar string
3605 * @opflags: bit-or of #ECalOperationFlags
3606 * @cancellable: optional #GCancellable object, or %NULL
3607 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3608 * @user_data: data to pass to the callback function
3609 *
3610 * Asynchronously receives the set of iCalendar objects specified by
3611 * @calobj. This is used for iTIP confirmation and cancellation messages
3612 * for scheduled meetings.
3613 *
3614 * When the operation is finished, @callback will be called. You can then
3615 * call e_cal_backend_receive_objects_finish() to get the result of the
3616 * operation.
3617 *
3618 * Since: 3.10
3619 **/
3620 void
e_cal_backend_receive_objects(ECalBackend * backend,const gchar * calobj,ECalOperationFlags opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3621 e_cal_backend_receive_objects (ECalBackend *backend,
3622 const gchar *calobj,
3623 ECalOperationFlags opflags,
3624 GCancellable *cancellable,
3625 GAsyncReadyCallback callback,
3626 gpointer user_data)
3627 {
3628 GSimpleAsyncResult *simple;
3629 AsyncContext *async_context;
3630
3631 g_return_if_fail (E_IS_CAL_BACKEND (backend));
3632 g_return_if_fail (calobj != NULL);
3633
3634 async_context = g_slice_new0 (AsyncContext);
3635 async_context->calobj = g_strdup (calobj);
3636 async_context->opflags = opflags;
3637
3638 simple = g_simple_async_result_new (
3639 G_OBJECT (backend), callback, user_data,
3640 e_cal_backend_receive_objects);
3641
3642 g_simple_async_result_set_check_cancellable (simple, cancellable);
3643
3644 g_simple_async_result_set_op_res_gpointer (
3645 simple, async_context, (GDestroyNotify) async_context_free);
3646
3647 cal_backend_push_operation (
3648 backend, simple, cancellable, FALSE,
3649 cal_backend_receive_objects_thread);
3650
3651 cal_backend_dispatch_next_operation (backend);
3652
3653 g_object_unref (simple);
3654 }
3655
3656 /**
3657 * e_cal_backend_receive_objects_finish:
3658 * @backend: an #ECalBackend
3659 * @result: a #GAsyncResult
3660 * @error: return location for a #GError, or %NULL
3661 *
3662 * Finishes the operation started with e_cal_backend_receive_objects().
3663 *
3664 * If an error occurred, the function will set @error and erturn %FALSE.
3665 *
3666 * Returns: %TRUE on success, %FALSE on failure
3667 *
3668 * Since: 3.10
3669 **/
3670 gboolean
e_cal_backend_receive_objects_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)3671 e_cal_backend_receive_objects_finish (ECalBackend *backend,
3672 GAsyncResult *result,
3673 GError **error)
3674 {
3675 GSimpleAsyncResult *simple;
3676
3677 g_return_val_if_fail (
3678 g_simple_async_result_is_valid (
3679 result, G_OBJECT (backend),
3680 e_cal_backend_receive_objects), FALSE);
3681
3682 simple = G_SIMPLE_ASYNC_RESULT (result);
3683
3684 cal_backend_unblock_operations (backend, simple);
3685
3686 /* Assume success unless a GError is set. */
3687 return !g_simple_async_result_propagate_error (simple, error);
3688 }
3689
3690 /**
3691 * e_cal_backend_send_objects_sync:
3692 * @backend: an #ECalBackend
3693 * @calobj: an iCalendar string
3694 * @opflags: bit-or of #ECalOperationFlags
3695 * @out_users: a #GQueue in which to deposit results
3696 * @cancellable: optional #GCancellable object, or %NULL
3697 * @error: return location for a #GError, or %NULL
3698 *
3699 * Sends meeting information in @calobj. The @backend may modify @calobj
3700 * and send meeting information only to particular users. The function
3701 * returns the (maybe) modified @calobj and deposits the list of users the
3702 * meeting information was sent (to be send) to in @out_users.
3703 *
3704 * The returned pointer should be freed with g_free(), when no londer needed.
3705 *
3706 * If an error occurs, the function will set @error and return %NULL.
3707 *
3708 * Returns: a vCalendar string, or %NULL
3709 *
3710 * Since: 3.10
3711 **/
3712 gchar *
e_cal_backend_send_objects_sync(ECalBackend * backend,const gchar * calobj,ECalOperationFlags opflags,GQueue * out_users,GCancellable * cancellable,GError ** error)3713 e_cal_backend_send_objects_sync (ECalBackend *backend,
3714 const gchar *calobj,
3715 ECalOperationFlags opflags,
3716 GQueue *out_users,
3717 GCancellable *cancellable,
3718 GError **error)
3719 {
3720 EAsyncClosure *closure;
3721 GAsyncResult *result;
3722 gchar *out_calobj;
3723
3724 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
3725 g_return_val_if_fail (calobj != NULL, NULL);
3726
3727 closure = e_async_closure_new ();
3728
3729 e_cal_backend_send_objects (
3730 backend, calobj, opflags, cancellable,
3731 e_async_closure_callback, closure);
3732
3733 result = e_async_closure_wait (closure);
3734
3735 out_calobj = e_cal_backend_send_objects_finish (
3736 backend, result, out_users, error);
3737
3738 e_async_closure_free (closure);
3739
3740 return out_calobj;
3741 }
3742
3743 /* Helper for e_cal_backend_send_objects() */
3744 static void
cal_backend_send_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3745 cal_backend_send_objects_thread (GSimpleAsyncResult *simple,
3746 GObject *source_object,
3747 GCancellable *cancellable)
3748 {
3749 ECalBackend *backend;
3750 ECalBackendClass *class;
3751 EDataCal *data_cal;
3752 AsyncContext *async_context;
3753
3754 backend = E_CAL_BACKEND (source_object);
3755
3756 class = E_CAL_BACKEND_GET_CLASS (backend);
3757 g_return_if_fail (class != NULL);
3758 g_return_if_fail (class->impl_send_objects != NULL);
3759
3760 data_cal = e_cal_backend_ref_data_cal (backend);
3761 g_return_if_fail (data_cal != NULL);
3762
3763 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3764
3765 if (!e_cal_backend_is_opened (backend)) {
3766 g_simple_async_result_set_error (
3767 simple, E_CLIENT_ERROR,
3768 E_CLIENT_ERROR_NOT_OPENED,
3769 "%s", e_client_error_to_string (
3770 E_CLIENT_ERROR_NOT_OPENED));
3771 g_simple_async_result_complete_in_idle (simple);
3772
3773 } else {
3774 guint32 opid;
3775
3776 opid = cal_backend_stash_operation (backend, simple);
3777
3778 class->impl_send_objects (
3779 backend, data_cal, opid, cancellable,
3780 async_context->calobj,
3781 async_context->opflags);
3782 }
3783
3784 g_object_unref (data_cal);
3785 }
3786
3787 /**
3788 * e_cal_backend_send_objects:
3789 * @backend: an #ECalBackend
3790 * @calobj: an iCalendar string
3791 * @opflags: bit-or of #ECalOperationFlags
3792 * @cancellable: optional #GCancellable object, or %NULL
3793 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3794 * @user_data: data to pass to the callback function
3795 *
3796 * Asynchronously sends meeting information in @calobj. The @backend may
3797 * modify @calobj and send meeting information only to particular users.
3798 *
3799 * When the operation is finished, @callback will be called. You can then
3800 * call e_cal_backend_send_objects_finish() to get the result of the operation.
3801 *
3802 * Since: 3.10
3803 **/
3804 void
e_cal_backend_send_objects(ECalBackend * backend,const gchar * calobj,ECalOperationFlags opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3805 e_cal_backend_send_objects (ECalBackend *backend,
3806 const gchar *calobj,
3807 ECalOperationFlags opflags,
3808 GCancellable *cancellable,
3809 GAsyncReadyCallback callback,
3810 gpointer user_data)
3811 {
3812 GSimpleAsyncResult *simple;
3813 AsyncContext *async_context;
3814
3815 g_return_if_fail (E_IS_CAL_BACKEND (backend));
3816 g_return_if_fail (calobj != NULL);
3817
3818 async_context = g_slice_new0 (AsyncContext);
3819 async_context->calobj = g_strdup (calobj);
3820 async_context->opflags = opflags;
3821
3822 simple = g_simple_async_result_new (
3823 G_OBJECT (backend), callback, user_data,
3824 e_cal_backend_send_objects);
3825
3826 g_simple_async_result_set_check_cancellable (simple, cancellable);
3827
3828 g_simple_async_result_set_op_res_gpointer (
3829 simple, async_context, (GDestroyNotify) async_context_free);
3830
3831 cal_backend_push_operation (
3832 backend, simple, cancellable, FALSE,
3833 cal_backend_send_objects_thread);
3834
3835 cal_backend_dispatch_next_operation (backend);
3836
3837 g_object_unref (simple);
3838 }
3839
3840 /**
3841 * e_cal_backend_send_objects_finish:
3842 * @backend: an #ECalBackend
3843 * @result: a #GAsyncResult
3844 * @out_users: a #GQueue in which to deposit results
3845 * @error: return location for a #GError, or %NULL
3846 *
3847 * Finishes the operation started with e_cal_backend_send_objects().
3848 *
3849 * The function returns a string representation of a sent, or to be send,
3850 * vCalendar and deposits the list of users the meeting information was sent
3851 * to, or to be send to, in @out_users.
3852 *
3853 * Free the returned pointer with g_free(), when no longer needed.
3854 *
3855 * If an error occurs, the function will set @error and return %NULL.
3856 *
3857 * Returns: a newly allocated vCalendar string, or %NULL
3858 *
3859 * Since: 3.10
3860 **/
3861 gchar *
e_cal_backend_send_objects_finish(ECalBackend * backend,GAsyncResult * result,GQueue * out_users,GError ** error)3862 e_cal_backend_send_objects_finish (ECalBackend *backend,
3863 GAsyncResult *result,
3864 GQueue *out_users,
3865 GError **error)
3866 {
3867 GSimpleAsyncResult *simple;
3868 AsyncContext *async_context;
3869 gchar *calobj;
3870
3871 g_return_val_if_fail (
3872 g_simple_async_result_is_valid (
3873 result, G_OBJECT (backend),
3874 e_cal_backend_send_objects), NULL);
3875 g_return_val_if_fail (out_users != NULL, NULL);
3876
3877 simple = G_SIMPLE_ASYNC_RESULT (result);
3878 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3879
3880 cal_backend_unblock_operations (backend, simple);
3881
3882 if (g_simple_async_result_propagate_error (simple, error))
3883 return NULL;
3884
3885 calobj = g_queue_pop_head (&async_context->result_queue);
3886
3887 e_queue_transfer (&async_context->result_queue, out_users);
3888
3889 return calobj;
3890 }
3891
3892 /**
3893 * e_cal_backend_get_attachment_uris_sync:
3894 * @backend: an #ECalBackend
3895 * @uid: a unique ID for an iCalendar object
3896 * @rid: a recurrence ID, or %NULL
3897 * @out_attachment_uris: a #GQueue in which to deposit results
3898 * @cancellable: optional #GCancellable object, or %NULL
3899 * @error: return location for a #GError, or %NULL
3900 *
3901 * Inspects the iCalendar object specified by @uid and, optionally, @rid
3902 * for attachments and deposits a URI string for each attachment in
3903 * @out_attachment_uris. Free the returned strings with g_free() when
3904 * finished with them.
3905 *
3906 * If an error occurs, the function will set @error and return %FALSE.
3907 * Note that an empty result set does not necessarily imply an error.
3908 *
3909 * Returns: %TRUE on success, %FALSE on failure
3910 *
3911 * Since: 3.10
3912 **/
3913 gboolean
e_cal_backend_get_attachment_uris_sync(ECalBackend * backend,const gchar * uid,const gchar * rid,GQueue * out_attachment_uris,GCancellable * cancellable,GError ** error)3914 e_cal_backend_get_attachment_uris_sync (ECalBackend *backend,
3915 const gchar *uid,
3916 const gchar *rid,
3917 GQueue *out_attachment_uris,
3918 GCancellable *cancellable,
3919 GError **error)
3920 {
3921 EAsyncClosure *closure;
3922 GAsyncResult *result;
3923 gboolean success;
3924
3925 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
3926 g_return_val_if_fail (uid != NULL, FALSE);
3927 /* rid can be NULL */
3928
3929 closure = e_async_closure_new ();
3930
3931 e_cal_backend_get_attachment_uris (
3932 backend, uid, rid, cancellable,
3933 e_async_closure_callback, closure);
3934
3935 result = e_async_closure_wait (closure);
3936
3937 success = e_cal_backend_get_attachment_uris_finish (
3938 backend, result, out_attachment_uris, error);
3939
3940 e_async_closure_free (closure);
3941
3942 return success;
3943 }
3944
3945 /* Helper for e_cal_backend_get_attachment_uris() */
3946 static void
cal_backend_get_attachment_uris_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3947 cal_backend_get_attachment_uris_thread (GSimpleAsyncResult *simple,
3948 GObject *source_object,
3949 GCancellable *cancellable)
3950 {
3951 ECalBackend *backend;
3952 ECalBackendClass *class;
3953 EDataCal *data_cal;
3954 AsyncContext *async_context;
3955
3956 backend = E_CAL_BACKEND (source_object);
3957
3958 class = E_CAL_BACKEND_GET_CLASS (backend);
3959 g_return_if_fail (class != NULL);
3960 g_return_if_fail (class->impl_get_attachment_uris != NULL);
3961
3962 data_cal = e_cal_backend_ref_data_cal (backend);
3963 g_return_if_fail (data_cal != NULL);
3964
3965 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3966
3967 if (!e_cal_backend_is_opened (backend)) {
3968 g_simple_async_result_set_error (
3969 simple, E_CLIENT_ERROR,
3970 E_CLIENT_ERROR_NOT_OPENED,
3971 "%s", e_client_error_to_string (
3972 E_CLIENT_ERROR_NOT_OPENED));
3973 g_simple_async_result_complete_in_idle (simple);
3974
3975 } else {
3976 guint32 opid;
3977
3978 opid = cal_backend_stash_operation (backend, simple);
3979
3980 class->impl_get_attachment_uris (
3981 backend, data_cal, opid, cancellable,
3982 async_context->uid,
3983 async_context->rid);
3984 }
3985
3986 g_object_unref (data_cal);
3987 }
3988
3989 /**
3990 * e_cal_backend_get_attachment_uris:
3991 * @backend: an #ECalBackend
3992 * @uid: a unique ID for an iCalendar object
3993 * @rid: a recurrence ID, or %NULL
3994 * @cancellable: optional #GCancellable object, or %NULL
3995 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
3996 * @user_data: data to pass to the callback function
3997 *
3998 * Asynchronously inspects the iCalendar object specified by @uid and,
3999 * optionally, @rid for attachments.
4000 *
4001 * When the operation is finished, @callback will be called. You can then
4002 * call e_cal_backend_get_attachment_uris_finish() to get the result of the
4003 * operation.
4004 *
4005 * Since: 3.10
4006 **/
4007 void
e_cal_backend_get_attachment_uris(ECalBackend * backend,const gchar * uid,const gchar * rid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4008 e_cal_backend_get_attachment_uris (ECalBackend *backend,
4009 const gchar *uid,
4010 const gchar *rid,
4011 GCancellable *cancellable,
4012 GAsyncReadyCallback callback,
4013 gpointer user_data)
4014 {
4015 GSimpleAsyncResult *simple;
4016 AsyncContext *async_context;
4017
4018 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4019 g_return_if_fail (uid != NULL);
4020 /* rid is optional */
4021
4022 async_context = g_slice_new0 (AsyncContext);
4023 async_context->uid = g_strdup (uid);
4024 async_context->rid = g_strdup (rid);
4025
4026 simple = g_simple_async_result_new (
4027 G_OBJECT (backend), callback, user_data,
4028 e_cal_backend_get_attachment_uris);
4029
4030 g_simple_async_result_set_check_cancellable (simple, cancellable);
4031
4032 g_simple_async_result_set_op_res_gpointer (
4033 simple, async_context, (GDestroyNotify) async_context_free);
4034
4035 cal_backend_push_operation (
4036 backend, simple, cancellable, FALSE,
4037 cal_backend_get_attachment_uris_thread);
4038
4039 cal_backend_dispatch_next_operation (backend);
4040
4041 g_object_unref (simple);
4042 }
4043
4044 /**
4045 * e_cal_backend_get_attachment_uris_finish:
4046 * @backend: an #ECalBackend
4047 * @result: a #GAsyncResult
4048 * @out_attachment_uris: a #GQueue in which to deposit results
4049 * @error: return location for a #GError, or %NULL
4050 *
4051 * Finishes the operation started with e_cal_backend_get_attachment_uris().
4052 *
4053 * The requested attachment URI strings are deposited in @out_attachment_uris.
4054 * Free the returned strings with g_free() when finished with them.
4055 *
4056 * If an error occurred, the function will set @error and return %FALSE.
4057 * Note that an empty result set does not necessarily imply an error.
4058 *
4059 * Returns: %TRUE on success, %FALSE on failure
4060 *
4061 * Since: 3.10
4062 **/
4063 gboolean
e_cal_backend_get_attachment_uris_finish(ECalBackend * backend,GAsyncResult * result,GQueue * out_attachment_uris,GError ** error)4064 e_cal_backend_get_attachment_uris_finish (ECalBackend *backend,
4065 GAsyncResult *result,
4066 GQueue *out_attachment_uris,
4067 GError **error)
4068 {
4069 GSimpleAsyncResult *simple;
4070 AsyncContext *async_context;
4071
4072 g_return_val_if_fail (
4073 g_simple_async_result_is_valid (
4074 result, G_OBJECT (backend),
4075 e_cal_backend_get_attachment_uris), FALSE);
4076 g_return_val_if_fail (out_attachment_uris != NULL, FALSE);
4077
4078 simple = G_SIMPLE_ASYNC_RESULT (result);
4079 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4080
4081 cal_backend_unblock_operations (backend, simple);
4082
4083 if (g_simple_async_result_propagate_error (simple, error))
4084 return FALSE;
4085
4086 e_queue_transfer (&async_context->result_queue, out_attachment_uris);
4087
4088 return TRUE;
4089 }
4090
4091 /**
4092 * e_cal_backend_discard_alarm_sync:
4093 * @backend: an #ECalBackend
4094 * @uid: a unique ID for an iCalendar object
4095 * @rid: a recurrence ID, or %NULL
4096 * @alarm_uid: a unique ID for an iCalendar VALARM object
4097 * @opflags: bit-or of #ECalOperationFlags
4098 * @cancellable: optional #GCancellable object, or %NULL
4099 * @error: return location for a #GError, or %NULL
4100 *
4101 * Discards the VALARM object with a unique ID of @alarm_uid from the
4102 * iCalendar object identified by @uid and, optionally, @rid.
4103 *
4104 * If an error occurs, the function will set @error and return %FALSE.
4105 *
4106 * Returns: %TRUE on success, %FALSE on failure
4107 *
4108 * Since: 3.10
4109 **/
4110 gboolean
e_cal_backend_discard_alarm_sync(ECalBackend * backend,const gchar * uid,const gchar * rid,const gchar * alarm_uid,ECalOperationFlags opflags,GCancellable * cancellable,GError ** error)4111 e_cal_backend_discard_alarm_sync (ECalBackend *backend,
4112 const gchar *uid,
4113 const gchar *rid,
4114 const gchar *alarm_uid,
4115 ECalOperationFlags opflags,
4116 GCancellable *cancellable,
4117 GError **error)
4118 {
4119 EAsyncClosure *closure;
4120 GAsyncResult *result;
4121 gboolean success;
4122
4123 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
4124 g_return_val_if_fail (uid != NULL, FALSE);
4125 /* rid can be NULL */
4126 g_return_val_if_fail (alarm_uid != NULL, FALSE);
4127
4128 closure = e_async_closure_new ();
4129
4130 e_cal_backend_discard_alarm (
4131 backend, uid, rid, alarm_uid, opflags, cancellable,
4132 e_async_closure_callback, closure);
4133
4134 result = e_async_closure_wait (closure);
4135
4136 success = e_cal_backend_discard_alarm_finish (
4137 backend, result, error);
4138
4139 e_async_closure_free (closure);
4140
4141 return success;
4142 }
4143
4144 /* Helper for e_cal_backend_discard_alarm() */
4145 static void
cal_backend_discard_alarm_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4146 cal_backend_discard_alarm_thread (GSimpleAsyncResult *simple,
4147 GObject *source_object,
4148 GCancellable *cancellable)
4149 {
4150 ECalBackend *backend;
4151 ECalBackendClass *class;
4152 EDataCal *data_cal;
4153 AsyncContext *async_context;
4154
4155 backend = E_CAL_BACKEND (source_object);
4156
4157 class = E_CAL_BACKEND_GET_CLASS (backend);
4158 g_return_if_fail (class != NULL);
4159
4160 data_cal = e_cal_backend_ref_data_cal (backend);
4161 g_return_if_fail (data_cal != NULL);
4162
4163 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4164
4165 if (class->impl_discard_alarm == NULL) {
4166 g_simple_async_result_set_error (
4167 simple, E_CLIENT_ERROR,
4168 E_CLIENT_ERROR_NOT_SUPPORTED,
4169 "%s", e_client_error_to_string (
4170 E_CLIENT_ERROR_NOT_SUPPORTED));
4171 g_simple_async_result_complete_in_idle (simple);
4172
4173 } else if (!e_cal_backend_is_opened (backend)) {
4174 g_simple_async_result_set_error (
4175 simple, E_CLIENT_ERROR,
4176 E_CLIENT_ERROR_NOT_OPENED,
4177 "%s", e_client_error_to_string (
4178 E_CLIENT_ERROR_NOT_OPENED));
4179 g_simple_async_result_complete_in_idle (simple);
4180
4181 } else {
4182 guint32 opid;
4183
4184 opid = cal_backend_stash_operation (backend, simple);
4185
4186 class->impl_discard_alarm (
4187 backend, data_cal, opid, cancellable,
4188 async_context->uid,
4189 async_context->rid,
4190 async_context->alarm_uid,
4191 async_context->opflags);
4192 }
4193
4194 g_object_unref (data_cal);
4195 }
4196
4197 /**
4198 * e_cal_backend_discard_alarm:
4199 * @backend: an #ECalBackend
4200 * @uid: a unique ID for an iCalendar object
4201 * @rid: a recurrence ID, or %NULL
4202 * @alarm_uid: a unique ID for an iCalendar VALARM object
4203 * @opflags: bit-or of #ECalOperationFlags
4204 * @cancellable: optional #GCancellable object, or %NULL
4205 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
4206 * @user_data: data to pass to the callback function
4207 *
4208 * Asynchronously discards the VALARM object with a unique ID of @alarm_uid
4209 * from the iCalendar object identified by @uid and, optionally, @rid.
4210 *
4211 * When the operation is finished, @callback will be called. You can
4212 * then call e_cal_backend_discard_alarm_finish() to get the result of
4213 * the operation.
4214 *
4215 * Since: 3.10
4216 **/
4217 void
e_cal_backend_discard_alarm(ECalBackend * backend,const gchar * uid,const gchar * rid,const gchar * alarm_uid,ECalOperationFlags opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4218 e_cal_backend_discard_alarm (ECalBackend *backend,
4219 const gchar *uid,
4220 const gchar *rid,
4221 const gchar *alarm_uid,
4222 ECalOperationFlags opflags,
4223 GCancellable *cancellable,
4224 GAsyncReadyCallback callback,
4225 gpointer user_data)
4226 {
4227 GSimpleAsyncResult *simple;
4228 AsyncContext *async_context;
4229
4230 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4231 g_return_if_fail (uid != NULL);
4232 /* rid can be NULL */
4233 g_return_if_fail (alarm_uid != NULL);
4234
4235 async_context = g_slice_new0 (AsyncContext);
4236 async_context->uid = g_strdup (uid);
4237 async_context->rid = g_strdup (rid);
4238 async_context->alarm_uid = g_strdup (alarm_uid);
4239 async_context->opflags = opflags;
4240
4241 simple = g_simple_async_result_new (
4242 G_OBJECT (backend), callback, user_data,
4243 e_cal_backend_discard_alarm);
4244
4245 g_simple_async_result_set_check_cancellable (simple, cancellable);
4246
4247 g_simple_async_result_set_op_res_gpointer (
4248 simple, async_context, (GDestroyNotify) async_context_free);
4249
4250 cal_backend_push_operation (
4251 backend, simple, cancellable, FALSE,
4252 cal_backend_discard_alarm_thread);
4253
4254 cal_backend_dispatch_next_operation (backend);
4255
4256 g_object_unref (simple);
4257 }
4258
4259 /**
4260 * e_cal_backend_discard_alarm_finish:
4261 * @backend: an #ECalBackend
4262 * @result: a #GAsyncResult
4263 * @error: return location for a #GError, or %NULL
4264 *
4265 * Finishes the operation started with e_cal_backend_discard_alarm().
4266 *
4267 * If an error occurred, the function will set @error and return %FALSE.
4268 *
4269 * Returns: %TRUE on success, %FALSE on failure
4270 *
4271 * Since: 3.10
4272 **/
4273 gboolean
e_cal_backend_discard_alarm_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)4274 e_cal_backend_discard_alarm_finish (ECalBackend *backend,
4275 GAsyncResult *result,
4276 GError **error)
4277 {
4278 GSimpleAsyncResult *simple;
4279
4280 g_return_val_if_fail (
4281 g_simple_async_result_is_valid (
4282 result, G_OBJECT (backend),
4283 e_cal_backend_discard_alarm), FALSE);
4284
4285 simple = G_SIMPLE_ASYNC_RESULT (result);
4286
4287 cal_backend_unblock_operations (backend, simple);
4288
4289 /* Assume success unless a GError is set. */
4290 return !g_simple_async_result_propagate_error (simple, error);
4291 }
4292
4293 /**
4294 * e_cal_backend_get_timezone_sync:
4295 * @backend: an #ECalBackend
4296 * @tzid: a unique ID for an iCalendar VTIMEZONE object
4297 * @cancellable: optional #GCancellable object, or %NULL
4298 * @error: return location for a #GError, or %NULL
4299 *
4300 * Obtains the VTIMEZONE object identified by @tzid. Free the returned
4301 * string with g_free() when finished with it.
4302 *
4303 * If an error occurs, the function will set @error and return %NULL.
4304 *
4305 * Returns: an iCalendar string, or %NULL
4306 *
4307 * Since: 3.10
4308 **/
4309 gchar *
e_cal_backend_get_timezone_sync(ECalBackend * backend,const gchar * tzid,GCancellable * cancellable,GError ** error)4310 e_cal_backend_get_timezone_sync (ECalBackend *backend,
4311 const gchar *tzid,
4312 GCancellable *cancellable,
4313 GError **error)
4314 {
4315 EAsyncClosure *closure;
4316 GAsyncResult *result;
4317 gchar *tzobject;
4318
4319 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
4320 g_return_val_if_fail (tzid != NULL, NULL);
4321
4322 closure = e_async_closure_new ();
4323
4324 e_cal_backend_get_timezone (
4325 backend, tzid, cancellable,
4326 e_async_closure_callback, closure);
4327
4328 result = e_async_closure_wait (closure);
4329
4330 tzobject = e_cal_backend_get_timezone_finish (
4331 backend, result, error);
4332
4333 e_async_closure_free (closure);
4334
4335 return tzobject;
4336 }
4337
4338 /* Helper for e_cal_backend_get_timezone() */
4339 static void
cal_backend_get_timezone_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4340 cal_backend_get_timezone_thread (GSimpleAsyncResult *simple,
4341 GObject *source_object,
4342 GCancellable *cancellable)
4343 {
4344 ECalBackend *backend;
4345 ECalBackendClass *class;
4346 EDataCal *data_cal;
4347 AsyncContext *async_context;
4348
4349 backend = E_CAL_BACKEND (source_object);
4350
4351 class = E_CAL_BACKEND_GET_CLASS (backend);
4352 g_return_if_fail (class != NULL);
4353 g_return_if_fail (class->impl_get_timezone != NULL);
4354
4355 data_cal = e_cal_backend_ref_data_cal (backend);
4356 g_return_if_fail (data_cal != NULL);
4357
4358 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4359
4360 if (!e_cal_backend_is_opened (backend)) {
4361 g_simple_async_result_set_error (
4362 simple, E_CLIENT_ERROR,
4363 E_CLIENT_ERROR_NOT_OPENED,
4364 "%s", e_client_error_to_string (
4365 E_CLIENT_ERROR_NOT_OPENED));
4366 g_simple_async_result_complete_in_idle (simple);
4367
4368 } else {
4369 guint32 opid;
4370
4371 opid = cal_backend_stash_operation (backend, simple);
4372
4373 class->impl_get_timezone (
4374 backend, data_cal, opid, cancellable,
4375 async_context->tzid);
4376 }
4377
4378 g_object_unref (data_cal);
4379 }
4380
4381 /**
4382 * e_cal_backend_get_timezone:
4383 * @backend: an #ECalBackend
4384 * @tzid: a unique ID for an iCalendar VTIMEZONE object
4385 * @cancellable: optional #GCancellable object, or %NULL
4386 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
4387 * @user_data: data to pass to the callback function
4388 *
4389 * Asynchronously obtains the VTIMEZONE object identified by @tzid.
4390 *
4391 * When the operation is finished, @callback will be called. You can
4392 * then call e_cal_backend_get_timezone_finish() to get the result of
4393 * the operation.
4394 *
4395 * Since: 3.10
4396 **/
4397 void
e_cal_backend_get_timezone(ECalBackend * backend,const gchar * tzid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4398 e_cal_backend_get_timezone (ECalBackend *backend,
4399 const gchar *tzid,
4400 GCancellable *cancellable,
4401 GAsyncReadyCallback callback,
4402 gpointer user_data)
4403 {
4404 GSimpleAsyncResult *simple;
4405 AsyncContext *async_context;
4406
4407 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4408 g_return_if_fail (tzid != NULL);
4409
4410 async_context = g_slice_new0 (AsyncContext);
4411 async_context->tzid = g_strdup (tzid);
4412
4413 simple = g_simple_async_result_new (
4414 G_OBJECT (backend), callback, user_data,
4415 e_cal_backend_get_timezone);
4416
4417 g_simple_async_result_set_check_cancellable (simple, cancellable);
4418
4419 g_simple_async_result_set_op_res_gpointer (
4420 simple, async_context, (GDestroyNotify) async_context_free);
4421
4422 cal_backend_push_operation (
4423 backend, simple, cancellable, FALSE,
4424 cal_backend_get_timezone_thread);
4425
4426 cal_backend_dispatch_next_operation (backend);
4427
4428 g_object_unref (simple);
4429 }
4430
4431 /**
4432 * e_cal_backend_get_timezone_finish:
4433 * @backend: an #ECalBackend
4434 * @result: a #GAsyncResult
4435 * @error: return location for a #GError, or %NULL
4436 *
4437 * Finishes the operation started with e_cal_backend_get_timezone().
4438 *
4439 * Free the returned string with g_free() when finished with it.
4440 *
4441 * If an error occurred, the function will set @error and return %NULL.
4442 *
4443 * Returns: an iCalendar string, or %NULL
4444 *
4445 * Since: 3.10
4446 **/
4447 gchar *
e_cal_backend_get_timezone_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)4448 e_cal_backend_get_timezone_finish (ECalBackend *backend,
4449 GAsyncResult *result,
4450 GError **error)
4451 {
4452 GSimpleAsyncResult *simple;
4453 AsyncContext *async_context;
4454 gchar *tzobject;
4455
4456 g_return_val_if_fail (
4457 g_simple_async_result_is_valid (
4458 result, G_OBJECT (backend),
4459 e_cal_backend_get_timezone), NULL);
4460
4461 simple = G_SIMPLE_ASYNC_RESULT (result);
4462 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4463
4464 cal_backend_unblock_operations (backend, simple);
4465
4466 if (g_simple_async_result_propagate_error (simple, error))
4467 return NULL;
4468
4469 tzobject = g_queue_pop_head (&async_context->result_queue);
4470
4471 if (!tzobject)
4472 g_set_error_literal (error,
4473 E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND,
4474 e_cal_client_error_to_string (E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND));
4475
4476 g_warn_if_fail (g_queue_is_empty (&async_context->result_queue));
4477
4478 return tzobject;
4479 }
4480
4481 /**
4482 * e_cal_backend_add_timezone_sync:
4483 * @backend: an #ECalBackend
4484 * @tzobject: an iCalendar VTIMEZONE string
4485 * @cancellable: optional #GCancellable object, or %NULL
4486 * @error: return location for a #GError, or %NULL
4487 *
4488 * Adds the timezone described by @tzobject to @backend.
4489 *
4490 * If an error occurs, the function will set @error and return %FALSE.
4491 *
4492 * Returns: %TRUE on success, %FALSE on failure
4493 *
4494 * Since: 3.10
4495 **/
4496 gboolean
e_cal_backend_add_timezone_sync(ECalBackend * backend,const gchar * tzobject,GCancellable * cancellable,GError ** error)4497 e_cal_backend_add_timezone_sync (ECalBackend *backend,
4498 const gchar *tzobject,
4499 GCancellable *cancellable,
4500 GError **error)
4501 {
4502 EAsyncClosure *closure;
4503 GAsyncResult *result;
4504 gboolean success;
4505
4506 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
4507 g_return_val_if_fail (tzobject != NULL, FALSE);
4508
4509 closure = e_async_closure_new ();
4510
4511 e_cal_backend_add_timezone (
4512 backend, tzobject, cancellable,
4513 e_async_closure_callback, closure);
4514
4515 result = e_async_closure_wait (closure);
4516
4517 success = e_cal_backend_add_timezone_finish (
4518 backend, result, error);
4519
4520 e_async_closure_free (closure);
4521
4522 return success;
4523 }
4524
4525 /* Helper for e_cal_backend_add_timezone() */
4526 static void
cal_backend_add_timezone_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4527 cal_backend_add_timezone_thread (GSimpleAsyncResult *simple,
4528 GObject *source_object,
4529 GCancellable *cancellable)
4530 {
4531 ECalBackend *backend;
4532 ECalBackendClass *class;
4533 EDataCal *data_cal;
4534 AsyncContext *async_context;
4535
4536 backend = E_CAL_BACKEND (source_object);
4537
4538 class = E_CAL_BACKEND_GET_CLASS (backend);
4539 g_return_if_fail (class != NULL);
4540 g_return_if_fail (class->impl_add_timezone != NULL);
4541
4542 data_cal = e_cal_backend_ref_data_cal (backend);
4543 g_return_if_fail (data_cal != NULL);
4544
4545 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4546
4547 if (!e_cal_backend_is_opened (backend)) {
4548 g_simple_async_result_set_error (
4549 simple, E_CLIENT_ERROR,
4550 E_CLIENT_ERROR_NOT_OPENED,
4551 "%s", e_client_error_to_string (
4552 E_CLIENT_ERROR_NOT_OPENED));
4553 g_simple_async_result_complete_in_idle (simple);
4554
4555 } else {
4556 guint32 opid;
4557
4558 opid = cal_backend_stash_operation (backend, simple);
4559
4560 class->impl_add_timezone (
4561 backend, data_cal, opid, cancellable,
4562 async_context->tzobject);
4563 }
4564
4565 g_object_unref (data_cal);
4566 }
4567
4568 /**
4569 * e_cal_backend_add_timezone:
4570 * @backend: an #ECalBackend
4571 * @tzobject: an iCalendar VTIMEZONE string
4572 * @cancellable: optional #GCancellable object, or %NULL
4573 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
4574 * @user_data: data to pass to the callback function
4575 *
4576 * Asynchronously adds the timezone described by @tzobject to @backend.
4577 *
4578 * When the operation is finished, @callback will be called. You can
4579 * then call e_cal_backend_add_timezone_finish() to get the result of
4580 * the operation.
4581 *
4582 * Since: 3.10
4583 **/
4584 void
e_cal_backend_add_timezone(ECalBackend * backend,const gchar * tzobject,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4585 e_cal_backend_add_timezone (ECalBackend *backend,
4586 const gchar *tzobject,
4587 GCancellable *cancellable,
4588 GAsyncReadyCallback callback,
4589 gpointer user_data)
4590 {
4591 GSimpleAsyncResult *simple;
4592 AsyncContext *async_context;
4593
4594 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4595 g_return_if_fail (tzobject != NULL);
4596
4597 async_context = g_slice_new0 (AsyncContext);
4598 async_context->tzobject = g_strdup (tzobject);
4599
4600 simple = g_simple_async_result_new (
4601 G_OBJECT (backend), callback, user_data,
4602 e_cal_backend_add_timezone);
4603
4604 g_simple_async_result_set_check_cancellable (simple, cancellable);
4605
4606 g_simple_async_result_set_op_res_gpointer (
4607 simple, async_context, (GDestroyNotify) async_context_free);
4608
4609 cal_backend_push_operation (
4610 backend, simple, cancellable, FALSE,
4611 cal_backend_add_timezone_thread);
4612
4613 cal_backend_dispatch_next_operation (backend);
4614
4615 g_object_unref (simple);
4616 }
4617
4618 /**
4619 * e_cal_backend_add_timezone_finish:
4620 * @backend: an #ECalBackend
4621 * @result: a #GAsyncResult
4622 * @error: return location for a #GError, or %NULL
4623 *
4624 * Finishes the operation started with e_cal_backend_add_timezone().
4625 *
4626 * If an error occurred, the function will set @error and return %FALSE.
4627 *
4628 * Returns: %TRUE on success, %FALSE on failure
4629 *
4630 * Since: 3.10
4631 **/
4632 gboolean
e_cal_backend_add_timezone_finish(ECalBackend * backend,GAsyncResult * result,GError ** error)4633 e_cal_backend_add_timezone_finish (ECalBackend *backend,
4634 GAsyncResult *result,
4635 GError **error)
4636 {
4637 GSimpleAsyncResult *simple;
4638
4639 g_return_val_if_fail (
4640 g_simple_async_result_is_valid (
4641 result, G_OBJECT (backend),
4642 e_cal_backend_add_timezone), FALSE);
4643
4644 simple = G_SIMPLE_ASYNC_RESULT (result);
4645
4646 cal_backend_unblock_operations (backend, simple);
4647
4648 /* Assume success unless a GError is set. */
4649 return !g_simple_async_result_propagate_error (simple, error);
4650 }
4651
4652 /**
4653 * e_cal_backend_start_view:
4654 * @backend: an #ECalBackend
4655 * @view: The view to be started.
4656 *
4657 * Starts a new live view on the given backend.
4658 *
4659 * Since: 3.2
4660 */
4661 void
e_cal_backend_start_view(ECalBackend * backend,EDataCalView * view)4662 e_cal_backend_start_view (ECalBackend *backend,
4663 EDataCalView *view)
4664 {
4665 ECalBackendClass *klass;
4666
4667 g_return_if_fail (backend != NULL);
4668 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4669
4670 klass = E_CAL_BACKEND_GET_CLASS (backend);
4671 g_return_if_fail (klass != NULL);
4672 g_return_if_fail (klass->impl_start_view != NULL);
4673
4674 klass->impl_start_view (backend, view);
4675 }
4676
4677 /**
4678 * e_cal_backend_stop_view:
4679 * @backend: an #ECalBackend
4680 * @view: The view to be stopped.
4681 *
4682 * Stops a previously started live view on the given backend.
4683 *
4684 * Since: 3.2
4685 */
4686 void
e_cal_backend_stop_view(ECalBackend * backend,EDataCalView * view)4687 e_cal_backend_stop_view (ECalBackend *backend,
4688 EDataCalView *view)
4689 {
4690 ECalBackendClass *klass;
4691
4692 g_return_if_fail (backend != NULL);
4693 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4694
4695 klass = E_CAL_BACKEND_GET_CLASS (backend);
4696 g_return_if_fail (klass != NULL);
4697
4698 /* backward compatibility, do not force each backend define this function */
4699 if (klass->impl_stop_view)
4700 klass->impl_stop_view (backend, view);
4701 }
4702
4703 /**
4704 * e_cal_backend_notify_component_created:
4705 * @backend: an #ECalBackend
4706 * @component: the newly created #ECalComponent
4707 *
4708 * Notifies each of the backend's listeners about a new object.
4709 *
4710 * Uses the #EDataCalView's fields-of-interest to filter out unwanted
4711 * information from ical strings sent over the bus.
4712 *
4713 * Since: 3.4
4714 **/
4715 void
e_cal_backend_notify_component_created(ECalBackend * backend,ECalComponent * component)4716 e_cal_backend_notify_component_created (ECalBackend *backend,
4717 ECalComponent *component)
4718 {
4719 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4720 g_return_if_fail (E_IS_CAL_COMPONENT (component));
4721
4722 schedule_notify_changes (backend, NOTIFY_CHANGE_KIND_ADD, NULL, component, NULL);
4723 }
4724
4725 /**
4726 * e_cal_backend_notify_component_modified:
4727 * @backend: an #ECalBackend
4728 * @old_component: the #ECalComponent before the modification
4729 * @new_component: the #ECalComponent after the modification
4730 *
4731 * Notifies each of the backend's listeners about a modified object.
4732 *
4733 * Uses the #EDataCalView's fields-of-interest to filter out unwanted
4734 * information from ical strings sent over the bus.
4735 *
4736 * Since: 3.4
4737 **/
4738 void
e_cal_backend_notify_component_modified(ECalBackend * backend,ECalComponent * old_component,ECalComponent * new_component)4739 e_cal_backend_notify_component_modified (ECalBackend *backend,
4740 ECalComponent *old_component,
4741 ECalComponent *new_component)
4742 {
4743 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4744 g_return_if_fail (!old_component || E_IS_CAL_COMPONENT (old_component));
4745 g_return_if_fail (E_IS_CAL_COMPONENT (new_component));
4746
4747 schedule_notify_changes (backend, NOTIFY_CHANGE_KIND_MODIFY, old_component, new_component, NULL);
4748 }
4749
4750 /**
4751 * e_cal_backend_notify_component_removed:
4752 * @backend: an #ECalBackend
4753 * @id: the Id of the removed object
4754 * @old_component: the removed component
4755 * @new_component: the component after the removal. This only applies to recurrent
4756 * appointments that had an instance removed. In that case, this function
4757 * notifies a modification instead of a removal.
4758 *
4759 * Notifies each of the backend's listeners about a removed object.
4760 *
4761 * Uses the #EDataCalView's fields-of-interest to filter out unwanted
4762 * information from ical strings sent over the bus.
4763 *
4764 * Since: 3.4
4765 **/
4766 void
e_cal_backend_notify_component_removed(ECalBackend * backend,const ECalComponentId * id,ECalComponent * old_component,ECalComponent * new_component)4767 e_cal_backend_notify_component_removed (ECalBackend *backend,
4768 const ECalComponentId *id,
4769 ECalComponent *old_component,
4770 ECalComponent *new_component)
4771 {
4772 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4773 g_return_if_fail (id != NULL);
4774
4775 schedule_notify_changes (backend, NOTIFY_CHANGE_KIND_REMOVE, old_component, new_component, id);
4776 }
4777
4778 /**
4779 * e_cal_backend_notify_error:
4780 * @backend: an #ECalBackend
4781 * @message: Error message
4782 *
4783 * Notifies each of the backend's listeners about an error
4784 **/
4785 void
e_cal_backend_notify_error(ECalBackend * backend,const gchar * message)4786 e_cal_backend_notify_error (ECalBackend *backend,
4787 const gchar *message)
4788 {
4789 EDataCal *data_cal;
4790
4791 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4792 g_return_if_fail (message != NULL);
4793
4794 data_cal = e_cal_backend_ref_data_cal (backend);
4795
4796 if (data_cal != NULL) {
4797 e_data_cal_report_error (data_cal, message);
4798 g_object_unref (data_cal);
4799 }
4800 }
4801
4802 /**
4803 * e_cal_backend_notify_property_changed:
4804 * @backend: an #ECalBackend
4805 * @prop_name: property name, which changed
4806 * @prop_value: (nullable): new property value
4807 *
4808 * Notifies client about property value change.
4809 *
4810 * Since: 3.2
4811 **/
4812 void
e_cal_backend_notify_property_changed(ECalBackend * backend,const gchar * prop_name,const gchar * prop_value)4813 e_cal_backend_notify_property_changed (ECalBackend *backend,
4814 const gchar *prop_name,
4815 const gchar *prop_value)
4816 {
4817 EDataCal *data_cal;
4818
4819 g_return_if_fail (E_IS_CAL_BACKEND (backend));
4820 g_return_if_fail (prop_name != NULL);
4821
4822 data_cal = e_cal_backend_ref_data_cal (backend);
4823
4824 if (data_cal != NULL) {
4825 e_data_cal_report_backend_property_changed (data_cal, prop_name, prop_value ? prop_value : "");
4826 g_object_unref (data_cal);
4827 }
4828 }
4829
4830 /**
4831 * e_cal_backend_prepare_for_completion:
4832 * @backend: an #ECalBackend
4833 * @opid: an operation ID given to #EDataCal
4834 * @result_queue: return location for a #GQueue, or %NULL
4835 *
4836 * Obtains the #GSimpleAsyncResult for @opid and sets @result_queue as a
4837 * place to deposit results prior to completing the #GSimpleAsyncResult.
4838 *
4839 * <note>
4840 * <para>
4841 * This is a temporary function to serve #EDataCal's "respond"
4842 * functions until they can be removed. Nothing else should be
4843 * calling this function.
4844 * </para>
4845 * </note>
4846 *
4847 * Returns: (transfer full): a #GSimpleAsyncResult
4848 *
4849 * Since: 3.10
4850 **/
4851 GSimpleAsyncResult *
e_cal_backend_prepare_for_completion(ECalBackend * backend,guint32 opid,GQueue ** result_queue)4852 e_cal_backend_prepare_for_completion (ECalBackend *backend,
4853 guint32 opid,
4854 GQueue **result_queue)
4855 {
4856 GSimpleAsyncResult *simple;
4857 AsyncContext *async_context;
4858
4859 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
4860 g_return_val_if_fail (opid > 0, NULL);
4861
4862 simple = cal_backend_claim_operation (backend, opid);
4863 g_return_val_if_fail (simple != NULL, NULL);
4864
4865 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4866
4867 if (result_queue != NULL) {
4868 if (async_context != NULL)
4869 *result_queue = &async_context->result_queue;
4870 else
4871 *result_queue = NULL;
4872 }
4873
4874 return simple;
4875 }
4876
4877 /**
4878 * e_cal_backend_schedule_custom_operation:
4879 * @cal_backend: an #ECalBackend
4880 * @use_cancellable: (nullable): an optional #GCancellable to use for @func
4881 * @func: a function to call in a dedicated thread
4882 * @user_data: user data being passed to @func
4883 * @user_data_free: (nullable): optional destroy call back for @user_data
4884 *
4885 * Schedules user function @func to be run in a dedicated thread as
4886 * a blocking operation.
4887 *
4888 * The function adds its own reference to @use_cancellable, if not %NULL.
4889 *
4890 * The error returned from @func is propagated to client using
4891 * e_cal_backend_notify_error() function. If it's not desired,
4892 * then left the error unchanged and notify about errors manually.
4893 *
4894 * Since: 3.26
4895 **/
4896 void
e_cal_backend_schedule_custom_operation(ECalBackend * cal_backend,GCancellable * use_cancellable,ECalBackendCustomOpFunc func,gpointer user_data,GDestroyNotify user_data_free)4897 e_cal_backend_schedule_custom_operation (ECalBackend *cal_backend,
4898 GCancellable *use_cancellable,
4899 ECalBackendCustomOpFunc func,
4900 gpointer user_data,
4901 GDestroyNotify user_data_free)
4902 {
4903 DispatchNode *node;
4904
4905 g_return_if_fail (E_IS_CAL_BACKEND (cal_backend));
4906 g_return_if_fail (func != NULL);
4907
4908 g_mutex_lock (&cal_backend->priv->operation_lock);
4909
4910 node = g_slice_new0 (DispatchNode);
4911 node->blocking_operation = TRUE;
4912 node->cal_backend_weak_ref = e_weak_ref_new (cal_backend);
4913 node->custom_func = func;
4914 node->custom_func_user_data = user_data;
4915 node->custom_func_user_data_free = user_data_free;
4916
4917 if (G_IS_CANCELLABLE (use_cancellable))
4918 node->cancellable = g_object_ref (use_cancellable);
4919
4920 g_queue_push_tail (&cal_backend->priv->pending_operations, node);
4921
4922 g_mutex_unlock (&cal_backend->priv->operation_lock);
4923
4924 cal_backend_dispatch_next_operation (cal_backend);
4925 }
4926