1 /*
2  * e-cal-client.c
3  *
4  * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
5  *
6  * This library is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 /**
21  * SECTION: e-cal-client
22  * @include: libecal/libecal.h
23  * @short_description: Accessing and modifying a calendar
24  *
25  * This class is the main user facing API for accessing and modifying
26  * the calendar.
27  **/
28 
29 #include "evolution-data-server-config.h"
30 
31 #include <glib/gi18n-lib.h>
32 #include <gio/gio.h>
33 
34 /* Private D-Bus classes. */
35 #include <e-dbus-calendar.h>
36 #include <e-dbus-calendar-factory.h>
37 
38 #include <libedataserver/e-client-private.h>
39 
40 #include "e-cal-client.h"
41 #include "e-cal-component.h"
42 #include "e-cal-check-timezones.h"
43 #include "e-cal-enumtypes.h"
44 #include "e-cal-time-util.h"
45 #include "e-cal-enums.h"
46 #include "e-timezone-cache.h"
47 
48 /* Set this to a sufficiently large value
49  * to cover most long-running operations. */
50 #define DBUS_PROXY_TIMEOUT_MS (3 * 60 * 1000)  /* 3 minutes */
51 
52 typedef struct _AsyncContext AsyncContext;
53 typedef struct _SignalClosure SignalClosure;
54 typedef struct _ConnectClosure ConnectClosure;
55 typedef struct _RunInThreadClosure RunInThreadClosure;
56 
57 struct _ECalClientPrivate {
58 	EDBusCalendar *dbus_proxy;
59 	guint name_watcher_id;
60 
61 	ECalClientSourceType source_type;
62 	ICalTimezone *default_zone;
63 
64 	GMutex zone_cache_lock;
65 	GHashTable *zone_cache;
66 
67 	gulong dbus_proxy_error_handler_id;
68 	gulong dbus_proxy_notify_handler_id;
69 	gulong dbus_proxy_free_busy_data_handler_id;
70 };
71 
72 struct _AsyncContext {
73 	ECalClientView *client_view;
74 	ICalComponent *in_comp;
75 	ICalComponent *out_comp;
76 	ICalTimezone *zone;
77 	GSList *comp_list;
78 	GSList *object_list;
79 	GSList *string_list;
80 	GSList *ids_list; /* ECalComponentId * */
81 	gchar *sexp;
82 	gchar *tzid;
83 	gchar *uid;
84 	gchar *rid;
85 	gchar *auid;
86 	ECalObjModType mod;
87 	time_t start;
88 	time_t end;
89 	guint32 opflags;
90 };
91 
92 struct _SignalClosure {
93 	GWeakRef client;
94 	gchar *property_name;
95 	gchar *error_message;
96 	gchar **free_busy_data;
97 	ICalTimezone *cached_zone;
98 };
99 
100 struct _ConnectClosure {
101 	ESource *source;
102 	GCancellable *cancellable;
103 	guint32 wait_for_connected_seconds;
104 };
105 
106 struct _RunInThreadClosure {
107 	GSimpleAsyncThreadFunc func;
108 	GSimpleAsyncResult *simple;
109 	GCancellable *cancellable;
110 };
111 
112 enum {
113 	PROP_0,
114 	PROP_DEFAULT_TIMEZONE,
115 	PROP_SOURCE_TYPE
116 };
117 
118 enum {
119 	FREE_BUSY_DATA,
120 	LAST_SIGNAL
121 };
122 
123 /* Forward Declarations */
124 static void	e_cal_client_initable_init
125 					(GInitableIface *iface);
126 static void	e_cal_client_async_initable_init
127 					(GAsyncInitableIface *iface);
128 static void	e_cal_client_timezone_cache_init
129 					(ETimezoneCacheInterface *iface);
130 
131 static guint signals[LAST_SIGNAL];
132 
G_DEFINE_TYPE_WITH_CODE(ECalClient,e_cal_client,E_TYPE_CLIENT,G_ADD_PRIVATE (ECalClient)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,e_cal_client_initable_init)G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,e_cal_client_async_initable_init)G_IMPLEMENT_INTERFACE (E_TYPE_TIMEZONE_CACHE,e_cal_client_timezone_cache_init))133 G_DEFINE_TYPE_WITH_CODE (
134 	ECalClient,
135 	e_cal_client,
136 	E_TYPE_CLIENT,
137 	G_ADD_PRIVATE (ECalClient)
138 	G_IMPLEMENT_INTERFACE (
139 		G_TYPE_INITABLE,
140 		e_cal_client_initable_init)
141 	G_IMPLEMENT_INTERFACE (
142 		G_TYPE_ASYNC_INITABLE,
143 		e_cal_client_async_initable_init)
144 	G_IMPLEMENT_INTERFACE (
145 		E_TYPE_TIMEZONE_CACHE,
146 		e_cal_client_timezone_cache_init))
147 
148 static void
149 async_context_free (AsyncContext *async_context)
150 {
151 	g_clear_object (&async_context->client_view);
152 	g_clear_object (&async_context->in_comp);
153 	g_clear_object (&async_context->out_comp);
154 	g_clear_object (&async_context->zone);
155 
156 	g_slist_free_full (async_context->comp_list, g_object_unref);
157 	g_slist_free_full (async_context->object_list, g_object_unref);
158 	g_slist_free_full (async_context->string_list, g_free);
159 	g_slist_free_full (async_context->ids_list, e_cal_component_id_free);
160 
161 	g_free (async_context->sexp);
162 	g_free (async_context->tzid);
163 	g_free (async_context->uid);
164 	g_free (async_context->rid);
165 	g_free (async_context->auid);
166 
167 	g_slice_free (AsyncContext, async_context);
168 }
169 
170 static void
signal_closure_free(SignalClosure * signal_closure)171 signal_closure_free (SignalClosure *signal_closure)
172 {
173 	g_weak_ref_clear (&signal_closure->client);
174 
175 	g_free (signal_closure->property_name);
176 	g_free (signal_closure->error_message);
177 
178 	g_strfreev (signal_closure->free_busy_data);
179 	g_clear_object (&signal_closure->cached_zone);
180 
181 	/* The ICalTimezone is cached in ECalClient's internal
182 	 * "zone_cache" hash table and must not be freed here. */
183 
184 	g_slice_free (SignalClosure, signal_closure);
185 }
186 
187 static void
connect_closure_free(ConnectClosure * connect_closure)188 connect_closure_free (ConnectClosure *connect_closure)
189 {
190 	if (connect_closure->source != NULL)
191 		g_object_unref (connect_closure->source);
192 
193 	if (connect_closure->cancellable != NULL)
194 		g_object_unref (connect_closure->cancellable);
195 
196 	g_slice_free (ConnectClosure, connect_closure);
197 }
198 
199 static void
run_in_thread_closure_free(RunInThreadClosure * run_in_thread_closure)200 run_in_thread_closure_free (RunInThreadClosure *run_in_thread_closure)
201 {
202 	if (run_in_thread_closure->simple != NULL)
203 		g_object_unref (run_in_thread_closure->simple);
204 
205 	if (run_in_thread_closure->cancellable != NULL)
206 		g_object_unref (run_in_thread_closure->cancellable);
207 
208 	g_slice_free (RunInThreadClosure, run_in_thread_closure);
209 }
210 
211 /*
212  * Well-known calendar backend properties:
213  * @E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS: Contains default calendar's email
214  *   address suggested by the backend.
215  * @E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS: Contains default alarm email
216  *   address suggested by the backend.
217  * @E_CAL_BACKEND_PROPERTY_DEFAULT_OBJECT: Contains iCal component string
218  *   of an #ICalComponent with the default values for properties needed.
219  *   Preferred way of retrieving this property is by
220  *   calling e_cal_client_get_default_object().
221  *
222  * See also: @CLIENT_BACKEND_PROPERTY_OPENED, @CLIENT_BACKEND_PROPERTY_OPENING,
223  *   @CLIENT_BACKEND_PROPERTY_ONLINE, @CLIENT_BACKEND_PROPERTY_READONLY
224  *   @CLIENT_BACKEND_PROPERTY_CACHE_DIR, @CLIENT_BACKEND_PROPERTY_CAPABILITIES
225  */
226 
227 G_DEFINE_QUARK (e-cal-client-error-quark, e_cal_client_error)
228 
229 /**
230  * e_cal_client_error_to_string:
231  * @code: an #ECalClientError error code
232  *
233  * Get localized human readable description of the given error code.
234  *
235  * Returns: Localized human readable description of the given error code
236  *
237  * Since: 3.2
238  **/
239 const gchar *
e_cal_client_error_to_string(ECalClientError code)240 e_cal_client_error_to_string (ECalClientError code)
241 {
242 	switch (code) {
243 	case E_CAL_CLIENT_ERROR_NO_SUCH_CALENDAR:
244 		return _("No such calendar");
245 	case E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND:
246 		return _("Object not found");
247 	case E_CAL_CLIENT_ERROR_INVALID_OBJECT:
248 		return _("Invalid object");
249 	case E_CAL_CLIENT_ERROR_UNKNOWN_USER:
250 		return _("Unknown user");
251 	case E_CAL_CLIENT_ERROR_OBJECT_ID_ALREADY_EXISTS:
252 		return _("Object ID already exists");
253 	case E_CAL_CLIENT_ERROR_INVALID_RANGE:
254 		return _("Invalid range");
255 	}
256 
257 	return _("Unknown error");
258 }
259 
260 /**
261  * e_cal_client_error_create:
262  * @code: an #ECalClientError code to create
263  * @custom_msg: (nullable): custom message to use for the error; can be %NULL
264  *
265  * Returns: (transfer full): a new #GError containing an #E_CAL_CLIENT_ERROR of the given
266  *    @code. If the @custom_msg is NULL, then the error message is the one returned
267  *    from e_cal_client_error_to_string() for the @code, otherwise the given message is used.
268  *    Returned pointer should be freed with g_error_free().
269  *
270  * Since: 3.2
271  **/
272 GError *
e_cal_client_error_create(ECalClientError code,const gchar * custom_msg)273 e_cal_client_error_create (ECalClientError code,
274 			   const gchar *custom_msg)
275 {
276 	if (!custom_msg)
277 		custom_msg = e_cal_client_error_to_string (code);
278 
279 	return g_error_new_literal (E_CAL_CLIENT_ERROR, code, custom_msg);
280 }
281 
282 /**
283  * e_cal_client_error_create_fmt:
284  * @code: an #ECalClientError
285  * @format: (nullable): message format, or %NULL to use the default message for the @code
286  * @...: arguments for the format
287  *
288  * Similar as e_cal_client_error_create(), only here, instead of custom_msg,
289  * is used a printf() format to create a custom message for the error.
290  *
291  * Returns: (transfer full): a newly allocated #GError, which should be
292  *   freed with g_error_free(), when no longer needed.
293  *   The #GError has set the custom message, or the default message for
294  *   @code, when @format is %NULL.
295  *
296  * Since: 3.34
297  **/
298 GError *
e_cal_client_error_create_fmt(ECalClientError code,const gchar * format,...)299 e_cal_client_error_create_fmt (ECalClientError code,
300 			       const gchar *format,
301 			       ...)
302 {
303 	GError *error;
304 	gchar *custom_msg;
305 	va_list ap;
306 
307 	if (!format)
308 		return e_cal_client_error_create (code, NULL);
309 
310 	va_start (ap, format);
311 	custom_msg = g_strdup_vprintf (format, ap);
312 	va_end (ap);
313 
314 	error = e_cal_client_error_create (code, custom_msg);
315 
316 	g_free (custom_msg);
317 
318 	return error;
319 }
320 
321 static gpointer
cal_client_dbus_thread(gpointer user_data)322 cal_client_dbus_thread (gpointer user_data)
323 {
324 	GMainContext *main_context = user_data;
325 	GMainLoop *main_loop;
326 
327 	g_main_context_push_thread_default (main_context);
328 
329 	main_loop = g_main_loop_new (main_context, FALSE);
330 	g_main_loop_run (main_loop);
331 	g_main_loop_unref (main_loop);
332 
333 	g_main_context_pop_thread_default (main_context);
334 
335 	g_main_context_unref (main_context);
336 
337 	return NULL;
338 }
339 
340 static gpointer
cal_client_dbus_thread_init(gpointer unused)341 cal_client_dbus_thread_init (gpointer unused)
342 {
343 	GMainContext *main_context;
344 
345 	main_context = g_main_context_new ();
346 
347 	/* This thread terminates when the process itself terminates, so
348 	 * no need to worry about unreferencing the returned GThread. */
349 	g_thread_new (
350 		"cal-client-dbus-thread",
351 		cal_client_dbus_thread,
352 		g_main_context_ref (main_context));
353 
354 	return main_context;
355 }
356 
357 static GMainContext *
cal_client_ref_dbus_main_context(void)358 cal_client_ref_dbus_main_context (void)
359 {
360 	static GOnce cal_client_dbus_thread_once = G_ONCE_INIT;
361 
362 	g_once (
363 		&cal_client_dbus_thread_once,
364 		cal_client_dbus_thread_init, NULL);
365 
366 	return g_main_context_ref (cal_client_dbus_thread_once.retval);
367 }
368 
369 static gboolean
cal_client_run_in_dbus_thread_idle_cb(gpointer user_data)370 cal_client_run_in_dbus_thread_idle_cb (gpointer user_data)
371 {
372 	RunInThreadClosure *closure = user_data;
373 	GObject *source_object;
374 	GAsyncResult *result;
375 
376 	result = G_ASYNC_RESULT (closure->simple);
377 	source_object = g_async_result_get_source_object (result);
378 
379 	closure->func (
380 		closure->simple,
381 		source_object,
382 		closure->cancellable);
383 
384 	if (source_object != NULL)
385 		g_object_unref (source_object);
386 
387 	g_simple_async_result_complete_in_idle (closure->simple);
388 
389 	return FALSE;
390 }
391 
392 static void
cal_client_run_in_dbus_thread(GSimpleAsyncResult * simple,GSimpleAsyncThreadFunc func,gint io_priority,GCancellable * cancellable)393 cal_client_run_in_dbus_thread (GSimpleAsyncResult *simple,
394                                GSimpleAsyncThreadFunc func,
395                                gint io_priority,
396                                GCancellable *cancellable)
397 {
398 	RunInThreadClosure *closure;
399 	GMainContext *main_context;
400 	GSource *idle_source;
401 
402 	main_context = cal_client_ref_dbus_main_context ();
403 
404 	closure = g_slice_new0 (RunInThreadClosure);
405 	closure->func = func;
406 	closure->simple = g_object_ref (simple);
407 
408 	if (G_IS_CANCELLABLE (cancellable))
409 		closure->cancellable = g_object_ref (cancellable);
410 
411 	idle_source = g_idle_source_new ();
412 	g_source_set_priority (idle_source, io_priority);
413 	g_source_set_callback (
414 		idle_source, cal_client_run_in_dbus_thread_idle_cb,
415 		closure, (GDestroyNotify) run_in_thread_closure_free);
416 	g_source_attach (idle_source, main_context);
417 	g_source_unref (idle_source);
418 
419 	g_main_context_unref (main_context);
420 }
421 
422 static gboolean
cal_client_emit_backend_died_idle_cb(gpointer user_data)423 cal_client_emit_backend_died_idle_cb (gpointer user_data)
424 {
425 	SignalClosure *signal_closure = user_data;
426 	EClient *client;
427 
428 	client = g_weak_ref_get (&signal_closure->client);
429 
430 	if (client != NULL) {
431 		g_signal_emit_by_name (client, "backend-died");
432 		g_object_unref (client);
433 	}
434 
435 	return FALSE;
436 }
437 
438 static gboolean
cal_client_emit_backend_error_idle_cb(gpointer user_data)439 cal_client_emit_backend_error_idle_cb (gpointer user_data)
440 {
441 	SignalClosure *signal_closure = user_data;
442 	EClient *client;
443 
444 	client = g_weak_ref_get (&signal_closure->client);
445 
446 	if (client != NULL) {
447 		g_signal_emit_by_name (
448 			client, "backend-error",
449 			signal_closure->error_message);
450 		g_object_unref (client);
451 	}
452 
453 	return FALSE;
454 }
455 
456 static gboolean
cal_client_emit_backend_property_changed_idle_cb(gpointer user_data)457 cal_client_emit_backend_property_changed_idle_cb (gpointer user_data)
458 {
459 	SignalClosure *signal_closure = user_data;
460 	EClient *client;
461 
462 	client = g_weak_ref_get (&signal_closure->client);
463 
464 	if (client != NULL) {
465 		gchar *prop_value = NULL;
466 
467 		/* XXX Despite appearances, this function does not block. */
468 		e_client_get_backend_property_sync (
469 			client,
470 			signal_closure->property_name,
471 			&prop_value, NULL, NULL);
472 
473 		if (prop_value != NULL) {
474 			g_signal_emit_by_name (
475 				client,
476 				"backend-property-changed",
477 				signal_closure->property_name,
478 				prop_value);
479 			g_free (prop_value);
480 		}
481 
482 		g_object_unref (client);
483 	}
484 
485 	return FALSE;
486 }
487 
488 static gboolean
cal_client_emit_free_busy_data_idle_cb(gpointer user_data)489 cal_client_emit_free_busy_data_idle_cb (gpointer user_data)
490 {
491 	SignalClosure *signal_closure = user_data;
492 	EClient *client;
493 
494 	client = g_weak_ref_get (&signal_closure->client);
495 
496 	if (client != NULL) {
497 		GSList *list = NULL;
498 		gchar **strv;
499 		gint ii;
500 
501 		strv = signal_closure->free_busy_data;
502 
503 		for (ii = 0; strv[ii] != NULL; ii++) {
504 			ECalComponent *comp;
505 			ICalComponent *icalcomp;
506 			ICalComponentKind kind;
507 
508 			icalcomp = i_cal_component_new_from_string (strv[ii]);
509 			if (icalcomp == NULL)
510 				continue;
511 
512 			kind = i_cal_component_isa (icalcomp);
513 			if (kind != I_CAL_VFREEBUSY_COMPONENT) {
514 				i_cal_component_free (icalcomp);
515 				continue;
516 			}
517 
518 			comp = e_cal_component_new_from_icalcomponent (icalcomp);
519 			if (comp)
520 				list = g_slist_prepend (list, comp);
521 		}
522 
523 		list = g_slist_reverse (list);
524 
525 		g_signal_emit (client, signals[FREE_BUSY_DATA], 0, list);
526 
527 		g_slist_free_full (list, (GDestroyNotify) g_object_unref);
528 
529 		g_object_unref (client);
530 	}
531 
532 	return FALSE;
533 }
534 
535 static gboolean
cal_client_emit_timezone_added_idle_cb(gpointer user_data)536 cal_client_emit_timezone_added_idle_cb (gpointer user_data)
537 {
538 	SignalClosure *signal_closure = user_data;
539 	EClient *client;
540 
541 	client = g_weak_ref_get (&signal_closure->client);
542 
543 	if (client != NULL) {
544 		g_signal_emit_by_name (
545 			client, "timezone-added",
546 			signal_closure->cached_zone);
547 		g_object_unref (client);
548 	}
549 
550 	return FALSE;
551 }
552 
553 static void
cal_client_dbus_proxy_error_cb(EDBusCalendar * dbus_proxy,const gchar * error_message,GWeakRef * client_weak_ref)554 cal_client_dbus_proxy_error_cb (EDBusCalendar *dbus_proxy,
555                                 const gchar *error_message,
556                                 GWeakRef *client_weak_ref)
557 {
558 	EClient *client;
559 
560 	client = g_weak_ref_get (client_weak_ref);
561 
562 	if (client != NULL) {
563 		GSource *idle_source;
564 		GMainContext *main_context;
565 		SignalClosure *signal_closure;
566 
567 		signal_closure = g_slice_new0 (SignalClosure);
568 		g_weak_ref_init (&signal_closure->client, client);
569 		signal_closure->error_message = g_strdup (error_message);
570 
571 		main_context = e_client_ref_main_context (client);
572 
573 		idle_source = g_idle_source_new ();
574 		g_source_set_callback (
575 			idle_source,
576 			cal_client_emit_backend_error_idle_cb,
577 			signal_closure,
578 			(GDestroyNotify) signal_closure_free);
579 		g_source_attach (idle_source, main_context);
580 		g_source_unref (idle_source);
581 
582 		g_main_context_unref (main_context);
583 
584 		g_object_unref (client);
585 	}
586 }
587 
588 static void
cal_client_dbus_proxy_property_changed(EClient * client,const gchar * property_name,const GValue * value,gboolean is_in_main_thread)589 cal_client_dbus_proxy_property_changed (EClient *client,
590 					const gchar *property_name,
591 					const GValue *value,
592 					gboolean is_in_main_thread)
593 {
594 	const gchar *backend_prop_name = NULL;
595 
596 	g_return_if_fail (E_IS_CAL_CLIENT (client));
597 	g_return_if_fail (property_name != NULL);
598 
599 	if (g_str_equal (property_name, "alarm-email-address")) {
600 		backend_prop_name = E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS;
601 	}
602 
603 	if (g_str_equal (property_name, "cache-dir")) {
604 		backend_prop_name = CLIENT_BACKEND_PROPERTY_CACHE_DIR;
605 	}
606 
607 	if (g_str_equal (property_name, "cal-email-address")) {
608 		backend_prop_name = E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS;
609 	}
610 
611 	if (g_str_equal (property_name, "capabilities")) {
612 		gchar **strv;
613 		gchar *csv = NULL;
614 
615 		backend_prop_name = CLIENT_BACKEND_PROPERTY_CAPABILITIES;
616 
617 		strv = g_value_get_boxed (value);
618 		if (strv != NULL) {
619 			csv = g_strjoinv (",", strv);
620 		}
621 		e_client_set_capabilities (client, csv);
622 		g_free (csv);
623 	}
624 
625 	if (g_str_equal (property_name, "default-object")) {
626 		backend_prop_name = E_CAL_BACKEND_PROPERTY_DEFAULT_OBJECT;
627 	}
628 
629 	if (g_str_equal (property_name, "online")) {
630 		gboolean online;
631 
632 		backend_prop_name = CLIENT_BACKEND_PROPERTY_ONLINE;
633 
634 		online = g_value_get_boolean (value);
635 		e_client_set_online (client, online);
636 	}
637 
638 	if (g_str_equal (property_name, "revision")) {
639 		backend_prop_name = CLIENT_BACKEND_PROPERTY_REVISION;
640 	}
641 
642 	if (g_str_equal (property_name, "writable")) {
643 		gboolean writable;
644 
645 		backend_prop_name = CLIENT_BACKEND_PROPERTY_READONLY;
646 
647 		writable = g_value_get_boolean (value);
648 		e_client_set_readonly (client, !writable);
649 	}
650 
651 	if (backend_prop_name != NULL) {
652 		SignalClosure *signal_closure;
653 
654 		signal_closure = g_slice_new0 (SignalClosure);
655 		g_weak_ref_init (&signal_closure->client, client);
656 		signal_closure->property_name = g_strdup (backend_prop_name);
657 
658 		if (is_in_main_thread) {
659 			cal_client_emit_backend_property_changed_idle_cb (signal_closure);
660 			signal_closure_free (signal_closure);
661 		} else {
662 			GSource *idle_source;
663 			GMainContext *main_context;
664 
665 			main_context = e_client_ref_main_context (client);
666 
667 			idle_source = g_idle_source_new ();
668 			g_source_set_callback (
669 				idle_source,
670 				cal_client_emit_backend_property_changed_idle_cb,
671 				signal_closure,
672 				(GDestroyNotify) signal_closure_free);
673 			g_source_attach (idle_source, main_context);
674 			g_source_unref (idle_source);
675 
676 			g_main_context_unref (main_context);
677 		}
678 	}
679 
680 }
681 
682 typedef struct {
683 	EClient *client;
684 	gchar *property_name;
685 	GValue property_value;
686 } IdleProxyNotifyData;
687 
688 static void
idle_proxy_notify_data_free(gpointer ptr)689 idle_proxy_notify_data_free (gpointer ptr)
690 {
691 	IdleProxyNotifyData *ipn = ptr;
692 
693 	if (ipn) {
694 		g_clear_object (&ipn->client);
695 		g_free (ipn->property_name);
696 		g_value_unset (&ipn->property_value);
697 		g_slice_free (IdleProxyNotifyData, ipn);
698 	}
699 }
700 
701 static gboolean
cal_client_proxy_notify_idle_cb(gpointer user_data)702 cal_client_proxy_notify_idle_cb (gpointer user_data)
703 {
704 	IdleProxyNotifyData *ipn = user_data;
705 
706 	g_return_val_if_fail (ipn != NULL, FALSE);
707 
708 	cal_client_dbus_proxy_property_changed (ipn->client, ipn->property_name, &ipn->property_value, TRUE);
709 
710 	return FALSE;
711 }
712 
713 static void
cal_client_dbus_proxy_notify_cb(EDBusCalendar * dbus_proxy,GParamSpec * pspec,GWeakRef * client_weak_ref)714 cal_client_dbus_proxy_notify_cb (EDBusCalendar *dbus_proxy,
715                                  GParamSpec *pspec,
716                                  GWeakRef *client_weak_ref)
717 {
718 	EClient *client;
719 	GSource *idle_source;
720 	GMainContext *main_context;
721 	IdleProxyNotifyData *ipn;
722 
723 	client = g_weak_ref_get (client_weak_ref);
724 	if (client == NULL)
725 		return;
726 
727 	ipn = g_slice_new0 (IdleProxyNotifyData);
728 	ipn->client = g_object_ref (client);
729 	ipn->property_name = g_strdup (pspec->name);
730 	g_value_init (&ipn->property_value, pspec->value_type);
731 	g_object_get_property (G_OBJECT (dbus_proxy), pspec->name, &ipn->property_value);
732 
733 	main_context = e_client_ref_main_context (client);
734 
735 	idle_source = g_idle_source_new ();
736 	g_source_set_callback (idle_source, cal_client_proxy_notify_idle_cb,
737 		ipn, idle_proxy_notify_data_free);
738 	g_source_attach (idle_source, main_context);
739 	g_source_unref (idle_source);
740 
741 	g_main_context_unref (main_context);
742 	g_object_unref (client);
743 }
744 
745 static void
cal_client_dbus_proxy_free_busy_data_cb(EDBusCalendar * dbus_proxy,gchar ** free_busy_data,EClient * client)746 cal_client_dbus_proxy_free_busy_data_cb (EDBusCalendar *dbus_proxy,
747                                          gchar **free_busy_data,
748                                          EClient *client)
749 {
750 	GSource *idle_source;
751 	GMainContext *main_context;
752 	SignalClosure *signal_closure;
753 
754 	signal_closure = g_slice_new0 (SignalClosure);
755 	g_weak_ref_init (&signal_closure->client, client);
756 	signal_closure->free_busy_data = g_strdupv (free_busy_data);
757 
758 	main_context = e_client_ref_main_context (client);
759 
760 	idle_source = g_idle_source_new ();
761 	g_source_set_callback (
762 		idle_source,
763 		cal_client_emit_free_busy_data_idle_cb,
764 		signal_closure,
765 		(GDestroyNotify) signal_closure_free);
766 	g_source_attach (idle_source, main_context);
767 	g_source_unref (idle_source);
768 
769 	g_main_context_unref (main_context);
770 }
771 
772 static void
cal_client_name_vanished_cb(GDBusConnection * connection,const gchar * name,GWeakRef * client_weak_ref)773 cal_client_name_vanished_cb (GDBusConnection *connection,
774                              const gchar *name,
775                              GWeakRef *client_weak_ref)
776 {
777 	EClient *client;
778 
779 	client = g_weak_ref_get (client_weak_ref);
780 
781 	if (client != NULL) {
782 		GSource *idle_source;
783 		GMainContext *main_context;
784 		SignalClosure *signal_closure;
785 
786 		signal_closure = g_slice_new0 (SignalClosure);
787 		g_weak_ref_init (&signal_closure->client, client);
788 
789 		main_context = e_client_ref_main_context (client);
790 
791 		idle_source = g_idle_source_new ();
792 		g_source_set_callback (
793 			idle_source,
794 			cal_client_emit_backend_died_idle_cb,
795 			signal_closure,
796 			(GDestroyNotify) signal_closure_free);
797 		g_source_attach (idle_source, main_context);
798 		g_source_unref (idle_source);
799 
800 		g_main_context_unref (main_context);
801 
802 		g_object_unref (client);
803 	}
804 }
805 
806 static void
cal_client_set_source_type(ECalClient * cal_client,ECalClientSourceType source_type)807 cal_client_set_source_type (ECalClient *cal_client,
808                             ECalClientSourceType source_type)
809 {
810 	cal_client->priv->source_type = source_type;
811 }
812 
813 static void
cal_client_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)814 cal_client_set_property (GObject *object,
815                          guint property_id,
816                          const GValue *value,
817                          GParamSpec *pspec)
818 {
819 	switch (property_id) {
820 		case PROP_DEFAULT_TIMEZONE:
821 			e_cal_client_set_default_timezone (
822 				E_CAL_CLIENT (object),
823 				g_value_get_object (value));
824 			return;
825 
826 		case PROP_SOURCE_TYPE:
827 			cal_client_set_source_type (
828 				E_CAL_CLIENT (object),
829 				g_value_get_enum (value));
830 			return;
831 	}
832 
833 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
834 }
835 
836 static void
cal_client_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)837 cal_client_get_property (GObject *object,
838                          guint property_id,
839                          GValue *value,
840                          GParamSpec *pspec)
841 {
842 	switch (property_id) {
843 		case PROP_DEFAULT_TIMEZONE:
844 			g_value_set_object (
845 				value,
846 				e_cal_client_get_default_timezone (
847 				E_CAL_CLIENT (object)));
848 			return;
849 
850 		case PROP_SOURCE_TYPE:
851 			g_value_set_enum (
852 				value,
853 				e_cal_client_get_source_type (
854 				E_CAL_CLIENT (object)));
855 			return;
856 	}
857 
858 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
859 }
860 
861 static void
cal_client_dispose(GObject * object)862 cal_client_dispose (GObject *object)
863 {
864 	ECalClientPrivate *priv;
865 
866 	priv = E_CAL_CLIENT (object)->priv;
867 
868 	if (priv->dbus_proxy_error_handler_id > 0) {
869 		g_signal_handler_disconnect (
870 			priv->dbus_proxy,
871 			priv->dbus_proxy_error_handler_id);
872 		priv->dbus_proxy_error_handler_id = 0;
873 	}
874 
875 	if (priv->dbus_proxy_notify_handler_id > 0) {
876 		g_signal_handler_disconnect (
877 			priv->dbus_proxy,
878 			priv->dbus_proxy_notify_handler_id);
879 		priv->dbus_proxy_notify_handler_id = 0;
880 	}
881 
882 	if (priv->dbus_proxy_free_busy_data_handler_id > 0) {
883 		g_signal_handler_disconnect (
884 			priv->dbus_proxy,
885 			priv->dbus_proxy_free_busy_data_handler_id);
886 		priv->dbus_proxy_free_busy_data_handler_id = 0;
887 	}
888 
889 	if (priv->dbus_proxy != NULL) {
890 		/* Call close() asynchronously so we don't block dispose().
891 		 * Also omit a callback function, so the GDBusMessage uses
892 		 * G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED. */
893 		e_dbus_calendar_call_close (
894 			priv->dbus_proxy, NULL, NULL, NULL);
895 		g_object_unref (priv->dbus_proxy);
896 		priv->dbus_proxy = NULL;
897 	}
898 
899 	/* Chain up to parent's dispose() method. */
900 	G_OBJECT_CLASS (e_cal_client_parent_class)->dispose (object);
901 }
902 
903 static void
cal_client_finalize(GObject * object)904 cal_client_finalize (GObject *object)
905 {
906 	ECalClientPrivate *priv;
907 
908 	priv = E_CAL_CLIENT (object)->priv;
909 
910 	if (priv->name_watcher_id > 0)
911 		g_bus_unwatch_name (priv->name_watcher_id);
912 
913 	if (priv->default_zone && priv->default_zone != i_cal_timezone_get_utc_timezone ())
914 		g_clear_object (&priv->default_zone);
915 
916 	g_mutex_lock (&priv->zone_cache_lock);
917 	g_hash_table_destroy (priv->zone_cache);
918 	g_mutex_unlock (&priv->zone_cache_lock);
919 
920 	g_mutex_clear (&priv->zone_cache_lock);
921 
922 	/* Chain up to parent's finalize() method. */
923 	G_OBJECT_CLASS (e_cal_client_parent_class)->finalize (object);
924 }
925 
926 static void
cal_client_process_properties(ECalClient * cal_client,gchar * const * properties)927 cal_client_process_properties (ECalClient *cal_client,
928 			       gchar * const *properties)
929 {
930 	GObject *dbus_proxy;
931 	GObjectClass *object_class;
932 	gint ii;
933 
934 	g_return_if_fail (E_IS_CAL_CLIENT (cal_client));
935 
936 	dbus_proxy = G_OBJECT (cal_client->priv->dbus_proxy);
937 	g_return_if_fail (G_IS_OBJECT (dbus_proxy));
938 
939 	if (!properties)
940 		return;
941 
942 	object_class = G_OBJECT_GET_CLASS (dbus_proxy);
943 
944 	for (ii = 0; properties[ii]; ii++) {
945 		if (!(ii & 1) && properties[ii + 1]) {
946 			GParamSpec *param;
947 			GVariant *expected = NULL;
948 
949 			param = g_object_class_find_property (object_class, properties[ii]);
950 			if (param) {
951 				#define WORKOUT(gvl, gvr) \
952 					if (g_type_is_a (param->value_type, G_TYPE_ ## gvl)) { \
953 						expected = g_variant_parse (G_VARIANT_TYPE_ ## gvr, properties[ii + 1], NULL, NULL, NULL); \
954 					}
955 
956 				WORKOUT (BOOLEAN, BOOLEAN);
957 				WORKOUT (STRING, STRING);
958 				WORKOUT (STRV, STRING_ARRAY);
959 				WORKOUT (UCHAR, BYTE);
960 				WORKOUT (INT, INT32);
961 				WORKOUT (UINT, UINT32);
962 				WORKOUT (INT64, INT64);
963 				WORKOUT (UINT64, UINT64);
964 				WORKOUT (DOUBLE, DOUBLE);
965 
966 				#undef WORKOUT
967 			}
968 
969 			/* Update the property always, even when the current value on the GDBusProxy
970 			   matches the expected value, because sometimes the proxy can have up-to-date
971 			   values, but still not propagated into EClient properties. */
972 			if (expected) {
973 				GValue value = G_VALUE_INIT;
974 
975 				g_dbus_gvariant_to_gvalue (expected, &value);
976 
977 				cal_client_dbus_proxy_property_changed (E_CLIENT (cal_client), param->name, &value, FALSE);
978 
979 				g_value_unset (&value);
980 				g_variant_unref (expected);
981 			}
982 		}
983 	}
984 }
985 
986 static GDBusProxy *
cal_client_get_dbus_proxy(EClient * client)987 cal_client_get_dbus_proxy (EClient *client)
988 {
989 	ECalClientPrivate *priv;
990 
991 	priv = E_CAL_CLIENT (client)->priv;
992 
993 	return G_DBUS_PROXY (priv->dbus_proxy);
994 }
995 
996 static gboolean
cal_client_get_backend_property_sync(EClient * client,const gchar * prop_name,gchar ** prop_value,GCancellable * cancellable,GError ** error)997 cal_client_get_backend_property_sync (EClient *client,
998                                       const gchar *prop_name,
999                                       gchar **prop_value,
1000                                       GCancellable *cancellable,
1001                                       GError **error)
1002 {
1003 	ECalClient *cal_client;
1004 	EDBusCalendar *dbus_proxy;
1005 	gchar **strv;
1006 
1007 	cal_client = E_CAL_CLIENT (client);
1008 	dbus_proxy = cal_client->priv->dbus_proxy;
1009 
1010 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
1011 		*prop_value = g_strdup ("TRUE");
1012 		return TRUE;
1013 	}
1014 
1015 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
1016 		*prop_value = g_strdup ("FALSE");
1017 		return TRUE;
1018 	}
1019 
1020 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
1021 		if (e_dbus_calendar_get_online (dbus_proxy))
1022 			*prop_value = g_strdup ("TRUE");
1023 		else
1024 			*prop_value = g_strdup ("FALSE");
1025 		return TRUE;
1026 	}
1027 
1028 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
1029 		if (e_dbus_calendar_get_writable (dbus_proxy))
1030 			*prop_value = g_strdup ("FALSE");
1031 		else
1032 			*prop_value = g_strdup ("TRUE");
1033 		return TRUE;
1034 	}
1035 
1036 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
1037 		*prop_value = e_dbus_calendar_dup_cache_dir (dbus_proxy);
1038 		return TRUE;
1039 	}
1040 
1041 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_REVISION)) {
1042 		*prop_value = e_dbus_calendar_dup_revision (dbus_proxy);
1043 		return TRUE;
1044 	}
1045 
1046 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
1047 		strv = e_dbus_calendar_dup_capabilities (dbus_proxy);
1048 		if (strv != NULL)
1049 			*prop_value = g_strjoinv (",", strv);
1050 		else
1051 			*prop_value = g_strdup ("");
1052 		g_strfreev (strv);
1053 		return TRUE;
1054 	}
1055 
1056 	if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
1057 		*prop_value = e_dbus_calendar_dup_alarm_email_address (dbus_proxy);
1058 		return TRUE;
1059 	}
1060 
1061 	if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) {
1062 		*prop_value = e_dbus_calendar_dup_cal_email_address (dbus_proxy);
1063 		return TRUE;
1064 	}
1065 
1066 	if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_DEFAULT_OBJECT)) {
1067 		*prop_value = e_dbus_calendar_dup_default_object (dbus_proxy);
1068 		return TRUE;
1069 	}
1070 
1071 	g_set_error (
1072 		error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED,
1073 		_("Unknown calendar property “%s”"), prop_name);
1074 
1075 	return FALSE;
1076 }
1077 
1078 static gboolean
cal_client_set_backend_property_sync(EClient * client,const gchar * prop_name,const gchar * prop_value,GCancellable * cancellable,GError ** error)1079 cal_client_set_backend_property_sync (EClient *client,
1080                                       const gchar *prop_name,
1081                                       const gchar *prop_value,
1082                                       GCancellable *cancellable,
1083                                       GError **error)
1084 {
1085 	g_set_error (
1086 		error, E_CLIENT_ERROR,
1087 		E_CLIENT_ERROR_NOT_SUPPORTED,
1088 		_("Cannot change value of calendar property “%s”"),
1089 		prop_name);
1090 
1091 	return FALSE;
1092 }
1093 
1094 static gboolean
cal_client_open_sync(EClient * client,gboolean only_if_exists,GCancellable * cancellable,GError ** error)1095 cal_client_open_sync (EClient *client,
1096                       gboolean only_if_exists,
1097                       GCancellable *cancellable,
1098                       GError **error)
1099 {
1100 	ECalClient *cal_client;
1101 	gchar **properties = NULL;
1102 	GError *local_error = NULL;
1103 
1104 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
1105 
1106 	cal_client = E_CAL_CLIENT (client);
1107 
1108 	e_dbus_calendar_call_open_sync (
1109 		cal_client->priv->dbus_proxy, &properties, cancellable, &local_error);
1110 
1111 	cal_client_process_properties (cal_client, properties);
1112 	g_strfreev (properties);
1113 
1114 	if (local_error != NULL) {
1115 		g_dbus_error_strip_remote_error (local_error);
1116 		g_propagate_error (error, local_error);
1117 		return FALSE;
1118 	}
1119 
1120 	return TRUE;
1121 }
1122 
1123 static gboolean
cal_client_refresh_sync(EClient * client,GCancellable * cancellable,GError ** error)1124 cal_client_refresh_sync (EClient *client,
1125                          GCancellable *cancellable,
1126                          GError **error)
1127 {
1128 	ECalClient *cal_client;
1129 	GError *local_error = NULL;
1130 
1131 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
1132 
1133 	cal_client = E_CAL_CLIENT (client);
1134 
1135 	e_dbus_calendar_call_refresh_sync (
1136 		cal_client->priv->dbus_proxy, cancellable, &local_error);
1137 
1138 	if (local_error != NULL) {
1139 		g_dbus_error_strip_remote_error (local_error);
1140 		g_propagate_error (error, local_error);
1141 		return FALSE;
1142 	}
1143 
1144 	return TRUE;
1145 }
1146 
1147 static gboolean
cal_client_retrieve_properties_sync(EClient * client,GCancellable * cancellable,GError ** error)1148 cal_client_retrieve_properties_sync (EClient *client,
1149 				     GCancellable *cancellable,
1150 				     GError **error)
1151 {
1152 	ECalClient *cal_client;
1153 	gchar **properties = NULL;
1154 	GError *local_error = NULL;
1155 
1156 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
1157 
1158 	cal_client = E_CAL_CLIENT (client);
1159 
1160 	e_dbus_calendar_call_retrieve_properties_sync (cal_client->priv->dbus_proxy, &properties, cancellable, &local_error);
1161 
1162 	cal_client_process_properties (cal_client, properties);
1163 	g_strfreev (properties);
1164 
1165 	if (local_error != NULL) {
1166 		g_dbus_error_strip_remote_error (local_error);
1167 		g_propagate_error (error, local_error);
1168 		return FALSE;
1169 	}
1170 
1171 	return TRUE;
1172 }
1173 
1174 static void
cal_client_init_in_dbus_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1175 cal_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
1176                                 GObject *source_object,
1177                                 GCancellable *cancellable)
1178 {
1179 	ECalClientPrivate *priv;
1180 	EDBusCalendarFactory *factory_proxy;
1181 	GDBusConnection *connection;
1182 	GDBusProxy *proxy;
1183 	EClient *client;
1184 	ESource *source;
1185 	const gchar *uid;
1186 	gchar *object_path = NULL;
1187 	gchar *bus_name = NULL;
1188 	gulong handler_id;
1189 	GError *local_error = NULL;
1190 
1191 	priv = E_CAL_CLIENT (source_object)->priv;
1192 
1193 	client = E_CLIENT (source_object);
1194 	source = e_client_get_source (client);
1195 	uid = e_source_get_uid (source);
1196 
1197 	connection = g_bus_get_sync (
1198 		G_BUS_TYPE_SESSION, cancellable, &local_error);
1199 
1200 	/* Sanity check. */
1201 	g_return_if_fail (
1202 		((connection != NULL) && (local_error == NULL)) ||
1203 		((connection == NULL) && (local_error != NULL)));
1204 
1205 	if (local_error != NULL) {
1206 		g_dbus_error_strip_remote_error (local_error);
1207 		g_simple_async_result_take_error (simple, local_error);
1208 		return;
1209 	}
1210 
1211 	factory_proxy = e_dbus_calendar_factory_proxy_new_sync (
1212 		connection,
1213 		G_DBUS_PROXY_FLAGS_NONE,
1214 		CALENDAR_DBUS_SERVICE_NAME,
1215 		"/org/gnome/evolution/dataserver/CalendarFactory",
1216 		cancellable, &local_error);
1217 
1218 	/* Sanity check. */
1219 	g_return_if_fail (
1220 		((factory_proxy != NULL) && (local_error == NULL)) ||
1221 		((factory_proxy == NULL) && (local_error != NULL)));
1222 
1223 	if (local_error != NULL) {
1224 		g_dbus_error_strip_remote_error (local_error);
1225 		g_simple_async_result_take_error (simple, local_error);
1226 		g_object_unref (connection);
1227 		return;
1228 	}
1229 
1230 	switch (e_cal_client_get_source_type (E_CAL_CLIENT (client))) {
1231 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1232 			e_dbus_calendar_factory_call_open_calendar_sync (
1233 				factory_proxy, uid, &object_path, &bus_name,
1234 				cancellable, &local_error);
1235 			break;
1236 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1237 			e_dbus_calendar_factory_call_open_task_list_sync (
1238 				factory_proxy, uid, &object_path, &bus_name,
1239 				cancellable, &local_error);
1240 			break;
1241 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1242 			e_dbus_calendar_factory_call_open_memo_list_sync (
1243 				factory_proxy, uid, &object_path, &bus_name,
1244 				cancellable, &local_error);
1245 			break;
1246 		default:
1247 			g_return_if_reached ();
1248 	}
1249 
1250 	g_object_unref (factory_proxy);
1251 
1252 	/* Sanity check. */
1253 	g_return_if_fail (
1254 		(((object_path != NULL) || (bus_name != NULL)) && (local_error == NULL)) ||
1255 		(((object_path == NULL) || (bus_name == NULL)) && (local_error != NULL)));
1256 
1257 	if (local_error) {
1258 		g_dbus_error_strip_remote_error (local_error);
1259 		g_simple_async_result_take_error (simple, local_error);
1260 		g_object_unref (connection);
1261 		return;
1262 	}
1263 
1264 	e_client_set_bus_name (client, bus_name);
1265 
1266 	priv->dbus_proxy = e_dbus_calendar_proxy_new_sync (
1267 		connection,
1268 		G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
1269 		bus_name, object_path, cancellable, &local_error);
1270 
1271 	g_free (object_path);
1272 	g_free (bus_name);
1273 
1274 	/* Sanity check. */
1275 	g_return_if_fail (
1276 		((priv->dbus_proxy != NULL) && (local_error == NULL)) ||
1277 		((priv->dbus_proxy == NULL) && (local_error != NULL)));
1278 
1279 	if (local_error != NULL) {
1280 		g_dbus_error_strip_remote_error (local_error);
1281 		g_simple_async_result_take_error (simple, local_error);
1282 		g_object_unref (connection);
1283 		return;
1284 	}
1285 
1286 	/* Configure our new GDBusProxy. */
1287 
1288 	proxy = G_DBUS_PROXY (priv->dbus_proxy);
1289 
1290 	g_dbus_proxy_set_default_timeout (proxy, DBUS_PROXY_TIMEOUT_MS);
1291 
1292 	priv->name_watcher_id = g_bus_watch_name_on_connection (
1293 		connection,
1294 		g_dbus_proxy_get_name (proxy),
1295 		G_BUS_NAME_WATCHER_FLAGS_NONE,
1296 		(GBusNameAppearedCallback) NULL,
1297 		(GBusNameVanishedCallback) cal_client_name_vanished_cb,
1298 		e_weak_ref_new (client),
1299 		(GDestroyNotify) e_weak_ref_free);
1300 
1301 	handler_id = g_signal_connect_data (
1302 		proxy, "error",
1303 		G_CALLBACK (cal_client_dbus_proxy_error_cb),
1304 		e_weak_ref_new (client),
1305 		(GClosureNotify) e_weak_ref_free,
1306 		0);
1307 	priv->dbus_proxy_error_handler_id = handler_id;
1308 
1309 	handler_id = g_signal_connect_data (
1310 		proxy, "notify",
1311 		G_CALLBACK (cal_client_dbus_proxy_notify_cb),
1312 		e_weak_ref_new (client),
1313 		(GClosureNotify) e_weak_ref_free,
1314 		0);
1315 	priv->dbus_proxy_notify_handler_id = handler_id;
1316 
1317 	handler_id = g_signal_connect_object (
1318 		proxy, "free-busy-data",
1319 		G_CALLBACK (cal_client_dbus_proxy_free_busy_data_cb),
1320 		client, 0);
1321 	priv->dbus_proxy_free_busy_data_handler_id = handler_id;
1322 
1323 	/* Initialize our public-facing GObject properties. */
1324 	g_object_notify (G_OBJECT (proxy), "online");
1325 	g_object_notify (G_OBJECT (proxy), "writable");
1326 	g_object_notify (G_OBJECT (proxy), "capabilities");
1327 
1328 	g_object_unref (connection);
1329 }
1330 
1331 static gboolean
cal_client_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)1332 cal_client_initable_init (GInitable *initable,
1333                           GCancellable *cancellable,
1334                           GError **error)
1335 {
1336 	EAsyncClosure *closure;
1337 	GAsyncResult *result;
1338 	gboolean success;
1339 
1340 	closure = e_async_closure_new ();
1341 
1342 	g_async_initable_init_async (
1343 		G_ASYNC_INITABLE (initable),
1344 		G_PRIORITY_DEFAULT, cancellable,
1345 		e_async_closure_callback, closure);
1346 
1347 	result = e_async_closure_wait (closure);
1348 
1349 	success = g_async_initable_init_finish (
1350 		G_ASYNC_INITABLE (initable), result, error);
1351 
1352 	e_async_closure_free (closure);
1353 
1354 	return success;
1355 }
1356 
1357 static void
cal_client_initable_init_async(GAsyncInitable * initable,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1358 cal_client_initable_init_async (GAsyncInitable *initable,
1359                                 gint io_priority,
1360                                 GCancellable *cancellable,
1361                                 GAsyncReadyCallback callback,
1362                                 gpointer user_data)
1363 {
1364 	GSimpleAsyncResult *simple;
1365 
1366 	simple = g_simple_async_result_new (
1367 		G_OBJECT (initable), callback, user_data,
1368 		cal_client_initable_init_async);
1369 
1370 	g_simple_async_result_set_check_cancellable (simple, cancellable);
1371 
1372 	cal_client_run_in_dbus_thread (
1373 		simple, cal_client_init_in_dbus_thread,
1374 		io_priority, cancellable);
1375 
1376 	g_object_unref (simple);
1377 }
1378 
1379 static gboolean
cal_client_initable_init_finish(GAsyncInitable * initable,GAsyncResult * result,GError ** error)1380 cal_client_initable_init_finish (GAsyncInitable *initable,
1381                                  GAsyncResult *result,
1382                                  GError **error)
1383 {
1384 	GSimpleAsyncResult *simple;
1385 
1386 	g_return_val_if_fail (
1387 		g_simple_async_result_is_valid (
1388 		result, G_OBJECT (initable),
1389 		cal_client_initable_init_async), FALSE);
1390 
1391 	simple = G_SIMPLE_ASYNC_RESULT (result);
1392 
1393 	/* Assume success unless a GError is set. */
1394 	return !g_simple_async_result_propagate_error (simple, error);
1395 }
1396 
1397 static void
cal_client_add_cached_timezone(ETimezoneCache * cache,ICalTimezone * zone)1398 cal_client_add_cached_timezone (ETimezoneCache *cache,
1399                                 ICalTimezone *zone)
1400 {
1401 	ECalClientPrivate *priv;
1402 	const gchar *tzid;
1403 
1404 	priv = E_CAL_CLIENT (cache)->priv;
1405 
1406 	/* XXX Apparently this function can sometimes return NULL.
1407 	 *     I'm not sure when or why that happens, but we can't
1408 	 *     cache the ICalTimezone if it has no tzid string. */
1409 	tzid = i_cal_timezone_get_tzid (zone);
1410 	if (tzid == NULL)
1411 		return;
1412 
1413 	g_mutex_lock (&priv->zone_cache_lock);
1414 
1415 	/* Avoid replacing an existing cache entry.  We don't want to
1416 	 * invalidate any ICalTimezone pointers that may have already
1417 	 * been returned through e_timezone_cache_get_timezone(). */
1418 	if (!g_hash_table_contains (priv->zone_cache, tzid)) {
1419 		GSource *idle_source;
1420 		GMainContext *main_context;
1421 		SignalClosure *signal_closure;
1422 		ICalTimezone *cached_zone;
1423 
1424 		cached_zone = e_cal_util_copy_timezone (zone);
1425 
1426 		g_hash_table_insert (
1427 			priv->zone_cache,
1428 			g_strdup (tzid), cached_zone);
1429 
1430 		/* The closure's client reference will keep the
1431 		 * internally cached ICalTimezone alive for the
1432 		 * duration of the idle callback. */
1433 		signal_closure = g_slice_new0 (SignalClosure);
1434 		g_weak_ref_init (&signal_closure->client, cache);
1435 		signal_closure->cached_zone = g_object_ref (cached_zone);
1436 
1437 		main_context = e_client_ref_main_context (E_CLIENT (cache));
1438 
1439 		idle_source = g_idle_source_new ();
1440 		g_source_set_callback (
1441 			idle_source,
1442 			cal_client_emit_timezone_added_idle_cb,
1443 			signal_closure,
1444 			(GDestroyNotify) signal_closure_free);
1445 		g_source_attach (idle_source, main_context);
1446 		g_source_unref (idle_source);
1447 
1448 		g_main_context_unref (main_context);
1449 	}
1450 
1451 	g_mutex_unlock (&priv->zone_cache_lock);
1452 }
1453 
1454 static ICalTimezone *
cal_client_get_cached_timezone(ETimezoneCache * cache,const gchar * tzid)1455 cal_client_get_cached_timezone (ETimezoneCache *cache,
1456                                 const gchar *tzid)
1457 {
1458 	ECalClientPrivate *priv;
1459 	ICalTimezone *zone = NULL;
1460 	ICalTimezone *builtin_zone = NULL;
1461 	ICalComponent *icalcomp, *clone;
1462 	ICalProperty *prop;
1463 	const gchar *builtin_tzid;
1464 
1465 	priv = E_CAL_CLIENT (cache)->priv;
1466 
1467 	if (g_str_equal (tzid, "UTC"))
1468 		return i_cal_timezone_get_utc_timezone ();
1469 
1470 	g_mutex_lock (&priv->zone_cache_lock);
1471 
1472 	/* See if we already have it in the cache. */
1473 	zone = g_hash_table_lookup (priv->zone_cache, tzid);
1474 
1475 	if (zone != NULL)
1476 		goto exit;
1477 
1478 	/* Try to replace the original time zone with a more complete
1479 	 * and/or potentially updated built-in time zone.  Note this also
1480 	 * applies to TZIDs which match built-in time zones exactly: they
1481 	 * are extracted via i_cal_timezone_get_builtin_timezone_from_tzid()
1482 	 * below without a roundtrip to the backend. */
1483 
1484 	builtin_tzid = e_cal_match_tzid (tzid);
1485 
1486 	if (builtin_tzid != NULL)
1487 		builtin_zone = i_cal_timezone_get_builtin_timezone_from_tzid (builtin_tzid);
1488 
1489 	if (builtin_zone == NULL)
1490 		goto exit;
1491 
1492 	/* Use the built-in time zone *and* rename it.  Likely the caller
1493 	 * is asking for a specific TZID because it has an event with such
1494 	 * a TZID.  Returning an ICalTimezone with a different TZID would
1495 	 * lead to broken VCALENDARs in the caller. */
1496 
1497 	icalcomp = i_cal_timezone_get_component (builtin_zone);
1498 	clone = i_cal_component_clone (icalcomp);
1499 	g_object_unref (icalcomp);
1500 	icalcomp = clone;
1501 
1502 	for (prop = i_cal_component_get_first_property (icalcomp, I_CAL_ANY_PROPERTY);
1503 	     prop;
1504 	     g_object_unref (prop), prop = i_cal_component_get_next_property (icalcomp, I_CAL_ANY_PROPERTY)) {
1505 		if (i_cal_property_isa (prop) == I_CAL_TZID_PROPERTY) {
1506 			i_cal_property_set_value_from_string (prop, tzid, "NO");
1507 			g_object_unref (prop);
1508 			break;
1509 		}
1510 	}
1511 
1512 	zone = i_cal_timezone_new ();
1513 	if (i_cal_timezone_set_component (zone, icalcomp)) {
1514 		tzid = i_cal_timezone_get_tzid (zone);
1515 		g_hash_table_insert (priv->zone_cache, g_strdup (tzid), zone);
1516 	} else {
1517 		g_object_unref (zone);
1518 		zone = NULL;
1519 	}
1520 	g_object_unref (icalcomp);
1521 
1522 exit:
1523 	g_mutex_unlock (&priv->zone_cache_lock);
1524 
1525 	return zone;
1526 }
1527 
1528 static GList *
cal_client_list_cached_timezones(ETimezoneCache * cache)1529 cal_client_list_cached_timezones (ETimezoneCache *cache)
1530 {
1531 	ECalClientPrivate *priv;
1532 	GList *list;
1533 
1534 	priv = E_CAL_CLIENT (cache)->priv;
1535 
1536 	g_mutex_lock (&priv->zone_cache_lock);
1537 
1538 	list = g_hash_table_get_values (priv->zone_cache);
1539 
1540 	g_mutex_unlock (&priv->zone_cache_lock);
1541 
1542 	return list;
1543 }
1544 
1545 static void
e_cal_client_class_init(ECalClientClass * class)1546 e_cal_client_class_init (ECalClientClass *class)
1547 {
1548 	GObjectClass *object_class;
1549 	EClientClass *client_class;
1550 
1551 	object_class = G_OBJECT_CLASS (class);
1552 	object_class->set_property = cal_client_set_property;
1553 	object_class->get_property = cal_client_get_property;
1554 	object_class->dispose = cal_client_dispose;
1555 	object_class->finalize = cal_client_finalize;
1556 
1557 	client_class = E_CLIENT_CLASS (class);
1558 	client_class->get_dbus_proxy = cal_client_get_dbus_proxy;
1559 	client_class->get_backend_property_sync = cal_client_get_backend_property_sync;
1560 	client_class->set_backend_property_sync = cal_client_set_backend_property_sync;
1561 	client_class->open_sync = cal_client_open_sync;
1562 	client_class->refresh_sync = cal_client_refresh_sync;
1563 	client_class->retrieve_properties_sync = cal_client_retrieve_properties_sync;
1564 
1565 	g_object_class_install_property (
1566 		object_class,
1567 		PROP_DEFAULT_TIMEZONE,
1568 		g_param_spec_object (
1569 			"default-timezone",
1570 			"Default Timezone",
1571 			"Timezone used to resolve DATE and floating DATE-TIME values",
1572 			I_CAL_TYPE_TIMEZONE,
1573 			G_PARAM_READWRITE |
1574 			G_PARAM_EXPLICIT_NOTIFY |
1575 			G_PARAM_STATIC_STRINGS));
1576 
1577 	g_object_class_install_property (
1578 		object_class,
1579 		PROP_SOURCE_TYPE,
1580 		g_param_spec_enum (
1581 			"source-type",
1582 			"Source Type",
1583 			"The iCalendar data type",
1584 			E_TYPE_CAL_CLIENT_SOURCE_TYPE,
1585 			E_CAL_CLIENT_SOURCE_TYPE_EVENTS,
1586 			G_PARAM_READWRITE |
1587 			G_PARAM_CONSTRUCT_ONLY |
1588 			G_PARAM_STATIC_STRINGS));
1589 
1590 	/**
1591 	 * ECalClient::free-busy-data
1592 	 * @client: A calendar client.
1593 	 * @free_busy_ecalcomps: (type GSList<ECalComponent>):
1594 	 **/
1595 	signals[FREE_BUSY_DATA] = g_signal_new (
1596 		"free-busy-data",
1597 		G_OBJECT_CLASS_TYPE (class),
1598 		G_SIGNAL_RUN_FIRST,
1599 		G_STRUCT_OFFSET (ECalClientClass, free_busy_data),
1600 		NULL, NULL, NULL,
1601 		G_TYPE_NONE, 1,
1602 		G_TYPE_POINTER);
1603 }
1604 
1605 static void
e_cal_client_initable_init(GInitableIface * iface)1606 e_cal_client_initable_init (GInitableIface *iface)
1607 {
1608 	iface->init = cal_client_initable_init;
1609 }
1610 
1611 static void
e_cal_client_async_initable_init(GAsyncInitableIface * iface)1612 e_cal_client_async_initable_init (GAsyncInitableIface *iface)
1613 {
1614 	iface->init_async = cal_client_initable_init_async;
1615 	iface->init_finish = cal_client_initable_init_finish;
1616 }
1617 
1618 static void
e_cal_client_timezone_cache_init(ETimezoneCacheInterface * iface)1619 e_cal_client_timezone_cache_init (ETimezoneCacheInterface *iface)
1620 {
1621 	iface->tzcache_add_timezone = cal_client_add_cached_timezone;
1622 	iface->tzcache_get_timezone = cal_client_get_cached_timezone;
1623 	iface->tzcache_list_timezones = cal_client_list_cached_timezones;
1624 }
1625 
1626 static void
e_cal_client_init(ECalClient * client)1627 e_cal_client_init (ECalClient *client)
1628 {
1629 	GHashTable *zone_cache;
1630 
1631 	zone_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
1632 
1633 	client->priv = e_cal_client_get_instance_private (client);
1634 	client->priv->source_type = E_CAL_CLIENT_SOURCE_TYPE_LAST;
1635 	client->priv->default_zone = i_cal_timezone_get_utc_timezone ();
1636 	g_mutex_init (&client->priv->zone_cache_lock);
1637 	client->priv->zone_cache = zone_cache;
1638 }
1639 
1640 /**
1641  * e_cal_client_connect_sync:
1642  * @source: an #ESource
1643  * @source_type: source type of the calendar
1644  * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
1645  * @cancellable: optional #GCancellable object, or %NULL
1646  * @error: return location for a #GError, or %NULL
1647  *
1648  * Creates a new #ECalClient for @source and @source_type.  If an error
1649  * occurs, the function will set @error and return %FALSE.
1650  *
1651  * The @wait_for_connected_seconds argument had been added since 3.16,
1652  * to let the caller decide how long to wait for the backend to fully
1653  * connect to its (possibly remote) data store. This is required due
1654  * to a change in the authentication process, which is fully asynchronous
1655  * and done on the client side, while not every client is supposed to
1656  * response to authentication requests. In case the backend will not connect
1657  * within the set interval, then it is opened in an offline mode. A special
1658  * value -1 can be used to not wait for the connected state at all.
1659  *
1660  * Unlike with e_cal_client_new(), there is no need to call
1661  * e_client_open_sync() after obtaining the #ECalClient.
1662  *
1663  * For error handling convenience, any error message returned by this
1664  * function will have a descriptive prefix that includes the display
1665  * name of @source.
1666  *
1667  * Returns: (transfer full) (nullable): a new #ECalClient, or %NULL
1668  *
1669  * Since: 3.8
1670  **/
1671 EClient *
e_cal_client_connect_sync(ESource * source,ECalClientSourceType source_type,guint32 wait_for_connected_seconds,GCancellable * cancellable,GError ** error)1672 e_cal_client_connect_sync (ESource *source,
1673                            ECalClientSourceType source_type,
1674 			   guint32 wait_for_connected_seconds,
1675                            GCancellable *cancellable,
1676                            GError **error)
1677 {
1678 	ECalClient *client;
1679 	GError *local_error = NULL;
1680 
1681 	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1682 	g_return_val_if_fail (
1683 		source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ||
1684 		source_type == E_CAL_CLIENT_SOURCE_TYPE_TASKS ||
1685 		source_type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS, NULL);
1686 
1687 	client = g_object_new (
1688 		E_TYPE_CAL_CLIENT,
1689 		"source", source,
1690 		"source-type", source_type, NULL);
1691 
1692 	g_initable_init (G_INITABLE (client), cancellable, &local_error);
1693 
1694 	if (local_error == NULL) {
1695 		gchar **properties = NULL;
1696 
1697 		e_dbus_calendar_call_open_sync (
1698 			client->priv->dbus_proxy, &properties, cancellable, &local_error);
1699 
1700 		cal_client_process_properties (client, properties);
1701 		g_strfreev (properties);
1702 	}
1703 
1704 	if (!local_error && wait_for_connected_seconds != (guint32) -1) {
1705 		/* These errors are ignored, the book is left opened in an offline mode. */
1706 		e_client_wait_for_connected_sync (E_CLIENT (client),
1707 			wait_for_connected_seconds, cancellable, NULL);
1708 	}
1709 
1710 	if (local_error != NULL) {
1711 		g_dbus_error_strip_remote_error (local_error);
1712 		g_propagate_error (error, local_error);
1713 		g_prefix_error (
1714 			error,_("Unable to connect to “%s”: "),
1715 			e_source_get_display_name (source));
1716 		g_object_unref (client);
1717 		return NULL;
1718 	}
1719 
1720 	return E_CLIENT (client);
1721 }
1722 
1723 static void
cal_client_connect_wait_for_connected_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1724 cal_client_connect_wait_for_connected_cb (GObject *source_object,
1725 					   GAsyncResult *result,
1726 					   gpointer user_data)
1727 {
1728 	GSimpleAsyncResult *simple;
1729 
1730 	simple = G_SIMPLE_ASYNC_RESULT (user_data);
1731 
1732 	/* These errors are ignored, the book is left opened in an offline mode. */
1733 	e_client_wait_for_connected_finish (E_CLIENT (source_object), result, NULL);
1734 
1735 	g_simple_async_result_complete (simple);
1736 
1737 	g_object_unref (simple);
1738 }
1739 
1740 /* Helper for e_cal_client_connect() */
1741 static void
cal_client_connect_open_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1742 cal_client_connect_open_cb (GObject *source_object,
1743                             GAsyncResult *result,
1744                             gpointer user_data)
1745 {
1746 	GSimpleAsyncResult *simple;
1747 	gchar **properties = NULL;
1748 	GObject *client_object;
1749 	GError *local_error = NULL;
1750 
1751 	simple = G_SIMPLE_ASYNC_RESULT (user_data);
1752 
1753 	e_dbus_calendar_call_open_finish (
1754 		E_DBUS_CALENDAR (source_object), &properties, result, &local_error);
1755 
1756 	client_object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
1757 	if (client_object) {
1758 		cal_client_process_properties (E_CAL_CLIENT (client_object), properties);
1759 
1760 		if (!local_error) {
1761 			ConnectClosure *closure;
1762 
1763 			closure = g_simple_async_result_get_op_res_gpointer (simple);
1764 			if (closure->wait_for_connected_seconds != (guint32) -1) {
1765 				e_client_wait_for_connected (E_CLIENT (client_object),
1766 					closure->wait_for_connected_seconds,
1767 					closure->cancellable,
1768 					cal_client_connect_wait_for_connected_cb, g_object_ref (simple));
1769 
1770 				g_clear_object (&client_object);
1771 				g_object_unref (simple);
1772 				g_strfreev (properties);
1773 				return;
1774 			}
1775 		}
1776 
1777 		g_clear_object (&client_object);
1778 	}
1779 
1780 	if (local_error != NULL) {
1781 		g_dbus_error_strip_remote_error (local_error);
1782 		g_simple_async_result_take_error (simple, local_error);
1783 	}
1784 
1785 	g_simple_async_result_complete (simple);
1786 
1787 	g_object_unref (simple);
1788 	g_strfreev (properties);
1789 }
1790 
1791 /* Helper for e_cal_client_connect() */
1792 static void
cal_client_connect_init_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1793 cal_client_connect_init_cb (GObject *source_object,
1794                             GAsyncResult *result,
1795                             gpointer user_data)
1796 {
1797 	GSimpleAsyncResult *simple;
1798 	ECalClientPrivate *priv;
1799 	ConnectClosure *closure;
1800 	GError *local_error = NULL;
1801 
1802 	simple = G_SIMPLE_ASYNC_RESULT (user_data);
1803 
1804 	g_async_initable_init_finish (
1805 		G_ASYNC_INITABLE (source_object), result, &local_error);
1806 
1807 	if (local_error != NULL) {
1808 		g_simple_async_result_take_error (simple, local_error);
1809 		g_simple_async_result_complete (simple);
1810 		goto exit;
1811 	}
1812 
1813 	/* Note, we're repurposing some function parameters. */
1814 
1815 	result = G_ASYNC_RESULT (simple);
1816 	source_object = g_async_result_get_source_object (result);
1817 	closure = g_simple_async_result_get_op_res_gpointer (simple);
1818 
1819 	priv = E_CAL_CLIENT (source_object)->priv;
1820 
1821 	e_dbus_calendar_call_open (
1822 		priv->dbus_proxy,
1823 		closure->cancellable,
1824 		cal_client_connect_open_cb,
1825 		g_object_ref (simple));
1826 
1827 	g_object_unref (source_object);
1828 
1829 exit:
1830 	g_object_unref (simple);
1831 }
1832 
1833 /**
1834  * e_cal_client_connect:
1835  * @source: an #ESource
1836  * @source_type: source tpe of the calendar
1837  * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
1838  * @cancellable: optional #GCancellable object, or %NULL
1839  * @callback: a #GAsyncReadyCallback to call when the request
1840  *            is satisfied
1841  * @user_data: data to pass to the callback function
1842  *
1843  * Asynchronously creates a new #ECalClient for @source and @source_type.
1844  *
1845  * The @wait_for_connected_seconds argument had been added since 3.16,
1846  * to let the caller decide how long to wait for the backend to fully
1847  * connect to its (possibly remote) data store. This is required due
1848  * to a change in the authentication process, which is fully asynchronous
1849  * and done on the client side, while not every client is supposed to
1850  * response to authentication requests. In case the backend will not connect
1851  * within the set interval, then it is opened in an offline mode. A special
1852  * value -1 can be used to not wait for the connected state at all.
1853  *
1854  * Unlike with e_cal_client_new(), there is no need to call e_client_open()
1855  * after obtaining the #ECalClient.
1856  *
1857  * When the operation is finished, @callback will be called.  You can then
1858  * call e_cal_client_connect_finish() to get the result of the operation.
1859  *
1860  * Since: 3.8
1861  **/
1862 void
e_cal_client_connect(ESource * source,ECalClientSourceType source_type,guint32 wait_for_connected_seconds,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1863 e_cal_client_connect (ESource *source,
1864                       ECalClientSourceType source_type,
1865 		      guint32 wait_for_connected_seconds,
1866                       GCancellable *cancellable,
1867                       GAsyncReadyCallback callback,
1868                       gpointer user_data)
1869 {
1870 	GSimpleAsyncResult *simple;
1871 	ConnectClosure *closure;
1872 	ECalClient *client;
1873 
1874 	g_return_if_fail (E_IS_SOURCE (source));
1875 	g_return_if_fail (
1876 		source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ||
1877 		source_type == E_CAL_CLIENT_SOURCE_TYPE_TASKS ||
1878 		source_type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS);
1879 
1880 	/* Two things with this: 1) instantiate the client object
1881 	 * immediately to make sure the thread-default GMainContext
1882 	 * gets plucked, and 2) do not call the D-Bus open() method
1883 	 * from our designated D-Bus thread -- it may take a long
1884 	 * time and block other clients from receiving signals. */
1885 
1886 	closure = g_slice_new0 (ConnectClosure);
1887 	closure->source = g_object_ref (source);
1888 	closure->wait_for_connected_seconds = wait_for_connected_seconds;
1889 
1890 	if (G_IS_CANCELLABLE (cancellable))
1891 		closure->cancellable = g_object_ref (cancellable);
1892 
1893 	client = g_object_new (
1894 		E_TYPE_CAL_CLIENT,
1895 		"source", source,
1896 		"source-type", source_type, NULL);
1897 
1898 	simple = g_simple_async_result_new (
1899 		G_OBJECT (client), callback,
1900 		user_data, e_cal_client_connect);
1901 
1902 	g_simple_async_result_set_check_cancellable (simple, cancellable);
1903 
1904 	g_simple_async_result_set_op_res_gpointer (
1905 		simple, closure, (GDestroyNotify) connect_closure_free);
1906 
1907 	g_async_initable_init_async (
1908 		G_ASYNC_INITABLE (client),
1909 		G_PRIORITY_DEFAULT, cancellable,
1910 		cal_client_connect_init_cb,
1911 		g_object_ref (simple));
1912 
1913 	g_object_unref (simple);
1914 	g_object_unref (client);
1915 }
1916 
1917 /**
1918  * e_cal_client_connect_finish:
1919  * @result: a #GAsyncResult
1920  * @error: return location for a #GError, or %NULL
1921  *
1922  * Finishes the operation started with e_cal_client_connect().  If an
1923  * error occurs in connecting to the D-Bus service, the function sets
1924  * @error and returns %NULL.
1925  *
1926  * For error handling convenience, any error message returned by this
1927  * function will have a descriptive prefix that includes the display
1928  * name of the #ESource passed to e_cal_client_connect().
1929  *
1930  * Returns: (transfer full) (nullable): a new #ECalClient, or %NULL
1931  *
1932  * Since: 3.8
1933  **/
1934 EClient *
e_cal_client_connect_finish(GAsyncResult * result,GError ** error)1935 e_cal_client_connect_finish (GAsyncResult *result,
1936                              GError **error)
1937 {
1938 	GSimpleAsyncResult *simple;
1939 	ConnectClosure *closure;
1940 	gpointer source_tag;
1941 
1942 	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
1943 
1944 	simple = G_SIMPLE_ASYNC_RESULT (result);
1945 	closure = g_simple_async_result_get_op_res_gpointer (simple);
1946 
1947 	source_tag = g_simple_async_result_get_source_tag (simple);
1948 	g_return_val_if_fail (source_tag == e_cal_client_connect, NULL);
1949 
1950 	if (g_simple_async_result_propagate_error (simple, error)) {
1951 		g_prefix_error (
1952 			error, _("Unable to connect to “%s”: "),
1953 			e_source_get_display_name (closure->source));
1954 		return NULL;
1955 	}
1956 
1957 	return E_CLIENT (g_async_result_get_source_object (result));
1958 }
1959 
1960 /**
1961  * e_cal_client_get_source_type:
1962  * @client: A calendar client.
1963  *
1964  * Gets the source type of the calendar client.
1965  *
1966  * Returns: an #ECalClientSourceType value corresponding
1967  * to the source type of the calendar client.
1968  *
1969  * Since: 3.2
1970  **/
1971 ECalClientSourceType
e_cal_client_get_source_type(ECalClient * client)1972 e_cal_client_get_source_type (ECalClient *client)
1973 {
1974 	g_return_val_if_fail (
1975 		E_IS_CAL_CLIENT (client),
1976 		E_CAL_CLIENT_SOURCE_TYPE_LAST);
1977 
1978 	return client->priv->source_type;
1979 }
1980 
1981 /**
1982  * e_cal_client_get_local_attachment_store:
1983  * @client: A calendar client.
1984  *
1985  * Queries the URL where the calendar attachments are
1986  * serialized in the local filesystem. This enable clients
1987  * to operate with the reference to attachments rather than the data itself
1988  * unless it specifically uses the attachments for open/sending
1989  * operations.
1990  *
1991  * Returns: The URL where the attachments are serialized in the
1992  * local filesystem.
1993  *
1994  * Since: 3.2
1995  **/
1996 const gchar *
e_cal_client_get_local_attachment_store(ECalClient * client)1997 e_cal_client_get_local_attachment_store (ECalClient *client)
1998 {
1999 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), NULL);
2000 
2001 	return e_dbus_calendar_get_cache_dir (client->priv->dbus_proxy);
2002 }
2003 
2004 /**
2005  * e_cal_client_set_default_timezone:
2006  * @client: A calendar client.
2007  * @zone: A timezone object.
2008  *
2009  * Sets the default timezone to use to resolve DATE and floating DATE-TIME
2010  * values. This will typically be from the user's timezone setting. Call this
2011  * before using any other object fetching functions.
2012  *
2013  * Since: 3.2
2014  **/
2015 void
e_cal_client_set_default_timezone(ECalClient * client,ICalTimezone * zone)2016 e_cal_client_set_default_timezone (ECalClient *client,
2017                                    ICalTimezone *zone)
2018 {
2019 	g_return_if_fail (E_IS_CAL_CLIENT (client));
2020 	g_return_if_fail (zone != NULL);
2021 
2022 	if (zone == client->priv->default_zone)
2023 		return;
2024 
2025 	if (client->priv->default_zone != i_cal_timezone_get_utc_timezone ())
2026 		g_clear_object (&client->priv->default_zone);
2027 
2028 	if (zone == i_cal_timezone_get_utc_timezone ())
2029 		client->priv->default_zone = zone;
2030 	else
2031 		client->priv->default_zone = e_cal_util_copy_timezone (zone);
2032 
2033 	g_object_notify (G_OBJECT (client), "default-timezone");
2034 }
2035 
2036 /**
2037  * e_cal_client_get_default_timezone:
2038  * @client: A calendar client.
2039  *
2040  * Returns the default timezone previously set with
2041  * e_cal_client_set_default_timezone().  The returned pointer is owned by
2042  * the @client and should not be freed.
2043  *
2044  * Returns: (transfer none): an #ICalTimezone
2045  *
2046  * Since: 3.2
2047  **/
2048 ICalTimezone *
e_cal_client_get_default_timezone(ECalClient * client)2049 e_cal_client_get_default_timezone (ECalClient *client)
2050 {
2051 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), NULL);
2052 
2053 	return client->priv->default_zone;
2054 }
2055 
2056 /**
2057  * e_cal_client_check_one_alarm_only:
2058  * @client: A calendar client.
2059  *
2060  * Checks if a calendar supports only one alarm per component.
2061  *
2062  * Returns: TRUE if the calendar allows only one alarm, FALSE otherwise.
2063  *
2064  * Since: 3.2
2065  **/
2066 gboolean
e_cal_client_check_one_alarm_only(ECalClient * client)2067 e_cal_client_check_one_alarm_only (ECalClient *client)
2068 {
2069 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
2070 
2071 	return e_client_check_capability (
2072 		E_CLIENT (client),
2073 		E_CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY);
2074 }
2075 
2076 /**
2077  * e_cal_client_check_save_schedules:
2078  * @client: A calendar client.
2079  *
2080  * Checks whether the calendar saves schedules.
2081  *
2082  * Returns: TRUE if it saves schedules, FALSE otherwise.
2083  *
2084  * Since: 3.2
2085  **/
2086 gboolean
e_cal_client_check_save_schedules(ECalClient * client)2087 e_cal_client_check_save_schedules (ECalClient *client)
2088 {
2089 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
2090 
2091 	return e_client_check_capability (
2092 		E_CLIENT (client),
2093 		E_CAL_STATIC_CAPABILITY_SAVE_SCHEDULES);
2094 }
2095 
2096 /**
2097  * e_cal_client_check_organizer_must_attend:
2098  * @client: A calendar client.
2099  *
2100  * Checks if a calendar forces organizers of meetings to be also attendees.
2101  *
2102  * Returns: TRUE if the calendar forces organizers to attend meetings,
2103  * FALSE otherwise.
2104  *
2105  * Since: 3.2
2106  **/
2107 gboolean
e_cal_client_check_organizer_must_attend(ECalClient * client)2108 e_cal_client_check_organizer_must_attend (ECalClient *client)
2109 {
2110 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
2111 
2112 	return e_client_check_capability (
2113 		E_CLIENT (client),
2114 		E_CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ATTEND);
2115 }
2116 
2117 /**
2118  * e_cal_client_check_organizer_must_accept:
2119  * @client: A calendar client.
2120  *
2121  * Checks whether a calendar requires organizer to accept their attendance to
2122  * meetings.
2123  *
2124  * Returns: TRUE if the calendar requires organizers to accept, FALSE
2125  * otherwise.
2126  *
2127  * Since: 3.2
2128  **/
2129 gboolean
e_cal_client_check_organizer_must_accept(ECalClient * client)2130 e_cal_client_check_organizer_must_accept (ECalClient *client)
2131 {
2132 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
2133 
2134 	return e_client_check_capability (
2135 		E_CLIENT (client),
2136 		E_CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ACCEPT);
2137 }
2138 
2139 /**
2140  * e_cal_client_check_recurrences_no_master:
2141  * @client: A calendar client.
2142  *
2143  * Checks if the calendar has a master object for recurrences.
2144  *
2145  * Returns: TRUE if the calendar has a master object for recurrences,
2146  * FALSE otherwise.
2147  *
2148  * Since: 3.2
2149  **/
2150 gboolean
e_cal_client_check_recurrences_no_master(ECalClient * client)2151 e_cal_client_check_recurrences_no_master (ECalClient *client)
2152 {
2153 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
2154 
2155 	return e_client_check_capability (
2156 		E_CLIENT (client),
2157 		E_CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER);
2158 }
2159 
2160 struct comp_instance {
2161 	ECalComponent *comp;
2162 	ICalTime *start;
2163 	ICalTime *end;
2164 };
2165 
2166 static struct comp_instance *
comp_instance_new(void)2167 comp_instance_new (void)
2168 {
2169 	return g_slice_new0 (struct comp_instance);
2170 }
2171 
2172 static void
comp_instance_free(gpointer ptr)2173 comp_instance_free (gpointer ptr)
2174 {
2175 	struct comp_instance *ci = ptr;
2176 
2177 	if (ci) {
2178 		g_clear_object (&ci->comp);
2179 		g_clear_object (&ci->start);
2180 		g_clear_object (&ci->end);
2181 		g_slice_free (struct comp_instance, ci);
2182 	}
2183 }
2184 
2185 struct instances_info {
2186 	GSList **instances;
2187 };
2188 
2189 /* Called from cal_recur_generate_instances(); adds an instance to the list */
2190 static gboolean
add_instance_cb(ICalComponent * icomp,ICalTime * start,ICalTime * end,gpointer user_data,GCancellable * cancellable,GError ** error)2191 add_instance_cb (ICalComponent *icomp,
2192 		 ICalTime *start,
2193 		 ICalTime *end,
2194 		 gpointer user_data,
2195 		 GCancellable *cancellable,
2196 		 GError **error)
2197 {
2198 	GSList **list;
2199 	struct comp_instance *ci;
2200 	struct instances_info *instances_hold;
2201 
2202 	instances_hold = user_data;
2203 	list = instances_hold->instances;
2204 
2205 	ci = comp_instance_new ();
2206 
2207 	/* add the instance to the list */
2208 	ci->comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
2209 	if (!ci->comp) {
2210 		comp_instance_free (ci);
2211 		return FALSE;
2212 	}
2213 
2214 	/* make sure we return an instance */
2215 	if (e_cal_component_has_recurrences (ci->comp) &&
2216 	    !e_cal_component_is_instance (ci->comp)) {
2217 		ECalComponentDateTime *dtstart, *dtend;
2218 		ECalComponentRange *range;
2219 
2220 		/* Update DTSTART */
2221 		dtstart = e_cal_component_datetime_new (start, i_cal_time_get_tzid (start));
2222 		e_cal_component_set_dtstart (ci->comp, dtstart);
2223 
2224 		/* set the RECUR-ID for the instance */
2225 		range = e_cal_component_range_new (E_CAL_COMPONENT_RANGE_SINGLE, dtstart);
2226 
2227 		e_cal_component_set_recurid (ci->comp, range);
2228 
2229 		e_cal_component_datetime_free (dtstart);
2230 		e_cal_component_range_free (range);
2231 
2232 		/* Update DTEND */
2233 		dtend = e_cal_component_datetime_new (end, i_cal_time_get_tzid (end));
2234 		e_cal_component_set_dtend (ci->comp, dtend);
2235 		e_cal_component_datetime_free (dtend);
2236 	}
2237 
2238 	ci->start = i_cal_time_clone (start);
2239 	ci->end = i_cal_time_clone (end);
2240 
2241 	*list = g_slist_prepend (*list, ci);
2242 
2243 	return TRUE;
2244 }
2245 
2246 /* Used from g_slist_sort(); compares two struct comp_instance structures */
2247 static gint
compare_comp_instance(gconstpointer a,gconstpointer b)2248 compare_comp_instance (gconstpointer a,
2249                        gconstpointer b)
2250 {
2251 	const struct comp_instance *cia, *cib;
2252 
2253 	cia = a;
2254 	cib = b;
2255 
2256 	return i_cal_time_compare (cia->start, cib->start);
2257 }
2258 
2259 static time_t
convert_to_tt_with_zone(const ECalComponentDateTime * dt,ECalRecurResolveTimezoneCb tz_cb,gpointer tz_cb_data,ICalTimezone * default_timezone)2260 convert_to_tt_with_zone (const ECalComponentDateTime *dt,
2261 			 ECalRecurResolveTimezoneCb tz_cb,
2262 			 gpointer tz_cb_data,
2263 			 ICalTimezone *default_timezone)
2264 {
2265 	ICalTimezone *zone = NULL;
2266 	ICalTime *value;
2267 	time_t tt;
2268 
2269 	value = dt ? e_cal_component_datetime_get_value (dt) : NULL;
2270 
2271 	if (!dt || !value)
2272 		return (time_t) 0;
2273 
2274 	if (i_cal_time_is_utc (value)) {
2275 		zone = i_cal_timezone_get_utc_timezone ();
2276 	} else if (tz_cb && !i_cal_time_is_date (value) && e_cal_component_datetime_get_tzid (dt)) {
2277 		zone = (*tz_cb) (e_cal_component_datetime_get_tzid (dt), tz_cb_data, NULL, NULL);
2278 	}
2279 
2280 	if (!zone)
2281 		zone = default_timezone;
2282 
2283 	tt = i_cal_time_as_timet_with_zone (value, zone);
2284 
2285 	return tt;
2286 }
2287 
2288 static GSList *
process_detached_instances(GSList * instances,GSList * detached_instances,ECalRecurResolveTimezoneCb tz_cb,gpointer tz_cb_data,ICalTimezone * default_timezone)2289 process_detached_instances (GSList *instances,
2290 			    GSList *detached_instances,
2291 			    ECalRecurResolveTimezoneCb tz_cb,
2292 			    gpointer tz_cb_data,
2293 			    ICalTimezone *default_timezone)
2294 {
2295 	struct comp_instance *ci, *cid;
2296 	GSList *dl, *unprocessed_instances = NULL;
2297 
2298 	for (dl = detached_instances; dl != NULL; dl = dl->next) {
2299 		GSList *il;
2300 		const gchar *uid;
2301 		gboolean processed;
2302 		ECalComponentRange *recur_id;
2303 		time_t d_rid, i_rid;
2304 
2305 		processed = FALSE;
2306 
2307 		cid = dl->data;
2308 		uid = e_cal_component_get_uid (cid->comp);
2309 		recur_id = e_cal_component_get_recurid (cid->comp);
2310 
2311 		if (!recur_id ||
2312 		    !e_cal_component_range_get_datetime (recur_id) ||
2313 		    !e_cal_component_datetime_get_value (e_cal_component_range_get_datetime (recur_id))) {
2314 			e_cal_component_range_free (recur_id);
2315 			continue;
2316 		}
2317 
2318 		d_rid = convert_to_tt_with_zone (e_cal_component_range_get_datetime (recur_id), tz_cb, tz_cb_data, default_timezone);
2319 
2320 		/* search for coincident instances already expanded */
2321 		for (il = instances; il != NULL; il = il->next) {
2322 			const gchar *instance_uid;
2323 			gint cmp;
2324 
2325 			ci = il->data;
2326 			instance_uid = e_cal_component_get_uid (ci->comp);
2327 
2328 			if (g_strcmp0 (uid, instance_uid) == 0) {
2329 				ECalComponentRange *instance_recur_id;
2330 
2331 				instance_recur_id = e_cal_component_get_recurid (ci->comp);
2332 
2333 				if (!instance_recur_id ||
2334 				    !e_cal_component_range_get_datetime (instance_recur_id) ||
2335 				    !e_cal_component_datetime_get_value (e_cal_component_range_get_datetime (instance_recur_id))) {
2336 					/*
2337 					 * Prevent obvious segfault by ignoring missing
2338 					 * recurrency ids. Real problem might be elsewhere,
2339 					 * but anything is better than crashing...
2340 					 */
2341 					g_warning ("UID %s: instance RECURRENCE-ID and detached instance RECURRENCE-ID cannot compare", uid);
2342 
2343 					e_cal_component_range_free (instance_recur_id);
2344 					continue;
2345 				}
2346 
2347 				i_rid = convert_to_tt_with_zone (e_cal_component_range_get_datetime (instance_recur_id), tz_cb, tz_cb_data, default_timezone);
2348 
2349 				if (e_cal_component_range_get_kind (recur_id) == E_CAL_COMPONENT_RANGE_SINGLE && i_rid == d_rid) {
2350 					g_object_unref (ci->comp);
2351 					g_clear_object (&ci->start);
2352 					g_clear_object (&ci->end);
2353 					ci->comp = g_object_ref (cid->comp);
2354 					ci->start = i_cal_time_clone (cid->start);
2355 					ci->end = i_cal_time_clone (cid->end);
2356 
2357 					processed = TRUE;
2358 				} else {
2359 					cmp = i_rid == d_rid ? 0 : i_rid < d_rid ? -1 : 1;
2360 					if ((e_cal_component_range_get_kind (recur_id) == E_CAL_COMPONENT_RANGE_THISPRIOR && cmp <= 0) ||
2361 					    (e_cal_component_range_get_kind (recur_id) == E_CAL_COMPONENT_RANGE_THISFUTURE && cmp >= 0)) {
2362 						ECalComponent *comp;
2363 
2364 						comp = e_cal_component_clone (cid->comp);
2365 						e_cal_component_set_recurid (comp, instance_recur_id);
2366 
2367 						/* replace the generated instances */
2368 						g_object_unref (ci->comp);
2369 						ci->comp = comp;
2370 					}
2371 				}
2372 
2373 				e_cal_component_range_free (instance_recur_id);
2374 			}
2375 		}
2376 
2377 		e_cal_component_range_free (recur_id);
2378 
2379 		if (!processed)
2380 			unprocessed_instances = g_slist_prepend (unprocessed_instances, cid);
2381 	}
2382 
2383 	/* add the unprocessed instances
2384 	 * (ie, detached instances with no master object) */
2385 	while (unprocessed_instances != NULL) {
2386 		cid = unprocessed_instances->data;
2387 		ci = comp_instance_new ();
2388 		ci->comp = g_object_ref (cid->comp);
2389 		ci->start = i_cal_time_clone (cid->start);
2390 		ci->end = i_cal_time_clone (cid->end);
2391 		instances = g_slist_append (instances, ci);
2392 
2393 		unprocessed_instances = g_slist_remove (unprocessed_instances, cid);
2394 	}
2395 
2396 	return instances;
2397 }
2398 
2399 static void
generate_instances(ECalClient * client,time_t start,time_t end,GSList * objects,GCancellable * cancellable,ECalRecurInstanceCb cb,gpointer cb_data)2400 generate_instances (ECalClient *client,
2401                     time_t start,
2402                     time_t end,
2403                     GSList *objects,
2404                     GCancellable *cancellable,
2405                     ECalRecurInstanceCb cb,
2406                     gpointer cb_data)
2407 {
2408 	GSList *instances, *detached_instances = NULL;
2409 	GSList *l;
2410 	ECalClientPrivate *priv;
2411 	ICalTimezone *default_zone;
2412 	ICalTime *starttt, *endtt;
2413 
2414 	priv = client->priv;
2415 
2416 	instances = NULL;
2417 
2418 	if (priv->default_zone)
2419 		default_zone = priv->default_zone;
2420 	else
2421 		default_zone = i_cal_timezone_get_utc_timezone ();
2422 
2423 	starttt = i_cal_time_new_from_timet_with_zone (start, FALSE, NULL);
2424 	endtt = i_cal_time_new_from_timet_with_zone (end, FALSE, NULL);
2425 
2426 	for (l = objects; l && !g_cancellable_is_cancelled (cancellable); l = l->next) {
2427 		ECalComponent *comp;
2428 
2429 		comp = l->data;
2430 		if (e_cal_component_is_instance (comp)) {
2431 			struct comp_instance *ci;
2432 			ECalComponentDateTime *dtstart, *dtend;
2433 
2434 			/* keep the detached instances apart */
2435 			ci = comp_instance_new ();
2436 			ci->comp = g_object_ref (comp);
2437 
2438 			dtstart = e_cal_component_get_dtstart (comp);
2439 			dtend = e_cal_component_get_dtend (comp);
2440 
2441 			if (!dtstart || !e_cal_component_datetime_get_value (dtstart)) {
2442 				g_warn_if_reached ();
2443 
2444 				e_cal_component_datetime_free (dtstart);
2445 				e_cal_component_datetime_free (dtend);
2446 				comp_instance_free (ci);
2447 
2448 				continue;
2449 			}
2450 
2451 			ci->start = i_cal_time_clone (e_cal_component_datetime_get_value (dtstart));
2452 			if (e_cal_component_datetime_get_tzid (dtstart)) {
2453 				ICalTimezone *zone;
2454 
2455 				zone = e_cal_client_tzlookup_cb (e_cal_component_datetime_get_tzid (dtstart), client, NULL, NULL);
2456 
2457 				if (zone)
2458 					i_cal_time_set_timezone (ci->start, zone);
2459 			}
2460 
2461 			if (dtend && e_cal_component_datetime_get_value (dtend)) {
2462 				ci->end = i_cal_time_clone (e_cal_component_datetime_get_value (dtend));
2463 
2464 				if (e_cal_component_datetime_get_tzid (dtend)) {
2465 					ICalTimezone *zone;
2466 
2467 					zone = e_cal_client_tzlookup_cb (e_cal_component_datetime_get_tzid (dtend), client, NULL, NULL);
2468 
2469 					if (zone)
2470 						i_cal_time_set_timezone (ci->end, zone);
2471 				}
2472 			} else {
2473 				ci->end = i_cal_time_clone (ci->start);
2474 
2475 				if (i_cal_time_is_date (e_cal_component_datetime_get_value (dtstart)))
2476 					i_cal_time_adjust (ci->end, 1, 0, 0, 0);
2477 			}
2478 
2479 			e_cal_component_datetime_free (dtstart);
2480 			e_cal_component_datetime_free (dtend);
2481 
2482 			if (i_cal_time_compare (ci->start, endtt) <= 0 && i_cal_time_compare (ci->end, starttt) >= 0) {
2483 				detached_instances = g_slist_prepend (detached_instances, ci);
2484 			} else {
2485 				/* it doesn't fit to our time range, thus skip it */
2486 				comp_instance_free (ci);
2487 			}
2488 		} else {
2489 			struct instances_info instances_hold;
2490 
2491 			memset (&instances_hold, 0, sizeof (struct instances_info));
2492 			instances_hold.instances = &instances;
2493 
2494 			e_cal_recur_generate_instances_sync (
2495 				e_cal_component_get_icalcomponent (comp), starttt, endtt, add_instance_cb, &instances_hold,
2496 				e_cal_client_tzlookup_cb, client,
2497 				default_zone, cancellable, NULL);
2498 		}
2499 	}
2500 
2501 	g_slist_free_full (objects, g_object_unref);
2502 
2503 	/* Generate instances and spew them out */
2504 
2505 	if (!g_cancellable_is_cancelled (cancellable)) {
2506 		instances = g_slist_sort (instances, compare_comp_instance);
2507 		instances = process_detached_instances (instances, detached_instances,
2508 			e_cal_client_tzlookup_cb, client, default_zone);
2509 	}
2510 
2511 	for (l = instances; l && !g_cancellable_is_cancelled (cancellable); l = l->next) {
2512 		struct comp_instance *ci;
2513 		gboolean result;
2514 
2515 		ci = l->data;
2516 
2517 		result = (* cb) (e_cal_component_get_icalcomponent (ci->comp), ci->start, ci->end, cb_data, cancellable, NULL);
2518 
2519 		if (!result)
2520 			break;
2521 	}
2522 
2523 	/* Clean up */
2524 
2525 	g_slist_free_full (instances, comp_instance_free);
2526 	g_slist_free_full (detached_instances, comp_instance_free);
2527 	g_clear_object (&starttt);
2528 	g_clear_object (&endtt);
2529 }
2530 
2531 static GSList *
get_objects_sync(ECalClient * client,time_t start,time_t end,const gchar * uid)2532 get_objects_sync (ECalClient *client,
2533                   time_t start,
2534                   time_t end,
2535                   const gchar *uid)
2536 {
2537 	GSList *objects = NULL;
2538 
2539 	/* Generate objects */
2540 	if (uid && *uid) {
2541 		GError *local_error = NULL;
2542 
2543 		e_cal_client_get_objects_for_uid_sync (
2544 			client, uid, &objects, NULL, &local_error);
2545 
2546 		if (local_error != NULL) {
2547 			g_warning (
2548 				"Failed to get recurrence objects "
2549 				"for uid: %s\n", local_error->message);
2550 			g_error_free (local_error);
2551 			return NULL;
2552 		}
2553 	} else {
2554 		gchar *iso_start, *iso_end;
2555 		gchar *query;
2556 
2557 		iso_start = isodate_from_time_t (start);
2558 		if (!iso_start)
2559 			return NULL;
2560 
2561 		iso_end = isodate_from_time_t (end);
2562 		if (!iso_end) {
2563 			g_free (iso_start);
2564 			return NULL;
2565 		}
2566 
2567 		query = g_strdup_printf (
2568 			"(occur-in-time-range? "
2569 			"(make-time \"%s\") (make-time \"%s\"))",
2570 			iso_start, iso_end);
2571 		g_free (iso_start);
2572 		g_free (iso_end);
2573 		if (!e_cal_client_get_object_list_as_comps_sync (
2574 			client, query, &objects, NULL, NULL)) {
2575 			g_free (query);
2576 			return NULL;
2577 		}
2578 		g_free (query);
2579 	}
2580 
2581 	return objects;
2582 }
2583 
2584 struct get_objects_async_data {
2585 	GCancellable *cancellable;
2586 	ECalClient *client;
2587 	time_t start;
2588 	time_t end;
2589 	ECalRecurInstanceCb cb;
2590 	gpointer cb_data;
2591 	GDestroyNotify destroy_cb_data;
2592 	gchar *uid;
2593 	gchar *query;
2594 	guint tries;
2595 	void (* ready_cb) (struct get_objects_async_data *goad, GSList *objects);
2596 	ECalComponent *comp;
2597 };
2598 
2599 static void
free_get_objects_async_data(struct get_objects_async_data * goad)2600 free_get_objects_async_data (struct get_objects_async_data *goad)
2601 {
2602 	if (!goad)
2603 		return;
2604 
2605 	if (goad->cancellable)
2606 		g_object_unref (goad->cancellable);
2607 	if (goad->destroy_cb_data)
2608 		goad->destroy_cb_data (goad->cb_data);
2609 	if (goad->client)
2610 		g_object_unref (goad->client);
2611 	if (goad->comp)
2612 		g_object_unref (goad->comp);
2613 	g_free (goad->query);
2614 	g_free (goad->uid);
2615 	g_slice_free (struct get_objects_async_data, goad);
2616 }
2617 
2618 static void
got_objects_for_uid_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2619 got_objects_for_uid_cb (GObject *source_object,
2620                         GAsyncResult *result,
2621                         gpointer user_data)
2622 {
2623 	struct get_objects_async_data *goad = user_data;
2624 	GSList *objects = NULL;
2625 	GError *local_error = NULL;
2626 
2627 	g_return_if_fail (source_object != NULL);
2628 	g_return_if_fail (result != NULL);
2629 	g_return_if_fail (goad != NULL);
2630 	g_return_if_fail (goad->client == E_CAL_CLIENT (source_object));
2631 
2632 	e_cal_client_get_objects_for_uid_finish (
2633 		goad->client, result, &objects, &local_error);
2634 
2635 	if (local_error != NULL) {
2636 		if (g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
2637 		    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2638 			free_get_objects_async_data (goad);
2639 			g_error_free (local_error);
2640 			return;
2641 		}
2642 
2643 		g_clear_error (&local_error);
2644 		objects = NULL;
2645 	}
2646 
2647 	g_return_if_fail (goad->ready_cb != NULL);
2648 
2649 	/* takes care of the objects and goad */
2650 	goad->ready_cb (goad, objects);
2651 }
2652 
2653 static void
got_object_list_as_comps_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2654 got_object_list_as_comps_cb (GObject *source_object,
2655                              GAsyncResult *result,
2656                              gpointer user_data)
2657 {
2658 	struct get_objects_async_data *goad = user_data;
2659 	GSList *objects = NULL;
2660 	GError *local_error = NULL;
2661 
2662 	g_return_if_fail (source_object != NULL);
2663 	g_return_if_fail (result != NULL);
2664 	g_return_if_fail (goad != NULL);
2665 	g_return_if_fail (goad->client == E_CAL_CLIENT (source_object));
2666 
2667 	e_cal_client_get_object_list_as_comps_finish (
2668 		goad->client, result, &objects, &local_error);
2669 
2670 	if (local_error != NULL) {
2671 		if (g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
2672 		    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2673 			free_get_objects_async_data (goad);
2674 			g_error_free (local_error);
2675 			return;
2676 		}
2677 
2678 		g_clear_error (&local_error);
2679 		objects = NULL;
2680 	}
2681 
2682 	g_return_if_fail (goad->ready_cb != NULL);
2683 
2684 	/* takes care of the objects and goad */
2685 	goad->ready_cb (goad, objects);
2686 }
2687 
2688 /* ready_cb may take care of both arguments, goad and objects;
2689  * objects can be also NULL */
2690 static void
get_objects_async(void (* ready_cb)(struct get_objects_async_data * goad,GSList * objects),struct get_objects_async_data * goad)2691 get_objects_async (void (*ready_cb) (struct get_objects_async_data *goad,
2692                                      GSList *objects),
2693                    struct get_objects_async_data *goad)
2694 {
2695 	g_return_if_fail (ready_cb != NULL);
2696 	g_return_if_fail (goad != NULL);
2697 
2698 	goad->ready_cb = ready_cb;
2699 
2700 	if (goad->uid && *goad->uid) {
2701 		e_cal_client_get_objects_for_uid (
2702 			goad->client, goad->uid, goad->cancellable,
2703 			got_objects_for_uid_cb, goad);
2704 	} else {
2705 		gchar *iso_start, *iso_end;
2706 
2707 		iso_start = isodate_from_time_t (goad->start);
2708 		if (!iso_start) {
2709 			free_get_objects_async_data (goad);
2710 			return;
2711 		}
2712 
2713 		iso_end = isodate_from_time_t (goad->end);
2714 		if (!iso_end) {
2715 			g_free (iso_start);
2716 			free_get_objects_async_data (goad);
2717 			return;
2718 		}
2719 
2720 		goad->query = g_strdup_printf (
2721 			"(occur-in-time-range? "
2722 			"(make-time \"%s\") (make-time \"%s\"))",
2723 			iso_start, iso_end);
2724 
2725 		g_free (iso_start);
2726 		g_free (iso_end);
2727 
2728 		e_cal_client_get_object_list_as_comps (
2729 			goad->client, goad->query, goad->cancellable,
2730 			got_object_list_as_comps_cb, goad);
2731 	}
2732 }
2733 
2734 static void
generate_instances_got_objects_cb(struct get_objects_async_data * goad,GSList * objects)2735 generate_instances_got_objects_cb (struct get_objects_async_data *goad,
2736                                    GSList *objects)
2737 {
2738 	g_return_if_fail (goad != NULL);
2739 
2740 	/* generate_instaces () frees 'objects' slist */
2741 	if (objects)
2742 		generate_instances (
2743 			goad->client, goad->start, goad->end, objects,
2744 			goad->cancellable, goad->cb, goad->cb_data);
2745 
2746 	free_get_objects_async_data (goad);
2747 }
2748 
2749 /**
2750  * e_cal_client_generate_instances:
2751  * @client: A calendar client.
2752  * @start: Start time for query.
2753  * @end: End time for query.
2754  * @cancellable: a #GCancellable; can be %NULL
2755  * @cb: Callback for each generated instance.
2756  * @cb_data: Closure data for the callback.
2757  * @destroy_cb_data: Function to call when the processing is done, to free
2758  *                   @cb_data; can be %NULL.
2759  *
2760  * Does a combination of e_cal_client_get_object_list() and
2761  * e_cal_recur_generate_instances_sync(). Unlike
2762  * e_cal_client_generate_instances_sync(), this returns immediately and the
2763  * @cb callback is called asynchronously.
2764  *
2765  * The callback function should do a g_object_ref() of the calendar component
2766  * it gets passed if it intends to keep it around, since it will be unref'ed
2767  * as soon as the callback returns.
2768  *
2769  * Since: 3.2
2770  **/
2771 void
e_cal_client_generate_instances(ECalClient * client,time_t start,time_t end,GCancellable * cancellable,ECalRecurInstanceCb cb,gpointer cb_data,GDestroyNotify destroy_cb_data)2772 e_cal_client_generate_instances (ECalClient *client,
2773                                  time_t start,
2774                                  time_t end,
2775                                  GCancellable *cancellable,
2776                                  ECalRecurInstanceCb cb,
2777                                  gpointer cb_data,
2778                                  GDestroyNotify destroy_cb_data)
2779 {
2780 	struct get_objects_async_data *goad;
2781 	GCancellable *use_cancellable;
2782 
2783 	g_return_if_fail (E_IS_CAL_CLIENT (client));
2784 
2785 	g_return_if_fail (start >= 0);
2786 	g_return_if_fail (end >= 0);
2787 	g_return_if_fail (cb != NULL);
2788 
2789 	use_cancellable = cancellable;
2790 	if (!use_cancellable)
2791 		use_cancellable = g_cancellable_new ();
2792 
2793 	goad = g_slice_new0 (struct get_objects_async_data);
2794 	goad->cancellable = g_object_ref (use_cancellable);
2795 	goad->client = g_object_ref (client);
2796 	goad->start = start;
2797 	goad->end = end;
2798 	goad->cb = cb;
2799 	goad->cb_data = cb_data;
2800 	goad->destroy_cb_data = destroy_cb_data;
2801 
2802 	get_objects_async (generate_instances_got_objects_cb, goad);
2803 
2804 	if (use_cancellable != cancellable)
2805 		g_object_unref (use_cancellable);
2806 }
2807 
2808 /**
2809  * e_cal_client_generate_instances_sync:
2810  * @client: A calendar client
2811  * @start: Start time for query
2812  * @end: End time for query
2813  * @cancellable: a #GCancellable; can be %NULL
2814  * @cb: (closure cb_data) (scope call): Callback for each generated instance
2815  * @cb_data: (closure): Closure data for the callback
2816  *
2817  * Does a combination of e_cal_client_get_object_list() and
2818  * e_cal_recur_generate_instances_sync().
2819  *
2820  * The callback function should do a g_object_ref() of the calendar component
2821  * it gets passed if it intends to keep it around, since it will be unreffed
2822  * as soon as the callback returns.
2823  *
2824  * Since: 3.2
2825  **/
2826 void
e_cal_client_generate_instances_sync(ECalClient * client,time_t start,time_t end,GCancellable * cancellable,ECalRecurInstanceCb cb,gpointer cb_data)2827 e_cal_client_generate_instances_sync (ECalClient *client,
2828                                       time_t start,
2829                                       time_t end,
2830 				      GCancellable *cancellable,
2831                                       ECalRecurInstanceCb cb,
2832                                       gpointer cb_data)
2833 {
2834 	GSList *objects = NULL;
2835 
2836 	g_return_if_fail (E_IS_CAL_CLIENT (client));
2837 
2838 	g_return_if_fail (start >= 0);
2839 	g_return_if_fail (end >= 0);
2840 	g_return_if_fail (cb != NULL);
2841 
2842 	objects = get_objects_sync (client, start, end, NULL);
2843 	if (!objects)
2844 		return;
2845 
2846 	/* generate_instaces frees 'objects' slist */
2847 	generate_instances (client, start, end, objects, cancellable, cb, cb_data);
2848 }
2849 
2850 /* also frees 'instances' GSList */
2851 static void
process_instances(ECalClient * client,ECalComponent * comp,GSList * instances,ECalRecurInstanceCb cb,gpointer cb_data)2852 process_instances (ECalClient *client,
2853 		   ECalComponent *comp,
2854                    GSList *instances,
2855                    ECalRecurInstanceCb cb,
2856                    gpointer cb_data)
2857 {
2858 	gchar *rid;
2859 	gboolean result;
2860 
2861 	g_return_if_fail (E_IS_CAL_CLIENT (client));
2862 	g_return_if_fail (comp != NULL);
2863 	g_return_if_fail (cb != NULL);
2864 
2865 	rid = e_cal_component_get_recurid_as_string (comp);
2866 
2867 	/* Reverse the instances list because the add_instance_cb() function
2868 	 * is prepending. */
2869 	instances = g_slist_reverse (instances);
2870 
2871 	/* now only return back the instances for the given object */
2872 	result = TRUE;
2873 	while (instances != NULL) {
2874 		struct comp_instance *ci;
2875 		gchar *instance_rid = NULL;
2876 
2877 		ci = instances->data;
2878 
2879 		if (result) {
2880 			instance_rid = e_cal_component_get_recurid_as_string (ci->comp);
2881 
2882 			if (rid && *rid) {
2883 				if (instance_rid && *instance_rid && strcmp (rid, instance_rid) == 0) {
2884 					result = (* cb) (e_cal_component_get_icalcomponent (ci->comp), ci->start, ci->end, cb_data, NULL, NULL);
2885 				}
2886 			} else {
2887 				result = (* cb) (e_cal_component_get_icalcomponent (ci->comp), ci->start, ci->end, cb_data, NULL, NULL);
2888 			}
2889 		}
2890 
2891 		/* remove instance from list */
2892 		instances = g_slist_remove (instances, ci);
2893 		comp_instance_free (ci);
2894 		g_free (instance_rid);
2895 	}
2896 
2897 	/* clean up */
2898 	g_free (rid);
2899 }
2900 
2901 static void
generate_instances_for_object_got_objects_cb(struct get_objects_async_data * goad,GSList * objects)2902 generate_instances_for_object_got_objects_cb (struct get_objects_async_data *goad,
2903                                               GSList *objects)
2904 {
2905 	struct instances_info instances_hold;
2906 	GSList *instances = NULL;
2907 
2908 	g_return_if_fail (goad != NULL);
2909 
2910 	memset (&instances_hold, 0, sizeof (struct instances_info));
2911 	instances_hold.instances = &instances;
2912 
2913 	/* generate all instances in the given time range */
2914 	generate_instances (
2915 		goad->client, goad->start, goad->end, objects,
2916 		goad->cancellable, add_instance_cb, &instances_hold);
2917 
2918 	/* it also frees 'instances' GSList */
2919 	process_instances (
2920 		goad->client, goad->comp, *(instances_hold.instances),
2921 		goad->cb, goad->cb_data);
2922 
2923 	/* clean up */
2924 	free_get_objects_async_data (goad);
2925 }
2926 
2927 /**
2928  * e_cal_client_generate_instances_for_object:
2929  * @client: A calendar client.
2930  * @icalcomp: Object to generate instances from.
2931  * @start: Start time for query.
2932  * @end: End time for query.
2933  * @cancellable: a #GCancellable; can be %NULL
2934  * @cb: Callback for each generated instance.
2935  * @cb_data: Closure data for the callback.
2936  * @destroy_cb_data: Function to call when the processing is done, to
2937  *                   free @cb_data; can be %NULL.
2938  *
2939  * Does a combination of e_cal_client_get_object_list() and
2940  * e_cal_recur_generate_instances_sync(), like
2941  * e_cal_client_generate_instances(), but for a single object. Unlike
2942  * e_cal_client_generate_instances_for_object_sync(), this returns immediately
2943  * and the @cb callback is called asynchronously.
2944  *
2945  * The callback function should do a g_object_ref() of the calendar component
2946  * it gets passed if it intends to keep it around, since it will be unref'ed
2947  * as soon as the callback returns.
2948  *
2949  * Since: 3.2
2950  **/
2951 void
e_cal_client_generate_instances_for_object(ECalClient * client,ICalComponent * icalcomp,time_t start,time_t end,GCancellable * cancellable,ECalRecurInstanceCb cb,gpointer cb_data,GDestroyNotify destroy_cb_data)2952 e_cal_client_generate_instances_for_object (ECalClient *client,
2953                                             ICalComponent *icalcomp,
2954                                             time_t start,
2955                                             time_t end,
2956                                             GCancellable *cancellable,
2957                                             ECalRecurInstanceCb cb,
2958                                             gpointer cb_data,
2959                                             GDestroyNotify destroy_cb_data)
2960 {
2961 	ECalComponent *comp;
2962 	const gchar *uid;
2963 	struct get_objects_async_data *goad;
2964 	GCancellable *use_cancellable;
2965 
2966 	g_return_if_fail (E_IS_CAL_CLIENT (client));
2967 
2968 	g_return_if_fail (start >= 0);
2969 	g_return_if_fail (end >= 0);
2970 	g_return_if_fail (cb != NULL);
2971 
2972 	/* If the backend stores it as individual instances and does not
2973 	 * have a master object - do not expand */
2974 	if (!e_cal_util_component_has_recurrences (icalcomp) || e_client_check_capability (E_CLIENT (client), E_CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER)) {
2975 		ICalTime *dtstart, *dtend;
2976 
2977 		dtstart = i_cal_component_get_dtstart (icalcomp);
2978 		dtend = i_cal_component_get_dtend (icalcomp);
2979 
2980 		/* return the same instance */
2981 		(* cb)  (icalcomp, dtstart, dtend, cb_data, NULL, NULL);
2982 
2983 		g_clear_object (&dtstart);
2984 		g_clear_object (&dtend);
2985 
2986 		if (destroy_cb_data)
2987 			destroy_cb_data (cb_data);
2988 		return;
2989 	}
2990 
2991 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icalcomp));
2992 	g_return_if_fail (comp != NULL);
2993 
2994 	uid = e_cal_component_get_uid (comp);
2995 
2996 	use_cancellable = cancellable;
2997 	if (!use_cancellable)
2998 		use_cancellable = g_cancellable_new ();
2999 
3000 	goad = g_slice_new0 (struct get_objects_async_data);
3001 	goad->cancellable = g_object_ref (use_cancellable);
3002 	goad->client = g_object_ref (client);
3003 	goad->start = start;
3004 	goad->end = end;
3005 	goad->cb = cb;
3006 	goad->cb_data = cb_data;
3007 	goad->destroy_cb_data = destroy_cb_data;
3008 	goad->comp = comp;
3009 	goad->uid = g_strdup (uid);
3010 
3011 	get_objects_async (generate_instances_for_object_got_objects_cb, goad);
3012 
3013 	if (use_cancellable != cancellable)
3014 		g_object_unref (use_cancellable);
3015 }
3016 
3017 /**
3018  * e_cal_client_generate_instances_for_object_sync:
3019  * @client: A calendar client
3020  * @icalcomp: Object to generate instances from
3021  * @start: Start time for query
3022  * @end: End time for query
3023  * @cancellable: a #GCancellable; can be %NULL
3024  * @cb: (closure cb_data) (scope call): Callback for each generated instance
3025  * @cb_data: (closure): Closure data for the callback
3026  *
3027  * Does a combination of e_cal_client_get_object_list() and
3028  * e_cal_recur_generate_instances_sync(), like
3029  * e_cal_client_generate_instances_sync(), but for a single object.
3030  *
3031  * The callback function should do a g_object_ref() of the calendar component
3032  * it gets passed if it intends to keep it around, since it will be unref'ed
3033  * as soon as the callback returns.
3034  *
3035  * Since: 3.2
3036  **/
3037 void
e_cal_client_generate_instances_for_object_sync(ECalClient * client,ICalComponent * icalcomp,time_t start,time_t end,GCancellable * cancellable,ECalRecurInstanceCb cb,gpointer cb_data)3038 e_cal_client_generate_instances_for_object_sync (ECalClient *client,
3039                                                  ICalComponent *icalcomp,
3040                                                  time_t start,
3041                                                  time_t end,
3042 						 GCancellable *cancellable,
3043                                                  ECalRecurInstanceCb cb,
3044                                                  gpointer cb_data)
3045 {
3046 	ECalComponent *comp;
3047 	const gchar *uid;
3048 	GSList *instances = NULL;
3049 	struct instances_info instances_hold;
3050 
3051 	g_return_if_fail (E_IS_CAL_CLIENT (client));
3052 
3053 	g_return_if_fail (start >= 0);
3054 	g_return_if_fail (end >= 0);
3055 	g_return_if_fail (cb != NULL);
3056 
3057 	/* If the backend stores it as individual instances and does not
3058 	 * have a master object - do not expand */
3059 	if (!e_cal_util_component_has_recurrences (icalcomp) || e_client_check_capability (E_CLIENT (client), E_CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER)) {
3060 		ICalTime *dtstart, *dtend;
3061 
3062 		dtstart = i_cal_component_get_dtstart (icalcomp);
3063 		dtend = i_cal_component_get_dtend (icalcomp);
3064 
3065 		/* return the same instance */
3066 		(* cb)  (icalcomp, dtstart, dtend, cb_data, cancellable, NULL);
3067 
3068 		g_clear_object (&dtstart);
3069 		g_clear_object (&dtend);
3070 
3071 		return;
3072 	}
3073 
3074 	comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icalcomp));
3075 	g_return_if_fail (comp != NULL);
3076 
3077 	uid = e_cal_component_get_uid (comp);
3078 
3079 	memset (&instances_hold, 0, sizeof (struct instances_info));
3080 	instances_hold.instances = &instances;
3081 
3082 	/* generate all instances in the given time range */
3083 	generate_instances (
3084 		client, start, end,
3085 		get_objects_sync (client, start, end, uid),
3086 		cancellable, add_instance_cb, &instances_hold);
3087 
3088 	/* it also frees 'instances' GSList */
3089 	process_instances (client, comp, *(instances_hold.instances), cb, cb_data);
3090 
3091 	/* clean up */
3092 	g_object_unref (comp);
3093 }
3094 
3095 typedef struct _ForeachTZIDCallbackData ForeachTZIDCallbackData;
3096 struct _ForeachTZIDCallbackData {
3097 	ECalClient *client;
3098 	ICalComponent *icalcomp;
3099 	GHashTable *timezone_hash;
3100 	gboolean success;
3101 };
3102 
3103 /* This adds the VTIMEZONE given by the TZID parameter to the GHashTable in
3104  * data. */
3105 static void
foreach_tzid_callback(ICalParameter * param,gpointer cbdata)3106 foreach_tzid_callback (ICalParameter *param,
3107                        gpointer cbdata)
3108 {
3109 	ForeachTZIDCallbackData *data = cbdata;
3110 	const gchar *tzid;
3111 	ICalTimezone *zone = NULL;
3112 	ICalComponent *vtimezone_comp, *vtimezone_clone;
3113 	gchar *vtimezone_as_string;
3114 
3115 	/* Get the TZID string from the parameter. */
3116 	tzid = i_cal_parameter_get_tzid (param);
3117 	if (!tzid)
3118 		return;
3119 
3120 	/* Check if we've already added it to the GHashTable. */
3121 	if (g_hash_table_lookup (data->timezone_hash, tzid))
3122 		return;
3123 
3124 	if (!e_cal_client_get_timezone_sync (data->client, tzid, &zone, NULL, NULL) || !zone) {
3125 		data->success = FALSE;
3126 		return;
3127 	}
3128 
3129 	/* Convert it to a string and add it to the hash. */
3130 	vtimezone_comp = i_cal_timezone_get_component (zone);
3131 	if (!vtimezone_comp)
3132 		return;
3133 
3134 	vtimezone_clone = i_cal_component_clone (vtimezone_comp);
3135 	e_cal_util_clamp_vtimezone_by_component (vtimezone_clone, data->icalcomp);
3136 
3137 	vtimezone_as_string = i_cal_component_as_ical_string (vtimezone_clone);
3138 
3139 	g_hash_table_insert (data->timezone_hash, (gchar *) tzid, vtimezone_as_string);
3140 
3141 	g_clear_object (&vtimezone_clone);
3142 }
3143 
3144 /* This appends the value string to the GString given in data. */
3145 static void
append_timezone_string(gpointer key,gpointer value,gpointer data)3146 append_timezone_string (gpointer key,
3147                         gpointer value,
3148                         gpointer data)
3149 {
3150 	GString *vcal_string = data;
3151 
3152 	g_string_append (vcal_string, value);
3153 }
3154 
3155 /**
3156  * e_cal_client_get_component_as_string:
3157  * @client: A calendar client.
3158  * @icalcomp: A calendar component object.
3159  *
3160  * Gets a calendar component as an iCalendar string, with a toplevel
3161  * VCALENDAR component and all VTIMEZONEs needed for the component.
3162  *
3163  * Returns: (nullable): the component as a complete iCalendar string, or NULL on
3164  * failure. The string should be freed with g_free().
3165  *
3166  * Since: 3.2
3167  **/
3168 gchar *
e_cal_client_get_component_as_string(ECalClient * client,ICalComponent * icalcomp)3169 e_cal_client_get_component_as_string (ECalClient *client,
3170                                       ICalComponent *icalcomp)
3171 {
3172 	GHashTable *timezone_hash;
3173 	GString *vcal_string;
3174 	ForeachTZIDCallbackData cbdata;
3175 	gchar *obj_string;
3176 
3177 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), NULL);
3178 	g_return_val_if_fail (icalcomp != NULL, NULL);
3179 
3180 	timezone_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
3181 
3182 	/* Add any timezones needed to the hash. We use a hash since we only
3183 	 * want to add each timezone once at most. */
3184 	cbdata.client = client;
3185 	cbdata.icalcomp = icalcomp;
3186 	cbdata.timezone_hash = timezone_hash;
3187 	cbdata.success = TRUE;
3188 	i_cal_component_foreach_tzid (icalcomp, foreach_tzid_callback, &cbdata);
3189 	if (!cbdata.success) {
3190 		g_hash_table_destroy (timezone_hash);
3191 		return NULL;
3192 	}
3193 
3194 	/* Create the start of a VCALENDAR, to add the VTIMEZONES to,
3195 	 * and remember its length so we know if any VTIMEZONEs get added. */
3196 	vcal_string = g_string_new (NULL);
3197 	g_string_append (
3198 		vcal_string,
3199 		"BEGIN:VCALENDAR\r\n"
3200 		"PRODID:-//Ximian//NONSGML Evolution Calendar//EN\r\n"
3201 		"VERSION:2.0\r\n"
3202 		"METHOD:PUBLISH\r\n");
3203 
3204 	/* Now concatenate all the timezone strings. This also frees the
3205 	 * timezone strings as it goes. */
3206 	g_hash_table_foreach (timezone_hash, append_timezone_string, vcal_string);
3207 
3208 	/* Get the string for the VEVENT/VTODO. */
3209 	obj_string = i_cal_component_as_ical_string (icalcomp);
3210 
3211 	/* If there were any timezones to send, create a complete VCALENDAR,
3212 	 * else just send the VEVENT/VTODO string. */
3213 	g_string_append (vcal_string, obj_string);
3214 	g_string_append (vcal_string, "END:VCALENDAR\r\n");
3215 	g_free (obj_string);
3216 
3217 	g_hash_table_destroy (timezone_hash);
3218 
3219 	return g_string_free (vcal_string, FALSE);
3220 }
3221 
3222 /* Helper for e_cal_client_get_default_object() */
3223 static void
cal_client_get_default_object_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3224 cal_client_get_default_object_thread (GSimpleAsyncResult *simple,
3225                                       GObject *source_object,
3226                                       GCancellable *cancellable)
3227 {
3228 	AsyncContext *async_context;
3229 	GError *local_error = NULL;
3230 
3231 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3232 
3233 	if (!e_cal_client_get_default_object_sync (
3234 		E_CAL_CLIENT (source_object),
3235 		&async_context->out_comp,
3236 		cancellable, &local_error)) {
3237 
3238 		if (!local_error)
3239 			local_error = g_error_new_literal (
3240 				E_CLIENT_ERROR,
3241 				E_CLIENT_ERROR_OTHER_ERROR,
3242 				_("Unknown error"));
3243 	}
3244 
3245 	if (local_error != NULL)
3246 		g_simple_async_result_take_error (simple, local_error);
3247 }
3248 
3249 /**
3250  * e_cal_client_get_default_object:
3251  * @client: an #ECalClient
3252  * @cancellable: a #GCancellable; can be %NULL
3253  * @callback: callback to call when a result is ready
3254  * @user_data: user data for the @callback
3255  *
3256  * Retrives an #ICalComponent from the backend that contains the default
3257  * values for properties needed. The call is finished
3258  * by e_cal_client_get_default_object_finish() from the @callback.
3259  *
3260  * Since: 3.2
3261  **/
3262 void
e_cal_client_get_default_object(ECalClient * client,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3263 e_cal_client_get_default_object (ECalClient *client,
3264                                  GCancellable *cancellable,
3265                                  GAsyncReadyCallback callback,
3266                                  gpointer user_data)
3267 {
3268 	GSimpleAsyncResult *simple;
3269 	AsyncContext *async_context;
3270 
3271 	g_return_if_fail (E_IS_CAL_CLIENT (client));
3272 
3273 	async_context = g_slice_new0 (AsyncContext);
3274 
3275 	simple = g_simple_async_result_new (
3276 		G_OBJECT (client), callback, user_data,
3277 		e_cal_client_get_default_object);
3278 
3279 	g_simple_async_result_set_check_cancellable (simple, cancellable);
3280 
3281 	g_simple_async_result_set_op_res_gpointer (
3282 		simple, async_context, (GDestroyNotify) async_context_free);
3283 
3284 	g_simple_async_result_run_in_thread (
3285 		simple, cal_client_get_default_object_thread,
3286 		G_PRIORITY_DEFAULT, cancellable);
3287 
3288 	g_object_unref (simple);
3289 }
3290 
3291 /**
3292  * e_cal_client_get_default_object_finish:
3293  * @client: an #ECalClient
3294  * @result: a #GAsyncResult
3295  * @out_icalcomp: (out) (transfer full): Return value for the default calendar object.
3296  * @error: a #GError to set an error, if any
3297  *
3298  * Finishes previous call of e_cal_client_get_default_object() and
3299  * sets @out_icalcomp to an #ICalComponent from the backend that contains
3300  * the default values for properties needed. This @out_icalcomp should be
3301  * freed with g_object_unref(), when no longer needed.
3302  *
3303  * Returns: %TRUE if successful, %FALSE otherwise.
3304  *
3305  * Since: 3.2
3306  **/
3307 gboolean
e_cal_client_get_default_object_finish(ECalClient * client,GAsyncResult * result,ICalComponent ** out_icalcomp,GError ** error)3308 e_cal_client_get_default_object_finish (ECalClient *client,
3309                                         GAsyncResult *result,
3310                                         ICalComponent **out_icalcomp,
3311                                         GError **error)
3312 {
3313 	GSimpleAsyncResult *simple;
3314 	AsyncContext *async_context;
3315 
3316 	g_return_val_if_fail (
3317 		g_simple_async_result_is_valid (
3318 		result, G_OBJECT (client),
3319 		e_cal_client_get_default_object), FALSE);
3320 
3321 	simple = G_SIMPLE_ASYNC_RESULT (result);
3322 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3323 
3324 	if (g_simple_async_result_propagate_error (simple, error))
3325 		return FALSE;
3326 
3327 	g_return_val_if_fail (async_context->out_comp != NULL, FALSE);
3328 
3329 	if (out_icalcomp != NULL) {
3330 		*out_icalcomp = async_context->out_comp;
3331 		async_context->out_comp = NULL;
3332 	}
3333 
3334 	return TRUE;
3335 }
3336 
3337 /**
3338  * e_cal_client_get_default_object_sync:
3339  * @client: an #ECalClient
3340  * @out_icalcomp: (out) (transfer full): Return value for the default calendar object.
3341  * @cancellable: a #GCancellable; can be %NULL
3342  * @error: a #GError to set an error, if any
3343  *
3344  * Retrives an #ICalComponent from the backend that contains the default
3345  * values for properties needed. This @out_icalcomp should be freed with
3346  * g_object_unref(), when no longer needed.
3347  *
3348  * Returns: %TRUE if successful, %FALSE otherwise.
3349  *
3350  * Since: 3.2
3351  **/
3352 gboolean
e_cal_client_get_default_object_sync(ECalClient * client,ICalComponent ** out_icalcomp,GCancellable * cancellable,GError ** error)3353 e_cal_client_get_default_object_sync (ECalClient *client,
3354                                       ICalComponent **out_icalcomp,
3355                                       GCancellable *cancellable,
3356                                       GError **error)
3357 {
3358 	ICalComponent *icalcomp = NULL;
3359 	gchar *string;
3360 
3361 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
3362 	g_return_val_if_fail (out_icalcomp != NULL, FALSE);
3363 
3364 	string = e_dbus_calendar_dup_default_object (client->priv->dbus_proxy);
3365 	if (string != NULL) {
3366 		icalcomp = i_cal_parser_parse_string (string);
3367 		g_free (string);
3368 	}
3369 
3370 	if (icalcomp == NULL) {
3371 		g_set_error_literal (
3372 			error, E_CAL_CLIENT_ERROR,
3373 			E_CAL_CLIENT_ERROR_INVALID_OBJECT,
3374 			e_cal_client_error_to_string (
3375 			E_CAL_CLIENT_ERROR_INVALID_OBJECT));
3376 		return FALSE;
3377 	}
3378 
3379 	if (i_cal_component_get_uid (icalcomp) != NULL) {
3380 		gchar *new_uid;
3381 
3382 		/* Make sure the UID is always unique. */
3383 		new_uid = e_util_generate_uid ();
3384 		i_cal_component_set_uid (icalcomp, new_uid);
3385 		g_free (new_uid);
3386 	}
3387 
3388 	*out_icalcomp = icalcomp;
3389 
3390 	return TRUE;
3391 }
3392 
3393 /* Helper for e_cal_client_get_object() */
3394 static void
cal_client_get_object_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3395 cal_client_get_object_thread (GSimpleAsyncResult *simple,
3396                               GObject *source_object,
3397                               GCancellable *cancellable)
3398 {
3399 	AsyncContext *async_context;
3400 	GError *local_error = NULL;
3401 
3402 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3403 
3404 	if (!e_cal_client_get_object_sync (
3405 		E_CAL_CLIENT (source_object),
3406 		async_context->uid,
3407 		async_context->rid,
3408 		&async_context->out_comp,
3409 		cancellable, &local_error)) {
3410 
3411 		if (!local_error)
3412 			local_error = g_error_new_literal (
3413 				E_CLIENT_ERROR,
3414 				E_CLIENT_ERROR_OTHER_ERROR,
3415 				_("Unknown error"));
3416 	}
3417 
3418 	if (local_error != NULL)
3419 		g_simple_async_result_take_error (simple, local_error);
3420 }
3421 
3422 /**
3423  * e_cal_client_get_object:
3424  * @client: an #ECalClient
3425  * @uid: Unique identifier for a calendar component.
3426  * @rid: (nullable): Recurrence identifier.
3427  * @cancellable: a #GCancellable; can be %NULL
3428  * @callback: callback to call when a result is ready
3429  * @user_data: user data for the @callback
3430  *
3431  * Queries a calendar for a calendar component object based on its unique
3432  * identifier. The call is finished by e_cal_client_get_object_finish()
3433  * from the @callback.
3434  *
3435  * Use e_cal_client_get_objects_for_uid() to get list of all
3436  * objects for the given uid, which includes master object and
3437  * all detached instances.
3438  *
3439  * Since: 3.2
3440  **/
3441 void
e_cal_client_get_object(ECalClient * client,const gchar * uid,const gchar * rid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3442 e_cal_client_get_object (ECalClient *client,
3443                          const gchar *uid,
3444                          const gchar *rid,
3445                          GCancellable *cancellable,
3446                          GAsyncReadyCallback callback,
3447                          gpointer user_data)
3448 {
3449 	GSimpleAsyncResult *simple;
3450 	AsyncContext *async_context;
3451 
3452 	g_return_if_fail (E_IS_CAL_CLIENT (client));
3453 	g_return_if_fail (uid != NULL);
3454 	/* rid is optional */
3455 
3456 	async_context = g_slice_new0 (AsyncContext);
3457 	async_context->uid = g_strdup (uid);
3458 	async_context->rid = g_strdup (rid);
3459 
3460 	simple = g_simple_async_result_new (
3461 		G_OBJECT (client), callback, user_data,
3462 		e_cal_client_get_object);
3463 
3464 	g_simple_async_result_set_check_cancellable (simple, cancellable);
3465 
3466 	g_simple_async_result_set_op_res_gpointer (
3467 		simple, async_context, (GDestroyNotify) async_context_free);
3468 
3469 	g_simple_async_result_run_in_thread (
3470 		simple, cal_client_get_object_thread,
3471 		G_PRIORITY_DEFAULT, cancellable);
3472 
3473 	g_object_unref (simple);
3474 }
3475 
3476 /**
3477  * e_cal_client_get_object_finish:
3478  * @client: an #ECalClient
3479  * @result: a #GAsyncResult
3480  * @out_icalcomp: (out) (transfer full): Return value for the calendar component object.
3481  * @error: a #GError to set an error, if any
3482  *
3483  * Finishes previous call of e_cal_client_get_object() and
3484  * sets @out_icalcomp to queried component. This function always returns
3485  * master object for a case of @rid being NULL or an empty string.
3486  * This component should be freed with g_object_unref(), when no longer needed.
3487  *
3488  * Use e_cal_client_get_objects_for_uid() to get list of all
3489  * objects for the given uid, which includes master object and
3490  * all detached instances.
3491  *
3492  * Returns: %TRUE if successful, %FALSE otherwise.
3493  *
3494  * Since: 3.2
3495  **/
3496 gboolean
e_cal_client_get_object_finish(ECalClient * client,GAsyncResult * result,ICalComponent ** out_icalcomp,GError ** error)3497 e_cal_client_get_object_finish (ECalClient *client,
3498                                 GAsyncResult *result,
3499                                 ICalComponent **out_icalcomp,
3500                                 GError **error)
3501 {
3502 	GSimpleAsyncResult *simple;
3503 	AsyncContext *async_context;
3504 
3505 	g_return_val_if_fail (
3506 		g_simple_async_result_is_valid (
3507 		result, G_OBJECT (client),
3508 		e_cal_client_get_object), FALSE);
3509 
3510 	simple = G_SIMPLE_ASYNC_RESULT (result);
3511 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3512 
3513 	if (g_simple_async_result_propagate_error (simple, error))
3514 		return FALSE;
3515 
3516 	g_return_val_if_fail (async_context->out_comp != NULL, FALSE);
3517 
3518 	if (out_icalcomp != NULL) {
3519 		*out_icalcomp = async_context->out_comp;
3520 		async_context->out_comp = NULL;
3521 	}
3522 
3523 	return TRUE;
3524 }
3525 
3526 /**
3527  * e_cal_client_get_object_sync:
3528  * @client: an #ECalClient
3529  * @uid: Unique identifier for a calendar component.
3530  * @rid: (nullable): Recurrence identifier.
3531  * @out_icalcomp: (out) (transfer full): Return value for the calendar component object.
3532  * @cancellable: a #GCancellable; can be %NULL
3533  * @error: a #GError to set an error, if any
3534  *
3535  * Queries a calendar for a calendar component object based
3536  * on its unique identifier. This function always returns
3537  * master object for a case of @rid being NULL or an empty string.
3538  * This component should be freed with g_object_unref(),
3539  * when no longer needed.
3540  *
3541  * Use e_cal_client_get_objects_for_uid_sync() to get list of all
3542  * objects for the given uid, which includes master object and
3543  * all detached instances.
3544  *
3545  * Returns: %TRUE if successful, %FALSE otherwise.
3546  *
3547  * Since: 3.2
3548  **/
3549 gboolean
e_cal_client_get_object_sync(ECalClient * client,const gchar * uid,const gchar * rid,ICalComponent ** out_icalcomp,GCancellable * cancellable,GError ** error)3550 e_cal_client_get_object_sync (ECalClient *client,
3551                               const gchar *uid,
3552                               const gchar *rid,
3553                               ICalComponent **out_icalcomp,
3554                               GCancellable *cancellable,
3555                               GError **error)
3556 {
3557 	ICalComponent *icalcomp = NULL;
3558 	ICalComponentKind kind;
3559 	gchar *utf8_uid;
3560 	gchar *utf8_rid;
3561 	gchar *string = NULL;
3562 	GError *local_error = NULL;
3563 
3564 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
3565 	g_return_val_if_fail (uid != NULL, FALSE);
3566 	g_return_val_if_fail (out_icalcomp != NULL, FALSE);
3567 
3568 	*out_icalcomp = NULL;
3569 
3570 	if (rid == NULL)
3571 		rid = "";
3572 
3573 	utf8_uid = e_util_utf8_make_valid (uid);
3574 	utf8_rid = e_util_utf8_make_valid (rid);
3575 
3576 	e_dbus_calendar_call_get_object_sync (
3577 		client->priv->dbus_proxy, utf8_uid, utf8_rid,
3578 		&string, cancellable, &local_error);
3579 
3580 	g_free (utf8_uid);
3581 	g_free (utf8_rid);
3582 
3583 	/* Sanity check. */
3584 	g_return_val_if_fail (
3585 		((string != NULL) && (local_error == NULL)) ||
3586 		((string == NULL) && (local_error != NULL)), FALSE);
3587 
3588 	if (local_error != NULL) {
3589 		g_dbus_error_strip_remote_error (local_error);
3590 		g_propagate_error (error, local_error);
3591 		return FALSE;
3592 	}
3593 
3594 	icalcomp = i_cal_parser_parse_string (string);
3595 
3596 	g_free (string);
3597 
3598 	if (icalcomp == NULL) {
3599 		g_set_error_literal (
3600 			error, E_CAL_CLIENT_ERROR,
3601 			E_CAL_CLIENT_ERROR_INVALID_OBJECT,
3602 			e_cal_client_error_to_string (
3603 			E_CAL_CLIENT_ERROR_INVALID_OBJECT));
3604 		return FALSE;
3605 	}
3606 
3607 	switch (e_cal_client_get_source_type (client)) {
3608 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
3609 			kind = I_CAL_VEVENT_COMPONENT;
3610 			break;
3611 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
3612 			kind = I_CAL_VTODO_COMPONENT;
3613 			break;
3614 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
3615 			kind = I_CAL_VJOURNAL_COMPONENT;
3616 			break;
3617 		default:
3618 			g_warn_if_reached ();
3619 			kind = I_CAL_VEVENT_COMPONENT;
3620 			break;
3621 	}
3622 
3623 	if (i_cal_component_isa (icalcomp) == kind) {
3624 		*out_icalcomp = icalcomp;
3625 		icalcomp = NULL;
3626 
3627 	} else if (i_cal_component_isa (icalcomp) == I_CAL_VCALENDAR_COMPONENT) {
3628 		ICalComponent *subcomponent;
3629 
3630 		for (subcomponent = i_cal_component_get_first_component (icalcomp, kind);
3631 		     subcomponent != NULL;
3632 		     g_object_unref (subcomponent), subcomponent = i_cal_component_get_next_component (icalcomp, kind)) {
3633 			ICalTime *recurrenceid;
3634 
3635 			if (i_cal_component_get_uid (subcomponent) == NULL)
3636 				continue;
3637 
3638 			recurrenceid = i_cal_component_get_recurrenceid (subcomponent);
3639 
3640 			if (!recurrenceid ||
3641 			    i_cal_time_is_null_time (recurrenceid) ||
3642 			    !i_cal_time_is_valid_time (recurrenceid)) {
3643 				g_clear_object (&recurrenceid);
3644 				break;
3645 			}
3646 		}
3647 
3648 		if (subcomponent == NULL)
3649 			subcomponent = i_cal_component_get_first_component (icalcomp, kind);
3650 		if (subcomponent != NULL) {
3651 			ICalComponent *clone;
3652 
3653 			clone = i_cal_component_clone (subcomponent);
3654 			g_object_unref (subcomponent);
3655 			subcomponent = clone;
3656 		}
3657 
3658 		/* XXX Shouldn't we set an error if this is still NULL? */
3659 		*out_icalcomp = subcomponent;
3660 	}
3661 
3662 	g_clear_object (&icalcomp);
3663 
3664 	return TRUE;
3665 }
3666 
3667 /* Helper for e_cal_client_get_objects_for_uid() */
3668 static void
cal_client_get_objects_for_uid_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3669 cal_client_get_objects_for_uid_thread (GSimpleAsyncResult *simple,
3670                                        GObject *source_object,
3671                                        GCancellable *cancellable)
3672 {
3673 	AsyncContext *async_context;
3674 	GError *local_error = NULL;
3675 
3676 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3677 
3678 	if (!e_cal_client_get_objects_for_uid_sync (
3679 		E_CAL_CLIENT (source_object),
3680 		async_context->uid,
3681 		&async_context->object_list,
3682 		cancellable, &local_error)) {
3683 
3684 		if (!local_error)
3685 			local_error = g_error_new_literal (
3686 				E_CLIENT_ERROR,
3687 				E_CLIENT_ERROR_OTHER_ERROR,
3688 				_("Unknown error"));
3689 	}
3690 
3691 	if (local_error != NULL)
3692 		g_simple_async_result_take_error (simple, local_error);
3693 }
3694 
3695 /**
3696  * e_cal_client_get_objects_for_uid:
3697  * @client: an #ECalClient
3698  * @uid: Unique identifier for a calendar component
3699  * @cancellable: a #GCancellable; can be %NULL
3700  * @callback: callback to call when a result is ready
3701  * @user_data: user data for the @callback
3702  *
3703  * Queries a calendar for all calendar components with the given unique
3704  * ID. This will return any recurring event and all its detached recurrences.
3705  * For non-recurring events, it will just return the object with that ID.
3706  * The call is finished by e_cal_client_get_objects_for_uid_finish() from
3707  * the @callback.
3708  *
3709  * Since: 3.2
3710  **/
3711 void
e_cal_client_get_objects_for_uid(ECalClient * client,const gchar * uid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3712 e_cal_client_get_objects_for_uid (ECalClient *client,
3713                                   const gchar *uid,
3714                                   GCancellable *cancellable,
3715                                   GAsyncReadyCallback callback,
3716                                   gpointer user_data)
3717 {
3718 	GSimpleAsyncResult *simple;
3719 	AsyncContext *async_context;
3720 
3721 	g_return_if_fail (E_IS_CAL_CLIENT (client));
3722 	g_return_if_fail (uid != NULL);
3723 
3724 	async_context = g_slice_new0 (AsyncContext);
3725 	async_context->uid = g_strdup (uid);
3726 
3727 	simple = g_simple_async_result_new (
3728 		G_OBJECT (client), callback, user_data,
3729 		e_cal_client_get_objects_for_uid);
3730 
3731 	g_simple_async_result_set_check_cancellable (simple, cancellable);
3732 
3733 	g_simple_async_result_set_op_res_gpointer (
3734 		simple, async_context, (GDestroyNotify) async_context_free);
3735 
3736 	g_simple_async_result_run_in_thread (
3737 		simple, cal_client_get_objects_for_uid_thread,
3738 		G_PRIORITY_DEFAULT, cancellable);
3739 
3740 	g_object_unref (simple);
3741 }
3742 
3743 /**
3744  * e_cal_client_get_objects_for_uid_finish:
3745  * @client: an #ECalClient
3746  * @result: a #GAsyncResult
3747  * @out_ecalcomps: (out) (transfer full) (element-type ECalComponent):
3748  *                 Return location for the list of objects obtained from the
3749  *                 backend
3750  * @error: a #GError to set an error, if any
3751  *
3752  * Finishes previous call of e_cal_client_get_objects_for_uid() and
3753  * sets @out_ecalcomps to a list of #ECalComponent<!-- -->s corresponding to
3754  * found components for a given uid of the same type as this client.
3755  * This list should be freed with e_client_util_free_object_slist().
3756  *
3757  * Returns: %TRUE if successful, %FALSE otherwise.
3758  *
3759  * Since: 3.2
3760  **/
3761 gboolean
e_cal_client_get_objects_for_uid_finish(ECalClient * client,GAsyncResult * result,GSList ** out_ecalcomps,GError ** error)3762 e_cal_client_get_objects_for_uid_finish (ECalClient *client,
3763                                          GAsyncResult *result,
3764                                          GSList **out_ecalcomps,
3765                                          GError **error)
3766 {
3767 	GSimpleAsyncResult *simple;
3768 	AsyncContext *async_context;
3769 
3770 	g_return_val_if_fail (
3771 		g_simple_async_result_is_valid (
3772 		result, G_OBJECT (client),
3773 		e_cal_client_get_objects_for_uid), FALSE);
3774 
3775 	simple = G_SIMPLE_ASYNC_RESULT (result);
3776 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3777 
3778 	if (g_simple_async_result_propagate_error (simple, error))
3779 		return FALSE;
3780 
3781 	if (out_ecalcomps != NULL) {
3782 		*out_ecalcomps = async_context->object_list;
3783 		async_context->object_list = NULL;
3784 	}
3785 
3786 	return TRUE;
3787 }
3788 
3789 /**
3790  * e_cal_client_get_objects_for_uid_sync:
3791  * @client: an #ECalClient
3792  * @uid: Unique identifier for a calendar component
3793  * @out_ecalcomps: (out) (transfer full) (element-type ECalComponent):
3794  *                 Return location for the list of objects obtained from the
3795  *                 backend
3796  * @cancellable: a #GCancellable; can be %NULL
3797  * @error: a #GError to set an error, if any
3798  *
3799  * Queries a calendar for all calendar components with the given unique
3800  * ID. This will return any recurring event and all its detached recurrences.
3801  * For non-recurring events, it will just return the object with that ID.
3802  * This list should be freed with e_client_util_free_object_slist().
3803  *
3804  * Returns: %TRUE if successful, %FALSE otherwise.
3805  *
3806  * Since: 3.2
3807  **/
3808 gboolean
e_cal_client_get_objects_for_uid_sync(ECalClient * client,const gchar * uid,GSList ** out_ecalcomps,GCancellable * cancellable,GError ** error)3809 e_cal_client_get_objects_for_uid_sync (ECalClient *client,
3810                                        const gchar *uid,
3811                                        GSList **out_ecalcomps,
3812                                        GCancellable *cancellable,
3813                                        GError **error)
3814 {
3815 	ICalComponent *icalcomp;
3816 	ICalComponentKind kind;
3817 	gchar *utf8_uid;
3818 	gchar *string = NULL;
3819 	GError *local_error = NULL;
3820 
3821 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
3822 	g_return_val_if_fail (uid != NULL, FALSE);
3823 	g_return_val_if_fail (out_ecalcomps != NULL, FALSE);
3824 
3825 	*out_ecalcomps = NULL;
3826 
3827 	utf8_uid = e_util_utf8_make_valid (uid);
3828 
3829 	e_dbus_calendar_call_get_object_sync (
3830 		client->priv->dbus_proxy, utf8_uid, "",
3831 		&string, cancellable, &local_error);
3832 
3833 	g_free (utf8_uid);
3834 
3835 	/* Sanity check. */
3836 	g_return_val_if_fail (
3837 		((string != NULL) && (local_error == NULL)) ||
3838 		((string == NULL) && (local_error != NULL)), FALSE);
3839 
3840 	if (local_error != NULL) {
3841 		g_dbus_error_strip_remote_error (local_error);
3842 		g_propagate_error (error, local_error);
3843 		return FALSE;
3844 	}
3845 
3846 	icalcomp = i_cal_parser_parse_string (string);
3847 
3848 	g_free (string);
3849 
3850 	if (icalcomp == NULL) {
3851 		g_set_error_literal (
3852 			error, E_CAL_CLIENT_ERROR,
3853 			E_CAL_CLIENT_ERROR_INVALID_OBJECT,
3854 			e_cal_client_error_to_string (
3855 			E_CAL_CLIENT_ERROR_INVALID_OBJECT));
3856 		return FALSE;
3857 	}
3858 
3859 	switch (e_cal_client_get_source_type (client)) {
3860 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
3861 			kind = I_CAL_VEVENT_COMPONENT;
3862 			break;
3863 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
3864 			kind = I_CAL_VTODO_COMPONENT;
3865 			break;
3866 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
3867 			kind = I_CAL_VJOURNAL_COMPONENT;
3868 			break;
3869 		default:
3870 			g_warn_if_reached ();
3871 			kind = I_CAL_VEVENT_COMPONENT;
3872 			break;
3873 	}
3874 
3875 	if (i_cal_component_isa (icalcomp) == kind) {
3876 		ECalComponent *comp;
3877 
3878 		comp = e_cal_component_new_from_icalcomponent (icalcomp);
3879 		icalcomp = NULL;
3880 
3881 		*out_ecalcomps = g_slist_append (NULL, comp);
3882 
3883 	} else if (i_cal_component_isa (icalcomp) == I_CAL_VCALENDAR_COMPONENT) {
3884 		GSList *tmp = NULL;
3885 		ICalComponent *subcomponent;
3886 
3887 		for (subcomponent = i_cal_component_get_first_component (icalcomp, kind);
3888 		     subcomponent;
3889 		     g_object_unref (subcomponent), subcomponent = i_cal_component_get_next_component (icalcomp, kind)) {
3890 			ECalComponent *comp;
3891 
3892 			comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (subcomponent));
3893 			if (comp)
3894 				tmp = g_slist_prepend (tmp, comp);
3895 		}
3896 
3897 		*out_ecalcomps = g_slist_reverse (tmp);
3898 	}
3899 
3900 	g_clear_object (&icalcomp);
3901 
3902 	return TRUE;
3903 }
3904 
3905 /* Helper for e_cal_client_get_object_list() */
3906 static void
cal_client_get_object_list_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3907 cal_client_get_object_list_thread (GSimpleAsyncResult *simple,
3908                                    GObject *source_object,
3909                                    GCancellable *cancellable)
3910 {
3911 	AsyncContext *async_context;
3912 	GError *local_error = NULL;
3913 
3914 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3915 
3916 	if (!e_cal_client_get_object_list_sync (
3917 		E_CAL_CLIENT (source_object),
3918 		async_context->sexp,
3919 		&async_context->comp_list,
3920 		cancellable, &local_error)) {
3921 
3922 		if (!local_error)
3923 			local_error = g_error_new_literal (
3924 				E_CLIENT_ERROR,
3925 				E_CLIENT_ERROR_OTHER_ERROR,
3926 				_("Unknown error"));
3927 	}
3928 
3929 	if (local_error != NULL)
3930 		g_simple_async_result_take_error (simple, local_error);
3931 }
3932 
3933 /**
3934  * e_cal_client_get_object_list:
3935  * @client: an #ECalClient
3936  * @sexp: an S-expression representing the query
3937  * @cancellable: a #GCancellable; can be %NULL
3938  * @callback: callback to call when a result is ready
3939  * @user_data: user data for the @callback
3940  *
3941  * Gets a list of objects from the calendar that match the query specified
3942  * by the @sexp argument, returning matching objects as a list of #ICalComponent-s.
3943  * The call is finished by e_cal_client_get_object_list_finish() from
3944  * the @callback.
3945  *
3946  * Since: 3.2
3947  **/
3948 void
e_cal_client_get_object_list(ECalClient * client,const gchar * sexp,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3949 e_cal_client_get_object_list (ECalClient *client,
3950                               const gchar *sexp,
3951                               GCancellable *cancellable,
3952                               GAsyncReadyCallback callback,
3953                               gpointer user_data)
3954 {
3955 	GSimpleAsyncResult *simple;
3956 	AsyncContext *async_context;
3957 
3958 	g_return_if_fail (E_IS_CAL_CLIENT (client));
3959 	g_return_if_fail (sexp != NULL);
3960 
3961 	async_context = g_slice_new0 (AsyncContext);
3962 	async_context->sexp = g_strdup (sexp);
3963 
3964 	simple = g_simple_async_result_new (
3965 		G_OBJECT (client), callback, user_data,
3966 		e_cal_client_get_object_list);
3967 
3968 	g_simple_async_result_set_check_cancellable (simple, cancellable);
3969 
3970 	g_simple_async_result_set_op_res_gpointer (
3971 		simple, async_context, (GDestroyNotify) async_context_free);
3972 
3973 	g_simple_async_result_run_in_thread (
3974 		simple, cal_client_get_object_list_thread,
3975 		G_PRIORITY_DEFAULT, cancellable);
3976 
3977 	g_object_unref (simple);
3978 }
3979 
3980 /**
3981  * e_cal_client_get_object_list_finish:
3982  * @client: an #ECalClient
3983  * @result: a #GAsyncResult
3984  * @out_icalcomps: (out) (element-type ICalComponent): list of matching
3985  *                 #ICalComponent<!-- -->s
3986  * @error: a #GError to set an error, if any
3987  *
3988  * Finishes previous call of e_cal_client_get_object_list() and
3989  * sets @out_icalcomps to a matching list of #ICalComponent-s.
3990  * This list should be freed with e_client_util_free_object_slist().
3991  *
3992  * Returns: %TRUE if successful, %FALSE otherwise.
3993  *
3994  * Since: 3.2
3995  **/
3996 gboolean
e_cal_client_get_object_list_finish(ECalClient * client,GAsyncResult * result,GSList ** out_icalcomps,GError ** error)3997 e_cal_client_get_object_list_finish (ECalClient *client,
3998                                      GAsyncResult *result,
3999                                      GSList **out_icalcomps,
4000                                      GError **error)
4001 {
4002 	GSimpleAsyncResult *simple;
4003 	AsyncContext *async_context;
4004 
4005 	g_return_val_if_fail (
4006 		g_simple_async_result_is_valid (
4007 		result, G_OBJECT (client),
4008 		e_cal_client_get_object_list), FALSE);
4009 
4010 	simple = G_SIMPLE_ASYNC_RESULT (result);
4011 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4012 
4013 	if (g_simple_async_result_propagate_error (simple, error))
4014 		return FALSE;
4015 
4016 	if (out_icalcomps != NULL) {
4017 		*out_icalcomps = async_context->comp_list;
4018 		async_context->comp_list = NULL;
4019 	}
4020 
4021 	return TRUE;
4022 }
4023 
4024 /**
4025  * e_cal_client_get_object_list_sync:
4026  * @client: an #ECalClient
4027  * @sexp: an S-expression representing the query
4028  * @out_icalcomps: (out) (element-type ICalComponent): list of matching
4029  *                 #ICalComponent<!-- -->s
4030  * @cancellable: a #GCancellable; can be %NULL
4031  * @error: a #GError to set an error, if any
4032  *
4033  * Gets a list of objects from the calendar that match the query specified
4034  * by the @sexp argument. The objects will be returned in the @out_icalcomps
4035  * argument, which is a list of #ICalComponent.
4036  * This list should be freed with e_client_util_free_object_slist().
4037  *
4038  * Returns: %TRUE if successful, %FALSE otherwise.
4039  *
4040  * Since: 3.2
4041  **/
4042 gboolean
e_cal_client_get_object_list_sync(ECalClient * client,const gchar * sexp,GSList ** out_icalcomps,GCancellable * cancellable,GError ** error)4043 e_cal_client_get_object_list_sync (ECalClient *client,
4044                                    const gchar *sexp,
4045                                    GSList **out_icalcomps,
4046                                    GCancellable *cancellable,
4047                                    GError **error)
4048 {
4049 	GSList *tmp = NULL;
4050 	gchar *utf8_sexp;
4051 	gchar **strv = NULL;
4052 	gint ii;
4053 	GError *local_error = NULL;
4054 
4055 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
4056 	g_return_val_if_fail (sexp != NULL, FALSE);
4057 	g_return_val_if_fail (out_icalcomps != NULL, FALSE);
4058 
4059 	*out_icalcomps = NULL;
4060 
4061 	utf8_sexp = e_util_utf8_make_valid (sexp);
4062 
4063 	e_dbus_calendar_call_get_object_list_sync (
4064 		client->priv->dbus_proxy, utf8_sexp,
4065 		&strv, cancellable, &local_error);
4066 
4067 	g_free (utf8_sexp);
4068 
4069 	/* Sanity check. */
4070 	g_return_val_if_fail (
4071 		((strv != NULL) && (local_error == NULL)) ||
4072 		((strv == NULL) && (local_error != NULL)), FALSE);
4073 
4074 	if (local_error != NULL) {
4075 		g_dbus_error_strip_remote_error (local_error);
4076 		g_propagate_error (error, local_error);
4077 		return FALSE;
4078 	}
4079 
4080 	for (ii = 0; strv[ii] != NULL; ii++) {
4081 		ICalComponent *icalcomp;
4082 
4083 		icalcomp = i_cal_component_new_from_string (strv[ii]);
4084 		if (icalcomp == NULL)
4085 			continue;
4086 
4087 		tmp = g_slist_prepend (tmp, icalcomp);
4088 	}
4089 
4090 	*out_icalcomps = g_slist_reverse (tmp);
4091 
4092 	g_strfreev (strv);
4093 
4094 	return TRUE;
4095 }
4096 
4097 /* Helper for e_cal_client_get_object_list_as_comps() */
4098 static void
cal_client_get_object_list_as_comps_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4099 cal_client_get_object_list_as_comps_thread (GSimpleAsyncResult *simple,
4100                                             GObject *source_object,
4101                                             GCancellable *cancellable)
4102 {
4103 	AsyncContext *async_context;
4104 	GError *local_error = NULL;
4105 
4106 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4107 
4108 	if (!e_cal_client_get_object_list_as_comps_sync (
4109 		E_CAL_CLIENT (source_object),
4110 		async_context->sexp,
4111 		&async_context->object_list,
4112 		cancellable, &local_error)) {
4113 
4114 		if (!local_error)
4115 			local_error = g_error_new_literal (
4116 				E_CLIENT_ERROR,
4117 				E_CLIENT_ERROR_OTHER_ERROR,
4118 				_("Unknown error"));
4119 	}
4120 
4121 	if (local_error != NULL)
4122 		g_simple_async_result_take_error (simple, local_error);
4123 }
4124 
4125 /**
4126  * e_cal_client_get_object_list_as_comps:
4127  * @client: an #ECalClient
4128  * @sexp: an S-expression representing the query
4129  * @cancellable: a #GCancellable; can be %NULL
4130  * @callback: callback to call when a result is ready
4131  * @user_data: user data for the @callback
4132  *
4133  * Gets a list of objects from the calendar that match the query specified
4134  * by the @sexp argument, returning matching objects as a list of #ECalComponent-s.
4135  * The call is finished by e_cal_client_get_object_list_as_comps_finish() from
4136  * the @callback.
4137  *
4138  * Since: 3.2
4139  **/
4140 void
e_cal_client_get_object_list_as_comps(ECalClient * client,const gchar * sexp,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4141 e_cal_client_get_object_list_as_comps (ECalClient *client,
4142                                        const gchar *sexp,
4143                                        GCancellable *cancellable,
4144                                        GAsyncReadyCallback callback,
4145                                        gpointer user_data)
4146 {
4147 	GSimpleAsyncResult *simple;
4148 	AsyncContext *async_context;
4149 
4150 	g_return_if_fail (E_IS_CAL_CLIENT (client));
4151 	g_return_if_fail (sexp != NULL);
4152 
4153 	async_context = g_slice_new0 (AsyncContext);
4154 	async_context->sexp = g_strdup (sexp);
4155 
4156 	simple = g_simple_async_result_new (
4157 		G_OBJECT (client), callback, user_data,
4158 		e_cal_client_get_object_list_as_comps);
4159 
4160 	g_simple_async_result_set_check_cancellable (simple, cancellable);
4161 
4162 	g_simple_async_result_set_op_res_gpointer (
4163 		simple, async_context, (GDestroyNotify) async_context_free);
4164 
4165 	g_simple_async_result_run_in_thread (
4166 		simple, cal_client_get_object_list_as_comps_thread,
4167 		G_PRIORITY_DEFAULT, cancellable);
4168 
4169 	g_object_unref (simple);
4170 }
4171 
4172 /**
4173  * e_cal_client_get_object_list_as_comps_finish:
4174  * @client: an #ECalClient
4175  * @result: a #GAsyncResult
4176  * @out_ecalcomps: (out) (element-type ECalComponent): list of matching
4177  *                 #ECalComponent<!-- -->s
4178  * @error: a #GError to set an error, if any
4179  *
4180  * Finishes previous call of e_cal_client_get_object_list_as_comps() and
4181  * sets @out_ecalcomps to a matching list of #ECalComponent-s.
4182  * This list should be freed with e_client_util_free_object_slist().
4183  *
4184  * Returns: %TRUE if successful, %FALSE otherwise.
4185  *
4186  * Since: 3.2
4187  **/
4188 gboolean
e_cal_client_get_object_list_as_comps_finish(ECalClient * client,GAsyncResult * result,GSList ** out_ecalcomps,GError ** error)4189 e_cal_client_get_object_list_as_comps_finish (ECalClient *client,
4190                                               GAsyncResult *result,
4191                                               GSList **out_ecalcomps,
4192                                               GError **error)
4193 {
4194 	GSimpleAsyncResult *simple;
4195 	AsyncContext *async_context;
4196 
4197 	g_return_val_if_fail (
4198 		g_simple_async_result_is_valid (
4199 		result, G_OBJECT (client),
4200 		e_cal_client_get_object_list_as_comps), FALSE);
4201 
4202 	simple = G_SIMPLE_ASYNC_RESULT (result);
4203 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4204 
4205 	if (g_simple_async_result_propagate_error (simple, error))
4206 		return FALSE;
4207 
4208 	if (out_ecalcomps != NULL) {
4209 		*out_ecalcomps = async_context->object_list;
4210 		async_context->object_list = NULL;
4211 	}
4212 
4213 	return TRUE;
4214 }
4215 
4216 /**
4217  * e_cal_client_get_object_list_as_comps_sync:
4218  * @client: an #ECalClient
4219  * @sexp: an S-expression representing the query
4220  * @out_ecalcomps: (out) (element-type ECalComponent): list of matching
4221  *                 #ECalComponent<!-- -->s
4222  * @cancellable: a #GCancellable; can be %NULL
4223  * @error: a #GError to set an error, if any
4224  *
4225  * Gets a list of objects from the calendar that match the query specified
4226  * by the @sexp argument. The objects will be returned in the @out_ecalcomps
4227  * argument, which is a list of #ECalComponent.
4228  * This list should be freed with e_client_util_free_object_slist().
4229  *
4230  * Returns: %TRUE if successful, %FALSE otherwise.
4231  *
4232  * Since: 3.2
4233  **/
4234 gboolean
e_cal_client_get_object_list_as_comps_sync(ECalClient * client,const gchar * sexp,GSList ** out_ecalcomps,GCancellable * cancellable,GError ** error)4235 e_cal_client_get_object_list_as_comps_sync (ECalClient *client,
4236                                             const gchar *sexp,
4237                                             GSList **out_ecalcomps,
4238                                             GCancellable *cancellable,
4239                                             GError **error)
4240 {
4241 	GSList *list = NULL;
4242 	GSList *link;
4243 	gboolean success;
4244 
4245 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
4246 	g_return_val_if_fail (sexp != NULL, FALSE);
4247 	g_return_val_if_fail (out_ecalcomps != NULL, FALSE);
4248 
4249 	*out_ecalcomps = NULL;
4250 
4251 	success = e_cal_client_get_object_list_sync (
4252 		client, sexp, &list, cancellable, error);
4253 
4254 	if (!success) {
4255 		g_warn_if_fail (list == NULL);
4256 		return FALSE;
4257 	}
4258 
4259 	/* Convert the ICalComponent list to an ECalComponent list. */
4260 	for (link = list; link != NULL; link = g_slist_next (link)) {
4261 		ECalComponent *comp;
4262 		ICalComponent *icalcomp = link->data;
4263 
4264 		/* This takes ownership of the ICalComponent. */
4265 		comp = e_cal_component_new_from_icalcomponent (icalcomp);
4266 		if (comp)
4267 			*out_ecalcomps = g_slist_prepend (*out_ecalcomps, comp);
4268 
4269 	}
4270 
4271 	g_slist_free (list);
4272 
4273 	*out_ecalcomps = g_slist_reverse (*out_ecalcomps);
4274 
4275 	return TRUE;
4276 }
4277 
4278 /* Helper for e_cal_client_get_free_busy() */
4279 static void
cal_client_get_free_busy_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4280 cal_client_get_free_busy_thread (GSimpleAsyncResult *simple,
4281                                  GObject *source_object,
4282                                  GCancellable *cancellable)
4283 {
4284 	AsyncContext *async_context;
4285 	GError *local_error = NULL;
4286 
4287 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4288 
4289 	if (!e_cal_client_get_free_busy_sync (
4290 		E_CAL_CLIENT (source_object),
4291 		async_context->start,
4292 		async_context->end,
4293 		async_context->string_list,
4294 		&async_context->object_list,
4295 		cancellable, &local_error)) {
4296 
4297 		if (!local_error)
4298 			local_error = g_error_new_literal (
4299 				E_CLIENT_ERROR,
4300 				E_CLIENT_ERROR_OTHER_ERROR,
4301 				_("Unknown error"));
4302 	}
4303 
4304 	if (local_error != NULL)
4305 		g_simple_async_result_take_error (simple, local_error);
4306 }
4307 
4308 /**
4309  * e_cal_client_get_free_busy:
4310  * @client: an #ECalClient
4311  * @start: Start time for query
4312  * @end: End time for query
4313  * @users: (element-type utf8): List of users to retrieve free/busy information for
4314  * @cancellable: a #GCancellable; can be %NULL
4315  * @callback: callback to call when a result is ready
4316  * @user_data: user data for the @callback
4317  *
4318  * Begins retrieval of free/busy information from the calendar server
4319  * as a list of #ECalComponent-s. Connect to "free-busy-data" signal
4320  * to receive chunks of free/busy components.
4321  * The call is finished by e_cal_client_get_free_busy_finish() from
4322  * the @callback.
4323  *
4324  * Since: 3.2
4325  **/
4326 void
e_cal_client_get_free_busy(ECalClient * client,time_t start,time_t end,const GSList * users,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4327 e_cal_client_get_free_busy (ECalClient *client,
4328                             time_t start,
4329                             time_t end,
4330                             const GSList *users,
4331                             GCancellable *cancellable,
4332                             GAsyncReadyCallback callback,
4333                             gpointer user_data)
4334 {
4335 	GSimpleAsyncResult *simple;
4336 	AsyncContext *async_context;
4337 
4338 	g_return_if_fail (E_IS_CAL_CLIENT (client));
4339 	g_return_if_fail (start > 0);
4340 	g_return_if_fail (end > 0);
4341 
4342 	async_context = g_slice_new0 (AsyncContext);
4343 	async_context->start = start;
4344 	async_context->end = end;
4345 	async_context->string_list = g_slist_copy_deep (
4346 		(GSList *) users, (GCopyFunc) g_strdup, NULL);
4347 
4348 	simple = g_simple_async_result_new (
4349 		G_OBJECT (client), callback, user_data,
4350 		e_cal_client_get_free_busy);
4351 
4352 	g_simple_async_result_set_check_cancellable (simple, cancellable);
4353 
4354 	g_simple_async_result_set_op_res_gpointer (
4355 		simple, async_context, (GDestroyNotify) async_context_free);
4356 
4357 	g_simple_async_result_run_in_thread (
4358 		simple, cal_client_get_free_busy_thread,
4359 		G_PRIORITY_DEFAULT, cancellable);
4360 
4361 	g_object_unref (simple);
4362 }
4363 
4364 /**
4365  * e_cal_client_get_free_busy_finish:
4366  * @client: an #ECalClient
4367  * @result: a #GAsyncResult
4368  * @out_freebusy: (out) (element-type ECalComponent): a #GSList of #ECalComponent-s with overall returned Free/Busy data
4369  * @error: a #GError to set an error, if any
4370  *
4371  * Finishes previous call of e_cal_client_get_free_busy().
4372  * The @out_freebusy contains all VFREEBUSY #ECalComponent-s, which could be also
4373  * received by "free-busy-data" signal. The client is responsible to do a merge of
4374  * the components between this complete list and those received through the signal.
4375  *
4376  * Returns: %TRUE if successful, %FALSE otherwise.
4377  *
4378  * Since: 3.2
4379  **/
4380 gboolean
e_cal_client_get_free_busy_finish(ECalClient * client,GAsyncResult * result,GSList ** out_freebusy,GError ** error)4381 e_cal_client_get_free_busy_finish (ECalClient *client,
4382                                    GAsyncResult *result,
4383 				   GSList **out_freebusy,
4384                                    GError **error)
4385 {
4386 	GSimpleAsyncResult *simple;
4387 
4388 	g_return_val_if_fail (
4389 		g_simple_async_result_is_valid (
4390 		result, G_OBJECT (client),
4391 		e_cal_client_get_free_busy), FALSE);
4392 
4393 	simple = G_SIMPLE_ASYNC_RESULT (result);
4394 
4395 	/* Assume success unless a GError is set. */
4396 	if (g_simple_async_result_propagate_error (simple, error))
4397 		return FALSE;
4398 
4399 	if (out_freebusy != NULL) {
4400 		AsyncContext *async_context;
4401 
4402 		async_context = g_simple_async_result_get_op_res_gpointer (simple);
4403 
4404 		*out_freebusy = async_context->object_list;
4405 		async_context->object_list = NULL;
4406 	}
4407 
4408 	return TRUE;
4409 }
4410 
4411 /**
4412  * e_cal_client_get_free_busy_sync:
4413  * @client: an #ECalClient
4414  * @start: Start time for query
4415  * @end: End time for query
4416  * @users: (element-type utf8): List of users to retrieve free/busy information for
4417  * @out_freebusy: (out) (element-type ECalComponent): a #GSList of #ECalComponent-s with overall returned Free/Busy data
4418  * @cancellable: a #GCancellable; can be %NULL
4419  * @error: a #GError to set an error, if any
4420  *
4421  * Gets free/busy information from the calendar server.
4422  * The @out_freebusy contains all VFREEBUSY #ECalComponent-s, which could be also
4423  * received by "free-busy-data" signal. The client is responsible to do a merge of
4424  * the components between this complete list and those received through the signal.
4425  *
4426  * Returns: %TRUE if successful, %FALSE otherwise.
4427  *
4428  * Since: 3.2
4429  **/
4430 gboolean
e_cal_client_get_free_busy_sync(ECalClient * client,time_t start,time_t end,const GSList * users,GSList ** out_freebusy,GCancellable * cancellable,GError ** error)4431 e_cal_client_get_free_busy_sync (ECalClient *client,
4432                                  time_t start,
4433                                  time_t end,
4434                                  const GSList *users,
4435 				 GSList **out_freebusy,
4436                                  GCancellable *cancellable,
4437                                  GError **error)
4438 {
4439 	gchar **strv, **freebusy_strv = NULL;
4440 	gint ii = 0;
4441 	GError *local_error = NULL;
4442 
4443 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
4444 	g_return_val_if_fail (start > 0, FALSE);
4445 	g_return_val_if_fail (end > 0, FALSE);
4446 
4447 	strv = g_new0 (gchar *, g_slist_length ((GSList *) users) + 1);
4448 	while (users != NULL) {
4449 		strv[ii++] = e_util_utf8_make_valid (users->data);
4450 		users = g_slist_next (users);
4451 	}
4452 
4453 	e_dbus_calendar_call_get_free_busy_sync (
4454 		client->priv->dbus_proxy,
4455 		(gint64) start, (gint64) end,
4456 		(const gchar * const *) strv,
4457 		&freebusy_strv,
4458 		cancellable, &local_error);
4459 
4460 	g_strfreev (strv);
4461 
4462 	if (local_error != NULL) {
4463 		g_dbus_error_strip_remote_error (local_error);
4464 		g_propagate_error (error, local_error);
4465 		return FALSE;
4466 	}
4467 
4468 	if (out_freebusy) {
4469 		*out_freebusy = NULL;
4470 
4471 		for (ii = 0; freebusy_strv && freebusy_strv[ii] != NULL; ii++) {
4472 			ECalComponent *comp;
4473 
4474 			comp = e_cal_component_new_from_string (freebusy_strv[ii]);
4475 			if (comp)
4476 				*out_freebusy = g_slist_prepend (*out_freebusy, comp);
4477 		}
4478 
4479 		*out_freebusy = g_slist_reverse (*out_freebusy);
4480 	}
4481 
4482 	g_strfreev (freebusy_strv);
4483 
4484 	return TRUE;
4485 }
4486 
4487 /* Helper for e_cal_client_create_object() */
4488 static void
cal_client_create_object_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4489 cal_client_create_object_thread (GSimpleAsyncResult *simple,
4490                                  GObject *source_object,
4491                                  GCancellable *cancellable)
4492 {
4493 	AsyncContext *async_context;
4494 	GError *local_error = NULL;
4495 
4496 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4497 
4498 	if (!e_cal_client_create_object_sync (
4499 		E_CAL_CLIENT (source_object),
4500 		async_context->in_comp,
4501 		async_context->opflags,
4502 		&async_context->uid,
4503 		cancellable, &local_error)) {
4504 
4505 		if (!local_error)
4506 			local_error = g_error_new_literal (
4507 				E_CLIENT_ERROR,
4508 				E_CLIENT_ERROR_OTHER_ERROR,
4509 				_("Unknown error"));
4510 	}
4511 
4512 	if (local_error != NULL)
4513 		g_simple_async_result_take_error (simple, local_error);
4514 }
4515 
4516 /**
4517  * e_cal_client_create_object:
4518  * @client: an #ECalClient
4519  * @icalcomp: The component to create
4520  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
4521  * @cancellable: a #GCancellable; can be %NULL
4522  * @callback: callback to call when a result is ready
4523  * @user_data: user data for the @callback
4524  *
4525  * Requests the calendar backend to create the object specified by the @icalcomp
4526  * argument. Some backends would assign a specific UID to the newly created object,
4527  * but this function does not modify the original @icalcomp if its UID changes.
4528  * The call is finished by e_cal_client_create_object_finish() from
4529  * the @callback.
4530  *
4531  * Since: 3.2
4532  **/
4533 void
e_cal_client_create_object(ECalClient * client,ICalComponent * icalcomp,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4534 e_cal_client_create_object (ECalClient *client,
4535                             ICalComponent *icalcomp,
4536 			    guint32 opflags,
4537                             GCancellable *cancellable,
4538                             GAsyncReadyCallback callback,
4539                             gpointer user_data)
4540 {
4541 	GSimpleAsyncResult *simple;
4542 	AsyncContext *async_context;
4543 
4544 	g_return_if_fail (E_IS_CAL_CLIENT (client));
4545 	g_return_if_fail (icalcomp != NULL);
4546 
4547 	async_context = g_slice_new0 (AsyncContext);
4548 	async_context->in_comp = i_cal_component_clone (icalcomp);
4549 	async_context->opflags = opflags;
4550 
4551 	simple = g_simple_async_result_new (
4552 		G_OBJECT (client), callback, user_data,
4553 		e_cal_client_create_object);
4554 
4555 	g_simple_async_result_set_op_res_gpointer (
4556 		simple, async_context, (GDestroyNotify) async_context_free);
4557 
4558 	g_simple_async_result_run_in_thread (
4559 		simple, cal_client_create_object_thread,
4560 		G_PRIORITY_DEFAULT, cancellable);
4561 
4562 	g_object_unref (simple);
4563 }
4564 
4565 /**
4566  * e_cal_client_create_object_finish:
4567  * @client: an #ECalClient
4568  * @result: a #GAsyncResult
4569  * @out_uid: (out) (nullable) (optional): Return value for the UID assigned to the new component
4570  *           by the calendar backend
4571  * @error: a #GError to set an error, if any
4572  *
4573  * Finishes previous call of e_cal_client_create_object() and
4574  * sets @out_uid to newly assigned UID for the created object.
4575  * This @out_uid should be freed with g_free().
4576  *
4577  * Returns: %TRUE if successful, %FALSE otherwise.
4578  *
4579  * Since: 3.2
4580  **/
4581 gboolean
e_cal_client_create_object_finish(ECalClient * client,GAsyncResult * result,gchar ** out_uid,GError ** error)4582 e_cal_client_create_object_finish (ECalClient *client,
4583                                    GAsyncResult *result,
4584                                    gchar **out_uid,
4585                                    GError **error)
4586 {
4587 	GSimpleAsyncResult *simple;
4588 	AsyncContext *async_context;
4589 
4590 	g_return_val_if_fail (
4591 		g_simple_async_result_is_valid (
4592 		result, G_OBJECT (client),
4593 		e_cal_client_create_object), FALSE);
4594 
4595 	simple = G_SIMPLE_ASYNC_RESULT (result);
4596 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4597 
4598 	if (g_simple_async_result_propagate_error (simple, error))
4599 		return FALSE;
4600 
4601 	g_return_val_if_fail (async_context->uid != NULL, FALSE);
4602 
4603 	if (out_uid != NULL) {
4604 		*out_uid = async_context->uid;
4605 		async_context->uid = NULL;
4606 	}
4607 
4608 	return TRUE;
4609 }
4610 
4611 /**
4612  * e_cal_client_create_object_sync:
4613  * @client: an #ECalClient
4614  * @icalcomp: The component to create
4615  * @opflags: bit-or of #ECalOperationFlags
4616  * @out_uid: (out) (nullable) (optional): Return value for the UID assigned to the new component
4617  *           by the calendar backend
4618  * @cancellable: a #GCancellable; can be %NULL
4619  * @error: a #GError to set an error, if any
4620  *
4621  * Requests the calendar backend to create the object specified by the
4622  * @icalcomp argument. Some backends would assign a specific UID to the newly
4623  * created object, in those cases that UID would be returned in the @out_uid
4624  * argument. This function does not modify the original @icalcomp if its UID
4625  * changes.  Returned @out_uid should be freed with g_free().
4626  *
4627  * Returns: %TRUE if successful, %FALSE otherwise.
4628  *
4629  * Since: 3.2
4630  **/
4631 gboolean
e_cal_client_create_object_sync(ECalClient * client,ICalComponent * icalcomp,guint32 opflags,gchar ** out_uid,GCancellable * cancellable,GError ** error)4632 e_cal_client_create_object_sync (ECalClient *client,
4633                                  ICalComponent *icalcomp,
4634 				 guint32 opflags,
4635                                  gchar **out_uid,
4636                                  GCancellable *cancellable,
4637                                  GError **error)
4638 {
4639 	GSList link = { icalcomp, NULL };
4640 	GSList *string_list = NULL;
4641 	gboolean success;
4642 
4643 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
4644 	g_return_val_if_fail (icalcomp != NULL, FALSE);
4645 
4646 	success = e_cal_client_create_objects_sync (
4647 		client, &link, opflags, &string_list, cancellable, error);
4648 
4649 	/* Sanity check. */
4650 	g_return_val_if_fail (
4651 		(success && (string_list != NULL)) ||
4652 		(!success && (string_list == NULL)), FALSE);
4653 
4654 	if (out_uid != NULL && string_list != NULL)
4655 		*out_uid = g_strdup (string_list->data);
4656 	else if (out_uid)
4657 		*out_uid = NULL;
4658 
4659 	g_slist_free_full (string_list, (GDestroyNotify) g_free);
4660 
4661 	return success;
4662 }
4663 
4664 /* Helper for e_cal_client_create_objects() */
4665 static void
cal_client_create_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4666 cal_client_create_objects_thread (GSimpleAsyncResult *simple,
4667                                   GObject *source_object,
4668                                   GCancellable *cancellable)
4669 {
4670 	AsyncContext *async_context;
4671 	GError *local_error = NULL;
4672 
4673 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4674 
4675 	if (!e_cal_client_create_objects_sync (
4676 		E_CAL_CLIENT (source_object),
4677 		async_context->comp_list,
4678 		async_context->opflags,
4679 		&async_context->string_list,
4680 		cancellable, &local_error)) {
4681 
4682 		if (!local_error)
4683 			local_error = g_error_new_literal (
4684 				E_CLIENT_ERROR,
4685 				E_CLIENT_ERROR_OTHER_ERROR,
4686 				_("Unknown error"));
4687 	}
4688 
4689 	if (local_error != NULL)
4690 		g_simple_async_result_take_error (simple, local_error);
4691 }
4692 
4693 /**
4694  * e_cal_client_create_objects:
4695  * @client: an #ECalClient
4696  * @icalcomps: (element-type ICalComponent): The components to create
4697  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
4698  * @cancellable: a #GCancellable; can be %NULL
4699  * @callback: callback to call when a result is ready
4700  * @user_data: user data for the @callback
4701  *
4702  * Requests the calendar backend to create the objects specified by the @icalcomps
4703  * argument. Some backends would assign a specific UID to the newly created object,
4704  * but this function does not modify the original @icalcomps if their UID changes.
4705  * The call is finished by e_cal_client_create_objects_finish() from
4706  * the @callback.
4707  *
4708  * Since: 3.6
4709  **/
4710 void
e_cal_client_create_objects(ECalClient * client,GSList * icalcomps,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4711 e_cal_client_create_objects (ECalClient *client,
4712                              GSList *icalcomps,
4713 			     guint32 opflags,
4714                              GCancellable *cancellable,
4715                              GAsyncReadyCallback callback,
4716                              gpointer user_data)
4717 {
4718 	GSimpleAsyncResult *simple;
4719 	AsyncContext *async_context;
4720 
4721 	g_return_if_fail (E_IS_CAL_CLIENT (client));
4722 	g_return_if_fail (icalcomps != NULL);
4723 
4724 	async_context = g_slice_new0 (AsyncContext);
4725 	async_context->comp_list = g_slist_copy_deep (
4726 		icalcomps, (GCopyFunc) i_cal_component_clone, NULL);
4727 	async_context->opflags = opflags;
4728 
4729 	simple = g_simple_async_result_new (
4730 		G_OBJECT (client), callback, user_data,
4731 		e_cal_client_create_objects);
4732 
4733 	g_simple_async_result_set_check_cancellable (simple, cancellable);
4734 
4735 	g_simple_async_result_set_op_res_gpointer (
4736 		simple, async_context, (GDestroyNotify) async_context_free);
4737 
4738 	g_simple_async_result_run_in_thread (
4739 		simple, cal_client_create_objects_thread,
4740 		G_PRIORITY_DEFAULT, cancellable);
4741 
4742 	g_object_unref (simple);
4743 }
4744 
4745 /**
4746  * e_cal_client_create_objects_finish:
4747  * @client: an #ECalClient
4748  * @result: a #GAsyncResult
4749  * @out_uids: (out) (optional) (element-type utf8): Return value for the UIDs assigned
4750  *            to the new components by the calendar backend
4751  * @error: a #GError to set an error, if any
4752  *
4753  * Finishes previous call of e_cal_client_create_objects() and
4754  * sets @out_uids to newly assigned UIDs for the created objects.
4755  * This @out_uids should be freed with e_client_util_free_string_slist().
4756  *
4757  * Returns: %TRUE if successful, %FALSE otherwise.
4758  *
4759  * Since: 3.6
4760  **/
4761 gboolean
e_cal_client_create_objects_finish(ECalClient * client,GAsyncResult * result,GSList ** out_uids,GError ** error)4762 e_cal_client_create_objects_finish (ECalClient *client,
4763                                     GAsyncResult *result,
4764                                     GSList **out_uids,
4765                                     GError **error)
4766 {
4767 	GSimpleAsyncResult *simple;
4768 	AsyncContext *async_context;
4769 
4770 	g_return_val_if_fail (
4771 		g_simple_async_result_is_valid (
4772 		result, G_OBJECT (client),
4773 		e_cal_client_create_objects), FALSE);
4774 
4775 	simple = G_SIMPLE_ASYNC_RESULT (result);
4776 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4777 
4778 	if (g_simple_async_result_propagate_error (simple, error))
4779 		return FALSE;
4780 
4781 	if (out_uids != NULL) {
4782 		*out_uids = async_context->string_list;
4783 		async_context->string_list = NULL;
4784 	}
4785 
4786 	return TRUE;
4787 }
4788 
4789 /**
4790  * e_cal_client_create_objects_sync:
4791  * @client: an #ECalClient
4792  * @icalcomps: (element-type ICalComponent): The components to create
4793  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
4794  * @out_uids: (out) (nullable) (element-type utf8): Return value for the UIDs assigned
4795  *            to the new components by the calendar backend
4796  * @cancellable: a #GCancellable; can be %NULL
4797  * @error: a #GError to set an error, if any
4798  *
4799  * Requests the calendar backend to create the objects specified by the
4800  * @icalcomps argument. Some backends would assign a specific UID to the
4801  * newly created objects, in those cases these UIDs would be returned in
4802  * the @out_uids argument. This function does not modify the original
4803  * @icalcomps if their UID changes.  Returned @out_uids should be freed
4804  * with e_client_util_free_string_slist().
4805  *
4806  * Returns: %TRUE if successful, %FALSE otherwise.
4807  *
4808  * Since: 3.6
4809  **/
4810 gboolean
e_cal_client_create_objects_sync(ECalClient * client,GSList * icalcomps,guint32 opflags,GSList ** out_uids,GCancellable * cancellable,GError ** error)4811 e_cal_client_create_objects_sync (ECalClient *client,
4812                                   GSList *icalcomps,
4813 				  guint32 opflags,
4814                                   GSList **out_uids,
4815                                   GCancellable *cancellable,
4816                                   GError **error)
4817 {
4818 	gchar **strv;
4819 	gchar **uids = NULL;
4820 	gint ii = 0;
4821 	GError *local_error = NULL;
4822 
4823 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
4824 	g_return_val_if_fail (icalcomps != NULL, FALSE);
4825 	g_return_val_if_fail (out_uids != NULL, FALSE);
4826 
4827 	strv = g_new0 (gchar *, g_slist_length (icalcomps) + 1);
4828 	while (icalcomps != NULL) {
4829 		gchar *ical_string;
4830 
4831 		ical_string = i_cal_component_as_ical_string (icalcomps->data);
4832 		strv[ii++] = e_util_utf8_make_valid (ical_string);
4833 		g_free (ical_string);
4834 
4835 		icalcomps = g_slist_next (icalcomps);
4836 	}
4837 
4838 	e_dbus_calendar_call_create_objects_sync (
4839 		client->priv->dbus_proxy,
4840 		(const gchar * const *) strv,
4841 		opflags, &uids, cancellable, &local_error);
4842 
4843 	g_strfreev (strv);
4844 
4845 	/* Sanity check. */
4846 	g_return_val_if_fail (
4847 		((uids != NULL) && (local_error == NULL)) ||
4848 		((uids == NULL) && (local_error != NULL)), FALSE);
4849 
4850 	if (uids && out_uids) {
4851 		GSList *tmp = NULL;
4852 
4853 		/* Steal the string array elements. */
4854 		for (ii = 0; uids[ii] != NULL; ii++) {
4855 			tmp = g_slist_prepend (tmp, uids[ii]);
4856 			uids[ii] = NULL;
4857 		}
4858 
4859 		*out_uids = g_slist_reverse (tmp);
4860 	} else if (out_uids) {
4861 		*out_uids = NULL;
4862 	}
4863 
4864 	g_strfreev (uids);
4865 
4866 	if (local_error != NULL) {
4867 		g_dbus_error_strip_remote_error (local_error);
4868 		g_propagate_error (error, local_error);
4869 		return FALSE;
4870 	}
4871 
4872 	return TRUE;
4873 }
4874 
4875 /* Helper for e_cal_client_modify_object() */
4876 static void
cal_client_modify_object_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4877 cal_client_modify_object_thread (GSimpleAsyncResult *simple,
4878                                  GObject *source_object,
4879                                  GCancellable *cancellable)
4880 {
4881 	AsyncContext *async_context;
4882 	GError *local_error = NULL;
4883 
4884 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
4885 
4886 	if (!e_cal_client_modify_object_sync (
4887 		E_CAL_CLIENT (source_object),
4888 		async_context->in_comp,
4889 		async_context->mod,
4890 		async_context->opflags,
4891 		cancellable, &local_error)) {
4892 
4893 		if (!local_error)
4894 			local_error = g_error_new_literal (
4895 				E_CLIENT_ERROR,
4896 				E_CLIENT_ERROR_OTHER_ERROR,
4897 				_("Unknown error"));
4898 	}
4899 
4900 	if (local_error != NULL)
4901 		g_simple_async_result_take_error (simple, local_error);
4902 }
4903 
4904 /**
4905  * e_cal_client_modify_object:
4906  * @client: an #ECalClient
4907  * @icalcomp: Component to modify
4908  * @mod: Type of modification
4909  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
4910  * @cancellable: a #GCancellable; can be %NULL
4911  * @callback: callback to call when a result is ready
4912  * @user_data: user data for the @callback
4913  *
4914  * Requests the calendar backend to modify an existing object. If the object
4915  * does not exist on the calendar, an error will be returned.
4916  *
4917  * For recurrent appointments, the @mod argument specifies what to modify,
4918  * if all instances (#E_CAL_OBJ_MOD_ALL), a single instance (#E_CAL_OBJ_MOD_THIS),
4919  * or a specific set of instances (#E_CAL_OBJ_MOD_THIS_AND_PRIOR and
4920  * #E_CAL_OBJ_MOD_THIS_AND_FUTURE).
4921  *
4922  * The call is finished by e_cal_client_modify_object_finish() from
4923  * the @callback.
4924  *
4925  * Since: 3.2
4926  **/
4927 void
e_cal_client_modify_object(ECalClient * client,ICalComponent * icalcomp,ECalObjModType mod,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4928 e_cal_client_modify_object (ECalClient *client,
4929                             ICalComponent *icalcomp,
4930                             ECalObjModType mod,
4931 			    guint32 opflags,
4932                             GCancellable *cancellable,
4933                             GAsyncReadyCallback callback,
4934                             gpointer user_data)
4935 {
4936 	GSimpleAsyncResult *simple;
4937 	AsyncContext *async_context;
4938 
4939 	g_return_if_fail (E_IS_CAL_CLIENT (client));
4940 	g_return_if_fail (icalcomp != NULL);
4941 
4942 	async_context = g_slice_new0 (AsyncContext);
4943 	async_context->in_comp = i_cal_component_clone (icalcomp);
4944 	async_context->mod = mod;
4945 	async_context->opflags = opflags;
4946 
4947 	simple = g_simple_async_result_new (
4948 		G_OBJECT (client), callback, user_data,
4949 		e_cal_client_modify_object);
4950 
4951 	g_simple_async_result_set_check_cancellable (simple, cancellable);
4952 
4953 	g_simple_async_result_set_op_res_gpointer (
4954 		simple, async_context, (GDestroyNotify) async_context_free);
4955 
4956 	g_simple_async_result_run_in_thread (
4957 		simple, cal_client_modify_object_thread,
4958 		G_PRIORITY_DEFAULT, cancellable);
4959 
4960 	g_object_unref (simple);
4961 }
4962 
4963 /**
4964  * e_cal_client_modify_object_finish:
4965  * @client: an #ECalClient
4966  * @result: a #GAsyncResult
4967  * @error: a #GError to set an error, if any
4968  *
4969  * Finishes previous call of e_cal_client_modify_object().
4970  *
4971  * Returns: %TRUE if successful, %FALSE otherwise.
4972  *
4973  * Since: 3.2
4974  **/
4975 gboolean
e_cal_client_modify_object_finish(ECalClient * client,GAsyncResult * result,GError ** error)4976 e_cal_client_modify_object_finish (ECalClient *client,
4977                                    GAsyncResult *result,
4978                                    GError **error)
4979 {
4980 	GSimpleAsyncResult *simple;
4981 
4982 	g_return_val_if_fail (
4983 		g_simple_async_result_is_valid (
4984 		result, G_OBJECT (client),
4985 		e_cal_client_modify_object), FALSE);
4986 
4987 	simple = G_SIMPLE_ASYNC_RESULT (result);
4988 
4989 	/* Assume success unless a GError is set. */
4990 	return !g_simple_async_result_propagate_error (simple, error);
4991 }
4992 
4993 /**
4994  * e_cal_client_modify_object_sync:
4995  * @client: an #ECalClient
4996  * @icalcomp: Component to modify
4997  * @mod: Type of modification
4998  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
4999  * @cancellable: a #GCancellable; can be %NULL
5000  * @error: a #GError to set an error, if any
5001  *
5002  * Requests the calendar backend to modify an existing object. If the object
5003  * does not exist on the calendar, an error will be returned.
5004  *
5005  * For recurrent appointments, the @mod argument specifies what to modify,
5006  * if all instances (#E_CAL_OBJ_MOD_ALL), a single instance (#E_CAL_OBJ_MOD_THIS),
5007  * or a specific set of instances (#E_CAL_OBJ_MOD_THIS_AND_PRIOR and
5008  * #E_CAL_OBJ_MOD_THIS_AND_FUTURE).
5009  *
5010  * Returns: %TRUE if successful, %FALSE otherwise.
5011  *
5012  * Since: 3.2
5013  **/
5014 gboolean
e_cal_client_modify_object_sync(ECalClient * client,ICalComponent * icalcomp,ECalObjModType mod,guint32 opflags,GCancellable * cancellable,GError ** error)5015 e_cal_client_modify_object_sync (ECalClient *client,
5016                                  ICalComponent *icalcomp,
5017                                  ECalObjModType mod,
5018 				 guint32 opflags,
5019                                  GCancellable *cancellable,
5020                                  GError **error)
5021 {
5022 	GSList link = { icalcomp, NULL };
5023 
5024 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
5025 	g_return_val_if_fail (icalcomp != NULL, FALSE);
5026 
5027 	return e_cal_client_modify_objects_sync (
5028 		client, &link, mod, opflags, cancellable, error);
5029 }
5030 
5031 /* Helper for e_cal_client_modify_objects() */
5032 static void
cal_client_modify_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)5033 cal_client_modify_objects_thread (GSimpleAsyncResult *simple,
5034                                   GObject *source_object,
5035                                   GCancellable *cancellable)
5036 {
5037 	AsyncContext *async_context;
5038 	GError *local_error = NULL;
5039 
5040 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
5041 
5042 	if (!e_cal_client_modify_objects_sync (
5043 		E_CAL_CLIENT (source_object),
5044 		async_context->comp_list,
5045 		async_context->mod,
5046 		async_context->opflags,
5047 		cancellable, &local_error)) {
5048 
5049 		if (!local_error)
5050 			local_error = g_error_new_literal (
5051 				E_CLIENT_ERROR,
5052 				E_CLIENT_ERROR_OTHER_ERROR,
5053 				_("Unknown error"));
5054 	}
5055 
5056 	if (local_error != NULL)
5057 		g_simple_async_result_take_error (simple, local_error);
5058 }
5059 
5060 /**
5061  * e_cal_client_modify_objects:
5062  * @client: an #ECalClient
5063  * @icalcomps: (element-type ICalComponent): Components to modify
5064  * @mod: Type of modification
5065  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
5066  * @cancellable: a #GCancellable; can be %NULL
5067  * @callback: callback to call when a result is ready
5068  * @user_data: user data for the @callback
5069  *
5070  * Requests the calendar backend to modify existing objects. If an object
5071  * does not exist on the calendar, an error will be returned.
5072  *
5073  * For recurrent appointments, the @mod argument specifies what to modify,
5074  * if all instances (#E_CAL_OBJ_MOD_ALL), a single instance (#E_CAL_OBJ_MOD_THIS),
5075  * or a specific set of instances (#E_CAL_OBJ_MOD_THIS_AND_PRIOR and
5076  * #E_CAL_OBJ_MOD_THIS_AND_FUTURE).
5077  *
5078  * The call is finished by e_cal_client_modify_objects_finish() from
5079  * the @callback.
5080  *
5081  * Since: 3.6
5082  **/
5083 void
e_cal_client_modify_objects(ECalClient * client,GSList * icalcomps,ECalObjModType mod,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)5084 e_cal_client_modify_objects (ECalClient *client,
5085                              GSList *icalcomps,
5086                              ECalObjModType mod,
5087 			     guint32 opflags,
5088                              GCancellable *cancellable,
5089                              GAsyncReadyCallback callback,
5090                              gpointer user_data)
5091 {
5092 	GSimpleAsyncResult *simple;
5093 	AsyncContext *async_context;
5094 
5095 	g_return_if_fail (E_IS_CAL_CLIENT (client));
5096 	g_return_if_fail (icalcomps != NULL);
5097 
5098 	async_context = g_slice_new0 (AsyncContext);
5099 	async_context->comp_list = g_slist_copy_deep (
5100 		icalcomps, (GCopyFunc) i_cal_component_clone, NULL);
5101 	async_context->mod = mod;
5102 	async_context->opflags = opflags;
5103 
5104 	simple = g_simple_async_result_new (
5105 		G_OBJECT (client), callback, user_data,
5106 		e_cal_client_modify_objects);
5107 
5108 	g_simple_async_result_set_check_cancellable (simple, cancellable);
5109 
5110 	g_simple_async_result_set_op_res_gpointer (
5111 		simple, async_context, (GDestroyNotify) async_context_free);
5112 
5113 	g_simple_async_result_run_in_thread (
5114 		simple, cal_client_modify_objects_thread,
5115 		G_PRIORITY_DEFAULT, cancellable);
5116 
5117 	g_object_unref (simple);
5118 }
5119 
5120 /**
5121  * e_cal_client_modify_objects_finish:
5122  * @client: an #ECalClient
5123  * @result: a #GAsyncResult
5124  * @error: a #GError to set an error, if any
5125  *
5126  * Finishes previous call of e_cal_client_modify_objects().
5127  *
5128  * Returns: %TRUE if successful, %FALSE otherwise.
5129  *
5130  * Since: 3.6
5131  **/
5132 gboolean
e_cal_client_modify_objects_finish(ECalClient * client,GAsyncResult * result,GError ** error)5133 e_cal_client_modify_objects_finish (ECalClient *client,
5134                                     GAsyncResult *result,
5135                                     GError **error)
5136 {
5137 	GSimpleAsyncResult *simple;
5138 
5139 	g_return_val_if_fail (
5140 		g_simple_async_result_is_valid (
5141 		result, G_OBJECT (client),
5142 		e_cal_client_modify_objects), FALSE);
5143 
5144 	simple = G_SIMPLE_ASYNC_RESULT (result);
5145 
5146 	/* Assume success unless a GError is set. */
5147 	return !g_simple_async_result_propagate_error (simple, error);
5148 }
5149 
5150 /**
5151  * e_cal_client_modify_objects_sync:
5152  * @client: an #ECalClient
5153  * @icalcomps: (element-type ICalComponent): Components to modify
5154  * @mod: Type of modification
5155  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
5156  * @cancellable: a #GCancellable; can be %NULL
5157  * @error: a #GError to set an error, if any
5158  *
5159  * Requests the calendar backend to modify existing objects. If an object
5160  * does not exist on the calendar, an error will be returned.
5161  *
5162  * For recurrent appointments, the @mod argument specifies what to modify,
5163  * if all instances (#E_CAL_OBJ_MOD_ALL), a single instance (#E_CAL_OBJ_MOD_THIS),
5164  * or a specific set of instances (#E_CAL_OBJ_MOD_THIS_AND_PRIOR and
5165  * #E_CAL_OBJ_MOD_THIS_AND_FUTURE).
5166  *
5167  * Returns: %TRUE if successful, %FALSE otherwise.
5168  *
5169  * Since: 3.6
5170  **/
5171 gboolean
e_cal_client_modify_objects_sync(ECalClient * client,GSList * icalcomps,ECalObjModType mod,guint32 opflags,GCancellable * cancellable,GError ** error)5172 e_cal_client_modify_objects_sync (ECalClient *client,
5173                                   GSList *icalcomps,
5174                                   ECalObjModType mod,
5175 				  guint32 opflags,
5176                                   GCancellable *cancellable,
5177                                   GError **error)
5178 {
5179 	GFlagsClass *flags_class;
5180 	GFlagsValue *flags_value;
5181 	GString *mod_flags;
5182 	gchar **strv;
5183 	gint ii = 0;
5184 	GError *local_error = NULL;
5185 
5186 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
5187 	g_return_val_if_fail (icalcomps != NULL, FALSE);
5188 
5189 	mod_flags = g_string_new (NULL);
5190 	flags_class = g_type_class_ref (E_TYPE_CAL_OBJ_MOD_TYPE);
5191 	flags_value = g_flags_get_first_value (flags_class, mod);
5192 	while (flags_value != NULL) {
5193 		if (mod_flags->len > 0)
5194 			g_string_append_c (mod_flags, ':');
5195 		g_string_append (mod_flags, flags_value->value_nick);
5196 		mod &= ~flags_value->value;
5197 		flags_value = g_flags_get_first_value (flags_class, mod);
5198 	}
5199 
5200 	strv = g_new0 (gchar *, g_slist_length (icalcomps) + 1);
5201 	while (icalcomps != NULL) {
5202 		gchar *ical_string;
5203 
5204 		ical_string = i_cal_component_as_ical_string (icalcomps->data);
5205 		strv[ii++] = e_util_utf8_make_valid (ical_string);
5206 		g_free (ical_string);
5207 
5208 		icalcomps = g_slist_next (icalcomps);
5209 	}
5210 
5211 	e_dbus_calendar_call_modify_objects_sync (
5212 		client->priv->dbus_proxy,
5213 		(const gchar * const *) strv,
5214 		mod_flags->str, opflags, cancellable, &local_error);
5215 
5216 	g_strfreev (strv);
5217 
5218 	g_type_class_unref (flags_class);
5219 	g_string_free (mod_flags, TRUE);
5220 
5221 	if (local_error != NULL) {
5222 		g_dbus_error_strip_remote_error (local_error);
5223 		g_propagate_error (error, local_error);
5224 		return FALSE;
5225 	}
5226 
5227 	return TRUE;
5228 }
5229 
5230 /* Helper for e_cal_client_remove_object() */
5231 static void
cal_client_remove_object_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)5232 cal_client_remove_object_thread (GSimpleAsyncResult *simple,
5233                                  GObject *source_object,
5234                                  GCancellable *cancellable)
5235 {
5236 	AsyncContext *async_context;
5237 	GError *local_error = NULL;
5238 
5239 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
5240 
5241 	if (!e_cal_client_remove_object_sync (
5242 		E_CAL_CLIENT (source_object),
5243 		async_context->uid,
5244 		async_context->rid,
5245 		async_context->mod,
5246 		async_context->opflags,
5247 		cancellable, &local_error)) {
5248 
5249 		if (!local_error)
5250 			local_error = g_error_new_literal (
5251 				E_CLIENT_ERROR,
5252 				E_CLIENT_ERROR_OTHER_ERROR,
5253 				_("Unknown error"));
5254 	}
5255 
5256 	if (local_error != NULL)
5257 		g_simple_async_result_take_error (simple, local_error);
5258 }
5259 
5260 /**
5261  * e_cal_client_remove_object:
5262  * @client: an #ECalClient
5263  * @uid: UID of the object to remove
5264  * @rid: (nullable): Recurrence ID of the specific recurrence to remove
5265  * @mod: Type of the removal
5266  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
5267  * @cancellable: a #GCancellable; can be %NULL
5268  * @callback: callback to call when a result is ready
5269  * @user_data: user data for the @callback
5270  *
5271  * This function allows the removal of instances of a recurrent
5272  * appointment. By using a combination of the @uid, @rid and @mod
5273  * arguments, you can remove specific instances. If what you want
5274  * is to remove all instances, use %NULL @rid and #E_CAL_OBJ_MOD_ALL
5275  * for the @mod.
5276  *
5277  * The call is finished by e_cal_client_remove_object_finish() from
5278  * the @callback.
5279  *
5280  * Since: 3.2
5281  **/
5282 void
e_cal_client_remove_object(ECalClient * client,const gchar * uid,const gchar * rid,ECalObjModType mod,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)5283 e_cal_client_remove_object (ECalClient *client,
5284                             const gchar *uid,
5285                             const gchar *rid,
5286                             ECalObjModType mod,
5287 			    guint32 opflags,
5288                             GCancellable *cancellable,
5289                             GAsyncReadyCallback callback,
5290                             gpointer user_data)
5291 {
5292 	GSimpleAsyncResult *simple;
5293 	AsyncContext *async_context;
5294 
5295 	g_return_if_fail (E_IS_CAL_CLIENT (client));
5296 	g_return_if_fail (uid != NULL);
5297 	/* rid is optional */
5298 
5299 	async_context = g_slice_new0 (AsyncContext);
5300 	async_context->uid = g_strdup (uid);
5301 	async_context->rid = g_strdup (rid);
5302 	async_context->mod = mod;
5303 	async_context->opflags = opflags;
5304 
5305 	simple = g_simple_async_result_new (
5306 		G_OBJECT (client), callback, user_data,
5307 		e_cal_client_remove_object);
5308 
5309 	g_simple_async_result_set_check_cancellable (simple, cancellable);
5310 
5311 	g_simple_async_result_set_op_res_gpointer (
5312 		simple, async_context, (GDestroyNotify) async_context_free);
5313 
5314 	g_simple_async_result_run_in_thread (
5315 		simple, cal_client_remove_object_thread,
5316 		G_PRIORITY_DEFAULT, cancellable);
5317 
5318 	g_object_unref (simple);
5319 }
5320 
5321 /**
5322  * e_cal_client_remove_object_finish:
5323  * @client: an #ECalClient
5324  * @result: a #GAsyncResult
5325  * @error: a #GError to set an error, if any
5326  *
5327  * Finishes previous call of e_cal_client_remove_object().
5328  *
5329  * Returns: %TRUE if successful, %FALSE otherwise.
5330  *
5331  * Since: 3.2
5332  **/
5333 gboolean
e_cal_client_remove_object_finish(ECalClient * client,GAsyncResult * result,GError ** error)5334 e_cal_client_remove_object_finish (ECalClient *client,
5335                                    GAsyncResult *result,
5336                                    GError **error)
5337 {
5338 	GSimpleAsyncResult *simple;
5339 
5340 	g_return_val_if_fail (
5341 		g_simple_async_result_is_valid (
5342 		result, G_OBJECT (client),
5343 		e_cal_client_remove_object), FALSE);
5344 
5345 	simple = G_SIMPLE_ASYNC_RESULT (result);
5346 
5347 	/* Assume success unless a GError is set. */
5348 	return !g_simple_async_result_propagate_error (simple, error);
5349 }
5350 
5351 /**
5352  * e_cal_client_remove_object_sync:
5353  * @client: an #ECalClient
5354  * @uid: UID of the object to remove
5355  * @rid: (nullable): Recurrence ID of the specific recurrence to remove
5356  * @mod: Type of the removal
5357  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
5358  * @cancellable: a #GCancellable; can be %NULL
5359  * @error: a #GError to set an error, if any
5360  *
5361  * This function allows the removal of instances of a recurrent
5362  * appointment. By using a combination of the @uid, @rid and @mod
5363  * arguments, you can remove specific instances. If what you want
5364  * is to remove all instances, use %NULL @rid and #E_CAL_OBJ_MOD_ALL
5365  * for the @mod.
5366  *
5367  * Returns: %TRUE if successful, %FALSE otherwise.
5368  *
5369  * Since: 3.2
5370  **/
5371 gboolean
e_cal_client_remove_object_sync(ECalClient * client,const gchar * uid,const gchar * rid,ECalObjModType mod,guint32 opflags,GCancellable * cancellable,GError ** error)5372 e_cal_client_remove_object_sync (ECalClient *client,
5373                                  const gchar *uid,
5374                                  const gchar *rid,
5375                                  ECalObjModType mod,
5376 				 guint32 opflags,
5377                                  GCancellable *cancellable,
5378                                  GError **error)
5379 {
5380 	ECalComponentId *id;
5381 	GSList *link;
5382 	gboolean success;
5383 
5384 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
5385 	g_return_val_if_fail (uid != NULL, FALSE);
5386 
5387 	id = e_cal_component_id_new (uid, rid);
5388 	link = g_slist_prepend (NULL, id);
5389 
5390 	success = e_cal_client_remove_objects_sync (client, link, mod, opflags, cancellable, error);
5391 
5392 	g_slist_free_full (link, e_cal_component_id_free);
5393 
5394 	return success;
5395 }
5396 
5397 /* Helper for e_cal_client_remove_objects() */
5398 static void
cal_client_remove_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)5399 cal_client_remove_objects_thread (GSimpleAsyncResult *simple,
5400                                   GObject *source_object,
5401                                   GCancellable *cancellable)
5402 {
5403 	AsyncContext *async_context;
5404 	GError *local_error = NULL;
5405 
5406 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
5407 
5408 	if (!e_cal_client_remove_objects_sync (
5409 		E_CAL_CLIENT (source_object),
5410 		async_context->ids_list,
5411 		async_context->mod,
5412 		async_context->opflags,
5413 		cancellable, &local_error)) {
5414 
5415 		if (!local_error)
5416 			local_error = g_error_new_literal (
5417 				E_CLIENT_ERROR,
5418 				E_CLIENT_ERROR_OTHER_ERROR,
5419 				_("Unknown error"));
5420 	}
5421 
5422 	if (local_error != NULL)
5423 		g_simple_async_result_take_error (simple, local_error);
5424 }
5425 
5426 /**
5427  * e_cal_client_remove_objects:
5428  * @client: an #ECalClient
5429  * @ids: (element-type ECalComponentId): A list of #ECalComponentId objects
5430  * identifying the objects to remove
5431  * @mod: Type of the removal
5432  * @opflags: bit-or of #ECalOperationFlags
5433  * @cancellable: a #GCancellable; can be %NULL
5434  * @callback: callback to call when a result is ready
5435  * @user_data: user data for the @callback
5436  *
5437  * This function allows the removal of instances of recurrent appointments.
5438  * #ECalComponentId objects can identify specific instances (if rid is not
5439  * %NULL).  If what you want is to remove all instances, use a %NULL rid in
5440  * the #ECalComponentId and #E_CAL_OBJ_MOD_ALL for the @mod.
5441  *
5442  * The call is finished by e_cal_client_remove_objects_finish() from
5443  * the @callback.
5444  *
5445  * Since: 3.6
5446  **/
5447 void
e_cal_client_remove_objects(ECalClient * client,const GSList * ids,ECalObjModType mod,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)5448 e_cal_client_remove_objects (ECalClient *client,
5449                              const GSList *ids,
5450                              ECalObjModType mod,
5451 			     guint32 opflags,
5452                              GCancellable *cancellable,
5453                              GAsyncReadyCallback callback,
5454                              gpointer user_data)
5455 {
5456 	GSimpleAsyncResult *simple;
5457 	AsyncContext *async_context;
5458 
5459 	g_return_if_fail (E_IS_CAL_CLIENT (client));
5460 	g_return_if_fail (ids != NULL);
5461 
5462 	async_context = g_slice_new0 (AsyncContext);
5463 	async_context->ids_list = g_slist_copy_deep (
5464 		(GSList *) ids, (GCopyFunc) e_cal_component_id_copy, NULL);
5465 	async_context->mod = mod;
5466 	async_context->opflags = opflags;
5467 
5468 	simple = g_simple_async_result_new (
5469 		G_OBJECT (client), callback, user_data,
5470 		e_cal_client_remove_objects);
5471 
5472 	g_simple_async_result_set_check_cancellable (simple, cancellable);
5473 
5474 	g_simple_async_result_set_op_res_gpointer (
5475 		simple, async_context, (GDestroyNotify) async_context_free);
5476 
5477 	g_simple_async_result_run_in_thread (
5478 		simple, cal_client_remove_objects_thread,
5479 		G_PRIORITY_DEFAULT, cancellable);
5480 
5481 	g_object_unref (simple);
5482 }
5483 
5484 /**
5485  * e_cal_client_remove_objects_finish:
5486  * @client: an #ECalClient
5487  * @result: a #GAsyncResult
5488  * @error: a #GError to set an error, if any
5489  *
5490  * Finishes previous call of e_cal_client_remove_objects().
5491  *
5492  * Returns: %TRUE if successful, %FALSE otherwise.
5493  *
5494  * Since: 3.6
5495  **/
5496 gboolean
e_cal_client_remove_objects_finish(ECalClient * client,GAsyncResult * result,GError ** error)5497 e_cal_client_remove_objects_finish (ECalClient *client,
5498                                     GAsyncResult *result,
5499                                     GError **error)
5500 {
5501 	GSimpleAsyncResult *simple;
5502 
5503 	g_return_val_if_fail (
5504 		g_simple_async_result_is_valid (
5505 		result, G_OBJECT (client),
5506 		e_cal_client_remove_objects), FALSE);
5507 
5508 	simple = G_SIMPLE_ASYNC_RESULT (result);
5509 
5510 	/* Assume success unless a GError is set. */
5511 	return !g_simple_async_result_propagate_error (simple, error);
5512 }
5513 
5514 /**
5515  * e_cal_client_remove_objects_sync:
5516  * @client: an #ECalClient
5517  * @ids: (element-type ECalComponentId): a list of #ECalComponentId objects
5518  *       identifying the objects to remove
5519  * @mod: Type of the removal
5520  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
5521  * @cancellable: a #GCancellable; can be %NULL
5522  * @error: a #GError to set an error, if any
5523  *
5524  * This function allows the removal of instances of recurrent
5525  * appointments. #ECalComponentId objects can identify specific instances
5526  * (if rid is not %NULL).  If what you want is to remove all instances, use
5527  * a %NULL rid in the #ECalComponentId and #E_CAL_OBJ_MOD_ALL for the @mod.
5528  *
5529  * Returns: %TRUE if successful, %FALSE otherwise.
5530  *
5531  * Since: 3.6
5532  **/
5533 gboolean
e_cal_client_remove_objects_sync(ECalClient * client,const GSList * ids,ECalObjModType mod,guint32 opflags,GCancellable * cancellable,GError ** error)5534 e_cal_client_remove_objects_sync (ECalClient *client,
5535                                   const GSList *ids,
5536                                   ECalObjModType mod,
5537 				  guint32 opflags,
5538                                   GCancellable *cancellable,
5539                                   GError **error)
5540 {
5541 	GVariantBuilder builder;
5542 	GFlagsClass *flags_class;
5543 	GFlagsValue *flags_value;
5544 	GString *mod_flags;
5545 	guint n_valid_uids = 0;
5546 	GError *local_error = NULL;
5547 
5548 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
5549 	g_return_val_if_fail (ids != NULL, FALSE);
5550 
5551 	mod_flags = g_string_new (NULL);
5552 	flags_class = g_type_class_ref (E_TYPE_CAL_OBJ_MOD_TYPE);
5553 	flags_value = g_flags_get_first_value (flags_class, mod);
5554 	while (flags_value != NULL) {
5555 		if (mod_flags->len > 0)
5556 			g_string_append_c (mod_flags, ':');
5557 		g_string_append (mod_flags, flags_value->value_nick);
5558 		mod &= ~flags_value->value;
5559 		flags_value = g_flags_get_first_value (flags_class, mod);
5560 	}
5561 
5562 	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)"));
5563 	while (ids != NULL) {
5564 		ECalComponentId *id = ids->data;
5565 		const gchar *uid, *rid;
5566 		gchar *utf8_uid;
5567 		gchar *utf8_rid;
5568 
5569 		ids = g_slist_next (ids);
5570 
5571 		uid = e_cal_component_id_get_uid (id);
5572 		rid = e_cal_component_id_get_rid (id);
5573 
5574 		if (!uid)
5575 			continue;
5576 
5577 		/* Reject empty UIDs with an OBJECT_NOT_FOUND error for
5578 		 * backward-compatibility, even though INVALID_ARG might
5579 		 * be more appropriate. */
5580 		if (!*uid) {
5581 			local_error = g_error_new_literal (
5582 				E_CAL_CLIENT_ERROR,
5583 				E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND,
5584 				e_cal_client_error_to_string (
5585 				E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND));
5586 			n_valid_uids = 0;
5587 			break;
5588 		}
5589 
5590 		utf8_uid = e_util_utf8_make_valid (uid);
5591 		if (rid)
5592 			utf8_rid = e_util_utf8_make_valid (rid);
5593 		else
5594 			utf8_rid = g_strdup ("");
5595 
5596 		g_variant_builder_add (&builder, "(ss)", utf8_uid, utf8_rid);
5597 
5598 		g_free (utf8_uid);
5599 		g_free (utf8_rid);
5600 
5601 		n_valid_uids++;
5602 	}
5603 
5604 	if (n_valid_uids > 0) {
5605 		e_dbus_calendar_call_remove_objects_sync (
5606 			client->priv->dbus_proxy,
5607 			g_variant_builder_end (&builder),
5608 			mod_flags->str, opflags, cancellable, &local_error);
5609 	} else {
5610 		g_variant_builder_clear (&builder);
5611 	}
5612 
5613 	g_type_class_unref (flags_class);
5614 	g_string_free (mod_flags, TRUE);
5615 
5616 	if (local_error != NULL) {
5617 		g_dbus_error_strip_remote_error (local_error);
5618 		g_propagate_error (error, local_error);
5619 		return FALSE;
5620 	}
5621 
5622 	return TRUE;
5623 }
5624 
5625 /* Helper for e_cal_client_receive_objects() */
5626 static void
cal_client_receive_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)5627 cal_client_receive_objects_thread (GSimpleAsyncResult *simple,
5628                                    GObject *source_object,
5629                                    GCancellable *cancellable)
5630 {
5631 	AsyncContext *async_context;
5632 	GError *local_error = NULL;
5633 
5634 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
5635 
5636 	if (!e_cal_client_receive_objects_sync (
5637 		E_CAL_CLIENT (source_object),
5638 		async_context->in_comp,
5639 		async_context->opflags,
5640 		cancellable, &local_error)) {
5641 
5642 		if (!local_error)
5643 			local_error = g_error_new_literal (
5644 				E_CLIENT_ERROR,
5645 				E_CLIENT_ERROR_OTHER_ERROR,
5646 				_("Unknown error"));
5647 	}
5648 
5649 	if (local_error != NULL)
5650 		g_simple_async_result_take_error (simple, local_error);
5651 }
5652 
5653 /**
5654  * e_cal_client_receive_objects:
5655  * @client: an #ECalClient
5656  * @icalcomp: An #ICalComponent
5657  * @opflags: bit-or of #ECalOperationFlags
5658  * @cancellable: a #GCancellable; can be %NULL
5659  * @callback: callback to call when a result is ready
5660  * @user_data: user data for the @callback
5661  *
5662  * Makes the backend receive the set of iCalendar objects specified in the
5663  * @icalcomp argument. This is used for iTIP confirmation/cancellation
5664  * messages for scheduled meetings.
5665  *
5666  * The call is finished by e_cal_client_receive_objects_finish() from
5667  * the @callback.
5668  *
5669  * Since: 3.2
5670  **/
5671 void
e_cal_client_receive_objects(ECalClient * client,ICalComponent * icalcomp,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)5672 e_cal_client_receive_objects (ECalClient *client,
5673                               ICalComponent *icalcomp,
5674 			      guint32 opflags,
5675                               GCancellable *cancellable,
5676                               GAsyncReadyCallback callback,
5677                               gpointer user_data)
5678 {
5679 	GSimpleAsyncResult *simple;
5680 	AsyncContext *async_context;
5681 
5682 	g_return_if_fail (E_IS_CAL_CLIENT (client));
5683 	g_return_if_fail (icalcomp != NULL);
5684 
5685 	async_context = g_slice_new0 (AsyncContext);
5686 	async_context->in_comp = i_cal_component_clone (icalcomp);
5687 	async_context->opflags = opflags;
5688 
5689 	simple = g_simple_async_result_new (
5690 		G_OBJECT (client), callback, user_data,
5691 		e_cal_client_receive_objects);
5692 
5693 	g_simple_async_result_set_check_cancellable (simple, cancellable);
5694 
5695 	g_simple_async_result_set_op_res_gpointer (
5696 		simple, async_context, (GDestroyNotify) async_context_free);
5697 
5698 	g_simple_async_result_run_in_thread (
5699 		simple, cal_client_receive_objects_thread,
5700 		G_PRIORITY_DEFAULT, cancellable);
5701 
5702 	g_object_unref (simple);
5703 }
5704 
5705 /**
5706  * e_cal_client_receive_objects_finish:
5707  * @client: an #ECalClient
5708  * @result: a #GAsyncResult
5709  * @error: a #GError to set an error, if any
5710  *
5711  * Finishes previous call of e_cal_client_receive_objects().
5712  *
5713  * Returns: %TRUE if successful, %FALSE otherwise.
5714  *
5715  * Since: 3.2
5716  **/
5717 gboolean
e_cal_client_receive_objects_finish(ECalClient * client,GAsyncResult * result,GError ** error)5718 e_cal_client_receive_objects_finish (ECalClient *client,
5719                                      GAsyncResult *result,
5720                                      GError **error)
5721 {
5722 	GSimpleAsyncResult *simple;
5723 
5724 	g_return_val_if_fail (
5725 		g_simple_async_result_is_valid (
5726 		result, G_OBJECT (client),
5727 		e_cal_client_receive_objects), FALSE);
5728 
5729 	simple = G_SIMPLE_ASYNC_RESULT (result);
5730 
5731 	/* Assume success unless a GError is set. */
5732 	return !g_simple_async_result_propagate_error (simple, error);
5733 }
5734 
5735 /**
5736  * e_cal_client_receive_objects_sync:
5737  * @client: an #ECalClient
5738  * @icalcomp: An #ICalComponent
5739  * @opflags: bit-or of #ECalOperationFlags
5740  * @cancellable: a #GCancellable; can be %NULL
5741  * @error: a #GError to set an error, if any
5742  *
5743  * Makes the backend receive the set of iCalendar objects specified in the
5744  * @icalcomp argument. This is used for iTIP confirmation/cancellation
5745  * messages for scheduled meetings.
5746  *
5747  * Returns: %TRUE if successful, %FALSE otherwise.
5748  *
5749  * Since: 3.2
5750  **/
5751 gboolean
e_cal_client_receive_objects_sync(ECalClient * client,ICalComponent * icalcomp,guint32 opflags,GCancellable * cancellable,GError ** error)5752 e_cal_client_receive_objects_sync (ECalClient *client,
5753                                    ICalComponent *icalcomp,
5754 				   guint32 opflags,
5755                                    GCancellable *cancellable,
5756                                    GError **error)
5757 {
5758 	gchar *ical_string;
5759 	gchar *utf8_ical_string;
5760 	GError *local_error = NULL;
5761 
5762 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
5763 
5764 	ical_string = i_cal_component_as_ical_string (icalcomp);
5765 	utf8_ical_string = e_util_utf8_make_valid (ical_string);
5766 
5767 	e_dbus_calendar_call_receive_objects_sync (
5768 		client->priv->dbus_proxy, utf8_ical_string, opflags,
5769 		cancellable, &local_error);
5770 
5771 	g_free (utf8_ical_string);
5772 	g_free (ical_string);
5773 
5774 	if (local_error != NULL) {
5775 		g_dbus_error_strip_remote_error (local_error);
5776 		g_propagate_error (error, local_error);
5777 		return FALSE;
5778 	}
5779 
5780 	return TRUE;
5781 }
5782 
5783 /* Helper for e_cal_client_send_objects() */
5784 static void
cal_client_send_objects_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)5785 cal_client_send_objects_thread (GSimpleAsyncResult *simple,
5786                                 GObject *source_object,
5787                                 GCancellable *cancellable)
5788 {
5789 	AsyncContext *async_context;
5790 	GError *local_error = NULL;
5791 
5792 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
5793 
5794 	if (!e_cal_client_send_objects_sync (
5795 		E_CAL_CLIENT (source_object),
5796 		async_context->in_comp,
5797 		async_context->opflags,
5798 		&async_context->string_list,
5799 		&async_context->out_comp,
5800 		cancellable, &local_error)) {
5801 
5802 		if (!local_error)
5803 			local_error = g_error_new_literal (
5804 				E_CLIENT_ERROR,
5805 				E_CLIENT_ERROR_OTHER_ERROR,
5806 				_("Unknown error"));
5807 	}
5808 
5809 	if (local_error != NULL)
5810 		g_simple_async_result_take_error (simple, local_error);
5811 }
5812 
5813 /**
5814  * e_cal_client_send_objects:
5815  * @client: an #ECalClient
5816  * @icalcomp: An #ICalComponent to be sent
5817  * @opflags: bit-or of #ECalOperationFlags
5818  * @cancellable: a #GCancellable; can be %NULL
5819  * @callback: callback to call when a result is ready
5820  * @user_data: user data for the @callback
5821  *
5822  * Requests a calendar backend to send meeting information stored in @icalcomp.
5823  * The backend can modify this component and request a send to particular users.
5824  * The call is finished by e_cal_client_send_objects_finish() from
5825  * the @callback.
5826  *
5827  * Since: 3.2
5828  **/
5829 void
e_cal_client_send_objects(ECalClient * client,ICalComponent * icalcomp,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)5830 e_cal_client_send_objects (ECalClient *client,
5831                            ICalComponent *icalcomp,
5832 			   guint32 opflags,
5833                            GCancellable *cancellable,
5834                            GAsyncReadyCallback callback,
5835                            gpointer user_data)
5836 {
5837 	GSimpleAsyncResult *simple;
5838 	AsyncContext *async_context;
5839 
5840 	g_return_if_fail (E_IS_CAL_CLIENT (client));
5841 	g_return_if_fail (icalcomp != NULL);
5842 
5843 	async_context = g_slice_new0 (AsyncContext);
5844 	async_context->in_comp = i_cal_component_clone (icalcomp);
5845 	async_context->opflags = opflags;
5846 
5847 	simple = g_simple_async_result_new (
5848 		G_OBJECT (client), callback, user_data,
5849 		e_cal_client_send_objects);
5850 
5851 	g_simple_async_result_set_check_cancellable (simple, cancellable);
5852 
5853 	g_simple_async_result_set_op_res_gpointer (
5854 		simple, async_context, (GDestroyNotify) async_context_free);
5855 
5856 	g_simple_async_result_run_in_thread (
5857 		simple, cal_client_send_objects_thread,
5858 		G_PRIORITY_DEFAULT, cancellable);
5859 
5860 	g_object_unref (simple);
5861 }
5862 
5863 /**
5864  * e_cal_client_send_objects_finish:
5865  * @client: an #ECalClient
5866  * @result: a #GAsyncResult
5867  * @out_users: (out) (transfer full) (element-type utf8): List of users to send
5868  *             the @out_modified_icalcomp to
5869  * @out_modified_icalcomp: (out) (transfer full): Return value for the #ICalComponent to be sent
5870  * @error: a #GError to set an error, if any
5871  *
5872  * Finishes previous call of e_cal_client_send_objects() and
5873  * populates @out_users with a list of users to send @out_modified_icalcomp to.
5874  *
5875  * The @out_users list should be freed with e_client_util_free_string_slist()
5876  * and the @out_modified_icalcomp should be freed with g_object_unref().
5877  *
5878  * Returns: %TRUE if successful, %FALSE otherwise.
5879  *
5880  * Since: 3.2
5881  **/
5882 gboolean
e_cal_client_send_objects_finish(ECalClient * client,GAsyncResult * result,GSList ** out_users,ICalComponent ** out_modified_icalcomp,GError ** error)5883 e_cal_client_send_objects_finish (ECalClient *client,
5884                                   GAsyncResult *result,
5885                                   GSList **out_users,
5886                                   ICalComponent **out_modified_icalcomp,
5887                                   GError **error)
5888 {
5889 	GSimpleAsyncResult *simple;
5890 	AsyncContext *async_context;
5891 
5892 	g_return_val_if_fail (
5893 		g_simple_async_result_is_valid (
5894 		result, G_OBJECT (client),
5895 		e_cal_client_send_objects), FALSE);
5896 
5897 	simple = G_SIMPLE_ASYNC_RESULT (result);
5898 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
5899 
5900 	if (g_simple_async_result_propagate_error (simple, error))
5901 		return FALSE;
5902 
5903 	g_return_val_if_fail (async_context->out_comp != NULL, FALSE);
5904 
5905 	if (out_users != NULL) {
5906 		*out_users = async_context->string_list;
5907 		async_context->string_list = NULL;
5908 	}
5909 
5910 	if (out_modified_icalcomp != NULL) {
5911 		*out_modified_icalcomp = async_context->out_comp;
5912 		async_context->out_comp = NULL;
5913 	}
5914 
5915 	return TRUE;
5916 }
5917 
5918 /**
5919  * e_cal_client_send_objects_sync:
5920  * @client: an #ECalClient
5921  * @icalcomp: An #ICalComponent to be sent
5922  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
5923  * @out_users: (out) (transfer full) (element-type utf8): List of users to send the
5924  *             @out_modified_icalcomp to
5925  * @out_modified_icalcomp: (out) (transfer full): Return value for the #ICalComponent to be sent
5926  * @cancellable: a #GCancellable; can be %NULL
5927  * @error: a #GError to set an error, if any
5928  *
5929  * Requests a calendar backend to send meeting information stored in @icalcomp.
5930  * The backend can modify this component and request a send to users in the
5931  * @out_users list.
5932  *
5933  * The @out_users list should be freed with e_client_util_free_string_slist()
5934  * and the @out_modified_icalcomp should be freed with g_object_unref().
5935  *
5936  * Returns: %TRUE if successful, %FALSE otherwise.
5937  *
5938  * Since: 3.2
5939  **/
5940 gboolean
e_cal_client_send_objects_sync(ECalClient * client,ICalComponent * icalcomp,guint32 opflags,GSList ** out_users,ICalComponent ** out_modified_icalcomp,GCancellable * cancellable,GError ** error)5941 e_cal_client_send_objects_sync (ECalClient *client,
5942                                 ICalComponent *icalcomp,
5943 				guint32 opflags,
5944                                 GSList **out_users,
5945                                 ICalComponent **out_modified_icalcomp,
5946                                 GCancellable *cancellable,
5947                                 GError **error)
5948 {
5949 	gchar *ical_string;
5950 	gchar *utf8_ical_string;
5951 	gchar **users = NULL;
5952 	gchar *out_ical_string = NULL;
5953 	GError *local_error = NULL;
5954 
5955 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
5956 	g_return_val_if_fail (icalcomp != NULL, FALSE);
5957 	g_return_val_if_fail (out_users != NULL, FALSE);
5958 	g_return_val_if_fail (out_modified_icalcomp != NULL, FALSE);
5959 
5960 	ical_string = i_cal_component_as_ical_string (icalcomp);
5961 	utf8_ical_string = e_util_utf8_make_valid (ical_string);
5962 
5963 	e_dbus_calendar_call_send_objects_sync (
5964 		client->priv->dbus_proxy, utf8_ical_string, opflags,
5965 		&users, &out_ical_string, cancellable, &local_error);
5966 
5967 	g_free (utf8_ical_string);
5968 	g_free (ical_string);
5969 
5970 	/* Sanity check. */
5971 	g_return_val_if_fail (
5972 		((out_ical_string != NULL) && (local_error == NULL)) ||
5973 		((out_ical_string == NULL) && (local_error != NULL)), FALSE);
5974 
5975 	if (local_error != NULL) {
5976 		g_warn_if_fail (users == NULL);
5977 		g_dbus_error_strip_remote_error (local_error);
5978 		g_propagate_error (error, local_error);
5979 		return FALSE;
5980 	}
5981 
5982 	icalcomp = i_cal_parser_parse_string (out_ical_string);
5983 
5984 	g_free (out_ical_string);
5985 
5986 	if (icalcomp != NULL) {
5987 		*out_modified_icalcomp = icalcomp;
5988 	} else {
5989 		g_set_error_literal (
5990 			error, E_CAL_CLIENT_ERROR,
5991 			E_CAL_CLIENT_ERROR_INVALID_OBJECT,
5992 			e_cal_client_error_to_string (
5993 			E_CAL_CLIENT_ERROR_INVALID_OBJECT));
5994 		g_strfreev (users);
5995 		return FALSE;
5996 	}
5997 
5998 	if (users != NULL) {
5999 		GSList *tmp = NULL;
6000 		gint ii;
6001 
6002 		for (ii = 0; users[ii] != NULL; ii++) {
6003 			tmp = g_slist_prepend (tmp, users[ii]);
6004 			users[ii] = NULL;
6005 		}
6006 
6007 		*out_users = g_slist_reverse (tmp);
6008 	}
6009 
6010 	g_strfreev (users);
6011 
6012 	return TRUE;
6013 }
6014 
6015 /* Helper for e_cal_client_get_attachment_uris() */
6016 static void
cal_client_get_attachment_uris_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)6017 cal_client_get_attachment_uris_thread (GSimpleAsyncResult *simple,
6018                                        GObject *source_object,
6019                                        GCancellable *cancellable)
6020 {
6021 	AsyncContext *async_context;
6022 	GError *local_error = NULL;
6023 
6024 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
6025 
6026 	if (!e_cal_client_get_attachment_uris_sync (
6027 		E_CAL_CLIENT (source_object),
6028 		async_context->uid,
6029 		async_context->rid,
6030 		&async_context->string_list,
6031 		cancellable, &local_error)) {
6032 
6033 		if (!local_error)
6034 			local_error = g_error_new_literal (
6035 				E_CLIENT_ERROR,
6036 				E_CLIENT_ERROR_OTHER_ERROR,
6037 				_("Unknown error"));
6038 	}
6039 
6040 	if (local_error != NULL)
6041 		g_simple_async_result_take_error (simple, local_error);
6042 }
6043 
6044 /**
6045  * e_cal_client_get_attachment_uris:
6046  * @client: an #ECalClient
6047  * @uid: Unique identifier for a calendar component
6048  * @rid: (nullable): Recurrence identifier
6049  * @cancellable: a #GCancellable; can be %NULL
6050  * @callback: callback to call when a result is ready
6051  * @user_data: user data for the @callback
6052  *
6053  * Queries a calendar for a specified component's object attachment uris.
6054  * The call is finished by e_cal_client_get_attachment_uris_finish() from
6055  * the @callback.
6056  *
6057  * Since: 3.2
6058  **/
6059 void
e_cal_client_get_attachment_uris(ECalClient * client,const gchar * uid,const gchar * rid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)6060 e_cal_client_get_attachment_uris (ECalClient *client,
6061                                   const gchar *uid,
6062                                   const gchar *rid,
6063                                   GCancellable *cancellable,
6064                                   GAsyncReadyCallback callback,
6065                                   gpointer user_data)
6066 {
6067 	GSimpleAsyncResult *simple;
6068 	AsyncContext *async_context;
6069 
6070 	g_return_if_fail (E_CAL_CLIENT (client));
6071 	g_return_if_fail (uid != NULL);
6072 	/* rid is optional */
6073 
6074 	async_context = g_slice_new0 (AsyncContext);
6075 	async_context->uid = g_strdup (uid);
6076 	async_context->rid = g_strdup (rid);
6077 
6078 	simple = g_simple_async_result_new (
6079 		G_OBJECT (client), callback, user_data,
6080 		e_cal_client_get_attachment_uris);
6081 
6082 	g_simple_async_result_set_check_cancellable (simple, cancellable);
6083 
6084 	g_simple_async_result_set_op_res_gpointer (
6085 		simple, async_context, (GDestroyNotify) async_context_free);
6086 
6087 	g_simple_async_result_run_in_thread (
6088 		simple, cal_client_get_attachment_uris_thread,
6089 		G_PRIORITY_DEFAULT, cancellable);
6090 
6091 	g_object_unref (simple);
6092 }
6093 
6094 /**
6095  * e_cal_client_get_attachment_uris_finish:
6096  * @client: an #ECalClient
6097  * @result: a #GAsyncResult
6098  * @out_attachment_uris: (out) (element-type utf8): Return location for the
6099  *                       list of attachment URIs
6100  * @error: a #GError to set an error, if any
6101  *
6102  * Finishes previous call of e_cal_client_get_attachment_uris() and
6103  * sets @out_attachment_uris to uris for component's attachments.
6104  * The list should be freed with e_client_util_free_string_slist().
6105  *
6106  * Returns: %TRUE if successful, %FALSE otherwise.
6107  *
6108  * Since: 3.2
6109  **/
6110 gboolean
e_cal_client_get_attachment_uris_finish(ECalClient * client,GAsyncResult * result,GSList ** out_attachment_uris,GError ** error)6111 e_cal_client_get_attachment_uris_finish (ECalClient *client,
6112                                          GAsyncResult *result,
6113                                          GSList **out_attachment_uris,
6114                                          GError **error)
6115 {
6116 	GSimpleAsyncResult *simple;
6117 	AsyncContext *async_context;
6118 
6119 	g_return_val_if_fail (
6120 		g_simple_async_result_is_valid (
6121 		result, G_OBJECT (client),
6122 		e_cal_client_get_attachment_uris), FALSE);
6123 
6124 	simple = G_SIMPLE_ASYNC_RESULT (result);
6125 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
6126 
6127 	if (g_simple_async_result_propagate_error (simple, error))
6128 		return FALSE;
6129 
6130 	if (out_attachment_uris != NULL) {
6131 		*out_attachment_uris = async_context->string_list;
6132 		async_context->string_list = NULL;
6133 	}
6134 
6135 	return TRUE;
6136 }
6137 
6138 /**
6139  * e_cal_client_get_attachment_uris_sync:
6140  * @client: an #ECalClient
6141  * @uid: Unique identifier for a calendar component
6142  * @rid: (nullable): Recurrence identifier
6143  * @out_attachment_uris: (out) (element-type utf8): Return location for the
6144  *                       list of attachment URIs
6145  * @cancellable: a #GCancellable; can be %NULL
6146  * @error: a #GError to set an error, if any
6147  *
6148  * Queries a calendar for a specified component's object attachment URIs.
6149  * The list should be freed with e_client_util_free_string_slist().
6150  *
6151  * Returns: %TRUE if successful, %FALSE otherwise.
6152  *
6153  * Since: 3.2
6154  **/
6155 gboolean
e_cal_client_get_attachment_uris_sync(ECalClient * client,const gchar * uid,const gchar * rid,GSList ** out_attachment_uris,GCancellable * cancellable,GError ** error)6156 e_cal_client_get_attachment_uris_sync (ECalClient *client,
6157                                        const gchar *uid,
6158                                        const gchar *rid,
6159                                        GSList **out_attachment_uris,
6160                                        GCancellable *cancellable,
6161                                        GError **error)
6162 {
6163 	gchar *utf8_uid;
6164 	gchar *utf8_rid;
6165 	gchar **uris = NULL;
6166 	GError *local_error = NULL;
6167 
6168 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
6169 	g_return_val_if_fail (uid != NULL, FALSE);
6170 	g_return_val_if_fail (out_attachment_uris != NULL, FALSE);
6171 
6172 	if (rid == NULL)
6173 		rid = "";
6174 
6175 	utf8_uid = e_util_utf8_make_valid (uid);
6176 	utf8_rid = e_util_utf8_make_valid (rid);
6177 
6178 	e_dbus_calendar_call_get_attachment_uris_sync (
6179 		client->priv->dbus_proxy, utf8_uid, utf8_rid,
6180 		&uris, cancellable, &local_error);
6181 
6182 	g_free (utf8_uid);
6183 	g_free (utf8_rid);
6184 
6185 	/* Sanity check. */
6186 	g_return_val_if_fail (
6187 		((uris != NULL) && (local_error == NULL)) ||
6188 		((uris == NULL) && (local_error != NULL)), FALSE);
6189 
6190 	if (uris != NULL) {
6191 		GSList *tmp = NULL;
6192 		gint ii;
6193 
6194 		for (ii = 0; uris[ii] != NULL; ii++) {
6195 			tmp = g_slist_prepend (tmp, uris[ii]);
6196 			uris[ii] = NULL;
6197 		}
6198 
6199 		*out_attachment_uris = g_slist_reverse (tmp);
6200 
6201 		g_free (uris);
6202 	}
6203 
6204 	if (local_error != NULL) {
6205 		g_dbus_error_strip_remote_error (local_error);
6206 		g_propagate_error (error, local_error);
6207 		return FALSE;
6208 	}
6209 
6210 	return TRUE;
6211 }
6212 
6213 /* Helper for e_cal_client_discard_alarm() */
6214 static void
cal_client_discard_alarm_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)6215 cal_client_discard_alarm_thread (GSimpleAsyncResult *simple,
6216                                  GObject *source_object,
6217                                  GCancellable *cancellable)
6218 {
6219 	AsyncContext *async_context;
6220 	GError *local_error = NULL;
6221 
6222 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
6223 
6224 	if (!e_cal_client_discard_alarm_sync (
6225 		E_CAL_CLIENT (source_object),
6226 		async_context->uid,
6227 		async_context->rid,
6228 		async_context->auid,
6229 		async_context->opflags,
6230 		cancellable, &local_error)) {
6231 
6232 		if (!local_error)
6233 			local_error = g_error_new_literal (
6234 				E_CLIENT_ERROR,
6235 				E_CLIENT_ERROR_OTHER_ERROR,
6236 				_("Unknown error"));
6237 	}
6238 
6239 	if (local_error != NULL)
6240 		g_simple_async_result_take_error (simple, local_error);
6241 }
6242 
6243 /**
6244  * e_cal_client_discard_alarm:
6245  * @client: an #ECalClient
6246  * @uid: Unique identifier for a calendar component
6247  * @rid: (nullable): Recurrence identifier
6248  * @auid: Alarm identifier to discard
6249  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
6250  * @cancellable: a #GCancellable; can be %NULL
6251  * @callback: callback to call when a result is ready
6252  * @user_data: user data for the @callback
6253  *
6254  * Discards alarm @auid from a given component identified by @uid and @rid.
6255  * The call is finished by e_cal_client_discard_alarm_finish() from
6256  * the @callback.
6257  *
6258  * Since: 3.2
6259  **/
6260 void
e_cal_client_discard_alarm(ECalClient * client,const gchar * uid,const gchar * rid,const gchar * auid,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)6261 e_cal_client_discard_alarm (ECalClient *client,
6262                             const gchar *uid,
6263                             const gchar *rid,
6264                             const gchar *auid,
6265 			    guint32 opflags,
6266                             GCancellable *cancellable,
6267                             GAsyncReadyCallback callback,
6268                             gpointer user_data)
6269 {
6270 	GSimpleAsyncResult *simple;
6271 	AsyncContext *async_context;
6272 
6273 	g_return_if_fail (E_IS_CAL_CLIENT (client));
6274 	g_return_if_fail (uid != NULL);
6275 	/* rid is optional */
6276 	g_return_if_fail (auid != NULL);
6277 
6278 	async_context = g_slice_new0 (AsyncContext);
6279 	async_context->uid = g_strdup (uid);
6280 	async_context->rid = g_strdup (rid);
6281 	async_context->auid = g_strdup (auid);
6282 	async_context->opflags = opflags;
6283 
6284 	simple = g_simple_async_result_new (
6285 		G_OBJECT (client), callback, user_data,
6286 		e_cal_client_discard_alarm);
6287 
6288 	g_simple_async_result_set_check_cancellable (simple, cancellable);
6289 
6290 	g_simple_async_result_set_op_res_gpointer (
6291 		simple, async_context, (GDestroyNotify) async_context_free);
6292 
6293 	g_simple_async_result_run_in_thread (
6294 		simple, cal_client_discard_alarm_thread,
6295 		G_PRIORITY_DEFAULT, cancellable);
6296 
6297 	g_object_unref (simple);
6298 }
6299 
6300 /**
6301  * e_cal_client_discard_alarm_finish:
6302  * @client: an #ECalClient
6303  * @result: a #GAsyncResult
6304  * @error: a #GError to set an error, if any
6305  *
6306  * Finishes previous call of e_cal_client_discard_alarm().
6307  *
6308  * Returns: %TRUE if successful, %FALSE otherwise.
6309  *
6310  * Since: 3.2
6311  **/
6312 gboolean
e_cal_client_discard_alarm_finish(ECalClient * client,GAsyncResult * result,GError ** error)6313 e_cal_client_discard_alarm_finish (ECalClient *client,
6314                                    GAsyncResult *result,
6315                                    GError **error)
6316 {
6317 	GSimpleAsyncResult *simple;
6318 
6319 	g_return_val_if_fail (
6320 		g_simple_async_result_is_valid (
6321 		result, G_OBJECT (client),
6322 		e_cal_client_discard_alarm), FALSE);
6323 
6324 	simple = G_SIMPLE_ASYNC_RESULT (result);
6325 
6326 	/* Assume success unless a GError is set. */
6327 	return !g_simple_async_result_propagate_error (simple, error);
6328 }
6329 
6330 /**
6331  * e_cal_client_discard_alarm_sync:
6332  * @client: an #ECalClient
6333  * @uid: Unique identifier for a calendar component
6334  * @rid: (nullable): Recurrence identifier
6335  * @auid: Alarm identifier to discard
6336  * @opflags: (type ECalOperationFlags): bit-or of #ECalOperationFlags
6337  * @cancellable: a #GCancellable; can be %NULL
6338  * @error: a #GError to set an error, if any
6339  *
6340  * Discards alarm @auid from a given component identified by @uid and @rid.
6341  *
6342  * Returns: %TRUE if successful, %FALSE otherwise.
6343  *
6344  * Since: 3.2
6345  **/
6346 gboolean
e_cal_client_discard_alarm_sync(ECalClient * client,const gchar * uid,const gchar * rid,const gchar * auid,guint32 opflags,GCancellable * cancellable,GError ** error)6347 e_cal_client_discard_alarm_sync (ECalClient *client,
6348                                  const gchar *uid,
6349                                  const gchar *rid,
6350                                  const gchar *auid,
6351 				 guint32 opflags,
6352                                  GCancellable *cancellable,
6353                                  GError **error)
6354 {
6355 	gchar *utf8_uid;
6356 	gchar *utf8_rid;
6357 	gchar *utf8_auid;
6358 	GError *local_error = NULL;
6359 
6360 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
6361 	g_return_val_if_fail (uid != NULL, FALSE);
6362 	g_return_val_if_fail (auid != NULL, FALSE);
6363 
6364 	if (rid == NULL)
6365 		rid = "";
6366 
6367 	utf8_uid = e_util_utf8_make_valid (uid);
6368 	utf8_rid = e_util_utf8_make_valid (rid);
6369 	utf8_auid = e_util_utf8_make_valid (auid);
6370 
6371 	e_dbus_calendar_call_discard_alarm_sync (
6372 		client->priv->dbus_proxy,
6373 		utf8_uid, utf8_rid, utf8_auid, opflags,
6374 		cancellable, &local_error);
6375 
6376 	g_free (utf8_uid);
6377 	g_free (utf8_rid);
6378 	g_free (utf8_auid);
6379 
6380 	if (local_error != NULL) {
6381 		g_dbus_error_strip_remote_error (local_error);
6382 		g_propagate_error (error, local_error);
6383 		return FALSE;
6384 	}
6385 
6386 	return TRUE;
6387 }
6388 
6389 /* Helper for e_cal_client_get_view() */
6390 static void
cal_client_get_view_in_dbus_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)6391 cal_client_get_view_in_dbus_thread (GSimpleAsyncResult *simple,
6392                                     GObject *source_object,
6393                                     GCancellable *cancellable)
6394 {
6395 	ECalClient *client = E_CAL_CLIENT (source_object);
6396 	AsyncContext *async_context;
6397 	gchar *utf8_sexp;
6398 	gchar *object_path = NULL;
6399 	GError *local_error = NULL;
6400 
6401 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
6402 
6403 	utf8_sexp = e_util_utf8_make_valid (async_context->sexp);
6404 
6405 	e_dbus_calendar_call_get_view_sync (
6406 		client->priv->dbus_proxy, utf8_sexp,
6407 		&object_path, cancellable, &local_error);
6408 
6409 	g_free (utf8_sexp);
6410 
6411 	/* Sanity check. */
6412 	g_return_if_fail (
6413 		((object_path != NULL) && (local_error == NULL)) ||
6414 		((object_path == NULL) && (local_error != NULL)));
6415 
6416 	if (object_path != NULL) {
6417 		GDBusConnection *connection;
6418 		ECalClientView *client_view;
6419 
6420 		connection = g_dbus_proxy_get_connection (
6421 			G_DBUS_PROXY (client->priv->dbus_proxy));
6422 
6423 		client_view = g_initable_new (
6424 			E_TYPE_CAL_CLIENT_VIEW,
6425 			cancellable, &local_error,
6426 			"client", client,
6427 			"connection", connection,
6428 			"object-path", object_path,
6429 			NULL);
6430 
6431 		/* Sanity check. */
6432 		g_return_if_fail (
6433 			((client_view != NULL) && (local_error == NULL)) ||
6434 			((client_view == NULL) && (local_error != NULL)));
6435 
6436 		async_context->client_view = client_view;
6437 
6438 		g_free (object_path);
6439 	}
6440 
6441 	if (local_error != NULL) {
6442 		g_dbus_error_strip_remote_error (local_error);
6443 		g_simple_async_result_take_error (simple, local_error);
6444 	}
6445 }
6446 
6447 /**
6448  * e_cal_client_get_view:
6449  * @client: an #ECalClient
6450  * @sexp: an S-expression representing the query.
6451  * @cancellable: a #GCancellable; can be %NULL
6452  * @callback: callback to call when a result is ready
6453  * @user_data: user data for the @callback
6454  *
6455  * Query @client with @sexp, creating an #ECalClientView.
6456  * The call is finished by e_cal_client_get_view_finish()
6457  * from the @callback.
6458  *
6459  * Since: 3.2
6460  **/
6461 void
e_cal_client_get_view(ECalClient * client,const gchar * sexp,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)6462 e_cal_client_get_view (ECalClient *client,
6463                        const gchar *sexp,
6464                        GCancellable *cancellable,
6465                        GAsyncReadyCallback callback,
6466                        gpointer user_data)
6467 {
6468 	GSimpleAsyncResult *simple;
6469 	AsyncContext *async_context;
6470 
6471 	g_return_if_fail (E_IS_CAL_CLIENT (client));
6472 	g_return_if_fail (sexp != NULL);
6473 
6474 	async_context = g_slice_new0 (AsyncContext);
6475 	async_context->sexp = g_strdup (sexp);
6476 
6477 	simple = g_simple_async_result_new (
6478 		G_OBJECT (client), callback, user_data,
6479 		e_cal_client_get_view);
6480 
6481 	g_simple_async_result_set_check_cancellable (simple, cancellable);
6482 
6483 	g_simple_async_result_set_op_res_gpointer (
6484 		simple, async_context, (GDestroyNotify) async_context_free);
6485 
6486 	cal_client_run_in_dbus_thread (
6487 		simple, cal_client_get_view_in_dbus_thread,
6488 		G_PRIORITY_DEFAULT, cancellable);
6489 
6490 	g_object_unref (simple);
6491 }
6492 
6493 /**
6494  * e_cal_client_get_view_finish:
6495  * @client: an #ECalClient
6496  * @result: a #GAsyncResult
6497  * @out_view: (out) (transfer full): an #ECalClientView
6498  * @error: a #GError to set an error, if any
6499  *
6500  * Finishes previous call of e_cal_client_get_view().
6501  * If successful, then the @out_view is set to newly allocated #ECalClientView,
6502  * which should be freed with g_object_unref().
6503  *
6504  * Returns: %TRUE if successful, %FALSE otherwise.
6505  *
6506  * Since: 3.2
6507  **/
6508 gboolean
e_cal_client_get_view_finish(ECalClient * client,GAsyncResult * result,ECalClientView ** out_view,GError ** error)6509 e_cal_client_get_view_finish (ECalClient *client,
6510                               GAsyncResult *result,
6511                               ECalClientView **out_view,
6512                               GError **error)
6513 {
6514 	GSimpleAsyncResult *simple;
6515 	AsyncContext *async_context;
6516 
6517 	g_return_val_if_fail (
6518 		g_simple_async_result_is_valid (
6519 		result, G_OBJECT (client),
6520 		e_cal_client_get_view), FALSE);
6521 
6522 	simple = G_SIMPLE_ASYNC_RESULT (result);
6523 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
6524 
6525 	if (g_simple_async_result_propagate_error (simple, error))
6526 		return FALSE;
6527 
6528 	g_return_val_if_fail (async_context->client_view != NULL, FALSE);
6529 
6530 	if (out_view != NULL)
6531 		*out_view = g_object_ref (async_context->client_view);
6532 
6533 	return TRUE;
6534 }
6535 
6536 /**
6537  * e_cal_client_get_view_sync:
6538  * @client: an #ECalClient
6539  * @sexp: an S-expression representing the query.
6540  * @out_view: (out): an #ECalClientView
6541  * @cancellable: a #GCancellable; can be %NULL
6542  * @error: a #GError to set an error, if any
6543  *
6544  * Query @client with @sexp, creating an #ECalClientView.
6545  * If successful, then the @out_view is set to newly allocated #ECalClientView,
6546  * which should be freed with g_object_unref().
6547  *
6548  * Returns: %TRUE if successful, %FALSE otherwise.
6549  *
6550  * Since: 3.2
6551  **/
6552 gboolean
e_cal_client_get_view_sync(ECalClient * client,const gchar * sexp,ECalClientView ** out_view,GCancellable * cancellable,GError ** error)6553 e_cal_client_get_view_sync (ECalClient *client,
6554                             const gchar *sexp,
6555                             ECalClientView **out_view,
6556                             GCancellable *cancellable,
6557                             GError **error)
6558 {
6559 	EAsyncClosure *closure;
6560 	GAsyncResult *result;
6561 	gboolean success;
6562 
6563 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
6564 	g_return_val_if_fail (sexp != NULL, FALSE);
6565 	g_return_val_if_fail (out_view != NULL, FALSE);
6566 
6567 	closure = e_async_closure_new ();
6568 
6569 	e_cal_client_get_view (
6570 		client, sexp, cancellable,
6571 		e_async_closure_callback, closure);
6572 
6573 	result = e_async_closure_wait (closure);
6574 
6575 	success = e_cal_client_get_view_finish (
6576 		client, result, out_view, error);
6577 
6578 	e_async_closure_free (closure);
6579 
6580 	return success;
6581 }
6582 
6583 /* Helper for e_cal_client_get_timezone() */
6584 static void
cal_client_get_timezone_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)6585 cal_client_get_timezone_thread (GSimpleAsyncResult *simple,
6586                                 GObject *source_object,
6587                                 GCancellable *cancellable)
6588 {
6589 	AsyncContext *async_context;
6590 	GError *local_error = NULL;
6591 
6592 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
6593 
6594 	if (!e_cal_client_get_timezone_sync (
6595 		E_CAL_CLIENT (source_object),
6596 		async_context->tzid,
6597 		&async_context->zone,
6598 		cancellable, &local_error)) {
6599 
6600 		if (!local_error)
6601 			local_error = g_error_new_literal (
6602 				E_CLIENT_ERROR,
6603 				E_CLIENT_ERROR_OTHER_ERROR,
6604 				_("Unknown error"));
6605 	}
6606 
6607 	if (local_error != NULL)
6608 		g_simple_async_result_take_error (simple, local_error);
6609 }
6610 
6611 /**
6612  * e_cal_client_get_timezone:
6613  * @client: an #ECalClient
6614  * @tzid: ID of the timezone to retrieve
6615  * @cancellable: a #GCancellable; can be %NULL
6616  * @callback: callback to call when a result is ready
6617  * @user_data: user data for the @callback
6618  *
6619  * Retrieves a timezone object from the calendar backend.
6620  * The call is finished by e_cal_client_get_timezone_finish() from
6621  * the @callback.
6622  *
6623  * Since: 3.2
6624  **/
6625 void
e_cal_client_get_timezone(ECalClient * client,const gchar * tzid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)6626 e_cal_client_get_timezone (ECalClient *client,
6627                            const gchar *tzid,
6628                            GCancellable *cancellable,
6629                            GAsyncReadyCallback callback,
6630                            gpointer user_data)
6631 {
6632 	GSimpleAsyncResult *simple;
6633 	AsyncContext *async_context;
6634 
6635 	g_return_if_fail (E_IS_CAL_CLIENT (client));
6636 	g_return_if_fail (tzid != NULL);
6637 
6638 	async_context = g_slice_new0 (AsyncContext);
6639 	async_context->tzid = g_strdup (tzid);
6640 
6641 	simple = g_simple_async_result_new (
6642 		G_OBJECT (client), callback, user_data,
6643 		e_cal_client_get_timezone);
6644 
6645 	g_simple_async_result_set_check_cancellable (simple, cancellable);
6646 
6647 	g_simple_async_result_set_op_res_gpointer (
6648 		simple, async_context, (GDestroyNotify) async_context_free);
6649 
6650 	g_simple_async_result_run_in_thread (
6651 		simple, cal_client_get_timezone_thread,
6652 		G_PRIORITY_DEFAULT, cancellable);
6653 
6654 	g_object_unref (simple);
6655 }
6656 
6657 /**
6658  * e_cal_client_get_timezone_finish:
6659  * @client: an #ECalClient
6660  * @result: a #GAsyncResult
6661  * @out_zone: (out) (transfer none): Return value for the timezone
6662  * @error: a #GError to set an error, if any
6663  *
6664  * Finishes previous call of e_cal_client_get_timezone() and
6665  * sets @out_zone to a retrieved timezone object from the calendar backend.
6666  * This object is owned by the @client, thus do not free it.
6667  *
6668  * Returns: %TRUE if successful, %FALSE otherwise.
6669  *
6670  * Since: 3.2
6671  **/
6672 gboolean
e_cal_client_get_timezone_finish(ECalClient * client,GAsyncResult * result,ICalTimezone ** out_zone,GError ** error)6673 e_cal_client_get_timezone_finish (ECalClient *client,
6674                                   GAsyncResult *result,
6675                                   ICalTimezone **out_zone,
6676                                   GError **error)
6677 {
6678 	GSimpleAsyncResult *simple;
6679 	AsyncContext *async_context;
6680 
6681 	g_return_val_if_fail (
6682 		g_simple_async_result_is_valid (
6683 		result, G_OBJECT (client),
6684 		e_cal_client_get_timezone), FALSE);
6685 
6686 	simple = G_SIMPLE_ASYNC_RESULT (result);
6687 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
6688 
6689 	if (g_simple_async_result_propagate_error (simple, error))
6690 		return FALSE;
6691 
6692 	g_return_val_if_fail (async_context->zone != NULL, FALSE);
6693 
6694 	if (out_zone != NULL) {
6695 		*out_zone = async_context->zone;
6696 		async_context->zone = NULL;
6697 	}
6698 
6699 	return TRUE;
6700 }
6701 
6702 /**
6703  * e_cal_client_get_timezone_sync:
6704  * @client: an #ECalClient
6705  * @tzid: ID of the timezone to retrieve
6706  * @out_zone: (out) (transfer none): Return value for the timezone
6707  * @cancellable: a #GCancellable; can be %NULL
6708  * @error: a #GError to set an error, if any
6709  *
6710  * Retrieves a timezone object from the calendar backend.
6711  * This object is owned by the @client, thus do not free it.
6712  *
6713  * Returns: %TRUE if successful, %FALSE otherwise.
6714  *
6715  * Since: 3.2
6716  **/
6717 gboolean
e_cal_client_get_timezone_sync(ECalClient * client,const gchar * tzid,ICalTimezone ** out_zone,GCancellable * cancellable,GError ** error)6718 e_cal_client_get_timezone_sync (ECalClient *client,
6719                                 const gchar *tzid,
6720                                 ICalTimezone **out_zone,
6721                                 GCancellable *cancellable,
6722                                 GError **error)
6723 {
6724 	ICalComponent *icalcomp;
6725 	ICalTimezone *zone;
6726 	gchar *utf8_tzid;
6727 	gchar *string = NULL;
6728 	GError *local_error = NULL;
6729 
6730 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
6731 	g_return_val_if_fail (tzid != NULL, FALSE);
6732 	g_return_val_if_fail (out_zone != NULL, FALSE);
6733 
6734 	zone = e_timezone_cache_get_timezone (
6735 		E_TIMEZONE_CACHE (client), tzid);
6736 	if (zone != NULL) {
6737 		*out_zone = zone;
6738 		return TRUE;
6739 	}
6740 
6741 	utf8_tzid = e_util_utf8_make_valid (tzid);
6742 
6743 	e_dbus_calendar_call_get_timezone_sync (
6744 		client->priv->dbus_proxy, utf8_tzid,
6745 		&string, cancellable, &local_error);
6746 
6747 	g_free (utf8_tzid);
6748 
6749 	/* Sanity check. */
6750 	g_return_val_if_fail (
6751 		((string != NULL) && (local_error == NULL)) ||
6752 		((string == NULL) && (local_error != NULL)), FALSE);
6753 
6754 	if (local_error != NULL) {
6755 		g_dbus_error_strip_remote_error (local_error);
6756 		g_propagate_error (error, local_error);
6757 		return FALSE;
6758 	}
6759 
6760 	icalcomp = i_cal_parser_parse_string (string);
6761 
6762 	g_free (string);
6763 
6764 	if (icalcomp == NULL) {
6765 		g_set_error_literal (
6766 			error, E_CAL_CLIENT_ERROR,
6767 			E_CAL_CLIENT_ERROR_INVALID_OBJECT,
6768 			e_cal_client_error_to_string (
6769 			E_CAL_CLIENT_ERROR_INVALID_OBJECT));
6770 		return FALSE;
6771 	}
6772 
6773 	zone = i_cal_timezone_new ();
6774 	if (!i_cal_timezone_set_component (zone, icalcomp)) {
6775 		g_set_error_literal (
6776 			error, E_CAL_CLIENT_ERROR,
6777 			E_CAL_CLIENT_ERROR_INVALID_OBJECT,
6778 			e_cal_client_error_to_string (
6779 			E_CAL_CLIENT_ERROR_INVALID_OBJECT));
6780 		g_object_unref (icalcomp);
6781 		g_object_unref (zone);
6782 		return FALSE;
6783 	}
6784 
6785 	/* Add the timezone to the cache directly,
6786 	 * otherwise we'd have to free this struct
6787 	 * and fetch the cached copy. */
6788 	g_mutex_lock (&client->priv->zone_cache_lock);
6789 	if (g_hash_table_lookup (client->priv->zone_cache, tzid)) {
6790 		/* It can be that another thread already filled the zone into the cache,
6791 		   thus deal with it properly, because that other zone can be used by that
6792 		   other thread. */
6793 		g_object_unref (zone);
6794 		zone = g_hash_table_lookup (client->priv->zone_cache, tzid);
6795 	} else {
6796 		g_hash_table_insert (
6797 			client->priv->zone_cache, g_strdup (tzid), zone);
6798 	}
6799 	g_mutex_unlock (&client->priv->zone_cache_lock);
6800 
6801 	g_object_unref (icalcomp);
6802 
6803 	*out_zone = zone;
6804 
6805 	return TRUE;
6806 }
6807 
6808 /* Helper for e_cal_client_add_timezone() */
6809 static void
cal_client_add_timezone_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)6810 cal_client_add_timezone_thread (GSimpleAsyncResult *simple,
6811                                 GObject *source_object,
6812                                 GCancellable *cancellable)
6813 {
6814 	AsyncContext *async_context;
6815 	GError *local_error = NULL;
6816 
6817 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
6818 
6819 	if (!e_cal_client_add_timezone_sync (
6820 		E_CAL_CLIENT (source_object),
6821 		async_context->zone,
6822 		cancellable, &local_error)) {
6823 
6824 		if (!local_error)
6825 			local_error = g_error_new_literal (
6826 				E_CLIENT_ERROR,
6827 				E_CLIENT_ERROR_OTHER_ERROR,
6828 				_("Unknown error"));
6829 	}
6830 
6831 	if (local_error != NULL)
6832 		g_simple_async_result_take_error (simple, local_error);
6833 }
6834 
6835 /**
6836  * e_cal_client_add_timezone:
6837  * @client: an #ECalClient
6838  * @zone: The timezone to add
6839  * @cancellable: a #GCancellable; can be %NULL
6840  * @callback: callback to call when a result is ready
6841  * @user_data: user data for the @callback
6842  *
6843  * Add a VTIMEZONE object to the given calendar client.
6844  * The call is finished by e_cal_client_add_timezone_finish() from
6845  * the @callback.
6846  *
6847  * Since: 3.2
6848  **/
6849 void
e_cal_client_add_timezone(ECalClient * client,ICalTimezone * zone,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)6850 e_cal_client_add_timezone (ECalClient *client,
6851                            ICalTimezone *zone,
6852                            GCancellable *cancellable,
6853                            GAsyncReadyCallback callback,
6854                            gpointer user_data)
6855 {
6856 	GSimpleAsyncResult *simple;
6857 	AsyncContext *async_context;
6858 
6859 	g_return_if_fail (E_IS_CAL_CLIENT (client));
6860 	g_return_if_fail (zone != NULL);
6861 
6862 	async_context = g_slice_new0 (AsyncContext);
6863 	async_context->zone = e_cal_util_copy_timezone (zone);
6864 
6865 	simple = g_simple_async_result_new (
6866 		G_OBJECT (client), callback, user_data,
6867 		e_cal_client_add_timezone);
6868 
6869 	g_simple_async_result_set_check_cancellable (simple, cancellable);
6870 
6871 	g_simple_async_result_set_op_res_gpointer (
6872 		simple, async_context, (GDestroyNotify) async_context_free);
6873 
6874 	if (!async_context->zone || zone == i_cal_timezone_get_utc_timezone ())
6875 		g_simple_async_result_complete_in_idle (simple);
6876 	else
6877 		g_simple_async_result_run_in_thread (
6878 			simple, cal_client_add_timezone_thread,
6879 			G_PRIORITY_DEFAULT, cancellable);
6880 
6881 	g_object_unref (simple);
6882 }
6883 
6884 /**
6885  * e_cal_client_add_timezone_finish:
6886  * @client: an #ECalClient
6887  * @result: a #GAsyncResult
6888  * @error: a #GError to set an error, if any
6889  *
6890  * Finishes previous call of e_cal_client_add_timezone().
6891  *
6892  * Returns: %TRUE if successful, %FALSE otherwise.
6893  *
6894  * Since: 3.2
6895  **/
6896 gboolean
e_cal_client_add_timezone_finish(ECalClient * client,GAsyncResult * result,GError ** error)6897 e_cal_client_add_timezone_finish (ECalClient *client,
6898                                   GAsyncResult *result,
6899                                   GError **error)
6900 {
6901 	GSimpleAsyncResult *simple;
6902 
6903 	g_return_val_if_fail (
6904 		g_simple_async_result_is_valid (
6905 		result, G_OBJECT (client),
6906 		e_cal_client_add_timezone), FALSE);
6907 
6908 	simple = G_SIMPLE_ASYNC_RESULT (result);
6909 
6910 	/* Assume success unless a GError is set. */
6911 	return !g_simple_async_result_propagate_error (simple, error);
6912 }
6913 
6914 /**
6915  * e_cal_client_add_timezone_sync:
6916  * @client: an #ECalClient
6917  * @zone: The timezone to add
6918  * @cancellable: a #GCancellable; can be %NULL
6919  * @error: a #GError to set an error, if any
6920  *
6921  * Add a VTIMEZONE object to the given calendar client.
6922  *
6923  * Returns: %TRUE if successful, %FALSE otherwise.
6924  *
6925  * Since: 3.2
6926  **/
6927 gboolean
e_cal_client_add_timezone_sync(ECalClient * client,ICalTimezone * zone,GCancellable * cancellable,GError ** error)6928 e_cal_client_add_timezone_sync (ECalClient *client,
6929                                 ICalTimezone *zone,
6930                                 GCancellable *cancellable,
6931                                 GError **error)
6932 {
6933 	ICalComponent *icalcomp;
6934 	gchar *zone_str;
6935 	gchar *utf8_zone_str;
6936 	GError *local_error = NULL;
6937 
6938 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
6939 	g_return_val_if_fail (zone != NULL, FALSE);
6940 
6941 	if (zone == i_cal_timezone_get_utc_timezone ())
6942 		return TRUE;
6943 
6944 	icalcomp = i_cal_timezone_get_component (zone);
6945 	if (icalcomp == NULL) {
6946 		g_propagate_error (
6947 			error, e_client_error_create (
6948 			E_CLIENT_ERROR_INVALID_ARG, NULL));
6949 		return FALSE;
6950 	}
6951 
6952 	zone_str = i_cal_component_as_ical_string (icalcomp);
6953 	utf8_zone_str = e_util_utf8_make_valid (zone_str);
6954 
6955 	e_dbus_calendar_call_add_timezone_sync (
6956 		client->priv->dbus_proxy, utf8_zone_str,
6957 		cancellable, &local_error);
6958 
6959 	g_free (zone_str);
6960 	g_free (utf8_zone_str);
6961 	g_object_unref (icalcomp);
6962 
6963 	if (local_error != NULL) {
6964 		g_dbus_error_strip_remote_error (local_error);
6965 		g_propagate_error (error, local_error);
6966 		return FALSE;
6967 	}
6968 
6969 	return TRUE;
6970 }
6971 
6972