Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / calendar / backends / contacts / e-cal-backend-contacts.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar - iCalendar file backend
3  *
4  * Copyright (C) 2000-2003 Ximian, Inc.
5  * Copyright (C) 2003 Gergõ Érdi
6  *
7  * Authors: Federico Mena-Quintero <federico@ximian.com>
8  *          Rodrigo Moya <rodrigo@ximian.com>
9  *          Gergõ Érdi <cactus@cactus.rulez.org>
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU Lesser General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30
31 #include "e-cal-backend-contacts.h"
32
33 #include <glib/gi18n-lib.h>
34 #include "libedataserver/e-xml-hash-utils.h"
35 #include <libecal/e-cal-recur.h>
36 #include <libecal/e-cal-util.h>
37 #include <libedata-cal/e-cal-backend-util.h>
38 #include <libedata-cal/e-cal-backend-sexp.h>
39
40 #include <libebook/e-book.h>
41
42 #include "libedataserver/e-source-list.h"
43
44 static ECalBackendSyncClass *parent_class;
45
46 /* Private part of the ECalBackendContacts structure */
47 struct _ECalBackendContactsPrivate {
48         ESourceList  *addressbook_sources;
49         
50         GHashTable   *addressbooks;       /* UID -> BookRecord */
51         gboolean      addressbook_loaded;
52
53         EBookView    *book_view;
54         GHashTable   *tracked_contacts;   /* UID -> ContactRecord */
55
56         GHashTable *zones;      
57         icaltimezone *default_zone;
58 };
59
60 typedef struct _BookRecord {
61         EBook     *book;
62         EBookView *book_view;
63 } BookRecord;
64
65 typedef struct _ContactRecord {
66         ECalBackendContacts *cbc;
67         EContact            *contact;
68         ECalComponent       *comp_birthday, *comp_anniversary;
69 } ContactRecord;
70
71 #define d(x)
72
73 #define ANNIVERSARY_UID_EXT "-anniversary"
74 #define BIRTHDAY_UID_EXT   "-birthday"
75
76 static ECalComponent * create_birthday (ECalBackendContacts *cbc, EContact *contact);
77 static ECalComponent * create_anniversary (ECalBackendContacts *cbc, EContact *contact);
78
79 static void contacts_changed_cb (EBookView *book_view, const GList *contacts, gpointer user_data);
80 static void contacts_added_cb   (EBookView *book_view, const GList *contacts, gpointer user_data);
81 static void contacts_removed_cb (EBookView *book_view, const GList *contact_ids, gpointer user_data);
82 static ECalBackendSyncStatus
83 e_cal_backend_contacts_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj);
84
85 /* BookRecord methods */
86 static BookRecord *
87 book_record_new (ECalBackendContacts *cbc, ESource *source)
88 {
89         EBook      *book;
90         GList      *fields = NULL;
91         EBookQuery *query;
92         EBookView  *book_view;
93         BookRecord *br;
94         
95         book = e_book_new (source, NULL);
96         e_book_open (book, TRUE, NULL);
97         
98         /* Create book view */
99         fields = g_list_append (fields, (char*)e_contact_field_name (E_CONTACT_FILE_AS));
100         fields = g_list_append (fields, (char*)e_contact_field_name (E_CONTACT_BIRTH_DATE));
101         fields = g_list_append (fields, (char*)e_contact_field_name (E_CONTACT_ANNIVERSARY));
102         query = e_book_query_any_field_contains ("");
103
104         if (!e_book_get_book_view (book, query, fields, -1, &book_view, NULL)) {
105                 g_list_free (fields);
106                 e_book_query_unref (query);
107                 return NULL;
108         }
109         e_book_query_unref (query);
110         g_list_free (fields);
111
112         g_signal_connect (book_view, "contacts_added", G_CALLBACK (contacts_added_cb), cbc);
113         g_signal_connect (book_view, "contacts_removed", G_CALLBACK (contacts_removed_cb), cbc);
114         g_signal_connect (book_view, "contacts_changed", G_CALLBACK (contacts_changed_cb), cbc);
115
116         e_book_view_start (book_view);
117
118         br = g_new (BookRecord, 1);
119         br->book = book;
120         br->book_view = book_view;
121
122         return br;
123 }
124
125 static void
126 book_record_free (BookRecord *br)
127 {
128         if (!br)
129                 return;
130         
131         g_object_unref (br->book_view);
132         g_object_unref (br->book);
133
134         g_free (br);
135 }
136
137 /* ContactRecord methods */
138 static ContactRecord *
139 contact_record_new (ECalBackendContacts *cbc, EContact *contact)
140 {
141         ContactRecord *cr = g_new0 (ContactRecord, 1);
142         char *comp_str;
143         
144         cr->cbc = cbc;
145         cr->contact = contact;
146         cr->comp_birthday = create_birthday (cbc, contact);
147         cr->comp_anniversary = create_anniversary (cbc, contact);
148
149         if (cr->comp_birthday) {
150                 comp_str = e_cal_component_get_as_string (cr->comp_birthday);
151                 e_cal_backend_notify_object_created (E_CAL_BACKEND (cbc),
152                                                      comp_str);
153                 g_free (comp_str);
154         }
155         
156         if (cr->comp_anniversary) {
157
158                 comp_str = e_cal_component_get_as_string (cr->comp_anniversary);
159                 e_cal_backend_notify_object_created (E_CAL_BACKEND (cbc),
160                                                      comp_str);
161                 g_free (comp_str);
162         }
163
164         g_object_ref (G_OBJECT (contact));
165         
166         return cr;
167 }
168
169 static void
170 contact_record_free (ContactRecord *cr)
171 {
172         char *comp_str;
173         ECalComponentId *id;
174
175         g_object_unref (G_OBJECT (cr->contact));
176
177         /* Remove the birthday event */
178         if (cr->comp_birthday) {
179                 comp_str = e_cal_component_get_as_string (cr->comp_birthday);
180                 id = e_cal_component_get_id (cr->comp_birthday);
181                 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cr->cbc), id, comp_str, NULL);
182
183                 e_cal_component_free_id (id);
184                 g_free (comp_str);
185                 g_object_unref (G_OBJECT (cr->comp_birthday));
186         }
187
188         /* Remove the anniversary event */
189         if (cr->comp_anniversary) {
190                 comp_str = e_cal_component_get_as_string (cr->comp_anniversary);
191                 id = e_cal_component_get_id (cr->comp_anniversary);
192
193                 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cr->cbc), id, comp_str, NULL);
194                 
195                 e_cal_component_free_id (id);
196                 g_free (comp_str);
197                 g_object_unref (G_OBJECT (cr->comp_anniversary));
198         }
199         
200         g_free (cr);
201 }
202
203 /* ContactRecordCB methods */
204 typedef struct _ContactRecordCB {
205         ECalBackendContacts *cbc;
206         ECalBackendSExp     *sexp;        
207         GList               *result;
208 } ContactRecordCB;
209
210 static ContactRecordCB *
211 contact_record_cb_new (ECalBackendContacts *cbc, ECalBackendSExp *sexp)
212 {
213         ContactRecordCB *cb_data = g_new (ContactRecordCB, 1);
214
215         cb_data->cbc = cbc;
216         cb_data->sexp = sexp;
217         cb_data->result = NULL;
218
219         return cb_data;
220 }
221
222 static void
223 contact_record_cb_free (ContactRecordCB *cb_data)
224 {
225         g_list_foreach (cb_data->result, (GFunc) g_free, NULL);
226         g_list_free (cb_data->result);
227         
228         g_free (cb_data);
229 }
230
231 static void
232 contact_record_cb (gpointer key, gpointer value, gpointer user_data)
233 {
234         ContactRecordCB *cb_data = user_data;
235         ContactRecord   *record = value;
236
237         if (record->comp_birthday && e_cal_backend_sexp_match_comp (cb_data->sexp, record->comp_birthday, E_CAL_BACKEND (cb_data->cbc))) {
238                 char * comp_str = e_cal_component_get_as_string (record->comp_birthday);
239                 cb_data->result = g_list_append (cb_data->result, comp_str);
240         }
241
242         if (record->comp_anniversary && e_cal_backend_sexp_match_comp (cb_data->sexp, record->comp_anniversary, E_CAL_BACKEND (cb_data->cbc))) {
243                 char * comp_str = e_cal_component_get_as_string (record->comp_anniversary);
244                 cb_data->result = g_list_append (cb_data->result, comp_str);
245         }
246 }
247
248 /* SourceList callbacks */
249 static void
250 add_source (ECalBackendContacts *cbc, ESource *source)
251 {
252         BookRecord *br = book_record_new (cbc, source);
253         const char *uid = e_source_peek_uid (source);
254
255         g_hash_table_insert (cbc->priv->addressbooks, g_strdup (uid), br);
256 }
257
258 static void
259 source_added_cb (ESourceGroup *group, ESource *source, gpointer user_data)
260 {
261         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
262         
263         g_return_if_fail (cbc);
264
265         add_source (cbc, source);
266 }
267
268 static void
269 source_removed_cb (ESourceGroup *group, ESource *source, gpointer user_data)
270 {
271         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
272         const char          *uid = e_source_peek_uid (source);
273         
274         g_return_if_fail (cbc);
275
276         g_hash_table_remove (cbc->priv->addressbooks, uid);
277 }
278
279 static void
280 source_group_added_cb (ESourceList *source_list, ESourceGroup *group, gpointer user_data)
281 {
282         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
283         const gchar *base_uri;
284         GSList *i;
285         
286         g_return_if_fail (cbc);
287
288         base_uri = e_source_group_peek_base_uri (group);
289         if (!base_uri)
290                 return;
291
292         /* Load all address books from this group */
293         if (!strncmp (base_uri, "file", 4)) {
294                 for (i = e_source_group_peek_sources (group); i; i = i->next) {
295                         ESource *source = E_SOURCE (i->data);
296                         add_source (cbc, source);
297                 }
298
299                 /* Watch for future changes */
300                 g_signal_connect (group, "source_added", G_CALLBACK (source_added_cb), cbc);
301                 g_signal_connect (group, "source_removed", G_CALLBACK (source_removed_cb), cbc);
302         }
303 }
304
305 static void
306 source_group_removed_cb (ESourceList *source_list, ESourceGroup *group, gpointer user_data)
307 {
308         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
309         GSList *i = NULL;
310         
311         g_return_if_fail (cbc);
312         
313         /* Unload all address books from this group */
314         for (i = e_source_group_peek_sources (group); i; i = i->next) {
315                 ESource *source = E_SOURCE (i->data);
316                 const char *uid = e_source_peek_uid (source);
317                 
318                 g_hash_table_remove (cbc->priv->addressbooks, uid);
319         }
320 }
321
322 /************************************************************************************/
323
324 static void
325 contacts_changed_cb (EBookView *book_view, const GList *contacts, gpointer user_data)
326 {
327         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
328         const GList *i;
329
330         for (i = contacts; i; i = i->next) {
331                 EContact *contact = E_CONTACT (i->data);
332                 const char *uid = e_contact_get_const (contact, E_CONTACT_UID);
333                 
334                 /* Because this is a change of contact, then always remove old tracked data
335                    and if possible, add with (possibly) new values.
336                 */
337                 g_hash_table_remove (cbc->priv->tracked_contacts, (char *)uid);
338
339                 if (e_contact_get (contact, E_CONTACT_BIRTH_DATE) ||
340                     e_contact_get (contact, E_CONTACT_ANNIVERSARY)) {
341                         ContactRecord *cr = contact_record_new (cbc, contact);
342                         g_hash_table_insert (cbc->priv->tracked_contacts, g_strdup (uid), cr);
343                 }
344         }
345 }
346
347 static void
348 contacts_added_cb (EBookView *book_view, const GList *contacts, gpointer user_data)
349 {
350         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
351         const GList *i;
352
353         /* See if any new contacts have BIRTHDAY or ANNIVERSARY fields */
354         for (i = contacts; i; i = i->next)
355         {
356                 EContact *contact = E_CONTACT (i->data);
357                 EContactDate *birthday, *anniversary;
358                 
359                 birthday = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
360                 anniversary = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
361                                              
362                 if (birthday || anniversary) {
363                         ContactRecord *cr = contact_record_new (cbc, contact);
364                         const char    *uid = e_contact_get_const (contact, E_CONTACT_UID);
365                         
366                         g_hash_table_insert (cbc->priv->tracked_contacts, g_strdup (uid), cr);
367                 }
368                 
369                 e_contact_date_free (birthday);
370                 e_contact_date_free (anniversary);
371         }
372 }
373
374 static void
375 contacts_removed_cb (EBookView *book_view, const GList *contact_ids, gpointer user_data)
376 {
377         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
378         const GList *i;
379
380         /* Stop tracking these */
381         for (i = contact_ids; i; i = i->next)
382                 g_hash_table_remove (cbc->priv->tracked_contacts, i->data);
383 }
384
385 /************************************************************************************/
386 static struct icaltimetype
387 cdate_to_icaltime (EContactDate *cdate)
388 {
389         struct icaltimetype ret;
390
391 /*FIXME: this is a really _ugly_ (temporary) hack
392  *      since several functions are still depending on the epoch,
393  *      let entries start (earliest) at 19700101
394  */
395         ret.year = cdate->year >= 1970 ? cdate->year : 1970;
396         ret.month = cdate->month;
397         ret.day = cdate->day;
398         ret.is_date = TRUE;
399         ret.is_utc = FALSE;
400         ret.zone = NULL;
401         ret.is_daylight = FALSE;
402         
403         ret.hour = ret.minute = ret.second = 0;
404
405         return ret;
406 }
407
408 /* Contact -> Event creator */
409 static ECalComponent *
410 create_component (ECalBackendContacts *cbc, const char *uid, EContactDate *cdate, const char *summary)
411 {
412         ECalComponent             *cal_comp;
413         ECalComponentText          comp_summary;
414         icalcomponent             *ical_comp;
415         struct icaltimetype        itt;
416         ECalComponentDateTime      dt;
417         struct icalrecurrencetype  r;
418         GSList recur_list;
419
420         g_return_val_if_fail (E_IS_CAL_BACKEND_CONTACTS (cbc), NULL);
421
422         if (!cdate)
423                 return NULL;
424
425         ical_comp = icalcomponent_new (ICAL_VEVENT_COMPONENT);
426         
427         /* Create the event object */
428         cal_comp = e_cal_component_new ();
429         e_cal_component_set_icalcomponent (cal_comp, ical_comp);
430
431         /* Set uid */
432         d(g_message ("Creating UID: %s", uid));
433         e_cal_component_set_uid (cal_comp, uid);
434         
435         /* Set all-day event's date from contact data */
436         itt = cdate_to_icaltime (cdate);
437         dt.value = &itt;
438         dt.tzid = NULL;
439         e_cal_component_set_dtstart (cal_comp, &dt);
440         
441         itt = cdate_to_icaltime (cdate);
442         icaltime_adjust (&itt, 1, 0, 0, 0);
443         dt.value = &itt;
444         dt.tzid = NULL;
445         /* We have to add 1 day to DTEND, as it is not inclusive. */
446         e_cal_component_set_dtend (cal_comp, &dt);
447  
448         /* Create yearly recurrence */
449         icalrecurrencetype_clear (&r);
450         r.freq = ICAL_YEARLY_RECURRENCE;
451         r.interval = 1;
452         recur_list.data = &r;
453         recur_list.next = NULL;        
454         e_cal_component_set_rrule_list (cal_comp, &recur_list);
455
456         /* Create summary */
457         comp_summary.value = summary;
458         comp_summary.altrep = NULL;
459         e_cal_component_set_summary (cal_comp, &comp_summary);
460         
461         /* Set category and visibility */
462         if (g_str_has_suffix (uid, ANNIVERSARY_UID_EXT))
463                 e_cal_component_set_categories (cal_comp, _("Anniversary"));
464         else if (g_str_has_suffix (uid, BIRTHDAY_UID_EXT))
465                 e_cal_component_set_categories (cal_comp, _("Birthday"));
466         
467         e_cal_component_set_classification (cal_comp, E_CAL_COMPONENT_CLASS_PRIVATE);
468
469         /* Birthdays/anniversaries are shown as free time */
470         e_cal_component_set_transparency (cal_comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
471         
472         /* Don't forget to call commit()! */
473         e_cal_component_commit_sequence (cal_comp);
474         
475         return cal_comp;
476 }
477
478 static ECalComponent *
479 create_birthday (ECalBackendContacts *cbc, EContact *contact)
480 {
481         EContactDate  *cdate;
482         ECalComponent *cal_comp;
483         char          *summary;
484         const char    *name;
485         char *uid;
486
487         cdate = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
488         name = e_contact_get_const (contact, E_CONTACT_FILE_AS);
489
490         uid = g_strdup_printf ("%s%s", (char *) e_contact_get_const (contact, E_CONTACT_UID), BIRTHDAY_UID_EXT);
491         summary = g_strdup_printf (_("Birthday: %s"), name);
492         
493         cal_comp = create_component (cbc, uid, cdate, summary);
494
495         e_contact_date_free (cdate);
496         g_free (uid);
497         g_free (summary);
498         
499         return cal_comp;
500 }
501
502 static ECalComponent *
503 create_anniversary (ECalBackendContacts *cbc, EContact *contact)
504 {
505         EContactDate  *cdate;
506         ECalComponent *cal_comp;
507         char          *summary;
508         const char    *name;
509         char *uid;
510         
511         cdate = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
512         name = e_contact_get_const (contact, E_CONTACT_FILE_AS);
513
514         uid = g_strdup_printf ("%s%s", (char *) e_contact_get_const (contact, E_CONTACT_UID), ANNIVERSARY_UID_EXT);
515         summary = g_strdup_printf (_("Anniversary: %s"), name);
516         
517         cal_comp = create_component (cbc, uid, cdate, summary);
518
519         e_contact_date_free (cdate);
520         g_free (uid);
521         g_free (summary);
522
523         return cal_comp;
524 }
525
526 /************************************************************************************/
527 /* Calendar backend method implementations */
528
529 /* First the empty stubs */
530
531 static ECalBackendSyncStatus
532 e_cal_backend_contacts_get_cal_address (ECalBackendSync *backend, EDataCal *cal,
533                                         char **address)
534 {
535         /* A contact backend has no particular email address associated
536          * with it (although that would be a useful feature some day).
537          */
538         *address = NULL;
539
540         return GNOME_Evolution_Calendar_Success;
541 }
542
543 static ECalBackendSyncStatus
544 e_cal_backend_contacts_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal,
545                                            char **attribute)
546 {
547         *attribute = NULL;
548         
549         return GNOME_Evolution_Calendar_Success;
550 }
551
552 static ECalBackendSyncStatus
553 e_cal_backend_contacts_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal,
554                                                 char **address)
555 {
556         /* A contact backend has no particular email address associated
557          * with it (although that would be a useful feature some day).
558          */
559         *address = NULL;
560
561         return GNOME_Evolution_Calendar_Success;
562 }
563
564 static ECalBackendSyncStatus
565 e_cal_backend_contacts_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal,
566                                                 char **capabilities)
567 {
568         *capabilities = NULL;
569
570         return GNOME_Evolution_Calendar_Success;
571 }
572
573 static ECalBackendSyncStatus
574 e_cal_backend_contacts_remove (ECalBackendSync *backend, EDataCal *cal)
575 {
576         /* WRITE ME */
577         return GNOME_Evolution_Calendar_PermissionDenied;
578 }
579
580 static ECalBackendSyncStatus
581 e_cal_backend_contacts_get_default_object (ECalBackendSync *backend, EDataCal *cal,
582                                            char **object)
583 {
584         return GNOME_Evolution_Calendar_UnsupportedMethod;
585 }
586
587 static ECalBackendSyncStatus
588 e_cal_backend_contacts_get_object (ECalBackendSync *backend, EDataCal *cal,
589                                    const char *uid, const char *rid,
590                                    char **object)
591 {
592         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
593         ECalBackendContactsPrivate *priv = cbc->priv;   
594         ContactRecord *record;
595         char *real_uid;
596         
597         if (!uid)
598                 return GNOME_Evolution_Calendar_ObjectNotFound;
599         else if (g_str_has_suffix (uid, ANNIVERSARY_UID_EXT))
600                 real_uid = g_strndup (uid, strlen (uid) - strlen (ANNIVERSARY_UID_EXT));
601         else if (g_str_has_suffix (uid, BIRTHDAY_UID_EXT))
602                 real_uid = g_strndup (uid, strlen (uid) - strlen (BIRTHDAY_UID_EXT));
603         else
604                 return GNOME_Evolution_Calendar_ObjectNotFound;
605
606         record = g_hash_table_lookup (priv->tracked_contacts, real_uid);
607         g_free (real_uid);
608         
609         if (!record)
610                 return GNOME_Evolution_Calendar_ObjectNotFound;
611
612         if (record->comp_birthday && g_str_has_suffix (uid, BIRTHDAY_UID_EXT)) {
613                 *object = e_cal_component_get_as_string (record->comp_birthday);
614                 
615                 d(g_message ("Return birthday: %s", *object));
616                 return GNOME_Evolution_Calendar_Success;
617         }
618         
619         if (record->comp_anniversary && g_str_has_suffix (uid, ANNIVERSARY_UID_EXT)) {
620                 *object = e_cal_component_get_as_string (record->comp_anniversary);
621
622                 d(g_message ("Return anniversary: %s", *object));
623                 return GNOME_Evolution_Calendar_Success;
624         }
625
626         d(g_message ("Returning nothing for uid: %s", uid));
627
628         return GNOME_Evolution_Calendar_ObjectNotFound;
629 }
630
631 static ECalBackendSyncStatus
632 e_cal_backend_contacts_get_free_busy (ECalBackendSync *backend, EDataCal *cal,
633                                       GList *users, time_t start, time_t end,
634                                       GList **freebusy)
635 {
636         /* Birthdays/anniversaries don't count as busy time */
637
638         icalcomponent *vfb = icalcomponent_new_vfreebusy ();
639         icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
640         char *calobj;
641
642 #if 0
643         icalproperty *prop;
644         icalparameter *param;
645                 
646         prop = icalproperty_new_organizer (address);
647         if (prop != NULL && cn != NULL) {
648                 param = icalparameter_new_cn (cn);
649                 icalproperty_add_parameter (prop, param);                       
650         }
651         if (prop != NULL)
652                 icalcomponent_add_property (vfb, prop);         
653 #endif
654         
655         icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
656         icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));
657
658         calobj = icalcomponent_as_ical_string (vfb);
659         *freebusy = g_list_append (NULL, g_strdup (calobj));
660         icalcomponent_free (vfb);       
661         
662         /* WRITE ME */
663         return GNOME_Evolution_Calendar_Success;
664 }
665
666 static ECalBackendSyncStatus
667 e_cal_backend_contacts_get_changes (ECalBackendSync *backend, EDataCal *cal,
668                                     const char *change_id,
669                                     GList **adds, GList **modifies, GList **deletes)
670 {
671         /* WRITE ME */
672         return GNOME_Evolution_Calendar_Success;
673 }
674
675 static ECalBackendSyncStatus
676 e_cal_backend_contacts_discard_alarm (ECalBackendSync *backend, EDataCal *cal,
677                                       const char *uid, const char *auid)
678 {
679         /* WRITE ME */
680         return GNOME_Evolution_Calendar_Success;
681 }
682
683 static ECalBackendSyncStatus
684 e_cal_backend_contacts_receive_objects (ECalBackendSync *backend, EDataCal *cal,
685                                         const char *calobj)
686 {
687         return GNOME_Evolution_Calendar_PermissionDenied;       
688 }
689
690 static ECalBackendSyncStatus
691 e_cal_backend_contacts_send_objects (ECalBackendSync *backend, EDataCal *cal,
692                                      const char *calobj, GList **users, char **modified_calobj)
693 {
694         *users = NULL;
695         *modified_calobj = NULL;
696         /* TODO: Investigate this */
697         return GNOME_Evolution_Calendar_PermissionDenied;
698 }
699
700 /* Then the real implementations */
701
702 static CalMode
703 e_cal_backend_contacts_get_mode (ECalBackend *backend)
704 {
705         return CAL_MODE_LOCAL;  
706 }
707
708 static void
709 e_cal_backend_contacts_set_mode (ECalBackend *backend, CalMode mode)
710 {
711         e_cal_backend_notify_mode (backend,
712                                    GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
713                                    GNOME_Evolution_Calendar_MODE_LOCAL);
714         
715 }
716
717 static ECalBackendSyncStatus
718 e_cal_backend_contacts_is_read_only (ECalBackendSync *backend, EDataCal *cal,
719                                      gboolean *read_only)
720 {
721         *read_only = TRUE;
722         
723         return GNOME_Evolution_Calendar_Success;
724 }
725
726 static ECalBackendSyncStatus
727 e_cal_backend_contacts_open (ECalBackendSync *backend, EDataCal *cal,
728                              gboolean only_if_exists,
729                              const char *username, const char *password)
730 {
731         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
732         ECalBackendContactsPrivate *priv = cbc->priv;
733
734         GSList *i;
735
736         if (priv->addressbook_loaded)
737                 return GNOME_Evolution_Calendar_Success;
738                 
739         if (priv->default_zone && priv->default_zone != icaltimezone_get_utc_timezone ()) {
740                 icalcomponent *icalcomp = icaltimezone_get_component (priv->default_zone);
741                 icaltimezone *zone = icaltimezone_new ();
742
743                 icaltimezone_set_component (zone, icalcomponent_new_clone (icalcomp));
744
745                 g_hash_table_insert (priv->zones, g_strdup (icaltimezone_get_tzid (zone)), zone);
746         }
747
748         /* Create address books for existing sources */
749         for (i = e_source_list_peek_groups (priv->addressbook_sources); i; i = i->next) {
750                 ESourceGroup *source_group = E_SOURCE_GROUP (i->data);
751
752                 source_group_added_cb (priv->addressbook_sources, source_group, cbc);
753         }
754
755         /* Listen for source list changes */
756         g_signal_connect (priv->addressbook_sources, "group_added", G_CALLBACK (source_group_added_cb), cbc);
757         g_signal_connect (priv->addressbook_sources, "group_removed", G_CALLBACK (source_group_removed_cb), cbc);
758
759         priv->addressbook_loaded = TRUE;
760         return GNOME_Evolution_Calendar_Success;
761 }
762
763 static gboolean
764 e_cal_backend_contacts_is_loaded (ECalBackend *backend)
765 {
766         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
767         ECalBackendContactsPrivate *priv = cbc->priv;
768
769         return priv->addressbook_loaded;
770 }
771
772 static ECalBackendSyncStatus
773 e_cal_backend_contacts_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object)
774 {
775         ECalBackendContacts *cbcontacts;
776         ECalBackendContactsPrivate *priv;
777         icaltimezone *zone;
778         icalcomponent *icalcomp;
779
780         cbcontacts = E_CAL_BACKEND_CONTACTS (backend);
781         priv = cbcontacts->priv;
782
783         g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
784
785         zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (backend), tzid);
786         if (!zone)
787                 return GNOME_Evolution_Calendar_ObjectNotFound;
788
789         icalcomp = icaltimezone_get_component (zone);
790         if (!icalcomp)
791                 return GNOME_Evolution_Calendar_InvalidObject;
792
793         *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
794
795         return GNOME_Evolution_Calendar_Success;
796 }
797
798 /* Add_timezone handler for the file backend */
799 static ECalBackendSyncStatus
800 e_cal_backend_contacts_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
801 {
802         ECalBackendContacts *cbcontacts;
803         ECalBackendContactsPrivate *priv;
804         icalcomponent *tz_comp;
805         icaltimezone *zone;
806         char *tzid;     
807
808         cbcontacts = (ECalBackendContacts *) backend;
809
810         g_return_val_if_fail (E_IS_CAL_BACKEND_CONTACTS (cbcontacts), GNOME_Evolution_Calendar_OtherError);
811         g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
812
813         priv = cbcontacts->priv;
814         
815         tz_comp = icalparser_parse_string (tzobj);
816         if (!tz_comp)
817                 return GNOME_Evolution_Calendar_InvalidObject;
818
819         if (icalcomponent_isa (tz_comp) != ICAL_VTIMEZONE_COMPONENT)
820                 return GNOME_Evolution_Calendar_InvalidObject;
821         
822         zone = icaltimezone_new ();
823         icaltimezone_set_component (zone, tz_comp);
824         tzid = icaltimezone_get_tzid (zone);
825         
826         if (g_hash_table_lookup (priv->zones, tzid)) {
827                 icaltimezone_free (zone, TRUE);
828                 
829                 return GNOME_Evolution_Calendar_Success;
830         }
831         
832         g_hash_table_insert (priv->zones, g_strdup (tzid), zone);
833
834         return GNOME_Evolution_Calendar_Success;
835 }
836
837 static ECalBackendSyncStatus
838 e_cal_backend_contacts_set_default_zone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
839 {
840         icalcomponent *tz_comp;
841         ECalBackendContacts *cbcontacts;
842         ECalBackendContactsPrivate *priv;
843         icaltimezone *zone;
844
845         cbcontacts = (ECalBackendContacts *) backend;
846
847         g_return_val_if_fail (E_IS_CAL_BACKEND_CONTACTS (cbcontacts), GNOME_Evolution_Calendar_OtherError);
848         g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
849
850         priv = cbcontacts->priv;
851
852         tz_comp = icalparser_parse_string (tzobj);
853         if (!tz_comp)
854                 return GNOME_Evolution_Calendar_InvalidObject;
855
856         zone = icaltimezone_new ();
857         icaltimezone_set_component (zone, tz_comp);
858
859         if (priv->default_zone && priv->default_zone != icaltimezone_get_utc_timezone ())
860                 icaltimezone_free (priv->default_zone, 1);
861
862         /* Set the default timezone to it. */
863         priv->default_zone = zone;
864
865         return GNOME_Evolution_Calendar_Success;
866 }
867
868 static ECalBackendSyncStatus
869 e_cal_backend_contacts_get_object_list (ECalBackendSync *backend, EDataCal *cal,
870                                         const char *sexp_string, GList **objects)
871 {
872         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
873         ECalBackendContactsPrivate *priv = cbc->priv;
874         ECalBackendSExp *sexp = e_cal_backend_sexp_new (sexp_string);
875         ContactRecordCB *cb_data;
876
877         if (!sexp)
878                 return GNOME_Evolution_Calendar_InvalidQuery;
879
880         cb_data = contact_record_cb_new (cbc, sexp);
881         g_hash_table_foreach (priv->tracked_contacts, contact_record_cb, cb_data);
882         *objects = cb_data->result;
883
884         /* Don't call cb_data_free as that would destroy the results
885          * in *objects */
886         g_free (cb_data);
887         
888         return GNOME_Evolution_Calendar_Success;        
889 }
890
891 static void
892 e_cal_backend_contacts_start_query (ECalBackend *backend, EDataCalView *query)
893 {
894         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
895         ECalBackendContactsPrivate *priv = cbc->priv;
896         ECalBackendSExp *sexp;
897         ContactRecordCB *cb_data;
898         
899         sexp = e_data_cal_view_get_object_sexp (query);
900         if (!sexp) {
901                 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_InvalidQuery);
902                 return;
903         }
904
905         cb_data = contact_record_cb_new (cbc, sexp);
906         
907         g_hash_table_foreach (priv->tracked_contacts, contact_record_cb, cb_data);
908         e_data_cal_view_notify_objects_added (query, cb_data->result);
909
910         contact_record_cb_free (cb_data);
911         
912         e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
913 }
914
915 static icaltimezone *
916 e_cal_backend_contacts_internal_get_default_timezone (ECalBackend *backend)
917 {
918         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
919
920         return cbc->priv->default_zone;
921 }
922
923 static icaltimezone *
924 e_cal_backend_contacts_internal_get_timezone (ECalBackend *backend, const char *tzid)
925 {
926         ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
927
928         return cbc->priv->default_zone;
929 }
930
931 /***********************************************************************************
932  */
933
934 static void
935 free_zone (gpointer data)
936 {
937         icaltimezone_free (data, TRUE);
938 }
939
940 /* Finalize handler for the contacts backend */
941 static void
942 e_cal_backend_contacts_finalize (GObject *object)
943 {
944         ECalBackendContacts *cbc;
945         ECalBackendContactsPrivate *priv;
946
947         g_return_if_fail (object != NULL);
948         g_return_if_fail (E_IS_CAL_BACKEND_CONTACTS (object));
949
950         cbc = E_CAL_BACKEND_CONTACTS (object);
951         priv = cbc->priv;
952
953         if (priv->default_zone && priv->default_zone != icaltimezone_get_utc_timezone ()) {
954                 icaltimezone_free (priv->default_zone, 1);
955         }
956         
957         priv->default_zone = NULL;
958         g_hash_table_destroy (priv->addressbooks);
959         g_hash_table_destroy (priv->tracked_contacts);
960         g_hash_table_destroy (priv->zones);
961         
962         g_free (priv);
963         cbc->priv = NULL;
964
965         if (G_OBJECT_CLASS (parent_class)->finalize)
966                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
967 }
968
969 /* Object initialization function for the contacts backend */
970 static void
971 e_cal_backend_contacts_init (ECalBackendContacts *cbc, ECalBackendContactsClass *class)
972 {
973         ECalBackendContactsPrivate *priv;
974
975         priv = g_new0 (ECalBackendContactsPrivate, 1);
976
977         e_book_get_addressbooks (&priv->addressbook_sources, NULL);
978
979         priv->addressbooks = g_hash_table_new_full (g_str_hash, g_str_equal,
980                                                     g_free, (GDestroyNotify) book_record_free);
981         priv->tracked_contacts = g_hash_table_new_full (g_str_hash, g_str_equal,
982                                                         g_free, (GDestroyNotify)contact_record_free);
983         
984         priv->zones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_zone);
985         priv->default_zone = icaltimezone_get_utc_timezone ();
986         
987         cbc->priv = priv;
988
989         e_cal_backend_sync_set_lock (E_CAL_BACKEND_SYNC (cbc), TRUE);
990 }
991
992 static ECalBackendSyncStatus
993 e_cal_backend_contacts_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid)
994 {
995         ECalBackendContacts *cbcontacts;
996         ECalBackendContactsPrivate *priv;
997
998         cbcontacts = E_CAL_BACKEND_CONTACTS (backend);
999         priv = cbcontacts->priv;
1000
1001         return GNOME_Evolution_Calendar_PermissionDenied;
1002 }
1003
1004 /* Class initialization function for the contacts backend */
1005 static void
1006 e_cal_backend_contacts_class_init (ECalBackendContactsClass *class)
1007 {
1008         GObjectClass *object_class;
1009         ECalBackendClass *backend_class;
1010         ECalBackendSyncClass *sync_class;
1011
1012         object_class = (GObjectClass *) class;
1013         backend_class = (ECalBackendClass *) class;
1014         sync_class = (ECalBackendSyncClass *) class;
1015
1016         parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
1017
1018         object_class->finalize = e_cal_backend_contacts_finalize;
1019
1020         sync_class->is_read_only_sync = e_cal_backend_contacts_is_read_only;
1021         sync_class->get_cal_address_sync = e_cal_backend_contacts_get_cal_address;
1022         sync_class->get_alarm_email_address_sync = e_cal_backend_contacts_get_alarm_email_address;
1023         sync_class->get_ldap_attribute_sync = e_cal_backend_contacts_get_ldap_attribute;
1024         sync_class->get_static_capabilities_sync = e_cal_backend_contacts_get_static_capabilities;
1025         sync_class->open_sync = e_cal_backend_contacts_open;
1026         sync_class->remove_sync = e_cal_backend_contacts_remove;
1027         sync_class->create_object_sync = e_cal_backend_contacts_create_object;
1028         sync_class->discard_alarm_sync = e_cal_backend_contacts_discard_alarm;
1029         sync_class->receive_objects_sync = e_cal_backend_contacts_receive_objects;
1030         sync_class->send_objects_sync = e_cal_backend_contacts_send_objects;
1031         sync_class->get_default_object_sync = e_cal_backend_contacts_get_default_object;
1032         sync_class->get_object_sync = e_cal_backend_contacts_get_object;
1033         sync_class->get_object_list_sync = e_cal_backend_contacts_get_object_list;
1034         sync_class->get_timezone_sync = e_cal_backend_contacts_get_timezone;
1035         sync_class->add_timezone_sync = e_cal_backend_contacts_add_timezone;
1036         sync_class->set_default_zone_sync = e_cal_backend_contacts_set_default_zone;
1037         sync_class->get_freebusy_sync = e_cal_backend_contacts_get_free_busy;
1038         sync_class->get_changes_sync = e_cal_backend_contacts_get_changes;
1039         backend_class->is_loaded = e_cal_backend_contacts_is_loaded;
1040         backend_class->start_query = e_cal_backend_contacts_start_query;
1041         backend_class->get_mode = e_cal_backend_contacts_get_mode;
1042         backend_class->set_mode = e_cal_backend_contacts_set_mode;
1043
1044         backend_class->internal_get_default_timezone = e_cal_backend_contacts_internal_get_default_timezone;
1045         backend_class->internal_get_timezone = e_cal_backend_contacts_internal_get_timezone;
1046 }
1047
1048
1049 /**
1050  * e_cal_backend_contacts_get_type:
1051  * @void: 
1052  * 
1053  * Registers the #ECalBackendContacts class if necessary, and returns
1054  * the type ID associated to it.
1055  * 
1056  * Return value: The type ID of the #ECalBackendContacts class.
1057  **/
1058 GType
1059 e_cal_backend_contacts_get_type (void)
1060 {
1061         static GType e_cal_backend_contacts_type = 0;
1062
1063         if (!e_cal_backend_contacts_type) {
1064                 static GTypeInfo info = {
1065                         sizeof (ECalBackendContactsClass),
1066                         (GBaseInitFunc) NULL,
1067                         (GBaseFinalizeFunc) NULL,
1068                         (GClassInitFunc) e_cal_backend_contacts_class_init,
1069                         NULL, NULL,
1070                         sizeof (ECalBackendContacts),
1071                         0,
1072                         (GInstanceInitFunc) e_cal_backend_contacts_init
1073                 };
1074                 e_cal_backend_contacts_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
1075                                                                      "ECalBackendContacts", &info, 0);
1076         }
1077
1078         return e_cal_backend_contacts_type;
1079 }