Adapt libedata-cal to the new ESource API.
[platform/upstream/evolution-data-server.git] / calendar / libedata-cal / e-cal-backend-sync.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Author:
4  *   Chris Toshok (toshok@ximian.com)
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  */
8
9 #ifdef CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include "e-cal-backend-sync.h"
14 #include "libedataserver/e-data-server-util.h"
15 #include <libical/icaltz-util.h>
16
17 #define E_CAL_BACKEND_SYNC_GET_PRIVATE(obj) \
18         (G_TYPE_INSTANCE_GET_PRIVATE \
19         ((obj), E_TYPE_CAL_BACKEND_SYNC, ECalBackendSyncPrivate))
20
21 G_DEFINE_TYPE (ECalBackendSync, e_cal_backend_sync, E_TYPE_CAL_BACKEND)
22
23 struct _ECalBackendSyncPrivate {
24         GMutex *sync_mutex;
25
26         gboolean mutex_lock;
27 };
28
29 #define LOCK_WRAPPER(func, args) G_STMT_START {                                                                 \
30         gboolean locked = backend->priv->mutex_lock;                                                            \
31         e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->func, NotSupported);           \
32         if (locked)                                                                                             \
33                 g_mutex_lock (backend->priv->sync_mutex);                                                       \
34         (* E_CAL_BACKEND_SYNC_GET_CLASS (backend)->func) args;                                                  \
35         if (locked)                                                                                             \
36                 g_mutex_unlock (backend->priv->sync_mutex);                                                     \
37         } G_STMT_END
38
39 #define LOCK_WRAPPER_RET_VAL(func, args) G_STMT_START {                                                         \
40         gboolean locked = backend->priv->mutex_lock;                                                            \
41         e_return_data_cal_error_val_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->func, NotSupported);       \
42         if (locked)                                                                                             \
43                 g_mutex_lock (backend->priv->sync_mutex);                                                       \
44         res = (* E_CAL_BACKEND_SYNC_GET_CLASS (backend)->func) args;                                            \
45         if (locked)                                                                                             \
46                 g_mutex_unlock (backend->priv->sync_mutex);                                                     \
47         } G_STMT_END
48
49 /**
50  * e_cal_backend_sync_set_lock:
51  * @backend: An ECalBackendSync object.
52  * @lock: Lock mode.
53  *
54  * Sets the lock mode on the ECalBackendSync object. If TRUE, the backend
55  * will create a locking mutex for every operation, so that only one can
56  * happen at a time. If FALSE, no lock would be done and many operations
57  * can happen at the same time.
58  */
59 void
60 e_cal_backend_sync_set_lock (ECalBackendSync *backend,
61                              gboolean lock)
62 {
63         g_return_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend));
64
65         backend->priv->mutex_lock = lock;
66 }
67
68 /**
69  * e_cal_backend_sync_open:
70  * @backend: An ECalBackendSync object.
71  * @cal: An EDataCal object.
72  * @cancellable: a #GCancellable for the operation
73  * @only_if_exists: Whether to open the calendar if and only if it already exists
74  * or just create it when it does not exist.
75  * @error: Out parameter for a #GError.
76  *
77  * Calls the open_sync method on the given backend.
78  */
79 void
80 e_cal_backend_sync_open (ECalBackendSync *backend,
81                          EDataCal *cal,
82                          GCancellable *cancellable,
83                          gboolean only_if_exists,
84                          GError **error)
85 {
86         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
87
88         LOCK_WRAPPER (open_sync, (backend, cal, cancellable, only_if_exists, error));
89 }
90
91 /**
92  * e_cal_backend_sync_remove:
93  * @backend: An ECalBackendSync object.
94  * @cal: An EDataCal object.
95  * @cancellable: a #GCancellable for the operation
96  * @error: Out parameter for a #GError.
97  *
98  * Calls the remove_sync method on the given backend.
99  */
100 void
101 e_cal_backend_sync_remove (ECalBackendSync *backend,
102                            EDataCal *cal,
103                            GCancellable *cancellable,
104                            GError **error)
105 {
106         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
107
108         LOCK_WRAPPER (remove_sync, (backend, cal, cancellable, error));
109 }
110
111 /**
112  * e_cal_backend_sync_refresh:
113  * @backend: An ECalBackendSync object.
114  * @cal: An EDataCal object.
115  * @cancellable: a #GCancellable for the operation
116  * @error: Out parameter for a #GError.
117  *
118  * Calls the refresh_sync method on the given backend.
119  *
120  * Since: 2.30
121  */
122 void
123 e_cal_backend_sync_refresh (ECalBackendSync *backend,
124                             EDataCal *cal,
125                             GCancellable *cancellable,
126                             GError **error)
127 {
128         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
129         e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->refresh_sync != NULL, UnsupportedMethod);
130
131         LOCK_WRAPPER (refresh_sync, (backend, cal, cancellable, error));
132 }
133
134 /**
135  * e_cal_backend_sync_get_backend_property:
136  * @backend: An ECalBackendSync object.
137  * @cal: An EDataCal object.
138  * @cancellable: a #GCancellable for the operation
139  * @prop_name: Property name whose value to retrieve.
140  * @prop_value: Return value of the @prop_name.
141  * @error: Out parameter for a #GError.
142  *
143  * Calls the get_backend_property_sync method on the given backend.
144  *
145  * Returns whether processed this property. Returning FALSE means to pass
146  * the call to the ECalBackend parent class, thus neither @error should be
147  * set in this case.
148  *
149  * Since: 3.2
150  **/
151 gboolean
152 e_cal_backend_sync_get_backend_property (ECalBackendSync *backend,
153                                          EDataCal *cal,
154                                          GCancellable *cancellable,
155                                          const gchar *prop_name,
156                                          gchar **prop_value,
157                                          GError **error)
158 {
159         gboolean res = FALSE;
160
161         e_return_data_cal_error_val_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
162         e_return_data_cal_error_val_if_fail (prop_name, InvalidArg);
163         e_return_data_cal_error_val_if_fail (prop_value, InvalidArg);
164
165         LOCK_WRAPPER_RET_VAL (get_backend_property_sync, (backend, cal, cancellable, prop_name, prop_value, error));
166
167         return res;
168 }
169
170 /**
171  * e_cal_backend_sync_set_backend_property:
172  * @backend: An ECalBackendSync object.
173  * @cal: An EDataCal object.
174  * @cancellable: a #GCancellable for the operation
175  * @prop_name: Property name to set.
176  * @prop_value: New value of the @prop_name.
177  * @error: Out parameter for a #GError.
178  *
179  * Calls the set_backend_property_sync method on the given backend.
180  *
181  * Returns whether processed this property. Returning FALSE means to pass
182  * the call to the ECalBackend parent class, thus neither @error should be
183  * set in this case.
184  *
185  * Since: 3.2
186  **/
187 gboolean
188 e_cal_backend_sync_set_backend_property (ECalBackendSync *backend,
189                                          EDataCal *cal,
190                                          GCancellable *cancellable,
191                                          const gchar *prop_name,
192                                          const gchar *prop_value,
193                                          GError **error)
194 {
195         gboolean res = FALSE;
196
197         e_return_data_cal_error_val_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
198         e_return_data_cal_error_val_if_fail (prop_name, InvalidArg);
199         e_return_data_cal_error_val_if_fail (prop_value, InvalidArg);
200
201         LOCK_WRAPPER_RET_VAL (set_backend_property_sync, (backend, cal, cancellable, prop_name, prop_value, error));
202
203         return res;
204 }
205
206 /**
207  * e_cal_backend_sync_get_object:
208  * @backend: An ECalBackendSync object.
209  * @cal: An EDataCal object.
210  * @cancellable: a #GCancellable for the operation
211  * @uid: UID of the object to get.
212  * @rid: Recurrence ID of the specific instance to get, or NULL if getting the
213  * master object.
214  * @calobj: Placeholder for returned object.
215  * @error: Out parameter for a #GError.
216  *
217  * Calls the get_object_sync method on the given backend.
218  */
219 void
220 e_cal_backend_sync_get_object (ECalBackendSync *backend,
221                                EDataCal *cal,
222                                GCancellable *cancellable,
223                                const gchar *uid,
224                                const gchar *rid,
225                                gchar **calobj,
226                                GError **error)
227 {
228         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
229         e_return_data_cal_error_if_fail (calobj, InvalidArg);
230
231         LOCK_WRAPPER (get_object_sync, (backend, cal, cancellable, uid, rid, calobj, error));
232 }
233
234 /**
235  * e_cal_backend_sync_get_object_list:
236  * @backend: An ECalBackendSync object.
237  * @cal: An EDataCal object.
238  * @cancellable: a #GCancellable for the operation
239  * @sexp: Search query.
240  * @calobjs: Placeholder for list of returned objects.
241  * @error: Out parameter for a #GError.
242  *
243  * Calls the get_object_list_sync method on the given backend.
244  */
245 void
246 e_cal_backend_sync_get_object_list (ECalBackendSync *backend,
247                                     EDataCal *cal,
248                                     GCancellable *cancellable,
249                                     const gchar *sexp,
250                                     GSList **calobjs,
251                                     GError **error)
252 {
253         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
254         e_return_data_cal_error_if_fail (calobjs, InvalidArg);
255
256         LOCK_WRAPPER (get_object_list_sync, (backend, cal, cancellable, sexp, calobjs, error));
257 }
258
259 /**
260  * e_cal_backend_sync_get_free_busy:
261  * @backend: An ECalBackendSync object.
262  * @cal: An EDataCal object.
263  * @cancellable: a #GCancellable for the operation
264  * @users: List of users to get F/B info from.
265  * @start: Time range start.
266  * @end: Time range end.
267  * @freebusyobjects: Placeholder for F/B information.
268  * @error: Out parameter for a #GError.
269  *
270  * Calls the get_free_busy_sync method on the given backend.
271  */
272 void
273 e_cal_backend_sync_get_free_busy (ECalBackendSync *backend,
274                                   EDataCal *cal,
275                                   GCancellable *cancellable,
276                                   const GSList *users,
277                                   time_t start,
278                                   time_t end,
279                                   GSList **freebusyobjects,
280                                   GError **error)
281 {
282         e_return_data_cal_error_if_fail (E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
283
284         LOCK_WRAPPER (get_free_busy_sync, (backend, cal, cancellable, users, start, end, freebusyobjects, error));
285 }
286
287 /**
288  * e_cal_backend_sync_create_objects:
289  * @backend: An ECalBackendSync object.
290  * @cal: An EDataCal object.
291  * @cancellable: a #GCancellable for the operation
292  * @calobjs: The objects to be added.
293  * @uids: Placeholder for server-generated UIDs.
294  * @new_components: (out) (transfer full): Placeholder for returned #ECalComponent objects.
295  * @error: Out parameter for a #GError.
296  *
297  * Calls the create_objects_sync method on the given backend.
298  */
299 void
300 e_cal_backend_sync_create_objects (ECalBackendSync *backend,
301                                    EDataCal *cal,
302                                    GCancellable *cancellable,
303                                    const GSList *calobjs,
304                                    GSList **uids,
305                                    GSList **new_components,
306                                    GError **error)
307 {
308         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
309         e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->create_objects_sync != NULL, UnsupportedMethod);
310
311         LOCK_WRAPPER (create_objects_sync, (backend, cal, cancellable, calobjs, uids, new_components, error));
312 }
313
314 /**
315  * e_cal_backend_sync_modify_objects:
316  * @backend: An ECalBackendSync object.
317  * @cal: An EDataCal object.
318  * @cancellable: a #GCancellable for the operation
319  * @calobjs: Objects to be modified.
320  * @mod: Type of modification to be done.
321  * @old_components: (out) (transfer full): Placeholder for returning the old components as they were stored on the
322  * backend.
323  * @new_components: (out) (transfer full): Placeholder for returning the new components as they have been stored
324  * on the backend.
325  * @error: Out parameter for a #GError.
326  *
327  * Calls the modify_objects_sync method on the given backend.
328  */
329 void
330 e_cal_backend_sync_modify_objects (ECalBackendSync *backend,
331                                                                    EDataCal *cal,
332                                                                    GCancellable *cancellable,
333                                                                    const GSList *calobjs,
334                                                                    CalObjModType mod,
335                                                                    GSList **old_components,
336                                                                    GSList **new_components,
337                                                                    GError **error)
338 {
339         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
340         e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->modify_objects_sync != NULL, UnsupportedMethod);
341
342         LOCK_WRAPPER (modify_objects_sync, (backend, cal, cancellable, calobjs, mod, old_components, new_components, error));
343 }
344
345 /**
346  * e_cal_backend_sync_remove_objects:
347  * @backend: An ECalBackendSync object.
348  * @cal: An EDataCal object.
349  * @cancellable: a #GCancellable for the operation
350  * @ids: List of #ECalComponentId objects identifying the objects to remove.
351  * @mod: Type of removal.
352  * @old_components: (out) (transfer full): Placeholder for returning the old components as they were stored on the
353  * backend.
354  * @new_components: (out) (transfer full): Placeholder for returning the new components as they have been stored
355  * on the backend (when removing individual instances). If removing whole objects,
356  * they will be set to %NULL.
357  * @error: Out parameter for a #GError.
358  *
359  * Calls the remove_objects_sync method on the given backend.
360  */
361 void
362 e_cal_backend_sync_remove_objects (ECalBackendSync *backend,
363                                    EDataCal *cal,
364                                    GCancellable *cancellable,
365                                    const GSList *ids,
366                                    CalObjModType mod,
367                                    GSList **old_components,
368                                    GSList **new_components,
369                                    GError **error)
370 {
371         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
372         e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->remove_objects_sync != NULL, UnsupportedMethod);
373
374         LOCK_WRAPPER (remove_objects_sync, (backend, cal, cancellable, ids, mod, old_components, new_components, error));
375 }
376
377 /**
378  * e_cal_backend_sync_receive_objects:
379  * @backend: An ECalBackendSync object.
380  * @cal: An EDataCal object.
381  * @cancellable: a #GCancellable for the operation
382  * @calobj: iCalendar object to receive.
383  * @error: Out parameter for a #GError.
384  *
385  * Calls the receive_objects_sync method on the given backend.
386  */
387 void
388 e_cal_backend_sync_receive_objects (ECalBackendSync *backend,
389                                     EDataCal *cal,
390                                     GCancellable *cancellable,
391                                     const gchar *calobj,
392                                     GError **error)
393 {
394         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
395         e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->receive_objects_sync != NULL, UnsupportedMethod);
396
397         LOCK_WRAPPER (receive_objects_sync, (backend, cal, cancellable, calobj, error));
398 }
399
400 /**
401  * e_cal_backend_sync_send_objects:
402  * @backend: An ECalBackendSync object.
403  * @cal: An EDataCal object.
404  * @cancellable: a #GCancellable for the operation
405  * @calobj: The iCalendar object to send.
406  * @users: List of users to send notifications to.
407  * @modified_calobj: Placeholder for the iCalendar object after being modified.
408  * @error: Out parameter for a #GError.
409  *
410  * Calls the send_objects_sync method on the given backend.
411  */
412 void
413 e_cal_backend_sync_send_objects (ECalBackendSync *backend,
414                                  EDataCal *cal,
415                                  GCancellable *cancellable,
416                                  const gchar *calobj,
417                                  GSList **users,
418                                  gchar **modified_calobj,
419                                  GError **error)
420 {
421         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
422         e_return_data_cal_error_if_fail (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->send_objects_sync != NULL, UnsupportedMethod);
423
424         LOCK_WRAPPER (send_objects_sync, (backend, cal, cancellable, calobj, users, modified_calobj, error));
425 }
426
427 /**
428  * e_cal_backend_sync_get_attachment_uris:
429  * @backend: An ECalBackendSync object.
430  * @cal: An EDataCal object.
431  * @cancellable: a #GCancellable for the operation
432  * @uid: Unique id of the calendar object.
433  * @rid: Recurrence id of the calendar object.
434  * @attachments: Placeholder for list of returned attachment uris.
435  * @error: Out parameter for a #GError.
436  *
437  * Calls the get_attachment_uris_sync method on the given backend.
438  *
439  * Since: 3.2
440  */
441 void
442 e_cal_backend_sync_get_attachment_uris (ECalBackendSync *backend,
443                                         EDataCal *cal,
444                                         GCancellable *cancellable,
445                                         const gchar *uid,
446                                         const gchar *rid,
447                                         GSList **attachments,
448                                         GError **error)
449 {
450         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
451         e_return_data_cal_error_if_fail (attachments, InvalidArg);
452
453         LOCK_WRAPPER (get_attachment_uris_sync, (backend, cal, cancellable, uid, rid, attachments, error));
454 }
455
456 /**
457  * e_cal_backend_sync_discard_alarm:
458  * @backend: An ECalBackendSync object.
459  * @cal: An EDataCal object.
460  * @cancellable: a #GCancellable for the operation
461  * @uid: Unique id of the calendar object.
462  * @rid: Recurrence id of the calendar object.
463  * @auid: Alarm ID to remove.
464  * @error: Out parameter for a #GError.
465  *
466  * Calls the discard_alarm_sync method on the given backend.
467  **/
468 void
469 e_cal_backend_sync_discard_alarm (ECalBackendSync *backend,
470                                   EDataCal *cal,
471                                   GCancellable *cancellable,
472                                   const gchar *uid,
473                                   const gchar *rid,
474                                   const gchar *auid,
475                                   GError **error)
476 {
477         e_return_data_cal_error_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
478         e_return_data_cal_error_if_fail (uid, InvalidArg);
479         e_return_data_cal_error_if_fail (auid, InvalidArg);
480
481         LOCK_WRAPPER (discard_alarm_sync, (backend, cal, cancellable, uid, rid, auid, error));
482 }
483
484 /**
485  * e_cal_backend_sync_get_timezone:
486  * @backend: An ECalBackendSync object.
487  * @cal: An EDataCal object.
488  * @cancellable: a #GCancellable for the operation
489  * @tzid: ID of the timezone to retrieve.
490  * @tzobject: Placeholder for the returned timezone.
491  * @error: Out parameter for a #GError.
492  *
493  * Calls the get_timezone_sync method on the given backend.
494  * This method is not mandatory on the backend, because here
495  * is used internal_get_timezone call to fetch timezone from
496  * it and that is transformed to a string. In other words,
497  * any object deriving from ECalBackendSync can implement only
498  * internal_get_timezone and can skip implementation of
499  * get_timezone_sync completely.
500  */
501 void
502 e_cal_backend_sync_get_timezone (ECalBackendSync *backend,
503                                  EDataCal *cal,
504                                  GCancellable *cancellable,
505                                  const gchar *tzid,
506                                  gchar **tzobject,
507                                  GError **error)
508 {
509         e_return_data_cal_error_if_fail (E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
510
511         if (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->get_timezone_sync) {
512                 LOCK_WRAPPER (get_timezone_sync, (backend, cal, cancellable, tzid, tzobject, error));
513         }
514
515         if (tzobject && !*tzobject) {
516                 icaltimezone *zone = NULL;
517
518                 if (backend->priv->mutex_lock)
519                         g_mutex_lock (backend->priv->sync_mutex);
520                 zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (backend), tzid);
521                 if (backend->priv->mutex_lock)
522                         g_mutex_unlock (backend->priv->sync_mutex);
523
524                 if (!zone) {
525                         g_propagate_error (error, e_data_cal_create_error (ObjectNotFound, NULL));
526                 } else {
527                         icalcomponent *icalcomp;
528
529                         icalcomp = icaltimezone_get_component (zone);
530
531                         if (!icalcomp) {
532                                 g_propagate_error (error, e_data_cal_create_error (InvalidObject, NULL));
533                         } else {
534                                 *tzobject = icalcomponent_as_ical_string_r (icalcomp);
535                         }
536                 }
537         }
538 }
539
540 /**
541  * e_cal_backend_sync_add_timezone:
542  * @backend: An ECalBackendSync object.
543  * @cal: An EDataCal object.
544  * @cancellable: a #GCancellable for the operation
545  * @tzobject: VTIMEZONE object to be added.
546  * @error: Out parameter for a #GError.
547  *
548  * Calls the add_timezone_sync method on the given backend.
549  */
550 void
551 e_cal_backend_sync_add_timezone (ECalBackendSync *backend,
552                                  EDataCal *cal,
553                                  GCancellable *cancellable,
554                                  const gchar *tzobject,
555                                  GError **error)
556 {
557         e_return_data_cal_error_if_fail (E_IS_CAL_BACKEND_SYNC (backend), InvalidArg);
558
559         LOCK_WRAPPER (add_timezone_sync, (backend, cal, cancellable, tzobject, error));
560 }
561
562 static void
563 cal_backend_open (ECalBackend *backend,
564                   EDataCal *cal,
565                   guint32 opid,
566                   GCancellable *cancellable,
567                   gboolean only_if_exists)
568 {
569         GError *error = NULL;
570
571         e_cal_backend_sync_open (E_CAL_BACKEND_SYNC (backend), cal, cancellable, only_if_exists, &error);
572
573         e_data_cal_respond_open (cal, opid, error);
574 }
575
576 static void
577 cal_backend_remove (ECalBackend *backend,
578                     EDataCal *cal,
579                     guint32 opid,
580                     GCancellable *cancellable)
581 {
582         GError *error = NULL;
583
584         e_cal_backend_sync_remove (E_CAL_BACKEND_SYNC (backend), cal, cancellable, &error);
585
586         e_data_cal_respond_remove (cal, opid, error);
587 }
588
589 static void
590 cal_backend_refresh (ECalBackend *backend,
591                      EDataCal *cal,
592                      guint32 opid,
593                      GCancellable *cancellable)
594 {
595         GError *error = NULL;
596
597         e_cal_backend_sync_refresh (E_CAL_BACKEND_SYNC (backend), cal, cancellable, &error);
598
599         e_data_cal_respond_refresh (cal, opid, error);
600 }
601
602 static void
603 cal_backend_get_backend_property (ECalBackend *backend,
604                                   EDataCal *cal,
605                                   guint32 opid,
606                                   GCancellable *cancellable,
607                                   const gchar *prop_name)
608 {
609         GError *error = NULL;
610         gchar *prop_value = NULL;
611
612         if (e_cal_backend_sync_get_backend_property (E_CAL_BACKEND_SYNC (backend), cal, cancellable, prop_name, &prop_value, &error))
613                 e_data_cal_respond_get_backend_property (cal, opid, error, prop_value);
614         else
615                 (* E_CAL_BACKEND_CLASS (e_cal_backend_sync_parent_class)->get_backend_property) (backend, cal, opid, cancellable, prop_name);
616
617         g_free (prop_value);
618 }
619
620 static void
621 cal_backend_set_backend_property (ECalBackend *backend,
622                                   EDataCal *cal,
623                                   guint32 opid,
624                                   GCancellable *cancellable,
625                                   const gchar *prop_name,
626                                   const gchar *prop_value)
627 {
628         GError *error = NULL;
629
630         if (e_cal_backend_sync_set_backend_property (E_CAL_BACKEND_SYNC (backend), cal, cancellable, prop_name, prop_value, &error))
631                 e_data_cal_respond_set_backend_property (cal, opid, error);
632         else
633                 (* E_CAL_BACKEND_CLASS (e_cal_backend_sync_parent_class)->set_backend_property) (backend, cal, opid, cancellable, prop_name, prop_value);
634 }
635
636 static void
637 cal_backend_get_object (ECalBackend *backend,
638                         EDataCal *cal,
639                         guint32 opid,
640                         GCancellable *cancellable,
641                         const gchar *uid,
642                         const gchar *rid)
643 {
644         GError *error = NULL;
645         gchar *calobj = NULL;
646
647         e_cal_backend_sync_get_object (E_CAL_BACKEND_SYNC (backend), cal, cancellable, uid, rid, &calobj, &error);
648
649         e_data_cal_respond_get_object (cal, opid, error, calobj);
650
651         g_free (calobj);
652 }
653
654 static void
655 cal_backend_get_object_list (ECalBackend *backend,
656                              EDataCal *cal,
657                              guint32 opid,
658                              GCancellable *cancellable,
659                              const gchar *sexp)
660 {
661         GError *error = NULL;
662         GSList *calobjs = NULL;
663
664         e_cal_backend_sync_get_object_list (E_CAL_BACKEND_SYNC (backend), cal, cancellable, sexp, &calobjs, &error);
665
666         e_data_cal_respond_get_object_list (cal, opid, error, calobjs);
667
668         g_slist_foreach (calobjs, (GFunc) g_free, NULL);
669         g_slist_free (calobjs);
670 }
671
672 static void
673 cal_backend_get_free_busy (ECalBackend *backend,
674                            EDataCal *cal,
675                            guint32 opid,
676                            GCancellable *cancellable,
677                            const GSList *users,
678                            time_t start,
679                            time_t end)
680 {
681         GError *error = NULL;
682         GSList *freebusyobjs = NULL;
683
684         e_cal_backend_sync_get_free_busy (E_CAL_BACKEND_SYNC (backend), cal, cancellable, users, start, end, &freebusyobjs, &error);
685
686         if (freebusyobjs)
687                 e_data_cal_report_free_busy_data (cal, freebusyobjs);
688         e_data_cal_respond_get_free_busy (cal, opid, error);
689
690         g_slist_foreach (freebusyobjs, (GFunc) g_free, NULL);
691         g_slist_free (freebusyobjs);
692 }
693
694 static GSList *
695 ecalcomponent_slist_from_strings (const GSList *strings)
696 {
697         GSList *ecalcomps = NULL;
698         const GSList *l;
699
700         for (l = strings; l; l = l->next) {
701                 ECalComponent *component = e_cal_component_new_from_string (l->data);
702                 ecalcomps = g_slist_prepend (ecalcomps, component);
703         }
704
705         return g_slist_reverse (ecalcomps);
706 }
707
708 static void
709 cal_backend_create_objects (ECalBackend *backend,
710                             EDataCal *cal,
711                             guint32 opid,
712                             GCancellable *cancellable,
713                             const GSList *calobjs)
714 {
715         GError *error = NULL;
716         GSList *uids = NULL;
717         GSList *new_components = NULL;
718
719         e_cal_backend_sync_create_objects (E_CAL_BACKEND_SYNC (backend), cal, cancellable, calobjs, &uids, &new_components, &error);
720
721         if (!new_components)
722                 new_components = ecalcomponent_slist_from_strings (calobjs);
723
724         e_data_cal_respond_create_objects (cal, opid, error, uids, new_components);
725
726         g_slist_free_full (uids, g_free);
727         e_util_free_nullable_object_slist (new_components);
728 }
729
730 static void
731 cal_backend_modify_objects (ECalBackend *backend,
732                             EDataCal *cal,
733                             guint32 opid,
734                             GCancellable *cancellable,
735                             const GSList *calobjs,
736                             CalObjModType mod)
737 {
738         GError *error = NULL;
739         GSList *old_components = NULL, *new_components = NULL;
740
741         e_cal_backend_sync_modify_objects (E_CAL_BACKEND_SYNC (backend), cal, cancellable, calobjs, mod, &old_components, &new_components, &error);
742
743         if (!old_components)
744                 old_components = ecalcomponent_slist_from_strings (calobjs);
745
746         e_data_cal_respond_modify_objects (cal, opid, error, old_components, new_components);
747
748         e_util_free_nullable_object_slist (old_components);
749         e_util_free_nullable_object_slist (new_components);
750 }
751
752 static void
753 cal_backend_remove_objects (ECalBackend *backend,
754                             EDataCal *cal,
755                             guint32 opid,
756                             GCancellable *cancellable,
757                             const GSList *ids,
758                             CalObjModType mod)
759 {
760         GError *error = NULL;
761         GSList *old_components = NULL, *new_components = NULL;
762
763         e_cal_backend_sync_remove_objects (E_CAL_BACKEND_SYNC (backend), cal, cancellable, ids, mod, &old_components, &new_components, &error);
764
765         e_data_cal_respond_remove_objects (cal, opid, error, ids, old_components, new_components);
766
767         e_util_free_nullable_object_slist (old_components);
768         e_util_free_nullable_object_slist (new_components);
769 }
770
771 static void
772 cal_backend_receive_objects (ECalBackend *backend,
773                              EDataCal *cal,
774                              guint32 opid,
775                              GCancellable *cancellable,
776                              const gchar *calobj)
777 {
778         GError *error = NULL;
779
780         e_cal_backend_sync_receive_objects (E_CAL_BACKEND_SYNC (backend), cal, cancellable, calobj, &error);
781
782         e_data_cal_respond_receive_objects (cal, opid, error);
783 }
784
785 static void
786 cal_backend_send_objects (ECalBackend *backend,
787                           EDataCal *cal,
788                           guint32 opid,
789                           GCancellable *cancellable,
790                           const gchar *calobj)
791 {
792         GError *error = NULL;
793         GSList *users = NULL;
794         gchar *modified_calobj = NULL;
795
796         e_cal_backend_sync_send_objects (E_CAL_BACKEND_SYNC (backend), cal, cancellable, calobj, &users, &modified_calobj, &error);
797
798         e_data_cal_respond_send_objects (cal, opid, error, users, modified_calobj ? modified_calobj : calobj);
799
800         g_slist_foreach (users, (GFunc) g_free, NULL);
801         g_slist_free (users);
802         g_free (modified_calobj);
803 }
804
805 static void
806 cal_backend_get_attachment_uris (ECalBackend *backend,
807                                  EDataCal *cal,
808                                  guint32 opid,
809                                  GCancellable *cancellable,
810                                  const gchar *uid,
811                                  const gchar *rid)
812 {
813         GError *error = NULL;
814         GSList *attachments = NULL;
815
816         e_cal_backend_sync_get_attachment_uris (E_CAL_BACKEND_SYNC (backend), cal, cancellable, uid, rid, &attachments, &error);
817
818         e_data_cal_respond_get_attachment_uris (cal, opid, error, attachments);
819
820         g_slist_foreach (attachments, (GFunc) g_free, NULL);
821         g_slist_free (attachments);
822 }
823
824 static void
825 cal_backend_discard_alarm (ECalBackend *backend,
826                            EDataCal *cal,
827                            guint32 opid,
828                            GCancellable *cancellable,
829                            const gchar *uid,
830                            const gchar *rid,
831                            const gchar *auid)
832 {
833         GError *error = NULL;
834
835         e_cal_backend_sync_discard_alarm (E_CAL_BACKEND_SYNC (backend), cal, cancellable, uid, rid, auid, &error);
836
837         e_data_cal_respond_discard_alarm (cal, opid, error);
838 }
839
840 static void
841 cal_backend_get_timezone (ECalBackend *backend,
842                           EDataCal *cal,
843                           guint32 opid,
844                           GCancellable *cancellable,
845                           const gchar *tzid)
846 {
847         GError *error = NULL;
848         gchar *object = NULL;
849
850         e_cal_backend_sync_get_timezone (E_CAL_BACKEND_SYNC (backend), cal, cancellable, tzid, &object, &error);
851
852         if (!object && tzid) {
853                 /* fallback if tzid contains only the location of timezone */
854                 gint i, slashes = 0;
855
856                 for (i = 0; tzid[i]; i++) {
857                         if (tzid[i] == '/')
858                                 slashes++;
859                 }
860
861                 if (slashes == 1) {
862                         icalcomponent *icalcomp = NULL, *free_comp = NULL;
863
864                         icaltimezone *zone = icaltimezone_get_builtin_timezone (tzid);
865                         if (!zone) {
866                                 /* Try fetching the timezone from zone directory. There are some timezones like MST, US/Pacific etc. which do not appear in
867                                 zone.tab, so they will not be available in the libical builtin timezone */
868                                 icalcomp = free_comp = icaltzutil_fetch_timezone (tzid);
869                         }
870
871                         if (zone)
872                                 icalcomp = icaltimezone_get_component (zone);
873
874                         if (icalcomp) {
875                                 icalcomponent *clone = icalcomponent_new_clone (icalcomp);
876                                 icalproperty *prop;
877
878                                 prop = icalcomponent_get_first_property (clone, ICAL_TZID_PROPERTY);
879                                 if (prop) {
880                                         /* change tzid to our, because the component has the buildin tzid */
881                                         icalproperty_set_tzid (prop, tzid);
882
883                                         object = icalcomponent_as_ical_string_r (clone);
884                                         g_clear_error (&error);
885                                 }
886                                 icalcomponent_free (clone);
887                         }
888
889                         if (free_comp)
890                                 icalcomponent_free (free_comp);
891                 }
892
893                 /* also cache this timezone to backend */
894                 if (object)
895                         e_cal_backend_sync_add_timezone (E_CAL_BACKEND_SYNC (backend), cal, cancellable, object, NULL);
896         }
897
898         e_data_cal_respond_get_timezone  (cal, opid, error, object);
899
900         g_free (object);
901 }
902
903 static void
904 cal_backend_add_timezone (ECalBackend *backend,
905                           EDataCal *cal,
906                           guint32 opid,
907                           GCancellable *cancellable,
908                           const gchar *tzobject)
909 {
910         GError *error = NULL;
911
912         e_cal_backend_sync_add_timezone (E_CAL_BACKEND_SYNC (backend), cal, cancellable, tzobject, &error);
913
914         e_data_cal_respond_add_timezone (cal, opid, error);
915 }
916
917 /* The default implementation is looking for timezone in the ical's builtin timezones,
918  * and if that fails, then it tries to extract the location from the tzid and get the
919  * timezone based on it. If even that fails, then it's returning UTC timezone.
920  * That means, that any object deriving from ECalBackendSync is supposed to implement
921  * this function for checking for a timezone in its own timezone cache, and if that
922  * fails, then call parent's object internal_get_timezone, and that's all.
923  */
924 static icaltimezone *
925 cal_backend_internal_get_timezone (ECalBackend *backend,
926                                    const gchar *tzid)
927 {
928         icaltimezone *zone = NULL;
929
930         if (!tzid || !*tzid)
931                 return NULL;
932
933         zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
934
935         if (!zone) {
936                 const gchar *s, *slash1 = NULL, *slash2 = NULL;
937
938                 /* get builtin by a location, if any */
939                 for (s = tzid; *s; s++) {
940                         if (*s == '/') {
941                                 slash1 = slash2;
942                                 slash2 = s;
943                         }
944                 }
945
946                 if (slash1)
947                         zone = icaltimezone_get_builtin_timezone (slash1 + 1);
948                 else if (slash2)
949                         zone = icaltimezone_get_builtin_timezone (tzid);
950         }
951
952         if (!zone)
953                 zone = icaltimezone_get_utc_timezone ();
954
955         return zone;
956 }
957
958 static gboolean
959 cal_backend_sync_get_backend_property (ECalBackendSync *backend,
960                                        EDataCal *cal,
961                                        GCancellable *cancellable,
962                                        const gchar *prop_name,
963                                        gchar **prop_value,
964                                        GError **error)
965 {
966         /* to indicate to pass to the ECalBackend parent class */
967         return FALSE;
968 }
969
970 static gboolean
971 cal_backend_sync_set_backend_property (ECalBackendSync *backend,
972                                        EDataCal *cal,
973                                        GCancellable *cancellable,
974                                        const gchar *prop_name,
975                                        const gchar *prop_value,
976                                        GError **error)
977 {
978         /* to indicate to pass to the ECalBackend parent class */
979         return FALSE;
980 }
981
982 static void
983 e_cal_backend_sync_finalize (GObject *object)
984 {
985         ECalBackendSyncPrivate *priv;
986
987         priv = E_CAL_BACKEND_SYNC_GET_PRIVATE (object);
988
989         g_mutex_free (priv->sync_mutex);
990
991         /* Chain up to parent's finalize() method. */
992         G_OBJECT_CLASS (e_cal_backend_sync_parent_class)->finalize (object);
993 }
994
995 static void
996 e_cal_backend_sync_class_init (ECalBackendSyncClass *class)
997 {
998         GObjectClass *object_class;
999         ECalBackendClass *backend_class;
1000
1001         g_type_class_add_private (class, sizeof (ECalBackendSyncPrivate));
1002
1003         object_class = G_OBJECT_CLASS (class);
1004         object_class->finalize = e_cal_backend_sync_finalize;
1005
1006         backend_class = E_CAL_BACKEND_CLASS (class);
1007         backend_class->open                     = cal_backend_open;
1008         backend_class->remove                   = cal_backend_remove;
1009         backend_class->refresh                  = cal_backend_refresh;
1010         backend_class->get_backend_property     = cal_backend_get_backend_property;
1011         backend_class->set_backend_property     = cal_backend_set_backend_property;
1012         backend_class->get_object               = cal_backend_get_object;
1013         backend_class->get_object_list          = cal_backend_get_object_list;
1014         backend_class->get_free_busy            = cal_backend_get_free_busy;
1015         backend_class->create_objects           = cal_backend_create_objects;
1016         backend_class->modify_objects           = cal_backend_modify_objects;
1017         backend_class->remove_objects           = cal_backend_remove_objects;
1018         backend_class->receive_objects          = cal_backend_receive_objects;
1019         backend_class->send_objects             = cal_backend_send_objects;
1020         backend_class->get_attachment_uris      = cal_backend_get_attachment_uris;
1021         backend_class->discard_alarm            = cal_backend_discard_alarm;
1022         backend_class->get_timezone             = cal_backend_get_timezone;
1023         backend_class->add_timezone             = cal_backend_add_timezone;
1024         backend_class->internal_get_timezone    = cal_backend_internal_get_timezone;
1025
1026         class->get_backend_property_sync        = cal_backend_sync_get_backend_property;
1027         class->set_backend_property_sync        = cal_backend_sync_set_backend_property;
1028 }
1029
1030 static void
1031 e_cal_backend_sync_init (ECalBackendSync *backend)
1032 {
1033         backend->priv = E_CAL_BACKEND_SYNC_GET_PRIVATE (backend);
1034         backend->priv->sync_mutex = g_mutex_new ();
1035 }
1036