Bug #664572 - Change e_cal_backend_notify_component* to use ECalComponent
[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  * @tzobject: 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 component_created_cb (EDataCalView *view,
1377                       gpointer data)
1378 {
1379         ECalComponent *comp = data;
1380
1381         if (e_data_cal_view_component_matches (view, comp))
1382                 e_data_cal_view_notify_components_added_1 (view, comp);
1383
1384         return TRUE;
1385 }
1386
1387 /**
1388  * e_cal_backend_notify_component_created:
1389  * @backend: an #ECalBackend
1390  * @component: the newly created #ECalComponent
1391  *
1392  * Notifies each of the backend's listeners about a new object.
1393  *
1394  * Like e_cal_backend_notify_object_created() except takes an #ECalComponent
1395  * instead of an ical string representation and uses the #EDataCalView's
1396  * fields-of-interest to filter out unwanted information from ical strings
1397  * sent over the bus.
1398  *
1399  * Since: 3.4
1400  **/
1401 void
1402 e_cal_backend_notify_component_created (ECalBackend *backend,
1403                                         /* const */ ECalComponent *component)
1404 {
1405         ECalBackendPrivate *priv;
1406
1407         priv = backend->priv;
1408
1409         if (priv->notification_proxy) {
1410                 e_cal_backend_notify_component_created (priv->notification_proxy, component);
1411                 return;
1412         }
1413
1414         e_cal_backend_foreach_view (backend, component_created_cb, component);
1415 }
1416
1417 static void
1418 match_view_and_notify_component (EDataCalView *view,
1419                                  ECalComponent *old_component,
1420                                  ECalComponent *new_component)
1421 {
1422         gboolean old_match = FALSE, new_match = FALSE;
1423
1424         if (old_component)
1425                 old_match = e_data_cal_view_component_matches (view, old_component);
1426
1427         new_match = e_data_cal_view_component_matches (view, new_component);
1428
1429         if (old_match && new_match)
1430                 e_data_cal_view_notify_components_modified_1 (view, new_component);
1431         else if (new_match)
1432                 e_data_cal_view_notify_components_added_1 (view, new_component);
1433         else if (old_match) {
1434
1435                 ECalComponentId *id = e_cal_component_get_id (old_component);
1436
1437                 e_data_cal_view_notify_objects_removed_1 (view, id);
1438
1439                 e_cal_component_free_id (id);
1440         }
1441 }
1442
1443 struct component_call_data {
1444         ECalComponent         *old_component;
1445         ECalComponent         *new_component;
1446         const ECalComponentId *id;
1447 };
1448
1449 static gboolean
1450 call_match_and_notify_component (EDataCalView *view,
1451                                  gpointer user_data)
1452 {
1453         struct component_call_data *cd = user_data;
1454
1455         g_return_val_if_fail (user_data != NULL, FALSE);
1456
1457         match_view_and_notify_component (view, cd->old_component, cd->new_component);
1458
1459         return TRUE;
1460 }
1461
1462 /**
1463  * e_cal_backend_notify_component_modified:
1464  * @backend: an #ECalBackend
1465  * @old_component: the #ECalComponent before the modification
1466  * @new_component: the #ECalComponent after the modification
1467  *
1468  * Notifies each of the backend's listeners about a modified object.
1469  *
1470  * Like e_cal_backend_notify_object_modified() except takes an #ECalComponent
1471  * instead of an ical string representation and uses the #EDataCalView's
1472  * fields-of-interest to filter out unwanted information from ical strings
1473  * sent over the bus.
1474  *
1475  * Since: 3.4
1476  **/
1477 void
1478 e_cal_backend_notify_component_modified (ECalBackend *backend,
1479                                          /* const */ ECalComponent *old_component,
1480                                          /* const */ ECalComponent *new_component)
1481 {
1482         ECalBackendPrivate *priv;
1483         struct component_call_data cd;
1484
1485         priv = backend->priv;
1486
1487         if (priv->notification_proxy) {
1488                 e_cal_backend_notify_component_modified (priv->notification_proxy, old_component, new_component);
1489                 return;
1490         }
1491
1492         cd.old_component = old_component;
1493         cd.new_component = new_component;
1494         cd.id            = NULL;
1495
1496         e_cal_backend_foreach_view (backend, call_match_and_notify_component, &cd);
1497 }
1498
1499 static gboolean
1500 component_removed_cb (EDataCalView *view,
1501                       gpointer user_data)
1502 {
1503         struct component_call_data *cd = user_data;
1504
1505         g_return_val_if_fail (user_data != NULL, FALSE);
1506
1507         if (cd->new_component == NULL) {
1508                 /* if object == NULL, it means the object has been completely
1509                  * removed from the backend */
1510                 if (!cd->old_component || e_data_cal_view_component_matches (view, cd->old_component))
1511                         e_data_cal_view_notify_objects_removed_1 (view, cd->id);
1512         } else
1513                 match_view_and_notify_component (view, cd->old_component, cd->new_component);
1514
1515         return TRUE;
1516 }
1517
1518 /**
1519  * e_cal_backend_notify_component_removed:
1520  * @backend: an #ECalBackend
1521  * @id: the Id of the removed object
1522  * @old_component: the removed component
1523  * @new_component: the component after the removal. This only applies to recurrent 
1524  * appointments that had an instance removed. In that case, this function
1525  * notifies a modification instead of a removal.
1526  *
1527  * Notifies each of the backend's listeners about a removed object.
1528  *
1529  * Like e_cal_backend_notify_object_removed() except takes an #ECalComponent
1530  * instead of an ical string representation and uses the #EDataCalView's
1531  * fields-of-interest to filter out unwanted information from ical strings
1532  * sent over the bus.
1533  *
1534  * Since: 3.4
1535  **/
1536 void
1537 e_cal_backend_notify_component_removed (ECalBackend *backend,
1538                                         const ECalComponentId *id,
1539                                         /* const */ ECalComponent *old_component,
1540                                         /* const */ ECalComponent *new_component)
1541 {
1542         ECalBackendPrivate *priv;
1543         struct component_call_data cd;
1544
1545         priv = backend->priv;
1546
1547         if (priv->notification_proxy) {
1548                 e_cal_backend_notify_component_removed (priv->notification_proxy, id, old_component, new_component);
1549                 return;
1550         }
1551
1552         cd.old_component = old_component;
1553         cd.new_component = new_component;
1554         cd.id            = id;
1555
1556         e_cal_backend_foreach_view (backend, component_removed_cb, &cd);
1557 }
1558
1559 static gboolean
1560 object_created_cb (EDataCalView *view,
1561                    gpointer data)
1562 {
1563         const gchar *calobj = data;
1564
1565         if (e_data_cal_view_object_matches (view, calobj))
1566                 e_data_cal_view_notify_objects_added_1 (view, calobj);
1567
1568         return TRUE;
1569 }
1570
1571 /**
1572  * e_cal_backend_notify_object_created:
1573  * @backend: an #ECalBackend
1574  * @calobj: the newly created object
1575  *
1576  * Notifies each of the backend's listeners about a new object.
1577  *
1578  * #e_data_cal_notify_object_created() calls this for you. You only need to
1579  * call e_cal_backend_notify_object_created() yourself to report objects
1580  * created by non-EDS clients.
1581  *
1582  * Deprecated: 3.4: Use e_cal_backend_notify_component_created() instead.
1583  **/
1584 void
1585 e_cal_backend_notify_object_created (ECalBackend *backend,
1586                                      const gchar *calobj)
1587 {
1588         ECalBackendPrivate *priv;
1589
1590         priv = backend->priv;
1591
1592         if (priv->notification_proxy) {
1593                 e_cal_backend_notify_object_created (priv->notification_proxy, calobj);
1594                 return;
1595         }
1596
1597         e_cal_backend_foreach_view (backend, object_created_cb, (gpointer) calobj);
1598 }
1599
1600 /**
1601  * e_cal_backend_notify_objects_added:
1602  *
1603  * Since: 2.24
1604  *
1605  * Deprecated: 3.4: Use e_data_cal_view_notify_objects_added() instead.
1606  **/
1607 void
1608 e_cal_backend_notify_objects_added (ECalBackend *backend,
1609                                     EDataCalView *view,
1610                                     const GSList *objects)
1611 {
1612         e_data_cal_view_notify_objects_added (view, objects);
1613 }
1614
1615 static void
1616 match_view_and_notify_object (EDataCalView *view,
1617                               const gchar *old_object,
1618                               const gchar *object)
1619 {
1620         gboolean old_match = FALSE, new_match = FALSE;
1621
1622         if (old_object)
1623                 old_match = e_data_cal_view_object_matches (view, old_object);
1624
1625         new_match = e_data_cal_view_object_matches (view, object);
1626         if (old_match && new_match)
1627                 e_data_cal_view_notify_objects_modified_1 (view, object);
1628         else if (new_match)
1629                 e_data_cal_view_notify_objects_added_1 (view, object);
1630         else if (old_match) {
1631                 ECalComponent *comp = NULL;
1632
1633                 comp = e_cal_component_new_from_string (old_object);
1634                 if (comp) {
1635                         ECalComponentId *id = e_cal_component_get_id (comp);
1636
1637                         e_data_cal_view_notify_objects_removed_1 (view, id);
1638
1639                         e_cal_component_free_id (id);
1640                         g_object_unref (comp);
1641                 }
1642         }
1643 }
1644
1645 struct object_call_data {
1646         const gchar *old_object;
1647         const gchar *object;
1648         const ECalComponentId *id;
1649 };
1650
1651 static gboolean
1652 call_match_and_notify_object (EDataCalView *view,
1653                               gpointer user_data)
1654 {
1655         struct object_call_data *cd = user_data;
1656
1657         g_return_val_if_fail (user_data != NULL, FALSE);
1658
1659         match_view_and_notify_object (view, cd->old_object, cd->object);
1660
1661         return TRUE;
1662 }
1663
1664 /**
1665  * e_cal_backend_notify_object_modified:
1666  * @backend: an #ECalBackend
1667  * @old_object: iCalendar representation of the original form of the object
1668  * @object: iCalendar representation of the new form of the object
1669  *
1670  * Notifies each of the backend's listeners about a modified object.
1671  *
1672  * #e_data_cal_notify_object_modified() calls this for you. You only need to
1673  * call e_cal_backend_notify_object_modified() yourself to report objects
1674  * modified by non-EDS clients.
1675  *
1676  * Deprecated: 3.4: Use e_cal_backend_notify_component_modified() instead.
1677  **/
1678 void
1679 e_cal_backend_notify_object_modified (ECalBackend *backend,
1680                                       const gchar *old_object,
1681                                       const gchar *object)
1682 {
1683         ECalBackendPrivate *priv;
1684         struct object_call_data cd;
1685
1686         priv = backend->priv;
1687
1688         if (priv->notification_proxy) {
1689                 e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object);
1690                 return;
1691         }
1692
1693         cd.old_object = old_object;
1694         cd.object = object;
1695         cd.id = NULL;
1696
1697         e_cal_backend_foreach_view (backend, call_match_and_notify_object, &cd);
1698 }
1699
1700 /**
1701  * e_cal_backend_notify_objects_modified:
1702  *
1703  * Since: 2.24
1704  *
1705  * Deprecated: 3.4: Use e_data_cal_view_notify_objects_modified() instead.
1706  **/
1707 void
1708 e_cal_backend_notify_objects_modified (ECalBackend *backend,
1709                                        EDataCalView *view,
1710                                        const GSList *objects)
1711 {
1712         e_data_cal_view_notify_objects_modified (view, objects);
1713 }
1714
1715 static gboolean
1716 object_removed_cb (EDataCalView *view,
1717                    gpointer user_data)
1718 {
1719         struct object_call_data *cd = user_data;
1720
1721         g_return_val_if_fail (user_data != NULL, FALSE);
1722
1723         if (cd->object == NULL) {
1724                 /* if object == NULL, it means the object has been completely
1725                  * removed from the backend */
1726                 if (!cd->old_object || e_data_cal_view_object_matches (view, cd->old_object))
1727                         e_data_cal_view_notify_objects_removed_1 (view, cd->id);
1728         } else
1729                 match_view_and_notify_object (view, cd->old_object, cd->object);
1730
1731         return TRUE;
1732 }
1733
1734 /**
1735  * e_cal_backend_notify_object_removed:
1736  * @backend: an #ECalBackend
1737  * @id: the Id of the removed object
1738  * @old_object: iCalendar representation of the removed object
1739  * @new_object: iCalendar representation of the object after the removal. This
1740  * only applies to recurrent appointments that had an instance removed. In that
1741  * case, this function notifies a modification instead of a removal.
1742  *
1743  * Notifies each of the backend's listeners about a removed object.
1744  *
1745  * e_data_cal_notify_object_removed() calls this for you. You only need to
1746  * call e_cal_backend_notify_object_removed() yourself to report objects
1747  * removed by non-EDS clients.
1748  *
1749  * Deprecated: 3.4: Use e_cal_backend_notify_component_removed() instead.
1750  **/
1751 void
1752 e_cal_backend_notify_object_removed (ECalBackend *backend,
1753                                      const ECalComponentId *id,
1754                                      const gchar *old_object,
1755                                      const gchar *new_object)
1756 {
1757         ECalBackendPrivate *priv;
1758         struct object_call_data cd;
1759
1760         priv = backend->priv;
1761
1762         if (priv->notification_proxy) {
1763                 e_cal_backend_notify_object_removed (priv->notification_proxy, id, old_object, new_object);
1764                 return;
1765         }
1766
1767         cd.old_object = old_object;
1768         cd.object = new_object;
1769         cd.id = id;
1770
1771         e_cal_backend_foreach_view (backend, object_removed_cb, &cd);
1772 }
1773
1774 /**
1775  * e_cal_backend_notify_objects_removed:
1776  *
1777  * Since: 2.24
1778  *
1779  * Deprecated: 3.4: Use e_data_cal_view_notify_objects_removed() instead.
1780  **/
1781 void
1782 e_cal_backend_notify_objects_removed (ECalBackend *backend,
1783                                       EDataCalView *view,
1784                                       const GSList *ids)
1785 {
1786         e_data_cal_view_notify_objects_removed (view, ids);
1787 }
1788
1789 /**
1790  * e_cal_backend_notify_error:
1791  * @backend: an #ECalBackend
1792  * @message: Error message
1793  *
1794  * Notifies each of the backend's listeners about an error
1795  **/
1796 void
1797 e_cal_backend_notify_error (ECalBackend *backend,
1798                             const gchar *message)
1799 {
1800         ECalBackendPrivate *priv = backend->priv;
1801         GSList *l;
1802
1803         if (priv->notification_proxy) {
1804                 e_cal_backend_notify_error (priv->notification_proxy, message);
1805                 return;
1806         }
1807
1808         g_mutex_lock (priv->clients_mutex);
1809
1810         for (l = priv->clients; l; l = l->next)
1811                 e_data_cal_report_error (l->data, message);
1812
1813         g_mutex_unlock (priv->clients_mutex);
1814 }
1815
1816 /**
1817  * e_cal_backend_notify_readonly:
1818  * @backend: an #ECalBackend
1819  * @is_readonly: flag indicating readonly status
1820  *
1821  * Notifies all backend's clients about the current readonly state.
1822  * Meant to be used by backend implementations.
1823  **/
1824 void
1825 e_cal_backend_notify_readonly (ECalBackend *backend,
1826                                gboolean is_readonly)
1827 {
1828         ECalBackendPrivate *priv;
1829         GSList *l;
1830
1831         priv = backend->priv;
1832         priv->readonly = is_readonly;
1833
1834         if (priv->notification_proxy) {
1835                 e_cal_backend_notify_readonly (priv->notification_proxy, is_readonly);
1836                 return;
1837         }
1838
1839         g_mutex_lock (priv->clients_mutex);
1840
1841         for (l = priv->clients; l; l = l->next)
1842                 e_data_cal_report_readonly (l->data, is_readonly);
1843
1844         g_mutex_unlock (priv->clients_mutex);
1845 }
1846
1847 /**
1848  * e_cal_backend_notify_online:
1849  * @backend: an #ECalBackend
1850  * @is_online: flag indicating whether @backend is connected and online
1851  *
1852  * Notifies clients of @backend's connection status indicated by @is_online.
1853  * Meant to be used by backend implementations.
1854  *
1855  * Since: 3.2
1856  **/
1857 void
1858 e_cal_backend_notify_online (ECalBackend *backend,
1859                              gboolean is_online)
1860 {
1861         ECalBackendPrivate *priv;
1862         GSList *clients;
1863
1864         priv = backend->priv;
1865
1866         if (priv->notification_proxy) {
1867                 e_cal_backend_notify_online (priv->notification_proxy, is_online);
1868                 return;
1869         }
1870
1871         g_mutex_lock (priv->clients_mutex);
1872
1873         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1874                 e_data_cal_report_online (E_DATA_CAL (clients->data), is_online);
1875
1876         g_mutex_unlock (priv->clients_mutex);
1877 }
1878
1879 /**
1880  * e_cal_backend_notify_auth_required:
1881  * @backend: an #ECalBackend
1882  * @is_self: Use %TRUE to indicate the authentication is required
1883  *    for the @backend, otheriwse the authentication is for any
1884  *    other source. Having @credentials %NULL means @is_self
1885  *    automatically.
1886  * @credentials: an #ECredentials that contains extra information for
1887  *    a source for which authentication is requested.
1888  *    This parameter can be NULL to indicate "for this calendar".
1889  *
1890  * Notifies clients that @backend requires authentication in order to
1891  * connect. This function call does not influence 'opening', but 
1892  * influences 'opened' property, which is set to %FALSE when @is_self
1893  * is %TRUE or @credentials is %NULL. Opening phase is finished
1894  * by e_cal_backend_notify_opened() if this is requested for @backend.
1895  *
1896  * See e_cal_backend_open() for a description how the whole opening
1897  * phase works.
1898  *
1899  * Meant to be used by backend implementations.
1900  **/
1901 void
1902 e_cal_backend_notify_auth_required (ECalBackend *backend,
1903                                     gboolean is_self,
1904                                     const ECredentials *credentials)
1905 {
1906         ECalBackendPrivate *priv;
1907         GSList *clients;
1908
1909         priv = backend->priv;
1910
1911         if (priv->notification_proxy) {
1912                 e_cal_backend_notify_auth_required (priv->notification_proxy, is_self, credentials);
1913                 return;
1914         }
1915
1916         g_mutex_lock (priv->clients_mutex);
1917
1918         if (is_self || !credentials)
1919                 priv->opened = FALSE;
1920
1921         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1922                 e_data_cal_report_auth_required (E_DATA_CAL (clients->data), credentials);
1923
1924         g_mutex_unlock (priv->clients_mutex);
1925 }
1926
1927 /**
1928  * e_cal_backend_notify_opened:
1929  * @backend: an #ECalBackend
1930  * @error: a #GError corresponding to the error encountered during
1931  *    the opening phase. Use %NULL for success. The @error is freed
1932  *    automatically if not %NULL.
1933  *
1934  * Notifies clients that @backend finished its opening phase.
1935  * See e_cal_backend_open() for more information how the opening
1936  * phase works. Calling this function changes 'opening' property,
1937  * same as 'opened'. 'opening' is set to %FALSE and the backend
1938  * is considered 'opened' only if the @error is %NULL.
1939  *
1940  * See also: e_cal_backend_respond_opened()
1941  *
1942  * Note: The @error is freed automatically if not %NULL.
1943  *
1944  * Meant to be used by backend implementations.
1945  *
1946  * Since: 3.2
1947  **/
1948 void
1949 e_cal_backend_notify_opened (ECalBackend *backend,
1950                              GError *error)
1951 {
1952         ECalBackendPrivate *priv;
1953         GSList *clients;
1954
1955         priv = backend->priv;
1956         g_mutex_lock (priv->clients_mutex);
1957
1958         priv->opening = FALSE;
1959         priv->opened = error == NULL;
1960
1961         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1962                 e_data_cal_report_opened (E_DATA_CAL (clients->data), error);
1963
1964         g_mutex_unlock (priv->clients_mutex);
1965
1966         if (error)
1967                 g_error_free (error);
1968 }
1969
1970 /**
1971  * e_cal_backend_notify_property_changed:
1972  * @backend: an #ECalBackend
1973  * @prop_name: property name, which changed
1974  * @prop_value: new property value
1975  *
1976  * Notifies client about property value change.
1977  *
1978  * Since: 3.2
1979  **/
1980 void
1981 e_cal_backend_notify_property_changed (ECalBackend *backend,
1982                                        const gchar *prop_name,
1983                                        const gchar *prop_value)
1984 {
1985         ECalBackendPrivate *priv;
1986         GSList *clients;
1987
1988         g_return_if_fail (backend != NULL);
1989         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1990         g_return_if_fail (backend->priv != NULL);
1991         g_return_if_fail (prop_name != NULL);
1992         g_return_if_fail (*prop_name != '\0');
1993         g_return_if_fail (prop_value != NULL);
1994
1995         priv = backend->priv;
1996         g_mutex_lock (priv->clients_mutex);
1997
1998         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1999                 e_data_cal_report_backend_property_changed (E_DATA_CAL (clients->data), prop_name, prop_value);
2000
2001         g_mutex_unlock (priv->clients_mutex);
2002 }
2003
2004 /**
2005  * e_cal_backend_respond_opened:
2006  * @backend: an #ECalBackend
2007  * @cal: an #EDataCal
2008  * @opid: an operation ID
2009  * @error: result error; can be %NULL, if it isn't then it's automatically freed
2010  *
2011  * This is a replacement for e_data_cal_respond_open() for cases where
2012  * the finish of 'open' method call also finishes backend opening phase.
2013  * This function covers calling of both e_cal_backend_notify_opened() and
2014  * e_data_cal_respond_open() with the same @error.
2015  *
2016  * See e_cal_backend_open() for more details how the opening phase works.
2017  *
2018  * Since: 3.2
2019  **/
2020 void
2021 e_cal_backend_respond_opened (ECalBackend *backend,
2022                               EDataCal *cal,
2023                               guint32 opid,
2024                               GError *error)
2025 {
2026         GError *copy = NULL;
2027
2028         g_return_if_fail (backend != NULL);
2029         g_return_if_fail (E_IS_CAL_BACKEND (backend));
2030         g_return_if_fail (cal != NULL);
2031         g_return_if_fail (opid != 0);
2032
2033         if (error)
2034                 copy = g_error_copy (error);
2035
2036         e_cal_backend_notify_opened (backend, copy);
2037         e_data_cal_respond_open (cal, opid, error);
2038 }
2039
2040 /**
2041  * e_cal_backend_empty_cache:
2042  * @backend: an #ECalBackend
2043  * @cache: Backend's cache to empty.
2044  *
2045  * Empties backend's cache with all notifications and so on, thus all listening
2046  * will know there is nothing in this backend.
2047  *
2048  * Since: 2.28
2049  **/
2050 void
2051 e_cal_backend_empty_cache (ECalBackend *backend,
2052                            ECalBackendCache *cache)
2053 {
2054         GList *comps_in_cache;
2055
2056         g_return_if_fail (backend != NULL);
2057         g_return_if_fail (E_IS_CAL_BACKEND (backend));
2058
2059         if (!cache)
2060                 return;
2061
2062         g_return_if_fail (E_IS_CAL_BACKEND_CACHE (cache));
2063
2064         e_file_cache_freeze_changes (E_FILE_CACHE (cache));
2065
2066         for (comps_in_cache = e_cal_backend_cache_get_components (cache);
2067              comps_in_cache;
2068              comps_in_cache = comps_in_cache->next) {
2069                 ECalComponentId *id;
2070                 ECalComponent *comp = comps_in_cache->data;
2071
2072                 id = e_cal_component_get_id (comp);
2073
2074                 e_cal_backend_cache_remove_component (cache, id->uid, id->rid);
2075
2076                 e_cal_backend_notify_component_removed  (backend, id, comp, NULL);
2077
2078                 e_cal_component_free_id (id);
2079                 g_object_unref (comp);
2080         }
2081
2082         g_list_free (comps_in_cache);
2083
2084         e_file_cache_thaw_changes (E_FILE_CACHE (cache));
2085 }