1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar - iCalendar file backend
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 * Copyright (C) 2003 Gergõ Érdi
7 * Authors: Federico Mena-Quintero <federico@ximian.com>
8 * Rodrigo Moya <rodrigo@ximian.com>
9 * Gergõ Érdi <cactus@cactus.rulez.org>
11 * This library is free software you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation.
15 * This library is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, see <http://www.gnu.org/licenses/>.
30 #include "e-cal-backend-contacts.h"
32 #include <glib/gi18n-lib.h>
34 #include <libebook/libebook.h>
36 #include "e-source-contacts.h"
38 #define E_CAL_BACKEND_CONTACTS_GET_PRIVATE(obj) \
39 (G_TYPE_INSTANCE_GET_PRIVATE \
40 ((obj), E_TYPE_CAL_BACKEND_CONTACTS, ECalBackendContactsPrivate))
42 #define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
46 e_cal_backend_contacts,
47 E_TYPE_CAL_BACKEND_SYNC)
56 /* Private part of the ECalBackendContacts structure */
57 struct _ECalBackendContactsPrivate {
59 GRecMutex rec_mutex; /* guards 'addressbooks' */
60 GHashTable *addressbooks; /* UID -> BookRecord */
61 gboolean addressbook_loaded;
63 EBookClientView *book_view;
64 GHashTable *tracked_contacts; /* UID -> ContactRecord */
65 GRecMutex tracked_contacts_lock;
67 /* properties related to track alarm settings for this backend */
70 guint update_alarms_id;
71 gboolean alarm_enabled;
76 typedef struct _BookRecord {
77 volatile gint ref_count;
80 ECalBackendContacts *cbc;
81 EBookClient *book_client;
82 EBookClientView *book_view;
85 typedef struct _ContactRecord {
86 ECalBackendContacts *cbc;
87 EBookClient *book_client; /* where it comes from */
89 ECalComponent *comp_birthday, *comp_anniversary;
94 #define ANNIVERSARY_UID_EXT "-anniversary"
95 #define BIRTHDAY_UID_EXT "-birthday"
97 static ECalComponent *
98 create_birthday (ECalBackendContacts *cbc,
100 static ECalComponent *
101 create_anniversary (ECalBackendContacts *cbc,
103 static void contacts_modified_cb (EBookClientView *book_view,
104 const GSList *contacts,
106 static void contacts_added_cb (EBookClientView *book_view,
107 const GSList *contacts,
109 static void contacts_removed_cb (EBookClientView *book_view,
110 const GSList *contact_ids,
112 static void e_cal_backend_contacts_add_timezone
113 (ECalBackendSync *backend,
115 GCancellable *cancellable,
118 static void setup_alarm (ECalBackendContacts *cbc,
119 ECalComponent *comp);
120 static void book_client_connected_cb (GObject *source_object,
121 GAsyncResult *result,
125 remove_by_book (gpointer key,
129 ContactRecord *cr = value;
130 EBookClient *book_client = user_data;
132 return (cr && cr->book_client == book_client);
136 create_book_record (ECalBackendContacts *cbc,
141 br = g_slice_new0 (BookRecord);
143 g_mutex_init (&br->lock);
144 br->cbc = g_object_ref (cbc);
146 e_book_client_connect (
147 source, NULL, book_client_connected_cb, br);
151 book_record_ref (BookRecord *br)
153 g_return_val_if_fail (br != NULL, NULL);
154 g_return_val_if_fail (br->ref_count > 0, NULL);
156 g_atomic_int_inc (&br->ref_count);
162 book_record_unref (BookRecord *br)
164 g_return_if_fail (br != NULL);
165 g_return_if_fail (br->ref_count > 0);
167 if (g_atomic_int_dec_and_test (&br->ref_count)) {
168 g_rec_mutex_lock (&br->cbc->priv->tracked_contacts_lock);
169 g_hash_table_foreach_remove (
170 br->cbc->priv->tracked_contacts,
171 remove_by_book, br->book_client);
172 g_rec_mutex_unlock (&br->cbc->priv->tracked_contacts_lock);
174 g_mutex_clear (&br->lock);
175 g_object_unref (br->cbc);
176 g_object_unref (br->book_client);
178 if (br->book_view != NULL)
179 g_object_unref (br->book_view);
181 g_slice_free (BookRecord, br);
186 book_record_set_book_view (BookRecord *br,
187 EBookClientView *book_view)
189 g_return_if_fail (br != NULL);
191 g_mutex_lock (&br->lock);
193 if (book_view != NULL)
194 g_object_ref (book_view);
196 if (br->book_view != NULL)
197 g_object_unref (br->book_view);
199 br->book_view = book_view;
201 g_mutex_unlock (&br->lock);
205 cal_backend_contacts_insert_book_record (ECalBackendContacts *cbc,
209 g_rec_mutex_lock (&cbc->priv->rec_mutex);
211 g_hash_table_insert (
212 cbc->priv->addressbooks,
213 g_object_ref (source),
214 book_record_ref (br));
216 g_rec_mutex_unlock (&cbc->priv->rec_mutex);
220 cal_backend_contacts_remove_book_record (ECalBackendContacts *cbc,
225 g_rec_mutex_lock (&cbc->priv->rec_mutex);
227 removed = g_hash_table_remove (cbc->priv->addressbooks, source);
229 g_rec_mutex_unlock (&cbc->priv->rec_mutex);
235 book_record_get_view_thread (gpointer user_data)
239 EBookClientView *book_view = NULL;
241 GError *error = NULL;
244 g_return_val_if_fail (br != NULL, NULL);
246 book_record_set_book_view (br, NULL);
248 query = e_book_query_andv (
250 e_book_query_field_exists (E_CONTACT_FILE_AS),
251 e_book_query_field_exists (E_CONTACT_FULL_NAME),
252 e_book_query_field_exists (E_CONTACT_GIVEN_NAME),
253 e_book_query_field_exists (E_CONTACT_NICKNAME),
256 e_book_query_field_exists (E_CONTACT_BIRTH_DATE),
257 e_book_query_field_exists (E_CONTACT_ANNIVERSARY),
260 query_sexp = e_book_query_to_string (query);
261 e_book_query_unref (query);
263 if (!e_book_client_get_view_sync (
264 br->book_client, query_sexp, &book_view, NULL, &error)) {
267 error = g_error_new_literal (
269 E_CLIENT_ERROR_OTHER_ERROR,
274 g_return_val_if_fail (
275 ((book_view != NULL) && (error == NULL)) ||
276 ((book_view == NULL) && (error != NULL)), NULL);
281 source = e_client_get_source (E_CLIENT (br->book_client));
284 "%s: Failed to get book view on '%s': %s",
285 G_STRFUNC, e_source_get_display_name (source),
288 g_clear_error (&error);
294 book_view, "objects-added",
295 G_CALLBACK (contacts_added_cb), br->cbc);
297 book_view, "objects-removed",
298 G_CALLBACK (contacts_removed_cb), br->cbc);
300 book_view, "objects-modified",
301 G_CALLBACK (contacts_modified_cb), br->cbc);
303 e_book_client_view_start (book_view, NULL);
305 book_record_set_book_view (br, book_view);
307 g_object_unref (book_view);
312 book_record_unref (br);
318 book_client_connected_cb (GObject *source_object,
319 GAsyncResult *result,
325 BookRecord *br = user_data;
326 GError *error = NULL;
328 g_return_if_fail (br != NULL);
330 client = e_book_client_connect_finish (result, &error);
334 ((client != NULL) && (error == NULL)) ||
335 ((client == NULL) && (error != NULL)));
338 g_warning ("%s: %s", G_STRFUNC, error->message);
339 g_error_free (error);
340 g_slice_free (BookRecord, br);
344 source = e_client_get_source (client);
345 br->book_client = g_object_ref (client);
346 cal_backend_contacts_insert_book_record (br->cbc, source, br);
348 thread = g_thread_new (
349 NULL, book_record_get_view_thread, book_record_ref (br));
350 g_thread_unref (thread);
352 g_object_unref (client);
355 /* ContactRecord methods */
356 static ContactRecord *
357 contact_record_new (ECalBackendContacts *cbc,
358 EBookClient *book_client,
361 ContactRecord *cr = g_new0 (ContactRecord, 1);
364 cr->book_client = book_client;
365 cr->contact = contact;
366 cr->comp_birthday = create_birthday (cbc, contact);
367 cr->comp_anniversary = create_anniversary (cbc, contact);
369 if (cr->comp_birthday)
370 e_cal_backend_notify_component_created (E_CAL_BACKEND (cbc), cr->comp_birthday);
372 if (cr->comp_anniversary)
373 e_cal_backend_notify_component_created (E_CAL_BACKEND (cbc), cr->comp_anniversary);
375 g_object_ref (G_OBJECT (contact));
381 contact_record_free (ContactRecord *cr)
385 g_object_unref (G_OBJECT (cr->contact));
387 /* Remove the birthday event */
388 if (cr->comp_birthday) {
389 id = e_cal_component_get_id (cr->comp_birthday);
390 e_cal_backend_notify_component_removed (E_CAL_BACKEND (cr->cbc), id, cr->comp_birthday, NULL);
392 e_cal_component_free_id (id);
393 g_object_unref (G_OBJECT (cr->comp_birthday));
396 /* Remove the anniversary event */
397 if (cr->comp_anniversary) {
398 id = e_cal_component_get_id (cr->comp_anniversary);
400 e_cal_backend_notify_component_removed (E_CAL_BACKEND (cr->cbc), id, cr->comp_anniversary, NULL);
402 e_cal_component_free_id (id);
403 g_object_unref (G_OBJECT (cr->comp_anniversary));
409 /* ContactRecordCB methods */
410 typedef struct _ContactRecordCB {
411 ECalBackendContacts *cbc;
412 ECalBackendSExp *sexp;
417 static ContactRecordCB *
418 contact_record_cb_new (ECalBackendContacts *cbc,
419 ECalBackendSExp *sexp,
422 ContactRecordCB *cb_data = g_new (ContactRecordCB, 1);
425 cb_data->sexp = sexp;
426 cb_data->as_string = as_string;
427 cb_data->result = NULL;
433 contact_record_cb_free (ContactRecordCB *cb_data,
434 gboolean can_free_result)
436 if (can_free_result) {
437 if (cb_data->as_string)
438 g_slist_foreach (cb_data->result, (GFunc) g_free, NULL);
439 g_slist_free (cb_data->result);
446 contact_record_cb (gpointer key,
450 ETimezoneCache *timezone_cache;
451 ContactRecordCB *cb_data = user_data;
452 ContactRecord *record = value;
455 timezone_cache = E_TIMEZONE_CACHE (cb_data->cbc);
457 if (record->comp_birthday && e_cal_backend_sexp_match_comp (cb_data->sexp, record->comp_birthday, timezone_cache)) {
458 if (cb_data->as_string)
459 data = e_cal_component_get_as_string (record->comp_birthday);
461 data = record->comp_birthday;
463 cb_data->result = g_slist_prepend (cb_data->result, data);
466 if (record->comp_anniversary && e_cal_backend_sexp_match_comp (cb_data->sexp, record->comp_anniversary, timezone_cache)) {
467 if (cb_data->as_string)
468 data = e_cal_component_get_as_string (record->comp_anniversary);
470 data = record->comp_anniversary;
472 cb_data->result = g_slist_prepend (cb_data->result, data);
477 source_added_cb (ESourceRegistry *registry,
479 ECalBackendContacts *cbc)
481 ESourceContacts *extension;
482 const gchar *extension_name;
484 /* We're only interested in address books. */
485 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
486 if (!e_source_has_extension (source, extension_name))
489 extension_name = E_SOURCE_EXTENSION_CONTACTS_BACKEND;
490 extension = e_source_get_extension (source, extension_name);
492 if (extension == NULL)
495 if (e_source_contacts_get_include_me (extension))
496 create_book_record (cbc, source);
500 source_removed_cb (ESourceRegistry *registry,
502 ECalBackendContacts *cbc)
504 cal_backend_contacts_remove_book_record (cbc, source);
508 cal_backend_contacts_load_sources (gpointer user_data)
510 ESourceRegistry *registry;
511 ECalBackend *backend;
513 const gchar *extension_name;
515 backend = E_CAL_BACKEND (user_data);
516 registry = e_cal_backend_get_registry (backend);
518 /* Query all address book sources from the registry. */
520 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
521 list = e_source_registry_list_sources (registry, extension_name);
522 for (link = list; link != NULL; link = g_list_next (link))
524 registry, E_SOURCE (link->data),
525 E_CAL_BACKEND_CONTACTS (backend));
526 g_list_free_full (list, (GDestroyNotify) g_object_unref);
529 registry, "source-added",
530 G_CALLBACK (source_added_cb), backend);
533 registry, "source-removed",
534 G_CALLBACK (source_removed_cb), backend);
539 /************************************************************************************/
542 contacts_modified_cb (EBookClientView *book_view,
543 const GSList *contacts,
546 ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
547 EBookClient *book_client;
550 book_client = e_book_client_view_ref_client (book_view);
551 if (book_client == NULL)
554 g_rec_mutex_lock (&cbc->priv->tracked_contacts_lock);
556 for (ii = contacts; ii; ii = ii->next) {
557 EContact *contact = E_CONTACT (ii->data);
558 const gchar *uid = e_contact_get_const (contact, E_CONTACT_UID);
559 EContactDate *birthday, *anniversary;
561 /* Because this is a change of contact, then always remove old tracked data
562 * and if possible, add with (possibly) new values.
564 g_hash_table_remove (cbc->priv->tracked_contacts, (gchar *) uid);
566 birthday = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
567 anniversary = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
569 if (birthday || anniversary) {
570 ContactRecord *cr = contact_record_new (cbc, book_client, contact);
571 g_hash_table_insert (cbc->priv->tracked_contacts, g_strdup (uid), cr);
574 e_contact_date_free (birthday);
575 e_contact_date_free (anniversary);
578 g_rec_mutex_unlock (&cbc->priv->tracked_contacts_lock);
580 g_object_unref (book_client);
584 contacts_added_cb (EBookClientView *book_view,
585 const GSList *contacts,
588 ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
589 EBookClient *book_client;
592 book_client = e_book_client_view_ref_client (book_view);
593 if (book_client == NULL)
596 g_rec_mutex_lock (&cbc->priv->tracked_contacts_lock);
598 /* See if any new contacts have BIRTHDAY or ANNIVERSARY fields */
599 for (ii = contacts; ii; ii = ii->next) {
600 EContact *contact = E_CONTACT (ii->data);
601 EContactDate *birthday, *anniversary;
603 birthday = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
604 anniversary = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
606 if (birthday || anniversary) {
607 ContactRecord *cr = contact_record_new (cbc, book_client, contact);
608 const gchar *uid = e_contact_get_const (contact, E_CONTACT_UID);
610 g_hash_table_insert (cbc->priv->tracked_contacts, g_strdup (uid), cr);
613 e_contact_date_free (birthday);
614 e_contact_date_free (anniversary);
617 g_rec_mutex_unlock (&cbc->priv->tracked_contacts_lock);
619 g_object_unref (book_client);
623 contacts_removed_cb (EBookClientView *book_view,
624 const GSList *contact_ids,
627 ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (user_data);
630 g_rec_mutex_lock (&cbc->priv->tracked_contacts_lock);
632 /* Stop tracking these */
633 for (ii = contact_ids; ii; ii = ii->next)
634 g_hash_table_remove (cbc->priv->tracked_contacts, ii->data);
636 g_rec_mutex_unlock (&cbc->priv->tracked_contacts_lock);
639 /************************************************************************************/
640 static struct icaltimetype
641 cdate_to_icaltime (EContactDate *cdate)
643 struct icaltimetype ret = icaltime_null_time ();
645 ret.year = cdate->year;
646 ret.month = cdate->month;
647 ret.day = cdate->day;
651 ret.is_daylight = FALSE;
653 ret.hour = ret.minute = ret.second = 0;
659 manage_comp_alarm_update (ECalBackendContacts *cbc,
662 gchar *old_comp_str, *new_comp_str;
663 ECalComponent *old_comp;
665 g_return_if_fail (cbc != NULL);
666 g_return_if_fail (comp != NULL);
668 old_comp = e_cal_component_clone (comp);
669 setup_alarm (cbc, comp);
671 old_comp_str = e_cal_component_get_as_string (old_comp);
672 new_comp_str = e_cal_component_get_as_string (comp);
674 /* check if component changed and notify if so */
675 if (old_comp_str && new_comp_str && !g_str_equal (old_comp_str, new_comp_str))
676 e_cal_backend_notify_component_modified (E_CAL_BACKEND (cbc), old_comp, comp);
678 g_free (old_comp_str);
679 g_free (new_comp_str);
680 g_object_unref (old_comp);
684 update_alarm_cb (gpointer key,
688 ECalBackendContacts *cbc = user_data;
689 ContactRecord *record = value;
691 g_return_if_fail (cbc != NULL);
692 g_return_if_fail (record != NULL);
694 if (record->comp_birthday)
695 manage_comp_alarm_update (cbc, record->comp_birthday);
697 if (record->comp_anniversary)
698 manage_comp_alarm_update (cbc, record->comp_anniversary);
702 update_tracked_alarms_cb (gpointer user_data)
704 ECalBackendContacts *cbc = user_data;
706 g_return_val_if_fail (cbc != NULL, FALSE);
708 g_rec_mutex_lock (&cbc->priv->tracked_contacts_lock);
709 g_hash_table_foreach (cbc->priv->tracked_contacts, update_alarm_cb, cbc);
710 g_rec_mutex_unlock (&cbc->priv->tracked_contacts_lock);
712 cbc->priv->update_alarms_id = 0;
717 #define BA_CONF_ENABLED "contacts-reminder-enabled"
718 #define BA_CONF_INTERVAL "contacts-reminder-interval"
719 #define BA_CONF_UNITS "contacts-reminder-units"
722 alarm_config_changed_cb (GSettings *settings,
726 ECalBackendContacts *cbc = user_data;
728 g_return_if_fail (cbc != NULL);
730 if (g_strcmp0 (key, BA_CONF_ENABLED) != 0 &&
731 g_strcmp0 (key, BA_CONF_INTERVAL) != 0 &&
732 g_strcmp0 (key, BA_CONF_UNITS) != 0)
735 setup_alarm (cbc, NULL);
737 if (!cbc->priv->update_alarms_id)
738 cbc->priv->update_alarms_id = g_idle_add (update_tracked_alarms_cb, cbc);
741 /* When called with NULL, then just refresh local static variables on setup change from the user. */
743 setup_alarm (ECalBackendContacts *cbc,
746 ECalComponentAlarm *alarm;
747 ECalComponentAlarmTrigger trigger;
748 ECalComponentText summary;
750 g_return_if_fail (cbc != NULL);
752 if (!comp || cbc->priv->alarm_interval == -1) {
755 if (cbc->priv->alarm_interval == -1) {
756 /* initial setup, hook callback for changes too */
757 cbc->priv->notifyid = g_signal_connect (cbc->priv->settings,
758 "changed", G_CALLBACK (alarm_config_changed_cb), cbc);
761 cbc->priv->alarm_enabled = g_settings_get_boolean (cbc->priv->settings, BA_CONF_ENABLED);
762 cbc->priv->alarm_interval = g_settings_get_int (cbc->priv->settings, BA_CONF_INTERVAL);
764 str = g_settings_get_string (cbc->priv->settings, BA_CONF_UNITS);
765 if (str && !strcmp (str, "days"))
766 cbc->priv->alarm_units = CAL_DAYS;
767 else if (str && !strcmp (str, "hours"))
768 cbc->priv->alarm_units = CAL_HOURS;
770 cbc->priv->alarm_units = CAL_MINUTES;
774 if (cbc->priv->alarm_interval <= 0)
775 cbc->priv->alarm_interval = 1;
781 /* ensure no alarms left */
782 e_cal_component_remove_all_alarms (comp);
784 /* do not want alarms, return */
785 if (!cbc->priv->alarm_enabled)
788 alarm = e_cal_component_alarm_new ();
789 e_cal_component_get_summary (comp, &summary);
790 e_cal_component_alarm_set_description (alarm, &summary);
791 e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
793 trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
795 memset (&trigger.u.rel_duration, 0, sizeof (trigger.u.rel_duration));
797 trigger.u.rel_duration.is_neg = TRUE;
799 switch (cbc->priv->alarm_units) {
801 trigger.u.rel_duration.minutes = cbc->priv->alarm_interval;
805 trigger.u.rel_duration.hours = cbc->priv->alarm_interval;
809 trigger.u.rel_duration.days = cbc->priv->alarm_interval;
813 g_warning ("%s: wrong units %d\n", G_STRFUNC, cbc->priv->alarm_units);
814 e_cal_component_alarm_free (alarm);
818 e_cal_component_alarm_set_trigger (alarm, trigger);
819 e_cal_component_add_alarm (comp, alarm);
820 e_cal_component_alarm_free (alarm);
823 #undef BA_CONF_ENABLED
824 #undef BA_CONF_INTERVAL
827 /* Contact -> Event creator */
828 static ECalComponent *
829 create_component (ECalBackendContacts *cbc,
832 const gchar *summary)
834 ECalComponent *cal_comp;
835 ECalComponentText comp_summary;
836 icalcomponent *ical_comp;
838 struct icaltimetype itt;
839 ECalComponentDateTime dt;
840 struct icalrecurrencetype r;
844 g_return_val_if_fail (E_IS_CAL_BACKEND_CONTACTS (cbc), NULL);
849 ical_comp = icalcomponent_new (ICAL_VEVENT_COMPONENT);
851 since_year = g_strdup_printf ("%04d", cdate->year);
852 prop = icalproperty_new_x (since_year);
853 icalproperty_set_x_name (prop, "X-EVOLUTION-SINCE-YEAR");
854 icalcomponent_add_property (ical_comp, prop);
857 /* Create the event object */
858 cal_comp = e_cal_component_new ();
859 e_cal_component_set_icalcomponent (cal_comp, ical_comp);
862 d (g_message ("Creating UID: %s", uid));
863 e_cal_component_set_uid (cal_comp, uid);
865 /* Set all-day event's date from contact data */
866 itt = cdate_to_icaltime (cdate);
869 e_cal_component_set_dtstart (cal_comp, &dt);
871 itt = cdate_to_icaltime (cdate);
872 icaltime_adjust (&itt, 1, 0, 0, 0);
875 /* We have to add 1 day to DTEND, as it is not inclusive. */
876 e_cal_component_set_dtend (cal_comp, &dt);
878 /* Create yearly recurrence */
879 icalrecurrencetype_clear (&r);
880 r.freq = ICAL_YEARLY_RECURRENCE;
882 recur_list.data = &r;
883 recur_list.next = NULL;
884 e_cal_component_set_rrule_list (cal_comp, &recur_list);
887 comp_summary.value = summary;
888 comp_summary.altrep = NULL;
889 e_cal_component_set_summary (cal_comp, &comp_summary);
891 /* Set category and visibility */
892 if (g_str_has_suffix (uid, ANNIVERSARY_UID_EXT))
893 e_cal_component_set_categories (cal_comp, _("Anniversary"));
894 else if (g_str_has_suffix (uid, BIRTHDAY_UID_EXT))
895 e_cal_component_set_categories (cal_comp, _("Birthday"));
897 e_cal_component_set_classification (cal_comp, E_CAL_COMPONENT_CLASS_PRIVATE);
899 /* Birthdays/anniversaries are shown as free time */
900 e_cal_component_set_transparency (cal_comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
902 /* setup alarms if required */
903 setup_alarm (cbc, cal_comp);
905 /* Don't forget to call commit()! */
906 e_cal_component_commit_sequence (cal_comp);
911 static ECalComponent *
912 create_birthday (ECalBackendContacts *cbc,
916 ECalComponent *cal_comp;
921 cdate = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
922 name = e_contact_get_const (contact, E_CONTACT_FILE_AS);
924 name = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
926 name = e_contact_get_const (contact, E_CONTACT_NICKNAME);
930 uid = g_strdup_printf ("%s%s", (gchar *) e_contact_get_const (contact, E_CONTACT_UID), BIRTHDAY_UID_EXT);
931 summary = g_strdup_printf (_("Birthday: %s"), name);
933 cal_comp = create_component (cbc, uid, cdate, summary);
935 e_contact_date_free (cdate);
942 static ECalComponent *
943 create_anniversary (ECalBackendContacts *cbc,
947 ECalComponent *cal_comp;
952 cdate = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
953 name = e_contact_get_const (contact, E_CONTACT_FILE_AS);
955 name = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
957 name = e_contact_get_const (contact, E_CONTACT_NICKNAME);
961 uid = g_strdup_printf ("%s%s", (gchar *) e_contact_get_const (contact, E_CONTACT_UID), ANNIVERSARY_UID_EXT);
962 summary = g_strdup_printf (_("Anniversary: %s"), name);
964 cal_comp = create_component (cbc, uid, cdate, summary);
966 e_contact_date_free (cdate);
973 /************************************************************************************/
974 /* Calendar backend method implementations */
976 /* First the empty stubs */
979 e_cal_backend_contacts_get_backend_property (ECalBackend *backend,
980 const gchar *prop_name)
982 g_return_val_if_fail (prop_name != NULL, FALSE);
984 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
987 } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS) ||
988 g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
989 /* A contact backend has no particular email address associated
990 * with it (although that would be a useful feature some day).
994 } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT)) {
998 /* Chain up to parent's get_backend_property() method. */
999 return E_CAL_BACKEND_CLASS (e_cal_backend_contacts_parent_class)->
1000 get_backend_property (backend, prop_name);
1004 e_cal_backend_contacts_get_object (ECalBackendSync *backend,
1006 GCancellable *cancellable,
1012 ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
1013 ECalBackendContactsPrivate *priv = cbc->priv;
1014 ContactRecord *record;
1018 g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
1020 } else if (g_str_has_suffix (uid, ANNIVERSARY_UID_EXT))
1021 real_uid = g_strndup (uid, strlen (uid) - strlen (ANNIVERSARY_UID_EXT));
1022 else if (g_str_has_suffix (uid, BIRTHDAY_UID_EXT))
1023 real_uid = g_strndup (uid, strlen (uid) - strlen (BIRTHDAY_UID_EXT));
1025 g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
1029 g_rec_mutex_lock (&priv->tracked_contacts_lock);
1030 record = g_hash_table_lookup (priv->tracked_contacts, real_uid);
1034 g_rec_mutex_unlock (&priv->tracked_contacts_lock);
1035 g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
1039 if (record->comp_birthday && g_str_has_suffix (uid, BIRTHDAY_UID_EXT)) {
1040 *object = e_cal_component_get_as_string (record->comp_birthday);
1041 g_rec_mutex_unlock (&priv->tracked_contacts_lock);
1043 d (g_message ("Return birthday: %s", *object));
1047 if (record->comp_anniversary && g_str_has_suffix (uid, ANNIVERSARY_UID_EXT)) {
1048 *object = e_cal_component_get_as_string (record->comp_anniversary);
1049 g_rec_mutex_unlock (&priv->tracked_contacts_lock);
1051 d (g_message ("Return anniversary: %s", *object));
1055 g_rec_mutex_unlock (&priv->tracked_contacts_lock);
1057 d (g_message ("Returning nothing for uid: %s", uid));
1059 g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
1063 e_cal_backend_contacts_get_free_busy (ECalBackendSync *backend,
1065 GCancellable *cancellable,
1066 const GSList *users,
1072 /* Birthdays/anniversaries don't count as busy time */
1074 icalcomponent *vfb = icalcomponent_new_vfreebusy ();
1075 icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
1080 icalparameter *param;
1082 prop = icalproperty_new_organizer (address);
1083 if (prop != NULL && cn != NULL) {
1084 param = icalparameter_new_cn (cn);
1085 icalproperty_add_parameter (prop, param);
1088 icalcomponent_add_property (vfb, prop);
1091 icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
1092 icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));
1094 calobj = icalcomponent_as_ical_string_r (vfb);
1095 *freebusy = g_slist_append (NULL, calobj);
1096 icalcomponent_free (vfb);
1103 e_cal_backend_contacts_receive_objects (ECalBackendSync *backend,
1105 GCancellable *cancellable,
1106 const gchar *calobj,
1109 g_propagate_error (perror, EDC_ERROR (PermissionDenied));
1113 e_cal_backend_contacts_send_objects (ECalBackendSync *backend,
1115 GCancellable *cancellable,
1116 const gchar *calobj,
1118 gchar **modified_calobj,
1122 *modified_calobj = NULL;
1123 /* TODO: Investigate this */
1124 g_propagate_error (perror, EDC_ERROR (PermissionDenied));
1127 /* Then the real implementations */
1130 e_cal_backend_contacts_notify_online_cb (ECalBackend *backend,
1133 e_cal_backend_set_writable (backend, FALSE);
1137 e_cal_backend_contacts_open (ECalBackendSync *backend,
1139 GCancellable *cancellable,
1140 gboolean only_if_exists,
1143 ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
1144 ECalBackendContactsPrivate *priv = cbc->priv;
1146 if (priv->addressbook_loaded)
1149 priv->addressbook_loaded = TRUE;
1150 e_cal_backend_set_writable (E_CAL_BACKEND (backend), FALSE);
1151 e_backend_set_online (E_BACKEND (backend), TRUE);
1154 /* Add_timezone handler for the file backend */
1156 e_cal_backend_contacts_add_timezone (ECalBackendSync *backend,
1158 GCancellable *cancellable,
1162 icalcomponent *tz_comp;
1165 tz_comp = icalparser_parse_string (tzobj);
1167 g_propagate_error (error, EDC_ERROR (InvalidObject));
1171 if (icalcomponent_isa (tz_comp) != ICAL_VTIMEZONE_COMPONENT) {
1172 g_propagate_error (error, EDC_ERROR (InvalidObject));
1176 zone = icaltimezone_new ();
1177 icaltimezone_set_component (zone, tz_comp);
1178 e_timezone_cache_add_timezone (E_TIMEZONE_CACHE (backend), zone);
1179 icaltimezone_free (zone, TRUE);
1183 e_cal_backend_contacts_get_object_list (ECalBackendSync *backend,
1185 GCancellable *cancellable,
1186 const gchar *sexp_string,
1190 ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
1191 ECalBackendContactsPrivate *priv = cbc->priv;
1192 ECalBackendSExp *sexp = e_cal_backend_sexp_new (sexp_string);
1193 ContactRecordCB *cb_data;
1196 g_propagate_error (perror, EDC_ERROR (InvalidQuery));
1200 cb_data = contact_record_cb_new (cbc, sexp, TRUE);
1202 g_rec_mutex_lock (&priv->tracked_contacts_lock);
1203 g_hash_table_foreach (priv->tracked_contacts, contact_record_cb, cb_data);
1204 g_rec_mutex_unlock (&priv->tracked_contacts_lock);
1206 *objects = cb_data->result;
1208 contact_record_cb_free (cb_data, FALSE);
1212 e_cal_backend_contacts_start_view (ECalBackend *backend,
1213 EDataCalView *query)
1215 ECalBackendContacts *cbc = E_CAL_BACKEND_CONTACTS (backend);
1216 ECalBackendContactsPrivate *priv = cbc->priv;
1217 ECalBackendSExp *sexp;
1218 ContactRecordCB *cb_data;
1220 sexp = e_data_cal_view_get_sexp (query);
1222 GError *error = EDC_ERROR (InvalidQuery);
1223 e_data_cal_view_notify_complete (query, error);
1224 g_error_free (error);
1228 cb_data = contact_record_cb_new (cbc, sexp, FALSE);
1230 g_rec_mutex_lock (&priv->tracked_contacts_lock);
1231 g_hash_table_foreach (priv->tracked_contacts, contact_record_cb, cb_data);
1232 e_data_cal_view_notify_components_added (query, cb_data->result);
1233 g_rec_mutex_unlock (&priv->tracked_contacts_lock);
1235 contact_record_cb_free (cb_data, TRUE);
1237 e_data_cal_view_notify_complete (query, NULL /* Success */);
1240 /***********************************************************************************
1243 /* Finalize handler for the contacts backend */
1245 e_cal_backend_contacts_finalize (GObject *object)
1247 ECalBackendContactsPrivate *priv;
1249 priv = E_CAL_BACKEND_CONTACTS_GET_PRIVATE (object);
1251 if (priv->update_alarms_id) {
1252 g_source_remove (priv->update_alarms_id);
1253 priv->update_alarms_id = 0;
1256 g_hash_table_destroy (priv->addressbooks);
1257 g_hash_table_destroy (priv->tracked_contacts);
1259 g_signal_handler_disconnect (priv->settings, priv->notifyid);
1261 g_object_unref (priv->settings);
1262 g_rec_mutex_clear (&priv->rec_mutex);
1263 g_rec_mutex_clear (&priv->tracked_contacts_lock);
1265 /* Chain up to parent's finalize() method. */
1266 G_OBJECT_CLASS (e_cal_backend_contacts_parent_class)->finalize (object);
1270 e_cal_backend_contacts_dispose (GObject *object)
1272 ESourceRegistry *registry;
1274 registry = e_cal_backend_get_registry (E_CAL_BACKEND (object));
1275 g_signal_handlers_disconnect_by_data (registry, object);
1277 /* Chain up to parent's dispose() method. */
1278 G_OBJECT_CLASS (e_cal_backend_contacts_parent_class)->dispose (object);
1282 e_cal_backend_contacts_constructed (GObject *object)
1284 /* Load address book sources from an idle callback
1285 * to avoid deadlocking e_data_factory_ref_backend(). */
1287 G_PRIORITY_DEFAULT_IDLE,
1288 cal_backend_contacts_load_sources,
1289 g_object_ref (object),
1290 (GDestroyNotify) g_object_unref);
1292 /* Chain up to parent's constructed() method. */
1293 G_OBJECT_CLASS (e_cal_backend_contacts_parent_class)->
1294 constructed (object);
1297 /* Object initialization function for the contacts backend */
1299 e_cal_backend_contacts_init (ECalBackendContacts *cbc)
1301 cbc->priv = E_CAL_BACKEND_CONTACTS_GET_PRIVATE (cbc);
1303 g_rec_mutex_init (&cbc->priv->rec_mutex);
1304 g_rec_mutex_init (&cbc->priv->tracked_contacts_lock);
1306 cbc->priv->addressbooks = g_hash_table_new_full (
1307 (GHashFunc) e_source_hash,
1308 (GEqualFunc) e_source_equal,
1309 (GDestroyNotify) g_object_unref,
1310 (GDestroyNotify) book_record_unref);
1312 cbc->priv->tracked_contacts = g_hash_table_new_full (
1313 (GHashFunc) g_str_hash,
1314 (GEqualFunc) g_str_equal,
1315 (GDestroyNotify) g_free,
1316 (GDestroyNotify) contact_record_free);
1318 cbc->priv->settings = g_settings_new ("org.gnome.evolution-data-server.calendar");
1319 cbc->priv->notifyid = 0;
1320 cbc->priv->update_alarms_id = 0;
1321 cbc->priv->alarm_enabled = FALSE;
1322 cbc->priv->alarm_interval = -1;
1323 cbc->priv->alarm_units = CAL_MINUTES;
1326 cbc, "notify::online",
1327 G_CALLBACK (e_cal_backend_contacts_notify_online_cb), NULL);
1331 e_cal_backend_contacts_create_objects (ECalBackendSync *backend,
1333 GCancellable *cancellable,
1334 const GSList *calobjs,
1336 GSList **new_components,
1339 g_propagate_error (perror, EDC_ERROR (PermissionDenied));
1342 /* Class initialization function for the contacts backend */
1344 e_cal_backend_contacts_class_init (ECalBackendContactsClass *class)
1346 GObjectClass *object_class;
1347 ECalBackendClass *backend_class;
1348 ECalBackendSyncClass *sync_class;
1350 g_type_class_add_private (class, sizeof (ECalBackendContactsPrivate));
1352 object_class = (GObjectClass *) class;
1353 backend_class = (ECalBackendClass *) class;
1354 sync_class = (ECalBackendSyncClass *) class;
1356 object_class->finalize = e_cal_backend_contacts_finalize;
1357 object_class->dispose = e_cal_backend_contacts_dispose;
1358 object_class->constructed = e_cal_backend_contacts_constructed;
1360 /* Execute one method at a time. */
1361 backend_class->use_serial_dispatch_queue = TRUE;
1363 backend_class->get_backend_property = e_cal_backend_contacts_get_backend_property;
1365 sync_class->open_sync = e_cal_backend_contacts_open;
1366 sync_class->create_objects_sync = e_cal_backend_contacts_create_objects;
1367 sync_class->receive_objects_sync = e_cal_backend_contacts_receive_objects;
1368 sync_class->send_objects_sync = e_cal_backend_contacts_send_objects;
1369 sync_class->get_object_sync = e_cal_backend_contacts_get_object;
1370 sync_class->get_object_list_sync = e_cal_backend_contacts_get_object_list;
1371 sync_class->add_timezone_sync = e_cal_backend_contacts_add_timezone;
1372 sync_class->get_free_busy_sync = e_cal_backend_contacts_get_free_busy;
1374 backend_class->start_view = e_cal_backend_contacts_start_view;
1376 /* Register our ESource extension. */
1377 E_TYPE_SOURCE_CONTACTS;