Update libedata-cal API docs.
[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,
463                                      const gchar *uid,
464                                      const gchar *filename,
465                                      gint fileindex)
466 {
467         g_return_val_if_fail (backend != NULL, NULL);
468         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
469
470         return e_filename_mkdir_encoded (e_cal_backend_get_cache_dir (backend), uid, filename, fileindex);
471 }
472
473 /**
474  * e_cal_backend_get_backend_property:
475  * @backend: an #ECalBackend
476  * @cal: an #EDataCal
477  * @opid: the ID to use for this operation
478  * @cancellable: a #GCancellable for the operation
479  * @prop_name: property name to get value of; cannot be NULL
480  *
481  * Calls the get_backend_property method on the given backend.
482  * This might be finished with e_data_cal_respond_get_backend_property().
483  * Default implementation takes care of common properties and returns
484  * an 'unsupported' error for any unknown properties. The subclass may
485  * always call this default implementation for properties which fetching
486  * it doesn't overwrite.
487  *
488  * Since: 3.2
489  **/
490 void
491 e_cal_backend_get_backend_property (ECalBackend *backend,
492                                     EDataCal *cal,
493                                     guint32 opid,
494                                     GCancellable *cancellable,
495                                     const gchar *prop_name)
496 {
497         g_return_if_fail (backend != NULL);
498         g_return_if_fail (E_IS_CAL_BACKEND (backend));
499         g_return_if_fail (prop_name != NULL);
500         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property != NULL);
501
502         (* E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property) (backend, cal, opid, cancellable, prop_name);
503 }
504
505 /**
506  * e_cal_backend_set_backend_property:
507  * @backend: an #ECalBackend
508  * @cal: an #EDataCal
509  * @opid: the ID to use for this operation
510  * @cancellable: a #GCancellable for the operation
511  * @prop_name: property name to change; cannot be NULL
512  * @prop_value: value to set to @prop_name; cannot be NULL
513  *
514  * Calls the set_backend_property method on the given backend.
515  * This might be finished with e_data_cal_respond_set_backend_property().
516  * Default implementation simply returns an 'unsupported' error.
517  * The subclass may always call this default implementation for properties
518  * which fetching it doesn't overwrite.
519  *
520  * Since: 3.2
521  **/
522 void
523 e_cal_backend_set_backend_property (ECalBackend *backend,
524                                     EDataCal *cal,
525                                     guint32 opid,
526                                     GCancellable *cancellable,
527                                     const gchar *prop_name,
528                                     const gchar *prop_value)
529 {
530         g_return_if_fail (backend != NULL);
531         g_return_if_fail (E_IS_CAL_BACKEND (backend));
532         g_return_if_fail (prop_name != NULL);
533         g_return_if_fail (prop_value != NULL);
534         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->set_backend_property != NULL);
535
536         (* E_CAL_BACKEND_GET_CLASS (backend)->set_backend_property) (backend, cal, opid, cancellable, prop_name, prop_value);
537 }
538
539 static void
540 cal_destroy_cb (gpointer data,
541                 GObject *where_cal_was)
542 {
543         e_cal_backend_remove_client_private (E_CAL_BACKEND (data),
544                                              (EDataCal *) where_cal_was, FALSE);
545 }
546
547 /**
548  * e_cal_backend_add_client:
549  * @backend: an #ECalBackend
550  * @cal: an #EDataCal
551  *
552  * Adds a new client to the given backend. For any event, the backend will
553  * notify all clients added via this function.
554  */
555 void
556 e_cal_backend_add_client (ECalBackend *backend,
557                           EDataCal *cal)
558 {
559         ECalBackendPrivate *priv;
560
561         g_return_if_fail (backend != NULL);
562         g_return_if_fail (E_IS_CAL_BACKEND (backend));
563         g_return_if_fail (cal != NULL);
564         g_return_if_fail (E_IS_DATA_CAL (cal));
565
566         priv = backend->priv;
567
568         g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
569
570         g_mutex_lock (priv->clients_mutex);
571         priv->clients = g_slist_append (priv->clients, cal);
572         g_mutex_unlock (priv->clients_mutex);
573 }
574
575 static void
576 e_cal_backend_remove_client_private (ECalBackend *backend,
577                                      EDataCal *cal,
578                                      gboolean weak_unref)
579 {
580         ECalBackendPrivate *priv;
581
582         /* XXX this needs a bit more thinking wrt the mutex - we
583          * should be holding it when we check to see if clients is
584          * NULL */
585         g_return_if_fail (backend != NULL);
586         g_return_if_fail (E_IS_CAL_BACKEND (backend));
587         g_return_if_fail (cal != NULL);
588         g_return_if_fail (E_IS_DATA_CAL (cal));
589
590         priv = backend->priv;
591
592         if (weak_unref)
593                 g_object_weak_unref (G_OBJECT (cal), cal_destroy_cb, backend);
594
595         /* Disconnect */
596         g_mutex_lock (priv->clients_mutex);
597         priv->clients = g_slist_remove (priv->clients, cal);
598         g_mutex_unlock (priv->clients_mutex);
599
600         /* When all clients go away, notify the parent factory about it so that
601          * it may decide whether to kill the backend or not.
602          */
603         if (!priv->clients) {
604                 priv->opening = FALSE;
605                 e_backend_last_client_gone (E_BACKEND (backend));
606         }
607 }
608
609 /**
610  * e_cal_backend_remove_client:
611  * @backend: an #ECalBackend
612  * @cal: an #EDataCal
613  *
614  * Removes a client from the list of connected clients to the given backend.
615  */
616 void
617 e_cal_backend_remove_client (ECalBackend *backend,
618                              EDataCal *cal)
619 {
620         e_cal_backend_remove_client_private (backend, cal, TRUE);
621 }
622
623 /**
624  * e_cal_backend_add_view:
625  * @backend: an #ECalBackend
626  * @view: An #EDataCalView object.
627  *
628  * Adds a view to the list of live views being run by the given backend.
629  * Doing so means that any listener on the view will get notified of any
630  * change that affect the live view.
631  *
632  * Since: 3.2
633  */
634 void
635 e_cal_backend_add_view (ECalBackend *backend,
636                         EDataCalView *view)
637 {
638         g_return_if_fail (backend != NULL);
639         g_return_if_fail (E_IS_CAL_BACKEND (backend));
640
641         g_mutex_lock (backend->priv->views_mutex);
642
643         backend->priv->views = g_slist_append (backend->priv->views, view);
644
645         g_mutex_unlock (backend->priv->views_mutex);
646 }
647
648 /**
649  * e_cal_backend_remove_view
650  * @backend: an #ECalBackend
651  * @view: An #EDataCalView object, previously added with @ref e_cal_backend_add_view.
652  *
653  * Removes view from the list of live views for the backend.
654  *
655  * Since: 3.2
656  **/
657 void
658 e_cal_backend_remove_view (ECalBackend *backend,
659                            EDataCalView *view)
660 {
661         g_return_if_fail (backend != NULL);
662         g_return_if_fail (E_IS_CAL_BACKEND (backend));
663
664         g_mutex_lock (backend->priv->views_mutex);
665
666         backend->priv->views = g_slist_remove (backend->priv->views, view);
667
668         g_mutex_unlock (backend->priv->views_mutex);
669 }
670
671 /**
672  * e_cal_backend_foreach_view:
673  * @backend: an #ECalBackend
674  * @callback: callback to call
675  * @user_data: user_data passed into the @callback
676  *
677  * Calls @callback for each known calendar view of this @backend.
678  * @callback returns %FALSE to stop further processing.
679  *
680  * Since: 3.2
681  **/
682 void
683 e_cal_backend_foreach_view (ECalBackend *backend,
684                             gboolean (*callback) (EDataCalView *view,
685                                                   gpointer user_data),
686                             gpointer user_data)
687 {
688         const GSList *views;
689         EDataCalView *view;
690         gboolean stop = FALSE;
691
692         g_return_if_fail (backend != NULL);
693         g_return_if_fail (callback != NULL);
694
695         g_mutex_lock (backend->priv->views_mutex);
696
697         for (views = backend->priv->views; views && !stop; views = views->next) {
698                 view = E_DATA_CAL_VIEW (views->data);
699
700                 g_object_ref (view);
701                 stop = !callback (view, user_data);
702                 g_object_unref (view);
703         }
704
705         g_mutex_unlock (backend->priv->views_mutex);
706 }
707
708 /**
709  * e_cal_backend_set_notification_proxy:
710  * @backend: an #ECalBackend
711  * @proxy: The calendar backend to act as notification proxy.
712  *
713  * Sets the backend that will act as notification proxy for the given backend.
714  *
715  * Since: 3.2
716  */
717 void
718 e_cal_backend_set_notification_proxy (ECalBackend *backend,
719                                       ECalBackend *proxy)
720 {
721         g_return_if_fail (E_IS_CAL_BACKEND (backend));
722
723         backend->priv->notification_proxy = proxy;
724 }
725
726 /**
727  * e_cal_backend_open:
728  * @backend: an #ECalBackend
729  * @cal: an #EDataCal
730  * @opid: the ID to use for this operation
731  * @cancellable: a #GCancellable for the operation
732  * @only_if_exists: Whether the calendar should be opened only if it already
733  * exists.  If FALSE, a new calendar will be created when the specified @uri
734  * does not exist.
735  *
736  * Opens a calendar backend with data from a calendar stored at the specified URI.
737  * This might be finished with e_data_cal_respond_open() or e_cal_backend_respond_opened(),
738  * though the overall opening phase finishes only after call
739  * of e_cal_backend_notify_opened() after which call the backend
740  * is either fully opened (including authentication against (remote)
741  * server/storage) or an error was encountered during this opening phase.
742  * 'opened' and 'opening' properties are updated automatically.
743  * The backend refuses all other operations until the opening phase is finished.
744  *
745  * The e_cal_backend_notify_opened() is called either from this function
746  * or from e_cal_backend_authenticate_user(), or after necessary steps
747  * initiated by these two functions.
748  *
749  * The opening phase usually works like this:
750  * 1) client requests open for the backend
751  * 2) server receives this request and calls e_cal_backend_open() - the opening phase begun
752  * 3) either the backend is opened during this call, and notifies client
753  *    with e_cal_backend_notify_opened() about that. This is usually
754  *    for local backends; their opening phase is finished
755  * 4) or the backend requires authentication, thus it notifies client
756  *    about that with e_cal_backend_notify_auth_required() and is
757  *    waiting for credentials, which will be received from client
758  *    by e_cal_backend_authenticate_user() call. Backend's opening
759  *    phase is still running in this case, thus it doesn't call
760  *    e_cal_backend_notify_opened() within e_cal_backend_open() call.
761  * 5) when backend receives credentials in e_cal_backend_authenticate_user()
762  *    then it tries to authenticate against a server/storage with them
763  *    and only after it knows result of the authentication, whether user
764  *    was or wasn't authenticated, it notifies client with the result
765  *    by e_cal_backend_notify_opened() and it's opening phase is
766  *    finished now. If there was no error returned then the backend is
767  *    considered opened, otherwise it's considered closed. Use AuthenticationFailed
768  *    error when the given credentials were rejected by the server/store, which
769  *    will result in a re-prompt on the client side, otherwise use AuthenticationRequired
770  *    if there was anything wrong with the given credentials. Set error's
771  *    message to a reason for a re-prompt, it'll be shown to a user.
772  * 6) client checks error returned from e_cal_backend_notify_opened() and
773  *    reprompts for a password if it was AuthenticationFailed. Otherwise
774  *    considers backend opened based on the error presence (no error means success).
775  *
776  * In any case, the call of e_cal_backend_open() should be always finished
777  * with e_data_cal_respond_open(), which has no influence on the opening phase,
778  * or alternatively with e_cal_backend_respond_opened(). Never use authentication
779  * errors in e_data_cal_respond_open() to notify the client the authentication is
780  * required, there is e_cal_backend_notify_auth_required() for this.
781  **/
782 void
783 e_cal_backend_open (ECalBackend *backend,
784                     EDataCal *cal,
785                     guint32 opid,
786                     GCancellable *cancellable,
787                     gboolean only_if_exists)
788 {
789         g_return_if_fail (backend != NULL);
790         g_return_if_fail (E_IS_CAL_BACKEND (backend));
791         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->open != NULL);
792
793         g_mutex_lock (backend->priv->clients_mutex);
794
795         if (e_cal_backend_is_opened (backend)) {
796                 gboolean online;
797
798                 g_mutex_unlock (backend->priv->clients_mutex);
799
800                 e_data_cal_report_readonly (cal, backend->priv->readonly);
801
802                 online = e_backend_get_online (E_BACKEND (backend));
803                 e_data_cal_report_online (cal, online);
804
805                 e_cal_backend_respond_opened (backend, cal, opid, NULL);
806         } else if (e_cal_backend_is_opening (backend)) {
807                 g_mutex_unlock (backend->priv->clients_mutex);
808
809                 e_data_cal_respond_open (cal, opid, EDC_OPENING_ERROR);
810         } else {
811                 backend->priv->opening = TRUE;
812                 g_mutex_unlock (backend->priv->clients_mutex);
813
814                 (* E_CAL_BACKEND_GET_CLASS (backend)->open) (backend, cal, opid, cancellable, only_if_exists);
815         }
816 }
817
818 /**
819  * e_cal_backend_authenticate_user:
820  * @backend: an #ECalBackend
821  * @cancellable: a #GCancellable for the operation
822  * @credentials: #ECredentials to use for authentication
823  *
824  * Notifies @backend about @credentials provided by user to use
825  * for authentication. This notification is usually called during
826  * opening phase as a response to e_cal_backend_notify_auth_required()
827  * on the client side and it results in setting property 'opening' to %TRUE
828  * unless the backend is already opened. This function finishes opening
829  * phase, thus it should be finished with e_cal_backend_notify_opened().
830  *
831  * See information at e_cal_backend_open() for more details
832  * how the opening phase works.
833  *
834  * Since: 3.2
835  **/
836 void
837 e_cal_backend_authenticate_user (ECalBackend *backend,
838                                  GCancellable *cancellable,
839                                  ECredentials *credentials)
840 {
841         g_return_if_fail (E_IS_CAL_BACKEND (backend));
842         g_return_if_fail (credentials != NULL);
843         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->authenticate_user);
844
845         if (!e_cal_backend_is_opened (backend))
846                 backend->priv->opening = TRUE;
847
848         (* E_CAL_BACKEND_GET_CLASS (backend)->authenticate_user) (backend, cancellable, credentials);
849 }
850
851 /**
852  * e_cal_backend_remove:
853  * @backend: an #ECalBackend
854  * @cal: an #EDataCal
855  * @opid: the ID to use for this operation
856  * @cancellable: a #GCancellable for the operation
857  *
858  * Removes the calendar being accessed by the given backend.
859  * This might be finished with e_data_cal_respond_remove().
860  **/
861 void
862 e_cal_backend_remove (ECalBackend *backend,
863                       EDataCal *cal,
864                       guint32 opid,
865                       GCancellable *cancellable)
866 {
867         g_return_if_fail (backend != NULL);
868         g_return_if_fail (E_IS_CAL_BACKEND (backend));
869         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove != NULL);
870
871         if (e_cal_backend_is_opening (backend))
872                 e_data_cal_respond_remove (cal, opid, EDC_OPENING_ERROR);
873         else
874                 (* E_CAL_BACKEND_GET_CLASS (backend)->remove) (backend, cal, opid, cancellable);
875 }
876
877 /**
878  * e_cal_backend_refresh:
879  * @backend: an #ECalBackend
880  * @cal: an #EDataCal
881  * @opid: the ID to use for this operation
882  * @cancellable: a #GCancellable for the operation
883  *
884  * Refreshes the calendar being accessed by the given backend.
885  * This might be finished with e_data_cal_respond_refresh(),
886  * and it might be called as soon as possible; it doesn't mean
887  * that the refreshing is done after calling that, the backend
888  * is only notifying client whether it started the refresh process
889  * or not.
890  *
891  * Since: 2.30
892  **/
893 void
894 e_cal_backend_refresh (ECalBackend *backend,
895                        EDataCal *cal,
896                        guint32 opid,
897                        GCancellable *cancellable)
898 {
899         g_return_if_fail (backend != NULL);
900         g_return_if_fail (E_IS_CAL_BACKEND (backend));
901
902         if (e_cal_backend_is_opening (backend))
903                 e_data_cal_respond_refresh (cal, opid, EDC_OPENING_ERROR);
904         else if (!E_CAL_BACKEND_GET_CLASS (backend)->refresh)
905                 e_data_cal_respond_refresh (cal, opid, EDC_ERROR (UnsupportedMethod));
906         else if (!e_cal_backend_is_opened (backend))
907                 e_data_cal_respond_refresh (cal, opid, EDC_NOT_OPENED_ERROR);
908         else
909                 (* E_CAL_BACKEND_GET_CLASS (backend)->refresh) (backend, cal, opid, cancellable);
910 }
911
912 /**
913  * e_cal_backend_get_object:
914  * @backend: an #ECalBackend
915  * @cal: an #EDataCal
916  * @opid: the ID to use for this operation
917  * @cancellable: a #GCancellable for the operation
918  * @uid: Unique identifier for a calendar object.
919  * @rid: ID for the object's recurrence to get.
920  *
921  * Queries a calendar backend for a calendar object based on its unique
922  * identifier and its recurrence ID (if a recurrent appointment).
923  * This might be finished with e_data_cal_respond_get_object().
924  **/
925 void
926 e_cal_backend_get_object (ECalBackend *backend,
927                           EDataCal *cal,
928                           guint32 opid,
929                           GCancellable *cancellable,
930                           const gchar *uid,
931                           const gchar *rid)
932 {
933         g_return_if_fail (backend != NULL);
934         g_return_if_fail (E_IS_CAL_BACKEND (backend));
935         g_return_if_fail (uid != NULL);
936         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object != NULL);
937
938         if (e_cal_backend_is_opening (backend))
939                 e_data_cal_respond_get_object (cal, opid, EDC_OPENING_ERROR, NULL);
940         else if (!e_cal_backend_is_opened (backend))
941                 e_data_cal_respond_get_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
942         else
943                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_object) (backend, cal, opid, cancellable, uid, rid);
944 }
945
946 /**
947  * e_cal_backend_get_object_list:
948  * @backend: an #ECalBackend
949  * @cal: an #EDataCal
950  * @opid: the ID to use for this operation
951  * @cancellable: a #GCancellable for the operation
952  * @sexp: Expression to search for.
953  *
954  * Calls the get_object_list method on the given backend.
955  * This might be finished with e_data_cal_respond_get_object_list().
956  **/
957 void
958 e_cal_backend_get_object_list (ECalBackend *backend,
959                                EDataCal *cal,
960                                guint32 opid,
961                                GCancellable *cancellable,
962                                const gchar *sexp)
963 {
964         g_return_if_fail (backend != NULL);
965         g_return_if_fail (E_IS_CAL_BACKEND (backend));
966         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object_list != NULL);
967
968         if (e_cal_backend_is_opening (backend))
969                 e_data_cal_respond_get_object_list (cal, opid, EDC_OPENING_ERROR, NULL);
970         else if (!e_cal_backend_is_opened (backend))
971                 e_data_cal_respond_get_object_list (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
972         else
973                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_object_list) (backend, cal, opid, cancellable, sexp);
974 }
975
976 /**
977  * e_cal_backend_get_free_busy:
978  * @backend: an #ECalBackend
979  * @cal: an #EDataCal
980  * @opid: the ID to use for this operation
981  * @cancellable: a #GCancellable for the operation
982  * @users: List of users to get free/busy information for.
983  * @start: Start time for query.
984  * @end: End time for query.
985  *
986  * Gets a free/busy object for the given time interval. Client side is
987  * notified about free/busy objects throug e_data_cal_report_free_busy_data().
988  * This might be finished with e_data_cal_respond_get_free_busy().
989  **/
990 void
991 e_cal_backend_get_free_busy (ECalBackend *backend,
992                              EDataCal *cal,
993                              guint32 opid,
994                              GCancellable *cancellable,
995                              const GSList *users,
996                              time_t start,
997                              time_t end)
998 {
999         g_return_if_fail (backend != NULL);
1000         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1001         g_return_if_fail (start != -1 && end != -1);
1002         g_return_if_fail (start <= end);
1003         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy != NULL);
1004
1005         if (e_cal_backend_is_opening (backend))
1006                 e_data_cal_respond_get_free_busy (cal, opid, EDC_OPENING_ERROR);
1007         else if (!e_cal_backend_is_opened (backend))
1008                 e_data_cal_respond_get_free_busy (cal, opid, EDC_NOT_OPENED_ERROR);
1009         else
1010                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy) (backend, cal, opid, cancellable, users, start, end);
1011 }
1012
1013 /**
1014  * e_cal_backend_create_object:
1015  * @backend: an #ECalBackend
1016  * @cal: an #EDataCal
1017  * @opid: the ID to use for this operation
1018  * @cancellable: a #GCancellable for the operation
1019  * @calobj: The object to create.
1020  *
1021  * Calls the create_object method on the given backend.
1022  * This might be finished with e_data_cal_respond_create_object().
1023  **/
1024 void
1025 e_cal_backend_create_object (ECalBackend *backend,
1026                              EDataCal *cal,
1027                              guint32 opid,
1028                              GCancellable *cancellable,
1029                              const gchar *calobj)
1030 {
1031         g_return_if_fail (backend != NULL);
1032         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1033         g_return_if_fail (calobj != NULL);
1034
1035         if (e_cal_backend_is_opening (backend))
1036                 e_data_cal_respond_create_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1037         else if (!E_CAL_BACKEND_GET_CLASS (backend)->create_object)
1038                 e_data_cal_respond_create_object (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
1039         else if (!e_cal_backend_is_opened (backend))
1040                 e_data_cal_respond_create_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1041         else
1042                 (* E_CAL_BACKEND_GET_CLASS (backend)->create_object) (backend, cal, opid, cancellable, calobj);
1043 }
1044
1045 /**
1046  * e_cal_backend_modify_object:
1047  * @backend: an #ECalBackend
1048  * @cal: an #EDataCal
1049  * @opid: the ID to use for this operation
1050  * @cancellable: a #GCancellable for the operation
1051  * @calobj: Object to be modified.
1052  * @mod: Type of modification.
1053  *
1054  * Calls the modify_object method on the given backend.
1055  * This might be finished with e_data_cal_respond_modify_object().
1056  **/
1057 void
1058 e_cal_backend_modify_object (ECalBackend *backend,
1059                              EDataCal *cal,
1060                              guint32 opid,
1061                              GCancellable *cancellable,
1062                              const gchar *calobj,
1063                              CalObjModType mod)
1064 {
1065         g_return_if_fail (backend != NULL);
1066         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1067         g_return_if_fail (calobj != NULL);
1068
1069         if (e_cal_backend_is_opening (backend))
1070                 e_data_cal_respond_modify_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1071         else if (!E_CAL_BACKEND_GET_CLASS (backend)->modify_object)
1072                 e_data_cal_respond_modify_object (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
1073         else if (!e_cal_backend_is_opened (backend))
1074                 e_data_cal_respond_modify_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1075         else
1076                 (* E_CAL_BACKEND_GET_CLASS (backend)->modify_object) (backend, cal, opid, cancellable, calobj, mod);
1077 }
1078
1079 /**
1080  * e_cal_backend_remove_object:
1081  * @backend: an #ECalBackend
1082  * @cal: an #EDataCal
1083  * @opid: the ID to use for this operation
1084  * @cancellable: a #GCancellable for the operation
1085  * @uid: Unique identifier of the object to remove.
1086  * @rid: A recurrence ID.
1087  * @mod: Type of removal.
1088  *
1089  * Removes an object in a calendar backend.  The backend will notify all of its
1090  * clients about the change.
1091  * This might be finished with e_data_cal_respond_remove_object().
1092  **/
1093 void
1094 e_cal_backend_remove_object (ECalBackend *backend,
1095                              EDataCal *cal,
1096                              guint32 opid,
1097                              GCancellable *cancellable,
1098                              const gchar *uid,
1099                              const gchar *rid,
1100                              CalObjModType mod)
1101 {
1102         g_return_if_fail (backend != NULL);
1103         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1104         g_return_if_fail (uid != NULL);
1105         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove_object != NULL);
1106
1107         if (e_cal_backend_is_opening (backend))
1108                 e_data_cal_respond_remove_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL, NULL);
1109         else if (!e_cal_backend_is_opened (backend))
1110                 e_data_cal_respond_remove_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL, NULL);
1111         else
1112                 (* E_CAL_BACKEND_GET_CLASS (backend)->remove_object) (backend, cal, opid, cancellable, uid, rid, mod);
1113 }
1114
1115 /**
1116  * e_cal_backend_receive_objects:
1117  * @backend: an #ECalBackend
1118  * @cal: an #EDataCal
1119  * @opid: the ID to use for this operation
1120  * @cancellable: a #GCancellable for the operation
1121  * @calobj: iCalendar object.
1122  *
1123  * Calls the receive_objects method on the given backend.
1124  * This might be finished with e_data_cal_respond_receive_objects().
1125  **/
1126 void
1127 e_cal_backend_receive_objects (ECalBackend *backend,
1128                                EDataCal *cal,
1129                                guint32 opid,
1130                                GCancellable *cancellable,
1131                                const gchar *calobj)
1132 {
1133         g_return_if_fail (backend != NULL);
1134         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1135         g_return_if_fail (calobj != NULL);
1136         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->receive_objects != NULL);
1137
1138         if (e_cal_backend_is_opening (backend))
1139                 e_data_cal_respond_receive_objects (cal, opid, EDC_OPENING_ERROR);
1140         else if (!e_cal_backend_is_opened (backend))
1141                 e_data_cal_respond_receive_objects (cal, opid, EDC_NOT_OPENED_ERROR);
1142         else
1143                 (* E_CAL_BACKEND_GET_CLASS (backend)->receive_objects) (backend, cal, opid, cancellable, calobj);
1144 }
1145
1146 /**
1147  * e_cal_backend_send_objects:
1148  * @backend: an #ECalBackend
1149  * @cal: an #EDataCal
1150  * @opid: the ID to use for this operation
1151  * @cancellable: a #GCancellable for the operation
1152  * @calobj: iCalendar object to be sent.
1153  *
1154  * Calls the send_objects method on the given backend.
1155  * This might be finished with e_data_cal_respond_send_objects().
1156  **/
1157 void
1158 e_cal_backend_send_objects (ECalBackend *backend,
1159                             EDataCal *cal,
1160                             guint32 opid,
1161                             GCancellable *cancellable,
1162                             const gchar *calobj)
1163 {
1164         g_return_if_fail (backend != NULL);
1165         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1166         g_return_if_fail (calobj != NULL);
1167         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->send_objects != NULL);
1168
1169         if (e_cal_backend_is_opening (backend))
1170                 e_data_cal_respond_send_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1171         else if (!e_cal_backend_is_opened (backend))
1172                 e_data_cal_respond_send_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1173         else
1174                 (* E_CAL_BACKEND_GET_CLASS (backend)->send_objects) (backend, cal, opid, cancellable, calobj);
1175 }
1176
1177 /**
1178  * e_cal_backend_get_attachment_uris:
1179  * @backend: an #ECalBackend
1180  * @cal: an #EDataCal
1181  * @opid: the ID to use for this operation
1182  * @cancellable: a #GCancellable for the operation
1183  * @uid: Unique identifier for a calendar object.
1184  * @rid: ID for the object's recurrence to get.
1185  *
1186  * Queries a calendar backend for attachments present in a calendar object based
1187  * on its unique identifier and its recurrence ID (if a recurrent appointment).
1188  * This might be finished with e_data_cal_respond_get_attachment_uris().
1189  *
1190  * Since: 3.2
1191  **/
1192 void
1193 e_cal_backend_get_attachment_uris (ECalBackend *backend,
1194                                    EDataCal *cal,
1195                                    guint32 opid,
1196                                    GCancellable *cancellable,
1197                                    const gchar *uid,
1198                                    const gchar *rid)
1199 {
1200         g_return_if_fail (backend != NULL);
1201         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1202         g_return_if_fail (uid != NULL);
1203         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris != NULL);
1204
1205         if (e_cal_backend_is_opening (backend))
1206                 e_data_cal_respond_get_attachment_uris (cal, opid, EDC_OPENING_ERROR, NULL);
1207         else if (!e_cal_backend_is_opened (backend))
1208                 e_data_cal_respond_get_attachment_uris (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
1209         else
1210                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris) (backend, cal, opid, cancellable, uid, rid);
1211 }
1212
1213 /**
1214  * e_cal_backend_discard_alarm:
1215  * @backend: an #ECalBackend
1216  * @cal: an #EDataCal
1217  * @opid: the ID to use for this operation
1218  * @cancellable: a #GCancellable for the operation
1219  * @uid: Unique identifier for a calendar object.
1220  * @rid: ID for the object's recurrence to discard alarm in.
1221  * @auid: Unique identifier of the alarm itself.
1222  *
1223  * Discards alarm @auid from the object identified by @uid and @rid.
1224  * This might be finished with e_data_cal_respond_discard_alarm().
1225  * Default implementation of this method returns Not Supported error.
1226  **/
1227 void
1228 e_cal_backend_discard_alarm (ECalBackend *backend,
1229                              EDataCal *cal,
1230                              guint32 opid,
1231                              GCancellable *cancellable,
1232                              const gchar *uid,
1233                              const gchar *rid,
1234                              const gchar *auid)
1235 {
1236         g_return_if_fail (backend != NULL);
1237         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1238         g_return_if_fail (uid != NULL);
1239         g_return_if_fail (auid != NULL);
1240
1241         if (e_cal_backend_is_opening (backend))
1242                 e_data_cal_respond_discard_alarm (cal, opid, EDC_OPENING_ERROR);
1243         else if (!E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm)
1244                 e_data_cal_respond_discard_alarm (cal, opid, e_data_cal_create_error (NotSupported, NULL));
1245         else if (!e_cal_backend_is_opened (backend))
1246                 e_data_cal_respond_discard_alarm (cal, opid, EDC_NOT_OPENED_ERROR);
1247         else
1248                 (* E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm) (backend, cal, opid, cancellable, uid, rid, auid);
1249 }
1250
1251 /**
1252  * e_cal_backend_get_timezone:
1253  * @backend: an #ECalBackend
1254  * @cal: an #EDataCal
1255  * @opid: the ID to use for this operation
1256  * @cancellable: a #GCancellable for the operation
1257  * @tzid: Unique identifier of a VTIMEZONE object. Note that this must not be
1258  * NULL.
1259  *
1260  * Returns the icaltimezone* corresponding to the TZID, or NULL if the TZID
1261  * can't be found.
1262  * This might be finished with e_data_cal_respond_get_timezone().
1263  **/
1264 void
1265 e_cal_backend_get_timezone (ECalBackend *backend,
1266                             EDataCal *cal,
1267                             guint32 opid,
1268                             GCancellable *cancellable,
1269                             const gchar *tzid)
1270 {
1271         g_return_if_fail (backend != NULL);
1272         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1273         g_return_if_fail (tzid != NULL);
1274         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_timezone != NULL);
1275
1276         if (e_cal_backend_is_opening (backend))
1277                 e_data_cal_respond_get_timezone (cal, opid, EDC_OPENING_ERROR, NULL);
1278         else if (!e_cal_backend_is_opened (backend))
1279                 e_data_cal_respond_get_timezone (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
1280         else
1281                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_timezone) (backend, cal, opid, cancellable, tzid);
1282 }
1283
1284 /**
1285  * e_cal_backend_add_timezone
1286  * @backend: an #ECalBackend
1287  * @cal: an #EDataCal
1288  * @opid: the ID to use for this operation
1289  * @cancellable: a #GCancellable for the operation
1290  * @tzobj: The timezone object, in a string.
1291  *
1292  * Add a timezone object to the given backend.
1293  * This might be finished with e_data_cal_respond_add_timezone().
1294  **/
1295 void
1296 e_cal_backend_add_timezone (ECalBackend *backend,
1297                             EDataCal *cal,
1298                             guint32 opid,
1299                             GCancellable *cancellable,
1300                             const gchar *tzobject)
1301 {
1302         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1303         g_return_if_fail (tzobject != NULL);
1304         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->add_timezone != NULL);
1305
1306         if (e_cal_backend_is_opening (backend))
1307                 e_data_cal_respond_add_timezone (cal, opid, EDC_OPENING_ERROR);
1308         else if (!e_cal_backend_is_opened (backend))
1309                 e_data_cal_respond_add_timezone (cal, opid, EDC_NOT_OPENED_ERROR);
1310         else
1311                 (* E_CAL_BACKEND_GET_CLASS (backend)->add_timezone) (backend, cal, opid, cancellable, tzobject);
1312 }
1313
1314 /**
1315  * e_cal_backend_internal_get_timezone:
1316  * @backend: an #ECalBackend
1317  * @tzid: ID of the timezone to get.
1318  *
1319  * Calls the internal_get_timezone method on the given backend.
1320  */
1321 icaltimezone *
1322 e_cal_backend_internal_get_timezone (ECalBackend *backend,
1323                                      const gchar *tzid)
1324 {
1325         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1326         g_return_val_if_fail (tzid != NULL, NULL);
1327         g_return_val_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->internal_get_timezone != NULL, NULL);
1328
1329         return (* E_CAL_BACKEND_GET_CLASS (backend)->internal_get_timezone) (backend, tzid);
1330 }
1331
1332 /**
1333  * e_cal_backend_start_view:
1334  * @backend: an #ECalBackend
1335  * @view: The view to be started.
1336  *
1337  * Starts a new live view on the given backend.
1338  *
1339  * Since: 3.2
1340  */
1341 void
1342 e_cal_backend_start_view (ECalBackend *backend,
1343                           EDataCalView *view)
1344 {
1345         g_return_if_fail (backend != NULL);
1346         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1347         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->start_view != NULL);
1348
1349         (* E_CAL_BACKEND_GET_CLASS (backend)->start_view) (backend, view);
1350 }
1351
1352 /**
1353  * e_cal_backend_stop_view:
1354  * @backend: an #ECalBackend
1355  * @view: The view to be stopped.
1356  *
1357  * Stops a previously started live view on the given backend.
1358  *
1359  * Since: 3.2
1360  */
1361 void
1362 e_cal_backend_stop_view (ECalBackend *backend,
1363                          EDataCalView *view)
1364 {
1365         g_return_if_fail (backend != NULL);
1366         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1367
1368         /* backward compatibility, do not force each backend define this function */
1369         if (!E_CAL_BACKEND_GET_CLASS (backend)->stop_view)
1370                 return;
1371
1372         (* E_CAL_BACKEND_GET_CLASS (backend)->stop_view) (backend, view);
1373 }
1374
1375 static gboolean
1376 object_created_cb (EDataCalView *view,
1377                    gpointer calobj)
1378 {
1379         if (e_data_cal_view_object_matches (view, calobj))
1380                 e_data_cal_view_notify_objects_added_1 (view, calobj);
1381
1382         return TRUE;
1383 }
1384
1385 /**
1386  * e_cal_backend_notify_object_created:
1387  * @backend: an #ECalBackend
1388  * @calobj: iCalendar representation of new object
1389  *
1390  * Notifies each of the backend's listeners about a new object.
1391  *
1392  * #e_data_cal_notify_object_created() calls this for you. You only need to
1393  * call e_cal_backend_notify_object_created() yourself to report objects
1394  * created by non-EDS clients.
1395  **/
1396 void
1397 e_cal_backend_notify_object_created (ECalBackend *backend,
1398                                      const gchar *calobj)
1399 {
1400         ECalBackendPrivate *priv;
1401
1402         priv = backend->priv;
1403
1404         if (priv->notification_proxy) {
1405                 e_cal_backend_notify_object_created (priv->notification_proxy, calobj);
1406                 return;
1407         }
1408
1409         e_cal_backend_foreach_view (backend, object_created_cb, (gpointer) calobj);
1410 }
1411
1412 /**
1413  * e_cal_backend_notify_objects_added:
1414  *
1415  * Since: 2.24
1416  **/
1417 void
1418 e_cal_backend_notify_objects_added (ECalBackend *backend,
1419                                     EDataCalView *view,
1420                                     const GSList *objects)
1421 {
1422         e_data_cal_view_notify_objects_added (view, objects);
1423 }
1424
1425 static void
1426 match_view_and_notify (EDataCalView *view,
1427                        const gchar *old_object,
1428                        const gchar *object)
1429 {
1430         gboolean old_match = FALSE, new_match = FALSE;
1431
1432         if (old_object)
1433                 old_match = e_data_cal_view_object_matches (view, old_object);
1434
1435         new_match = e_data_cal_view_object_matches (view, object);
1436         if (old_match && new_match)
1437                 e_data_cal_view_notify_objects_modified_1 (view, object);
1438         else if (new_match)
1439                 e_data_cal_view_notify_objects_added_1 (view, object);
1440         else if (old_match) {
1441                 ECalComponent *comp = NULL;
1442
1443                 comp = e_cal_component_new_from_string (old_object);
1444                 if (comp) {
1445                         ECalComponentId *id = e_cal_component_get_id (comp);
1446
1447                         e_data_cal_view_notify_objects_removed_1 (view, id);
1448
1449                         e_cal_component_free_id (id);
1450                         g_object_unref (comp);
1451                 }
1452         }
1453 }
1454
1455 struct call_data {
1456         const gchar *old_object;
1457         const gchar *object;
1458         const ECalComponentId *id;
1459 };
1460
1461 static gboolean
1462 call_match_and_notify (EDataCalView *view,
1463                        gpointer user_data)
1464 {
1465         struct call_data *cd = user_data;
1466
1467         g_return_val_if_fail (user_data != NULL, FALSE);
1468
1469         match_view_and_notify (view, cd->old_object, cd->object);
1470
1471         return TRUE;
1472 }
1473
1474 /**
1475  * e_cal_backend_notify_object_modified:
1476  * @backend: an #ECalBackend
1477  * @old_object: iCalendar representation of the original form of the object
1478  * @object: iCalendar representation of the new form of the object
1479  *
1480  * Notifies each of the backend's listeners about a modified object.
1481  *
1482  * #e_data_cal_notify_object_modified() calls this for you. You only need to
1483  * call e_cal_backend_notify_object_modified() yourself to report objects
1484  * modified by non-EDS clients.
1485  **/
1486 void
1487 e_cal_backend_notify_object_modified (ECalBackend *backend,
1488                                       const gchar *old_object,
1489                                       const gchar *object)
1490 {
1491         ECalBackendPrivate *priv;
1492         struct call_data cd;
1493
1494         priv = backend->priv;
1495
1496         if (priv->notification_proxy) {
1497                 e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object);
1498                 return;
1499         }
1500
1501         cd.old_object = old_object;
1502         cd.object = object;
1503         cd.id = NULL;
1504
1505         e_cal_backend_foreach_view (backend, call_match_and_notify, &cd);
1506 }
1507
1508 /**
1509  * e_cal_backend_notify_objects_modified:
1510  *
1511  * Since: 2.24
1512  **/
1513 void
1514 e_cal_backend_notify_objects_modified (ECalBackend *backend,
1515                                        EDataCalView *view,
1516                                        const GSList *objects)
1517 {
1518         e_data_cal_view_notify_objects_modified (view, objects);
1519 }
1520
1521 static gboolean
1522 object_removed_cb (EDataCalView *view,
1523                    gpointer user_data)
1524 {
1525         struct call_data *cd = user_data;
1526
1527         g_return_val_if_fail (user_data != NULL, FALSE);
1528
1529         if (cd->object == NULL) {
1530                 /* if object == NULL, it means the object has been completely
1531                  * removed from the backend */
1532                 if (!cd->old_object || e_data_cal_view_object_matches (view, cd->old_object))
1533                         e_data_cal_view_notify_objects_removed_1 (view, cd->id);
1534         } else
1535                 match_view_and_notify (view, cd->old_object, cd->object);
1536
1537         return TRUE;
1538 }
1539
1540 /**
1541  * e_cal_backend_notify_object_removed:
1542  * @backend: an #ECalBackend
1543  * @id: the Id of the removed object
1544  * @old_object: iCalendar representation of the removed object
1545  * @new_object: iCalendar representation of the object after the removal. This
1546  * only applies to recurrent appointments that had an instance removed. In that
1547  * case, this function notifies a modification instead of a removal.
1548  *
1549  * Notifies each of the backend's listeners about a removed object.
1550  *
1551  * e_data_cal_notify_object_removed() calls this for you. You only need to
1552  * call e_cal_backend_notify_object_removed() yourself to report objects
1553  * removed by non-EDS clients.
1554  **/
1555 void
1556 e_cal_backend_notify_object_removed (ECalBackend *backend,
1557                                      const ECalComponentId *id,
1558                                      const gchar *old_object,
1559                                      const gchar *object)
1560 {
1561         ECalBackendPrivate *priv;
1562         struct call_data cd;
1563
1564         priv = backend->priv;
1565
1566         if (priv->notification_proxy) {
1567                 e_cal_backend_notify_object_removed (priv->notification_proxy, id, old_object, object);
1568                 return;
1569         }
1570
1571         cd.old_object = old_object;
1572         cd.object = object;
1573         cd.id = id;
1574
1575         e_cal_backend_foreach_view (backend, object_removed_cb, &cd);
1576 }
1577
1578 /**
1579  * e_cal_backend_notify_objects_removed:
1580  *
1581  * Since: 2.24
1582  **/
1583 void
1584 e_cal_backend_notify_objects_removed (ECalBackend *backend,
1585                                       EDataCalView *view,
1586                                       const GSList *ids)
1587 {
1588         e_data_cal_view_notify_objects_removed (view, ids);
1589 }
1590
1591 /**
1592  * e_cal_backend_notify_error:
1593  * @backend: an #ECalBackend
1594  * @message: Error message
1595  *
1596  * Notifies each of the backend's listeners about an error
1597  **/
1598 void
1599 e_cal_backend_notify_error (ECalBackend *backend,
1600                             const gchar *message)
1601 {
1602         ECalBackendPrivate *priv = backend->priv;
1603         GSList *l;
1604
1605         if (priv->notification_proxy) {
1606                 e_cal_backend_notify_error (priv->notification_proxy, message);
1607                 return;
1608         }
1609
1610         g_mutex_lock (priv->clients_mutex);
1611
1612         for (l = priv->clients; l; l = l->next)
1613                 e_data_cal_report_error (l->data, message);
1614
1615         g_mutex_unlock (priv->clients_mutex);
1616 }
1617
1618 /**
1619  * e_cal_backend_notify_readonly:
1620  * @backend: an #ECalBackend
1621  * @is_readonly: flag indicating readonly status
1622  *
1623  * Notifies all backend's clients about the current readonly state.
1624  * Meant to be used by backend implementations.
1625  **/
1626 void
1627 e_cal_backend_notify_readonly (ECalBackend *backend,
1628                                gboolean is_readonly)
1629 {
1630         ECalBackendPrivate *priv;
1631         GSList *l;
1632
1633         priv = backend->priv;
1634         priv->readonly = is_readonly;
1635
1636         if (priv->notification_proxy) {
1637                 e_cal_backend_notify_readonly (priv->notification_proxy, is_readonly);
1638                 return;
1639         }
1640
1641         g_mutex_lock (priv->clients_mutex);
1642
1643         for (l = priv->clients; l; l = l->next)
1644                 e_data_cal_report_readonly (l->data, is_readonly);
1645
1646         g_mutex_unlock (priv->clients_mutex);
1647 }
1648
1649 /**
1650  * e_cal_backend_notify_online:
1651  * @backend: an #ECalBackend
1652  * @is_online: flag indicating whether @backend is connected and online
1653  *
1654  * Notifies clients of @backend's connection status indicated by @is_online.
1655  * Meant to be used by backend implementations.
1656  *
1657  * Since: 3.2
1658  **/
1659 void
1660 e_cal_backend_notify_online (ECalBackend *backend,
1661                              gboolean is_online)
1662 {
1663         ECalBackendPrivate *priv;
1664         GSList *clients;
1665
1666         priv = backend->priv;
1667
1668         if (priv->notification_proxy) {
1669                 e_cal_backend_notify_online (priv->notification_proxy, is_online);
1670                 return;
1671         }
1672
1673         g_mutex_lock (priv->clients_mutex);
1674
1675         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1676                 e_data_cal_report_online (E_DATA_CAL (clients->data), is_online);
1677
1678         g_mutex_unlock (priv->clients_mutex);
1679 }
1680
1681 /**
1682  * e_cal_backend_notify_auth_required:
1683  * @backend: an #ECalBackend
1684  * @is_self: Use %TRUE to indicate the authentication is required
1685  *    for the @backend, otheriwse the authentication is for any
1686  *    other source. Having @credentials %NULL means @is_self
1687  *    automatically.
1688  * @credentials: an #ECredentials that contains extra information for
1689  *    a source for which authentication is requested.
1690  *    This parameter can be NULL to indicate "for this calendar".
1691  *
1692  * Notifies clients that @backend requires authentication in order to
1693  * connect. This function call does not influence 'opening', but 
1694  * influences 'opened' property, which is set to %FALSE when @is_self
1695  * is %TRUE or @credentials is %NULL. Opening phase is finished
1696  * by e_cal_backend_notify_opened() if this is requested for @backend.
1697  *
1698  * See e_cal_backend_open() for a description how the whole opening
1699  * phase works.
1700  *
1701  * Meant to be used by backend implementations.
1702  **/
1703 void
1704 e_cal_backend_notify_auth_required (ECalBackend *backend,
1705                                     gboolean is_self,
1706                                     const ECredentials *credentials)
1707 {
1708         ECalBackendPrivate *priv;
1709         GSList *clients;
1710
1711         priv = backend->priv;
1712
1713         if (priv->notification_proxy) {
1714                 e_cal_backend_notify_auth_required (priv->notification_proxy, is_self, credentials);
1715                 return;
1716         }
1717
1718         g_mutex_lock (priv->clients_mutex);
1719
1720         if (is_self || !credentials)
1721                 priv->opened = FALSE;
1722
1723         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1724                 e_data_cal_report_auth_required (E_DATA_CAL (clients->data), credentials);
1725
1726         g_mutex_unlock (priv->clients_mutex);
1727 }
1728
1729 /**
1730  * e_cal_backend_notify_opened:
1731  * @backend: an #ECalBackend
1732  * @error: a #GError corresponding to the error encountered during
1733  *    the opening phase. Use %NULL for success. The @error is freed
1734  *    automatically if not %NULL.
1735  *
1736  * Notifies clients that @backend finished its opening phase.
1737  * See e_cal_backend_open() for more information how the opening
1738  * phase works. Calling this function changes 'opening' property,
1739  * same as 'opened'. 'opening' is set to %FALSE and the backend
1740  * is considered 'opened' only if the @error is %NULL.
1741  *
1742  * See also: e_cal_backend_respond_opened()
1743  *
1744  * Note: The @error is freed automatically if not %NULL.
1745  *
1746  * Meant to be used by backend implementations.
1747  *
1748  * Since: 3.2
1749  **/
1750 void
1751 e_cal_backend_notify_opened (ECalBackend *backend,
1752                              GError *error)
1753 {
1754         ECalBackendPrivate *priv;
1755         GSList *clients;
1756
1757         priv = backend->priv;
1758         g_mutex_lock (priv->clients_mutex);
1759
1760         priv->opening = FALSE;
1761         priv->opened = error == NULL;
1762
1763         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1764                 e_data_cal_report_opened (E_DATA_CAL (clients->data), error);
1765
1766         g_mutex_unlock (priv->clients_mutex);
1767
1768         if (error)
1769                 g_error_free (error);
1770 }
1771
1772 /**
1773  * e_cal_backend_notify_property_changed:
1774  * @backend: an #ECalBackend
1775  * @prop_name: property name, which changed
1776  * @prop_value: new property value
1777  *
1778  * Notifies client about property value change.
1779  *
1780  * Since: 3.2
1781  **/
1782 void
1783 e_cal_backend_notify_property_changed (ECalBackend *backend,
1784                                        const gchar *prop_name,
1785                                        const gchar *prop_value)
1786 {
1787         ECalBackendPrivate *priv;
1788         GSList *clients;
1789
1790         g_return_if_fail (backend != NULL);
1791         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1792         g_return_if_fail (backend->priv != NULL);
1793         g_return_if_fail (prop_name != NULL);
1794         g_return_if_fail (*prop_name != '\0');
1795         g_return_if_fail (prop_value != NULL);
1796
1797         priv = backend->priv;
1798         g_mutex_lock (priv->clients_mutex);
1799
1800         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1801                 e_data_cal_report_backend_property_changed (E_DATA_CAL (clients->data), prop_name, prop_value);
1802
1803         g_mutex_unlock (priv->clients_mutex);
1804 }
1805
1806 /**
1807  * e_cal_backend_respond_opened:
1808  * @backend: an #ECalBackend
1809  * @cal: an #EDataCal
1810  * @opid: an operation ID
1811  * @error: result error; can be %NULL, if it isn't then it's automatically freed
1812  *
1813  * This is a replacement for e_data_cal_respond_open() for cases where
1814  * the finish of 'open' method call also finishes backend opening phase.
1815  * This function covers calling of both e_cal_backend_notify_opened() and
1816  * e_data_cal_respond_open() with the same @error.
1817  *
1818  * See e_cal_backend_open() for more details how the opening phase works.
1819  *
1820  * Since: 3.2
1821  **/
1822 void
1823 e_cal_backend_respond_opened (ECalBackend *backend,
1824                               EDataCal *cal,
1825                               guint32 opid,
1826                               GError *error)
1827 {
1828         GError *copy = NULL;
1829
1830         g_return_if_fail (backend != NULL);
1831         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1832         g_return_if_fail (cal != NULL);
1833         g_return_if_fail (opid != 0);
1834
1835         if (error)
1836                 copy = g_error_copy (error);
1837
1838         e_cal_backend_notify_opened (backend, copy);
1839         e_data_cal_respond_open (cal, opid, error);
1840 }
1841
1842 /**
1843  * e_cal_backend_empty_cache:
1844  * @backend: an #ECalBackend
1845  * @cache: Backend's cache to empty.
1846  *
1847  * Empties backend's cache with all notifications and so on, thus all listening
1848  * will know there is nothing in this backend.
1849  *
1850  * Since: 2.28
1851  **/
1852 void
1853 e_cal_backend_empty_cache (ECalBackend *backend,
1854                            ECalBackendCache *cache)
1855 {
1856         GList *comps_in_cache;
1857
1858         g_return_if_fail (backend != NULL);
1859         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1860
1861         if (!cache)
1862                 return;
1863
1864         g_return_if_fail (E_IS_CAL_BACKEND_CACHE (cache));
1865
1866         e_file_cache_freeze_changes (E_FILE_CACHE (cache));
1867
1868         for (comps_in_cache = e_cal_backend_cache_get_components (cache);
1869              comps_in_cache;
1870              comps_in_cache = comps_in_cache->next) {
1871                 gchar *comp_str;
1872                 ECalComponentId *id;
1873                 ECalComponent *comp = comps_in_cache->data;
1874
1875                 id = e_cal_component_get_id (comp);
1876                 comp_str = e_cal_component_get_as_string (comp);
1877
1878                 e_cal_backend_cache_remove_component (cache, id->uid, id->rid);
1879                 e_cal_backend_notify_object_removed (backend, id, comp_str, NULL);
1880
1881                 g_free (comp_str);
1882                 e_cal_component_free_id (id);
1883                 g_object_unref (comp);
1884         }
1885
1886         g_list_free (comps_in_cache);
1887
1888         e_file_cache_thaw_changes (E_FILE_CACHE (cache));
1889 }