Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / calendar / libecal / e-cal.c
1 /*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar ecal
3  *
4  * Copyright (C) 2001 Ximian, Inc.
5  * Copyright (C) 2004 Novell, Inc.
6  *
7  * Authors: Federico Mena-Quintero <federico@ximian.com>
8  *          Rodrigo Moya <rodrigo@novell.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 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <pthread.h>
29 #include <string.h>
30 #include <glib/gi18n-lib.h>
31 #include <bonobo-activation/bonobo-activation.h>
32 #include <bonobo/bonobo-exception.h>
33 #include <bonobo/bonobo-main.h>
34
35 #include "libedataserver/e-component-listener.h"
36 #include "libedataserver/e-flag.h"
37 #include "libedataserver/e-url.h"
38 #include "e-cal-marshal.h"
39 #include "e-cal-time-util.h"
40 #include "e-cal-listener.h"
41 #include "e-cal-view-listener.h"
42 #include "e-cal-view-private.h"
43 #include "e-cal.h"
44
45
46 static gboolean
47 open_calendar (ECal *ecal, gboolean only_if_exists, GError **error, ECalendarStatus *status, gboolean needs_auth);
48
49 static gboolean
50 get_read_only (ECal *ecal, gboolean *read_only, GError **error);
51 \f
52
53 typedef struct {
54         EFlag *done;
55         ECalendarStatus status;
56
57         char *uid;
58         GList *list;
59         GSList *slist;
60         gboolean bool;
61         char *string;
62
63         ECalView *query;
64         ECalViewListener *listener;
65 } ECalendarOp;
66
67 /* Private part of the ECal structure */
68 struct _ECalPrivate {
69         /* Load state to avoid multiple loads */
70         ECalLoadState load_state;
71
72         /* URI of the calendar that is being loaded or is already loaded, or
73          * NULL if we are not loaded.
74          */
75         ESource *source;
76         char *uri;
77         ECalSourceType type;
78         
79         ECalendarOp *current_op;
80
81         GMutex *mutex;
82         
83         /* Email address associated with this calendar, or NULL */
84         char *cal_address;
85         char *alarm_email_address;
86         char *ldap_attribute;
87
88         /* Scheduling info */
89         char *capabilities;
90         
91         int mode;
92         
93         gboolean read_only;
94         
95         /* The calendar factories we are contacting */
96         GList *factories;
97
98         /* Our calendar listener implementation */
99         ECalListener *listener;
100
101         /* The calendar ecal interface object we are contacting */
102         GNOME_Evolution_Calendar_Cal cal;
103
104         /* The authentication function */
105         ECalAuthFunc auth_func;
106         gpointer auth_user_data;
107
108         /* A cache of timezones retrieved from the server, to avoid getting
109            them repeatedly for each get_object() call. */
110         GHashTable *timezones;
111
112         /* The default timezone to use to resolve DATE and floating DATE-TIME
113            values. */
114         icaltimezone *default_zone;
115
116         /* The component listener to keep track of the lifetime of backends */
117         EComponentListener *comp_listener;
118
119         char *local_attachment_store;
120 };
121
122 \f
123
124 /* Signal IDs */
125 enum {
126         CAL_OPENED,
127         CAL_SET_MODE,
128         BACKEND_ERROR,
129         BACKEND_DIED,
130         LAST_SIGNAL
131 };
132
133 static guint e_cal_signals[LAST_SIGNAL];
134
135 static GObjectClass *parent_class;
136
137 #ifdef __PRETTY_FUNCTION__
138 #define e_return_error_if_fail(expr,error_code) G_STMT_START{           \
139      if G_LIKELY(expr) { } else                                         \
140        {                                                                \
141          g_log (G_LOG_DOMAIN,                                           \
142                 G_LOG_LEVEL_CRITICAL,                                   \
143                 "file %s: line %d (%s): assertion `%s' failed",         \
144                 __FILE__,                                               \
145                 __LINE__,                                               \
146                 __PRETTY_FUNCTION__,                                    \
147                 #expr);                                                 \
148          g_set_error (error, E_CALENDAR_ERROR, (error_code),                \
149                 "file %s: line %d (%s): assertion `%s' failed",         \
150                 __FILE__,                                               \
151                 __LINE__,                                               \
152                 __PRETTY_FUNCTION__,                                    \
153                 #expr);                                                 \
154          return FALSE;                                                  \
155        };                               }G_STMT_END
156 #else
157 #define e_return_error_if_fail(expr,error_code) G_STMT_START{           \
158      if G_LIKELY(expr) { } else                                         \
159        {                                                                \
160          g_log (G_LOG_DOMAIN,                                           \
161                 G_LOG_LEVEL_CRITICAL,                                   \
162                 "file %s: line %d: assertion `%s' failed",              \
163                 __FILE__,                                               \
164                 __LINE__,                                               \
165                 #expr);                                                 \
166          g_set_error (error, E_CALENDAR_ERROR, (error_code),                \
167                 "file %s: line %d: assertion `%s' failed",              \
168                 __FILE__,                                               \
169                 __LINE__,                                               \
170                 #expr);                                                 \
171          return FALSE;                                                  \
172        };                               }G_STMT_END
173 #endif
174
175 #define E_CALENDAR_CHECK_STATUS(status,error) G_STMT_START{             \
176         if ((status) == E_CALENDAR_STATUS_OK) {                         \
177                 return TRUE;                                            \
178         }                                                               \
179         else {                                                          \
180                 const char *msg;                                        \
181                 msg = e_cal_get_error_message ((status));          \
182                 g_set_error ((error), E_CALENDAR_ERROR, (status), msg, (status));       \
183                 return FALSE;                                           \
184         }                               }G_STMT_END
185
186 \f
187
188 /* Error quark */
189 GQuark
190 e_calendar_error_quark (void)
191 {
192         static GQuark q = 0;
193         if (q == 0)
194                 q = g_quark_from_static_string ("e-calendar-error-quark");
195
196         return q;
197 }
198
199 /**
200  * e_cal_source_type_enum_get_type:
201  *
202  * Registers the #ECalSourceTypeEnum type with glib.
203  *
204  * Return value: the ID of the #ECalSourceTypeEnum type.
205  */
206 GType
207 e_cal_source_type_enum_get_type (void)
208 {
209         static GType e_cal_source_type_enum_type = 0;
210
211         if (!e_cal_source_type_enum_type) {
212                 static GEnumValue values [] = {
213                         { E_CAL_SOURCE_TYPE_EVENT, "Event", NULL},
214                         { E_CAL_SOURCE_TYPE_TODO, "ToDo", NULL},
215                         { E_CAL_SOURCE_TYPE_JOURNAL, "Journal", NULL},
216                         { E_CAL_SOURCE_TYPE_LAST, "Invalid", NULL},
217                         { -1, NULL, NULL}
218                 };
219
220                 e_cal_source_type_enum_type =
221                         g_enum_register_static ("ECalSourceTypeEnum", values);
222         }
223
224         return e_cal_source_type_enum_type;
225 }
226
227 /**
228  * e_cal_set_mode_status_enum_get_type:
229  *
230  * Registers the #ECalSetModeStatusEnum type with glib.
231  *
232  * Return value: the ID of the #ECalSetModeStatusEnum type.
233  */
234 GType
235 e_cal_set_mode_status_enum_get_type (void)
236 {
237         static GType e_cal_set_mode_status_enum_type = 0;
238
239         if (!e_cal_set_mode_status_enum_type) {
240                 static GEnumValue values [] = {
241                         { E_CAL_SET_MODE_SUCCESS,          "ECalSetModeSuccess",         "success"     },
242                         { E_CAL_SET_MODE_ERROR,            "ECalSetModeError",           "error"       },
243                         { E_CAL_SET_MODE_NOT_SUPPORTED,    "ECalSetModeNotSupported",    "unsupported" },
244                         { -1,                                   NULL,                              NULL          }
245                 };
246
247                 e_cal_set_mode_status_enum_type =
248                         g_enum_register_static ("ECalSetModeStatusEnum", values);
249         }
250
251         return e_cal_set_mode_status_enum_type;
252 }
253
254 /**
255  * cal_mode_enum_get_type:
256  *
257  * Registers the #CalModeEnum type with glib.
258  *
259  * Return value: the ID of the #CalModeEnum type.
260  */
261 GType
262 cal_mode_enum_get_type (void)
263 {
264         static GType cal_mode_enum_type = 0;
265
266         if (!cal_mode_enum_type) {
267                 static GEnumValue values [] = {
268                         { CAL_MODE_INVALID,                     "CalModeInvalid",                  "invalid" },
269                         { CAL_MODE_LOCAL,                       "CalModeLocal",                    "local"   },
270                         { CAL_MODE_REMOTE,                      "CalModeRemote",                   "remote"  },
271                         { CAL_MODE_ANY,                         "CalModeAny",                      "any"     },
272                         { -1,                                   NULL,                              NULL      }
273                 };
274
275                 cal_mode_enum_type = g_enum_register_static ("CalModeEnum", values);
276         }
277
278         return cal_mode_enum_type;
279 }
280
281
282
283 static GNOME_Evolution_Calendar_CalObjType
284 convert_type (ECalSourceType type) 
285 {
286         switch (type){
287         case E_CAL_SOURCE_TYPE_EVENT:
288                 return GNOME_Evolution_Calendar_TYPE_EVENT;
289         case E_CAL_SOURCE_TYPE_TODO:
290                 return GNOME_Evolution_Calendar_TYPE_TODO;
291         case E_CAL_SOURCE_TYPE_JOURNAL:
292                 return GNOME_Evolution_Calendar_TYPE_JOURNAL;
293         default:
294                 return GNOME_Evolution_Calendar_TYPE_ANY;
295         }
296         
297         return GNOME_Evolution_Calendar_TYPE_ANY;
298 }
299
300 /* EBookOp calls */
301
302 static ECalendarOp*
303 e_calendar_new_op (ECal *ecal)
304 {
305         ECalendarOp *op = g_new0 (ECalendarOp, 1);
306
307         op->done = e_flag_new ();
308
309         ecal->priv->current_op = op;
310
311         return op;
312 }
313
314 static ECalendarOp*
315 e_calendar_get_op (ECal *ecal)
316 {
317         if (!ecal->priv->current_op) {
318                 g_warning (G_STRLOC ": Unexpected response");
319                 return NULL;
320         }
321                 
322         return ecal->priv->current_op;
323 }
324
325 static void
326 e_calendar_free_op (ECalendarOp *op)
327 {
328         /* XXX more stuff here */
329         e_flag_free (op->done);
330         g_free (op);
331 }
332
333 static void
334 e_calendar_remove_op (ECal *ecal, ECalendarOp *op)
335 {
336         if (ecal->priv->current_op != op)
337                 g_warning (G_STRLOC ": Cannot remove op, it's not current");
338
339         ecal->priv->current_op = NULL;
340 }
341
342 /* Gets rid of the factories that a ecal knows about */
343 static void
344 destroy_factories (ECal *ecal)
345 {
346         ECalPrivate *priv;
347         CORBA_Object factory;
348         CORBA_Environment ev;
349         int result;
350         GList *f;
351
352         priv = ecal->priv;
353
354         CORBA_exception_init (&ev);
355
356         for (f = priv->factories; f; f = f->next) {
357                 factory = f->data;
358
359                 result = CORBA_Object_is_nil (factory, &ev);
360                 if (BONOBO_EX (&ev)) {
361                         g_message (G_STRLOC ": could not see if a factory was nil");
362                         CORBA_exception_free (&ev);
363
364                         continue;
365                 }
366
367                 if (result)
368                         continue;
369
370                 CORBA_Object_release (factory, &ev);
371                 if (BONOBO_EX (&ev)) {
372                         g_message (G_STRLOC ": could not release a factory");
373                         CORBA_exception_free (&ev);
374                 }
375         }
376
377         g_list_free (priv->factories);
378         priv->factories = NULL;
379 }
380
381 /* Gets rid of the calendar ecal interface object that a ecal knows about */
382 static void
383 destroy_cal (ECal *ecal)
384 {
385         ECalPrivate *priv;
386         CORBA_Environment ev;
387         int result;
388
389         priv = ecal->priv;
390
391         CORBA_exception_init (&ev);
392         result = CORBA_Object_is_nil (priv->cal, &ev);
393         if (BONOBO_EX (&ev)) {
394                 g_message (G_STRLOC ": could not see if the "
395                            "calendar ecal interface object was nil");
396                 priv->cal = CORBA_OBJECT_NIL;
397                 CORBA_exception_free (&ev);
398                 return;
399         }
400         CORBA_exception_free (&ev);
401
402         if (result)
403                 return;
404
405         bonobo_object_release_unref (priv->cal, NULL);  
406         priv->cal = CORBA_OBJECT_NIL;
407
408 }
409
410 static void
411 free_timezone (gpointer key, gpointer value, gpointer data)
412 {
413         /* Note that the key comes from within the icaltimezone value, so we
414            don't free that. */
415         icaltimezone_free (value, TRUE);
416 }
417
418 \f
419
420 static void
421 backend_died_cb (EComponentListener *cl, gpointer user_data)
422 {
423         ECalPrivate *priv;
424         ECal *ecal = (ECal *) user_data;
425
426         priv = ecal->priv;
427         priv->load_state = E_CAL_LOAD_NOT_LOADED;
428         g_signal_emit (G_OBJECT (ecal), e_cal_signals[BACKEND_DIED], 0);
429 }
430
431 /* Signal handlers for the listener's signals */
432 /* Handle the cal_opened notification from the listener */
433
434 static void
435 cal_read_only_cb (ECalListener *listener, ECalendarStatus status, gboolean read_only, gpointer data)
436 {
437         ECal *ecal = data;
438         ECalendarOp *op;
439
440         op = e_calendar_get_op (ecal);
441
442         if (op == NULL || !op->bool) {
443                 ecal->priv->read_only = read_only; 
444                 return;
445         }
446
447         op->status = status;
448         op->bool = read_only;
449
450         e_flag_set (op->done);
451 }
452
453 static void
454 cal_cal_address_cb (ECalListener *listener, ECalendarStatus status, const char *address, gpointer data)
455 {
456         ECal *ecal = data;
457         ECalendarOp *op;
458
459         op = e_calendar_get_op (ecal);
460
461         if (ecal->priv->cal_address) {
462                 g_free (ecal->priv->cal_address);
463                 ecal->priv->cal_address = NULL;
464         }
465
466         ecal->priv->cal_address = g_strdup (address);
467
468         if (op == NULL) {
469                 return;
470         }
471
472         op->status = status;
473         op->string = g_strdup (address);
474
475         e_flag_set (op->done);
476 }
477
478 static void
479 cal_alarm_address_cb (ECalListener *listener, ECalendarStatus status, const char *address, gpointer data)
480 {
481         ECal *ecal = data;
482         ECalendarOp *op;
483
484         op = e_calendar_get_op (ecal);
485
486         if (op == NULL) {
487                 g_warning (G_STRLOC ": Cannot find operation ");
488                 return;
489         }
490
491         op->status = status;
492         op->string = g_strdup (address);
493
494         e_flag_set (op->done);
495 }
496
497 static void
498 cal_ldap_attribute_cb (ECalListener *listener, ECalendarStatus status, const char *attribute, gpointer data)
499 {
500         ECal *ecal = data;
501         ECalendarOp *op;
502
503         op = e_calendar_get_op (ecal);
504
505         if (op == NULL) {
506                 g_warning (G_STRLOC ": Cannot find operation ");
507                 return;
508         }
509
510         op->status = status;
511         op->string = g_strdup (attribute);
512
513         e_flag_set (op->done);
514 }
515
516 static void
517 cal_static_capabilities_cb (ECalListener *listener, ECalendarStatus status, const char *capabilities, gpointer data)
518 {
519         ECal *ecal = data;
520         ECalendarOp *op;
521
522         op = e_calendar_get_op (ecal);
523
524         if (op == NULL) {
525                 g_warning (G_STRLOC ": Cannot find operation ");
526                 return;
527         }
528
529         op->status = status;
530         op->string = g_strdup (capabilities);
531
532         e_flag_set (op->done);
533 }
534
535 static void
536 cal_opened_cb (ECalListener *listener, ECalendarStatus status, gpointer data)
537 {
538         ECal *ecal = data;
539         ECalendarOp *op;
540
541         op = e_calendar_get_op (ecal);
542
543         if (op == NULL) {
544                 g_warning (G_STRLOC ": Cannot find operation ");
545                 return;
546         }
547
548         op->status = status;
549
550         e_flag_set (op->done);
551 }
552
553 static void
554 cal_removed_cb (ECalListener *listener, ECalendarStatus status, gpointer data)
555 {
556         ECal *ecal = data;
557         ECalendarOp *op;
558
559         op = e_calendar_get_op (ecal);
560
561         if (op == NULL) {
562                 g_warning (G_STRLOC ": Cannot find operation ");
563                 return;
564         }
565
566         op->status = status;
567
568         e_flag_set (op->done);
569 }
570
571 static void
572 cal_object_created_cb (ECalListener *listener, ECalendarStatus status, const char *uid, gpointer data)
573 {
574         ECal *ecal = data;
575         ECalendarOp *op;
576
577         op = e_calendar_get_op (ecal);
578
579         if (op == NULL) {
580                 g_warning (G_STRLOC ": Cannot find operation ");
581                 return;
582         }
583
584         op->status = status;
585         op->uid = g_strdup (uid);
586         
587         e_flag_set (op->done);
588 }
589
590 static void
591 cal_object_modified_cb (ECalListener *listener, ECalendarStatus status, gpointer data)
592 {
593         ECal *ecal = data;
594         ECalendarOp *op;
595
596         op = e_calendar_get_op (ecal);
597
598         if (op == NULL) {
599                 g_warning (G_STRLOC ": Cannot find operation ");
600                 return;
601         }
602
603         op->status = status;
604
605         e_flag_set (op->done);
606 }
607
608 static void
609 cal_object_removed_cb (ECalListener *listener, ECalendarStatus status, gpointer data)
610 {
611         ECal *ecal = data;
612         ECalendarOp *op;
613
614         op = e_calendar_get_op (ecal);
615
616         if (op == NULL) {
617                 g_warning (G_STRLOC ": Cannot find operation ");
618                 return;
619         }
620
621         op->status = status;
622
623         e_flag_set (op->done);
624 }
625
626 static void
627 cal_alarm_discarded_cb (ECalListener *listener, ECalendarStatus status, gpointer data)
628 {
629         ECal *ecal = data;
630         ECalendarOp *op;
631
632         op = e_calendar_get_op (ecal);
633
634         if (op == NULL) {
635                 g_warning (G_STRLOC ": Cannot find operation ");
636                 return;
637         }
638
639         op->status = status;
640
641         e_flag_set (op->done);
642 }
643
644 static void
645 cal_objects_received_cb (ECalListener *listener, ECalendarStatus status, gpointer data)
646 {
647         ECal *ecal = data;
648         ECalendarOp *op;
649
650         op = e_calendar_get_op (ecal);
651
652         if (op == NULL) {
653                 g_warning (G_STRLOC ": Cannot find operation ");
654                 return;
655         }
656
657         op->status = status;
658
659         e_flag_set (op->done);
660 }
661
662 static void
663 cal_objects_sent_cb (ECalListener *listener, ECalendarStatus status, GList *users, const char *object, gpointer data)
664 {
665         ECal *ecal = data;
666         ECalendarOp *op;
667         GList *l;
668
669         op = e_calendar_get_op (ecal);
670
671         if (op == NULL) {
672                 g_warning (G_STRLOC ": Cannot find operation ");
673                 return;
674         }
675
676         op->status = status;
677         op->list = g_list_copy (users);
678         op->string = g_strdup (object);
679
680         for (l = op->list; l; l = l->next)
681                 l->data = g_strdup (l->data);
682
683         e_flag_set (op->done);
684 }
685
686 static void
687 cal_default_object_requested_cb (ECalListener *listener, ECalendarStatus status, const char *object, gpointer data)
688 {
689         ECal *ecal = data;
690         ECalendarOp *op;
691
692         op = e_calendar_get_op (ecal);
693
694         if (op == NULL) {
695                 g_warning (G_STRLOC ": Cannot find operation ");
696                 return;
697         }
698
699         op->status = status;
700         op->string = g_strdup (object);
701         
702         e_flag_set (op->done);
703 }
704
705 static void
706 cal_object_requested_cb (ECalListener *listener, ECalendarStatus status, const char *object, gpointer data)
707 {
708         ECal *ecal = data;
709         ECalendarOp *op;
710
711         op = e_calendar_get_op (ecal);
712
713         if (op == NULL) {
714                 g_warning (G_STRLOC ": Cannot find operation ");
715                 return;
716         }
717
718         op->status = status;
719         op->string = g_strdup (object);
720         
721         e_flag_set (op->done);
722 }
723
724 static void
725 cal_object_list_cb (ECalListener *listener, ECalendarStatus status, GList *objects, gpointer data)
726 {
727         ECal *ecal = data;
728         ECalendarOp *op;
729         GList *l;
730         
731         op = e_calendar_get_op (ecal);
732
733         if (op == NULL) {
734                 g_warning (G_STRLOC ": Cannot find operation ");
735                 return;
736         }
737
738         op->status = status;
739         op->list = g_list_copy (objects);
740         
741         for (l = op->list; l; l = l->next)
742                 l->data = icalcomponent_new_clone (l->data);
743         
744         e_flag_set (op->done);
745 }
746
747 static void
748 cal_attachment_list_cb (ECalListener *listener, ECalendarStatus status, GSList *attachments, gpointer data)
749 {
750         ECal *ecal = data;
751         ECalendarOp *op;
752         
753         op = e_calendar_get_op (ecal);
754
755         if (op == NULL) {
756                 g_warning (G_STRLOC ": Cannot find operation ");
757                 return;
758         }
759
760         op->status = status;
761         op->slist = g_slist_copy (attachments);
762         
763         e_flag_set (op->done);
764 }
765
766 static void
767 cal_get_timezone_cb (ECalListener *listener, ECalendarStatus status, const char *object, gpointer data)
768 {
769         ECal *ecal = data;
770         ECalendarOp *op;
771
772         op = e_calendar_get_op (ecal);
773
774         if (op == NULL) {
775                 g_warning (G_STRLOC ": Cannot find operation ");
776                 return;
777         }
778
779         op->status = status;
780         op->string = g_strdup (object);
781
782         e_flag_set (op->done);
783 }
784
785 static void
786 cal_add_timezone_cb (ECalListener *listener, ECalendarStatus status, const char *tzid, gpointer data)
787 {
788         ECal *ecal = data;
789         ECalendarOp *op;
790
791         op = e_calendar_get_op (ecal);
792
793         if (op == NULL) {
794                 g_warning (G_STRLOC ": Cannot find operation ");
795                 return;
796         }
797
798         op->status = status;
799         op->uid = g_strdup (tzid);
800
801         e_flag_set (op->done);
802 }
803
804 static void
805 cal_set_default_timezone_cb (ECalListener *listener, ECalendarStatus status, gpointer data)
806 {
807         ECal *ecal = data;
808         ECalendarOp *op;
809
810         op = e_calendar_get_op (ecal);
811
812         if (op == NULL) {
813                 g_warning (G_STRLOC ": Cannot find operation ");
814                 return;
815         }
816
817         op->status = status;
818
819         e_flag_set (op->done);
820 }
821
822 static void
823 cal_get_changes_cb (ECalListener *listener, ECalendarStatus status, GList *changes, gpointer data)
824 {
825         ECal *ecal = data;
826         ECalendarOp *op;
827         GList *l;
828         
829         op = e_calendar_get_op (ecal);
830
831         if (op == NULL) {
832                 g_warning (G_STRLOC ": Cannot find operation ");
833                 return;
834         }
835
836         op->status = status;
837         op->list = g_list_copy (changes);
838
839         for (l = op->list; l; l = l->next) {
840                 ECalChange *ccc = l->data, *new_ccc;
841
842                 new_ccc = g_new (ECalChange, 1);
843                 new_ccc->comp = e_cal_component_clone (ccc->comp);
844                 new_ccc->type = ccc->type;
845                 
846                 l->data = new_ccc;
847         }
848         
849         e_flag_set (op->done);
850 }
851
852 static void
853 cal_get_free_busy_cb (ECalListener *listener, ECalendarStatus status, GList *freebusy, gpointer data)
854 {
855         ECal *ecal = data;
856         ECalendarOp *op;
857         GList *l;
858         
859         op = e_calendar_get_op (ecal);
860
861         if (op == NULL) {
862                 g_warning (G_STRLOC ": Cannot find operation ");
863                 return;
864         }
865
866         op->status = status;
867         op->list = g_list_copy (freebusy);
868
869         for (l = op->list; l; l = l->next)
870                 l->data = e_cal_component_clone (l->data);
871
872         e_flag_set (op->done);
873 }
874
875 static void
876 cal_query_cb (ECalListener *listener, ECalendarStatus status, GNOME_Evolution_Calendar_CalView query, gpointer data)
877 {
878         ECal *ecal = data;
879         ECalendarOp *op;
880         
881         op = e_calendar_get_op (ecal);
882
883         if (op == NULL) {
884                 g_warning (G_STRLOC ": Cannot find operation ");
885                 return;
886         }
887
888         op->status = status;
889         op->query = e_cal_view_new (query, op->listener, ecal);
890         
891         e_flag_set (op->done);
892 }
893
894 static gboolean  
895 reopen_with_auth (gpointer data)
896 {
897         ECalendarStatus status;
898         
899         open_calendar (E_CAL (data), TRUE, NULL, &status, TRUE);
900         return FALSE;
901 }
902
903 static void
904 auth_required_cb (ECalListener *listener, gpointer data)
905 {
906         g_idle_add (reopen_with_auth, data);    
907
908 }
909
910 /* Handle the cal_set_mode notification from the listener */
911 static void
912 cal_set_mode_cb (ECalListener *listener,
913                  GNOME_Evolution_Calendar_CalListener_SetModeStatus status,
914                  GNOME_Evolution_Calendar_CalMode mode,
915                  gpointer data)
916 {
917         ECal *ecal;
918         ECalPrivate *priv;
919         ECalSetModeStatus ecal_status;
920
921         ecal = E_CAL (data);
922         priv = ecal->priv;
923         priv->mode = mode;
924         ecal_status = E_CAL_SET_MODE_ERROR;
925
926         switch (status) {
927         case GNOME_Evolution_Calendar_CalListener_MODE_SET:
928                 ecal_status = E_CAL_SET_MODE_SUCCESS;
929                 break;          
930         case GNOME_Evolution_Calendar_CalListener_MODE_NOT_SET:
931                 ecal_status = E_CAL_SET_MODE_ERROR;
932                 break;
933         case GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED:
934                 ecal_status = E_CAL_SET_MODE_NOT_SUPPORTED;
935                 break;          
936         default:
937                 g_assert_not_reached ();
938         }
939
940         /* We are *not* inside a signal handler (this is just a simple callback
941          * called from the listener), so there is not a temporary reference to
942          * the ecal object.  We ref() so that we can safely emit our own
943          * signal and clean up.
944          */
945
946         g_object_ref (G_OBJECT (ecal));
947
948         g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_SET_MODE],
949                        0, ecal_status, mode);
950
951         g_object_unref (G_OBJECT (ecal));
952 }
953
954 typedef struct
955 {
956         ECal *ecal;
957         char *message;
958 }  ECalErrorData;
959
960 static gboolean
961 backend_error_idle_cb (gpointer data)
962 {
963         ECalErrorData *error_data = data;
964         
965         g_signal_emit (G_OBJECT (error_data->ecal), e_cal_signals[BACKEND_ERROR], 0, error_data->message);
966
967         g_object_unref (error_data->ecal);
968         g_free (error_data->message);
969         g_free (error_data);
970         
971         return FALSE;
972 }
973
974 /* Handle the error_occurred signal from the listener */
975 static void
976 backend_error_cb (ECalListener *listener, const char *message, gpointer data)
977 {
978         ECalErrorData *error_data;
979         
980         error_data = g_new0 (ECalErrorData, 1);
981
982         error_data->ecal = g_object_ref (data);
983         error_data->message = g_strdup (message);
984
985         g_idle_add (backend_error_idle_cb, error_data);
986 }
987
988 \f
989
990 static gboolean 
991 get_factories (const char *str_uri, GList **factories)
992 {
993         GNOME_Evolution_Calendar_CalFactory factory;
994         Bonobo_ServerInfoList *servers;
995         EUri *uri;
996         char *query;
997         int i;
998
999
1000         /* Determine the protocol and query for factory supporting that */
1001         uri = e_uri_new (str_uri);
1002         if (!uri) {
1003                 g_warning (G_STRLOC ": Invalid uri string");
1004                 
1005                 return FALSE;
1006         }
1007
1008         query = "repo_ids.has ('IDL:GNOME/Evolution/DataServer/CalFactory:" API_VERSION "')";
1009
1010         
1011         servers = bonobo_activation_query (query, NULL, NULL);
1012
1013         e_uri_free (uri);
1014
1015         if (!servers) {
1016                 g_warning (G_STRLOC ": Unable to query for calendar factories");
1017                 
1018                 return FALSE;
1019         }       
1020         
1021         /* Try to activate the servers for the protocol */
1022         for (i = 0; i < servers->_length; i++) {
1023                 const Bonobo_ServerInfo *info;
1024
1025                 info = servers->_buffer + i;
1026
1027 #if 0
1028                 g_message (G_STRLOC ": Activating calendar factory (%s)", info->iid);
1029 #endif
1030                 factory = bonobo_activation_activate_from_id (info->iid, 0, NULL, NULL);
1031                 
1032                 if (factory == CORBA_OBJECT_NIL)
1033                         g_warning (G_STRLOC ": Could not activate calendar factory (%s)", info->iid);
1034                 else
1035                         *factories = g_list_append (*factories, factory);
1036         }
1037
1038         CORBA_free (servers);
1039
1040         return TRUE;
1041 }
1042
1043 /* Object initialization function for the calendar ecal */
1044 static void
1045 e_cal_init (ECal *ecal, ECalClass *klass)
1046 {
1047         ECalPrivate *priv;
1048
1049         priv = g_new0 (ECalPrivate, 1);
1050         ecal->priv = priv;
1051
1052         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1053         priv->uri = NULL;
1054         priv->local_attachment_store = NULL;
1055         priv->mutex = g_mutex_new ();
1056         priv->listener = e_cal_listener_new (cal_set_mode_cb, ecal);
1057
1058         priv->cal_address = NULL;
1059         priv->alarm_email_address = NULL;
1060         priv->ldap_attribute = NULL;
1061         priv->capabilities = FALSE;
1062         priv->factories = NULL;
1063         priv->timezones = g_hash_table_new (g_str_hash, g_str_equal);
1064         priv->default_zone = icaltimezone_get_utc_timezone ();
1065         priv->comp_listener = NULL;
1066
1067         g_signal_connect (G_OBJECT (priv->listener), "read_only", G_CALLBACK (cal_read_only_cb), ecal);
1068         g_signal_connect (G_OBJECT (priv->listener), "cal_address", G_CALLBACK (cal_cal_address_cb), ecal);
1069         g_signal_connect (G_OBJECT (priv->listener), "alarm_address", G_CALLBACK (cal_alarm_address_cb), ecal);
1070         g_signal_connect (G_OBJECT (priv->listener), "ldap_attribute", G_CALLBACK (cal_ldap_attribute_cb), ecal);
1071         g_signal_connect (G_OBJECT (priv->listener), "static_capabilities", G_CALLBACK (cal_static_capabilities_cb), ecal);
1072         g_signal_connect (G_OBJECT (priv->listener), "open", G_CALLBACK (cal_opened_cb), ecal);
1073         g_signal_connect (G_OBJECT (priv->listener), "remove", G_CALLBACK (cal_removed_cb), ecal);
1074         g_signal_connect (G_OBJECT (priv->listener), "create_object", G_CALLBACK (cal_object_created_cb), ecal);
1075         g_signal_connect (G_OBJECT (priv->listener), "modify_object", G_CALLBACK (cal_object_modified_cb), ecal);
1076         g_signal_connect (G_OBJECT (priv->listener), "remove_object", G_CALLBACK (cal_object_removed_cb), ecal);
1077         g_signal_connect (G_OBJECT (priv->listener), "discard_alarm", G_CALLBACK (cal_alarm_discarded_cb), ecal);
1078         g_signal_connect (G_OBJECT (priv->listener), "receive_objects", G_CALLBACK (cal_objects_received_cb), ecal);
1079         g_signal_connect (G_OBJECT (priv->listener), "send_objects", G_CALLBACK (cal_objects_sent_cb), ecal);
1080         g_signal_connect (G_OBJECT (priv->listener), "default_object", G_CALLBACK (cal_default_object_requested_cb), ecal);
1081         g_signal_connect (G_OBJECT (priv->listener), "object", G_CALLBACK (cal_object_requested_cb), ecal);
1082         g_signal_connect (G_OBJECT (priv->listener), "object_list", G_CALLBACK (cal_object_list_cb), ecal);
1083         g_signal_connect (G_OBJECT (priv->listener), "attachment_list", G_CALLBACK (cal_attachment_list_cb), ecal);
1084         g_signal_connect (G_OBJECT (priv->listener), "get_timezone", G_CALLBACK (cal_get_timezone_cb), ecal);
1085         g_signal_connect (G_OBJECT (priv->listener), "add_timezone", G_CALLBACK (cal_add_timezone_cb), ecal);
1086         g_signal_connect (G_OBJECT (priv->listener), "set_default_timezone", G_CALLBACK (cal_set_default_timezone_cb), ecal);
1087         g_signal_connect (G_OBJECT (priv->listener), "get_changes", G_CALLBACK (cal_get_changes_cb), ecal);
1088         g_signal_connect (G_OBJECT (priv->listener), "get_free_busy", G_CALLBACK (cal_get_free_busy_cb), ecal);
1089         g_signal_connect (G_OBJECT (priv->listener), "query", G_CALLBACK (cal_query_cb), ecal);
1090         g_signal_connect (G_OBJECT (priv->listener), "backend_error", G_CALLBACK (backend_error_cb), ecal);
1091         g_signal_connect (G_OBJECT (priv->listener), "auth_required", G_CALLBACK (auth_required_cb), ecal);
1092 }
1093
1094 /* Finalize handler for the calendar ecal */
1095 static void
1096 e_cal_finalize (GObject *object)
1097 {
1098         ECal *ecal;
1099         ECalPrivate *priv;
1100
1101         g_return_if_fail (object != NULL);
1102         g_return_if_fail (E_IS_CAL (object));
1103
1104         ecal = E_CAL (object);
1105         priv = ecal->priv;
1106
1107         if (priv->listener) {
1108                 e_cal_listener_stop_notification (priv->listener);
1109                 bonobo_object_unref (priv->listener);
1110                 priv->listener = NULL;
1111         }
1112
1113         if (priv->comp_listener) {
1114                 g_signal_handlers_disconnect_matched (G_OBJECT (priv->comp_listener),
1115                                                       G_SIGNAL_MATCH_DATA,
1116                                                       0, 0, NULL, NULL,
1117                                                       ecal);
1118                 g_object_unref (G_OBJECT (priv->comp_listener));
1119                 priv->comp_listener = NULL;
1120         }
1121
1122         destroy_factories (ecal);
1123         destroy_cal (ecal);
1124
1125         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1126
1127         if (priv->source) {
1128                 g_object_unref (priv->source);
1129                 priv->source = NULL;
1130         }
1131
1132         if (priv->uri) {
1133                 g_free (priv->uri);
1134                 priv->uri = NULL;
1135         }
1136
1137         if (priv->local_attachment_store) {
1138                 g_free (priv->local_attachment_store);
1139                 priv->local_attachment_store = NULL;
1140         }
1141
1142         if (priv->mutex) {
1143                 g_mutex_free (priv->mutex);
1144                 priv->mutex = NULL;
1145         }
1146         
1147         if (priv->cal_address) {
1148                 g_free (priv->cal_address);
1149                 priv->cal_address = NULL;
1150         }
1151         if (priv->alarm_email_address) {
1152                 g_free (priv->alarm_email_address);
1153                 priv->alarm_email_address = NULL;
1154         }
1155         if (priv->ldap_attribute) {
1156                 g_free (priv->ldap_attribute);
1157                 priv->ldap_attribute = NULL;
1158         }
1159         if (priv->capabilities) {
1160                 g_free (priv->capabilities);
1161                 priv->capabilities = NULL;
1162         }
1163
1164         g_hash_table_foreach (priv->timezones, free_timezone, NULL);
1165         g_hash_table_destroy (priv->timezones);
1166         priv->timezones = NULL;
1167
1168         g_free (priv);
1169         ecal->priv = NULL;
1170
1171         if (G_OBJECT_CLASS (parent_class)->finalize)
1172                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
1173 }
1174
1175 /* Class initialization function for the calendar ecal */
1176 static void
1177 e_cal_class_init (ECalClass *klass)
1178 {
1179         GObjectClass *object_class;
1180
1181         object_class = (GObjectClass *) klass;
1182
1183         parent_class = g_type_class_peek_parent (klass);
1184
1185         e_cal_signals[CAL_OPENED] =
1186                 g_signal_new ("cal_opened",
1187                               G_TYPE_FROM_CLASS (klass),
1188                               G_SIGNAL_RUN_FIRST,
1189                               G_STRUCT_OFFSET (ECalClass, cal_opened),
1190                               NULL, NULL,
1191                               g_cclosure_marshal_VOID__INT,
1192                               G_TYPE_NONE, 1, G_TYPE_INT);
1193         e_cal_signals[CAL_SET_MODE] =
1194                 g_signal_new ("cal_set_mode",
1195                               G_TYPE_FROM_CLASS (klass),
1196                               G_SIGNAL_RUN_FIRST,
1197                               G_STRUCT_OFFSET (ECalClass, cal_set_mode),
1198                               NULL, NULL,
1199                               e_cal_marshal_VOID__ENUM_ENUM,
1200                               G_TYPE_NONE, 2,
1201                               E_CAL_SET_MODE_STATUS_ENUM_TYPE,
1202                               CAL_MODE_ENUM_TYPE);
1203         e_cal_signals[BACKEND_ERROR] =
1204                 g_signal_new ("backend_error",
1205                               G_TYPE_FROM_CLASS (klass),
1206                               G_SIGNAL_RUN_FIRST,
1207                               G_STRUCT_OFFSET (ECalClass, backend_error),
1208                               NULL, NULL,
1209                               g_cclosure_marshal_VOID__STRING,
1210                               G_TYPE_NONE, 1,
1211                               G_TYPE_STRING);
1212         e_cal_signals[BACKEND_DIED] =
1213                 g_signal_new ("backend_died",
1214                               G_TYPE_FROM_CLASS (klass),
1215                               G_SIGNAL_RUN_FIRST,
1216                               G_STRUCT_OFFSET (ECalClass, backend_died),
1217                               NULL, NULL,
1218                               g_cclosure_marshal_VOID__VOID,
1219                               G_TYPE_NONE, 0);
1220
1221         klass->cal_opened = NULL;
1222         klass->backend_died = NULL;
1223
1224         object_class->finalize = e_cal_finalize;
1225 }
1226
1227 /**
1228  * e_cal_get_type:
1229  *
1230  * Registers the #ECal class if necessary, and returns the type ID assigned
1231  * to it.
1232  *
1233  * Return value: The type ID of the #ECal class.
1234  **/
1235 GType
1236 e_cal_get_type (void)
1237 {
1238         static GType e_cal_type = 0;
1239
1240         if (!e_cal_type) {
1241                 static GTypeInfo info = {
1242                         sizeof (ECalClass),
1243                         (GBaseInitFunc) NULL,
1244                         (GBaseFinalizeFunc) NULL,
1245                         (GClassInitFunc) e_cal_class_init,
1246                         NULL, NULL,
1247                         sizeof (ECal),
1248                         0,
1249                         (GInstanceInitFunc) e_cal_init
1250                 };
1251                 e_cal_type = g_type_register_static (G_TYPE_OBJECT, "ECal", &info, 0);
1252         }
1253
1254         return e_cal_type;
1255 }
1256
1257
1258 static gboolean
1259 fetch_corba_cal (ECal *ecal, ESource *source, ECalSourceType type)
1260 {
1261         ECalPrivate *priv;
1262         GList *f;
1263         CORBA_Environment ev;
1264         gchar *source_xml;
1265         gchar *str_uri;
1266         gboolean result = FALSE;
1267         
1268         priv = ecal->priv;
1269         g_return_val_if_fail (priv->load_state == E_CAL_LOAD_NOT_LOADED, FALSE);
1270         g_assert (priv->uri == NULL);
1271         g_return_val_if_fail (source != NULL, FALSE);
1272
1273         str_uri = e_source_get_uri (source);
1274         if (!str_uri)
1275                 return FALSE;
1276
1277         if (!get_factories (str_uri, &priv->factories)) {
1278                 g_free (str_uri);
1279                 return FALSE;
1280         }
1281
1282         g_object_ref (source);
1283         priv->source = source;
1284
1285         priv->uri = g_strdup (str_uri);
1286         priv->type = type;
1287
1288         source_xml = e_source_to_standalone_xml (source);
1289
1290         for (f = priv->factories; f; f = f->next) {
1291                 GNOME_Evolution_Calendar_Cal cal;
1292
1293                 CORBA_exception_init (&ev);
1294
1295                 cal = GNOME_Evolution_Calendar_CalFactory_getCal (f->data, source_xml, convert_type (priv->type),
1296                                                                   BONOBO_OBJREF (priv->listener), &ev);
1297                 if (BONOBO_EX (&ev))
1298                         continue;
1299                 
1300                 priv->cal = cal;
1301
1302                 result = TRUE;
1303                 break;
1304         }
1305
1306         g_free (str_uri);
1307         g_free (source_xml);
1308         return result;
1309 }
1310
1311 /* one-time start up for libecal */
1312 static void
1313 e_cal_activate ()
1314 {
1315         static GStaticMutex e_cal_lock = G_STATIC_MUTEX_INIT;
1316         static gboolean activated = FALSE;
1317         
1318         g_static_mutex_lock (&e_cal_lock);
1319         if (!activated) {
1320                 activated = TRUE;
1321                 
1322                 if (!bonobo_is_initialized ())
1323                         bonobo_init (NULL, NULL);
1324         }
1325         g_static_mutex_unlock (&e_cal_lock);
1326 }
1327
1328
1329 /* TODO - For now, the policy of where each backend serializes its
1330  * attachment data is hardcoded below. Should this end up as a
1331  * gconf key set during the account creation  and fetched
1332  * from eds???
1333  */
1334 static void
1335 set_local_attachment_store (ECal *ecal)
1336 {
1337         ECalPrivate *priv;
1338         char *mangled_uri;
1339         int i;
1340
1341         priv = ecal->priv;
1342         mangled_uri = g_strdup (priv->uri);
1343         /* mangle the URI to not contain invalid characters */
1344         for (i = 0; i < strlen (mangled_uri); i++) {
1345                 switch (mangled_uri[i]) {
1346                 case ':' :
1347                 case '/' :
1348                         mangled_uri[i] = '_';
1349                 }
1350         }
1351
1352         /* the file backend uses its uri as the attachment store*/
1353         if (g_str_has_prefix (priv->uri, "file://")) {
1354                 priv->local_attachment_store = g_strdup (priv->uri);
1355         } else if (g_str_has_prefix (priv->uri, "groupwise://")) {
1356                 /* points to the location of the cache*/
1357                 gchar *filename = g_build_filename (g_get_home_dir (),
1358                                                     ".evolution/cache/calendar",
1359                                                     mangled_uri,
1360                                                     NULL);
1361                 priv->local_attachment_store = 
1362                         g_filename_to_uri (filename, NULL, NULL);
1363                 g_free (filename);
1364         } else if (g_str_has_prefix (priv->uri, "exchange://")) {
1365                 gchar *filename = g_build_filename (g_get_home_dir (),
1366                                                     ".evolution/exchange",
1367                                                     mangled_uri,
1368                                                     NULL);
1369                 priv->local_attachment_store =
1370                         g_filename_to_uri (filename, NULL, NULL);
1371                 g_free (filename);
1372         } else if (g_str_has_prefix (priv->uri, "scalix://")) {
1373                 gchar *filename = g_build_filename (g_get_home_dir (),
1374                                                     ".evolution/cache/scalix",
1375                                                     mangled_uri,
1376                                                     "attach",
1377                                                     NULL);
1378                 priv->local_attachment_store =
1379                         g_filename_to_uri (filename, NULL, NULL);
1380                 g_free (filename);
1381         }
1382
1383         g_free (mangled_uri);
1384 }
1385
1386 /**
1387  * e_cal_new:
1388  * @source: An #ESource to be used for the client.
1389  * @type: Type of the client.
1390  *
1391  * Creates a new calendar client. This does not open the calendar itself,
1392  * for that, #e_cal_open or #e_cal_open_async needs to be called.
1393  *
1394  * Return value: A newly-created calendar client, or NULL if the client could
1395  * not be constructed because it could not contact the calendar server.
1396  **/
1397 ECal *
1398 e_cal_new (ESource *source, ECalSourceType type)
1399 {
1400         ECal *ecal;
1401         
1402         e_cal_activate ();
1403         
1404         ecal = g_object_new (E_TYPE_CAL, NULL);
1405
1406         if (!fetch_corba_cal (ecal, source, type)) {
1407                 g_object_unref (ecal);
1408
1409                 return NULL;
1410         }
1411
1412         /* Set the local attachment store path for the calendar */
1413         set_local_attachment_store (ecal);
1414
1415         /* initialize component listener */
1416         ecal->priv->comp_listener = e_component_listener_new ((Bonobo_Unknown) ecal->priv->cal);
1417         g_signal_connect (G_OBJECT (ecal->priv->comp_listener), "component_died",
1418                           G_CALLBACK (backend_died_cb), ecal);
1419
1420         return ecal;
1421 }
1422
1423 /**
1424  * e_cal_new_from_uri:
1425  * @uri: The URI pointing to the calendar to open.
1426  * @type: Type of the client.
1427  *
1428  * Creates a new calendar client. This does not open the calendar itself,
1429  * for that, #e_cal_open or #e_cal_open_async needs to be called.
1430  *
1431  * Return value: A newly-created calendar client, or NULL if the client could
1432  * not be constructed because it could not contact the calendar server.
1433  **/
1434 ECal *
1435 e_cal_new_from_uri (const gchar *uri, ECalSourceType type)
1436 {
1437         ESource *source;
1438         ECal *cal;
1439
1440         source = e_source_new_with_absolute_uri ("", uri);
1441         cal = e_cal_new (source, type);
1442
1443         g_object_unref (source);
1444
1445         return cal;
1446 }
1447
1448 /**
1449  * e_cal_new_system_calendar:
1450  *
1451  * Create a calendar client for the system calendar, which should always be present in
1452  * all Evolution installations. This does not open the calendar itself,
1453  * for that, #e_cal_open or #e_cal_open_async needs to be called.
1454  *
1455  * Return value: A newly-created calendar client, or NULL if the client could
1456  * not be constructed.
1457  */
1458 ECal *
1459 e_cal_new_system_calendar (void)
1460 {
1461         ECal *ecal;
1462         char *filename;
1463         char *uri;
1464
1465         filename = g_build_filename (g_get_home_dir (),
1466                                      ".evolution/calendar/local/system",
1467                                      NULL);
1468         uri = g_filename_to_uri (filename, NULL, NULL);
1469         g_free (filename);
1470         ecal = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_EVENT);
1471         g_free (uri);
1472         
1473         return ecal;
1474 }
1475
1476 /**
1477  * e_cal_new_system_tasks:
1478  *
1479  * Create a calendar client for the system task list, which should always be present in
1480  * all Evolution installations. This does not open the tasks list itself,
1481  * for that, #e_cal_open or #e_cal_open_async needs to be called.
1482  *
1483  * Return value: A newly-created calendar client, or NULL if the client could
1484  * not be constructed.
1485  */
1486 ECal *
1487 e_cal_new_system_tasks (void)
1488 {
1489         ECal *ecal;
1490         char *filename;
1491         char *uri;
1492
1493         filename = g_build_filename (g_get_home_dir (),
1494                                      ".evolution/tasks/local/system",
1495                                      NULL);
1496         uri = g_filename_to_uri (filename, NULL, NULL);
1497         g_free (filename);
1498         ecal = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_TODO);
1499         g_free (uri);
1500         
1501         return ecal;
1502 }
1503
1504 /**
1505  * e_cal_new_system_memos:
1506  *
1507  * Create a calendar client for the system memos, which should always be present
1508  * in all Evolution installations. This does not open the memos itself, for
1509  * that, #e_cal_open or #e_cal_open_async needs to be called.
1510  *
1511  * Return value: A newly-created calendar client, or NULL if the client could
1512  * not be constructed.
1513  */
1514 ECal *
1515 e_cal_new_system_memos (void)
1516 {
1517         ECal *ecal;
1518         char *uri;
1519         char *filename;
1520
1521         filename = g_build_filename (g_get_home_dir (),
1522                                      ".evolution/memos/local/system",
1523                                      NULL);
1524         uri = g_filename_to_uri (filename, NULL, NULL);
1525         g_free (filename);
1526         ecal = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_JOURNAL);
1527         g_free (uri);
1528         
1529         return ecal;
1530 }
1531
1532 /**
1533  * e_cal_set_auth_func
1534  * @ecal: A calendar client.
1535  * @func: The authentication function
1536  * @data: User data to be used when calling the authentication function
1537  *
1538  * Sets the given authentication function on the calendar client. This
1539  * function will be called any time the calendar server needs a
1540  * password for an operation associated with the calendar and should
1541  * be supplied before any calendar is opened.
1542  *
1543  * When a calendar is opened asynchronously, the open function is
1544  * processed in a concurrent thread.  This means that the
1545  * authentication function will also be called from this thread.  As
1546  * such, the authentication callback cannot directly call any
1547  * functions that must be called from the main thread.  For example
1548  * any Gtk+ related functions, which must be proxied synchronously to
1549  * the main thread by the callback.
1550  *
1551  * The authentication function has the following signature
1552  * (ECalAuthFunc):
1553  *      char * auth_func (ECal *ecal,
1554  *                        const gchar *prompt,
1555  *                        const gchar *key,
1556  *                        gpointer user_data)
1557  */
1558 void
1559 e_cal_set_auth_func (ECal *ecal, ECalAuthFunc func, gpointer data)
1560 {
1561         g_return_if_fail (ecal != NULL);
1562         g_return_if_fail (E_IS_CAL (ecal));
1563
1564         ecal->priv->auth_func = func;
1565         ecal->priv->auth_user_data = data;
1566 }
1567
1568 static char *
1569 build_proxy_pass_key (ECal *ecal, const char* parent_user)
1570 {
1571         char *euri_str;
1572         const char *uri;
1573         EUri *euri;
1574
1575         uri = e_cal_get_uri (ecal);
1576
1577         euri = e_uri_new (uri);
1578         g_free (euri->user);
1579         euri->user = g_strdup(parent_user);
1580
1581         euri_str = e_uri_to_string (euri, FALSE);
1582
1583         e_uri_free (euri);
1584         return euri_str;
1585 }
1586
1587 static char *
1588 build_pass_key (ECal *ecal)
1589 {
1590         char *euri_str;
1591         const char *uri;
1592         EUri *euri;
1593
1594         uri = e_cal_get_uri (ecal);
1595
1596         euri = e_uri_new (uri);
1597         euri_str = e_uri_to_string (euri, FALSE);
1598
1599         e_uri_free (euri);
1600         return euri_str;
1601 }
1602
1603 static gboolean
1604 open_calendar (ECal *ecal, gboolean only_if_exists, GError **error, ECalendarStatus *status, gboolean needs_auth)
1605 {
1606         ECalPrivate *priv;
1607         CORBA_Environment ev;
1608         ECalendarOp *our_op;
1609         char *username = NULL, *auth_type = NULL, *password = NULL;
1610         gboolean read_only = FALSE;
1611         
1612         e_return_error_if_fail (ecal != NULL, E_CALENDAR_STATUS_INVALID_ARG);
1613         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
1614
1615         priv = ecal->priv;
1616         
1617         g_mutex_lock (ecal->priv->mutex);
1618
1619         if (!needs_auth && priv->load_state == E_CAL_LOAD_LOADED) {
1620                 g_mutex_unlock (ecal->priv->mutex);
1621                 return TRUE;
1622         }
1623         
1624         if (ecal->priv->current_op != NULL) {
1625                 g_mutex_unlock (ecal->priv->mutex);
1626                 *status = E_CALENDAR_STATUS_BUSY;
1627                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
1628         }
1629         /* start the open operation */
1630         our_op = e_calendar_new_op (ecal);
1631
1632         g_mutex_unlock (priv->mutex);
1633
1634         /* see if the backend needs authentication */
1635         if ( (priv->mode !=  CAL_MODE_LOCAL) && e_source_get_property (priv->source, "auth")) {
1636                 char *prompt, *key;
1637                 char *parent_user;
1638
1639                 priv->load_state = E_CAL_LOAD_AUTHENTICATING;
1640
1641                 if (priv->auth_func == NULL) {
1642                         e_calendar_remove_op (ecal, our_op);
1643                         e_calendar_free_op (our_op);
1644                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1645                         *status = E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED;
1646                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
1647                 }
1648
1649                 username = e_source_get_duped_property (priv->source, "username");
1650                 if (!username) {
1651                         e_calendar_remove_op (ecal, our_op);
1652                         e_calendar_free_op (our_op);
1653                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1654                         *status = E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED;
1655                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
1656                 }
1657
1658                 prompt = g_strdup_printf (_("Enter password for %s (user %s)"),
1659                                 e_source_peek_name (priv->source), username);
1660
1661                 auth_type = e_source_get_duped_property (priv->source, "auth-type");
1662                 if (auth_type) 
1663                         key = build_pass_key (ecal);
1664                 else {
1665                         parent_user = e_source_get_duped_property (priv->source, "parent_id_name");
1666                         if (parent_user) {
1667                                 key = build_proxy_pass_key (ecal, parent_user);
1668                                 /* 
1669                                    This password prompt will be prompted rarely. Since the key that is passed to 
1670                                    the auth_func corresponds to the parent user.
1671                                  */
1672                                 prompt = g_strdup_printf (_("Enter password for %s to enable proxy for user %s"), e_source_peek_name (priv->source), parent_user);
1673                                 g_free (parent_user);
1674                         } else 
1675                                 key = g_strdup (e_cal_get_uri (ecal));
1676                 }
1677                 g_free (auth_type);
1678
1679                 if (!key) {
1680                         e_calendar_remove_op (ecal, our_op);
1681                         e_calendar_free_op (our_op);
1682                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1683                         *status = E_CALENDAR_STATUS_URI_NOT_LOADED;
1684                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
1685                 }
1686
1687                 password = priv->auth_func (ecal, prompt, key, priv->auth_user_data);
1688
1689                 if (!password) {
1690                         e_calendar_remove_op (ecal, our_op);
1691                         e_calendar_free_op (our_op);
1692                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1693                         *status = E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED; 
1694                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
1695                 } 
1696
1697                 g_free (prompt);
1698                 g_free (key);
1699         }
1700
1701
1702         CORBA_exception_init (&ev);
1703
1704         priv->load_state = E_CAL_LOAD_LOADING;
1705
1706         GNOME_Evolution_Calendar_Cal_open (priv->cal, only_if_exists,
1707                                            username ? username : "",
1708                                            password ? password : "",
1709                                            &ev);
1710         g_free (password);
1711         g_free (username);
1712
1713         if (BONOBO_EX (&ev)) {
1714                 e_calendar_remove_op (ecal, our_op);
1715                 e_calendar_free_op (our_op);
1716                 
1717                 CORBA_exception_free (&ev);
1718
1719                 g_warning (G_STRLOC ": Unable to contact backend");
1720
1721                 *status = E_CALENDAR_STATUS_CORBA_EXCEPTION;
1722                 priv->load_state = E_CAL_LOAD_NOT_LOADED;
1723                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
1724         }
1725
1726         CORBA_exception_free (&ev);
1727
1728         e_flag_wait (our_op->done);
1729
1730         *status = our_op->status;
1731         
1732         e_calendar_remove_op (ecal, our_op);
1733         e_calendar_free_op (our_op);
1734         if (*status == E_CALENDAR_STATUS_OK) {
1735                 priv->load_state = E_CAL_LOAD_LOADED;
1736                 if (get_read_only (ecal, &read_only, NULL))
1737                         priv->read_only = read_only;
1738         } else
1739                 priv->load_state = E_CAL_LOAD_NOT_LOADED;
1740
1741         E_CALENDAR_CHECK_STATUS (*status, error);
1742 }
1743
1744 /**
1745  * e_cal_open
1746  * @ecal: A calendar client.
1747  * @only_if_exists: FALSE if the calendar should be opened even if there
1748  * was no storage for it, i.e. to create a new calendar or load an existing
1749  * one if it already exists.  TRUE if it should only try to load calendars
1750  * that already exist.
1751  * @error: Placeholder for error information.
1752  *
1753  * Makes a calendar client initiate a request to open a calendar.  The calendar
1754  * client will emit the "cal_opened" signal when the response from the server is
1755  * received.
1756  *
1757  * Return value: TRUE on success, FALSE on failure to issue the open request.
1758  **/
1759 gboolean
1760 e_cal_open (ECal *ecal, gboolean only_if_exists, GError **error)
1761 {
1762         ECalendarStatus status;
1763         gboolean result;
1764
1765         result = open_calendar (ecal, only_if_exists, error, &status, FALSE);
1766         g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_OPENED], 0, status);
1767
1768         return result;
1769 }
1770
1771 typedef struct {
1772         ECal *ecal;
1773         gboolean exists;
1774         gboolean result;
1775         ECalendarStatus status;
1776         const char *auth_prompt;
1777         const char *auth_key;
1778         char *password;
1779 } ECalAsyncData;
1780
1781 static gboolean
1782 async_signal_idle_cb (gpointer data)
1783 {
1784         ECalAsyncData *ccad = data;
1785
1786         g_signal_emit (G_OBJECT (ccad->ecal), e_cal_signals[CAL_OPENED], 0, ccad->status);
1787
1788         /* free memory */
1789         g_object_unref (ccad->ecal);
1790         g_free (ccad);
1791         
1792         return FALSE;
1793 }
1794
1795 static gpointer
1796 open_async (gpointer data) 
1797 {
1798         ECalAsyncData *ccad = data;
1799
1800         ccad->result = open_calendar (ccad->ecal, ccad->exists, NULL, &ccad->status, FALSE);
1801         g_idle_add ((GSourceFunc) async_signal_idle_cb, ccad);
1802         
1803         return GINT_TO_POINTER (ccad->result);
1804 }
1805
1806 /**
1807  * e_cal_open_async:
1808  * @ecal: A calendar client.
1809  * @only_if_exists: If TRUE, then only open the calendar if it already
1810  * exists.  If FALSE, then create a new calendar if it doesn't already
1811  * exist.
1812  * 
1813  * Open the calendar asynchronously.  The calendar will emit the
1814  * "cal_opened" signal when the operation has completed.
1815  * 
1816  * Because this operation runs in another thread, any authentication
1817  * callback set on the calendar will be called from this other thread.
1818  * See #e_cal_set_auth_func() for details.
1819  **/
1820 void
1821 e_cal_open_async (ECal *ecal, gboolean only_if_exists)
1822 {
1823         ECalAsyncData *ccad;
1824         GThread *thread;
1825         GError *error = NULL;
1826         g_return_if_fail (ecal != NULL);
1827         g_return_if_fail (E_IS_CAL (ecal));
1828
1829         switch (ecal->priv->load_state) {
1830         case E_CAL_LOAD_AUTHENTICATING :
1831         case E_CAL_LOAD_LOADING :
1832                 g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_OPENED], 0, E_CALENDAR_STATUS_BUSY);
1833                 return;
1834         case E_CAL_LOAD_LOADED :
1835                 g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_OPENED], 0, E_CALENDAR_STATUS_OK);
1836                 return;
1837         default:
1838                 /* ignore everything else */
1839                 break;
1840         }
1841
1842         ccad = g_new0 (ECalAsyncData, 1);
1843         ccad->ecal = g_object_ref (ecal);
1844         ccad->exists = only_if_exists;
1845
1846         /* spawn a new thread for opening the calendar */
1847         thread = g_thread_create ((GThreadFunc) open_async, ccad, FALSE, &error);
1848         if (!thread) {
1849                 g_warning (G_STRLOC ": %s", error->message);
1850                 g_error_free (error);
1851
1852                 /* notify listeners of the error */
1853                 g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_OPENED], 0, E_CALENDAR_STATUS_OTHER_ERROR);
1854         }
1855 }
1856
1857 /**
1858  * e_cal_remove:
1859  * @ecal: A calendar client.
1860  * @error: Placeholder for error information.
1861  *
1862  * Removes a calendar.
1863  *
1864  * Return value: TRUE if the calendar was removed, FALSE if there was an error.
1865  */
1866 gboolean 
1867 e_cal_remove (ECal *ecal, GError **error)
1868 {
1869         ECalPrivate *priv;
1870         CORBA_Environment ev;
1871         ECalendarStatus status;
1872         ECalendarOp *our_op;
1873         
1874         g_return_val_if_fail (ecal != NULL, FALSE);
1875         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
1876
1877         priv = ecal->priv;
1878         
1879         g_mutex_lock (ecal->priv->mutex);
1880
1881         if (ecal->priv->current_op != NULL) {
1882                 g_mutex_unlock (ecal->priv->mutex);
1883                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
1884         }
1885
1886         our_op = e_calendar_new_op (ecal);
1887
1888         g_mutex_unlock (ecal->priv->mutex);
1889
1890
1891         CORBA_exception_init (&ev);
1892
1893         GNOME_Evolution_Calendar_Cal_remove (priv->cal, &ev);
1894         if (BONOBO_EX (&ev)) {
1895                 e_calendar_remove_op (ecal, our_op);
1896                 e_calendar_free_op (our_op);
1897
1898                 CORBA_exception_free (&ev);
1899
1900                 g_warning (G_STRLOC ": Unable to contact backend");
1901
1902                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
1903         }
1904
1905         CORBA_exception_free (&ev);
1906
1907         e_flag_wait (our_op->done);
1908
1909         status = our_op->status;
1910         
1911         e_calendar_remove_op (ecal, our_op);
1912         e_calendar_free_op (our_op);
1913
1914         E_CALENDAR_CHECK_STATUS (status, error);
1915 }
1916
1917 #if 0
1918 /* Builds an URI list out of a CORBA string sequence */
1919 static GList *
1920 build_uri_list (GNOME_Evolution_Calendar_StringSeq *seq)
1921 {
1922         GList *uris = NULL;
1923         int i;
1924
1925         for (i = 0; i < seq->_length; i++)
1926                 uris = g_list_prepend (uris, g_strdup (seq->_buffer[i]));
1927
1928         return uris;
1929 }
1930 #endif
1931
1932 /**
1933  * e_cal_uri_list:
1934  * @ecal: A calendar client.
1935  * @mode: Mode of the URIs to get.
1936  *
1937  * Retrieves a list of all calendar clients for the given mode.
1938  *
1939  * Return value: list of uris.
1940  */
1941 GList *
1942 e_cal_uri_list (ECal *ecal, CalMode mode)
1943 {
1944 #if 0
1945         ECalPrivate *priv;
1946         GNOME_Evolution_Calendar_StringSeq *uri_seq;
1947         GList *uris = NULL;     
1948         CORBA_Environment ev;
1949         GList *f;
1950
1951         g_return_val_if_fail (ecal != NULL, FALSE);
1952         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
1953
1954         priv = ecal->priv;
1955
1956         for (f = priv->factories; f; f = f->next) {
1957                 CORBA_exception_init (&ev);
1958                 uri_seq = GNOME_Evolution_Calendar_CalFactory_uriList (f->data, mode, &ev);
1959
1960                 if (BONOBO_EX (&ev)) {
1961                         g_message ("e_cal_uri_list(): request failed");
1962
1963                         /* free memory and return */
1964                         g_list_foreach (uris, (GFunc) g_free, NULL);
1965                         g_list_free (uris);
1966                         uris = NULL;
1967                         break;
1968                 }
1969                 else {
1970                         uris = g_list_concat (uris, build_uri_list (uri_seq));
1971                         CORBA_free (uri_seq);
1972                 }
1973         
1974                 CORBA_exception_free (&ev);
1975         }
1976         
1977         return uris;    
1978 #endif
1979
1980         return NULL;
1981 }
1982
1983 /**
1984  * e_cal_get_source_type:
1985  * @ecal: A calendar client.
1986  *
1987  * Gets the type of the calendar client.
1988  *
1989  * Return value: an #ECalSourceType value corresponding to the type
1990  * of the calendar client.
1991  */
1992 ECalSourceType
1993 e_cal_get_source_type (ECal *ecal)
1994 {
1995         ECalPrivate *priv;
1996
1997         g_return_val_if_fail (ecal != NULL, E_CAL_SOURCE_TYPE_LAST);
1998         g_return_val_if_fail (E_IS_CAL (ecal), E_CAL_SOURCE_TYPE_LAST);
1999
2000         priv = ecal->priv;
2001
2002         return priv->type;
2003 }
2004
2005 /**
2006  * e_cal_get_load_state:
2007  * @ecal: A calendar client.
2008  * 
2009  * Queries the state of loading of a calendar client.
2010  * 
2011  * Return value: A #ECalLoadState value indicating whether the client has
2012  * not been loaded with #e_cal_open yet, whether it is being
2013  * loaded, or whether it is already loaded.
2014  **/
2015 ECalLoadState
2016 e_cal_get_load_state (ECal *ecal)
2017 {
2018         ECalPrivate *priv;
2019
2020         g_return_val_if_fail (ecal != NULL, E_CAL_LOAD_NOT_LOADED);
2021         g_return_val_if_fail (E_IS_CAL (ecal), E_CAL_LOAD_NOT_LOADED);
2022
2023         priv = ecal->priv;
2024         return priv->load_state;
2025 }
2026
2027 /**
2028  * e_cal_get_source:
2029  * @ecal: A calendar client.
2030  * 
2031  * Queries the source that is open in a calendar client.
2032  * 
2033  * Return value: The source of the calendar that is already loaded or is being
2034  * loaded, or NULL if the ecal has not started a load request yet.
2035  **/
2036 ESource *
2037 e_cal_get_source (ECal *ecal)
2038 {
2039         ECalPrivate *priv;
2040
2041         g_return_val_if_fail (ecal != NULL, NULL);
2042         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
2043
2044         priv = ecal->priv;
2045         return priv->source;
2046 }
2047
2048 /**
2049  * e_cal_get_uri:
2050  * @ecal: A calendar client.
2051  * 
2052  * Queries the URI that is open in a calendar client.
2053  * 
2054  * Return value: The URI of the calendar that is already loaded or is being
2055  * loaded, or NULL if the client has not started a load request yet.
2056  **/
2057 const char *
2058 e_cal_get_uri (ECal *ecal)
2059 {
2060         ECalPrivate *priv;
2061
2062         g_return_val_if_fail (ecal != NULL, NULL);
2063         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
2064
2065         priv = ecal->priv;
2066         return priv->uri;
2067 }
2068
2069 /**
2070  * e_cal_get_local_attachment_store
2071  * @ecal: A calendar client.
2072  * 
2073  * Queries the URL where the calendar attachments are
2074  * serialized in the local filesystem. This enable clients
2075  * to operate with the reference to attachments rather than the data itself
2076  * unless it specifically uses the attachments for open/sending
2077  * operations.
2078  *
2079  * Return value: The URL where the attachments are serialized in the
2080  * local filesystem.
2081  **/
2082 const char *
2083 e_cal_get_local_attachment_store (ECal *ecal)
2084 {
2085         ECalPrivate *priv;
2086
2087         g_return_val_if_fail (ecal != NULL, NULL);
2088         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
2089
2090         priv = ecal->priv;
2091         return (const char *)priv->local_attachment_store;
2092 }
2093
2094 /**
2095  * e_cal_is_read_only:
2096  * @ecal: A calendar client.
2097  * @read_only: Return value for read only status.
2098  * @error: Placeholder for error information.
2099  *
2100  * Queries whether the calendar client can perform modifications
2101  * on the calendar or not. Whether the backend is read only or not
2102  * is specified, on exit, in the @read_only argument.
2103  *
2104  * Return value: TRUE if the call was successful, FALSE if there was an error.
2105  */
2106 gboolean
2107 e_cal_is_read_only (ECal *ecal, gboolean *read_only, GError **error)
2108 {
2109         ECalPrivate *priv;
2110         
2111         if (!(ecal && E_IS_CAL (ecal)))
2112                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_INVALID_ARG, error);
2113         
2114         priv = ecal->priv;
2115         *read_only = priv->read_only;
2116         
2117         return TRUE;
2118 }
2119
2120 static gboolean
2121 get_read_only (ECal *ecal, gboolean *read_only, GError **error)
2122 {
2123         ECalPrivate *priv;
2124         CORBA_Environment ev;
2125         ECalendarStatus status;
2126         ECalendarOp *our_op;
2127         
2128         priv = ecal->priv;
2129         g_mutex_lock (ecal->priv->mutex);
2130
2131         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2132                 g_mutex_unlock (ecal->priv->mutex);
2133                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2134         }
2135
2136         if (ecal->priv->current_op != NULL) {
2137                 g_mutex_unlock (ecal->priv->mutex);
2138                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2139         }
2140
2141         our_op = e_calendar_new_op (ecal);
2142
2143         /* set it to true so that op does not emit cond signals for all notifications
2144            from the backend */
2145         our_op->bool = TRUE;
2146         
2147         g_mutex_unlock (ecal->priv->mutex);
2148
2149
2150         CORBA_exception_init (&ev);
2151
2152         GNOME_Evolution_Calendar_Cal_isReadOnly (priv->cal, &ev);
2153         if (BONOBO_EX (&ev)) {
2154                 e_calendar_remove_op (ecal, our_op);
2155                 e_calendar_free_op (our_op);
2156
2157                 CORBA_exception_free (&ev);
2158
2159                 g_warning (G_STRLOC ": Unable to contact backend");
2160
2161                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2162         }
2163
2164         CORBA_exception_free (&ev);
2165
2166         e_flag_wait (our_op->done);
2167
2168         status = our_op->status;
2169         
2170         if (status == E_CALENDAR_STATUS_OK)
2171                 *read_only = our_op->bool;
2172
2173         e_calendar_remove_op (ecal, our_op);
2174         e_calendar_free_op (our_op);
2175         E_CALENDAR_CHECK_STATUS (status, error);
2176 }
2177
2178 /**
2179  * e_cal_get_cal_address:
2180  * @ecal: A calendar client.
2181  * @cal_address: Return value for address information.
2182  * @error: Placeholder for error information.
2183  *
2184  * Queries the calendar address associated with a calendar client.
2185  * 
2186  * Return value: TRUE if the operation was successful, FALSE if there
2187  * was an error.
2188  **/
2189 gboolean
2190 e_cal_get_cal_address (ECal *ecal, char **cal_address, GError **error)
2191 {
2192         ECalPrivate *priv;
2193         CORBA_Environment ev;
2194         ECalendarStatus status;
2195         ECalendarOp *our_op;
2196
2197
2198         if (!(ecal && E_IS_CAL (ecal)))
2199                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_INVALID_ARG, error);
2200         priv = ecal->priv;
2201         if (priv->cal_address == NULL) { 
2202                 g_mutex_lock (ecal->priv->mutex);
2203
2204                 if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2205                         g_mutex_unlock (ecal->priv->mutex);
2206                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2207                 }
2208
2209                 if (ecal->priv->current_op != NULL) {
2210                         g_mutex_unlock (ecal->priv->mutex);
2211                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2212                 }
2213
2214                 our_op = e_calendar_new_op (ecal);
2215
2216                 g_mutex_unlock (ecal->priv->mutex);
2217
2218
2219                 CORBA_exception_init (&ev);
2220
2221                 GNOME_Evolution_Calendar_Cal_getCalAddress (priv->cal, &ev);
2222                 if (BONOBO_EX (&ev)) {
2223                         e_calendar_remove_op (ecal, our_op);
2224                         e_calendar_free_op (our_op);
2225
2226                         CORBA_exception_free (&ev);
2227
2228                         g_warning (G_STRLOC ": Unable to contact backend");
2229
2230                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2231                 }
2232
2233                 CORBA_exception_free (&ev);
2234
2235                 e_flag_wait (our_op->done);
2236
2237                 status = our_op->status;
2238                 *cal_address = our_op->string;
2239                 e_calendar_remove_op (ecal, our_op);
2240                 e_calendar_free_op (our_op);
2241
2242                 E_CALENDAR_CHECK_STATUS (status, error);
2243         } else {        
2244                 *cal_address = g_strdup (priv->cal_address);
2245
2246                 return TRUE;
2247         }               
2248 }
2249
2250 /**
2251  * e_cal_get_alarm_email_address:
2252  * @ecal: A calendar client.
2253  * @alarm_address: Return value for alarm address.
2254  * @error: Placeholder for error information.
2255  *
2256  * Queries the address to be used for alarms in a calendar client.
2257  *
2258  * Return value: TRUE if the operation was successful, FALSE if there was
2259  * an error while contacting the backend.
2260  */
2261 gboolean
2262 e_cal_get_alarm_email_address (ECal *ecal, char **alarm_address, GError **error)
2263 {
2264         ECalPrivate *priv;
2265         CORBA_Environment ev;
2266         ECalendarStatus status;
2267         ECalendarOp *our_op;
2268
2269         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
2270
2271         priv = ecal->priv;
2272
2273         g_mutex_lock (ecal->priv->mutex);
2274
2275         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2276                 g_mutex_unlock (ecal->priv->mutex);
2277                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2278         }
2279
2280         if (ecal->priv->current_op != NULL) {
2281                 g_mutex_unlock (ecal->priv->mutex);
2282                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2283         }
2284
2285         our_op = e_calendar_new_op (ecal);
2286
2287         g_mutex_unlock (ecal->priv->mutex);
2288
2289
2290         CORBA_exception_init (&ev);
2291
2292         GNOME_Evolution_Calendar_Cal_getAlarmEmailAddress (priv->cal, &ev);
2293         if (BONOBO_EX (&ev)) {
2294                 e_calendar_remove_op (ecal, our_op);
2295                 e_calendar_free_op (our_op);
2296
2297                 CORBA_exception_free (&ev);
2298
2299                 g_warning (G_STRLOC ": Unable to contact backend");
2300
2301                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2302         }
2303
2304         CORBA_exception_free (&ev);
2305
2306         e_flag_wait (our_op->done);
2307
2308         status = our_op->status;
2309         *alarm_address = our_op->string;
2310         
2311         e_calendar_remove_op (ecal, our_op);
2312         e_calendar_free_op (our_op);
2313
2314         E_CALENDAR_CHECK_STATUS (status, error);
2315 }
2316
2317 /**
2318  * e_cal_get_ldap_attribute:
2319  * @ecal: A calendar client.
2320  * @ldap_attribute: Return value for the LDAP attribute.
2321  * @error: Placeholder for error information.
2322  *
2323  * Queries the LDAP attribute for a calendar client.
2324  *
2325  * Return value: TRUE if the call was successful, FALSE if there was an
2326  * error contacting the backend.
2327  */
2328 gboolean
2329 e_cal_get_ldap_attribute (ECal *ecal, char **ldap_attribute, GError **error)
2330 {
2331         ECalPrivate *priv;
2332         CORBA_Environment ev;
2333         ECalendarStatus status;
2334         ECalendarOp *our_op;
2335
2336         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
2337
2338         priv = ecal->priv;
2339
2340         g_mutex_lock (ecal->priv->mutex);
2341
2342         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2343                 g_mutex_unlock (ecal->priv->mutex);
2344                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2345         }
2346
2347         if (ecal->priv->current_op != NULL) {
2348                 g_mutex_unlock (ecal->priv->mutex);
2349                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2350         }
2351
2352         our_op = e_calendar_new_op (ecal);
2353
2354         g_mutex_unlock (ecal->priv->mutex);
2355
2356
2357         CORBA_exception_init (&ev);
2358
2359         GNOME_Evolution_Calendar_Cal_getLdapAttribute (priv->cal, &ev);
2360         if (BONOBO_EX (&ev)) {
2361                 e_calendar_remove_op (ecal, our_op);
2362                 e_calendar_free_op (our_op);
2363
2364                 CORBA_exception_free (&ev);
2365
2366                 g_warning (G_STRLOC ": Unable to contact backend");
2367
2368                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2369         }
2370
2371         CORBA_exception_free (&ev);
2372
2373         e_flag_wait (our_op->done);
2374
2375         status = our_op->status;
2376         *ldap_attribute = our_op->string;
2377         
2378         e_calendar_remove_op (ecal, our_op);
2379         e_calendar_free_op (our_op);
2380
2381         E_CALENDAR_CHECK_STATUS (status, error);
2382 }
2383
2384 static gboolean
2385 load_static_capabilities (ECal *ecal, GError **error) 
2386 {
2387         ECalPrivate *priv;
2388         CORBA_Environment ev;
2389         ECalendarStatus status;
2390         ECalendarOp *our_op;
2391         
2392         priv = ecal->priv;
2393
2394         if (priv->capabilities)
2395                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
2396
2397         g_mutex_lock (ecal->priv->mutex);
2398
2399         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2400                 g_mutex_unlock (ecal->priv->mutex);
2401                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2402         }
2403
2404         if (ecal->priv->current_op != NULL) {
2405                 g_mutex_unlock (ecal->priv->mutex);
2406                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2407         }
2408
2409         our_op = e_calendar_new_op (ecal);
2410
2411         g_mutex_unlock (ecal->priv->mutex);
2412
2413
2414         CORBA_exception_init (&ev);
2415
2416         GNOME_Evolution_Calendar_Cal_getStaticCapabilities (priv->cal, &ev);
2417         if (BONOBO_EX (&ev)) {
2418                 e_calendar_remove_op (ecal, our_op);
2419                 e_calendar_free_op (our_op);
2420
2421                 CORBA_exception_free (&ev);
2422
2423                 g_warning (G_STRLOC ": Unable to contact backend");
2424
2425                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2426         }
2427
2428         CORBA_exception_free (&ev);
2429
2430         e_flag_wait (our_op->done);
2431
2432         status = our_op->status;
2433         priv->capabilities = our_op->string;
2434         
2435         e_calendar_remove_op (ecal, our_op);
2436         e_calendar_free_op (our_op);
2437
2438         E_CALENDAR_CHECK_STATUS (status, error);
2439 }
2440
2441 static gboolean
2442 check_capability (ECal *ecal, const char *cap) 
2443 {
2444         ECalPrivate *priv;
2445
2446         priv = ecal->priv;
2447
2448         /* FIXME Check result */
2449         load_static_capabilities (ecal, NULL);
2450         if (priv->capabilities && strstr (priv->capabilities, cap))
2451                 return TRUE;
2452         
2453         return FALSE;
2454 }
2455
2456 /**
2457  * e_cal_get_one_alarm_only:
2458  * @ecal: A calendar client.
2459  *
2460  * Checks if a calendar supports only one alarm per component.
2461  *
2462  * Return value: TRUE if the calendar allows only one alarm, FALSE otherwise.
2463  */
2464 gboolean
2465 e_cal_get_one_alarm_only (ECal *ecal)
2466 {
2467         g_return_val_if_fail (ecal != NULL, FALSE);
2468         g_return_val_if_fail (ecal && E_IS_CAL (ecal), FALSE);
2469
2470         return check_capability (ecal, CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY);
2471 }
2472
2473 /**
2474  * e_cal_get_organizer_must_attend:
2475  * @ecal: A calendar client.
2476  *
2477  * Checks if a calendar forces organizers of meetings to be also attendees.
2478  *
2479  * Return value: TRUE if the calendar forces organizers to attend meetings,
2480  * FALSE otherwise.
2481  */
2482 gboolean 
2483 e_cal_get_organizer_must_attend (ECal *ecal)
2484 {
2485         g_return_val_if_fail (ecal != NULL, FALSE);
2486         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2487
2488         return check_capability (ecal, CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ATTEND);
2489 }
2490
2491 /**
2492  * e_cal_get_recurrences_no_master:
2493  * @ecal: A calendar client.
2494  *
2495  * Checks if the calendar has a master object for recurrences.
2496  *
2497  * Return value: TRUE if the calendar has a master object for recurrences,
2498  * FALSE otherwise.
2499  */
2500 gboolean 
2501 e_cal_get_recurrences_no_master (ECal *ecal)
2502 {
2503         g_return_val_if_fail (ecal != NULL, FALSE);
2504         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2505
2506         return check_capability (ecal, CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER);
2507 }
2508
2509 /**
2510  * e_cal_get_static_capability:
2511  * @ecal: A calendar client.
2512  * @cap: Name of the static capability to check.
2513  *
2514  * Queries the calendar for static capabilities.
2515  *
2516  * Return value: TRUE if the capability is supported, FALSE otherwise.
2517  */
2518 gboolean
2519 e_cal_get_static_capability (ECal *ecal, const char *cap)
2520 {
2521         g_return_val_if_fail (ecal != NULL, FALSE);
2522         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2523
2524         return check_capability (ecal, cap);
2525 }
2526
2527 /**
2528  * e_cal_get_save_schedules:
2529  * @ecal: A calendar client.
2530  *
2531  * Checks whether the calendar saves schedules.
2532  *
2533  * Return value: TRUE if it saves schedules, FALSE otherwise.
2534  */
2535 gboolean 
2536 e_cal_get_save_schedules (ECal *ecal)
2537 {
2538         g_return_val_if_fail (ecal != NULL, FALSE);
2539         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2540
2541         return check_capability (ecal, CAL_STATIC_CAPABILITY_SAVE_SCHEDULES);
2542 }
2543
2544 /**
2545  * e_cal_get_organizer_must_accept:
2546  * @ecal: A calendar client.
2547  *
2548  * Checks whether a calendar requires organizer to accept their attendance to
2549  * meetings.
2550  *
2551  * Return value: TRUE if the calendar requires organizers to accept, FALSE
2552  * otherwise.
2553  */
2554 gboolean 
2555 e_cal_get_organizer_must_accept (ECal *ecal)
2556 {
2557         g_return_val_if_fail (ecal != NULL, FALSE);
2558         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2559
2560         return check_capability (ecal, CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ACCEPT);
2561 }
2562
2563 /**
2564  * e_cal_set_mode:
2565  * @ecal: A calendar client.
2566  * @mode: Mode to switch to.
2567  *
2568  * Switches online/offline mode on the calendar.
2569  *
2570  * Return value: TRUE if the switch was successful, FALSE if there was an error.
2571  */
2572 gboolean
2573 e_cal_set_mode (ECal *ecal, CalMode mode)
2574 {
2575         ECalPrivate *priv;
2576         gboolean retval = TRUE; 
2577         CORBA_Environment ev;
2578
2579         g_return_val_if_fail (ecal != NULL, -1);
2580         g_return_val_if_fail (E_IS_CAL (ecal), -1);
2581
2582         priv = ecal->priv;
2583         g_return_val_if_fail (priv->load_state == E_CAL_LOAD_LOADED, -1);
2584
2585         CORBA_exception_init (&ev);
2586         GNOME_Evolution_Calendar_Cal_setMode (priv->cal, mode, &ev);
2587
2588         if (BONOBO_EX (&ev))
2589                 retval = FALSE;
2590                 
2591         CORBA_exception_free (&ev);
2592
2593         return retval;
2594 }
2595
2596 /* This is used in the callback which fetches all the timezones needed for an
2597    object. */
2598 typedef struct _ECalGetTimezonesData ECalGetTimezonesData;
2599 struct _ECalGetTimezonesData {
2600         ECal *ecal;
2601
2602         /* This starts out at E_CALENDAR_STATUS_OK. If an error occurs this
2603            contains the last error. */
2604         ECalendarStatus status;
2605 };
2606
2607 /**
2608  * e_cal_get_default_object:
2609  * @ecal: A calendar client.
2610  * @icalcomp: Return value for the default object.
2611  * @error: Placeholder for error information.
2612  *
2613  * Retrives an #icalcomponent from the backend that contains the default
2614  * values for properties needed.
2615  *
2616  * Return value: TRUE if the call was successful, FALSE otherwise.
2617  */
2618 gboolean
2619 e_cal_get_default_object (ECal *ecal, icalcomponent **icalcomp, GError **error)
2620 {
2621         ECalPrivate *priv;
2622         CORBA_Environment ev;
2623         ECalendarStatus status;
2624         ECalendarOp *our_op;
2625
2626         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
2627
2628         priv = ecal->priv;
2629
2630         g_mutex_lock (ecal->priv->mutex);
2631
2632         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2633                 g_mutex_unlock (ecal->priv->mutex);
2634                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2635         }
2636
2637         if (ecal->priv->current_op != NULL) {
2638                 g_mutex_unlock (ecal->priv->mutex);
2639                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2640         }
2641
2642         our_op = e_calendar_new_op (ecal);
2643
2644         g_mutex_unlock (ecal->priv->mutex);
2645
2646         CORBA_exception_init (&ev);
2647
2648         GNOME_Evolution_Calendar_Cal_getDefaultObject (priv->cal, &ev);
2649         if (BONOBO_EX (&ev)) {
2650                 e_calendar_remove_op (ecal, our_op);
2651                 e_calendar_free_op (our_op);
2652
2653                 CORBA_exception_free (&ev);
2654
2655                 g_warning (G_STRLOC ": Unable to contact backend");
2656
2657                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2658         }
2659
2660         CORBA_exception_free (&ev);
2661
2662         e_flag_wait (our_op->done);
2663
2664         status = our_op->status;
2665         if (status != E_CALENDAR_STATUS_OK) {
2666                 *icalcomp = NULL;
2667         } else {
2668                 *icalcomp = icalparser_parse_string (our_op->string);
2669                 if (!(*icalcomp))
2670                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
2671         }
2672         g_free (our_op->string);
2673
2674         e_calendar_remove_op (ecal, our_op);
2675         e_calendar_free_op (our_op);
2676
2677         E_CALENDAR_CHECK_STATUS (status, error);
2678 }
2679
2680 /**
2681  * e_cal_get_attachments_for_comp:
2682  * @ecal: A calendar client.
2683  * @uid: Unique identifier for a calendar component.
2684  * @rid: Recurrence identifier.
2685  * @list: Return the list of attachment uris.
2686  * @error: Placeholder for error information.
2687  *
2688  * Queries a calendar for a calendar component object based on its unique
2689  * identifier and gets the attachments for the component.
2690  *
2691  * Return value: TRUE if the call was successful, FALSE otherwise.
2692  **/
2693 gboolean
2694 e_cal_get_attachments_for_comp (ECal *ecal, const char *uid, const char *rid, GSList **list, GError **error)
2695 {
2696         ECalPrivate *priv;
2697         CORBA_Environment ev;
2698         ECalendarStatus status;
2699         ECalendarOp *our_op;
2700
2701         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
2702
2703         priv = ecal->priv;
2704
2705         g_mutex_lock (ecal->priv->mutex);
2706
2707         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2708                 g_mutex_unlock (ecal->priv->mutex);
2709                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2710         }
2711
2712         if (ecal->priv->current_op != NULL) {
2713                 g_mutex_unlock (ecal->priv->mutex);
2714                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2715         }
2716
2717         our_op = e_calendar_new_op (ecal);
2718
2719         g_mutex_unlock (ecal->priv->mutex);
2720
2721         CORBA_exception_init (&ev);
2722         GNOME_Evolution_Calendar_Cal_getAttachmentList (priv->cal, uid, rid ? rid : "", &ev);
2723         if (BONOBO_EX (&ev)) {
2724                 e_calendar_remove_op (ecal, our_op);
2725                 e_calendar_free_op (our_op);
2726
2727                 CORBA_exception_free (&ev);
2728
2729                 g_warning (G_STRLOC ": Unable to contact backend");
2730
2731                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2732         }
2733
2734         CORBA_exception_free (&ev);
2735
2736         e_flag_wait (our_op->done);
2737
2738         status = our_op->status;
2739         if (status != E_CALENDAR_STATUS_OK){ 
2740                 *list = NULL;
2741         } else {
2742                 *list = our_op->slist;
2743         }
2744
2745         e_calendar_remove_op (ecal, our_op);
2746         e_calendar_free_op (our_op);
2747
2748         E_CALENDAR_CHECK_STATUS (status, error);
2749 }
2750
2751 /**
2752  * e_cal_get_object:
2753  * @ecal: A calendar client.
2754  * @uid: Unique identifier for a calendar component.
2755  * @rid: Recurrence identifier.
2756  * @icalcomp: Return value for the calendar component object.
2757  * @error: Placeholder for error information.
2758  *
2759  * Queries a calendar for a calendar component object based on its unique
2760  * identifier.
2761  *
2762  * Return value: TRUE if the call was successful, FALSE otherwise.
2763  **/
2764 gboolean
2765 e_cal_get_object (ECal *ecal, const char *uid, const char *rid, icalcomponent **icalcomp, GError **error)
2766 {
2767         ECalPrivate *priv;
2768         CORBA_Environment ev;
2769         ECalendarStatus status;
2770         ECalendarOp *our_op;
2771
2772         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
2773
2774         priv = ecal->priv;
2775
2776         g_mutex_lock (ecal->priv->mutex);
2777
2778         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2779                 g_mutex_unlock (ecal->priv->mutex);
2780                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2781         }
2782
2783         if (ecal->priv->current_op != NULL) {
2784                 g_mutex_unlock (ecal->priv->mutex);
2785                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2786         }
2787
2788         our_op = e_calendar_new_op (ecal);
2789
2790         g_mutex_unlock (ecal->priv->mutex);
2791
2792         CORBA_exception_init (&ev);
2793
2794         GNOME_Evolution_Calendar_Cal_getObject (priv->cal, uid, rid ? rid : "", &ev);
2795         if (BONOBO_EX (&ev)) {
2796                 e_calendar_remove_op (ecal, our_op);
2797                 e_calendar_free_op (our_op);
2798
2799                 CORBA_exception_free (&ev);
2800
2801                 g_warning (G_STRLOC ": Unable to contact backend");
2802
2803                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2804         }
2805
2806         CORBA_exception_free (&ev);
2807
2808         e_flag_wait (our_op->done);
2809
2810         status = our_op->status;
2811         if (status != E_CALENDAR_STATUS_OK){ 
2812                 *icalcomp = NULL;
2813         } else {
2814                 icalcomponent *tmp_icalcomp;
2815                 icalcomponent_kind kind;
2816
2817                 tmp_icalcomp = icalparser_parse_string (our_op->string);
2818                 if (!tmp_icalcomp) {
2819                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
2820                         *icalcomp = NULL;
2821                 } else {
2822                         kind = icalcomponent_isa (tmp_icalcomp);
2823                         if ((kind == ICAL_VEVENT_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_EVENT) ||
2824                             (kind == ICAL_VTODO_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_TODO) ||
2825                             (kind == ICAL_VJOURNAL_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_JOURNAL)) {
2826                                 *icalcomp = icalcomponent_new_clone (tmp_icalcomp);
2827                         } else if (kind == ICAL_VCALENDAR_COMPONENT) {
2828                                 icalcomponent *subcomp = NULL;
2829
2830                                 switch (priv->type) {
2831                                 case E_CAL_SOURCE_TYPE_EVENT :
2832                                         subcomp = icalcomponent_get_first_component (tmp_icalcomp, ICAL_VEVENT_COMPONENT);
2833                                         break;
2834                                 case E_CAL_SOURCE_TYPE_TODO :
2835                                         subcomp = icalcomponent_get_first_component (tmp_icalcomp, ICAL_VTODO_COMPONENT);
2836                                         break;
2837                                 case E_CAL_SOURCE_TYPE_JOURNAL :
2838                                         subcomp = icalcomponent_get_first_component (tmp_icalcomp, ICAL_VJOURNAL_COMPONENT);
2839                                         break;
2840                                 default:
2841                                         /* ignore everything else */
2842                                         break;
2843                                 }
2844
2845                                 /* we are only interested in the first component */
2846                                 if (subcomp)
2847                                         *icalcomp = icalcomponent_new_clone (subcomp);
2848                         }
2849
2850                         icalcomponent_free (tmp_icalcomp);
2851                 }
2852         }
2853         g_free (our_op->string);
2854
2855         e_calendar_remove_op (ecal, our_op);
2856         e_calendar_free_op (our_op);
2857
2858         E_CALENDAR_CHECK_STATUS (status, error);
2859 }
2860
2861 /**
2862  * e_cal_get_objects_for_uid:
2863  * @ecal: A calendar client.
2864  * @uid: Unique identifier for a calendar component.
2865  * @objects: Return value for the list of objects obtained from the backend.
2866  * @error: Placeholder for error information.
2867  *
2868  * Queries a calendar for all calendar components with the given unique
2869  * ID. This will return any recurring event and all its detached recurrences.
2870  * For non-recurring events, it will just return the object with that ID.
2871  *
2872  * Return value: TRUE if the call was successful, FALSE otherwise.
2873  **/
2874 gboolean
2875 e_cal_get_objects_for_uid (ECal *ecal, const char *uid, GList **objects, GError **error)
2876 {
2877         ECalPrivate *priv;
2878         CORBA_Environment ev;
2879         ECalendarStatus status;
2880         ECalendarOp *our_op;
2881
2882         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2883
2884         priv = ecal->priv;
2885         *objects = NULL;
2886
2887         g_mutex_lock (ecal->priv->mutex);
2888
2889         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
2890                 g_mutex_unlock (ecal->priv->mutex);
2891                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2892         }
2893
2894         if (ecal->priv->current_op != NULL) {
2895                 g_mutex_unlock (ecal->priv->mutex);
2896                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
2897         }
2898
2899         our_op = e_calendar_new_op (ecal);
2900
2901         g_mutex_unlock (ecal->priv->mutex);
2902
2903         CORBA_exception_init (&ev);
2904
2905         GNOME_Evolution_Calendar_Cal_getObject (priv->cal, uid, "", &ev);
2906         if (BONOBO_EX (&ev)) {
2907                 e_calendar_remove_op (ecal, our_op);
2908                 e_calendar_free_op (our_op);
2909
2910                 CORBA_exception_free (&ev);
2911
2912                 g_warning (G_STRLOC ": Unable to contact backend");
2913
2914                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
2915         }
2916
2917         CORBA_exception_free (&ev);
2918
2919         e_flag_wait (our_op->done);
2920
2921         status = our_op->status;
2922         if (status != E_CALENDAR_STATUS_OK){ 
2923                 *objects = NULL;
2924         } else {
2925                 icalcomponent *icalcomp;
2926                 icalcomponent_kind kind;
2927
2928                 icalcomp = icalparser_parse_string (our_op->string);
2929                 if (!icalcomp) {
2930                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
2931                         *objects = NULL;
2932                 } else {
2933                         ECalComponent *comp;
2934
2935                         kind = icalcomponent_isa (icalcomp);
2936                         if ((kind == ICAL_VEVENT_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_EVENT) ||
2937                             (kind == ICAL_VTODO_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_TODO)) {
2938                                 comp = e_cal_component_new ();
2939                                 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
2940                                 *objects = g_list_append (NULL, comp);
2941                         } else if (kind == ICAL_VCALENDAR_COMPONENT) {
2942                                 icalcomponent *subcomp;
2943                                 icalcomponent_kind kind_to_find;
2944
2945                                 switch (priv->type) {
2946                                 case E_CAL_SOURCE_TYPE_EVENT :
2947                                         kind_to_find = ICAL_VEVENT_COMPONENT;
2948                                         break;
2949                                 case E_CAL_SOURCE_TYPE_TODO :
2950                                         kind_to_find = ICAL_VTODO_COMPONENT;
2951                                         break;
2952                                 case E_CAL_SOURCE_TYPE_JOURNAL :
2953                                         kind_to_find = ICAL_VJOURNAL_COMPONENT;
2954                                         break;
2955                                 default:
2956                                         /* ignore everything else */
2957                                         kind_to_find = ICAL_NO_COMPONENT;
2958                                         break;
2959                                 }
2960
2961                                 *objects = NULL;
2962                                 subcomp = icalcomponent_get_first_component (icalcomp, kind_to_find);
2963                                 while (subcomp) {
2964                                         comp = e_cal_component_new ();
2965                                         e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp));
2966                                         *objects = g_list_append (*objects, comp);
2967                                         subcomp = icalcomponent_get_next_component (icalcomp, kind_to_find);
2968                                 }
2969                         }
2970
2971                         icalcomponent_free (icalcomp);
2972                 }
2973         }
2974         g_free (our_op->string);
2975
2976         e_calendar_remove_op (ecal, our_op);
2977         e_calendar_free_op (our_op);
2978
2979         E_CALENDAR_CHECK_STATUS (status, error);
2980 }
2981
2982 /**
2983  * e_cal_resolve_tzid_cb:
2984  * @tzid: ID of the timezone to resolve.
2985  * @data: Closure data for the callback.
2986  *
2987  * Resolves TZIDs for the recurrence generator.
2988  *
2989  * Return value: The timezone identified by the @tzid argument, or %NULL if
2990  * it could not be found.
2991  */
2992 icaltimezone*
2993 e_cal_resolve_tzid_cb (const char *tzid, gpointer data)
2994 {
2995         ECal *ecal;
2996         icaltimezone *zone = NULL;
2997
2998         g_return_val_if_fail (data != NULL, NULL);
2999         g_return_val_if_fail (E_IS_CAL (data), NULL);
3000         
3001         ecal = E_CAL (data);
3002
3003         /* FIXME: Handle errors. */
3004         e_cal_get_timezone (ecal, tzid, &zone, NULL);
3005
3006         return zone;
3007 }
3008
3009 /**
3010  * e_cal_get_changes:
3011  * @ecal: A calendar client.
3012  * @change_id: ID to use for comparing changes.
3013  * @changes: Return value for the list of changes.
3014  * @error: Placeholder for error information.
3015  *
3016  * Returns a list of changes made to the calendar since a specific time. That time
3017  * is identified by the @change_id argument, which is used by the backend to
3018  * compute the changes done.
3019  *
3020  * Return value: TRUE if the call was successful, FALSE otherwise.
3021  */
3022 gboolean
3023 e_cal_get_changes (ECal *ecal, const char *change_id, GList **changes, GError **error)
3024 {
3025         CORBA_Environment ev;
3026         ECalendarOp *our_op;
3027         ECalendarStatus status;
3028
3029         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
3030         e_return_error_if_fail (change_id, E_CALENDAR_STATUS_INVALID_ARG);
3031
3032         g_mutex_lock (ecal->priv->mutex);
3033
3034         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
3035                 g_mutex_unlock (ecal->priv->mutex);
3036                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
3037         }
3038
3039         if (ecal->priv->current_op != NULL) {
3040                 g_mutex_unlock (ecal->priv->mutex);
3041                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
3042         }
3043
3044         our_op = e_calendar_new_op (ecal);
3045
3046         g_mutex_unlock (ecal->priv->mutex);
3047
3048         CORBA_exception_init (&ev);
3049
3050         GNOME_Evolution_Calendar_Cal_getChanges (ecal->priv->cal, change_id, &ev);
3051
3052         if (BONOBO_EX (&ev)) {
3053                 e_calendar_remove_op (ecal, our_op);
3054                 e_calendar_free_op (our_op);
3055
3056                 CORBA_exception_free (&ev);
3057
3058                 g_warning (G_STRLOC ": Unable to contact backend");
3059
3060                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
3061         }
3062         
3063         CORBA_exception_free (&ev);
3064
3065         e_flag_wait (our_op->done);
3066
3067         status = our_op->status;
3068         *changes = our_op->list;
3069
3070         e_calendar_remove_op (ecal, our_op);
3071         e_calendar_free_op (our_op);
3072
3073         E_CALENDAR_CHECK_STATUS (status, error);
3074 }
3075
3076 /**
3077  * e_cal_free_change_list:
3078  * @list: List of changes to be freed.
3079  *
3080  * Free a list of changes as returned by #e_cal_get_changes.
3081  */
3082 void
3083 e_cal_free_change_list (GList *list)
3084 {
3085         ECalChange *c;
3086         GList *l;
3087
3088         for (l = list; l; l = l->next) {
3089                 c = l->data;
3090
3091                 g_assert (c != NULL);
3092                 g_assert (c->comp != NULL);
3093
3094                 g_object_unref (G_OBJECT (c->comp));
3095                 g_free (c);
3096         }
3097
3098         g_list_free (list);
3099 }
3100
3101 /**
3102  * e_cal_get_object_list:
3103  * @ecal: A calendar client.
3104  * @query: Query string.
3105  * @objects: Return value for list of objects.
3106  * @error: Placeholder for error information.
3107  * 
3108  * Gets a list of objects from the calendar that match the query specified
3109  * by the @query argument. The objects will be returned in the @objects
3110  * argument, which is a list of #icalcomponent. When done, this list
3111  * should be freed by using the #e_cal_free_object_list function.
3112  * 
3113  * Return value: TRUE if the operation was successful, FALSE otherwise.
3114  **/
3115 gboolean
3116 e_cal_get_object_list (ECal *ecal, const char *query, GList **objects, GError **error)
3117 {
3118         CORBA_Environment ev;
3119         ECalendarOp *our_op;
3120         ECalendarStatus status;
3121
3122
3123         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
3124         e_return_error_if_fail (query, E_CALENDAR_STATUS_INVALID_ARG);
3125
3126         g_mutex_lock (ecal->priv->mutex);
3127
3128         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
3129                 g_mutex_unlock (ecal->priv->mutex);
3130                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
3131         }
3132
3133         if (ecal->priv->current_op != NULL) {
3134                 g_mutex_unlock (ecal->priv->mutex);
3135                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
3136         }
3137
3138         our_op = e_calendar_new_op (ecal);
3139
3140         g_mutex_unlock (ecal->priv->mutex);
3141
3142         CORBA_exception_init (&ev);
3143
3144         GNOME_Evolution_Calendar_Cal_getObjectList (ecal->priv->cal, query, &ev);
3145
3146         if (BONOBO_EX (&ev)) {
3147                 e_calendar_remove_op (ecal, our_op);
3148                 e_calendar_free_op (our_op);
3149
3150                 CORBA_exception_free (&ev);
3151
3152                 g_warning (G_STRLOC ": Unable to contact backend");
3153
3154                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
3155         }
3156         
3157         CORBA_exception_free (&ev);
3158
3159         e_flag_wait (our_op->done);
3160
3161         status = our_op->status;
3162         *objects = our_op->list;
3163
3164         e_calendar_remove_op (ecal, our_op);
3165         e_calendar_free_op (our_op);
3166
3167         E_CALENDAR_CHECK_STATUS (status, error);
3168 }
3169
3170 /**
3171  * e_cal_get_object_list_as_comp:
3172  * @ecal: A calendar client.
3173  * @query: Query string.
3174  * @objects: Return value for list of objects.
3175  * @error: Placeholder for error information.
3176  * 
3177  * Gets a list of objects from the calendar that match the query specified
3178  * by the @query argument. The objects will be returned in the @objects
3179  * argument, which is a list of #ECalComponent.
3180  * 
3181  * Return value: TRUE if the operation was successful, FALSE otherwise.
3182  */
3183 gboolean
3184 e_cal_get_object_list_as_comp (ECal *ecal, const char *query, GList **objects, GError **error)
3185 {
3186         GList *ical_objects = NULL;
3187         GList *l;
3188
3189         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
3190         e_return_error_if_fail (query, E_CALENDAR_STATUS_INVALID_ARG);  
3191         e_return_error_if_fail (objects, E_CALENDAR_STATUS_INVALID_ARG);        
3192         
3193         if (!e_cal_get_object_list (ecal, query, &ical_objects, error))
3194                 return FALSE;
3195                 
3196         *objects = NULL;
3197         for (l = ical_objects; l; l = l->next) {
3198                 ECalComponent *comp;
3199                         
3200                 comp = e_cal_component_new ();
3201                 e_cal_component_set_icalcomponent (comp, l->data);
3202                 *objects = g_list_prepend (*objects, comp);
3203         }
3204                         
3205         g_list_free (ical_objects);
3206
3207         return TRUE;
3208 }
3209
3210 /**
3211  * e_cal_free_object_list:
3212  * @objects: List of objects to be freed.
3213  *
3214  * Frees a list of objects as returned by #e_cal_get_object_list.
3215  */
3216 void 
3217 e_cal_free_object_list (GList *objects)
3218 {
3219         GList *l;
3220         
3221         for (l = objects; l; l = l->next)
3222                 icalcomponent_free (l->data);
3223
3224         g_list_free (objects);
3225 }
3226
3227 /**
3228  * e_cal_get_free_busy
3229  * @ecal: A calendar client.
3230  * @users: List of users to retrieve free/busy information for.
3231  * @start: Start time for query.
3232  * @end: End time for query.
3233  * @freebusy: Return value for VFREEBUSY objects.
3234  * @error: Placeholder for error information.
3235  *
3236  * Gets free/busy information from the calendar server.
3237  *
3238  * Returns: TRUE if the operation was successful, FALSE otherwise.
3239  */
3240 gboolean
3241 e_cal_get_free_busy (ECal *ecal, GList *users, time_t start, time_t end,
3242                      GList **freebusy, GError **error)
3243 {
3244         CORBA_Environment ev;
3245         ECalendarOp *our_op;
3246         ECalendarStatus status;
3247         GNOME_Evolution_Calendar_UserList corba_users;
3248         GList *l;
3249         int i, len;
3250
3251         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
3252
3253         g_mutex_lock (ecal->priv->mutex);
3254
3255         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
3256                 g_mutex_unlock (ecal->priv->mutex);
3257                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
3258         }
3259
3260         if (ecal->priv->current_op != NULL) {
3261                 g_mutex_unlock (ecal->priv->mutex);
3262                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
3263         }
3264
3265         our_op = e_calendar_new_op (ecal);
3266
3267         g_mutex_unlock (ecal->priv->mutex);
3268
3269         /* create the CORBA user list to be passed to the backend */
3270         len = g_list_length (users);
3271
3272         corba_users._length = len;
3273         corba_users._buffer = CORBA_sequence_GNOME_Evolution_Calendar_User_allocbuf (len);
3274
3275         for (l = users, i = 0; l; l = l->next, i++)
3276                 corba_users._buffer[i] = CORBA_string_dup (l->data);
3277
3278         CORBA_exception_init (&ev);
3279
3280         GNOME_Evolution_Calendar_Cal_getFreeBusy (ecal->priv->cal, &corba_users, start, end, &ev);
3281
3282         CORBA_free (corba_users._buffer);
3283         
3284         if (BONOBO_EX (&ev)) {
3285                 e_calendar_remove_op (ecal, our_op);
3286                 e_calendar_free_op (our_op);
3287
3288                 CORBA_exception_free (&ev);
3289
3290                 g_warning (G_STRLOC ": Unable to contact backend");
3291
3292                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
3293         }
3294         
3295         CORBA_exception_free (&ev);
3296
3297         e_flag_wait (our_op->done);
3298
3299         status = our_op->status;
3300         *freebusy = our_op->list;
3301
3302         e_calendar_remove_op (ecal, our_op);
3303         e_calendar_free_op (our_op);
3304
3305         E_CALENDAR_CHECK_STATUS (status, error);
3306 }
3307
3308 struct comp_instance {
3309         ECalComponent *comp;
3310         time_t start;
3311         time_t end;
3312 };
3313
3314 struct instances_info {
3315         GList **instances;
3316         icaltimezone *start_zone;
3317 };
3318
3319 /* Called from cal_recur_generate_instances(); adds an instance to the list */
3320 static gboolean
3321 add_instance (ECalComponent *comp, time_t start, time_t end, gpointer data)
3322 {
3323         GList **list;
3324         struct comp_instance *ci;
3325         struct icaltimetype itt;
3326         icalcomponent *icalcomp;
3327         struct instances_info *instances_hold;
3328         ECalComponentDateTime datetime;
3329
3330         instances_hold = data;
3331         list = instances_hold->instances;
3332
3333         ci = g_new (struct comp_instance, 1);
3334
3335         icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
3336         e_cal_component_get_dtstart (comp, &datetime);
3337                 
3338         /* add the instance to the list */
3339         ci->comp = e_cal_component_new ();
3340         e_cal_component_set_icalcomponent (ci->comp, icalcomp);
3341
3342         /* set the RECUR-ID for the instance */
3343         if (e_cal_util_component_has_recurrences (icalcomp)) {
3344                 if (!(icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY))) {
3345                         ECalComponentRange *range;
3346
3347                         if (instances_hold->start_zone) 
3348                                 itt = icaltime_from_timet_with_zone (start, datetime.value->is_date, instances_hold->start_zone);
3349                         else {
3350                                 itt = icaltime_from_timet (start, datetime.value->is_date);
3351
3352                                 if (datetime.tzid) {
3353                                         g_free ((char *) datetime.tzid);
3354                                         datetime.tzid = NULL;
3355                                 }
3356                         }
3357
3358                         g_free (datetime.value);
3359                         datetime.value = &itt;
3360
3361                         range = g_new0 (ECalComponentRange, 1);
3362                         range->type = E_CAL_COMPONENT_RANGE_SINGLE;
3363                         range->datetime = datetime;
3364
3365                         e_cal_component_set_recurid (ci->comp, range);
3366
3367                         if (datetime.tzid)
3368                                 g_free ((char *) datetime.tzid);
3369                         g_free (range);
3370                 }
3371         }
3372
3373         ci->start = start;
3374         ci->end = end;
3375
3376         *list = g_list_prepend (*list, ci);
3377
3378         return TRUE;
3379 }
3380
3381 /* Used from g_list_sort(); compares two struct comp_instance structures */
3382 static gint
3383 compare_comp_instance (gconstpointer a, gconstpointer b)
3384 {
3385         const struct comp_instance *cia, *cib;
3386         time_t diff;
3387
3388         cia = a;
3389         cib = b;
3390
3391         diff = cia->start - cib->start;
3392         return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
3393 }
3394
3395 static GList *
3396 process_detached_instances (GList *instances, GList *detached_instances)
3397 {
3398         struct comp_instance *ci, *cid;
3399         GList *dl, *unprocessed_instances = NULL;
3400
3401         for (dl = detached_instances; dl != NULL; dl = dl->next) {
3402                 GList *il;
3403                 const char *uid;
3404                 gboolean processed;
3405                 ECalComponentRange recur_id, instance_recur_id;
3406
3407                 processed = FALSE;
3408
3409                 cid = dl->data;
3410                 e_cal_component_get_uid (cid->comp, &uid);
3411                 e_cal_component_get_recurid (cid->comp, &recur_id);
3412
3413                 /* search for coincident instances already expanded */
3414                 for (il = instances; il != NULL; il = il->next) {
3415                         const char *instance_uid;
3416                         int cmp;
3417
3418                         ci = il->data;
3419                         e_cal_component_get_uid (ci->comp, &instance_uid);
3420                         e_cal_component_get_recurid (ci->comp, &instance_recur_id);
3421                         if (strcmp (uid, instance_uid) == 0) {
3422                                 const char *i_rid = NULL, *d_rid = NULL;
3423
3424                                 i_rid = e_cal_component_get_recurid_as_string (ci->comp);
3425                                 d_rid = e_cal_component_get_recurid_as_string (cid->comp);
3426
3427                                 if (i_rid && d_rid && strcmp (i_rid, d_rid) == 0) {
3428                                         g_object_unref (ci->comp);
3429                                         ci->comp = g_object_ref (cid->comp);
3430                                         ci->start = cid->start;
3431                                         ci->end = cid->end;
3432
3433                                         processed = TRUE;
3434                                 } else {
3435                                         if (!instance_recur_id.datetime.value ||
3436                                             !recur_id.datetime.value) {
3437                                                 /*
3438                                                  * Prevent obvious segfault by ignoring missing
3439                                                  * recurrency ids. Real problem might be elsewhere,
3440                                                  * but anything is better than crashing...
3441                                                  */
3442                                                 g_log (G_LOG_DOMAIN,
3443                                                        G_LOG_LEVEL_CRITICAL,
3444                                                        "UID %s: instance RECURRENCE-ID %s + detached instance RECURRENCE-ID %s: cannot compare",
3445                                                        uid,
3446                                                        i_rid,
3447                                                        d_rid);
3448                                                 continue;
3449                                         }
3450                                         cmp = icaltime_compare (*instance_recur_id.datetime.value,
3451                                                                 *recur_id.datetime.value);
3452                                         if ((recur_id.type == E_CAL_COMPONENT_RANGE_THISPRIOR && cmp <= 0) ||
3453                                             (recur_id.type == E_CAL_COMPONENT_RANGE_THISFUTURE && cmp >= 0)) {
3454                                                 ECalComponent *comp;
3455
3456                                                 comp = e_cal_component_new ();
3457                                                 e_cal_component_set_icalcomponent (
3458                                                         comp,
3459                                                         icalcomponent_new_clone (e_cal_component_get_icalcomponent (cid->comp)));
3460                                                 e_cal_component_set_recurid (comp, &instance_recur_id);
3461
3462                                                 /* replace the generated instances */
3463                                                 g_object_unref (ci->comp);
3464                                                 ci->comp = comp;
3465                                         }
3466                                 }
3467                         }
3468                 }
3469
3470                 if (!processed)
3471                         unprocessed_instances = g_list_prepend (unprocessed_instances, cid);
3472         }
3473
3474         /* add the unprocessed instances (ie, detached instances with no master object */
3475         while (unprocessed_instances != NULL) {
3476                 cid = unprocessed_instances->data;
3477                 ci = g_new0 (struct comp_instance, 1);
3478                 ci->comp = g_object_ref (cid->comp);
3479                 ci->start = cid->start;
3480                 ci->end = cid->end;
3481                 instances = g_list_append (instances, ci);
3482
3483                 unprocessed_instances = g_list_remove (unprocessed_instances, cid);
3484         }
3485
3486         return instances;
3487 }
3488
3489 static void
3490 generate_instances (ECal *ecal, time_t start, time_t end, const char *uid,
3491                     ECalRecurInstanceFn cb, gpointer cb_data)
3492 {
3493         GList *objects = NULL;
3494         GList *instances, *detached_instances = NULL;
3495         GList *l;
3496         char *query;
3497         char *iso_start, *iso_end;
3498         ECalPrivate *priv;
3499
3500         priv = ecal->priv;
3501
3502         /* Generate objects */
3503         if (uid && *uid) {
3504                 if (!e_cal_get_objects_for_uid (ecal, uid, &objects, NULL))
3505                         return;
3506         }
3507         else {
3508                 iso_start = isodate_from_time_t (start);
3509                 if (!iso_start)
3510                         return;
3511
3512                 iso_end = isodate_from_time_t (end);
3513                 if (!iso_end) {
3514                         g_free (iso_start);
3515                         return;
3516                 }
3517
3518                 query = g_strdup_printf ("(occur-in-time-range? (make-time \"%s\") (make-time \"%s\"))",
3519                                          iso_start, iso_end);
3520                 g_free (iso_start);
3521                 g_free (iso_end);
3522                 if (!e_cal_get_object_list_as_comp (ecal, query, &objects, NULL)) {
3523                         g_free (query);
3524                         return;
3525                 }
3526                 g_free (query);
3527         }
3528
3529         instances = NULL;
3530
3531         for (l = objects; l; l = l->next) {
3532                 ECalComponent *comp;
3533                 icaltimezone *default_zone;
3534
3535                 if (priv->default_zone)
3536                         default_zone = priv->default_zone;
3537                 else
3538                         default_zone = icaltimezone_get_utc_timezone ();
3539
3540                 comp = l->data;
3541                 if (e_cal_component_is_instance (comp)) {
3542                         struct comp_instance *ci;
3543                         ECalComponentDateTime dtstart, dtend;
3544                         icaltimezone *start_zone = NULL, *end_zone = NULL;
3545
3546                         /* keep the detached instances apart */
3547                         ci = g_new0 (struct comp_instance, 1);
3548                         ci->comp = comp;
3549
3550                         e_cal_component_get_dtstart (comp, &dtstart);
3551                         e_cal_component_get_dtend (comp, &dtend);
3552
3553                         /* For DATE-TIME values with a TZID, we use 
3554                         e_cal_resolve_tzid_cb to resolve the TZID. 
3555                         For DATE values and DATE-TIME values without a
3556                         TZID (i.e. floating times) we use the default 
3557                         timezone. */
3558                         if (dtstart.tzid && !dtstart.value->is_date) {
3559                                 start_zone = e_cal_resolve_tzid_cb (dtstart.tzid, ecal);
3560                                 if (!start_zone)
3561                                         start_zone = default_zone;
3562                         } else {
3563                                 start_zone = default_zone;
3564                         }
3565
3566                         if (dtend.tzid && !dtend.value->is_date) {
3567                                 end_zone = e_cal_resolve_tzid_cb (dtend.tzid, ecal);
3568                                 if (!end_zone)
3569                                         end_zone = default_zone;
3570                         } else {
3571                                 end_zone = default_zone;
3572                         }
3573
3574                         ci->start = icaltime_as_timet_with_zone (*dtstart.value, start_zone);
3575
3576                         if (dtend.value)
3577                                 ci->end = icaltime_as_timet_with_zone (*dtend.value, end_zone);
3578                         else if (icaltime_is_date (*dtstart.value))
3579                                 ci->end = time_day_end (ci->start);
3580                         else 
3581                                 ci->end = ci->start;
3582
3583                         e_cal_component_free_datetime (&dtstart);
3584                         e_cal_component_free_datetime (&dtend);
3585  
3586                         detached_instances = g_list_prepend (detached_instances, ci);
3587                 } else {
3588                         ECalComponentDateTime datetime;
3589                         icaltimezone *start_zone;
3590                         struct instances_info *instances_hold;
3591                         
3592                         /* Get the start timezone */
3593                         e_cal_component_get_dtstart (comp, &datetime);
3594                         e_cal_get_timezone (ecal, datetime.tzid, &start_zone, NULL);
3595                         e_cal_component_free_datetime (&datetime);
3596
3597                         instances_hold = g_new0 (struct instances_info, 1);
3598                         instances_hold->instances = &instances;
3599                         instances_hold->start_zone = start_zone;
3600
3601                         e_cal_recur_generate_instances (comp, start, end, add_instance, instances_hold,
3602                                                         e_cal_resolve_tzid_cb, ecal,
3603                                                         default_zone);
3604                         
3605                         g_free (instances_hold);
3606                         g_object_unref (comp);
3607                 }
3608         }
3609
3610         g_list_free (objects);
3611
3612         /* Generate instances and spew them out */
3613
3614         instances = g_list_sort (instances, compare_comp_instance);
3615         instances = process_detached_instances (instances, detached_instances);
3616
3617         for (l = instances; l; l = l->next) {
3618                 struct comp_instance *ci;
3619                 gboolean result;
3620                 
3621                 ci = l->data;
3622                 
3623                 result = (* cb) (ci->comp, ci->start, ci->end, cb_data);
3624
3625                 if (!result)
3626                         break;
3627         }
3628
3629         /* Clean up */
3630
3631         for (l = instances; l; l = l->next) {
3632                 struct comp_instance *ci;
3633
3634                 ci = l->data;
3635                 g_object_unref (G_OBJECT (ci->comp));
3636                 g_free (ci);
3637         }
3638
3639         g_list_free (instances);
3640
3641         for (l = detached_instances; l; l = l->next) {
3642                 struct comp_instance *ci;
3643
3644                 ci = l->data;
3645                 g_object_unref (G_OBJECT (ci->comp));
3646                 g_free (ci);
3647         }
3648
3649         g_list_free (detached_instances);
3650
3651 }
3652
3653 /**
3654  * e_cal_generate_instances:
3655  * @ecal: A calendar client.
3656  * @start: Start time for query.
3657  * @end: End time for query.
3658  * @cb: Callback for each generated instance.
3659  * @cb_data: Closure data for the callback.
3660  * 
3661  * Does a combination of #e_cal_get_object_list () and
3662  * #e_cal_recur_generate_instances().  
3663  *
3664  * The callback function should do a g_object_ref() of the calendar component
3665  * it gets passed if it intends to keep it around, since it will be unref'ed
3666  * as soon as the callback returns.
3667  **/
3668 void
3669 e_cal_generate_instances (ECal *ecal, time_t start, time_t end,
3670                           ECalRecurInstanceFn cb, gpointer cb_data)
3671 {
3672         ECalPrivate *priv;
3673         
3674         g_return_if_fail (ecal != NULL);
3675         g_return_if_fail (E_IS_CAL (ecal));
3676
3677         priv = ecal->priv;
3678         g_return_if_fail (priv->load_state == E_CAL_LOAD_LOADED);
3679
3680         g_return_if_fail (start >= 0);
3681         g_return_if_fail (end >= 0);
3682         g_return_if_fail (cb != NULL);
3683
3684         generate_instances (ecal, start, end, NULL, cb, cb_data);
3685 }
3686
3687 /**
3688  * e_cal_generate_instances_for_object:
3689  * @ecal: A calendar client.
3690  * @icalcomp: Object to generate instances from.
3691  * @start: Start time for query.
3692  * @end: End time for query.
3693  * @cb: Callback for each generated instance.
3694  * @cb_data: Closure data for the callback.
3695  *
3696  * Does a combination of #e_cal_get_object_list () and
3697  * #e_cal_recur_generate_instances(), like #e_cal_generate_instances(), but
3698  * for a single object.
3699  *
3700  * The callback function should do a g_object_ref() of the calendar component
3701  * it gets passed if it intends to keep it around, since it will be unref'ed
3702  * as soon as the callback returns.
3703  **/
3704 void
3705 e_cal_generate_instances_for_object (ECal *ecal, icalcomponent *icalcomp,
3706                                      time_t start, time_t end,
3707                                      ECalRecurInstanceFn cb, gpointer cb_data)
3708 {
3709         ECalPrivate *priv;
3710         ECalComponent *comp;
3711         const char *uid;
3712         char *rid;
3713         gboolean result;
3714         GList *instances = NULL;
3715         ECalComponentDateTime datetime;
3716         icaltimezone *start_zone;
3717         struct instances_info *instances_hold;
3718
3719         g_return_if_fail (E_IS_CAL (ecal));
3720         g_return_if_fail (start >= 0);
3721         g_return_if_fail (end >= 0);
3722         g_return_if_fail (cb != NULL);
3723
3724         priv = ecal->priv;
3725
3726         comp = e_cal_component_new ();
3727         e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
3728
3729         /*If the backend stores it as individual instances and does not 
3730          * have a master object - do not expand*/
3731         if (e_cal_get_static_capability (ecal, CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER)) {
3732
3733                 /*return the same instance */
3734                 result = (* cb)  (comp, icaltime_as_timet_with_zone (icalcomponent_get_dtstart (icalcomp), ecal->priv->default_zone),
3735                                 icaltime_as_timet_with_zone (icalcomponent_get_dtend (icalcomp), ecal->priv->default_zone), cb_data);
3736                 g_object_unref (comp);
3737                 return;
3738         }
3739                 
3740         e_cal_component_get_uid (comp, &uid);
3741         /* string might be freed at any time, keep a copy */
3742         rid = g_strdup (e_cal_component_get_recurid_as_string (comp));
3743
3744         /* Get the start timezone */
3745         e_cal_component_get_dtstart (comp, &datetime);
3746         e_cal_get_timezone (ecal, datetime.tzid, &start_zone, NULL);
3747         e_cal_component_free_datetime (&datetime);
3748
3749         instances_hold = g_new0 (struct instances_info, 1);
3750         instances_hold->instances = &instances;
3751         instances_hold->start_zone = start_zone;
3752         
3753         /* generate all instances in the given time range */
3754         generate_instances (ecal, start, end, uid, add_instance, instances_hold);
3755
3756         instances = *(instances_hold->instances);
3757         /* now only return back the instances for the given object */
3758         result = TRUE;
3759         while (instances != NULL) {
3760                 struct comp_instance *ci;
3761                 const char *instance_rid;
3762
3763                 ci = instances->data;
3764
3765                 if (result) {
3766                         instance_rid = e_cal_component_get_recurid_as_string (ci->comp);
3767
3768                         if (rid && *rid) {
3769                                 if (instance_rid && *instance_rid && strcmp (rid, instance_rid) == 0)
3770                                         result = (* cb) (ci->comp, ci->start, ci->end, cb_data);
3771                         } else
3772                                 result = (* cb)  (ci->comp, ci->start, ci->end, cb_data);
3773                 }
3774
3775                 /* remove instance from list */
3776                 instances = g_list_remove (instances, ci);
3777                 g_object_unref (ci->comp);
3778                 g_free (ci);
3779         }
3780
3781         /* clean up */
3782         g_object_unref (comp);
3783         g_free (instances_hold);
3784         g_free (rid);
3785 }
3786
3787 /* Builds a list of ECalComponentAlarms structures */
3788 static GSList *
3789 build_component_alarms_list (ECal *ecal, GList *object_list, time_t start, time_t end)
3790 {
3791         GSList *comp_alarms;
3792         GList *l;
3793
3794         comp_alarms = NULL;
3795
3796         for (l = object_list; l != NULL; l = l->next) {
3797                 ECalComponent *comp;
3798                 ECalComponentAlarms *alarms;
3799                 ECalComponentAlarmAction omit[] = {-1};
3800
3801                 comp = e_cal_component_new ();
3802                 if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (l->data))) {
3803                         g_object_unref (G_OBJECT (comp));
3804                         continue;
3805                 }
3806
3807                 alarms = e_cal_util_generate_alarms_for_comp (comp, start, end, omit, e_cal_resolve_tzid_cb,
3808                                                               ecal, ecal->priv->default_zone);
3809                 if (alarms)
3810                         comp_alarms = g_slist_prepend (comp_alarms, alarms);
3811         }
3812
3813         return comp_alarms;
3814 }
3815
3816 /**
3817  * e_cal_get_alarms_in_range:
3818  * @ecal: A calendar client.
3819  * @start: Start time for query.
3820  * @end: End time for query.
3821  *
3822  * Queries a calendar for the alarms that trigger in the specified range of
3823  * time.
3824  *
3825  * Return value: A list of #ECalComponentAlarms structures.  This should be freed
3826  * using the #e_cal_free_alarms() function, or by freeing each element
3827  * separately with #e_cal_component_alarms_free() and then freeing the list with
3828  * #g_slist_free().
3829  **/
3830 GSList *
3831 e_cal_get_alarms_in_range (ECal *ecal, time_t start, time_t end)
3832 {
3833         ECalPrivate *priv;
3834         GSList *alarms;
3835         char *sexp, *iso_start, *iso_end;
3836         GList *object_list = NULL;
3837
3838         g_return_val_if_fail (ecal != NULL, NULL);
3839         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
3840
3841         priv = ecal->priv;
3842         g_return_val_if_fail (priv->load_state == E_CAL_LOAD_LOADED, NULL);
3843
3844         g_return_val_if_fail (start >= 0 && end >= 0, NULL);
3845         g_return_val_if_fail (start <= end, NULL);
3846
3847         iso_start = isodate_from_time_t (start);
3848         if (!iso_start)
3849                 return NULL;
3850
3851         iso_end = isodate_from_time_t (end);
3852         if (!iso_end) {
3853                 g_free (iso_start);
3854                 return NULL;
3855         }
3856
3857         /* build the query string */
3858         sexp = g_strdup_printf ("(has-alarms-in-range? (make-time \"%s\") (make-time \"%s\"))",
3859                                 iso_start, iso_end);
3860         g_free (iso_start);
3861         g_free (iso_end);
3862
3863         /* execute the query on the server */
3864         if (!e_cal_get_object_list (ecal, sexp, &object_list, NULL)) {
3865                 g_free (sexp);
3866                 return NULL;
3867         }
3868
3869         alarms = build_component_alarms_list (ecal, object_list, start, end);
3870
3871         g_list_foreach (object_list, (GFunc) icalcomponent_free, NULL);
3872         g_list_free (object_list);
3873         g_free (sexp);
3874
3875         return alarms;
3876 }
3877
3878 /**
3879  * e_cal_free_alarms:
3880  * @comp_alarms: A list of #ECalComponentAlarms structures.
3881  * 
3882  * Frees a list of #ECalComponentAlarms structures as returned by
3883  * e_cal_get_alarms_in_range().
3884  **/
3885 void
3886 e_cal_free_alarms (GSList *comp_alarms)
3887 {
3888         GSList *l;
3889
3890         for (l = comp_alarms; l; l = l->next) {
3891                 ECalComponentAlarms *alarms;
3892
3893                 alarms = l->data;
3894                 g_assert (alarms != NULL);
3895
3896                 e_cal_component_alarms_free (alarms);
3897         }
3898
3899         g_slist_free (comp_alarms);
3900 }
3901
3902 /**
3903  * e_cal_get_alarms_for_object:
3904  * @ecal: A calendar client.
3905  * @id: Unique identifier for a calendar component.
3906  * @start: Start time for query.
3907  * @end: End time for query.
3908  * @alarms: Return value for the component's alarm instances.  Will return NULL
3909  * if no instances occur within the specified time range.  This should be freed
3910  * using the e_cal_component_alarms_free() function.
3911  *
3912  * Queries a calendar for the alarms of a particular object that trigger in the
3913  * specified range of time.
3914  *
3915  * Return value: TRUE on success, FALSE if the object was not found.
3916  **/
3917 gboolean
3918 e_cal_get_alarms_for_object (ECal *ecal, const ECalComponentId *id,
3919                              time_t start, time_t end,
3920                              ECalComponentAlarms **alarms)
3921 {
3922         ECalPrivate *priv;
3923         icalcomponent *icalcomp;
3924         ECalComponent *comp;
3925         ECalComponentAlarmAction omit[] = {-1};
3926
3927         g_return_val_if_fail (ecal != NULL, FALSE);
3928         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
3929
3930         priv = ecal->priv;
3931         g_return_val_if_fail (priv->load_state == E_CAL_LOAD_LOADED, FALSE);
3932
3933         g_return_val_if_fail (id != NULL, FALSE);
3934         g_return_val_if_fail (start >= 0 && end >= 0, FALSE);
3935         g_return_val_if_fail (start <= end, FALSE);
3936         g_return_val_if_fail (alarms != NULL, FALSE);
3937
3938         *alarms = NULL;
3939
3940         if (!e_cal_get_object (ecal, id->uid, id->rid, &icalcomp, NULL))
3941                 return FALSE;
3942         if (!icalcomp)
3943                 return FALSE;
3944
3945         comp = e_cal_component_new ();
3946         if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
3947                 icalcomponent_free (icalcomp);
3948                 g_object_unref (G_OBJECT (comp));
3949                 return FALSE;
3950         }
3951
3952         *alarms = e_cal_util_generate_alarms_for_comp (comp, start, end, omit, e_cal_resolve_tzid_cb,
3953                                                        ecal, priv->default_zone);
3954
3955         return TRUE;
3956 }
3957
3958 /**
3959  * e_cal_discard_alarm
3960  * @ecal: A calendar ecal.
3961  * @comp: The component to discard the alarm from.
3962  * @auid: Unique identifier of the alarm to be discarded.
3963  * @error: Placeholder for error information.
3964  *
3965  * Tells the calendar backend to get rid of the alarm identified by the
3966  * @auid argument in @comp. Some backends might remove the alarm or
3967  * update internal information about the alarm be discarded, or, like
3968  * the file backend does, ignore the operation.
3969  *
3970  * Return value: TRUE if the operation was successful, FALSE otherwise.
3971  */
3972 gboolean
3973 e_cal_discard_alarm (ECal *ecal, ECalComponent *comp, const char *auid, GError **error)
3974 {
3975         ECalPrivate *priv;
3976         CORBA_Environment ev;
3977         ECalendarStatus status;
3978         ECalendarOp *our_op;
3979         const char *uid;
3980         
3981         g_return_val_if_fail (ecal != NULL, FALSE);
3982         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
3983
3984         priv = ecal->priv;
3985
3986         g_mutex_lock (ecal->priv->mutex);
3987
3988         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
3989                 g_mutex_unlock (ecal->priv->mutex);
3990                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
3991         }
3992
3993         if (ecal->priv->current_op != NULL) {
3994                 g_mutex_unlock (ecal->priv->mutex);
3995                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
3996         }
3997
3998         our_op = e_calendar_new_op (ecal);
3999
4000         g_mutex_unlock (ecal->priv->mutex);
4001
4002         e_cal_component_get_uid (comp, &uid);
4003
4004         CORBA_exception_init (&ev);
4005
4006         GNOME_Evolution_Calendar_Cal_discardAlarm (priv->cal, uid, auid, &ev);
4007         if (BONOBO_EX (&ev)) {
4008                 e_calendar_remove_op (ecal, our_op);
4009                 e_calendar_free_op (our_op);
4010
4011                 CORBA_exception_free (&ev);
4012
4013                 g_warning (G_STRLOC ": Unable to contact backend");
4014
4015                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4016         }
4017
4018         CORBA_exception_free (&ev);
4019
4020         e_flag_wait (our_op->done);
4021
4022         status = our_op->status;
4023         
4024         e_calendar_remove_op (ecal, our_op);
4025         e_calendar_free_op (our_op);
4026
4027         E_CALENDAR_CHECK_STATUS (status, error);
4028 }
4029
4030 typedef struct _ForeachTZIDCallbackData ForeachTZIDCallbackData;
4031 struct _ForeachTZIDCallbackData {
4032         ECal *ecal;
4033         GHashTable *timezone_hash;
4034         gboolean include_all_timezones;
4035         gboolean success;
4036 };
4037
4038 /* This adds the VTIMEZONE given by the TZID parameter to the GHashTable in
4039    data. */
4040 static void
4041 foreach_tzid_callback (icalparameter *param, void *cbdata)
4042 {
4043         ForeachTZIDCallbackData *data = cbdata;
4044         ECalPrivate *priv;
4045         const char *tzid;
4046         icaltimezone *zone;
4047         icalcomponent *vtimezone_comp;
4048         char *vtimezone_as_string;
4049
4050         priv = data->ecal->priv;
4051
4052         /* Get the TZID string from the parameter. */
4053         tzid = icalparameter_get_tzid (param);
4054         if (!tzid)
4055                 return;
4056
4057         /* Check if we've already added it to the GHashTable. */
4058         if (g_hash_table_lookup (data->timezone_hash, tzid))
4059                 return;
4060
4061         if (data->include_all_timezones) {
4062                 if (!e_cal_get_timezone (data->ecal, tzid, &zone, NULL)) {
4063                         data->success = FALSE;
4064                         return;
4065                 }
4066         } else {
4067                 /* Check if it is in our cache. If it is, it must already be
4068                    on the server so return. */
4069                 if (g_hash_table_lookup (priv->timezones, tzid))
4070                         return;
4071
4072                 /* Check if it is a builtin timezone. If it isn't, return. */
4073                 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
4074                 if (!zone)
4075                         return;
4076         }
4077
4078         /* Convert it to a string and add it to the hash. */
4079         vtimezone_comp = icaltimezone_get_component (zone);
4080         if (!vtimezone_comp)
4081                 return;
4082
4083         vtimezone_as_string = icalcomponent_as_ical_string (vtimezone_comp);
4084
4085         g_hash_table_insert (data->timezone_hash, (char*) tzid,
4086                              g_strdup (vtimezone_as_string));
4087 }
4088
4089 /* This appends the value string to the GString given in data. */
4090 static void
4091 append_timezone_string (gpointer key, gpointer value, gpointer data)
4092 {
4093         GString *vcal_string = data;
4094
4095         g_string_append (vcal_string, value);
4096         g_free (value);
4097 }
4098
4099
4100 /* This simply frees the hash values. */
4101 static void
4102 free_timezone_string (gpointer key, gpointer value, gpointer data)
4103 {
4104         g_free (value);
4105 }
4106
4107
4108 /* This converts the VEVENT/VTODO to a string. If include_all_timezones is
4109    TRUE, it includes all the VTIMEZONE components needed for the VEVENT/VTODO.
4110    If not, it only includes builtin timezones that may not be on the server.
4111
4112    To do that we check every TZID in the component to see if it is a builtin
4113    timezone. If it is, we see if it it in our cache. If it is in our cache,
4114    then we know the server already has it and we don't need to send it.
4115    If it isn't in our cache, then we need to send it to the server.
4116    If we need to send any timezones to the server, then we have to create a
4117    complete VCALENDAR object, otherwise we can just send a single VEVENT/VTODO
4118    as before. */
4119 static char*
4120 e_cal_get_component_as_string_internal (ECal *ecal,
4121                                         icalcomponent *icalcomp,
4122                                         gboolean include_all_timezones)
4123 {
4124         GHashTable *timezone_hash;
4125         GString *vcal_string;
4126         int initial_vcal_string_len;
4127         ForeachTZIDCallbackData cbdata;
4128         char *obj_string;
4129         ECalPrivate *priv;
4130
4131         priv = ecal->priv;
4132
4133         timezone_hash = g_hash_table_new (g_str_hash, g_str_equal);
4134
4135         /* Add any timezones needed to the hash. We use a hash since we only
4136            want to add each timezone once at most. */
4137         cbdata.ecal = ecal;
4138         cbdata.timezone_hash = timezone_hash;
4139         cbdata.include_all_timezones = include_all_timezones;
4140         cbdata.success = TRUE;
4141         icalcomponent_foreach_tzid (icalcomp, foreach_tzid_callback, &cbdata);
4142         if (!cbdata.success) {
4143                 g_hash_table_foreach (timezone_hash, free_timezone_string,
4144                                       NULL);
4145                 return NULL;
4146         }
4147
4148         /* Create the start of a VCALENDAR, to add the VTIMEZONES to,
4149            and remember its length so we know if any VTIMEZONEs get added. */
4150         vcal_string = g_string_new (NULL);
4151         g_string_append (vcal_string,
4152                          "BEGIN:VCALENDAR\n"
4153                          "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
4154                          "VERSION:2.0\n"
4155                          "METHOD:PUBLISH\n");
4156         initial_vcal_string_len = vcal_string->len;
4157
4158         /* Now concatenate all the timezone strings. This also frees the
4159            timezone strings as it goes. */
4160         g_hash_table_foreach (timezone_hash, append_timezone_string,
4161                               vcal_string);
4162
4163         /* Get the string for the VEVENT/VTODO. */
4164         obj_string = g_strdup (icalcomponent_as_ical_string (icalcomp));
4165
4166         /* If there were any timezones to send, create a complete VCALENDAR,
4167            else just send the VEVENT/VTODO string. */
4168         if (!include_all_timezones
4169             && vcal_string->len == initial_vcal_string_len) {
4170                 g_string_free (vcal_string, TRUE);
4171         } else {
4172                 g_string_append (vcal_string, obj_string);
4173                 g_string_append (vcal_string, "END:VCALENDAR\n");
4174                 g_free (obj_string);
4175                 obj_string = vcal_string->str;
4176                 g_string_free (vcal_string, FALSE);
4177         }
4178
4179         g_hash_table_destroy (timezone_hash);
4180
4181         return obj_string;
4182 }
4183
4184 /**
4185  * e_cal_get_component_as_string:
4186  * @ecal: A calendar client.
4187  * @icalcomp: A calendar component object.
4188  *
4189  * Gets a calendar component as an iCalendar string, with a toplevel
4190  * VCALENDAR component and all VTIMEZONEs needed for the component.
4191  *
4192  * Return value: the component as a complete iCalendar string, or NULL on
4193  * failure. The string should be freed after use.
4194  **/
4195 char*
4196 e_cal_get_component_as_string (ECal *ecal, icalcomponent *icalcomp)
4197 {
4198         return e_cal_get_component_as_string_internal (ecal, icalcomp, TRUE);
4199 }
4200
4201 /**
4202  * e_cal_create_object:
4203  * @ecal: A calendar client.
4204  * @icalcomp: The component to create.
4205  * @uid: Return value for the UID assigned to the new component by the calendar backend.
4206  * @error: Placeholder for error information.
4207  *
4208  * Requests the calendar backend to create the object specified by the @icalcomp
4209  * argument. Some backends would assign a specific UID to the newly created object,
4210  * in those cases that UID would be returned in the @uid argument.
4211  *
4212  * Return value: TRUE if the operation was successful, FALSE otherwise.
4213  */
4214 gboolean
4215 e_cal_create_object (ECal *ecal, icalcomponent *icalcomp, char **uid, GError **error)
4216 {
4217         ECalPrivate *priv;
4218         CORBA_Environment ev;
4219         ECalendarStatus status;
4220         ECalendarOp *our_op;
4221
4222         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4223
4224         priv = ecal->priv;
4225
4226         g_mutex_lock (ecal->priv->mutex);
4227
4228         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
4229                 g_mutex_unlock (ecal->priv->mutex);
4230                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4231         }
4232
4233         if (ecal->priv->current_op != NULL) {
4234                 g_mutex_unlock (ecal->priv->mutex);
4235                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4236         }
4237
4238         our_op = e_calendar_new_op (ecal);
4239
4240         g_mutex_unlock (ecal->priv->mutex);
4241
4242         CORBA_exception_init (&ev);
4243
4244         GNOME_Evolution_Calendar_Cal_createObject (priv->cal, icalcomponent_as_ical_string (icalcomp), &ev);
4245         if (BONOBO_EX (&ev)) {
4246                 e_calendar_remove_op (ecal, our_op);
4247                 e_calendar_free_op (our_op);
4248
4249                 CORBA_exception_free (&ev);
4250
4251                 g_warning (G_STRLOC ": Unable to contact backend");
4252
4253                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4254         }
4255
4256         CORBA_exception_free (&ev);
4257
4258         e_flag_wait (our_op->done);
4259
4260         status = our_op->status;
4261         if (uid)
4262         {
4263                 *uid = our_op->uid;
4264                 icalcomponent_set_uid (icalcomp, *uid);
4265         }
4266         
4267         e_calendar_remove_op (ecal, our_op);
4268         e_calendar_free_op (our_op);
4269
4270         E_CALENDAR_CHECK_STATUS (status, error);
4271 }
4272
4273 /**
4274  * e_cal_modify_object:
4275  * @ecal: A calendar client.
4276  * @icalcomp: Component to modify.
4277  * @mod: Type of modification.
4278  * @error: Placeholder for error information.
4279  *
4280  * Requests the calendar backend to modify an existing object. If the object
4281  * does not exist on the calendar, an error will be returned.
4282  *
4283  * For recurrent appointments, the @mod argument specifies what to modify,
4284  * if all instances (CALOBJ_MOD_ALL), a single instance (CALOBJ_MOD_THIS),
4285  * or a specific set of instances (CALOBJ_MOD_THISNADPRIOR and
4286  * CALOBJ_MOD_THISANDFUTURE).
4287  *
4288  * Return value: TRUE if the operation was successful, FALSE otherwise.
4289  */
4290 gboolean
4291 e_cal_modify_object (ECal *ecal, icalcomponent *icalcomp, CalObjModType mod, GError **error)
4292 {
4293         ECalPrivate *priv;
4294         CORBA_Environment ev;
4295         ECalendarStatus status;
4296         ECalendarOp *our_op;
4297
4298         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4299         e_return_error_if_fail (icalcomp, E_CALENDAR_STATUS_INVALID_ARG);
4300
4301         priv = ecal->priv;
4302
4303         g_mutex_lock (ecal->priv->mutex);
4304
4305         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
4306                 g_mutex_unlock (ecal->priv->mutex);
4307                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4308         }
4309
4310         if (ecal->priv->current_op != NULL) {
4311                 g_mutex_unlock (ecal->priv->mutex);
4312                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4313         }
4314
4315         our_op = e_calendar_new_op (ecal);
4316
4317         g_mutex_unlock (ecal->priv->mutex);
4318
4319         CORBA_exception_init (&ev);
4320
4321         GNOME_Evolution_Calendar_Cal_modifyObject (priv->cal, icalcomponent_as_ical_string (icalcomp), mod, &ev);
4322         if (BONOBO_EX (&ev)) {
4323                 e_calendar_remove_op (ecal, our_op);
4324                 e_calendar_free_op (our_op);
4325
4326                 CORBA_exception_free (&ev);
4327
4328                 g_warning (G_STRLOC ": Unable to contact backend");
4329
4330                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4331         }
4332
4333         CORBA_exception_free (&ev);
4334
4335         e_flag_wait (our_op->done);
4336
4337         status = our_op->status;
4338         
4339         e_calendar_remove_op (ecal, our_op);
4340         e_calendar_free_op (our_op);
4341
4342         E_CALENDAR_CHECK_STATUS (status, error);
4343 }
4344
4345 /**
4346  * e_cal_remove_object_with_mod:
4347  * @ecal: A calendar client.
4348  * @uid: UID og the object to remove.
4349  * @rid: Recurrence ID of the specific recurrence to remove.
4350  * @mod: Type of removal.
4351  * @error: Placeholder for error information.
4352  *
4353  * This function allows the removal of instances of a recurrent
4354  * appointment. By using a combination of the @uid, @rid and @mod
4355  * arguments, you can remove specific instances. If what you want
4356  * is to remove all instances, use e_cal_remove_object instead.
4357  *
4358  * If not all instances are removed, the client will get a "obj_modified"
4359  * signal, while it will get a "obj_removed" signal when all instances
4360  * are removed.
4361  *
4362  * Return value: TRUE if the operation was successful, FALSE otherwise.
4363  */
4364 gboolean
4365 e_cal_remove_object_with_mod (ECal *ecal, const char *uid,
4366                               const char *rid, CalObjModType mod, GError **error)
4367 {
4368         ECalPrivate *priv;
4369         CORBA_Environment ev;
4370         ECalendarStatus status;
4371         ECalendarOp *our_op;
4372
4373         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4374         e_return_error_if_fail (uid, E_CALENDAR_STATUS_INVALID_ARG);
4375
4376         priv = ecal->priv;
4377
4378         g_mutex_lock (ecal->priv->mutex);
4379
4380         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
4381                 g_mutex_unlock (ecal->priv->mutex);
4382                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4383         }
4384
4385         if (ecal->priv->current_op != NULL) {
4386                 g_mutex_unlock (ecal->priv->mutex);
4387                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4388         }
4389
4390         our_op = e_calendar_new_op (ecal);
4391
4392         g_mutex_unlock (ecal->priv->mutex);
4393
4394
4395         CORBA_exception_init (&ev);
4396
4397         GNOME_Evolution_Calendar_Cal_removeObject (priv->cal, uid, rid ? rid : "", mod, &ev);
4398         if (BONOBO_EX (&ev)) {
4399                 e_calendar_remove_op (ecal, our_op);
4400                 e_calendar_free_op (our_op);
4401
4402                 CORBA_exception_free (&ev);
4403
4404                 g_warning (G_STRLOC ": Unable to contact backend");
4405
4406                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4407         }
4408
4409         CORBA_exception_free (&ev);
4410
4411         e_flag_wait (our_op->done);
4412
4413         status = our_op->status;
4414         
4415         e_calendar_remove_op (ecal, our_op);
4416         e_calendar_free_op (our_op);
4417
4418         E_CALENDAR_CHECK_STATUS (status, error);
4419 }
4420
4421 /**
4422  * e_cal_remove_object:
4423  * @ecal:  A calendar client.
4424  * @uid: Unique identifier of the calendar component to remove.
4425  * @error: Placeholder for error information.
4426  * 
4427  * Asks a calendar to remove a component.  If the server is able to remove the
4428  * component, all clients will be notified and they will emit the "obj_removed"
4429  * signal.
4430  * 
4431  * Return value: %TRUE if successful, %FALSE otherwise.
4432  **/
4433 gboolean
4434 e_cal_remove_object (ECal *ecal, const char *uid, GError **error)
4435 {
4436         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4437         e_return_error_if_fail (uid, E_CALENDAR_STATUS_INVALID_ARG);
4438
4439         return e_cal_remove_object_with_mod (ecal, uid, NULL, CALOBJ_MOD_THIS, error);
4440 }
4441
4442 /**
4443  * e_cal_receive_objects:
4444  * @ecal:  A calendar client.
4445  * @icalcomp: An icalcomponent.
4446  * @error: Placeholder for error information.
4447  *
4448  * Makes the backend receive the set of iCalendar objects specified in the
4449  * @icalcomp argument. This is used for iTIP confirmation/cancellation
4450  * messages for scheduled meetings.
4451  *
4452  * Return value: %TRUE if successful, %FALSE otherwise.
4453  */
4454 gboolean
4455 e_cal_receive_objects (ECal *ecal, icalcomponent *icalcomp, GError **error)
4456 {
4457         ECalPrivate *priv;
4458         CORBA_Environment ev;
4459         ECalendarStatus status;
4460         ECalendarOp *our_op;
4461
4462         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4463
4464         priv = ecal->priv;
4465
4466         g_mutex_lock (ecal->priv->mutex);
4467
4468         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
4469                 g_mutex_unlock (ecal->priv->mutex);
4470                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4471         }
4472
4473         if (ecal->priv->current_op != NULL) {
4474                 g_mutex_unlock (ecal->priv->mutex);
4475                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4476         }
4477
4478         our_op = e_calendar_new_op (ecal);
4479
4480         g_mutex_unlock (ecal->priv->mutex);
4481
4482         CORBA_exception_init (&ev);
4483
4484         GNOME_Evolution_Calendar_Cal_receiveObjects (priv->cal, icalcomponent_as_ical_string (icalcomp), &ev);
4485         if (BONOBO_EX (&ev)) {
4486                 e_calendar_remove_op (ecal, our_op);
4487                 e_calendar_free_op (our_op);
4488
4489                 CORBA_exception_free (&ev);
4490
4491                 g_warning (G_STRLOC ": Unable to contact backend");
4492
4493                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4494         }
4495
4496         CORBA_exception_free (&ev);
4497
4498         e_flag_wait (our_op->done);
4499
4500         status = our_op->status;
4501         
4502         e_calendar_remove_op (ecal, our_op);
4503         e_calendar_free_op (our_op);
4504
4505         E_CALENDAR_CHECK_STATUS (status, error);
4506 }
4507
4508 /**
4509  * e_cal_send_objects:
4510  * @ecal: A calendar client.
4511  * @icalcomp: An icalcomponent.
4512  * @users: List of users to send the objects to.
4513  * @modified_icalcomp: Return value for the icalcomponent after all the operations
4514  * performed.
4515  * @error: Placeholder for error information.
4516  *
4517  * Requests a calendar backend to send meeting information to the specified list
4518  * of users.
4519  *
4520  * Return value: TRUE if the operation was successful, FALSE otherwise.
4521  */
4522 gboolean
4523 e_cal_send_objects (ECal *ecal, icalcomponent *icalcomp, GList **users, icalcomponent **modified_icalcomp, GError **error)
4524 {
4525         ECalPrivate *priv;
4526         CORBA_Environment ev;
4527         ECalendarStatus status;
4528         ECalendarOp *our_op;
4529
4530         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4531
4532         priv = ecal->priv;
4533
4534         g_mutex_lock (ecal->priv->mutex);
4535
4536         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
4537                 g_mutex_unlock (ecal->priv->mutex);
4538                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4539         }
4540
4541         if (ecal->priv->current_op != NULL) {
4542                 g_mutex_unlock (ecal->priv->mutex);
4543                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4544         }
4545
4546         our_op = e_calendar_new_op (ecal);
4547
4548         g_mutex_unlock (ecal->priv->mutex);
4549
4550         CORBA_exception_init (&ev);
4551
4552         GNOME_Evolution_Calendar_Cal_sendObjects (priv->cal, icalcomponent_as_ical_string (icalcomp), &ev);
4553         if (BONOBO_EX (&ev)) {
4554                 e_calendar_remove_op (ecal, our_op);
4555                 e_calendar_free_op (our_op);
4556
4557                 CORBA_exception_free (&ev);
4558
4559                 g_warning (G_STRLOC ": Unable to contact backend");
4560
4561                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4562         }
4563
4564         CORBA_exception_free (&ev);
4565
4566         e_flag_wait (our_op->done);
4567
4568         status = our_op->status;
4569         *users = our_op->list;
4570         if (status != E_CALENDAR_STATUS_OK) {
4571                 *modified_icalcomp = NULL;
4572                 g_list_foreach (*users, (GFunc) g_free, NULL);
4573                 g_list_free (*users);
4574                 *users = NULL;
4575         } else {
4576                 *modified_icalcomp = icalparser_parse_string (our_op->string);
4577                 if (!(*modified_icalcomp)) {
4578                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
4579                         g_list_foreach (*users, (GFunc) g_free, NULL);
4580                         g_list_free (*users);
4581                         *users = NULL;
4582                 }               
4583         }
4584         g_free (our_op->string);
4585         
4586         e_calendar_remove_op (ecal, our_op);
4587         e_calendar_free_op (our_op);
4588
4589         E_CALENDAR_CHECK_STATUS (status, error);
4590 }
4591
4592 /**
4593  * e_cal_get_timezone:
4594  * @ecal: A calendar client.
4595  * @tzid: ID of the timezone to retrieve.
4596  * @zone: Return value for the timezone.
4597  * @error: Placeholder for error information.
4598  *
4599  * Retrieves a timezone object from the calendar backend.
4600  *
4601  * Return value: TRUE if the operation was successful, FALSE otherwise.
4602  */
4603 gboolean
4604 e_cal_get_timezone (ECal *ecal, const char *tzid, icaltimezone **zone, GError **error)
4605 {
4606         ECalPrivate *priv;
4607         CORBA_Environment ev;
4608         ECalendarStatus status;
4609         ECalendarOp *our_op;
4610         icalcomponent *icalcomp;
4611
4612         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4613         e_return_error_if_fail (zone, E_CALENDAR_STATUS_INVALID_ARG);
4614
4615         priv = ecal->priv;
4616
4617         g_mutex_lock (priv->mutex);
4618
4619         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
4620                 g_mutex_unlock (ecal->priv->mutex);
4621                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4622         }
4623
4624         if (ecal->priv->current_op != NULL) {
4625                 g_mutex_unlock (ecal->priv->mutex);
4626                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4627         }
4628
4629         our_op = e_calendar_new_op (ecal);
4630
4631         g_mutex_unlock (priv->mutex);
4632
4633         /* Check for well known zones and in the cache */
4634         *zone = NULL;
4635         
4636         /* If tzid is NULL or "" we return NULL, since it is a 'local time'. */
4637         if (!tzid || !tzid[0]) {
4638                 e_calendar_remove_op (ecal, our_op);
4639                 e_calendar_free_op (our_op);
4640
4641                 *zone = NULL;
4642                 
4643                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);          
4644         }
4645
4646         /* If it is UTC, we return the special UTC timezone. */
4647         if (!strcmp (tzid, "UTC")) {
4648                 *zone = icaltimezone_get_utc_timezone ();
4649         } else {
4650                 /* See if we already have it in the cache. */
4651                 *zone = g_hash_table_lookup (priv->timezones, tzid);
4652         }
4653         
4654         if (*zone) {
4655                 e_calendar_remove_op (ecal, our_op);
4656                 e_calendar_free_op (our_op);
4657
4658                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);  
4659         }
4660         
4661         /* call the backend */
4662         CORBA_exception_init (&ev);
4663
4664         GNOME_Evolution_Calendar_Cal_getTimezone (priv->cal, tzid, &ev);
4665         if (BONOBO_EX (&ev)) {
4666                 e_calendar_remove_op (ecal, our_op);
4667                 e_calendar_free_op (our_op);
4668
4669                 CORBA_exception_free (&ev);
4670
4671                 g_warning (G_STRLOC ": Unable to contact backend");
4672
4673                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4674         }
4675
4676         CORBA_exception_free (&ev);
4677
4678         e_flag_wait (our_op->done);
4679
4680         status = our_op->status;
4681         if (status != E_CALENDAR_STATUS_OK){ 
4682                 icalcomp = NULL;
4683         } else {
4684                 icalcomp = icalparser_parse_string (our_op->string);
4685                 if (!icalcomp)
4686                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
4687         }
4688         g_free (our_op->string);
4689         
4690         if (!icalcomp) {
4691                 e_calendar_remove_op (ecal, our_op);
4692                 e_calendar_free_op (our_op);
4693
4694                 E_CALENDAR_CHECK_STATUS (status, error);
4695         }
4696         
4697         *zone = icaltimezone_new ();    
4698         if (!icaltimezone_set_component (*zone, icalcomp)) {
4699                 icaltimezone_free (*zone, 1);
4700
4701                 e_calendar_remove_op (ecal, our_op);
4702                 e_calendar_free_op (our_op);
4703
4704                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OBJECT_NOT_FOUND, error);
4705         }
4706
4707         /* Now add it to the cache, to avoid the server call in future. */
4708         g_hash_table_insert (priv->timezones, icaltimezone_get_tzid (*zone), *zone);
4709
4710         e_calendar_remove_op (ecal, our_op);
4711         e_calendar_free_op (our_op);
4712
4713         E_CALENDAR_CHECK_STATUS (status, error);
4714 }
4715
4716 /**
4717  * e_cal_add_timezone
4718  * @ecal: A calendar client.
4719  * @izone: The timezone to add.
4720  * @error: Placeholder for error information.
4721  *
4722  * Add a VTIMEZONE object to the given calendar.
4723  *
4724  * Returns: TRUE if successful, FALSE otherwise.
4725  */
4726 gboolean
4727 e_cal_add_timezone (ECal *ecal, icaltimezone *izone, GError **error)
4728 {
4729         ECalPrivate *priv;
4730         CORBA_Environment ev;
4731         ECalendarStatus status;
4732         ECalendarOp *our_op;
4733         const char *tzobj;
4734         icalcomponent *icalcomp;
4735
4736         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4737         e_return_error_if_fail (izone, E_CALENDAR_STATUS_INVALID_ARG);
4738         
4739         priv = ecal->priv;
4740
4741         g_mutex_lock (priv->mutex);
4742
4743         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
4744                 g_mutex_unlock (ecal->priv->mutex);
4745                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4746         }
4747
4748         if (ecal->priv->current_op != NULL) {
4749                 g_mutex_unlock (ecal->priv->mutex);
4750                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4751         }
4752
4753         our_op = e_calendar_new_op (ecal);
4754
4755         g_mutex_unlock (priv->mutex);
4756
4757         /* Make sure we have a valid component - UTC doesn't, nor do
4758          * we really have to add it */
4759         if (izone == icaltimezone_get_utc_timezone ()) {
4760                 e_calendar_remove_op (ecal, our_op);
4761                 e_calendar_free_op (our_op);
4762                 
4763                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4764         }
4765         
4766         icalcomp = icaltimezone_get_component (izone);
4767         if (!icalcomp) {
4768                 e_calendar_remove_op (ecal, our_op);
4769                 e_calendar_free_op (our_op);
4770
4771                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_INVALID_ARG, error);
4772         }
4773
4774         /* convert icaltimezone into a string */        
4775         tzobj = icalcomponent_as_ical_string (icalcomp);
4776
4777         /* call the backend */
4778         CORBA_exception_init (&ev);
4779
4780         GNOME_Evolution_Calendar_Cal_addTimezone (priv->cal, tzobj, &ev);
4781         if (BONOBO_EX (&ev)) {
4782                 e_calendar_remove_op (ecal, our_op);
4783                 e_calendar_free_op (our_op);
4784
4785                 CORBA_exception_free (&ev);
4786
4787                 g_warning (G_STRLOC ": Unable to contact backend");
4788
4789                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4790         }
4791
4792         CORBA_exception_free (&ev);
4793
4794         e_flag_wait (our_op->done);
4795
4796         status = our_op->status;
4797         
4798         e_calendar_remove_op (ecal, our_op);
4799         e_calendar_free_op (our_op);
4800
4801         E_CALENDAR_CHECK_STATUS (status, error);
4802 }
4803
4804 /**
4805  * e_cal_get_query:
4806  * @ecal: A calendar client.
4807  * @sexp: S-expression representing the query.
4808  * @query: Return value for the new query.
4809  * @error: Placeholder for error information.
4810  * 
4811  * Creates a live query object from a loaded calendar.
4812  * 
4813  * Return value: A query object that will emit notification signals as calendar
4814  * components are added and removed from the query in the server.
4815  **/
4816 gboolean
4817 e_cal_get_query (ECal *ecal, const char *sexp, ECalView **query, GError **error)
4818 {
4819         CORBA_Environment ev;
4820         ECalendarOp *our_op;
4821         ECalendarStatus status;
4822
4823         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4824         e_return_error_if_fail (query, E_CALENDAR_STATUS_INVALID_ARG);
4825
4826         g_mutex_lock (ecal->priv->mutex);
4827
4828         if (ecal->priv->load_state != E_CAL_LOAD_LOADED) {
4829                 g_mutex_unlock (ecal->priv->mutex);
4830                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4831         }
4832
4833         if (ecal->priv->current_op != NULL) {
4834                 g_mutex_unlock (ecal->priv->mutex);
4835                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4836         }
4837
4838         our_op = e_calendar_new_op (ecal);
4839
4840         g_mutex_unlock (ecal->priv->mutex);
4841
4842         CORBA_exception_init (&ev);
4843
4844         our_op->listener = e_cal_view_listener_new ();
4845         GNOME_Evolution_Calendar_Cal_getQuery (ecal->priv->cal, sexp, BONOBO_OBJREF (our_op->listener), &ev);
4846
4847         if (BONOBO_EX (&ev)) {
4848                 e_calendar_remove_op (ecal, our_op);
4849                 e_calendar_free_op (our_op);
4850
4851                 CORBA_exception_free (&ev);
4852
4853                 g_warning (G_STRLOC ": Unable to contact backend");
4854
4855                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4856         }
4857         
4858         CORBA_exception_free (&ev);
4859
4860         e_flag_wait (our_op->done);
4861
4862         status = our_op->status;
4863         *query = our_op->query;
4864
4865         bonobo_object_unref (BONOBO_OBJECT (our_op->listener));
4866         
4867         e_calendar_remove_op (ecal, our_op);
4868         e_calendar_free_op (our_op);
4869
4870         E_CALENDAR_CHECK_STATUS (status, error);
4871 }
4872
4873 /**
4874  * e_cal_set_default_timezone:
4875  * @ecal: A calendar client.
4876  * @zone: A timezone object.
4877  * @error: Placeholder for error information.
4878  *
4879  * Sets the default timezone on the calendar. This should be called before opening
4880  * the calendar.
4881  *
4882  * Return value: TRUE if the operation was successful, FALSE otherwise.
4883  */
4884 gboolean
4885 e_cal_set_default_timezone (ECal *ecal, icaltimezone *zone, GError **error)
4886 {
4887         ECalPrivate *priv;
4888         CORBA_Environment ev;
4889         ECalendarStatus status;
4890         ECalendarOp *our_op;
4891         icalcomponent *icalcomp = NULL;
4892         char *tzobj;
4893
4894         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
4895         e_return_error_if_fail (zone, E_CALENDAR_STATUS_INVALID_ARG);   
4896
4897         priv = ecal->priv;
4898
4899         /* Don't set the same timezone multiple times */
4900         if (priv->default_zone == zone)
4901                 return FALSE;
4902         
4903         g_mutex_lock (priv->mutex);
4904
4905         if (ecal->priv->current_op != NULL) {
4906                 g_mutex_unlock (ecal->priv->mutex);
4907                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_BUSY, error);
4908         }
4909
4910         our_op = e_calendar_new_op (ecal);
4911
4912         g_mutex_unlock (priv->mutex);
4913
4914         /* FIXME Adding it to the server to change the tzid */
4915         icalcomp = icaltimezone_get_component (zone);
4916         if (!icalcomp) {
4917                 e_calendar_remove_op (ecal, our_op);
4918                 e_calendar_free_op (our_op);
4919
4920                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_INVALID_ARG, error);
4921         }
4922
4923         /* convert icaltimezone into a string */        
4924         tzobj = icalcomponent_as_ical_string (icalcomp);
4925
4926         /* call the backend */
4927         CORBA_exception_init (&ev);
4928
4929         GNOME_Evolution_Calendar_Cal_setDefaultTimezone (priv->cal, tzobj, &ev);
4930         if (BONOBO_EX (&ev)) {
4931                 e_calendar_remove_op (ecal, our_op);
4932                 e_calendar_free_op (our_op);
4933
4934                 CORBA_exception_free (&ev);
4935
4936                 g_warning (G_STRLOC ": Unable to contact backend");
4937
4938                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
4939         }
4940
4941         CORBA_exception_free (&ev);
4942
4943         e_flag_wait (our_op->done);
4944
4945         status = our_op->status;
4946
4947         /* set the default timezone internally if successful */
4948         if (our_op->status == E_CALENDAR_STATUS_OK) {
4949                 g_mutex_lock (priv->mutex);
4950                 priv->default_zone = zone;
4951                 g_mutex_unlock (priv->mutex);
4952         }
4953
4954         e_calendar_remove_op (ecal, our_op);
4955         e_calendar_free_op (our_op);
4956
4957         E_CALENDAR_CHECK_STATUS (status, error);
4958 }
4959
4960 /**
4961  * e_cal_get_error_message
4962  * @status: A status code.
4963  *
4964  * Gets an error message for the given status code.
4965  *
4966  * Returns: the error message.
4967  */
4968 const char *
4969 e_cal_get_error_message (ECalendarStatus status)
4970 {
4971         switch (status) {
4972         case E_CALENDAR_STATUS_INVALID_ARG :
4973                 return _("Invalid argument");
4974         case E_CALENDAR_STATUS_BUSY :
4975                 return _("Backend is busy");
4976         case E_CALENDAR_STATUS_REPOSITORY_OFFLINE :
4977                 return _("Repository is offline");
4978         case E_CALENDAR_STATUS_NO_SUCH_CALENDAR :
4979                 return _("No such calendar");
4980         case E_CALENDAR_STATUS_OBJECT_NOT_FOUND :
4981                 return _("Object not found");
4982         case E_CALENDAR_STATUS_INVALID_OBJECT :
4983                 return _("Invalid object");
4984         case E_CALENDAR_STATUS_URI_NOT_LOADED :
4985                 return _("URI not loaded");
4986         case E_CALENDAR_STATUS_URI_ALREADY_LOADED :
4987                 return _("URI already loaded");
4988         case E_CALENDAR_STATUS_PERMISSION_DENIED :
4989                 return _("Permission denied");
4990         case E_CALENDAR_STATUS_UNKNOWN_USER :
4991                 return _("Unknown User");
4992         case E_CALENDAR_STATUS_OBJECT_ID_ALREADY_EXISTS :
4993                 return _("Object ID already exists");
4994         case E_CALENDAR_STATUS_PROTOCOL_NOT_SUPPORTED :
4995                 return _("Protocol not supported");
4996         case E_CALENDAR_STATUS_CANCELLED :
4997                 return _("Operation has been canceled");
4998         case E_CALENDAR_STATUS_COULD_NOT_CANCEL :
4999                 return _("Could not cancel operation");
5000         case E_CALENDAR_STATUS_AUTHENTICATION_FAILED :
5001                 return _("Authentication failed");
5002         case E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED :
5003                 return _("Authentication required");
5004         case E_CALENDAR_STATUS_CORBA_EXCEPTION :
5005                 return _("A CORBA exception has occurred");
5006         case E_CALENDAR_STATUS_OTHER_ERROR :
5007                 return _("Unknown error");
5008         case E_CALENDAR_STATUS_OK :
5009                 return _("No error");
5010         default:
5011                 /* ignore everything else */
5012                 break;
5013         }
5014
5015         return NULL;
5016 }
5017
5018 static gboolean
5019 get_default (ECal **ecal, ESourceList *sources, ECalSourceType type, ECalAuthFunc func, gpointer data, GError **error)
5020 {
5021         GSList *g;
5022         GError *err = NULL;
5023         ESource *default_source = NULL;
5024         gboolean rv = TRUE;
5025
5026         for (g = e_source_list_peek_groups (sources); g; g = g->next) {
5027                 ESourceGroup *group = E_SOURCE_GROUP (g->data);
5028                 GSList *s;
5029                 for (s = e_source_group_peek_sources (group); s; s = s->next) {
5030                         ESource *source = E_SOURCE (s->data);
5031
5032                         if (e_source_get_property (source, "default")) {
5033                                 default_source = source;
5034                                 break;
5035                         }
5036                 }
5037
5038                 if (default_source)
5039                         break;
5040         }
5041
5042         if (default_source) {
5043                 *ecal = e_cal_new (default_source, type);
5044                 if (!*ecal) {                   
5045                         g_propagate_error (error, err);
5046                         rv = FALSE;
5047                         goto done;
5048                 }
5049
5050                 e_cal_set_auth_func (*ecal, func, data);
5051                 if (!e_cal_open (*ecal, TRUE, &err)) {
5052                         g_propagate_error (error, err);
5053                         rv = FALSE;
5054                         goto done;              
5055                 }
5056         } else {
5057                 switch (type) {
5058                 case E_CAL_SOURCE_TYPE_EVENT:
5059                         *ecal = e_cal_new_system_calendar ();
5060                         break;
5061                 case E_CAL_SOURCE_TYPE_TODO:
5062                         *ecal = e_cal_new_system_tasks ();
5063                         break;
5064                 case E_CAL_SOURCE_TYPE_JOURNAL:
5065                         *ecal = e_cal_new_system_memos ();
5066                         break;
5067                 default:
5068                         break;
5069                 }
5070                 
5071                 if (!*ecal) {                   
5072                         g_propagate_error (error, err);
5073                         rv = FALSE;
5074                         goto done;
5075                 }
5076
5077                 e_cal_set_auth_func (*ecal, func, data);
5078                 if (!e_cal_open (*ecal, TRUE, &err)) {
5079                         g_propagate_error (error, err);
5080                         rv = FALSE;
5081                         goto done;              
5082                 }
5083         }
5084
5085  done:
5086         if (!rv && *ecal) {
5087                 g_object_unref (*ecal);
5088                 *ecal = NULL;
5089         }
5090         g_object_unref (sources);
5091
5092         return rv;
5093 }
5094
5095 /**
5096  * e_cal_open_default:
5097  * @ecal: A calendar client.
5098  * @type: Type of the calendar.
5099  * @func: Authentication function.
5100  * @data: Closure data for the authentication function.
5101  * @error: Placeholder for error information.
5102  *
5103  * Opens the default calendar.
5104  *
5105  * Return value: TRUE if it opened correctly, FALSE otherwise.
5106  */
5107 gboolean
5108 e_cal_open_default (ECal **ecal, ECalSourceType type, ECalAuthFunc func, gpointer data, GError **error)
5109 {
5110         ESourceList *sources;
5111         GError *err = NULL;
5112
5113         if (!e_cal_get_sources (&sources, type, &err)) {
5114                 g_propagate_error (error, err);
5115                 return FALSE;
5116         }
5117
5118         return get_default (ecal, sources, type, func, data, error);
5119 }
5120
5121 /**
5122  * e_cal_set_default:
5123  * @ecal: A calendar client.
5124  * @error: Placeholder for error information.
5125  *
5126  * Sets a calendar as the default one.
5127  *
5128  * Return value: TRUE if the operation was successful, FALSE otherwise.
5129  */
5130 gboolean
5131 e_cal_set_default (ECal *ecal, GError **error)
5132 {
5133         ESource *source;
5134
5135         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);        
5136
5137         source = e_cal_get_source (ecal);
5138         if (!source) {
5139                 /* XXX gerror */
5140                 return FALSE;
5141         }
5142
5143         return e_cal_set_default_source (source, ecal->priv->type, error);
5144 }
5145
5146 static gboolean
5147 set_default_source (ESourceList *sources, ESource *source, GError **error)
5148 {
5149         const char *uid;
5150         GError *err = NULL;
5151         GSList *g;
5152
5153         uid = e_source_peek_uid (source);
5154
5155         /* make sure the source is actually in the ESourceList.  if
5156            it's not we don't bother adding it, just return an error */
5157         source = e_source_list_peek_source_by_uid (sources, uid);
5158         if (!source) {
5159                 /* XXX gerror */
5160                 g_object_unref (sources);
5161                 return FALSE;
5162         }
5163
5164         /* loop over all the sources clearing out any "default"
5165            properties we find */
5166         for (g = e_source_list_peek_groups (sources); g; g = g->next) {
5167                 GSList *s;
5168                 for (s = e_source_group_peek_sources (E_SOURCE_GROUP (g->data));
5169                      s; s = s->next) {
5170                         e_source_set_property (E_SOURCE (s->data), "default", NULL);
5171                 }
5172         }
5173
5174         /* set the "default" property on the source */
5175         e_source_set_property (source, "default", "true");
5176
5177         if (!e_source_list_sync (sources, &err)) {
5178                 g_propagate_error (error, err);
5179                 return FALSE;
5180         }
5181
5182         return TRUE;
5183 }
5184
5185 /**
5186  * e_cal_set_default_source:
5187  * @source: An #ESource.
5188  * @type: Type of the source.
5189  * @error: Placeholder for error information.
5190  *
5191  * Sets the default source for the specified @type.
5192  *
5193  * Return value: TRUE if the operation was successful, FALSE otherwise.
5194  */
5195 gboolean
5196 e_cal_set_default_source (ESource *source, ECalSourceType type, GError **error)
5197 {
5198         ESourceList *sources;
5199         GError *err = NULL;
5200
5201         if (!e_cal_get_sources (&sources, type, &err)) {
5202                 g_propagate_error (error, err);
5203                 return FALSE;
5204         }
5205
5206         return set_default_source (sources, source, error);
5207 }
5208
5209 static gboolean
5210 get_sources (ESourceList **sources, const char *key, GError **error)
5211 {
5212         GConfClient *gconf = gconf_client_get_default();
5213
5214         *sources = e_source_list_new_for_gconf (gconf, key);
5215         g_object_unref (gconf);
5216
5217         return TRUE;
5218 }
5219
5220 /**
5221  * e_cal_get_sources:
5222  * @sources: Return value for list of sources.
5223  * @type: Type of the sources to get.
5224  * @error: Placeholder for error information.
5225  *
5226  * Gets the list of sources defined in the configuration for the given @type.
5227  *
5228  * Return value: TRUE if the operation was successful, FALSE otherwise.
5229  */
5230 gboolean
5231 e_cal_get_sources (ESourceList **sources, ECalSourceType type, GError **error)
5232 {
5233         switch (type) {
5234         case E_CAL_SOURCE_TYPE_EVENT:
5235                 return get_sources (sources, "/apps/evolution/calendar/sources", error);                
5236                 break;
5237         case E_CAL_SOURCE_TYPE_TODO:
5238                 return get_sources (sources, "/apps/evolution/tasks/sources", error);
5239                 break;
5240         case E_CAL_SOURCE_TYPE_JOURNAL:
5241                 return get_sources (sources, "/apps/evolution/memos/sources", error);
5242                 break;
5243         default:
5244                 /* FIXME Fill in error */
5245                 return FALSE;
5246         }
5247         
5248         /* FIXME Fill in error */
5249         return FALSE;   
5250 }