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