/* cal-backend-wcap.c * * Copyright (C) 2002-2004 Sun Microsystems, Inc * * AUTHORS * Jack Jia * Harry Lu * Alfred Peng * Jedy Wang * Rodrigo Moya * JP Rosevear * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/sunone-ace.h" #include "lib/sunone-util.h" #include "storage/sunone-component.h" #include "storage/sunone-config-listener.h" #include "storage/sunone-account.h" #include "cal-backend-wcap.h" struct _CalBackendWCAPPrivate { char *uri; char *calid; char *alarm_email_address; char *account_email_address; SunOneConnection *so_cnc; SunOneCalendarProperties *props; SunOneCompType type; CalMode mode; char *cache_name; ECalBackendSyncStatus open_error; gint timeout_id; time_t timestamp; GMutex *set_mode_lock; GMutex *open_lock; gboolean is_dirty; guint dirty_idle_id; GMutex *idle_save_mutex; GHashTable *timezones; icaltimezone *default_zone; icaltimezone *server_default_zone; GHashTable *objects; GHashTable *instances; }; extern char *evolution_dir; static ECalBackendSyncStatus cal_backend_wcap_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object); static ECalBackendSyncStatus cal_backend_wcap_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address); static ECalBackendSyncStatus add_timezone (CalBackendWCAP *wcap, icalcomponent *icalcomp); static void match_object (gpointer key, gpointer value, gpointer data); static ECalBackendClass *parent_class = NULL; #define d(x) G_DEFINE_TYPE (CalBackendWCAP, cal_backend_wcap, E_TYPE_CAL_BACKEND_SYNC) static gboolean compare_dates (ECalComponentDateTime *adt, ECalComponentDateTime *bdt) { if (!adt->value && !bdt->value) return TRUE; else if (!adt->value || !bdt->value) return FALSE; if ((adt->tzid && !bdt->tzid) || (!adt->tzid && bdt->tzid)) return FALSE; if (adt->tzid && bdt->tzid && strcmp (adt->tzid, bdt->tzid)) return FALSE; return !icaltime_compare_date_only (*adt->value, *bdt->value); } static gboolean compare_times (ECalComponentDateTime *adt, ECalComponentDateTime *bdt) { if (!adt->value && !bdt->value) return TRUE; else if (!adt->value || !bdt->value) return FALSE; if ((adt->tzid && !bdt->tzid) || (!adt->tzid && bdt->tzid)) return FALSE; if (adt->tzid && bdt->tzid && strcmp (adt->tzid, bdt->tzid)) return FALSE; return (adt->value->hour == bdt->value->hour) && (adt->value->minute == bdt->value->minute) && (adt->value->second == bdt->value->second); } static gboolean start_equal (ECalComponent *acomp, ECalComponent *bcomp, gboolean times) { ECalComponentDateTime adt, bdt; gboolean retval; e_cal_component_get_dtstart (acomp, &adt); e_cal_component_get_dtstart (bcomp, &bdt); retval = times ? compare_times (&adt, &bdt) : compare_dates (&adt, &bdt); e_cal_component_free_datetime (&adt); e_cal_component_free_datetime (&bdt); return retval; } static gboolean end_equal (ECalComponent *acomp, ECalComponent *bcomp, gboolean times) { ECalComponentDateTime adt, bdt; gboolean retval; e_cal_component_get_dtend (acomp, &adt); e_cal_component_get_dtend (bcomp, &bdt); retval = times ? compare_times (&adt, &bdt) : compare_dates (&adt, &bdt); e_cal_component_free_datetime (&adt); e_cal_component_free_datetime (&bdt); return retval; } static gboolean rrules_equal (ECalComponent *acomp, ECalComponent *bcomp) { GSList *alist, *blist, *l, *m; gboolean retval = TRUE; /* If neither have rrules, we call them equal */ if (!e_cal_component_has_rrules (acomp) && !e_cal_component_has_rrules (bcomp)) return TRUE; /* If only one has rrules, they are not equal */ if ((e_cal_component_has_rrules (acomp) && !e_cal_component_has_rrules (bcomp)) || (!e_cal_component_has_rrules (acomp) && e_cal_component_has_rrules (bcomp))) return FALSE; /* Try to match each rrule */ e_cal_component_get_rrule_list (acomp, &alist); e_cal_component_get_rrule_list (bcomp, &blist); for (l = alist; l != NULL; l = l->next) { struct icalrecurrencetype *a = l->data; gboolean match = FALSE; for (m = blist; m != NULL; m = m->next) { struct icalrecurrencetype *b = m->data; if (!memcmp (a, b, sizeof (struct icalrecurrencetype))) { blist = g_slist_remove_link (blist, m); e_cal_component_free_recur_list (m); match = TRUE; break; } } if (!match) { retval = FALSE; break; } } e_cal_component_free_recur_list (alist); e_cal_component_free_recur_list (blist); return retval; } gboolean cal_backend_wcap_need_import (CalBackendWCAP *wcap, ECalComponent *comp) { ECalComponentOrganizer organizer; gboolean needImport = FALSE; e_cal_component_get_organizer (comp, &organizer); if (organizer.value) { if (!strncasecmp ("mailto:", organizer.value, 7)) { /* If we received this via email, we need to import it to the server */ needImport = TRUE; } else if (strcasecmp (organizer.value, cal_backend_wcap_get_calid (wcap))) { /* If we are not the organizer and we are not in the attendee list, * it must be a import */ GSList *attendees, *l; if (!e_cal_component_has_attendees (comp)) { needImport = TRUE; } else { char *email = g_strconcat ("MAILTO:", cal_backend_wcap_get_account_email (wcap), NULL); e_cal_component_get_attendee_list (comp, &attendees); needImport = TRUE; for (l = attendees; l!= NULL; l=l->next) { ECalComponentAttendee *a = l->data; if (!strcasecmp (cal_backend_wcap_get_calid (wcap), a->value) || !strcasecmp (email, a->value)) { needImport = FALSE; break; } } g_free (email); } } } return needImport; } ECalBackendSyncStatus cal_backend_wcap_handle_import (CalBackendWCAP *wcap, ECalComponent *comp, CalObjModType mod, icalcomponent *toplevel_comp, icalcomponent *subcomp, icalcomponent_kind child_kind) { ECalComponent *existing; icalcomponent *toplevel_import, *import_comp; icalcompiter iter; icalcomponent_kind import_kind; const char *uid; guint error_code; ECalBackendSyncStatus result = GNOME_Evolution_Calendar_Success; e_cal_component_get_uid (comp, &uid); existing = g_hash_table_lookup (cal_backend_wcap_get_objects (wcap), uid); /* If its the original item being saved again, it won't have * the mangled uid so we'll get the first instance */ if (!existing && e_cal_component_has_recurrences (comp) && !e_cal_component_is_instance (comp)) { GList *ilist; ilist = g_hash_table_lookup (cal_backend_wcap_get_instances (wcap), uid); if (ilist) existing = ilist->data; } /* If it already exists in our hashes, unmangle the uid so we import it with the correct uid */ if (existing && e_cal_component_is_instance (comp)) sunone_util_unmangle_uid (comp); e_cal_component_get_uid (comp, &uid); toplevel_import = e_cal_util_new_top_level (); iter = icalcomponent_begin_component (toplevel_comp, ICAL_ANY_COMPONENT); while ((import_comp = icalcompiter_deref (&iter))) { import_kind = icalcomponent_isa (import_comp); if (import_kind != child_kind && import_kind != ICAL_VTIMEZONE_COMPONENT) { icalcompiter_next (&iter); continue; } if (import_kind == ICAL_VTIMEZONE_COMPONENT) { icalcomponent *clone; clone = icalcomponent_new_clone (import_comp); icalcomponent_add_component (toplevel_import, clone); } else if (import_comp == subcomp) { icalcomponent *clone; icalproperty *prop, *prop_clone; if (!existing || !e_cal_component_has_recurrences (comp) || mod == CALOBJ_MOD_THIS) { clone = icalcomponent_new_clone (import_comp); icalcomponent_add_component (toplevel_import, clone); } else { GList *ilist, *l; gpointer orig_key; gboolean time_change; if (!g_hash_table_lookup_extended (cal_backend_wcap_get_instances (wcap), uid, &orig_key, (gpointer *)&ilist)) g_assert_not_reached (); if (!start_equal (comp, existing, FALSE) || !end_equal (comp, existing, FALSE) || !rrules_equal (comp, existing)) { /* Delete the old ones and let the server re-expand */ if (import_kind == ICAL_VEVENT_COMPONENT) sunone_connection_deleteevents_by_id (cal_backend_wcap_get_connection (wcap), cal_backend_wcap_get_calid (wcap), uid, cal_backend_wcap_get_rid_string (existing), cal_backend_wcap_to_sunone_mod (mod, existing)); else if (import_kind == ICAL_VTODO_COMPONENT) sunone_connection_deletetodos_by_id (cal_backend_wcap_get_connection (wcap), cal_backend_wcap_get_calid (wcap), uid, cal_backend_wcap_get_rid_string (existing), cal_backend_wcap_to_sunone_mod (mod, existing)); clone = icalcomponent_new_clone (import_comp); prop = icalcomponent_get_first_property (clone, ICAL_RECURRENCEID_PROPERTY); icalcomponent_remove_property (clone, prop); icalcomponent_add_component (toplevel_import, clone); icalcompiter_next (&iter); continue; } time_change = !start_equal (comp, existing, TRUE) || !end_equal (comp, existing, TRUE); switch (mod) { case CALOBJ_MOD_THISANDPRIOR: break; case CALOBJ_MOD_THISANDFUTURE: ilist = g_list_find_custom (ilist, comp, cal_backend_wcap_instance_list_compare); break; case CALOBJ_MOD_ALL: default: break; } for (l = ilist; l; l = l->next) { ECalComponent *recur_comp = l->data; icalcomponent *recur_icalcomp; ECalComponentDateTime dt; struct icaltimetype tt; recur_icalcomp = e_cal_component_get_icalcomponent (recur_comp); clone = icalcomponent_new_clone (import_comp); /* Set correct recurrence id */ prop = icalcomponent_get_first_property (recur_icalcomp, ICAL_RECURRENCEID_PROPERTY); prop_clone = icalproperty_new_clone (prop); prop = icalcomponent_get_first_property (clone, ICAL_RECURRENCEID_PROPERTY); icalcomponent_remove_property (clone, prop); icalproperty_free (prop); icalcomponent_add_property (clone, prop_clone); /* Preserve the start date (and possibly the time) */ e_cal_component_get_dtstart (comp, &dt); prop = icalcomponent_get_first_property (recur_icalcomp, ICAL_DTSTART_PROPERTY); tt = icalproperty_get_dtstart (prop); if (time_change) { /* FIXME handle the case where an instance has a different tz */ tt.hour = dt.value->hour; tt.minute = dt.value->minute; tt.second = dt.value->second; } prop = icalcomponent_get_first_property (clone, ICAL_DTSTART_PROPERTY); icalproperty_set_dtstart (prop, tt); e_cal_component_free_datetime (&dt); /* Preserve the end date (and possibly the time) */ e_cal_component_get_dtend (comp, &dt); prop = icalcomponent_get_first_property (recur_icalcomp, ICAL_DTEND_PROPERTY); tt = icalproperty_get_dtend (prop); if (time_change) { /* FIXME handle the case where an instance has a different tz */ tt.hour = dt.value->hour; tt.minute = dt.value->minute; tt.second = dt.value->second; } prop = icalcomponent_get_first_property (clone, ICAL_DTEND_PROPERTY); icalproperty_set_dtend (prop, tt); e_cal_component_free_datetime (&dt); icalcomponent_add_component (toplevel_import, clone); } } } icalcompiter_next (&iter); } error_code = sunone_connection_import (cal_backend_wcap_get_connection (wcap), cal_backend_wcap_get_calid (wcap), toplevel_import); if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code)) result = cal_backend_wcap_result_from_error (error_code); icalcomponent_free (toplevel_import); return result; } SunOneConnection * cal_backend_wcap_get_connection (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return priv->so_cnc; } const char * cal_backend_wcap_get_uri (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return (const char*) priv->uri; } const char * cal_backend_wcap_get_calid (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return (const char*) priv->calid; } const char * cal_backend_wcap_get_alarm_email (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return (const char*) priv->alarm_email_address; } const char * cal_backend_wcap_get_account_email (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return (const char*) priv->account_email_address; } SunOneCalendarProperties * cal_backend_wcap_get_props (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return priv->props; } SunOneCompType cal_backend_wcap_get_comp_type (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), 0); return priv->type; } const char * cal_backend_wcap_get_cache_name (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return (const char *) priv->cache_name; } icaltimezone * cal_backend_wcap_get_default_zone (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return priv->default_zone; } icaltimezone * cal_backend_wcap_get_server_default_zone (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return priv->server_default_zone; } GHashTable * cal_backend_wcap_get_objects (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return priv->objects; } GHashTable * cal_backend_wcap_get_instances (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return priv->instances; } icaltimezone * cal_backend_wcap_get_timezone_from_tzid (CalBackendWCAP *wcap, const char *tzid, gboolean local_only) { CalBackendWCAPPrivate *priv = wcap->priv; icaltimezone *izone = NULL; izone = g_hash_table_lookup (priv->timezones, tzid); if (!izone && !local_only) izone = icaltimezone_get_builtin_timezone_from_tzid (tzid); return izone; } SunOneModType cal_backend_wcap_to_sunone_mod (CalObjModType mod, ECalComponent *comp) { if (!e_cal_component_is_instance (comp) && !e_cal_component_has_recurrences (comp)) return THISINSTANCE; switch (mod) { case CALOBJ_MOD_THIS: return THISINSTANCE; case CALOBJ_MOD_THISANDPRIOR: return THISANDPRIOR; case CALOBJ_MOD_THISANDFUTURE: return THISANDFUTURE; case CALOBJ_MOD_ALL: return THISANDALL; } return THISINSTANCE; } gboolean cal_backend_wcap_is_online (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; return (priv->mode == CAL_MODE_REMOTE); } SunOneMethod cal_backend_wcap_guess_method (CalBackendWCAP *wcap, EDataCal *cal, ECalComponent *comp) { CalBackendWCAPPrivate *priv = wcap->priv; ECalComponentOrganizer organizer; GSList *attendees; char *address; int len; /* See if it already exists or not */ if (!e_cal_component_has_organizer (comp)) return PUBLISH; if (!e_cal_component_has_attendees (comp)) return PUBLISH; e_cal_component_get_attendee_list (comp, &attendees); len = g_slist_length (attendees); e_cal_component_free_attendee_list (attendees); /* If we are the organizer, request, if not, reply */ e_cal_component_get_organizer (comp, &organizer); if (!strncasecmp (organizer.value, "mailto:", 7)) organizer.value += 7; cal_backend_wcap_get_cal_address (E_CAL_BACKEND_SYNC (wcap), cal, &address); if (organizer.value && !strcmp (organizer.value, priv->calid)) return REQUEST; return REPLY; } const char * cal_backend_wcap_get_rid_string (ECalComponent *comp) { ECalComponentRange range; struct icaltimetype tt; e_cal_component_get_recurid (comp, &range); if (!range.datetime.value) return "0"; tt = *range.datetime.value; e_cal_component_free_range (&range); return icaltime_is_valid_time (tt) && !icaltime_is_null_time (tt) ? (const char *)icaltime_as_ical_string (tt) : "0"; } gint cal_backend_wcap_instance_list_compare (gconstpointer a, gconstpointer b) { return strcmp (cal_backend_wcap_get_rid_string (E_CAL_COMPONENT (a)), cal_backend_wcap_get_rid_string (E_CAL_COMPONENT (b))); } static void process_component_from_server (CalBackendWCAP *wcap, ECalComponent *comp) { CalBackendWCAPPrivate *priv = wcap->priv; icalproperty *prop; icalcomponent *icalcomp; ECalComponentDateTime dt_start, dt_end; ECalComponentOrganizer organizer; GSList *attendee_list, *l; icalcomp = e_cal_component_get_icalcomponent (comp); /* Remove the request status else things will be seen as different after a poll */ prop = icalcomponent_get_first_property (icalcomp, ICAL_REQUESTSTATUS_PROPERTY); if (prop) icalcomponent_remove_property (icalcomp, prop); /* Put everything in the right timezone */ if (priv->default_zone) { ECalComponentDateTime dt; icaltimezone *utc_zone; icaltimezone *local_zone; local_zone = icaltimezone_get_builtin_timezone (icaltimezone_get_location (priv->default_zone)); utc_zone = icaltimezone_get_utc_timezone (); e_cal_component_get_dtstart (comp, &dt); if (dt.value && !icaltime_is_null_time (*dt.value)) { icaltimezone_convert_time (dt.value, utc_zone, priv->default_zone); g_free ((char *)dt.tzid); if (local_zone) { dt.tzid = g_strdup (icaltimezone_get_tzid (local_zone)); } else { dt.tzid = g_strdup (icaltimezone_get_tzid (priv->default_zone)); } e_cal_component_set_dtstart (comp, &dt); } e_cal_component_free_datetime (&dt); e_cal_component_get_dtend (comp, &dt); if (dt.value && !icaltime_is_null_time (*dt.value)) { icaltimezone_convert_time (dt.value, utc_zone, priv->default_zone); g_free ((char *)dt.tzid); if (local_zone) { dt.tzid = g_strdup (icaltimezone_get_tzid (local_zone)); } else { dt.tzid = g_strdup (icaltimezone_get_tzid (priv->default_zone)); } e_cal_component_set_dtend (comp, &dt); } e_cal_component_free_datetime (&dt); e_cal_component_get_due (comp, &dt); if (dt.value && !icaltime_is_null_time (*dt.value)) { icaltimezone_convert_time (dt.value, utc_zone, priv->default_zone); g_free ((char *)dt.tzid); if (local_zone) dt.tzid = g_strdup (icaltimezone_get_tzid (local_zone)); else dt.tzid = g_strdup (icaltimezone_get_tzid (priv->default_zone)); e_cal_component_set_due (comp, &dt); } e_cal_component_free_datetime (&dt); } #if 0 /* force exdate to be a date */ if (e_cal_component_has_exdates (comp)) { GSList *exdate_list, *l; e_cal_component_get_exdate_list (comp, &exdate_list); for (l = exdate_list; l != NULL; l = l->next) { ECalComponentDateTime *ex; ex = l->data; /* convert from utc zone to local zone */ if (priv->default_zone) { icaltimezone_convert_time (ex->value, icaltimezone_get_utc_timezone(), priv->default_zone); ex->value->is_utc = FALSE; } if (!ex->value->is_date) { ex->value->is_date = TRUE; ex->value->hour = 0; ex->value->minute = 0; ex->value->second = 0; } } e_cal_component_set_exdate_list (comp, exdate_list); e_cal_component_free_recur_list (exdate_list); } #endif /* Handle All Day Events */ e_cal_component_get_dtstart (comp, &dt_start); e_cal_component_get_dtend (comp, &dt_end); if (dt_start.value && dt_start.value->is_date) { g_free ((char*)dt_start.tzid); dt_start.tzid = g_strdup (icaltimezone_get_tzid (priv->default_zone)); e_cal_component_set_dtstart (comp, &dt_start); if (!dt_end.value) { dt_start.value->day = dt_start.value->day + 1; e_cal_component_set_dtend (comp, &dt_start); } else if (dt_end.value && dt_end.value->is_date) { g_free ((char*)dt_end.tzid); dt_end.tzid = g_strdup (icaltimezone_get_tzid (priv->default_zone)); e_cal_component_set_dtend (comp, &dt_end); } } e_cal_component_free_datetime (&dt_start); e_cal_component_free_datetime (&dt_end); /*if a meeting, set organizer to account's email address*/ e_cal_component_get_organizer (comp, &organizer); if (organizer.value) { if (!strcasecmp (organizer.value, priv->calid)) { char *email = g_strconcat ("MAILTO:", priv->account_email_address, NULL); organizer.value = email; e_cal_component_set_organizer (comp, &organizer); g_free (email); } } /*if a meeting and account is an attendee, set attendee to account's email address*/ e_cal_component_get_attendee_list (comp, &attendee_list); for (l = attendee_list; l != NULL; l = l->next) { ECalComponentAttendee *a = l->data; if (!strcasecmp (a->value, priv->calid)) break; } /*yes, we have to set attendee to account's email address*/ if (l != NULL) { GSList *new_al = NULL; for (l = attendee_list; l != NULL; l = l->next) { ECalComponentAttendee *a = l->data; ECalComponentAttendee *ca = g_new0 (ECalComponentAttendee, 1); if (!strcasecmp (a->value, priv->calid) && (priv->account_email_address != NULL)) { char *email = g_strconcat ("MAILTO:", priv->account_email_address, NULL); ca->value = email; } else { if (a->value != NULL) ca->value = g_strdup (a->value); } if (a->cn != NULL) ca->cn = g_strdup (a->cn); if (a->member != NULL) ca->member = g_strdup (a->member); if (a->delto != NULL) ca->delto = g_strdup (a->delto); if (a->delfrom != NULL) ca->delfrom = g_strdup (a->delfrom); if (a->sentby != NULL) ca->sentby = g_strdup (a->sentby); if (a->language != NULL) ca->language = g_strdup (a->language); ca->cutype = a->cutype; ca->role = a->role; ca->status = a-> status; ca->rsvp = a->rsvp; new_al = g_slist_append (new_al, ca); } e_cal_component_set_attendee_list (comp, new_al); e_cal_component_free_attendee_list (new_al); } e_cal_component_free_attendee_list (attendee_list); /*if there is no attendee, clear organizer*/ if(!e_cal_component_has_attendees (comp)) { e_cal_component_set_organizer (comp, NULL); } /* Alarms */ if (e_cal_component_has_alarms (comp)) { ECalComponentDateTime dtstart; icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); e_cal_component_get_dtstart (comp, &dtstart); if (dtstart.value) { GList *auids, *l; dtstart.value->is_date = 0; icaltimezone_convert_time (dtstart.value, cal_backend_wcap_get_default_zone (wcap), utc_zone); auids = e_cal_component_get_alarm_uids (comp); for (l = auids; l != NULL; l = l->next) { const char *auid = l->data; ECalComponentAlarm *alarm; ECalComponentAlarmTrigger trigger; alarm = e_cal_component_get_alarm (comp, auid); e_cal_component_alarm_get_trigger (alarm, &trigger); if (trigger.type == E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE) { struct icaldurationtype duration = icaltime_subtract (trigger.u.abs_time, *dtstart.value); trigger.u.rel_duration = duration; trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START; e_cal_component_alarm_set_trigger (alarm, trigger); } e_cal_component_alarm_free (alarm); } cal_obj_uid_list_free (auids); } e_cal_component_free_datetime (&dtstart); } e_cal_component_abort_sequence (comp); } void cal_backend_wcap_add_component (CalBackendWCAP *wcap, icalcomponent *icalcomp, gboolean process, char **old_object, char **new_object) { CalBackendWCAPPrivate *priv = wcap->priv; icalcomponent_kind kind; ECalComponent *comp; const char *uid; gpointer orig_key, orig_value; GList *ilist = NULL; icalcomponent *clone; kind = icalcomponent_isa (icalcomp); if (kind != ICAL_VEVENT_COMPONENT && kind != ICAL_VTODO_COMPONENT) return; comp = e_cal_component_new (); clone = icalcomponent_new_clone (icalcomp); if (!e_cal_component_set_icalcomponent (comp, clone)) { icalcomponent_free (clone); g_object_unref (G_OBJECT (comp)); g_warning ("Could not associate icalcomponent to ECalComponent"); return; } /* Track the instances by original uid, in order of existence */ e_cal_component_get_uid (comp, &uid); if (g_hash_table_lookup_extended (priv->instances, uid, &orig_key, (gpointer *)&ilist)) { GList *l; l = g_list_find_custom (ilist, comp, cal_backend_wcap_instance_list_compare); if (l) { if (old_object && !*old_object) *old_object = e_cal_component_get_as_string (l->data); ilist = g_list_remove_link (ilist, l); } ilist = g_list_insert_sorted (ilist, comp, cal_backend_wcap_instance_list_compare); g_hash_table_remove (priv->instances, uid); g_free (orig_key); g_hash_table_insert (priv->instances, g_strdup (uid), ilist); } else { ilist = g_list_prepend (ilist, comp); g_hash_table_insert (priv->instances, g_strdup (uid), ilist); } sunone_util_mangle_uid (comp); e_cal_component_get_uid (comp, &uid); /* Track the mangled uid components */ if (g_hash_table_lookup_extended (priv->objects, uid, &orig_key, &orig_value)) { ECalComponentId *id; char *calobj = e_cal_component_get_as_string (orig_value); id = e_cal_component_get_id (comp); e_cal_backend_notify_object_removed (E_CAL_BACKEND(wcap), id, calobj, NULL); e_cal_component_free_id (id); g_free (calobj); g_hash_table_remove (priv->objects, uid); g_free (orig_key); g_object_unref (G_OBJECT (orig_value)); } /* We do this so we don't double process the cache items, * which causes bad things */ if (process) process_component_from_server (wcap, comp); g_hash_table_insert (priv->objects, g_strdup (uid), comp); char *calobj = e_cal_component_get_as_string (comp); if(calobj) { if (new_object && !*new_object) *new_object = g_strdup (calobj); e_cal_backend_notify_object_created (E_CAL_BACKEND (wcap), calobj); } g_free (calobj); } static void add_toplevel_component (CalBackendWCAP *wcap, icalcomponent *toplevel, gboolean process, gboolean notify) { icalcomponent *subcomp; icalcomponent_kind kind; kind = icalcomponent_isa (toplevel); if (kind == ICAL_VCALENDAR_COMPONENT) { subcomp = icalcomponent_get_first_component (toplevel, ICAL_ANY_COMPONENT); while (subcomp) { kind = icalcomponent_isa (subcomp); if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT) { cal_backend_wcap_add_component (wcap, subcomp, process, NULL, NULL); } else if (kind == ICAL_VTIMEZONE_COMPONENT) { add_timezone (wcap, subcomp); } subcomp = icalcomponent_get_next_component (toplevel, ICAL_ANY_COMPONENT); } } else if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT) { cal_backend_wcap_add_component (wcap, toplevel, process, NULL, NULL); } else if (kind == ICAL_VTIMEZONE_COMPONENT) { add_timezone (wcap, toplevel); } } void cal_backend_wcap_remove_component (CalBackendWCAP *wcap, const char *uid, CalObjModType mod, char **old_object) { CalBackendWCAPPrivate *priv = wcap->priv; ECalComponent *comp; gpointer orig_key, orig_value; GList *start = NULL, *ilist, *l; const char *unmangled_uid, *mangled_uid; g_return_if_fail (IS_CAL_BACKEND_WCAP (wcap)); g_return_if_fail (uid != NULL); if ((comp = g_hash_table_lookup (priv->objects, uid)) == NULL) return; /* Determine the list of instances to remove */ sunone_util_unmangle_uid (comp); e_cal_component_get_uid (comp, &unmangled_uid); if (g_hash_table_lookup_extended (priv->instances, unmangled_uid, &orig_key, (gpointer *)&ilist)) { switch (mod) { case CALOBJ_MOD_THIS: start = g_list_find_custom (ilist, comp, cal_backend_wcap_instance_list_compare); if (start) { ilist = g_list_remove_link (ilist, start); g_hash_table_remove (priv->instances, unmangled_uid); g_free (orig_key); if (ilist) g_hash_table_insert (priv->instances, g_strdup (unmangled_uid), ilist); } break; case CALOBJ_MOD_ALL: start = ilist; g_hash_table_remove (priv->instances, unmangled_uid); g_free (orig_key); break; default: return; } } sunone_util_mangle_uid (comp); /* Remove the list of instances */ for (l = start; l != NULL; l = l->next) { ECalComponent *remove_comp = l->data; e_cal_component_get_uid (remove_comp, &mangled_uid); if (g_hash_table_lookup_extended (priv->objects, mangled_uid, &orig_key, &orig_value)) { ECalComponentId *id; char *calobj = e_cal_component_get_as_string (remove_comp); if (old_object && !*old_object) *old_object = g_strdup (calobj); g_hash_table_remove (priv->objects, mangled_uid); id = e_cal_component_get_id (remove_comp); e_cal_backend_notify_object_removed (E_CAL_BACKEND(wcap), id, calobj, NULL); e_cal_component_free_id (id); g_free (calobj); g_free (orig_key); g_object_unref (G_OBJECT (orig_value)); } } g_list_free (start); return; } static void remove_icalcomp_list (CalBackendWCAP *wcap, icalcomponent *icalcomp) { ECalComponent *comp; icalcomponent *subcomp; comp = e_cal_component_new (); subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT); while (subcomp) { /* We ignore anything except VEVENT and VTODO components. */ icalcomponent_kind child_kind = icalcomponent_isa (subcomp); if (child_kind == ICAL_VEVENT_COMPONENT || child_kind == ICAL_VTODO_COMPONENT) { const char *mangled_uid; e_cal_component_set_icalcomponent (comp, subcomp); sunone_util_mangle_uid (comp); e_cal_component_get_uid (comp, &mangled_uid); cal_backend_wcap_remove_component (wcap, mangled_uid, CALOBJ_MOD_THIS, NULL); e_cal_component_set_icalcomponent (comp, NULL); } subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT); } g_object_unref (G_OBJECT (comp)); } typedef struct { GPtrArray *uids; GPtrArray *rids; } WCAPVerifyData; static void collect_rids_cb (gpointer key, gpointer value, gpointer data) { WCAPVerifyData *vd = data; char *uid = key; GList *ilist = value, *l; int len, i; len = g_list_length (ilist); g_ptr_array_set_size (vd->uids, vd->uids->len + len); g_ptr_array_set_size (vd->rids, vd->rids->len + len); for (i = vd->uids->len - len, l = ilist; i < vd->uids->len; i++, l = l->next) { ECalComponent *comp = l->data; g_ptr_array_index (vd->uids, i) = g_strdup (uid); g_ptr_array_index (vd->rids, i) = g_strdup (cal_backend_wcap_get_rid_string (comp)); } } static void free_rids_collection (GPtrArray *uids, GPtrArray *rids) { int i; for (i = 0; i < uids->len; i++) { g_free (g_ptr_array_index (uids, i)); g_free (g_ptr_array_index (rids, i)); } g_ptr_array_free (uids, TRUE); g_ptr_array_free (rids, TRUE); } static void verify_exists (CalBackendWCAP *wcap, GPtrArray *uids, GPtrArray *rids, gboolean tasks) { CalBackendWCAPPrivate *priv = wcap->priv; icalcomponent *icalcomp; guint error_code; GPtrArray *tmp_uids, *tmp_rids; gint i, ids_len, index = 0; const guint ids_max = 500; g_assert (uids->len == rids->len); if (uids->len <= 0) return; do { tmp_uids = g_ptr_array_sized_new (ids_max); tmp_rids = g_ptr_array_sized_new (ids_max); if ((uids->len - index) > ids_max) ids_len = ids_max; else ids_len = uids->len - index; for (i=0; i < ids_len; i++) { g_ptr_array_add (tmp_uids,g_ptr_array_index(uids,index+i)); g_ptr_array_add (tmp_rids,g_ptr_array_index(rids,index+i)); } g_ptr_array_add (tmp_uids, NULL); g_ptr_array_add (tmp_rids, NULL); if (!tasks) error_code = sunone_connection_verifyevents_by_ids (priv->so_cnc, (const char *) priv->calid, (const char **) tmp_uids->pdata, (const char **) tmp_rids->pdata, &icalcomp); else error_code = sunone_connection_verifytodos_by_ids (priv->so_cnc, (const char *) priv->calid, (const char **) tmp_uids->pdata, (const char **) tmp_rids->pdata, &icalcomp); if (SUNONE_ERROR_IS_SUCCESSFUL (error_code)) { remove_icalcomp_list (wcap, icalcomp); icalcomponent_free (icalcomp); } g_ptr_array_free (tmp_uids, TRUE); g_ptr_array_free (tmp_rids, TRUE); index += ids_max; } while (uids->len > index); } void cal_backend_wcap_verify_exists_uid (CalBackendWCAP *wcap, const char *uid) { CalBackendWCAPPrivate *priv = wcap->priv; WCAPVerifyData vd; GList *ilist; gboolean tasks = FALSE; ilist = g_hash_table_lookup (priv->instances, uid); if (priv->type == TYPE_TODO) tasks = TRUE; if (!ilist) return; vd.uids = g_ptr_array_new (); vd.rids = g_ptr_array_new (); collect_rids_cb ((char *)uid, ilist, &vd); verify_exists (wcap, vd.uids, vd.rids, tasks); free_rids_collection (vd.uids, vd.rids); } static void verify_components (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; /* Only verify if we are online and found an appropriate timestamp */ if (priv->so_cnc && priv->timestamp != 0) { WCAPVerifyData vd; /* Look for deleted components */ if (IS_WCAP_2_0(wcap)) { vd.uids = g_ptr_array_new (); vd.rids = g_ptr_array_new (); g_hash_table_foreach (priv->instances, collect_rids_cb, &vd); if (priv->type == TYPE_EVENT) { verify_exists (wcap, vd.uids, vd.rids, FALSE); } else if (priv->type == TYPE_TODO) { verify_exists (wcap, vd.uids, vd.rids, TRUE); } free_rids_collection (vd.uids, vd.rids); } else { guint error_code; icalcomponent *deletedComp; icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); /* for wcap 3.0, we can use fetch_deletedcomponents */ error_code = sunone_connection_fetch_deletedcomponents (priv->so_cnc, priv->calid, icaltime_from_timet_with_zone (priv->timestamp, FALSE, utc_zone), icaltime_null_time (), priv->type, &deletedComp); if (SUNONE_ERROR_IS_SUCCESSFUL (error_code)) { remove_icalcomp_list (wcap, deletedComp); icalcomponent_free (deletedComp); } } } } static void build_calendar (gpointer key, gpointer value, gpointer data) { GList *ilist = value, *l; icalcomponent *icalcomp = data; for (l = ilist; l; l = l->next) { ECalComponent *comp = l->data, *clone; clone = e_cal_component_clone (comp); sunone_util_unmangle_uid (clone); icalcomponent_add_component (icalcomp, icalcomponent_new_clone (e_cal_component_get_icalcomponent (clone))); g_object_unref (G_OBJECT (clone)); } } static char * get_cache_name (CalBackendWCAP *wcap, gboolean create) { CalBackendWCAPPrivate *priv = wcap->priv; SunOneAccount *account; gchar *dirname = NULL, *name, *fixed; g_assert (evolution_dir); account = sunone_component_get_account_from_uri (global_sunone_component, priv->uri); if (!account) return NULL; if (priv->type == TYPE_EVENT) dirname = g_strdup_printf ("%s/sunone/%s@%s/Calendar", evolution_dir, sunone_account_get_user (account), sunone_account_get_server (account)); else if (priv->type == TYPE_TODO) dirname = g_strdup_printf ("%s/sunone/%s@%s/Tasks", evolution_dir, sunone_account_get_user (account), sunone_account_get_server (account)); g_object_unref (G_OBJECT (account)); if (!dirname) return NULL; if (create && g_mkdir_with_parents (dirname, S_IRWXU) != 0) { g_free (dirname); return NULL; } fixed = sunone_util_fix_calid (priv->calid); name = g_strdup_printf ("%s/%s.cache", dirname, fixed); g_free (fixed); g_free (dirname); return name; } static void read_cache (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; icalcomponent *icalcomp; icalproperty *prop; if (!priv->cache_name) return; icalcomp = e_cal_util_parse_ics_file (priv->cache_name); if (!icalcomp) { return; } prop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); while (prop) { const char *x_name, *x_val; x_name = icalproperty_get_x_name (prop); x_val = icalproperty_get_x (prop); if (!strcmp (x_name, "X-EVOLUTION-TIMESTAMP")) { priv->timestamp = strtoul (x_val, NULL, 0); break; } prop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); } add_toplevel_component (wcap, icalcomp, FALSE, FALSE); verify_components (wcap); icalcomponent_free (icalcomp); } static gboolean write_cache_when_idle (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; GnomeVFSURI *uri, *backup_uri; GnomeVFSHandle *handle = NULL; GnomeVFSResult result = GNOME_VFS_ERROR_BAD_FILE; GnomeVFSFileSize out; icalcomponent *icalcomp; icalproperty *prop; gchar *tmp, *backup_uristr; char *buf; if (!priv->cache_name) return FALSE; g_mutex_lock (priv->idle_save_mutex); if (!priv->is_dirty) { priv->dirty_idle_id = 0; g_mutex_unlock (priv->idle_save_mutex); return FALSE; } uri = gnome_vfs_uri_new (priv->cache_name); if (!uri) goto error_malformed_uri; /* save to backup file */ tmp = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE); if (!tmp) { gnome_vfs_uri_unref (uri); goto error_malformed_uri; } backup_uristr = g_strconcat (tmp, "~", NULL); backup_uri = gnome_vfs_uri_new (backup_uristr); g_free (tmp); g_free (backup_uristr); if (!backup_uri) { gnome_vfs_uri_unref (uri); goto error_malformed_uri; } result = gnome_vfs_create_uri (&handle, backup_uri, GNOME_VFS_OPEN_WRITE, FALSE, 0666); if (result != GNOME_VFS_OK) { gnome_vfs_uri_unref (uri); gnome_vfs_uri_unref (backup_uri); goto error; } icalcomp = e_cal_util_new_top_level (); buf = g_strdup_printf ("%ld", priv->timestamp); prop = icalproperty_new_x (buf); g_free (buf); icalproperty_set_x_name (prop, "X-EVOLUTION-TIMESTAMP"); icalcomponent_add_property (icalcomp, prop); g_hash_table_foreach (priv->instances, build_calendar, icalcomp); buf = icalcomponent_as_ical_string (icalcomp); result = gnome_vfs_write (handle, buf, strlen (buf) * sizeof (char), &out); icalcomponent_free (icalcomp); gnome_vfs_close (handle); if (result != GNOME_VFS_OK) { gnome_vfs_uri_unref (uri); gnome_vfs_uri_unref (backup_uri); goto error; } /* now copy the temporary file to the real file */ result = gnome_vfs_move_uri (backup_uri, uri, TRUE); gnome_vfs_uri_unref (uri); gnome_vfs_uri_unref (backup_uri); if (result != GNOME_VFS_OK) goto error; priv->is_dirty = FALSE; priv->dirty_idle_id = 0; g_mutex_unlock (priv->idle_save_mutex); return FALSE; error_malformed_uri: g_mutex_unlock (priv->idle_save_mutex); e_cal_backend_notify_error (E_CAL_BACKEND (wcap), _("Can't save calendar data: Malformed URI.")); return TRUE; error: g_mutex_unlock (priv->idle_save_mutex); e_cal_backend_notify_error (E_CAL_BACKEND (wcap), gnome_vfs_result_to_string (result)); return TRUE; } void cal_backend_wcap_write_cache (CalBackendWCAP *wcap) { CalBackendWCAPPrivate *priv = wcap->priv; g_mutex_lock (priv->idle_save_mutex); priv->is_dirty = TRUE; if (!priv->dirty_idle_id) priv->dirty_idle_id = g_idle_add ((GSourceFunc) write_cache_when_idle, wcap); g_mutex_unlock (priv->idle_save_mutex); } gboolean cal_backend_wcap_poll_cb (gpointer data) { CalBackendWCAP *wcap = data; CalBackendWCAPPrivate *priv = wcap->priv; SunOneCalendarProperties *props; WCAPVerifyData vd; icaltimezone *utc_zone; icalcomponent *icalcomp; guint error_code; if (!priv->so_cnc) return FALSE; utc_zone = icaltimezone_get_utc_timezone (); /* Get the latest properties */ props = sunone_connection_get_calprops (priv->so_cnc, priv->calid, FALSE); if (props) { sunone_connection_free_calprops (priv->props); priv->props = props; } /* Look for deleted components */ if (IS_WCAP_2_0(wcap)) { vd.uids = g_ptr_array_new (); vd.rids = g_ptr_array_new (); g_hash_table_foreach (priv->instances, collect_rids_cb, &vd); if (priv->type == TYPE_EVENT) { verify_exists (wcap, vd.uids, vd.rids, FALSE); } else if (priv->type == TYPE_TODO) { verify_exists (wcap, vd.uids, vd.rids, TRUE); } free_rids_collection (vd.uids, vd.rids); } else { /* for wcap 3.0, we can use fetch_deletedcomponents */ error_code = sunone_connection_fetch_deletedcomponents (priv->so_cnc, priv->calid, icaltime_from_timet_with_zone (priv->timestamp, FALSE, utc_zone), icaltime_null_time (), priv->type, &icalcomp); if (SUNONE_ERROR_IS_SUCCESSFUL (error_code)) { remove_icalcomp_list (wcap, icalcomp); icalcomponent_free (icalcomp); } } /* Find new components */ error_code = sunone_connection_fetchcomponents_by_lastmod (priv->so_cnc, priv->calid, icaltime_from_timet_with_zone (priv->timestamp, FALSE, utc_zone), icaltime_null_time (), priv->type, REQUEST_ALL, &icalcomp, &priv->timestamp); if (SUNONE_ERROR_IS_SUCCESSFUL (error_code)) { add_toplevel_component (wcap, icalcomp, TRUE, TRUE); icalcomponent_free (icalcomp); } /* Write out the cache */ cal_backend_wcap_write_cache (wcap); return TRUE; } static void destroy_timezone_hash (gpointer key, gpointer value, gpointer user_data) { icaltimezone_free (value, TRUE); } static void destroy_object_hash (gpointer key, gpointer value, gpointer user_data) { g_free (key); g_object_unref (G_OBJECT (value)); } static void destroy_instance_hash (gpointer key, gpointer value, gpointer user_data) { g_free (key); g_list_free (value); } static void cal_backend_wcap_dispose (GObject *object) { CalBackendWCAP *wcap = CAL_BACKEND_WCAP (object); CalBackendWCAPPrivate *priv = wcap->priv; /*save if necessary*/ if (priv->is_dirty) write_cache_when_idle (wcap); if (G_OBJECT_CLASS (parent_class)->dispose) (* G_OBJECT_CLASS (parent_class)->dispose) (object); } static void cal_backend_wcap_clean_wcap (CalBackendWCAP *wcap, gboolean free_uri) { CalBackendWCAPPrivate *priv = wcap->priv; if (priv) { if (priv->timeout_id != -1) { g_source_remove (priv->timeout_id); priv->timeout_id = -1; } if (priv->uri && free_uri) { g_free (priv->uri); priv->uri = NULL; } if (priv->calid) { g_free (priv->calid); priv->calid = NULL; } if (priv->alarm_email_address) { g_free (priv->alarm_email_address); priv->alarm_email_address = NULL; } if (priv->account_email_address) { g_free (priv->account_email_address); priv->account_email_address = NULL; } if (priv->so_cnc && IS_SUNONE_CONNECTION (priv->so_cnc)) { g_object_unref (G_OBJECT (priv->so_cnc)); priv->so_cnc = NULL; } if (priv->props) { sunone_connection_free_calprops (priv->props); priv->props = NULL; } if (priv->cache_name) { g_free (priv->cache_name); priv->cache_name = NULL; } } } static void in_offline (CalBackendWCAP *wcap) { cal_backend_wcap_clean_wcap (wcap, FALSE); } static void cal_backend_wcap_finalize (GObject *object) { CalBackendWCAP *wcap = (CalBackendWCAP *) object; CalBackendWCAPPrivate *priv = wcap->priv; g_return_if_fail (IS_CAL_BACKEND_WCAP (wcap)); cal_backend_wcap_clean_wcap (wcap, TRUE); if (priv) { if (priv->dirty_idle_id) { g_source_remove (priv->dirty_idle_id); priv->dirty_idle_id = 0; } if (priv->idle_save_mutex) { g_mutex_free (priv->idle_save_mutex); priv->idle_save_mutex = NULL; } if (priv->timezones) { g_hash_table_foreach (priv->timezones, (GHFunc) destroy_timezone_hash, NULL); g_hash_table_destroy (priv->timezones); priv->timezones = NULL; } if (priv->default_zone) { icaltimezone_free (priv->default_zone, 1); priv->default_zone = NULL; } if (priv->objects) { g_hash_table_foreach (priv->objects, (GHFunc) destroy_object_hash, NULL); g_hash_table_destroy (priv->objects); priv->objects = NULL; } if (priv->instances) { g_hash_table_foreach (priv->instances, (GHFunc) destroy_instance_hash, NULL); g_hash_table_destroy (priv->instances); priv->instances = NULL; } if (priv->set_mode_lock) { g_mutex_free (priv->set_mode_lock); priv->set_mode_lock = NULL; } if (priv->open_lock) { g_mutex_free (priv->open_lock); priv->open_lock = NULL; } g_free (priv); wcap->priv = NULL; } if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize) (object); } static ECalBackendSyncStatus cal_backend_wcap_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; CalBackendWCAPPrivate *priv = wcap->priv; gboolean hasWrite, hasDelete; g_return_val_if_fail (read_only != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); d(printf("cal_backend_wcap_is_read_only(%p, %d)\n", backend, *read_only)); if (priv->mode == CAL_MODE_LOCAL) { *read_only = TRUE; return GNOME_Evolution_Calendar_Success; } if (!priv->so_cnc || !priv->props) { return GNOME_Evolution_Calendar_OtherError; } hasWrite = sunone_util_has_permissions (priv->props, sunone_connection_get_user (priv->so_cnc), SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, SUNONE_ACE_PERMISSION_WRITE); hasDelete = sunone_util_has_permissions (priv->props, sunone_connection_get_user (priv->so_cnc), SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, SUNONE_ACE_PERMISSION_DELETE); *read_only = ! (hasWrite || hasDelete); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (address != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); *address = g_strdup (priv->account_email_address); d(printf("cal_backend_wcap_get_cal_address(%p, %s)\n", backend, *address)); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; CalBackendWCAPPrivate *priv = wcap->priv; SunOneConnectionPreferences *prefs; g_return_val_if_fail (address != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); d(printf("cal_backend_wcap_get_alarm_email_address(%p, %s)\n", backend, *address)); if (!priv->alarm_email_address && priv->so_cnc) { prefs = sunone_connection_get_preferences (priv->so_cnc); if (prefs->alarm_mail) priv->alarm_email_address = g_strdup (prefs->alarm_mail); else priv->alarm_email_address = g_strdup (prefs->mail); sunone_connection_free_preferences (prefs); } *address = g_strdup (priv->alarm_email_address); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; g_return_val_if_fail (attribute != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); *attribute = g_strdup ("icscalendar"); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (capabilities != NULL, GNOME_Evolution_Calendar_InvalidObject); if (priv->so_cnc && IS_WCAP_2_0(wcap)) *capabilities = g_strdup ( CAL_STATIC_CAPABILITY_NO_TRANSPARENCY "," \ CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY "," \ CAL_STATIC_CAPABILITY_NO_ALARM_REPEAT "," \ CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT "," \ CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ATTEND "," \ CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS "," \ CAL_STATIC_CAPABILITY_NO_THISANDPRIOR "," \ CAL_STATIC_CAPABILITY_NO_THISANDFUTURE "," \ CAL_STATIC_CAPABILITY_SAVE_SCHEDULES); else *capabilities = g_strdup ( CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY "," \ CAL_STATIC_CAPABILITY_NO_ALARM_REPEAT "," \ CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT "," \ CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS "," \ CAL_STATIC_CAPABILITY_NO_THISANDPRIOR "," \ CAL_STATIC_CAPABILITY_NO_THISANDFUTURE "," \ CAL_STATIC_CAPABILITY_SAVE_SCHEDULES); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus go_online_with_thread (CalBackendWCAP *wcap, SunOneAccount *account) { CalBackendWCAPPrivate *priv = wcap->priv; icalcomponent *icalcomp; SunOneConnection *cnc; guint error_code; SunOneConnectionPreferences *prefs; char *key; const char *uristr; gboolean readonly; g_mutex_lock (priv->open_lock); uristr = e_cal_backend_get_uri (E_CAL_BACKEND (wcap)); cnc = sunone_account_get_connection (account); if (!cnc) { g_mutex_unlock (priv->open_lock); e_cal_backend_notify_readonly (E_CAL_BACKEND(wcap), TRUE); priv->open_error = GNOME_Evolution_Calendar_OtherError; return priv->open_error; } if (priv->calid) g_free (priv->calid); priv->calid = sunone_util_get_calid_from_uri (uristr); if (!priv->calid) { g_mutex_unlock (priv->open_lock); e_cal_backend_notify_readonly (E_CAL_BACKEND(wcap), TRUE); priv->open_error = GNOME_Evolution_Calendar_OtherError; return priv->open_error; } if (priv->uri) g_free (priv->uri); priv->uri = g_strdup (uristr); if (priv->cache_name) g_free (priv->cache_name); priv->cache_name = get_cache_name (wcap, TRUE); priv->so_cnc = g_object_ref (cnc); /* load timezones */ error_code = sunone_connection_get_all_timezones (priv->so_cnc, &icalcomp); if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code)) { cal_backend_wcap_clean_wcap (wcap, TRUE); g_mutex_unlock (priv->open_lock); e_cal_backend_notify_readonly (E_CAL_BACKEND(wcap), TRUE); priv->open_error = GNOME_Evolution_Calendar_OtherError; return priv->open_error; } add_toplevel_component (wcap, icalcomp, TRUE, FALSE); icalcomponent_free (icalcomp); /* load the properties */ if (priv->props) sunone_connection_free_calprops (priv->props); priv->props = sunone_connection_get_calprops (priv->so_cnc, priv->calid, FALSE); if (!priv->props) { cal_backend_wcap_clean_wcap (wcap, TRUE); g_mutex_unlock (priv->open_lock); e_cal_backend_notify_readonly (E_CAL_BACKEND(wcap), TRUE); priv->open_error = GNOME_Evolution_Calendar_OtherError; return priv->open_error; } /* Get the appropriate timezone for this calendar */ prefs = sunone_connection_get_preferences (priv->so_cnc); if (!prefs) { cal_backend_wcap_clean_wcap (wcap, TRUE); g_mutex_unlock (priv->open_lock); e_cal_backend_notify_readonly (E_CAL_BACKEND(wcap), TRUE); priv->open_error = GNOME_Evolution_Calendar_OtherError; return priv->open_error; } if (prefs->single_calendar_tzid && priv->props->timezone) { priv->server_default_zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (wcap), priv->props->timezone); } if (!priv->server_default_zone && prefs->timezone) { priv->server_default_zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (wcap), prefs->timezone); } /* Failsafe for a server side timezone */ if (!priv->server_default_zone) priv->server_default_zone = icaltimezone_get_utc_timezone (); sunone_connection_free_preferences (prefs); priv->account_email_address = g_strdup (sunone_account_get_email (account)); /* verify objects */ verify_components (wcap); error_code = sunone_connection_fetchcomponents_by_lastmod (priv->so_cnc, priv->calid, priv->timestamp ? icaltime_from_timet_with_zone (priv->timestamp, FALSE, icaltimezone_get_utc_timezone ()): icaltime_null_time (), icaltime_null_time (), priv->type, REQUEST_ALL, &icalcomp, &priv->timestamp); if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code)) { cal_backend_wcap_clean_wcap (wcap, TRUE); g_mutex_unlock (priv->open_lock); e_cal_backend_notify_readonly (E_CAL_BACKEND(wcap), TRUE); if (error_code == SUNONE_ERROR_ACCESS_DENIED) { priv->open_error = GNOME_Evolution_Calendar_PermissionDenied; return priv->open_error; } else if (error_code == SUNONE_ERROR_LOGIN_OK_DEFAULT_CALENDAR_NOT_FOUND) { priv->open_error = GNOME_Evolution_Calendar_NoSuchCal; return priv->open_error; } else { priv->open_error = GNOME_Evolution_Calendar_OtherError; return priv->open_error; } } add_toplevel_component (wcap, icalcomp, TRUE, FALSE); icalcomponent_free (icalcomp); if (priv->type == TYPE_EVENT) key = g_strconcat (priv->calid, ":calendar", NULL); else if (priv->type == TYPE_TODO) key = g_strconcat (priv->calid, ":tasks", NULL); else key = g_strconcat (priv->calid, ":all", NULL); sunone_connection_add_wcap (priv->so_cnc, key, (gpointer)wcap); g_free (key); cal_backend_wcap_is_read_only (E_CAL_BACKEND_SYNC(wcap), NULL, &readonly); e_cal_backend_notify_readonly (E_CAL_BACKEND(wcap), readonly); priv->timeout_id = g_timeout_add (sunone_account_get_poll_interval (account) * 60 * 1000, cal_backend_wcap_poll_cb, wcap); cal_backend_wcap_write_cache (wcap); g_mutex_unlock (priv->open_lock); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus go_online (CalBackendWCAP *wcap) { SunOneAccount *account; ECalBackendSyncStatus ret; const char *uristr; uristr = e_cal_backend_get_uri (E_CAL_BACKEND (wcap)); account = sunone_component_get_account_from_uri (global_sunone_component, uristr); if (!account) { e_cal_backend_notify_readonly (E_CAL_BACKEND(wcap), TRUE); wcap->priv->open_error = GNOME_Evolution_Calendar_OtherError; return wcap->priv->open_error; } sunone_account_set_connecting (account, TRUE); ret = go_online_with_thread (wcap, account); sunone_account_set_connecting (account, FALSE); g_object_unref (account); return ret; } static ECalBackendSyncStatus cal_backend_wcap_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists, const char *username, const char *password) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; CalBackendWCAPPrivate *priv = wcap->priv; icalcomponent_kind kind; GThread *thread = NULL; GError *error = NULL; const char *uristr; d(printf("cal_backend_wcap_open(%p, %d)\n", backend, only_if_exists)); /* check whether already open */ if (priv->uri) return GNOME_Evolution_Calendar_Success; kind = e_cal_backend_get_kind (E_CAL_BACKEND(wcap)); if (kind == ICAL_VTODO_COMPONENT) priv->type = TYPE_TODO; else if (kind == ICAL_VEVENT_COMPONENT) priv->type = TYPE_EVENT; uristr = e_cal_backend_get_uri (E_CAL_BACKEND (wcap)); priv->calid = sunone_util_get_calid_from_uri (uristr); if (!priv->calid) return GNOME_Evolution_Calendar_NoSuchCal; priv->uri = g_strdup (uristr); priv->cache_name = get_cache_name (wcap, TRUE); if (priv->mode == CAL_MODE_LOCAL) { ESource *source; const char *display_contents = NULL; source = e_cal_backend_get_source (E_CAL_BACKEND (backend)); display_contents = e_source_get_property (source, "offline_sync"); if (!display_contents || !g_str_equal (display_contents, "1")) { cal_backend_wcap_clean_wcap (wcap, TRUE); return GNOME_Evolution_Calendar_RepositoryOffline; } read_cache (wcap); return GNOME_Evolution_Calendar_Success; } read_cache (wcap); thread = g_thread_create ((GThreadFunc)go_online, wcap, FALSE, &error); if (!thread) { g_warning (G_STRLOC ": %s", error->message); g_error_free (error); return GNOME_Evolution_Calendar_OtherError; } return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_remove (ECalBackendSync *backend, EDataCal *cal) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; #if 0 CalBackendWCAPPrivate *priv = wcap->priv; SunOneAccount *account; #endif g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); d(printf("cal_backend_wcap_remove(%p)\n", backend)); return GNOME_Evolution_Calendar_PermissionDenied; #if 0 g_return_val_if_fail (priv->uri != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (priv->calid != NULL, GNOME_Evolution_Calendar_InvalidObject); account = sunone_component_get_account_from_uri (global_sunone_component, priv->uri); remove_source (account, priv->calid, FALSE); sunone_folder_tree_sync (); g_object_unref (G_OBJECT (account)); if (unlink (priv->cache_name)) { g_object_unref (wcap); return GNOME_Evolution_Calendar_ObjectNotFound; } g_object_unref (wcap); return GNOME_Evolution_Calendar_Success; #endif } static ECalBackendSyncStatus cal_backend_wcap_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid) { return GNOME_Evolution_Calendar_OtherError; } static ECalBackendSyncStatus cal_backend_wcap_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj) { return GNOME_Evolution_Calendar_OtherError; } static ECalBackendSyncStatus cal_backend_wcap_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj, GList **users, char **modified_calobj) { icalcomponent *top_level, *icalcomp; ECalComponent *comp; icalproperty_method method; CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend); g_return_val_if_fail (users != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (modified_calobj != NULL, GNOME_Evolution_Calendar_InvalidObject); d(printf("cal_backend_wcap_send_objects(%p, %s, %s)\n", backend, calobj, *modified_calobj)); if (!cal_backend_wcap_is_online (wcap)) return GNOME_Evolution_Calendar_RepositoryOffline; *users = NULL; *modified_calobj = NULL; top_level = icalparser_parse_string (calobj); icalcomp = icalcomponent_get_inner (top_level); if (!icalcomp) return GNOME_Evolution_Calendar_InvalidObject; method = icalcomponent_get_method (icalcomp); if ((icalcomponent_isa (icalcomp) != ICAL_VEVENT_COMPONENT && icalcomponent_isa (icalcomp) != ICAL_VTODO_COMPONENT) || (method != ICAL_METHOD_REQUEST && method != ICAL_METHOD_CANCEL)) { *modified_calobj = g_strdup (calobj); goto cleanup; } comp = e_cal_component_new(); if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) { GSList *attendee_list = NULL, *tmp; e_cal_component_get_attendee_list (comp, &attendee_list); for (tmp = attendee_list; tmp; tmp = tmp->next) *users = g_list_prepend (*users, tmp); } g_object_unref (comp); *modified_calobj = g_strdup (icalcomponent_as_ical_string (top_level)); cleanup: icalcomponent_free (top_level); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object) { ECalComponent *comp; g_return_val_if_fail (object != NULL, GNOME_Evolution_Calendar_InvalidObject); comp = e_cal_component_new (); switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) { case ICAL_VEVENT_COMPONENT: e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT); break; case ICAL_VTODO_COMPONENT: e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO); break; default: g_object_unref (comp); return GNOME_Evolution_Calendar_ObjectNotFound; } *object = e_cal_component_get_as_string (comp); g_object_unref (comp); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object) { CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend); CalBackendWCAPPrivate *priv = wcap->priv; ECalComponent *comp; g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (object != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); d(printf("cal_backend_wcap_get_object(%p, %s)\n", backend, uid)); comp = g_hash_table_lookup (priv->objects, uid); if (!comp) return GNOME_Evolution_Calendar_ObjectNotFound; *object = e_cal_component_get_as_string (comp); return GNOME_Evolution_Calendar_Success; } struct search_data { gboolean search_needed; ECalBackendSExp *sexp; const char *query; GList *matches; ECalBackend *backend; }; static ECalBackendSyncStatus cal_backend_wcap_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects) { CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend); CalBackendWCAPPrivate *priv = wcap->priv; struct search_data match_data; const char *uristr; g_return_val_if_fail (sexp != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (objects != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); match_data.search_needed = TRUE; match_data.query = sexp; match_data.matches = NULL; match_data.backend = E_CAL_BACKEND (backend); if (!strcmp (sexp, "#t")) match_data.search_needed = FALSE; match_data.sexp = e_cal_backend_sexp_new (sexp); if (!match_data.sexp) return GNOME_Evolution_Calendar_InvalidQuery; uristr = e_cal_backend_get_uri (E_CAL_BACKEND (backend)); g_hash_table_foreach (priv->objects, match_object, &match_data); g_object_unref (match_data.sexp); *objects = match_data.matches; return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus add_timezone (CalBackendWCAP *wcap, icalcomponent *icalcomp) { CalBackendWCAPPrivate *priv = wcap->priv; icaltimezone *zone; icalcomponent *clone, *vcomp; icalproperty *prop; char *tzid; zone = icaltimezone_new (); clone = icalcomponent_new_clone (icalcomp); /* libical does not deal well with start dates before 1970 */ vcomp = icalcomponent_get_first_component (clone, ICAL_ANY_COMPONENT); while (vcomp) { struct icaltimetype tt; prop = icalcomponent_get_first_property (vcomp, ICAL_DTSTART_PROPERTY); if (prop) { tt = icalproperty_get_dtstart (prop); if (tt.year < 1970) { tt.year = 1970; /* Hopefully no one is this evil */ if (tt.month == 2 && tt.day == 29) tt.day = 28; icalproperty_set_dtstart (prop, tt); } } vcomp = icalcomponent_get_next_component (vcomp, ICAL_ANY_COMPONENT); } /* We set the location so the display in the gui is nice */ prop = icalcomponent_get_first_property (clone, ICAL_TZID_PROPERTY); prop = icalproperty_new_x (icalproperty_get_tzid (prop)); icalproperty_set_x_name (prop, "X-LIC-LOCATION"); icalcomponent_add_property (clone, prop); if (!icaltimezone_set_component (zone, clone)) { icalcomponent_free (clone); icaltimezone_free (zone, TRUE); return GNOME_Evolution_Calendar_ObjectNotFound; } tzid = icaltimezone_get_tzid (zone); if (g_hash_table_lookup (priv->timezones, tzid)) { icaltimezone_free (zone, TRUE); return GNOME_Evolution_Calendar_ObjectIdAlreadyExists; } g_hash_table_insert (priv->timezones, tzid, zone); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj) { return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_set_default_zone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; CalBackendWCAPPrivate *priv; icalcomponent *tz_comp; icaltimezone *zone; g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); d(printf("cal_backend_wcap_set_default_zone(%p, %s)\n", backend, tzobj)); printf("cal_backend_wcap_set_default_zone(%p, %s)\n", backend, tzobj); priv = wcap->priv; tz_comp = icalparser_parse_string (tzobj); if (!tz_comp) return GNOME_Evolution_Calendar_InvalidObject; zone = icaltimezone_new (); icaltimezone_set_component (zone, tz_comp); if (priv->default_zone != NULL) icaltimezone_free (priv->default_zone, 1); priv->default_zone = zone; if (priv->default_zone) return GNOME_Evolution_Calendar_Success; return GNOME_Evolution_Calendar_InvalidObject; } static ECalBackendSyncStatus cal_backend_wcap_get_freebusy (ECalBackendSync *backend, EDataCal *cal, GList *users, time_t start, time_t end, GList **freebusy) { return GNOME_Evolution_Calendar_OtherError; } struct ChangeData { EXmlHash *ehash; GList *adds; GList *modifies; }; static void check_change_type (gpointer key, gpointer value, gpointer data) { ECalComponent *comp = value; struct ChangeData *change_data = data; char *calobj; char *uid = key; g_assert (comp != NULL); calobj = e_cal_component_get_as_string (comp); switch (e_xmlhash_compare (change_data->ehash, uid, calobj)) { case E_XMLHASH_STATUS_SAME: break; case E_XMLHASH_STATUS_NOT_FOUND: change_data->adds = g_list_prepend (change_data->adds, g_strdup (calobj)); e_xmlhash_add (change_data->ehash, uid, calobj); break; case E_XMLHASH_STATUS_DIFFERENT: change_data->modifies = g_list_prepend (change_data->modifies, g_strdup (calobj)); e_xmlhash_add (change_data->ehash, uid, calobj); break; } g_free (calobj); } struct cbw_data { CalBackendWCAP *wcap; icalcomponent_kind kind; GList *deletes; EXmlHash *ehash; }; static void e_cal_backend_wcap_compute_changes_foreach_key (const char *key, gpointer value, gpointer data) { struct cbw_data *cbwdata = data; ECalComponent *comp = NULL; g_return_if_fail (cbwdata != NULL); comp = g_hash_table_lookup (cbwdata->wcap->priv->objects, key); if (comp == NULL) { comp = e_cal_component_new (); if (cbwdata->kind == ICAL_VTODO_COMPONENT) e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO); else if (cbwdata->kind == ICAL_VEVENT_COMPONENT) e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT); else { g_object_unref (comp); return; } e_cal_component_set_uid (comp, key); cbwdata->deletes = g_list_prepend (cbwdata->deletes, e_cal_component_get_as_string (comp)); e_xmlhash_remove (cbwdata->ehash, key); g_object_unref (comp); } } static ECalBackendSyncStatus cal_backend_wcap_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id, GList **adds, GList **modifies, GList **deletes) { CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend); CalBackendWCAPPrivate *priv = wcap->priv; SunOneAccount *account; icalcomponent_kind kind; const char *uristr; char *dirname, *filename; EXmlHash *xmlhash; struct ChangeData data; struct cbw_data cbwdata; g_return_val_if_fail (adds != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (modifies != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (deletes != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_InvalidObject); d(printf("cal_backend_wcap_get_changes(%p, %s)\n", backend, change_id)); uristr = e_cal_backend_get_uri (E_CAL_BACKEND (backend)); kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend)); /* open the changes file */ account = sunone_component_get_account_from_uri (global_sunone_component, priv->uri); if (!account) return GNOME_Evolution_Calendar_ObjectNotFound; if (kind == ICAL_VEVENT_COMPONENT) dirname = g_strdup_printf ("%s/sunone/%s@%s/Calendar", evolution_dir, sunone_account_get_user (account), sunone_account_get_server (account)); else if (kind == ICAL_VTODO_COMPONENT) dirname = g_strdup_printf ("%s/sunone/%s@%s/Tasks", evolution_dir, sunone_account_get_user (account), sunone_account_get_server (account)); else { g_object_unref (account); return GNOME_Evolution_Calendar_ObjectNotFound; } g_object_unref (G_OBJECT (account)); if (g_mkdir_with_parents (dirname, S_IRWXU) != 0) { g_message ("cal_backend_wcap_get_changes: Cannot create directory %s", dirname); return GNOME_Evolution_Calendar_OtherError; } filename = g_strdup_printf ("%s/%s.changes", dirname, change_id); xmlhash = e_xmlhash_new (filename); g_free (filename); g_free (dirname); /* Make sure we are as up to date as possible */ cal_backend_wcap_poll_cb (wcap); /* calculate additions and modifications */ data.ehash = xmlhash; data.adds = NULL; data.modifies = NULL; if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT) g_hash_table_foreach (priv->objects, check_change_type, &data); else return GNOME_Evolution_Calendar_OtherError; *adds = data.adds; *modifies = data.modifies; xmlhash = data.ehash; /* calculate deletions */ cbwdata.wcap = wcap; cbwdata.kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend)); cbwdata.deletes = NULL; cbwdata.ehash = xmlhash; e_xmlhash_foreach_key (xmlhash, (EXmlHashFunc) e_cal_backend_wcap_compute_changes_foreach_key, &cbwdata); *deletes = cbwdata.deletes; e_xmlhash_write (xmlhash); e_xmlhash_destroy (xmlhash); return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus cal_backend_wcap_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid) { return GNOME_Evolution_Calendar_OtherError; } static ECalBackendSyncStatus cal_backend_wcap_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, CalObjModType mod, char **old_object, char **new_object) { return GNOME_Evolution_Calendar_OtherError; } static ECalBackendSyncStatus cal_backend_wcap_remove_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, CalObjModType mod, char **old_object, char **object) { return GNOME_Evolution_Calendar_OtherError; } static void match_object (gpointer key, gpointer value, gpointer data) { struct search_data *sd = data; ECalComponent *comp = value; if ((!sd->search_needed) || e_cal_backend_sexp_match_comp (sd->sexp, comp, sd->backend)) sd->matches = g_list_prepend (sd->matches, e_cal_component_get_as_string (comp)); } static void cal_backend_wcap_start_query (ECalBackend *backend, EDataCalView *view) { ECalBackendSyncStatus status; GList *objects = NULL; d(printf("cal_backend_wcap_start_query(%p)\n", backend)); status = cal_backend_wcap_get_object_list (E_CAL_BACKEND_SYNC (backend), NULL, e_data_cal_view_get_text (view), &objects); if (status != GNOME_Evolution_Calendar_Success) { e_data_cal_view_notify_done (view, status); return; } if (objects) { e_data_cal_view_notify_objects_added (view, (const GList *) objects); /* free memory */ g_list_foreach (objects, (GFunc) g_free, NULL); g_list_free (objects); } e_data_cal_view_notify_done (view, GNOME_Evolution_Calendar_Success); } CalMode cal_backend_wcap_get_mode (ECalBackend *backend) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; CalBackendWCAPPrivate *priv = wcap->priv; d(printf("cal_backend_wcap_get_mode(%p)\n", backend)); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), 0); return priv->mode; } static void cal_backend_wcap_set_mode (ECalBackend *backend, CalMode mode) { CalBackendWCAP *wcap = (CalBackendWCAP *) backend; CalBackendWCAPPrivate *priv = wcap->priv; ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success; SunOneAccount *account; d(printf("cal_backend_wcap_set_mode(%p, %d)\n", backend, mode)); g_return_if_fail (IS_CAL_BACKEND_WCAP (wcap)); if (priv->mode == mode) { e_cal_backend_notify_mode ( backend, GNOME_Evolution_Calendar_CalListener_MODE_SET, cal_mode_to_corba (mode)); return; } g_mutex_lock (priv->set_mode_lock); if (mode == CAL_MODE_REMOTE) { if (priv->uri) { account = sunone_component_get_account_from_uri (global_sunone_component, priv->uri); if (account) { sunone_account_set_online (account); status = go_online (wcap); g_object_unref (account); } } } else if (mode == CAL_MODE_LOCAL) { if (priv->so_cnc && sunone_connection_is_open (priv->so_cnc)) sunone_connection_logout (priv->so_cnc); in_offline (wcap); if (priv->uri) { account = sunone_component_get_account_from_uri (global_sunone_component, priv->uri); if (account) { sunone_account_set_offline (account); g_object_unref (account); } } } else { e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED, cal_mode_to_corba (mode)); g_mutex_unlock (priv->set_mode_lock); return; } if (status != GNOME_Evolution_Calendar_Success) { e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_NOT_SET, cal_mode_to_corba (mode)); g_mutex_unlock (priv->set_mode_lock); return; } priv->mode = mode; e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_SET, cal_mode_to_corba (mode)); g_mutex_unlock (priv->set_mode_lock); } static icaltimezone * cal_backend_wcap_internal_get_default_timezone (ECalBackend *backend) { CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend); CalBackendWCAPPrivate *priv = wcap->priv; g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return priv->default_zone; } static icaltimezone * cal_backend_wcap_internal_get_timezone (ECalBackend *backend, const char *tzid) { CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), NULL); return cal_backend_wcap_get_timezone_from_tzid (wcap, tzid, FALSE); } ECalBackendSyncStatus cal_backend_wcap_result_from_error (guint error_code) { switch (error_code) { case SUNONE_ERROR_ACCESS_DENIED : case SUNONE_ERROR_NOT_ALLOWED_TO_REQUEST_PUBLISH : case SUNONE_ERROR_MUSTBEOWNER_OPERATION : return GNOME_Evolution_Calendar_PermissionDenied; case SUNONE_ERROR_ILLEGAL_CALID_NAME : case SUNONE_ERROR_CALENDAR_DISABLED : case SUNONE_ERROR_CALID_NOT_SPECIFIED : return GNOME_Evolution_Calendar_ObjectNotFound; } return GNOME_Evolution_Calendar_InvalidObject; } ECalBackendSyncStatus cal_backend_wcap_get_open_error (CalBackendWCAP *wcap) { g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); return wcap->priv->open_error; } static ECalBackendSyncStatus cal_backend_wcap_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object) { CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend); icaltimezone *zone; icalcomponent *vtzcomp; g_return_val_if_fail (object != NULL, GNOME_Evolution_Calendar_InvalidObject); g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError); zone = cal_backend_wcap_get_timezone_from_tzid (wcap, tzid, FALSE); if (!zone) return GNOME_Evolution_Calendar_ObjectNotFound; vtzcomp = icaltimezone_get_component (zone); if (!vtzcomp) return GNOME_Evolution_Calendar_OtherError; *object = g_strdup (icalcomponent_as_ical_string (vtzcomp)); return GNOME_Evolution_Calendar_Success; } /* Class initialization function for the WCAP backend */ static void cal_backend_wcap_class_init (CalBackendWCAPClass *klass) { GObjectClass *object_class; ECalBackendClass *backend_class; ECalBackendSyncClass *sync_class; object_class = G_OBJECT_CLASS (klass); backend_class = E_CAL_BACKEND_CLASS (klass); sync_class = E_CAL_BACKEND_SYNC_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->dispose = cal_backend_wcap_dispose; object_class->finalize = cal_backend_wcap_finalize; sync_class->is_read_only_sync = cal_backend_wcap_is_read_only; sync_class->get_cal_address_sync = cal_backend_wcap_get_cal_address; sync_class->get_alarm_email_address_sync = cal_backend_wcap_get_alarm_email_address; sync_class->get_ldap_attribute_sync = cal_backend_wcap_get_ldap_attribute; sync_class->get_static_capabilities_sync = cal_backend_wcap_get_static_capabilities; sync_class->open_sync = cal_backend_wcap_open; sync_class->remove_sync = cal_backend_wcap_remove; sync_class->discard_alarm_sync = cal_backend_wcap_discard_alarm; sync_class->receive_objects_sync = cal_backend_wcap_receive_objects; sync_class->send_objects_sync = cal_backend_wcap_send_objects; sync_class->get_default_object_sync = cal_backend_wcap_get_default_object; sync_class->get_object_sync = cal_backend_wcap_get_object; sync_class->get_object_list_sync = cal_backend_wcap_get_object_list; sync_class->get_timezone_sync = cal_backend_wcap_get_timezone; sync_class->add_timezone_sync = cal_backend_wcap_add_timezone; sync_class->set_default_zone_sync = cal_backend_wcap_set_default_zone; sync_class->get_freebusy_sync = cal_backend_wcap_get_freebusy; sync_class->get_changes_sync = cal_backend_wcap_get_changes; sync_class->create_object_sync = cal_backend_wcap_create_object; sync_class->modify_object_sync = cal_backend_wcap_modify_object; sync_class->remove_object_sync = cal_backend_wcap_remove_object; backend_class->start_query = cal_backend_wcap_start_query; backend_class->get_mode = cal_backend_wcap_get_mode; backend_class->set_mode = cal_backend_wcap_set_mode; backend_class->internal_get_default_timezone = cal_backend_wcap_internal_get_default_timezone; backend_class->internal_get_timezone = cal_backend_wcap_internal_get_timezone; } static void cal_backend_wcap_init (CalBackendWCAP *object) { CalBackendWCAP *wcap = CAL_BACKEND_WCAP (object); CalBackendWCAPPrivate *priv; wcap->priv = g_new0 (CalBackendWCAPPrivate, 1); priv = wcap->priv; priv->type = TYPE_ALL; priv->mode = CAL_MODE_REMOTE; priv->open_error = GNOME_Evolution_Calendar_OtherError; priv->timeout_id = -1; priv->is_dirty = FALSE; priv->dirty_idle_id = 0; priv->idle_save_mutex = g_mutex_new (); priv->timezones = g_hash_table_new (g_str_hash, g_str_equal); priv->objects = g_hash_table_new (g_str_hash, g_str_equal); priv->instances = g_hash_table_new (g_str_hash, g_str_equal); priv->set_mode_lock = g_mutex_new (); priv->open_lock = g_mutex_new (); e_cal_backend_sync_set_lock (E_CAL_BACKEND_SYNC (wcap), TRUE); }