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