commiting the code for progress status indication.
[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) 2000 Ximian, Inc.
5  * Copyright (C) 2000 Ximian, Inc.
6  *
7  * Authors: Federico Mena-Quintero <federico@ximian.com>
8  *          JP Rosevear <jpr@ximian.com>
9  *          Rodrigo Moya <rodrigo@ximian.com>    
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
23  */
24
25 #include <config.h>
26 #include <libxml/parser.h>
27 #include <libxml/parserInternals.h>
28 #include <libxml/xmlmemory.h>
29
30 #include "e-cal-backend.h"
31
32 \f
33
34 /* Private part of the CalBackend structure */
35 struct _ECalBackendPrivate {
36         /* The source for this backend */
37         ESource *source;
38
39         /* URI, from source. This is cached, since we return const. */
40         char *uri;
41
42         /* The kind of components for this backend */
43         icalcomponent_kind kind;
44         
45         /* List of Cal objects */
46         GMutex *clients_mutex;
47         GList *clients;
48
49         GMutex *queries_mutex;
50         EList *queries;
51
52         /* ECalBackend to pass notifications on to */
53         ECalBackend *notification_proxy;
54 };
55
56 /* Property IDs */
57 enum props {
58         PROP_0,
59         PROP_SOURCE,
60         PROP_URI,
61         PROP_KIND
62 };
63
64 /* Signal IDs */
65 enum {
66         LAST_CLIENT_GONE,
67         OPENED,
68         REMOVED,
69         LAST_SIGNAL
70 };
71 static guint e_cal_backend_signals[LAST_SIGNAL];
72
73 static void e_cal_backend_class_init (ECalBackendClass *class);
74 static void e_cal_backend_init (ECalBackend *backend);
75 static void e_cal_backend_finalize (GObject *object);
76
77 #define CLASS(backend) (E_CAL_BACKEND_CLASS (G_OBJECT_GET_CLASS (backend)))
78
79 static GObjectClass *parent_class;
80
81 \f
82
83 /**
84  * e_cal_backend_get_type:
85  *
86  * Registers the #ECalBackend class if necessary, and returns the type ID
87  * associated to it.
88  *
89  * Return value: The type ID of the #ECalBackend class.
90  **/
91 GType
92 e_cal_backend_get_type (void)
93 {
94         static GType e_cal_backend_type = 0;
95
96         if (!e_cal_backend_type) {
97                 static GTypeInfo info = {
98                         sizeof (ECalBackendClass),
99                         (GBaseInitFunc) NULL,
100                         (GBaseFinalizeFunc) NULL,
101                         (GClassInitFunc) e_cal_backend_class_init,
102                         NULL, NULL,
103                         sizeof (ECalBackend),
104                         0,
105                         (GInstanceInitFunc) e_cal_backend_init,
106                 };
107                 e_cal_backend_type = g_type_register_static (G_TYPE_OBJECT, "ECalBackend", &info, 0);
108         }
109
110         return e_cal_backend_type;
111 }
112
113 static void
114 e_cal_backend_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
115 {
116         ECalBackend *backend;
117         ECalBackendPrivate *priv;
118         
119         backend = E_CAL_BACKEND (object);
120         priv = backend->priv;
121         
122         switch (property_id) {
123         case PROP_SOURCE:
124                 {
125                         ESource *new_source;
126
127                         new_source = g_value_get_object (value);
128                         if (new_source)
129                                 g_object_ref (new_source);
130
131                         if (priv->source)
132                                 g_object_unref (priv->source);
133
134                         priv->source = new_source;
135
136                         /* Cache the URI */
137                         if (new_source) {
138                                 g_free (priv->uri);
139                                 priv->uri = e_source_get_uri (priv->source);
140                         }
141                 }
142                 break;
143         case PROP_URI:
144                 if (!priv->source) {
145                         g_free (priv->uri);
146                         priv->uri = g_value_dup_string (value);
147                 }
148                 break;
149         case PROP_KIND:
150                 priv->kind = g_value_get_ulong (value);
151                 break;
152         default:
153                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
154                 break;
155         }
156 }
157
158 static void
159 e_cal_backend_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
160 {
161         ECalBackend *backend;
162         ECalBackendPrivate *priv;
163         
164         backend = E_CAL_BACKEND (object);
165         priv = backend->priv;
166
167         switch (property_id) {
168         case PROP_SOURCE:
169                 g_value_set_object (value, e_cal_backend_get_source (backend));
170                 break;
171         case PROP_URI:
172                 g_value_set_string (value, e_cal_backend_get_uri (backend));
173                 break;
174         case PROP_KIND:
175                 g_value_set_ulong (value, e_cal_backend_get_kind (backend));
176                 break;
177         default:
178                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
179                 break;
180         }
181 }
182
183 /* Class initialization function for the calendar backend */
184 static void
185 e_cal_backend_class_init (ECalBackendClass *class)
186 {
187         GObjectClass *object_class;
188
189         parent_class = (GObjectClass *) g_type_class_peek_parent (class);
190
191         object_class = (GObjectClass *) class;
192
193         object_class->set_property = e_cal_backend_set_property;
194         object_class->get_property = e_cal_backend_get_property;
195         object_class->finalize = e_cal_backend_finalize;
196
197         g_object_class_install_property (object_class, PROP_SOURCE, 
198                                          g_param_spec_object ("source", NULL, NULL, E_TYPE_SOURCE,
199                                                               G_PARAM_READABLE | G_PARAM_WRITABLE
200                                                               | G_PARAM_CONSTRUCT_ONLY));
201
202         g_object_class_install_property (object_class, PROP_URI, 
203                                          g_param_spec_string ("uri", NULL, NULL, "",
204                                                               G_PARAM_READABLE | G_PARAM_WRITABLE
205                                                               | G_PARAM_CONSTRUCT_ONLY));
206
207         g_object_class_install_property (object_class, PROP_KIND, 
208                                          g_param_spec_ulong ("kind", NULL, NULL, 
209                                                              ICAL_NO_COMPONENT, ICAL_XLICMIMEPART_COMPONENT, 
210                                                              ICAL_NO_COMPONENT,
211                                                              G_PARAM_READABLE | G_PARAM_WRITABLE
212                                                              | G_PARAM_CONSTRUCT_ONLY));        
213         e_cal_backend_signals[LAST_CLIENT_GONE] =
214                 g_signal_new ("last_client_gone",
215                               G_TYPE_FROM_CLASS (class),
216                               G_SIGNAL_RUN_FIRST,
217                               G_STRUCT_OFFSET (ECalBackendClass, last_client_gone),
218                               NULL, NULL,
219                               g_cclosure_marshal_VOID__VOID,
220                               G_TYPE_NONE, 0);
221         e_cal_backend_signals[OPENED] =
222                 g_signal_new ("opened",
223                               G_TYPE_FROM_CLASS (class),
224                               G_SIGNAL_RUN_FIRST,
225                               G_STRUCT_OFFSET (ECalBackendClass, opened),
226                               NULL, NULL,
227                               g_cclosure_marshal_VOID__ENUM,
228                               G_TYPE_NONE, 1,
229                               G_TYPE_INT);
230         e_cal_backend_signals[REMOVED] =
231                 g_signal_new ("removed",
232                               G_TYPE_FROM_CLASS (class),
233                               G_SIGNAL_RUN_FIRST,
234                               G_STRUCT_OFFSET (ECalBackendClass, removed),
235                               NULL, NULL,
236                               g_cclosure_marshal_VOID__ENUM,
237                               G_TYPE_NONE, 1,
238                               G_TYPE_INT);
239
240         class->last_client_gone = NULL;
241         class->opened = NULL;
242         class->obj_updated = NULL;
243
244         class->get_cal_address = NULL;
245         class->get_alarm_email_address = NULL;
246         class->get_static_capabilities = NULL;
247         class->open = NULL;
248         class->is_loaded = NULL;
249         class->is_read_only = NULL;
250         class->start_query = NULL;
251         class->get_mode = NULL;
252         class->set_mode = NULL; 
253         class->get_object = NULL;
254         class->get_default_object = NULL;
255         class->get_object_list = NULL;
256         class->get_free_busy = NULL;
257         class->get_changes = NULL;
258         class->discard_alarm = NULL;
259         class->create_object = NULL;
260         class->modify_object = NULL;
261         class->remove_object = NULL;
262         class->receive_objects = NULL;
263         class->send_objects = NULL;
264         class->get_timezone = NULL;
265         class->add_timezone = NULL;
266         class->set_default_timezone = NULL;
267 }
268
269 /* Object initialization func for the calendar backend */
270 void
271 e_cal_backend_init (ECalBackend *backend)
272 {
273         ECalBackendPrivate *priv;
274
275         priv = g_new0 (ECalBackendPrivate, 1);
276         backend->priv = priv;
277
278         priv->clients = NULL;
279         priv->clients_mutex = g_mutex_new ();
280
281         /* FIXME bonobo_object_ref/unref? */
282         priv->queries = e_list_new((EListCopyFunc) g_object_ref, (EListFreeFunc) g_object_unref, NULL);
283         priv->queries_mutex = g_mutex_new ();
284 }
285
286 void
287 e_cal_backend_finalize (GObject *object)
288 {
289         ECalBackend *backend = (ECalBackend *)object;
290         ECalBackendPrivate *priv;
291
292         priv = backend->priv;
293
294         g_assert (priv->clients == NULL);
295
296         g_object_unref (priv->queries);
297
298         g_mutex_free (priv->clients_mutex);
299         g_mutex_free (priv->queries_mutex);
300
301         g_free (priv);
302
303         G_OBJECT_CLASS (parent_class)->finalize (object);
304 }
305
306 \f
307
308 /**
309  * e_cal_backend_get_source:
310  * @backend: An #ECalBackend object.
311  *
312  * Gets the #ESource associated with the given backend.
313  *
314  * Return value: The #ESource for the backend.
315  */
316 ESource *
317 e_cal_backend_get_source (ECalBackend *backend)
318 {
319         ECalBackendPrivate *priv;
320         
321         g_return_val_if_fail (backend != NULL, NULL);
322         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
323
324         priv = backend->priv;
325         
326         return priv->source;
327 }
328
329 /**
330  * e_cal_backend_get_uri:
331  * @backend: A calendar backend.
332  *
333  * Queries the URI of a calendar backend, which must already have an open
334  * calendar.
335  *
336  * Return value: The URI where the calendar is stored.
337  **/
338 const char *
339 e_cal_backend_get_uri (ECalBackend *backend)
340 {
341         ECalBackendPrivate *priv;
342         
343         g_return_val_if_fail (backend != NULL, NULL);
344         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
345
346         priv = backend->priv;
347         
348         return priv->uri;
349 }
350
351 /**
352  * e_cal_backend_get_kind:
353  * @backend: An #ECalBackend object.
354  *
355  * Gets the kind of components the given backend stores.
356  *
357  * Return value: The kind of components for this backend.
358  */
359 icalcomponent_kind
360 e_cal_backend_get_kind (ECalBackend *backend)
361 {
362         ECalBackendPrivate *priv;
363         
364         g_return_val_if_fail (backend != NULL, ICAL_NO_COMPONENT);
365         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), ICAL_NO_COMPONENT);
366
367         priv = backend->priv;
368         
369         return priv->kind;
370 }
371
372 static void
373 cal_destroy_cb (gpointer data, GObject *where_cal_was)
374 {
375         ECalBackend *backend = E_CAL_BACKEND (data);
376
377         e_cal_backend_remove_client (backend, (EDataCal *) where_cal_was);
378 }
379
380 static void
381 listener_died_cb (gpointer cnx, gpointer data)
382 {
383         EDataCal *cal = E_DATA_CAL (data);
384
385         e_cal_backend_remove_client (e_data_cal_get_backend (cal), cal);
386 }
387
388 static void
389 last_client_gone (ECalBackend *backend)
390 {
391         g_signal_emit (backend, e_cal_backend_signals[LAST_CLIENT_GONE], 0);
392 }
393
394 /**
395  * e_cal_backend_add_client:
396  * @backend: An ECalBackend object.
397  * @cal: An EDataCal object.
398  *
399  * Adds a new client to the given backend. For any event, the backend will
400  * notify all clients added via this function.
401  */
402 void
403 e_cal_backend_add_client (ECalBackend *backend, EDataCal *cal)
404 {
405         ECalBackendPrivate *priv;
406         
407         g_return_if_fail (backend != NULL);
408         g_return_if_fail (E_IS_CAL_BACKEND (backend));
409         g_return_if_fail (cal != NULL);
410         g_return_if_fail (E_IS_DATA_CAL (cal));
411
412         priv = backend->priv;
413         
414         bonobo_object_set_immortal (BONOBO_OBJECT (cal), TRUE);
415
416         g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
417
418         ORBit_small_listen_for_broken (e_data_cal_get_listener (cal), G_CALLBACK (listener_died_cb), cal);
419
420         g_mutex_lock (priv->clients_mutex);
421         priv->clients = g_list_append (priv->clients, cal);
422         g_mutex_unlock (priv->clients_mutex);
423 }
424
425 /**
426  * e_cal_backend_remove_client:
427  * @backend: An #ECalBackend object.
428  * @cal: An #EDataCal object.
429  *
430  * Removes a client from the list of connected clients to the given backend.
431  */
432 void
433 e_cal_backend_remove_client (ECalBackend *backend, EDataCal *cal)
434 {
435         ECalBackendPrivate *priv;
436         
437         /* XXX this needs a bit more thinking wrt the mutex - we
438            should be holding it when we check to see if clients is
439            NULL */
440         g_return_if_fail (backend != NULL);
441         g_return_if_fail (E_IS_CAL_BACKEND (backend));
442         g_return_if_fail (cal != NULL);
443         g_return_if_fail (E_IS_DATA_CAL (cal));
444
445         priv = backend->priv;
446
447         /* Disconnect */
448         g_mutex_lock (priv->clients_mutex);
449         priv->clients = g_list_remove (priv->clients, cal);
450         g_mutex_unlock (priv->clients_mutex);
451
452         /* When all clients go away, notify the parent factory about it so that
453          * it may decide whether to kill the backend or not.
454          */
455         if (!priv->clients)
456                 last_client_gone (backend);
457 }
458
459 /**
460  * e_cal_backend_add_query:
461  * @backend: An #ECalBackend object.
462  * @query: An #EDataCalView object.
463  *
464  * Adds a query to the list of live queries being run by the given backend.
465  * Doing so means that any listener on the query will get notified of any
466  * change that affect the live query.
467  */
468 void
469 e_cal_backend_add_query (ECalBackend *backend, EDataCalView *query)
470 {
471         g_return_if_fail (backend != NULL);
472         g_return_if_fail (E_IS_CAL_BACKEND (backend));
473
474         g_mutex_lock (backend->priv->queries_mutex);
475
476         e_list_append (backend->priv->queries, query);
477         
478         g_mutex_unlock (backend->priv->queries_mutex);
479 }
480
481 /**
482  * e_cal_backend_get_queries:
483  * @backend: An #ECalBackend object.
484  *
485  * Gets the list of live queries being run on the given backend.
486  *
487  * Return value: The list of live queries.
488  */
489 EList *
490 e_cal_backend_get_queries (ECalBackend *backend)
491 {
492         g_return_val_if_fail (backend != NULL, NULL);
493         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
494
495         return backend->priv->queries;
496 }
497
498
499 /**
500  * e_cal_backend_get_cal_address:
501  * @backend: A calendar backend.
502  *
503  * Queries the cal address associated with a calendar backend, which
504  * must already have an open calendar.
505  **/
506 void
507 e_cal_backend_get_cal_address (ECalBackend *backend, EDataCal *cal)
508 {
509         g_return_if_fail (backend != NULL);
510         g_return_if_fail (E_IS_CAL_BACKEND (backend));
511
512         g_assert (CLASS (backend)->get_cal_address != NULL);
513         (* CLASS (backend)->get_cal_address) (backend, cal);
514 }
515
516 /**
517  * e_cal_backend_get_alarm_email_address:
518  * @backend: An #ECalBackend object.
519  * @cal: An #EDataCal object.
520  *
521  * Calls the get_alarm_email_address method on the given backend.
522  */
523 void
524 e_cal_backend_get_alarm_email_address (ECalBackend *backend, EDataCal *cal)
525 {
526         g_return_if_fail (backend != NULL);
527         g_return_if_fail (E_IS_CAL_BACKEND (backend));
528
529         g_assert (CLASS (backend)->get_alarm_email_address != NULL);
530         (* CLASS (backend)->get_alarm_email_address) (backend, cal);
531 }
532
533 /**
534  *e_cal_backend_get_alarm_email_address:
535  * @backend: An #ECalBackend object.
536  * @cal: An #EDataCal object.
537  *
538  * Calls the get_ldap_attribute method of the given backend.
539  */
540 void
541 e_cal_backend_get_ldap_attribute (ECalBackend *backend, EDataCal *cal)
542 {
543         g_return_if_fail (backend != NULL);
544         g_return_if_fail (E_IS_CAL_BACKEND (backend));
545
546         g_assert (CLASS (backend)->get_ldap_attribute != NULL);
547         (* CLASS (backend)->get_ldap_attribute) (backend, cal);
548 }
549
550 /**
551  * e_cal_backend_get_alarm_email_address:
552  * @backend: An #ECalBackend object.
553  * @cal: An #EDataCal object.
554  *
555  * Calls the get_static_capabilities method on the given backend.
556  */
557 void
558 e_cal_backend_get_static_capabilities (ECalBackend *backend, EDataCal *cal)
559 {
560         g_return_if_fail (backend != NULL);
561         g_return_if_fail (E_IS_CAL_BACKEND (backend));
562
563         g_assert (CLASS (backend)->get_static_capabilities != NULL);
564         (* CLASS (backend)->get_static_capabilities) (backend, cal);
565 }
566
567 /**
568  * e_cal_backend_open:
569  * @backend: A calendar backend.
570  * @cal: An #EDataCal object.
571  * @only_if_exists: Whether the calendar should be opened only if it already
572  * exists.  If FALSE, a new calendar will be created when the specified @uri
573  * does not exist.
574  * @username: User name to use for authentication (if needed).
575  * @password: Password for @username.
576  *
577  * Opens a calendar backend with data from a calendar stored at the specified
578  * URI.
579  */
580 void
581 e_cal_backend_open (ECalBackend *backend, EDataCal *cal, gboolean only_if_exists,
582                     const char *username, const char *password)
583 {
584         g_return_if_fail (backend != NULL);
585         g_return_if_fail (E_IS_CAL_BACKEND (backend));
586
587         g_assert (CLASS (backend)->open != NULL);
588         (* CLASS (backend)->open) (backend, cal, only_if_exists, username, password);
589 }
590
591 /**
592  * e_cal_backend_remove:
593  * @backend: A calendar backend.
594  * @cal: An #EDataCal object.
595  *
596  * Removes the calendar being accessed by the given backend.
597  */
598 void
599 e_cal_backend_remove (ECalBackend *backend, EDataCal *cal)
600 {
601         g_return_if_fail (backend != NULL);
602         g_return_if_fail (E_IS_CAL_BACKEND (backend));
603
604         g_assert (CLASS (backend)->remove != NULL);
605         (* CLASS (backend)->remove) (backend, cal);
606 }
607
608 /**
609  * e_cal_backend_is_loaded:
610  * @backend: A calendar backend.
611  * 
612  * Queries whether a calendar backend has been loaded yet.
613  * 
614  * Return value: TRUE if the backend has been loaded with data, FALSE
615  * otherwise.
616  */
617 gboolean
618 e_cal_backend_is_loaded (ECalBackend *backend)
619 {
620         gboolean result;
621
622         g_return_val_if_fail (backend != NULL, FALSE);
623         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
624
625         g_assert (CLASS (backend)->is_loaded != NULL);
626         result = (* CLASS (backend)->is_loaded) (backend);
627
628         return result;
629 }
630
631 /**
632  * e_cal_backend_is_read_only
633  * @backend: A calendar backend.
634  * @cal: An #EDataCal object.
635  *
636  * Queries whether a calendar backend is read only or not.
637  *
638  */
639 void
640 e_cal_backend_is_read_only (ECalBackend *backend, EDataCal *cal)
641 {
642         g_return_if_fail (backend != NULL);
643         g_return_if_fail (E_IS_CAL_BACKEND (backend));
644
645         g_assert (CLASS (backend)->is_read_only != NULL);
646         (* CLASS (backend)->is_read_only) (backend, cal);
647 }
648
649 /**
650  * e_cal_backend_start_query:
651  * @backend: A calendar backend.
652  * @query: The query to be started.
653  *
654  * Starts a new live query on the given backend.
655  */
656 void 
657 e_cal_backend_start_query (ECalBackend *backend, EDataCalView *query)
658 {
659         g_return_if_fail (backend != NULL);
660         g_return_if_fail (E_IS_CAL_BACKEND (backend));
661
662         g_assert (CLASS (backend)->start_query != NULL);
663         (* CLASS (backend)->start_query) (backend, query);
664 }
665
666 /**
667  * e_cal_backend_get_mode:
668  * @backend: A calendar backend. 
669  * 
670  * Queries whether a calendar backend is connected remotely.
671  * 
672  * Return value: The current mode the calendar is in
673  **/
674 CalMode
675 e_cal_backend_get_mode (ECalBackend *backend)
676 {
677         CalMode result;
678
679         g_return_val_if_fail (backend != NULL, FALSE);
680         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
681
682         g_assert (CLASS (backend)->get_mode != NULL);
683         result = (* CLASS (backend)->get_mode) (backend);
684
685         return result;
686 }
687
688
689 /**
690  * e_cal_backend_set_mode:
691  * @backend: A calendar backend
692  * @mode: Mode to change to
693  * 
694  * Sets the mode of the calendar 
695  */
696 void
697 e_cal_backend_set_mode (ECalBackend *backend, CalMode mode)
698 {
699         g_return_if_fail (backend != NULL);
700         g_return_if_fail (E_IS_CAL_BACKEND (backend));
701
702         g_assert (CLASS (backend)->set_mode != NULL);
703         (* CLASS (backend)->set_mode) (backend, mode);
704 }
705
706 /**
707  * e_cal_backend_get_default_object:
708  * @backend: A calendar backend.
709  * @cal: An #EDataCal object.
710  *
711  * Calls the get_default_object method on the given backend.
712  */
713 void
714 e_cal_backend_get_default_object (ECalBackend *backend, EDataCal *cal)
715 {
716         g_return_if_fail (backend != NULL);
717         g_return_if_fail (E_IS_CAL_BACKEND (backend));
718
719         g_assert (CLASS (backend)->get_default_object != NULL);
720         (* CLASS (backend)->get_default_object) (backend, cal);
721 }
722
723 /**
724  * e_cal_backend_get_object:
725  * @backend: A calendar backend.
726  * @cal: An #EDataCal object.
727  * @uid: Unique identifier for a calendar object.
728  * @rid: ID for the object's recurrence to get.
729  *
730  * Queries a calendar backend for a calendar object based on its unique
731  * identifier and its recurrence ID (if a recurrent appointment).
732  */
733 void
734 e_cal_backend_get_object (ECalBackend *backend, EDataCal *cal, const char *uid, const char *rid)
735 {
736         g_return_if_fail (backend != NULL);
737         g_return_if_fail (E_IS_CAL_BACKEND (backend));
738         g_return_if_fail (uid != NULL);
739
740         g_assert (CLASS (backend)->get_object != NULL);
741         (* CLASS (backend)->get_object) (backend, cal, uid, rid);
742 }
743
744 /**
745  * e_cal_backend_get_object_list:
746  * @backend: A calendar backend.
747  * @cal: An #EDataCal object.
748  * @sexp: Expression to search for.
749  * 
750  * Calls the get_object_list method on the given backend.
751  */
752 void
753 e_cal_backend_get_object_list (ECalBackend *backend, EDataCal *cal, const char *sexp)
754 {
755         g_return_if_fail (backend != NULL);
756         g_return_if_fail (E_IS_CAL_BACKEND (backend));
757
758         g_assert (CLASS (backend)->get_object_list != NULL);
759         (* CLASS (backend)->get_object_list) (backend, cal, sexp);
760 }
761
762 /**
763  * e_cal_backend_get_free_busy:
764  * @backend: A calendar backend.
765  * @cal: An #EDataCal object.
766  * @users: List of users to get free/busy information for.
767  * @start: Start time for query.
768  * @end: End time for query.
769  * 
770  * Gets a free/busy object for the given time interval
771  */
772 void
773 e_cal_backend_get_free_busy (ECalBackend *backend, EDataCal *cal, GList *users, time_t start, time_t end)
774 {
775         g_return_if_fail (backend != NULL);
776         g_return_if_fail (E_IS_CAL_BACKEND (backend));
777         g_return_if_fail (start != -1 && end != -1);
778         g_return_if_fail (start <= end);
779
780         g_assert (CLASS (backend)->get_free_busy != NULL);
781         (* CLASS (backend)->get_free_busy) (backend, cal, users, start, end);
782 }
783
784 /**
785  * e_cal_backend_get_changes:
786  * @backend: A calendar backend.
787  * @cal: An #EDataCal object.
788  * @change_id: A unique uid for the callers change list
789  * 
790  * Builds a sequence of objects and the type of change that occurred on them since
791  * the last time the give change_id was seen
792  */
793 void
794 e_cal_backend_get_changes (ECalBackend *backend, EDataCal *cal, const char *change_id) 
795 {
796         g_return_if_fail (backend != NULL);
797         g_return_if_fail (E_IS_CAL_BACKEND (backend));
798         g_return_if_fail (change_id != NULL);
799
800         g_assert (CLASS (backend)->get_changes != NULL);
801         (* CLASS (backend)->get_changes) (backend, cal, change_id);
802 }
803
804 /**
805  * e_cal_backend_discard_alarm
806  * @backend: A calendar backend.
807  * @cal: An #EDataCal object.
808  * @uid: UID of the component to discard the alarm from.
809  * @auid: Alarm ID.
810  *
811  * Discards an alarm from the given component. This allows the specific backend
812  * to do whatever is needed to really discard the alarm.
813  */
814 void
815 e_cal_backend_discard_alarm (ECalBackend *backend, EDataCal *cal, const char *uid, const char *auid)
816 {
817         g_return_if_fail (backend != NULL);
818         g_return_if_fail (E_IS_CAL_BACKEND (backend));
819         g_return_if_fail (uid != NULL);
820         g_return_if_fail (auid != NULL);
821
822         g_assert (CLASS (backend)->discard_alarm != NULL);
823         (* CLASS (backend)->discard_alarm) (backend, cal, uid, auid);
824 }
825
826 /**
827  * e_cal_backend_create_object:
828  * @backend: A calendar backend.
829  * @cal: An #EDataCal object.
830  * @calobj: The object to create.
831  *
832  * Calls the create_object method on the given backend.
833  */
834 void
835 e_cal_backend_create_object (ECalBackend *backend, EDataCal *cal, const char *calobj)
836 {
837         g_return_if_fail (backend != NULL);
838         g_return_if_fail (E_IS_CAL_BACKEND (backend));
839         g_return_if_fail (calobj != NULL);
840
841         if (CLASS (backend)->create_object)
842                 (* CLASS (backend)->create_object) (backend, cal, calobj);
843         else
844                 e_data_cal_notify_object_created (cal, GNOME_Evolution_Calendar_PermissionDenied, NULL, NULL);
845 }
846
847 /**
848  * e_cal_backend_modify_object:
849  * @backend: A calendar backend.
850  * @cal: An #EDataCal object.
851  * @calobj: Object to be modified.
852  * @mod: Type of modification.
853  *
854  * Calls the modify_object method on the given backend.
855  */
856 void
857 e_cal_backend_modify_object (ECalBackend *backend, EDataCal *cal, const char *calobj, CalObjModType mod)
858 {
859         g_return_if_fail (backend != NULL);
860         g_return_if_fail (E_IS_CAL_BACKEND (backend));
861         g_return_if_fail (calobj != NULL);
862
863         if (CLASS (backend)->modify_object)
864                 (* CLASS (backend)->modify_object) (backend, cal, calobj, mod);
865         else
866                 e_data_cal_notify_object_removed (cal, GNOME_Evolution_Calendar_PermissionDenied, NULL, NULL, NULL);
867 }
868
869 /**
870  * e_cal_backend_remove_object:
871  * @backend: A calendar backend.
872  * @cal: An #EDataCal object.
873  * @uid: Unique identifier of the object to remove.
874  * @rid: A recurrence ID.
875  * @mod: Type of removal.
876  * 
877  * Removes an object in a calendar backend.  The backend will notify all of its
878  * clients about the change.
879  */
880 void
881 e_cal_backend_remove_object (ECalBackend *backend, EDataCal *cal, const char *uid, const char *rid, CalObjModType mod)
882 {
883         g_return_if_fail (backend != NULL);
884         g_return_if_fail (E_IS_CAL_BACKEND (backend));
885         g_return_if_fail (uid != NULL);
886
887         g_assert (CLASS (backend)->remove_object != NULL);
888         (* CLASS (backend)->remove_object) (backend, cal, uid, rid, mod);
889 }
890
891 /**
892  * e_cal_backend_receive_objects:
893  * @backend: A calendar backend.
894  * @cal: An #EDataCal object.
895  * @calobj: iCalendar object.
896  *
897  * Calls the receive_objects method on the given backend.
898  */
899 void
900 e_cal_backend_receive_objects (ECalBackend *backend, EDataCal *cal, const char *calobj)
901 {
902         g_return_if_fail (backend != NULL);
903         g_return_if_fail (E_IS_CAL_BACKEND (backend));
904         g_return_if_fail (calobj != NULL);
905
906         g_assert (CLASS (backend)->receive_objects != NULL);
907         (* CLASS (backend)->receive_objects) (backend, cal, calobj);
908 }
909
910 /**
911  * e_cal_backend_send_objects:
912  * @backend: A calendar backend.
913  * @cal: An #EDataCal object.
914  * @calobj: iCalendar object to be sent.
915  *
916  * Calls the send_objects method on the given backend.
917  */
918 void
919 e_cal_backend_send_objects (ECalBackend *backend, EDataCal *cal, const char *calobj)
920 {
921         g_return_if_fail (backend != NULL);
922         g_return_if_fail (E_IS_CAL_BACKEND (backend));
923         g_return_if_fail (calobj != NULL);
924
925         g_assert (CLASS (backend)->send_objects != NULL);
926         (* CLASS (backend)->send_objects) (backend, cal, calobj);
927 }
928
929 /**
930  * e_cal_backend_get_timezone:
931  * @backend: A calendar backend.
932  * @cal: An #EDataCal object.
933  * @tzid: Unique identifier of a VTIMEZONE object. Note that this must not be
934  * NULL.
935  * 
936  * Returns the icaltimezone* corresponding to the TZID, or NULL if the TZID
937  * can't be found.
938  */
939 void
940 e_cal_backend_get_timezone (ECalBackend *backend, EDataCal *cal, const char *tzid)
941 {
942         g_return_if_fail (backend != NULL);
943         g_return_if_fail (E_IS_CAL_BACKEND (backend));
944         g_return_if_fail (tzid != NULL);
945
946         g_assert (CLASS (backend)->get_timezone != NULL);
947         (* CLASS (backend)->get_timezone) (backend, cal, tzid);
948 }
949
950 /**
951  * e_cal_backend_set_default_timezone:
952  * @backend: A calendar backend.
953  * @cal: An #EDataCal object.
954  * @tzid: The TZID identifying the timezone.
955  * 
956  * Sets the default timezone for the calendar, which is used to resolve
957  * DATE and floating DATE-TIME values. 
958  */
959 void
960 e_cal_backend_set_default_timezone (ECalBackend *backend, EDataCal *cal, const char *tzid)
961 {
962         g_return_if_fail (backend != NULL);
963         g_return_if_fail (E_IS_CAL_BACKEND (backend));
964         g_return_if_fail (tzid != NULL);
965
966         g_assert (CLASS (backend)->set_default_timezone != NULL);
967         (* CLASS (backend)->set_default_timezone) (backend, cal, tzid);
968 }
969
970 /**
971  * e_cal_backend_add_timezone
972  * @backend: A calendar backend.
973  * @cal: An #EDataCal object.
974  * @tzobj: The timezone object, in a string.
975  *
976  * Add a timezone object to the given backend.
977  */
978 void
979 e_cal_backend_add_timezone (ECalBackend *backend, EDataCal *cal, const char *tzobj)
980 {
981         g_return_if_fail (E_IS_CAL_BACKEND (backend));
982         g_return_if_fail (tzobj != NULL);
983         g_return_if_fail (CLASS (backend)->add_timezone != NULL);
984
985         (* CLASS (backend)->add_timezone) (backend, cal, tzobj);
986 }
987
988 /**
989  * e_cal_backend_internal_get_default_timezone:
990  * @backend: A calendar backend.
991  *
992  * Calls the internal_get_default_timezone method on the given backend.
993  */
994 icaltimezone *
995 e_cal_backend_internal_get_default_timezone (ECalBackend *backend)
996 {
997         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
998         g_return_val_if_fail (CLASS (backend)->internal_get_default_timezone != NULL, NULL);
999
1000         return (* CLASS (backend)->internal_get_default_timezone) (backend);
1001 }
1002
1003 /**
1004  * e_cal_backend_internal_get_timezone:
1005  * @backend: A calendar backend.
1006  * @tzid: ID of the timezone to get.
1007  *
1008  * Calls the internal_get_timezone method on the given backend.
1009  */
1010 icaltimezone *
1011 e_cal_backend_internal_get_timezone (ECalBackend *backend, const char *tzid)
1012 {
1013         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1014         g_return_val_if_fail (tzid != NULL, NULL);
1015         g_return_val_if_fail (CLASS (backend)->internal_get_timezone != NULL, NULL);
1016
1017         return (* CLASS (backend)->internal_get_timezone) (backend, tzid);
1018 }
1019
1020 /**
1021  * e_cal_backend_set_notification_proxy:
1022  * @backend: A calendar backend.
1023  * @proxy: The calendar backend to act as notification proxy.
1024  *
1025  * Sets the backend that will act as notification proxy for the given backend.
1026  */
1027 void
1028 e_cal_backend_set_notification_proxy (ECalBackend *backend, ECalBackend *proxy)
1029 {
1030         ECalBackendPrivate *priv;
1031
1032         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1033
1034         priv = backend->priv;
1035
1036         priv->notification_proxy = proxy;
1037 }
1038
1039 /**
1040  * e_cal_backend_notify_object_created:
1041  * @backend: A calendar backend.
1042  * @calobj: iCalendar representation of new object
1043  *
1044  * Notifies each of the backend's listeners about a new object.
1045  *
1046  * #e_data_cal_notify_object_created() calls this for you. You only need to
1047  * call e_cal_backend_notify_object_created() yourself to report objects
1048  * created by non-EDS clients.
1049  **/
1050 void
1051 e_cal_backend_notify_object_created (ECalBackend *backend, const char *calobj)
1052 {
1053         ECalBackendPrivate *priv;
1054         EList *queries;
1055         EIterator *iter;
1056         EDataCalView *query;
1057
1058         priv = backend->priv;
1059
1060         if (priv->notification_proxy) {
1061                 e_cal_backend_notify_object_created (priv->notification_proxy, calobj);
1062                 return;
1063         }
1064
1065         queries = e_cal_backend_get_queries (backend);
1066         iter = e_list_get_iterator (queries);
1067
1068         while (e_iterator_is_valid (iter)) {
1069                 query = QUERY (e_iterator_get (iter));
1070
1071                 bonobo_object_ref (query);
1072                 if (e_data_cal_view_object_matches (query, calobj))             
1073                         e_data_cal_view_notify_objects_added_1 (query, calobj);
1074                 bonobo_object_unref (query);
1075
1076                 e_iterator_next (iter);
1077         }
1078         g_object_unref (iter);
1079 }
1080
1081 static void
1082 match_query_and_notify (EDataCalView *query, const char *old_object, const char *object)
1083 {
1084         gboolean old_match, new_match;
1085
1086         old_match = e_data_cal_view_object_matches (query, old_object);
1087         new_match = e_data_cal_view_object_matches (query, object);
1088         if (old_match && new_match)
1089                 e_data_cal_view_notify_objects_modified_1 (query, object);
1090         else if (new_match)
1091                 e_data_cal_view_notify_objects_added_1 (query, object);
1092         else if (old_match) {
1093                 icalcomponent *icalcomp;
1094         
1095                 icalcomp = icalcomponent_new_from_string ((char *) old_object);
1096                 if (icalcomp) {
1097                         e_data_cal_view_notify_objects_removed_1 (query, icalcomponent_get_uid (icalcomp));
1098                         icalcomponent_free (icalcomp);
1099                 }
1100         }
1101 }
1102
1103 /**
1104  * e_cal_backend_notify_view_progress:
1105  * @backend: A calendar backend.
1106  * @message: the UID of the removed object
1107  * @percent: percentage of the objects loaded in the view
1108  *
1109  * Notifies each of the backend's listeners about the view_progress in downloading the items.
1110  **/
1111 void
1112 e_cal_backend_notify_view_progress (ECalBackend *backend, const char *message, int percent)
1113 {
1114         ECalBackendPrivate *priv;
1115         EList *queries;
1116         EIterator *iter;
1117         EDataCalView *query;
1118
1119         priv = backend->priv;
1120
1121         if (priv->notification_proxy) {
1122                 e_cal_backend_notify_view_progress (priv->notification_proxy, message, percent);
1123                 return;
1124         }
1125
1126         queries = e_cal_backend_get_queries (backend);
1127         iter = e_list_get_iterator (queries);
1128
1129         while (e_iterator_is_valid (iter)) {
1130                 query = QUERY (e_iterator_get (iter));
1131
1132                 bonobo_object_ref (query);
1133
1134                 e_data_cal_view_notify_progress (query, message, percent);
1135
1136                 bonobo_object_unref (query);
1137
1138                 e_iterator_next (iter);
1139         }
1140         g_object_unref (iter);
1141 }
1142
1143 /**
1144  * e_cal_backend_notify_view_done:
1145  * @backend: A calendar backend.
1146  * @status: returns the status once the view is fully populated.
1147  *
1148  * Notifies each of the backend's listeners about the view_done in downloading the items.
1149  **/
1150 void
1151 e_cal_backend_notify_view_done (ECalBackend *backend, GNOME_Evolution_Calendar_CallStatus status)
1152 {
1153         ECalBackendPrivate *priv;
1154         EList *queries;
1155         EIterator *iter;
1156         EDataCalView *query;
1157
1158         priv = backend->priv;
1159
1160         if (priv->notification_proxy) {
1161                 e_cal_backend_notify_view_done (priv->notification_proxy, status);
1162                 return;
1163         }
1164
1165         queries = e_cal_backend_get_queries (backend);
1166         iter = e_list_get_iterator (queries);
1167
1168         while (e_iterator_is_valid (iter)) {
1169                 query = QUERY (e_iterator_get (iter));
1170
1171                 bonobo_object_ref (query);
1172
1173                 e_data_cal_view_notify_done (query, status);
1174
1175                 bonobo_object_unref (query);
1176
1177                 e_iterator_next (iter);
1178         }
1179         g_object_unref (iter);
1180 }
1181
1182 /**
1183  * e_cal_backend_notify_object_modified:
1184  * @backend: A calendar backend.
1185  * @old_object: iCalendar representation of the original form of the object
1186  * @object: iCalendar representation of the new form of the object
1187  *
1188  * Notifies each of the backend's listeners about a modified object.
1189  *
1190  * #e_data_cal_notify_object_modified() calls this for you. You only need to
1191  * call e_cal_backend_notify_object_modified() yourself to report objects
1192  * modified by non-EDS clients.
1193  **/
1194 void
1195 e_cal_backend_notify_object_modified (ECalBackend *backend, 
1196                                       const char *old_object, const char *object)
1197 {
1198         ECalBackendPrivate *priv;
1199         EList *queries;
1200         EIterator *iter;
1201         EDataCalView *query;
1202
1203         priv = backend->priv;
1204
1205         if (priv->notification_proxy) {
1206                 e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object);
1207                 return;
1208         }
1209
1210         queries = e_cal_backend_get_queries (backend);
1211         iter = e_list_get_iterator (queries);
1212
1213         while (e_iterator_is_valid (iter)) {
1214                 query = QUERY (e_iterator_get (iter));
1215
1216                 bonobo_object_ref (query);
1217                 match_query_and_notify (query, old_object, object);
1218                 bonobo_object_unref (query);
1219
1220                 e_iterator_next (iter);
1221         }
1222         g_object_unref (iter);
1223 }
1224
1225 /**
1226  * e_cal_backend_notify_object_removed:
1227  * @backend: A calendar backend.
1228  * @uid: the UID of the removed object
1229  * @old_object: iCalendar representation of the removed object
1230  * @new_object: iCalendar representation of the object after the removal. This
1231  * only applies to recurrent appointments that had an instance removed. In that
1232  * case, this function notifies a modification instead of a removal.
1233  *
1234  * Notifies each of the backend's listeners about a removed object.
1235  *
1236  * e_data_cal_notify_object_removed() calls this for you. You only need to
1237  * call e_cal_backend_notify_object_removed() yourself to report objects
1238  * removed by non-EDS clients.
1239  **/
1240 void
1241 e_cal_backend_notify_object_removed (ECalBackend *backend, const char *uid,
1242                                      const char *old_object, const char *object)
1243 {
1244         ECalBackendPrivate *priv;
1245         EList *queries;
1246         EIterator *iter;
1247         EDataCalView *query;
1248
1249         priv = backend->priv;
1250
1251         if (priv->notification_proxy) {
1252                 e_cal_backend_notify_object_removed (priv->notification_proxy, uid, old_object, object);
1253                 return;
1254         }
1255
1256         queries = e_cal_backend_get_queries (backend);
1257         iter = e_list_get_iterator (queries);
1258
1259         while (e_iterator_is_valid (iter)) {
1260                 query = QUERY (e_iterator_get (iter));
1261
1262                 bonobo_object_ref (query);
1263
1264                 if (object == NULL) {
1265                         /* if object == NULL, it means the object has been completely
1266                            removed from the backend */
1267                         if (e_data_cal_view_object_matches (query, old_object))
1268                                 e_data_cal_view_notify_objects_removed_1 (query, uid);
1269                 } else
1270                         match_query_and_notify (query, old_object, object);
1271
1272                 bonobo_object_unref (query);
1273
1274                 e_iterator_next (iter);
1275         }
1276         g_object_unref (iter);
1277 }
1278
1279 /**
1280  * e_cal_backend_notify_mode:
1281  * @backend: A calendar backend.
1282  * @status: Status of the mode set
1283  * @mode: the current mode
1284  *
1285  * Notifies each of the backend's listeners about the results of a
1286  * setMode call.
1287  **/
1288 void
1289 e_cal_backend_notify_mode (ECalBackend *backend,
1290                            GNOME_Evolution_Calendar_CalListener_SetModeStatus status, 
1291                            GNOME_Evolution_Calendar_CalMode mode)
1292 {
1293         ECalBackendPrivate *priv = backend->priv;
1294         GList *l;
1295
1296         if (priv->notification_proxy) {
1297                 e_cal_backend_notify_mode (priv->notification_proxy, status, mode);
1298                 return;
1299         }
1300
1301         for (l = priv->clients; l; l = l->next)
1302                 e_data_cal_notify_mode (l->data, status, mode);
1303 }
1304
1305 /**
1306  * e_cal_backend_notify_auth_required:
1307  * @backend: A calendar backend.
1308  *
1309  * Notifies each of the backend's listeners that authentication is required to
1310  * open the calendar.
1311  */
1312 void
1313 e_cal_backend_notify_auth_required (ECalBackend *backend)
1314 {
1315         ECalBackendPrivate *priv = backend->priv;
1316         GList *l;
1317                                                                                                                              
1318         for (l = priv->clients; l; l = l->next)
1319                 e_data_cal_notify_auth_required (l->data);
1320 }
1321
1322 /**
1323  * e_cal_backend_notify_error:
1324  * @backend: A calendar backend.
1325  * @message: Error message
1326  *
1327  * Notifies each of the backend's listeners about an error
1328  **/
1329 void
1330 e_cal_backend_notify_error (ECalBackend *backend, const char *message)
1331 {
1332         ECalBackendPrivate *priv = backend->priv;
1333         GList *l;
1334
1335         if (priv->notification_proxy) {
1336                 e_cal_backend_notify_error (priv->notification_proxy, message);
1337                 return;
1338         }
1339
1340         for (l = priv->clients; l; l = l->next)
1341                 e_data_cal_notify_error (l->data, message);
1342 }