libedata-cal: Use new base classes in libebackend.
[platform/upstream/evolution-data-server.git] / calendar / libedata-cal / e-cal-backend.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar - generic backend class
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5  *
6  * Authors: Federico Mena-Quintero <federico@ximian.com>
7  *          JP Rosevear <jpr@ximian.com>
8  *          Rodrigo Moya <rodrigo@ximian.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23
24 #include <config.h>
25
26 #include <glib/gi18n-lib.h>
27
28 #include <libedataserver/e-data-server-util.h>
29
30 #include "e-cal-backend.h"
31 #include "e-cal-backend-cache.h"
32
33 #define EDC_ERROR(_code)        e_data_cal_create_error (_code, NULL)
34 #define EDC_OPENING_ERROR       e_data_cal_create_error (Busy, _("Cannot process, calendar backend is opening"))
35 #define EDC_NOT_OPENED_ERROR    e_data_cal_create_error (NotOpened, NULL)
36
37 /* Private part of the CalBackend structure */
38 struct _ECalBackendPrivate {
39         /* The kind of components for this backend */
40         icalcomponent_kind kind;
41
42         gboolean opening, opened, readonly, removed;
43
44         /* URI, from source. This is cached, since we return const. */
45         gchar *uri;
46
47         gchar *cache_dir;
48
49         /* List of Cal objects */
50         GMutex *clients_mutex;
51         GSList *clients;
52
53         GMutex *views_mutex;
54         GSList *views;
55
56         /* ECalBackend to pass notifications on to */
57         ECalBackend *notification_proxy;
58
59 };
60
61 /* Property IDs */
62 enum {
63         PROP_0,
64         PROP_CACHE_DIR,
65         PROP_KIND
66 };
67
68 static void e_cal_backend_remove_client_private (ECalBackend *backend, EDataCal *cal, gboolean weak_unref);
69
70 G_DEFINE_TYPE (ECalBackend, e_cal_backend, E_TYPE_BACKEND);
71
72 static void
73 cal_backend_set_default_cache_dir (ECalBackend *backend)
74 {
75         ESource *source;
76         icalcomponent_kind kind;
77         const gchar *component_type;
78         const gchar *user_cache_dir;
79         gchar *mangled_uri;
80         gchar *filename;
81
82         user_cache_dir = e_get_user_cache_dir ();
83
84         kind = e_cal_backend_get_kind (backend);
85         source = e_backend_get_source (E_BACKEND (backend));
86
87         switch (kind) {
88                 case ICAL_VEVENT_COMPONENT:
89                         component_type = "calendar";
90                         break;
91                 case ICAL_VTODO_COMPONENT:
92                         component_type = "tasks";
93                         break;
94                 case ICAL_VJOURNAL_COMPONENT:
95                         component_type = "memos";
96                         break;
97                 default:
98                         g_return_if_reached ();
99         }
100
101         /* Mangle the URI to not contain invalid characters. */
102         mangled_uri = g_strdelimit (e_source_get_uri (source), ":/", '_');
103
104         filename = g_build_filename (
105                 user_cache_dir, component_type, mangled_uri, NULL);
106         e_cal_backend_set_cache_dir (backend, filename);
107         g_free (filename);
108
109         g_free (mangled_uri);
110 }
111
112 static void
113 cal_backend_set_kind (ECalBackend *backend,
114                       icalcomponent_kind kind)
115 {
116         backend->priv->kind = kind;
117 }
118
119 static void
120 cal_backend_get_backend_property (ECalBackend *backend,
121                                   EDataCal *cal,
122                                   guint32 opid,
123                                   GCancellable *cancellable,
124                                   const gchar *prop_name)
125 {
126         g_return_if_fail (backend != NULL);
127         g_return_if_fail (E_IS_CAL_BACKEND (backend));
128         g_return_if_fail (cal != NULL);
129         g_return_if_fail (prop_name != NULL);
130
131         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
132                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_opened (backend) ? "TRUE" : "FALSE");
133         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
134                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_opening (backend) ? "TRUE" : "FALSE");
135         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
136                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_backend_get_online (E_BACKEND (backend)) ? "TRUE" : "FALSE");
137         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
138                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_readonly (backend) ? "TRUE" : "FALSE");
139         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
140                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_get_cache_dir (backend));
141         } else {
142                 e_data_cal_respond_get_backend_property (cal, opid, e_data_cal_create_error_fmt (NotSupported, _("Unknown calendar property '%s'"), prop_name), NULL);
143         }
144 }
145
146 static void
147 cal_backend_set_backend_property (ECalBackend *backend,
148                                   EDataCal *cal,
149                                   guint32 opid,
150                                   GCancellable *cancellable,
151                                   const gchar *prop_name,
152                                   const gchar *prop_value)
153 {
154         g_return_if_fail (backend != NULL);
155         g_return_if_fail (E_IS_CAL_BACKEND (backend));
156         g_return_if_fail (cal != NULL);
157         g_return_if_fail (prop_name != NULL);
158
159         e_data_cal_respond_set_backend_property (cal, opid, e_data_cal_create_error_fmt (NotSupported, _("Cannot change value of calendar property '%s'"), prop_name));
160 }
161
162 static void
163 cal_backend_set_property (GObject *object,
164                           guint property_id,
165                           const GValue *value,
166                           GParamSpec *pspec)
167 {
168         switch (property_id) {
169                 case PROP_CACHE_DIR:
170                         e_cal_backend_set_cache_dir (
171                                 E_CAL_BACKEND (object),
172                                 g_value_get_string (value));
173                         return;
174                 case PROP_KIND:
175                         cal_backend_set_kind (
176                                 E_CAL_BACKEND (object),
177                                 g_value_get_ulong (value));
178                         return;
179         }
180
181         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
182 }
183
184 static void
185 cal_backend_get_property (GObject *object,
186                           guint property_id,
187                           GValue *value,
188                           GParamSpec *pspec)
189 {
190         switch (property_id) {
191                 case PROP_CACHE_DIR:
192                         g_value_set_string (
193                                 value, e_cal_backend_get_cache_dir (
194                                 E_CAL_BACKEND (object)));
195                         return;
196                 case PROP_KIND:
197                         g_value_set_ulong (
198                                 value, e_cal_backend_get_kind (
199                                 E_CAL_BACKEND (object)));
200                         return;
201         }
202
203         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
204 }
205
206 static void
207 cal_backend_finalize (GObject *object)
208 {
209         ECalBackendPrivate *priv;
210
211         priv = E_CAL_BACKEND (object)->priv;
212
213         g_assert (priv->clients == NULL);
214
215         g_slist_free (priv->views);
216         /* should be NULL, anyway */
217         g_slist_free (priv->clients);
218
219         g_mutex_free (priv->clients_mutex);
220         g_mutex_free (priv->views_mutex);
221
222         g_free (priv->cache_dir);
223
224         /* Chain up to parent's finalize() method. */
225         G_OBJECT_CLASS (e_cal_backend_parent_class)->finalize (object);
226 }
227
228 static void
229 cal_backend_constructed (GObject *object)
230 {
231         cal_backend_set_default_cache_dir (E_CAL_BACKEND (object));
232
233         G_OBJECT_CLASS (e_cal_backend_parent_class)->constructed (object);
234 }
235
236 static void
237 e_cal_backend_class_init (ECalBackendClass *klass)
238 {
239         GObjectClass *object_class;
240
241         g_type_class_add_private (klass, sizeof (ECalBackendPrivate));
242
243         object_class = G_OBJECT_CLASS (klass);
244         object_class->set_property = cal_backend_set_property;
245         object_class->get_property = cal_backend_get_property;
246         object_class->finalize = cal_backend_finalize;
247         object_class->constructed = cal_backend_constructed;
248
249         klass->get_backend_property = cal_backend_get_backend_property;
250         klass->set_backend_property = cal_backend_set_backend_property;
251
252         g_object_class_install_property (
253                 object_class,
254                 PROP_CACHE_DIR,
255                 g_param_spec_string (
256                         "cache-dir",
257                         NULL,
258                         NULL,
259                         NULL,
260                         G_PARAM_READWRITE));
261
262         g_object_class_install_property (
263                 object_class,
264                 PROP_KIND,
265                 g_param_spec_ulong (
266                         "kind",
267                         NULL,
268                         NULL,
269                         ICAL_NO_COMPONENT,
270                         ICAL_XLICMIMEPART_COMPONENT,
271                         ICAL_NO_COMPONENT,
272                         G_PARAM_READWRITE |
273                         G_PARAM_CONSTRUCT_ONLY));
274 }
275
276 static void
277 e_cal_backend_init (ECalBackend *backend)
278 {
279         backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (
280                 backend, E_TYPE_CAL_BACKEND, ECalBackendPrivate);
281
282         backend->priv->clients = NULL;
283         backend->priv->clients_mutex = g_mutex_new ();
284
285         backend->priv->views = NULL;
286         backend->priv->views_mutex = g_mutex_new ();
287
288         backend->priv->readonly = TRUE;
289 }
290
291 /**
292  * e_cal_backend_get_kind:
293  * @backend: an #ECalBackend
294  *
295  * Gets the kind of components the given backend stores.
296  *
297  * Returns: The kind of components for this backend.
298  */
299 icalcomponent_kind
300 e_cal_backend_get_kind (ECalBackend *backend)
301 {
302         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), ICAL_NO_COMPONENT);
303
304         return backend->priv->kind;
305 }
306
307 /**
308  * e_cal_backend_is_opened:
309  * @backend: an #ECalBackend
310  *
311  * Checks if @backend's storage has been opened (and
312  * authenticated, if necessary) and the backend itself
313  * is ready for accessing. This property is changed automatically
314  * within call of e_cal_backend_notify_opened().
315  *
316  * Returns: %TRUE if fully opened, %FALSE otherwise.
317  *
318  * Since: 3.2
319  **/
320 gboolean
321 e_cal_backend_is_opened (ECalBackend *backend)
322 {
323         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
324
325         return backend->priv->opened;
326 }
327
328 /**
329  * e_cal_backend_is_opening:
330  * @backend: an #ECalBackend
331  *
332  * Checks if @backend is processing its opening phase, which
333  * includes everything since the e_cal_backend_open() call,
334  * through authentication, up to e_cal_backend_notify_opened().
335  * This property is managed automatically and the backend deny
336  * every operation except of cancel and authenticate_user while
337  * it is being opening.
338  *
339  * Returns: %TRUE if opening phase is in the effect, %FALSE otherwise.
340  *
341  * Since: 3.2
342  **/
343 gboolean
344 e_cal_backend_is_opening (ECalBackend *backend)
345 {
346         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
347
348         return backend->priv->opening;
349 }
350
351 /**
352  * e_cal_backend_is_readonly:
353  * @backend: an #ECalBackend
354  *
355  * Returns: Whether is backend read-only. This value is the last used
356  * in a call of e_cal_backend_notify_readonly().
357  *
358  * Since: 3.2
359  **/
360 gboolean
361 e_cal_backend_is_readonly (ECalBackend *backend)
362 {
363         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
364
365         return backend->priv->readonly;
366 }
367
368 /**
369  * e_cal_backend_is_removed:
370  * @backend: an #ECalBackend
371  *
372  * Checks if @backend has been removed from its physical storage.
373  *
374  * Returns: %TRUE if @backend has been removed, %FALSE otherwise.
375  *
376  * Since: 3.2
377  **/
378 gboolean
379 e_cal_backend_is_removed (ECalBackend *backend)
380 {
381         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
382
383         return backend->priv->removed;
384 }
385
386 /**
387  * e_cal_backend_set_is_removed:
388  * @backend: an #ECalBackend
389  * @is_removed: A flag indicating whether the backend's storage was removed
390  *
391  * Sets the flag indicating whether @backend was removed to @is_removed.
392  * Meant to be used by backend implementations.
393  *
394  * Since: 3.2
395  **/
396 void
397 e_cal_backend_set_is_removed (ECalBackend *backend,
398                               gboolean is_removed)
399 {
400         g_return_if_fail (E_IS_CAL_BACKEND (backend));
401
402         backend->priv->removed = is_removed;
403 }
404
405 /**
406  * e_cal_backend_get_cache_dir:
407  * @backend: an #ECalBackend
408  *
409  * Returns the cache directory for the given backend.
410  *
411  * Returns: the cache directory for the backend
412  *
413  * Since: 2.32
414  **/
415 const gchar *
416 e_cal_backend_get_cache_dir (ECalBackend *backend)
417 {
418         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
419
420         return backend->priv->cache_dir;
421 }
422
423 /**
424  * e_cal_backend_set_cache_dir:
425  * @backend: an #ECalBackend
426  * @cache_dir: a local cache directory
427  *
428  * Sets the cache directory for the given backend.
429  *
430  * Note that #ECalBackend is initialized with a usable default based on
431  * #ECalBackend:source and #ECalBackend:kind properties.  Backends should
432  * not override the default without good reason.
433  *
434  * Since: 2.32
435  **/
436 void
437 e_cal_backend_set_cache_dir (ECalBackend *backend,
438                              const gchar *cache_dir)
439 {
440         g_return_if_fail (E_IS_CAL_BACKEND (backend));
441         g_return_if_fail (cache_dir != NULL);
442
443         g_free (backend->priv->cache_dir);
444         backend->priv->cache_dir = g_strdup (cache_dir);
445
446         g_object_notify (G_OBJECT (backend), "cache-dir");
447 }
448
449 /**
450  * e_cal_backend_create_cache_filename:
451  * @backend: an #ECalBackend
452  * @uid: a component UID
453  * @filename: a filename to use; can be NULL
454  * @fileindex: index of a file; used only when @filename is NULL
455  *
456  * Returns: a filename for an attachment in a local cache dir. Free returned
457  * pointer with a g_free().
458  *
459  * Since: 3.4
460  **/
461 gchar *
462 e_cal_backend_create_cache_filename (ECalBackend *backend, const gchar *uid, const gchar *filename, gint fileindex)
463 {
464         g_return_val_if_fail (backend != NULL, NULL);
465         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
466
467         return e_filename_mkdir_encoded (e_cal_backend_get_cache_dir (backend), uid, filename, fileindex);
468 }
469
470 /**
471  * e_cal_backend_get_backend_property:
472  * @backend: an #ECalBackend
473  * @cal: an #EDataCal
474  * @opid: the ID to use for this operation
475  * @cancellable: a #GCancellable for the operation
476  * @prop_name: property name to get value of; cannot be NULL
477  *
478  * Calls the get_backend_property method on the given backend.
479  * This might be finished with e_data_cal_respond_get_backend_property().
480  * Default implementation takes care of common properties and returns
481  * an 'unsupported' error for any unknown properties. The subclass may
482  * always call this default implementation for properties which fetching
483  * it doesn't overwrite.
484  *
485  * Since: 3.2
486  **/
487 void
488 e_cal_backend_get_backend_property (ECalBackend *backend,
489                                     EDataCal *cal,
490                                     guint32 opid,
491                                     GCancellable *cancellable,
492                                     const gchar *prop_name)
493 {
494         g_return_if_fail (backend != NULL);
495         g_return_if_fail (E_IS_CAL_BACKEND (backend));
496         g_return_if_fail (prop_name != NULL);
497         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property != NULL);
498
499         (* E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property) (backend, cal, opid, cancellable, prop_name);
500 }
501
502 /**
503  * e_cal_backend_set_backend_property:
504  * @backend: an #ECalBackend
505  * @cal: an #EDataCal
506  * @opid: the ID to use for this operation
507  * @cancellable: a #GCancellable for the operation
508  * @prop_name: property name to change; cannot be NULL
509  * @prop_value: value to set to @prop_name; cannot be NULL
510  *
511  * Calls the set_backend_property method on the given backend.
512  * This might be finished with e_data_cal_respond_set_backend_property().
513  * Default implementation simply returns an 'unsupported' error.
514  * The subclass may always call this default implementation for properties
515  * which fetching it doesn't overwrite.
516  *
517  * Since: 3.2
518  **/
519 void
520 e_cal_backend_set_backend_property (ECalBackend *backend,
521                                     EDataCal *cal,
522                                     guint32 opid,
523                                     GCancellable *cancellable,
524                                     const gchar *prop_name,
525                                     const gchar *prop_value)
526 {
527         g_return_if_fail (backend != NULL);
528         g_return_if_fail (E_IS_CAL_BACKEND (backend));
529         g_return_if_fail (prop_name != NULL);
530         g_return_if_fail (prop_value != NULL);
531         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->set_backend_property != NULL);
532
533         (* E_CAL_BACKEND_GET_CLASS (backend)->set_backend_property) (backend, cal, opid, cancellable, prop_name, prop_value);
534 }
535
536 static void
537 cal_destroy_cb (gpointer data,
538                 GObject *where_cal_was)
539 {
540         e_cal_backend_remove_client_private (E_CAL_BACKEND (data),
541                                              (EDataCal *) where_cal_was, FALSE);
542 }
543
544 /**
545  * e_cal_backend_add_client:
546  * @backend: an #ECalBackend
547  * @cal: an #EDataCal
548  *
549  * Adds a new client to the given backend. For any event, the backend will
550  * notify all clients added via this function.
551  */
552 void
553 e_cal_backend_add_client (ECalBackend *backend,
554                           EDataCal *cal)
555 {
556         ECalBackendPrivate *priv;
557
558         g_return_if_fail (backend != NULL);
559         g_return_if_fail (E_IS_CAL_BACKEND (backend));
560         g_return_if_fail (cal != NULL);
561         g_return_if_fail (E_IS_DATA_CAL (cal));
562
563         priv = backend->priv;
564
565         g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
566
567         g_mutex_lock (priv->clients_mutex);
568         priv->clients = g_slist_append (priv->clients, cal);
569         g_mutex_unlock (priv->clients_mutex);
570 }
571
572 static void
573 e_cal_backend_remove_client_private (ECalBackend *backend,
574                                      EDataCal *cal,
575                                      gboolean weak_unref)
576 {
577         ECalBackendPrivate *priv;
578
579         /* XXX this needs a bit more thinking wrt the mutex - we
580          * should be holding it when we check to see if clients is
581          * NULL */
582         g_return_if_fail (backend != NULL);
583         g_return_if_fail (E_IS_CAL_BACKEND (backend));
584         g_return_if_fail (cal != NULL);
585         g_return_if_fail (E_IS_DATA_CAL (cal));
586
587         priv = backend->priv;
588
589         if (weak_unref)
590                 g_object_weak_unref (G_OBJECT (cal), cal_destroy_cb, backend);
591
592         /* Disconnect */
593         g_mutex_lock (priv->clients_mutex);
594         priv->clients = g_slist_remove (priv->clients, cal);
595         g_mutex_unlock (priv->clients_mutex);
596
597         /* When all clients go away, notify the parent factory about it so that
598          * it may decide whether to kill the backend or not.
599          */
600         if (!priv->clients) {
601                 priv->opening = FALSE;
602                 e_backend_last_client_gone (E_BACKEND (backend));
603         }
604 }
605
606 /**
607  * e_cal_backend_remove_client:
608  * @backend: an #ECalBackend
609  * @cal: an #EDataCal
610  *
611  * Removes a client from the list of connected clients to the given backend.
612  */
613 void
614 e_cal_backend_remove_client (ECalBackend *backend,
615                              EDataCal *cal)
616 {
617         e_cal_backend_remove_client_private (backend, cal, TRUE);
618 }
619
620 /**
621  * e_cal_backend_add_view:
622  * @backend: an #ECalBackend
623  * @view: An #EDataCalView object.
624  *
625  * Adds a view to the list of live views being run by the given backend.
626  * Doing so means that any listener on the view will get notified of any
627  * change that affect the live view.
628  *
629  * Since: 3.2
630  */
631 void
632 e_cal_backend_add_view (ECalBackend *backend,
633                         EDataCalView *view)
634 {
635         g_return_if_fail (backend != NULL);
636         g_return_if_fail (E_IS_CAL_BACKEND (backend));
637
638         g_mutex_lock (backend->priv->views_mutex);
639
640         backend->priv->views = g_slist_append (backend->priv->views, view);
641
642         g_mutex_unlock (backend->priv->views_mutex);
643 }
644
645 /**
646  * e_cal_backend_remove_view
647  * @backend: an #ECalBackend
648  * @view: An #EDataCalView object, previously added with @ref e_cal_backend_add_view.
649  *
650  * Removes view from the list of live views for the backend.
651  *
652  * Since: 3.2
653  **/
654 void
655 e_cal_backend_remove_view (ECalBackend *backend,
656                            EDataCalView *view)
657 {
658         g_return_if_fail (backend != NULL);
659         g_return_if_fail (E_IS_CAL_BACKEND (backend));
660
661         g_mutex_lock (backend->priv->views_mutex);
662
663         backend->priv->views = g_slist_remove (backend->priv->views, view);
664
665         g_mutex_unlock (backend->priv->views_mutex);
666 }
667
668 /**
669  * e_cal_backend_foreach_view:
670  * @backend: an #ECalBackend
671  * @callback: callback to call
672  * @user_data: user_data passed into the @callback
673  *
674  * Calls @callback for each known calendar view of this @backend.
675  * @callback returns %FALSE to stop further processing.
676  *
677  * Since: 3.2
678  **/
679 void
680 e_cal_backend_foreach_view (ECalBackend *backend,
681                             gboolean (*callback) (EDataCalView *view,
682                                                   gpointer user_data),
683                             gpointer user_data)
684 {
685         const GSList *views;
686         EDataCalView *view;
687         gboolean stop = FALSE;
688
689         g_return_if_fail (backend != NULL);
690         g_return_if_fail (callback != NULL);
691
692         g_mutex_lock (backend->priv->views_mutex);
693
694         for (views = backend->priv->views; views && !stop; views = views->next) {
695                 view = E_DATA_CAL_VIEW (views->data);
696
697                 g_object_ref (view);
698                 stop = !callback (view, user_data);
699                 g_object_unref (view);
700         }
701
702         g_mutex_unlock (backend->priv->views_mutex);
703 }
704
705 /**
706  * e_cal_backend_set_notification_proxy:
707  * @backend: an #ECalBackend
708  * @proxy: The calendar backend to act as notification proxy.
709  *
710  * Sets the backend that will act as notification proxy for the given backend.
711  *
712  * Since: 3.2
713  */
714 void
715 e_cal_backend_set_notification_proxy (ECalBackend *backend,
716                                       ECalBackend *proxy)
717 {
718         g_return_if_fail (E_IS_CAL_BACKEND (backend));
719
720         backend->priv->notification_proxy = proxy;
721 }
722
723 /**
724  * e_cal_backend_open:
725  * @backend: an #ECalBackend
726  * @cal: an #EDataCal
727  * @opid: the ID to use for this operation
728  * @cancellable: a #GCancellable for the operation
729  * @only_if_exists: Whether the calendar should be opened only if it already
730  * exists.  If FALSE, a new calendar will be created when the specified @uri
731  * does not exist.
732  *
733  * Opens a calendar backend with data from a calendar stored at the specified URI.
734  * This might be finished with e_data_cal_respond_open() or e_cal_backend_respond_opened(),
735  * though the overall opening phase finishes only after call
736  * of e_cal_backend_notify_opened() after which call the backend
737  * is either fully opened (including authentication against (remote)
738  * server/storage) or an error was encountered during this opening phase.
739  * 'opened' and 'opening' properties are updated automatically.
740  * The backend refuses all other operations until the opening phase is finished.
741  *
742  * The e_cal_backend_notify_opened() is called either from this function
743  * or from e_cal_backend_authenticate_user(), or after necessary steps
744  * initiated by these two functions.
745  *
746  * The opening phase usually works like this:
747  * 1) client requests open for the backend
748  * 2) server receives this request and calls e_cal_backend_open() - the opening phase begun
749  * 3) either the backend is opened during this call, and notifies client
750  *    with e_cal_backend_notify_opened() about that. This is usually
751  *    for local backends; their opening phase is finished
752  * 4) or the backend requires authentication, thus it notifies client
753  *    about that with e_cal_backend_notify_auth_required() and is
754  *    waiting for credentials, which will be received from client
755  *    by e_cal_backend_authenticate_user() call. Backend's opening
756  *    phase is still running in this case, thus it doesn't call
757  *    e_cal_backend_notify_opened() within e_cal_backend_open() call.
758  * 5) when backend receives credentials in e_cal_backend_authenticate_user()
759  *    then it tries to authenticate against a server/storage with them
760  *    and only after it knows result of the authentication, whether user
761  *    was or wasn't authenticated, it notifies client with the result
762  *    by e_cal_backend_notify_opened() and it's opening phase is
763  *    finished now. If there was no error returned then the backend is
764  *    considered opened, otherwise it's considered closed. Use AuthenticationFailed
765  *    error when the given credentials were rejected by the server/store, which
766  *    will result in a re-prompt on the client side, otherwise use AuthenticationRequired
767  *    if there was anything wrong with the given credentials. Set error's
768  *    message to a reason for a re-prompt, it'll be shown to a user.
769  * 6) client checks error returned from e_cal_backend_notify_opened() and
770  *    reprompts for a password if it was AuthenticationFailed. Otherwise
771  *    considers backend opened based on the error presence (no error means success).
772  *
773  * In any case, the call of e_cal_backend_open() should be always finished
774  * with e_data_cal_respond_open(), which has no influence on the opening phase,
775  * or alternatively with e_cal_backend_respond_opened(). Never use authentication
776  * errors in e_data_cal_respond_open() to notify the client the authentication is
777  * required, there is e_cal_backend_notify_auth_required() for this.
778  **/
779 void
780 e_cal_backend_open (ECalBackend *backend,
781                     EDataCal *cal,
782                     guint32 opid,
783                     GCancellable *cancellable,
784                     gboolean only_if_exists)
785 {
786         g_return_if_fail (backend != NULL);
787         g_return_if_fail (E_IS_CAL_BACKEND (backend));
788         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->open != NULL);
789
790         g_mutex_lock (backend->priv->clients_mutex);
791
792         if (e_cal_backend_is_opened (backend)) {
793                 gboolean online;
794
795                 g_mutex_unlock (backend->priv->clients_mutex);
796
797                 e_data_cal_report_readonly (cal, backend->priv->readonly);
798
799                 online = e_backend_get_online (E_BACKEND (backend));
800                 e_data_cal_report_online (cal, online);
801
802                 e_cal_backend_respond_opened (backend, cal, opid, NULL);
803         } else if (e_cal_backend_is_opening (backend)) {
804                 g_mutex_unlock (backend->priv->clients_mutex);
805
806                 e_data_cal_respond_open (cal, opid, EDC_OPENING_ERROR);
807         } else {
808                 backend->priv->opening = TRUE;
809                 g_mutex_unlock (backend->priv->clients_mutex);
810
811                 (* E_CAL_BACKEND_GET_CLASS (backend)->open) (backend, cal, opid, cancellable, only_if_exists);
812         }
813 }
814
815 /**
816  * e_cal_backend_authenticate_user:
817  * @backend: an #ECalBackend
818  * @cancellable: a #GCancellable for the operation
819  * @credentials: #ECredentials to use for authentication
820  *
821  * Notifies @backend about @credentials provided by user to use
822  * for authentication. This notification is usually called during
823  * opening phase as a response to e_cal_backend_notify_auth_required()
824  * on the client side and it results in setting property 'opening' to %TRUE
825  * unless the backend is already opened. This function finishes opening
826  * phase, thus it should be finished with e_cal_backend_notify_opened().
827  *
828  * See information at e_cal_backend_open() for more details
829  * how the opening phase works.
830  *
831  * Since: 3.2
832  **/
833 void
834 e_cal_backend_authenticate_user (ECalBackend *backend,
835                                  GCancellable *cancellable,
836                                  ECredentials *credentials)
837 {
838         g_return_if_fail (E_IS_CAL_BACKEND (backend));
839         g_return_if_fail (credentials != NULL);
840         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->authenticate_user);
841
842         if (!e_cal_backend_is_opened (backend))
843                 backend->priv->opening = TRUE;
844
845         (* E_CAL_BACKEND_GET_CLASS (backend)->authenticate_user) (backend, cancellable, credentials);
846 }
847
848 /**
849  * e_cal_backend_remove:
850  * @backend: an #ECalBackend
851  * @cal: an #EDataCal
852  * @opid: the ID to use for this operation
853  * @cancellable: a #GCancellable for the operation
854  *
855  * Removes the calendar being accessed by the given backend.
856  * This might be finished with e_data_cal_respond_remove().
857  **/
858 void
859 e_cal_backend_remove (ECalBackend *backend,
860                       EDataCal *cal,
861                       guint32 opid,
862                       GCancellable *cancellable)
863 {
864         g_return_if_fail (backend != NULL);
865         g_return_if_fail (E_IS_CAL_BACKEND (backend));
866         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove != NULL);
867
868         if (e_cal_backend_is_opening (backend))
869                 e_data_cal_respond_remove (cal, opid, EDC_OPENING_ERROR);
870         else
871                 (* E_CAL_BACKEND_GET_CLASS (backend)->remove) (backend, cal, opid, cancellable);
872 }
873
874 /**
875  * e_cal_backend_refresh:
876  * @backend: an #ECalBackend
877  * @cal: an #EDataCal
878  * @opid: the ID to use for this operation
879  * @cancellable: a #GCancellable for the operation
880  *
881  * Refreshes the calendar being accessed by the given backend.
882  * This might be finished with e_data_cal_respond_refresh(),
883  * and it might be called as soon as possible; it doesn't mean
884  * that the refreshing is done after calling that, the backend
885  * is only notifying client whether it started the refresh process
886  * or not.
887  *
888  * Since: 2.30
889  **/
890 void
891 e_cal_backend_refresh (ECalBackend *backend,
892                        EDataCal *cal,
893                        guint32 opid,
894                        GCancellable *cancellable)
895 {
896         g_return_if_fail (backend != NULL);
897         g_return_if_fail (E_IS_CAL_BACKEND (backend));
898
899         if (e_cal_backend_is_opening (backend))
900                 e_data_cal_respond_refresh (cal, opid, EDC_OPENING_ERROR);
901         else if (!E_CAL_BACKEND_GET_CLASS (backend)->refresh)
902                 e_data_cal_respond_refresh (cal, opid, EDC_ERROR (UnsupportedMethod));
903         else if (!e_cal_backend_is_opened (backend))
904                 e_data_cal_respond_refresh (cal, opid, EDC_NOT_OPENED_ERROR);
905         else
906                 (* E_CAL_BACKEND_GET_CLASS (backend)->refresh) (backend, cal, opid, cancellable);
907 }
908
909 /**
910  * e_cal_backend_get_object:
911  * @backend: an #ECalBackend
912  * @cal: an #EDataCal
913  * @opid: the ID to use for this operation
914  * @cancellable: a #GCancellable for the operation
915  * @uid: Unique identifier for a calendar object.
916  * @rid: ID for the object's recurrence to get.
917  *
918  * Queries a calendar backend for a calendar object based on its unique
919  * identifier and its recurrence ID (if a recurrent appointment).
920  * This might be finished with e_data_cal_respond_get_object().
921  **/
922 void
923 e_cal_backend_get_object (ECalBackend *backend,
924                           EDataCal *cal,
925                           guint32 opid,
926                           GCancellable *cancellable,
927                           const gchar *uid,
928                           const gchar *rid)
929 {
930         g_return_if_fail (backend != NULL);
931         g_return_if_fail (E_IS_CAL_BACKEND (backend));
932         g_return_if_fail (uid != NULL);
933         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object != NULL);
934
935         if (e_cal_backend_is_opening (backend))
936                 e_data_cal_respond_get_object (cal, opid, EDC_OPENING_ERROR, NULL);
937         else if (!e_cal_backend_is_opened (backend))
938                 e_data_cal_respond_get_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
939         else
940                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_object) (backend, cal, opid, cancellable, uid, rid);
941 }
942
943 /**
944  * e_cal_backend_get_object_list:
945  * @backend: an #ECalBackend
946  * @cal: an #EDataCal
947  * @opid: the ID to use for this operation
948  * @cancellable: a #GCancellable for the operation
949  * @sexp: Expression to search for.
950  *
951  * Calls the get_object_list method on the given backend.
952  * This might be finished with e_data_cal_respond_get_object_list().
953  **/
954 void
955 e_cal_backend_get_object_list (ECalBackend *backend,
956                                EDataCal *cal,
957                                guint32 opid,
958                                GCancellable *cancellable,
959                                const gchar *sexp)
960 {
961         g_return_if_fail (backend != NULL);
962         g_return_if_fail (E_IS_CAL_BACKEND (backend));
963         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object_list != NULL);
964
965         if (e_cal_backend_is_opening (backend))
966                 e_data_cal_respond_get_object_list (cal, opid, EDC_OPENING_ERROR, NULL);
967         else if (!e_cal_backend_is_opened (backend))
968                 e_data_cal_respond_get_object_list (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
969         else
970                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_object_list) (backend, cal, opid, cancellable, sexp);
971 }
972
973 /**
974  * e_cal_backend_get_free_busy:
975  * @backend: an #ECalBackend
976  * @cal: an #EDataCal
977  * @opid: the ID to use for this operation
978  * @cancellable: a #GCancellable for the operation
979  * @users: List of users to get free/busy information for.
980  * @start: Start time for query.
981  * @end: End time for query.
982  *
983  * Gets a free/busy object for the given time interval. Client side is
984  * notified about free/busy objects throug e_data_cal_report_free_busy_data().
985  * This might be finished with e_data_cal_respond_get_free_busy().
986  **/
987 void
988 e_cal_backend_get_free_busy (ECalBackend *backend,
989                              EDataCal *cal,
990                              guint32 opid,
991                              GCancellable *cancellable,
992                              const GSList *users,
993                              time_t start,
994                              time_t end)
995 {
996         g_return_if_fail (backend != NULL);
997         g_return_if_fail (E_IS_CAL_BACKEND (backend));
998         g_return_if_fail (start != -1 && end != -1);
999         g_return_if_fail (start <= end);
1000         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy != NULL);
1001
1002         if (e_cal_backend_is_opening (backend))
1003                 e_data_cal_respond_get_free_busy (cal, opid, EDC_OPENING_ERROR);
1004         else if (!e_cal_backend_is_opened (backend))
1005                 e_data_cal_respond_get_free_busy (cal, opid, EDC_NOT_OPENED_ERROR);
1006         else
1007                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy) (backend, cal, opid, cancellable, users, start, end);
1008 }
1009
1010 /**
1011  * e_cal_backend_create_object:
1012  * @backend: an #ECalBackend
1013  * @cal: an #EDataCal
1014  * @opid: the ID to use for this operation
1015  * @cancellable: a #GCancellable for the operation
1016  * @calobj: The object to create.
1017  *
1018  * Calls the create_object method on the given backend.
1019  * This might be finished with e_data_cal_respond_create_object().
1020  **/
1021 void
1022 e_cal_backend_create_object (ECalBackend *backend,
1023                              EDataCal *cal,
1024                              guint32 opid,
1025                              GCancellable *cancellable,
1026                              const gchar *calobj)
1027 {
1028         g_return_if_fail (backend != NULL);
1029         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1030         g_return_if_fail (calobj != NULL);
1031
1032         if (e_cal_backend_is_opening (backend))
1033                 e_data_cal_respond_create_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1034         else if (!E_CAL_BACKEND_GET_CLASS (backend)->create_object)
1035                 e_data_cal_respond_create_object (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
1036         else if (!e_cal_backend_is_opened (backend))
1037                 e_data_cal_respond_create_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1038         else
1039                 (* E_CAL_BACKEND_GET_CLASS (backend)->create_object) (backend, cal, opid, cancellable, calobj);
1040 }
1041
1042 /**
1043  * e_cal_backend_modify_object:
1044  * @backend: an #ECalBackend
1045  * @cal: an #EDataCal
1046  * @opid: the ID to use for this operation
1047  * @cancellable: a #GCancellable for the operation
1048  * @calobj: Object to be modified.
1049  * @mod: Type of modification.
1050  *
1051  * Calls the modify_object method on the given backend.
1052  * This might be finished with e_data_cal_respond_modify_object().
1053  **/
1054 void
1055 e_cal_backend_modify_object (ECalBackend *backend,
1056                              EDataCal *cal,
1057                              guint32 opid,
1058                              GCancellable *cancellable,
1059                              const gchar *calobj,
1060                              CalObjModType mod)
1061 {
1062         g_return_if_fail (backend != NULL);
1063         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1064         g_return_if_fail (calobj != NULL);
1065
1066         if (e_cal_backend_is_opening (backend))
1067                 e_data_cal_respond_modify_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1068         else if (!E_CAL_BACKEND_GET_CLASS (backend)->modify_object)
1069                 e_data_cal_respond_modify_object (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
1070         else if (!e_cal_backend_is_opened (backend))
1071                 e_data_cal_respond_modify_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1072         else
1073                 (* E_CAL_BACKEND_GET_CLASS (backend)->modify_object) (backend, cal, opid, cancellable, calobj, mod);
1074 }
1075
1076 /**
1077  * e_cal_backend_remove_object:
1078  * @backend: an #ECalBackend
1079  * @cal: an #EDataCal
1080  * @opid: the ID to use for this operation
1081  * @cancellable: a #GCancellable for the operation
1082  * @uid: Unique identifier of the object to remove.
1083  * @rid: A recurrence ID.
1084  * @mod: Type of removal.
1085  *
1086  * Removes an object in a calendar backend.  The backend will notify all of its
1087  * clients about the change.
1088  * This might be finished with e_data_cal_respond_remove_object().
1089  **/
1090 void
1091 e_cal_backend_remove_object (ECalBackend *backend,
1092                              EDataCal *cal,
1093                              guint32 opid,
1094                              GCancellable *cancellable,
1095                              const gchar *uid,
1096                              const gchar *rid,
1097                              CalObjModType mod)
1098 {
1099         g_return_if_fail (backend != NULL);
1100         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1101         g_return_if_fail (uid != NULL);
1102         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove_object != NULL);
1103
1104         if (e_cal_backend_is_opening (backend))
1105                 e_data_cal_respond_remove_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL, NULL);
1106         else if (!e_cal_backend_is_opened (backend))
1107                 e_data_cal_respond_remove_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL, NULL);
1108         else
1109                 (* E_CAL_BACKEND_GET_CLASS (backend)->remove_object) (backend, cal, opid, cancellable, uid, rid, mod);
1110 }
1111
1112 /**
1113  * e_cal_backend_receive_objects:
1114  * @backend: an #ECalBackend
1115  * @cal: an #EDataCal
1116  * @opid: the ID to use for this operation
1117  * @cancellable: a #GCancellable for the operation
1118  * @calobj: iCalendar object.
1119  *
1120  * Calls the receive_objects method on the given backend.
1121  * This might be finished with e_data_cal_respond_receive_objects().
1122  **/
1123 void
1124 e_cal_backend_receive_objects (ECalBackend *backend,
1125                                EDataCal *cal,
1126                                guint32 opid,
1127                                GCancellable *cancellable,
1128                                const gchar *calobj)
1129 {
1130         g_return_if_fail (backend != NULL);
1131         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1132         g_return_if_fail (calobj != NULL);
1133         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->receive_objects != NULL);
1134
1135         if (e_cal_backend_is_opening (backend))
1136                 e_data_cal_respond_receive_objects (cal, opid, EDC_OPENING_ERROR);
1137         else if (!e_cal_backend_is_opened (backend))
1138                 e_data_cal_respond_receive_objects (cal, opid, EDC_NOT_OPENED_ERROR);
1139         else
1140                 (* E_CAL_BACKEND_GET_CLASS (backend)->receive_objects) (backend, cal, opid, cancellable, calobj);
1141 }
1142
1143 /**
1144  * e_cal_backend_send_objects:
1145  * @backend: an #ECalBackend
1146  * @cal: an #EDataCal
1147  * @opid: the ID to use for this operation
1148  * @cancellable: a #GCancellable for the operation
1149  * @calobj: iCalendar object to be sent.
1150  *
1151  * Calls the send_objects method on the given backend.
1152  * This might be finished with e_data_cal_respond_send_objects().
1153  **/
1154 void
1155 e_cal_backend_send_objects (ECalBackend *backend,
1156                             EDataCal *cal,
1157                             guint32 opid,
1158                             GCancellable *cancellable,
1159                             const gchar *calobj)
1160 {
1161         g_return_if_fail (backend != NULL);
1162         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1163         g_return_if_fail (calobj != NULL);
1164         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->send_objects != NULL);
1165
1166         if (e_cal_backend_is_opening (backend))
1167                 e_data_cal_respond_send_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1168         else if (!e_cal_backend_is_opened (backend))
1169                 e_data_cal_respond_send_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1170         else
1171                 (* E_CAL_BACKEND_GET_CLASS (backend)->send_objects) (backend, cal, opid, cancellable, calobj);
1172 }
1173
1174 /**
1175  * e_cal_backend_get_attachment_uris:
1176  * @backend: an #ECalBackend
1177  * @cal: an #EDataCal
1178  * @opid: the ID to use for this operation
1179  * @cancellable: a #GCancellable for the operation
1180  * @uid: Unique identifier for a calendar object.
1181  * @rid: ID for the object's recurrence to get.
1182  *
1183  * Queries a calendar backend for attachments present in a calendar object based
1184  * on its unique identifier and its recurrence ID (if a recurrent appointment).
1185  * This might be finished with e_data_cal_respond_get_attachment_uris().
1186  *
1187  * Since: 3.2
1188  **/
1189 void
1190 e_cal_backend_get_attachment_uris (ECalBackend *backend,
1191                                    EDataCal *cal,
1192                                    guint32 opid,
1193                                    GCancellable *cancellable,
1194                                    const gchar *uid,
1195                                    const gchar *rid)
1196 {
1197         g_return_if_fail (backend != NULL);
1198         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1199         g_return_if_fail (uid != NULL);
1200         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris != NULL);
1201
1202         if (e_cal_backend_is_opening (backend))
1203                 e_data_cal_respond_get_attachment_uris (cal, opid, EDC_OPENING_ERROR, NULL);
1204         else if (!e_cal_backend_is_opened (backend))
1205                 e_data_cal_respond_get_attachment_uris (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
1206         else
1207                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris) (backend, cal, opid, cancellable, uid, rid);
1208 }
1209
1210 /**
1211  * e_cal_backend_discard_alarm:
1212  * @backend: an #ECalBackend
1213  * @cal: an #EDataCal
1214  * @opid: the ID to use for this operation
1215  * @cancellable: a #GCancellable for the operation
1216  * @uid: Unique identifier for a calendar object.
1217  * @rid: ID for the object's recurrence to discard alarm in.
1218  * @auid: Unique identifier of the alarm itself.
1219  *
1220  * Discards alarm @auid from the object identified by @uid and @rid.
1221  * This might be finished with e_data_cal_respond_discard_alarm().
1222  * Default implementation of this method returns Not Supported error.
1223  **/
1224 void
1225 e_cal_backend_discard_alarm (ECalBackend *backend,
1226                              EDataCal *cal,
1227                              guint32 opid,
1228                              GCancellable *cancellable,
1229                              const gchar *uid,
1230                              const gchar *rid,
1231                              const gchar *auid)
1232 {
1233         g_return_if_fail (backend != NULL);
1234         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1235         g_return_if_fail (uid != NULL);
1236         g_return_if_fail (auid != NULL);
1237
1238         if (e_cal_backend_is_opening (backend))
1239                 e_data_cal_respond_discard_alarm (cal, opid, EDC_OPENING_ERROR);
1240         else if (!E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm)
1241                 e_data_cal_respond_discard_alarm (cal, opid, e_data_cal_create_error (NotSupported, NULL));
1242         else if (!e_cal_backend_is_opened (backend))
1243                 e_data_cal_respond_discard_alarm (cal, opid, EDC_NOT_OPENED_ERROR);
1244         else
1245                 (* E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm) (backend, cal, opid, cancellable, uid, rid, auid);
1246 }
1247
1248 /**
1249  * e_cal_backend_get_timezone:
1250  * @backend: an #ECalBackend
1251  * @cal: an #EDataCal
1252  * @opid: the ID to use for this operation
1253  * @cancellable: a #GCancellable for the operation
1254  * @tzid: Unique identifier of a VTIMEZONE object. Note that this must not be
1255  * NULL.
1256  *
1257  * Returns the icaltimezone* corresponding to the TZID, or NULL if the TZID
1258  * can't be found.
1259  * This might be finished with e_data_cal_respond_get_timezone().
1260  **/
1261 void
1262 e_cal_backend_get_timezone (ECalBackend *backend,
1263                             EDataCal *cal,
1264                             guint32 opid,
1265                             GCancellable *cancellable,
1266                             const gchar *tzid)
1267 {
1268         g_return_if_fail (backend != NULL);
1269         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1270         g_return_if_fail (tzid != NULL);
1271         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_timezone != NULL);
1272
1273         if (e_cal_backend_is_opening (backend))
1274                 e_data_cal_respond_get_timezone (cal, opid, EDC_OPENING_ERROR, NULL);
1275         else if (!e_cal_backend_is_opened (backend))
1276                 e_data_cal_respond_get_timezone (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
1277         else
1278                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_timezone) (backend, cal, opid, cancellable, tzid);
1279 }
1280
1281 /**
1282  * e_cal_backend_add_timezone
1283  * @backend: an #ECalBackend
1284  * @cal: an #EDataCal
1285  * @opid: the ID to use for this operation
1286  * @cancellable: a #GCancellable for the operation
1287  * @tzobj: The timezone object, in a string.
1288  *
1289  * Add a timezone object to the given backend.
1290  * This might be finished with e_data_cal_respond_add_timezone().
1291  **/
1292 void
1293 e_cal_backend_add_timezone (ECalBackend *backend,
1294                             EDataCal *cal,
1295                             guint32 opid,
1296                             GCancellable *cancellable,
1297                             const gchar *tzobject)
1298 {
1299         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1300         g_return_if_fail (tzobject != NULL);
1301         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->add_timezone != NULL);
1302
1303         if (e_cal_backend_is_opening (backend))
1304                 e_data_cal_respond_add_timezone (cal, opid, EDC_OPENING_ERROR);
1305         else if (!e_cal_backend_is_opened (backend))
1306                 e_data_cal_respond_add_timezone (cal, opid, EDC_NOT_OPENED_ERROR);
1307         else
1308                 (* E_CAL_BACKEND_GET_CLASS (backend)->add_timezone) (backend, cal, opid, cancellable, tzobject);
1309 }
1310
1311 /**
1312  * e_cal_backend_internal_get_timezone:
1313  * @backend: an #ECalBackend
1314  * @tzid: ID of the timezone to get.
1315  *
1316  * Calls the internal_get_timezone method on the given backend.
1317  */
1318 icaltimezone *
1319 e_cal_backend_internal_get_timezone (ECalBackend *backend,
1320                                      const gchar *tzid)
1321 {
1322         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1323         g_return_val_if_fail (tzid != NULL, NULL);
1324         g_return_val_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->internal_get_timezone != NULL, NULL);
1325
1326         return (* E_CAL_BACKEND_GET_CLASS (backend)->internal_get_timezone) (backend, tzid);
1327 }
1328
1329 /**
1330  * e_cal_backend_start_view:
1331  * @backend: an #ECalBackend
1332  * @view: The view to be started.
1333  *
1334  * Starts a new live view on the given backend.
1335  *
1336  * Since: 3.2
1337  */
1338 void
1339 e_cal_backend_start_view (ECalBackend *backend,
1340                           EDataCalView *view)
1341 {
1342         g_return_if_fail (backend != NULL);
1343         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1344         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->start_view != NULL);
1345
1346         (* E_CAL_BACKEND_GET_CLASS (backend)->start_view) (backend, view);
1347 }
1348
1349 /**
1350  * e_cal_backend_stop_view:
1351  * @backend: an #ECalBackend
1352  * @view: The view to be stopped.
1353  *
1354  * Stops a previously started live view on the given backend.
1355  *
1356  * Since: 3.2
1357  */
1358 void
1359 e_cal_backend_stop_view (ECalBackend *backend,
1360                          EDataCalView *view)
1361 {
1362         g_return_if_fail (backend != NULL);
1363         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1364
1365         /* backward compatibility, do not force each backend define this function */
1366         if (!E_CAL_BACKEND_GET_CLASS (backend)->stop_view)
1367                 return;
1368
1369         (* E_CAL_BACKEND_GET_CLASS (backend)->stop_view) (backend, view);
1370 }
1371
1372 static gboolean
1373 object_created_cb (EDataCalView *view,
1374                    gpointer calobj)
1375 {
1376         if (e_data_cal_view_object_matches (view, calobj))
1377                 e_data_cal_view_notify_objects_added_1 (view, calobj);
1378
1379         return TRUE;
1380 }
1381
1382 /**
1383  * e_cal_backend_notify_object_created:
1384  * @backend: an #ECalBackend
1385  * @calobj: iCalendar representation of new object
1386  *
1387  * Notifies each of the backend's listeners about a new object.
1388  *
1389  * #e_data_cal_notify_object_created() calls this for you. You only need to
1390  * call e_cal_backend_notify_object_created() yourself to report objects
1391  * created by non-EDS clients.
1392  **/
1393 void
1394 e_cal_backend_notify_object_created (ECalBackend *backend,
1395                                      const gchar *calobj)
1396 {
1397         ECalBackendPrivate *priv;
1398
1399         priv = backend->priv;
1400
1401         if (priv->notification_proxy) {
1402                 e_cal_backend_notify_object_created (priv->notification_proxy, calobj);
1403                 return;
1404         }
1405
1406         e_cal_backend_foreach_view (backend, object_created_cb, (gpointer) calobj);
1407 }
1408
1409 /**
1410  * e_cal_backend_notify_objects_added:
1411  *
1412  * Since: 2.24
1413  **/
1414 void
1415 e_cal_backend_notify_objects_added (ECalBackend *backend,
1416                                     EDataCalView *view,
1417                                     const GSList *objects)
1418 {
1419         e_data_cal_view_notify_objects_added (view, objects);
1420 }
1421
1422 static void
1423 match_view_and_notify (EDataCalView *view,
1424                        const gchar *old_object,
1425                        const gchar *object)
1426 {
1427         gboolean old_match = FALSE, new_match = FALSE;
1428
1429         if (old_object)
1430                 old_match = e_data_cal_view_object_matches (view, old_object);
1431
1432         new_match = e_data_cal_view_object_matches (view, object);
1433         if (old_match && new_match)
1434                 e_data_cal_view_notify_objects_modified_1 (view, object);
1435         else if (new_match)
1436                 e_data_cal_view_notify_objects_added_1 (view, object);
1437         else if (old_match) {
1438                 ECalComponent *comp = NULL;
1439
1440                 comp = e_cal_component_new_from_string (old_object);
1441                 if (comp) {
1442                         ECalComponentId *id = e_cal_component_get_id (comp);
1443
1444                         e_data_cal_view_notify_objects_removed_1 (view, id);
1445
1446                         e_cal_component_free_id (id);
1447                         g_object_unref (comp);
1448                 }
1449         }
1450 }
1451
1452 struct call_data {
1453         const gchar *old_object;
1454         const gchar *object;
1455         const ECalComponentId *id;
1456 };
1457
1458 static gboolean
1459 call_match_and_notify (EDataCalView *view,
1460                        gpointer user_data)
1461 {
1462         struct call_data *cd = user_data;
1463
1464         g_return_val_if_fail (user_data != NULL, FALSE);
1465
1466         match_view_and_notify (view, cd->old_object, cd->object);
1467
1468         return TRUE;
1469 }
1470
1471 /**
1472  * e_cal_backend_notify_object_modified:
1473  * @backend: an #ECalBackend
1474  * @old_object: iCalendar representation of the original form of the object
1475  * @object: iCalendar representation of the new form of the object
1476  *
1477  * Notifies each of the backend's listeners about a modified object.
1478  *
1479  * #e_data_cal_notify_object_modified() calls this for you. You only need to
1480  * call e_cal_backend_notify_object_modified() yourself to report objects
1481  * modified by non-EDS clients.
1482  **/
1483 void
1484 e_cal_backend_notify_object_modified (ECalBackend *backend,
1485                                       const gchar *old_object,
1486                                       const gchar *object)
1487 {
1488         ECalBackendPrivate *priv;
1489         struct call_data cd;
1490
1491         priv = backend->priv;
1492
1493         if (priv->notification_proxy) {
1494                 e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object);
1495                 return;
1496         }
1497
1498         cd.old_object = old_object;
1499         cd.object = object;
1500         cd.id = NULL;
1501
1502         e_cal_backend_foreach_view (backend, call_match_and_notify, &cd);
1503 }
1504
1505 /**
1506  * e_cal_backend_notify_objects_modified:
1507  *
1508  * Since: 2.24
1509  **/
1510 void
1511 e_cal_backend_notify_objects_modified (ECalBackend *backend,
1512                                        EDataCalView *view,
1513                                        const GSList *objects)
1514 {
1515         e_data_cal_view_notify_objects_modified (view, objects);
1516 }
1517
1518 static gboolean
1519 object_removed_cb (EDataCalView *view,
1520                    gpointer user_data)
1521 {
1522         struct call_data *cd = user_data;
1523
1524         g_return_val_if_fail (user_data != NULL, FALSE);
1525
1526         if (cd->object == NULL) {
1527                 /* if object == NULL, it means the object has been completely
1528                  * removed from the backend */
1529                 if (!cd->old_object || e_data_cal_view_object_matches (view, cd->old_object))
1530                         e_data_cal_view_notify_objects_removed_1 (view, cd->id);
1531         } else
1532                 match_view_and_notify (view, cd->old_object, cd->object);
1533
1534         return TRUE;
1535 }
1536
1537 /**
1538  * e_cal_backend_notify_object_removed:
1539  * @backend: an #ECalBackend
1540  * @id: the Id of the removed object
1541  * @old_object: iCalendar representation of the removed object
1542  * @new_object: iCalendar representation of the object after the removal. This
1543  * only applies to recurrent appointments that had an instance removed. In that
1544  * case, this function notifies a modification instead of a removal.
1545  *
1546  * Notifies each of the backend's listeners about a removed object.
1547  *
1548  * e_data_cal_notify_object_removed() calls this for you. You only need to
1549  * call e_cal_backend_notify_object_removed() yourself to report objects
1550  * removed by non-EDS clients.
1551  **/
1552 void
1553 e_cal_backend_notify_object_removed (ECalBackend *backend,
1554                                      const ECalComponentId *id,
1555                                      const gchar *old_object,
1556                                      const gchar *object)
1557 {
1558         ECalBackendPrivate *priv;
1559         struct call_data cd;
1560
1561         priv = backend->priv;
1562
1563         if (priv->notification_proxy) {
1564                 e_cal_backend_notify_object_removed (priv->notification_proxy, id, old_object, object);
1565                 return;
1566         }
1567
1568         cd.old_object = old_object;
1569         cd.object = object;
1570         cd.id = id;
1571
1572         e_cal_backend_foreach_view (backend, object_removed_cb, &cd);
1573 }
1574
1575 /**
1576  * e_cal_backend_notify_objects_removed:
1577  *
1578  * Since: 2.24
1579  **/
1580 void
1581 e_cal_backend_notify_objects_removed (ECalBackend *backend,
1582                                       EDataCalView *view,
1583                                       const GSList *ids)
1584 {
1585         e_data_cal_view_notify_objects_removed (view, ids);
1586 }
1587
1588 /**
1589  * e_cal_backend_notify_error:
1590  * @backend: an #ECalBackend
1591  * @message: Error message
1592  *
1593  * Notifies each of the backend's listeners about an error
1594  **/
1595 void
1596 e_cal_backend_notify_error (ECalBackend *backend,
1597                             const gchar *message)
1598 {
1599         ECalBackendPrivate *priv = backend->priv;
1600         GSList *l;
1601
1602         if (priv->notification_proxy) {
1603                 e_cal_backend_notify_error (priv->notification_proxy, message);
1604                 return;
1605         }
1606
1607         g_mutex_lock (priv->clients_mutex);
1608
1609         for (l = priv->clients; l; l = l->next)
1610                 e_data_cal_report_error (l->data, message);
1611
1612         g_mutex_unlock (priv->clients_mutex);
1613 }
1614
1615 /**
1616  * e_cal_backend_notify_readonly:
1617  * @backend: an #ECalBackend
1618  * @is_readonly: flag indicating readonly status
1619  *
1620  * Notifies all backend's clients about the current readonly state.
1621  * Meant to be used by backend implementations.
1622  **/
1623 void
1624 e_cal_backend_notify_readonly (ECalBackend *backend,
1625                                gboolean is_readonly)
1626 {
1627         ECalBackendPrivate *priv;
1628         GSList *l;
1629
1630         priv = backend->priv;
1631         priv->readonly = is_readonly;
1632
1633         if (priv->notification_proxy) {
1634                 e_cal_backend_notify_readonly (priv->notification_proxy, is_readonly);
1635                 return;
1636         }
1637
1638         g_mutex_lock (priv->clients_mutex);
1639
1640         for (l = priv->clients; l; l = l->next)
1641                 e_data_cal_report_readonly (l->data, is_readonly);
1642
1643         g_mutex_unlock (priv->clients_mutex);
1644 }
1645
1646 /**
1647  * e_cal_backend_notify_online:
1648  * @backend: an #ECalBackend
1649  * @is_online: flag indicating whether @backend is connected and online
1650  *
1651  * Notifies clients of @backend's connection status indicated by @is_online.
1652  * Meant to be used by backend implementations.
1653  *
1654  * Since: 3.2
1655  **/
1656 void
1657 e_cal_backend_notify_online (ECalBackend *backend,
1658                              gboolean is_online)
1659 {
1660         ECalBackendPrivate *priv;
1661         GSList *clients;
1662
1663         priv = backend->priv;
1664
1665         if (priv->notification_proxy) {
1666                 e_cal_backend_notify_online (priv->notification_proxy, is_online);
1667                 return;
1668         }
1669
1670         g_mutex_lock (priv->clients_mutex);
1671
1672         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1673                 e_data_cal_report_online (E_DATA_CAL (clients->data), is_online);
1674
1675         g_mutex_unlock (priv->clients_mutex);
1676 }
1677
1678 /**
1679  * e_cal_backend_notify_auth_required:
1680  * @backend: an #ECalBackend
1681  * @is_self: Use %TRUE to indicate the authentication is required
1682  *    for the @backend, otheriwse the authentication is for any
1683  *    other source. Having @credentials %NULL means @is_self
1684  *    automatically.
1685  * @credentials: an #ECredentials that contains extra information for
1686  *    a source for which authentication is requested.
1687  *    This parameter can be NULL to indicate "for this calendar".
1688  *
1689  * Notifies clients that @backend requires authentication in order to
1690  * connect. This function call does not influence 'opening', but 
1691  * influences 'opened' property, which is set to %FALSE when @is_self
1692  * is %TRUE or @credentials is %NULL. Opening phase is finished
1693  * by e_cal_backend_notify_opened() if this is requested for @backend.
1694  *
1695  * See e_cal_backend_open() for a description how the whole opening
1696  * phase works.
1697  *
1698  * Meant to be used by backend implementations.
1699  **/
1700 void
1701 e_cal_backend_notify_auth_required (ECalBackend *backend,
1702                                     gboolean is_self,
1703                                     const ECredentials *credentials)
1704 {
1705         ECalBackendPrivate *priv;
1706         GSList *clients;
1707
1708         priv = backend->priv;
1709
1710         if (priv->notification_proxy) {
1711                 e_cal_backend_notify_auth_required (priv->notification_proxy, is_self, credentials);
1712                 return;
1713         }
1714
1715         g_mutex_lock (priv->clients_mutex);
1716
1717         if (is_self || !credentials)
1718                 priv->opened = FALSE;
1719
1720         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1721                 e_data_cal_report_auth_required (E_DATA_CAL (clients->data), credentials);
1722
1723         g_mutex_unlock (priv->clients_mutex);
1724 }
1725
1726 /**
1727  * e_cal_backend_notify_opened:
1728  * @backend: an #ECalBackend
1729  * @error: a #GError corresponding to the error encountered during
1730  *    the opening phase. Use %NULL for success. The @error is freed
1731  *    automatically if not %NULL.
1732  *
1733  * Notifies clients that @backend finished its opening phase.
1734  * See e_cal_backend_open() for more information how the opening
1735  * phase works. Calling this function changes 'opening' property,
1736  * same as 'opened'. 'opening' is set to %FALSE and the backend
1737  * is considered 'opened' only if the @error is %NULL.
1738  *
1739  * See also: e_cal_backend_respond_opened()
1740  *
1741  * Note: The @error is freed automatically if not %NULL.
1742  *
1743  * Meant to be used by backend implementations.
1744  *
1745  * Since: 3.2
1746  **/
1747 void
1748 e_cal_backend_notify_opened (ECalBackend *backend,
1749                              GError *error)
1750 {
1751         ECalBackendPrivate *priv;
1752         GSList *clients;
1753
1754         priv = backend->priv;
1755         g_mutex_lock (priv->clients_mutex);
1756
1757         priv->opening = FALSE;
1758         priv->opened = error == NULL;
1759
1760         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1761                 e_data_cal_report_opened (E_DATA_CAL (clients->data), error);
1762
1763         g_mutex_unlock (priv->clients_mutex);
1764
1765         if (error)
1766                 g_error_free (error);
1767 }
1768
1769 /**
1770  * e_cal_backend_notify_property_changed:
1771  * @backend: an #ECalBackend
1772  * @prop_name: property name, which changed
1773  * @prop_value: new property value
1774  *
1775  * Notifies client about property value change.
1776  *
1777  * Since: 3.2
1778  **/
1779 void
1780 e_cal_backend_notify_property_changed (ECalBackend *backend,
1781                                        const gchar *prop_name,
1782                                        const gchar *prop_value)
1783 {
1784         ECalBackendPrivate *priv;
1785         GSList *clients;
1786
1787         g_return_if_fail (backend != NULL);
1788         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1789         g_return_if_fail (backend->priv != NULL);
1790         g_return_if_fail (prop_name != NULL);
1791         g_return_if_fail (*prop_name != '\0');
1792         g_return_if_fail (prop_value != NULL);
1793
1794         priv = backend->priv;
1795         g_mutex_lock (priv->clients_mutex);
1796
1797         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1798                 e_data_cal_report_backend_property_changed (E_DATA_CAL (clients->data), prop_name, prop_value);
1799
1800         g_mutex_unlock (priv->clients_mutex);
1801 }
1802
1803 /**
1804  * e_cal_backend_respond_opened:
1805  * @backend: an #ECalBackend
1806  * @cal: an #EDataCal
1807  * @opid: an operation ID
1808  * @error: result error; can be %NULL, if it isn't then it's automatically freed
1809  *
1810  * This is a replacement for e_data_cal_respond_open() for cases where
1811  * the finish of 'open' method call also finishes backend opening phase.
1812  * This function covers calling of both e_cal_backend_notify_opened() and
1813  * e_data_cal_respond_open() with the same @error.
1814  *
1815  * See e_cal_backend_open() for more details how the opening phase works.
1816  *
1817  * Since: 3.2
1818  **/
1819 void
1820 e_cal_backend_respond_opened (ECalBackend *backend,
1821                               EDataCal *cal,
1822                               guint32 opid,
1823                               GError *error)
1824 {
1825         GError *copy = NULL;
1826
1827         g_return_if_fail (backend != NULL);
1828         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1829         g_return_if_fail (cal != NULL);
1830         g_return_if_fail (opid != 0);
1831
1832         if (error)
1833                 copy = g_error_copy (error);
1834
1835         e_cal_backend_notify_opened (backend, copy);
1836         e_data_cal_respond_open (cal, opid, error);
1837 }
1838
1839 /**
1840  * e_cal_backend_empty_cache:
1841  * @backend: an #ECalBackend
1842  * @cache: Backend's cache to empty.
1843  *
1844  * Empties backend's cache with all notifications and so on, thus all listening
1845  * will know there is nothing in this backend.
1846  *
1847  * Since: 2.28
1848  **/
1849 void
1850 e_cal_backend_empty_cache (ECalBackend *backend,
1851                            ECalBackendCache *cache)
1852 {
1853         GList *comps_in_cache;
1854
1855         g_return_if_fail (backend != NULL);
1856         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1857
1858         if (!cache)
1859                 return;
1860
1861         g_return_if_fail (E_IS_CAL_BACKEND_CACHE (cache));
1862
1863         e_file_cache_freeze_changes (E_FILE_CACHE (cache));
1864
1865         for (comps_in_cache = e_cal_backend_cache_get_components (cache);
1866              comps_in_cache;
1867              comps_in_cache = comps_in_cache->next) {
1868                 gchar *comp_str;
1869                 ECalComponentId *id;
1870                 ECalComponent *comp = comps_in_cache->data;
1871
1872                 id = e_cal_component_get_id (comp);
1873                 comp_str = e_cal_component_get_as_string (comp);
1874
1875                 e_cal_backend_cache_remove_component (cache, id->uid, id->rid);
1876                 e_cal_backend_notify_object_removed (backend, id, comp_str, NULL);
1877
1878                 g_free (comp_str);
1879                 e_cal_component_free_id (id);
1880                 g_object_unref (comp);
1881         }
1882
1883         g_list_free (comps_in_cache);
1884
1885         e_file_cache_thaw_changes (E_FILE_CACHE (cache));
1886 }