/* cal-backend-wcap-events.c
 *
 * Copyright (C) 2002-2004 Sun Microsystems, Inc
 *
 * AUTHORS
 *     Jack Jia <jack.jia@sun.com>
 *     Harry Lu <harry.lu@sun.com>
 *     Alfred Peng <alfred.peng@sun.com>
 *     Jedy Wang <jedy.wang@sun.com>
 *     Rodrigo Moya <rodrigo@ximian.com>
 *     JP Rosevear <jpr@ximian.com>
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <lib/sunone-util.h>
#include "cal-backend-wcap-events.h"

static CalBackendWCAPClass *parent_class = NULL;

#define d(x)

G_DEFINE_TYPE (CalBackendWCAPEvents, cal_backend_wcap_events,  CAL_BACKEND_WCAP_TYPE)

static ECalComponent *
find_master_event (CalBackendWCAP *wcap, ECalComponent *comp)
{
	const char *unmangled_uid;
	ECalComponent *master_comp = NULL;
	GList *ilist;

	sunone_util_unmangle_uid (comp);
	e_cal_component_get_uid (comp, &unmangled_uid);

	if ((ilist = g_hash_table_lookup (cal_backend_wcap_get_instances (wcap), unmangled_uid))) {
		master_comp = ilist->data;
	}

	sunone_util_mangle_uid (comp);
	return master_comp;
}

static ECalComponent *
find_old_event (CalBackendWCAP *wcap, ECalComponent *comp)
{
	const char *uid;
	ECalComponent *tmp_comp;

	e_cal_component_get_uid (comp, &uid);

	if ((tmp_comp = g_hash_table_lookup (cal_backend_wcap_get_objects (wcap), uid))) {
		return tmp_comp;
	} else {
		return NULL;
	}
}

static void
process_modified_recurrence (CalBackendWCAP *wcap, ECalComponent *comp, CalObjModType mod)
{
	ECalComponentDateTime dtstart, dtend;
	ECalComponentDateTime old_dtstart, old_dtend;
	ECalComponentDateTime master_dtstart, master_dtend;
	struct icaldurationtype dur;
	ECalComponent *old_comp;
	ECalComponent *master_comp;
	ECalComponentRange recur_id;
	gboolean allday = FALSE;

	/* all modified recurrences should exist? */
	old_comp = find_old_event (wcap, comp);
	if (!old_comp)
		return;
	/* all modified recurrences should have master? */
	master_comp = find_master_event (wcap, old_comp);
	if (!master_comp)
		return;
	
	if (CALOBJ_MOD_ALL != mod)
		return;

	e_cal_component_get_recurid (master_comp, &recur_id);
	if(!recur_id.datetime.value)
		return;
	e_cal_component_set_recurid (comp, &recur_id);
	e_cal_component_free_range (&recur_id);

	e_cal_component_get_dtstart (comp, &dtstart);
	e_cal_component_get_dtend (comp, &dtend);
	e_cal_component_get_dtstart (old_comp, &old_dtstart);
	e_cal_component_get_dtend (old_comp, &old_dtend);
	e_cal_component_get_dtstart (master_comp, &master_dtstart);
	e_cal_component_get_dtend (master_comp, &master_dtend);

	if (dtstart.value && dtstart.value->is_date) {
		if (dtend.value && dtend.value->is_date)
			allday = TRUE;

		if (!dtend.value) {
			allday = TRUE;
		}
	}

	if (!old_dtend.value->is_date)
		old_dtend.value->day = old_dtend.value->day + 1;
	old_dtstart.value->is_date = TRUE;
	old_dtend.value->is_date = TRUE;

	if (!master_dtend.value->is_date)
		master_dtend.value->day = master_dtend.value->day + 1;
	master_dtstart.value->is_date = TRUE;
	master_dtend.value->is_date = TRUE;

	/* same time zone as the master */
	if (master_dtstart.tzid) {
		g_free ((char *)dtstart.tzid);
		dtstart.tzid = g_strdup (master_dtstart.tzid);
	}

	dur = icaltime_subtract (*master_dtstart.value, *old_dtstart.value);
	dtstart.value->is_date = FALSE;
	*dtstart.value = icaltime_add (*dtstart.value, dur);
	dtstart.value->is_utc = FALSE;

	if (master_dtend.tzid) {
		g_free ((char *)dtend.tzid);
		dtend.tzid = g_strdup (master_dtend.tzid);
	}

	dur = icaltime_subtract (*master_dtend.value, *old_dtend.value);
	dtend.value->is_date = FALSE;
	*dtend.value = icaltime_add (*dtend.value, dur);
	dtend.value->is_utc = FALSE;

	if (allday) {
		dtstart.value->is_date = TRUE;
		dtend.value->is_date = TRUE;
	}

	e_cal_component_set_dtstart (comp, &dtstart);
	e_cal_component_set_dtend (comp, &dtend);

	e_cal_component_free_datetime (&master_dtstart);
	e_cal_component_free_datetime (&master_dtend);
	e_cal_component_free_datetime (&old_dtstart);
	e_cal_component_free_datetime (&old_dtend);
	e_cal_component_free_datetime (&dtstart);
	e_cal_component_free_datetime (&dtend);
}

static void
process_events_component_to_server (CalBackendWCAP *wcap, ECalComponent *comp, gboolean *relativeAlarm, gboolean *allday)
{
	ECalComponentDateTime dt,dtstart,dtend;
	ECalComponentOrganizer organizer;
	icalcomponent *icalcomp;
	icalproperty *prop;
	icaltimezone *zone = NULL, *old_zone = NULL;
	char *location = NULL;

	if (allday)
		*allday = FALSE;

	/* Check for all day event and  get starting timezone (if there is one) */
	e_cal_component_get_dtstart (comp, &dtstart);
	e_cal_component_get_dtend (comp, &dtend);
	if (allday && dtstart.value && dtstart.value->is_date) {
		if (dtend.value && dtend.value->is_date)
			*allday = TRUE;
		if (!dtend.value)
			*allday = TRUE;

		if (*allday) {
			icaltimezone_convert_time (dtstart.value,
					cal_backend_wcap_get_default_zone (wcap), cal_backend_wcap_get_server_default_zone (wcap));
			g_free ((char *)dtstart.tzid);
			dtstart.tzid = g_strdup (icaltimezone_get_tzid (cal_backend_wcap_get_server_default_zone (wcap)));
			dtstart.value->hour = 0;
			dtstart.value->minute = 0;
			dtstart.value->second = 0;
			dtstart.value->is_date = TRUE;
			dtstart.value->is_utc = FALSE;
			e_cal_component_set_dtstart (comp, &dtstart);

			icaltimezone_convert_time (dtend.value,
					cal_backend_wcap_get_default_zone (wcap), cal_backend_wcap_get_server_default_zone (wcap));
			g_free ((char *)dtend.tzid);
			dtend.tzid = g_strdup (icaltimezone_get_tzid (cal_backend_wcap_get_server_default_zone (wcap)));
			dtend.value->hour = 0;
			dtend.value->minute = 0;
			dtend.value->second = 0;
			dtend.value->is_date = TRUE;
			dtend.value->is_utc = FALSE;
			e_cal_component_set_dtend (comp, &dtend);

			zone = cal_backend_wcap_get_server_default_zone (wcap);
		}
	}

	/* Ok, Sunone can't handle other timezones, so, we make a
	 * guess as to the timezone intended, if we fail, we convert
	 * it to the default server side timezone for this calendar */
	if (dtstart.value && dtstart.tzid && allday && !(*allday)) {
		zone = cal_backend_wcap_get_timezone_from_tzid (wcap, dtstart.tzid, TRUE);
		if (!zone) {
			zone = icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid);
			if (zone) {
				icaltimezone *local_zone;
				
				location = icaltimezone_get_location (zone);
				local_zone = cal_backend_wcap_get_timezone_from_tzid (wcap, location, TRUE);
				if (!local_zone)
					local_zone = cal_backend_wcap_get_server_default_zone (wcap);
				icaltimezone_convert_time (dtstart.value, zone, local_zone);
				g_free ((char *)dtstart.tzid);
				dtstart.tzid = g_strdup (icaltimezone_get_tzid (local_zone));
				e_cal_component_set_dtstart (comp, &dtstart);
				zone = local_zone;
			}
		}
	}
	e_cal_component_free_datetime (&dtstart);
	e_cal_component_free_datetime (&dtend);

	if (zone)
		location = icaltimezone_get_tzid (zone);

	/* Convert other Date-Time values to the same zone as the start time */
	if (zone) {
		e_cal_component_get_dtend (comp, &dt);
		if (dt.value && dt.tzid) {
			old_zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (wcap), dt.tzid);
			icaltimezone_convert_time (dt.value, old_zone, zone);
			g_free ((char *)dt.tzid);
			dt.tzid = g_strdup (icaltimezone_get_tzid (zone));
			e_cal_component_set_dtend (comp, &dt);
		}
		e_cal_component_free_datetime (&dt);
	}
	
	if (zone) {
		e_cal_component_get_due (comp, &dt);
		if (dt.value && dt.tzid) {
			old_zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (wcap), dt.tzid);
			icaltimezone_convert_time (dt.value, old_zone, zone);
			g_free ((char *)dt.tzid);
			dt.tzid = g_strdup (icaltimezone_get_tzid (zone));
			e_cal_component_set_due (comp, &dt);
		}
		e_cal_component_free_datetime (&dt);
	}

	/* Fix exdates to be UTC and date/times */
	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;
			icaltimezone *from_zone, *to_zone;
			
			e_cal_component_get_dtstart (comp, &dt);
			ex = l->data;
			/* make exdates use the same timezone as dtstart */
			if (dt.tzid) {
				g_free ((char *)ex->tzid);
				ex->tzid = g_strdup (dt.tzid);
			}

			if (allday && *allday) {
				ex->value->is_date = TRUE;
				if (dt.tzid == NULL)
					from_zone = icaltimezone_get_utc_timezone ();
				else
					from_zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (wcap), dt.tzid);
				to_zone = icaltimezone_get_utc_timezone ();
				icaltimezone_convert_time (ex->value, from_zone, to_zone);

				ex->value->is_utc = TRUE;
				e_cal_component_free_datetime (&dt);
				continue;
			}
			
			ex->value->hour = dt.value->hour;
			ex->value->minute = dt.value->minute;
			ex->value->second = dt.value->second;
			ex->value->is_date = FALSE;
			
			if (dt.tzid == NULL)
				from_zone = icaltimezone_get_utc_timezone ();
			else
				from_zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (wcap), dt.tzid);
			to_zone = icaltimezone_get_utc_timezone ();
			icaltimezone_convert_time (ex->value, from_zone, to_zone);

			ex->value->is_utc = TRUE;
			e_cal_component_free_datetime (&dt);
		}	
		e_cal_component_set_exdate_list (comp, exdate_list);
		e_cal_component_free_recur_list (exdate_list);
	}	
	
	/* Alarms */
	if (e_cal_component_has_alarms (comp)) {
		GList *auids, *l;
		icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
		
		e_cal_component_get_dtstart (comp, &dtstart);
		e_cal_component_get_dtend (comp, &dtend);
		if (dtstart.value) {
			dtstart.value->is_date = 0;
			icaltimezone_convert_time (dtstart.value, zone ? zone : cal_backend_wcap_get_default_zone (wcap), utc_zone);
			
			if (dtend.value) {
				dtend.value->is_date = 0;
				icaltimezone_convert_time (dtend.value, zone ? zone : 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;
				ECalComponentAlarmAction action;
				struct icaltimetype t;
				int is_neg;
				
				alarm = e_cal_component_get_alarm (comp, auid);
				e_cal_component_alarm_get_trigger (alarm, &trigger);
				e_cal_component_alarm_get_action (alarm, &action);
				
				if (trigger.type == E_CAL_COMPONENT_ALARM_TRIGGER_NONE
				    || trigger.type == E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE) {
					e_cal_component_alarm_free (alarm);
					continue;
				}
				
				switch (trigger.type) {
				case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START:
					if (!IS_WCAP_2_0(wcap)) {
						e_cal_component_alarm_free (alarm);
						if (relativeAlarm)
							*relativeAlarm = TRUE;
						continue;
					}
					t = *dtstart.value;
					break;
				case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END:
					if (dtend.value)
						t = *dtend.value;
					else
						t = *dtstart.value;
					break;
				default : 
					t = *dtstart.value;
					break;
				}

				trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE;
				is_neg = trigger.u.rel_duration.is_neg;
				t = icaltime_add(t, trigger.u.rel_duration);
				trigger.u.abs_time = t;
				trigger.u.abs_time.is_utc = 1;

				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_free_datetime (&dtend);
	}

	/*if a meeting, set email address to calid*/
	e_cal_component_get_organizer (comp, &organizer);
	if (organizer.value) {
		if (!strncasecmp ("mailto:", organizer.value, 7)) {
			const char *temp = organizer.value + 7;

			if (!strcasecmp (temp, cal_backend_wcap_get_account_email (wcap))) {
				organizer.value = cal_backend_wcap_get_calid (wcap);
				e_cal_component_set_organizer (comp, &organizer);
			}
		}
	}

 	/*if there is no attendee, clear organizer*/
	if(!e_cal_component_has_attendees (comp)) {
		e_cal_component_set_organizer (comp, NULL);
	}

	e_cal_component_abort_sequence (comp);

	/* Set x properties */
	icalcomp = e_cal_component_get_icalcomponent (comp);
	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-NSCP-DTSTART-TZID") && location) {
			icalproperty_set_x (prop, location);
			location = NULL;
		}
		
		prop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
	}
	
	if (location) {
		prop = icalproperty_new_x (location);
		icalproperty_set_x_name (prop, "X-NSCP-DTSTART-TZID");
		icalcomponent_add_property (icalcomp, prop);
	}
	
	e_cal_component_abort_sequence (comp);
}

ECalBackendSyncStatus
cal_backend_wcap_events_update_objects (ECalBackendSync *backend, EDataCal *cal,
				 const char *calobj, CalObjModType mod, char **old_object, char **new_object)
{
	ECalBackendSyncStatus result = GNOME_Evolution_Calendar_Success;
	CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend);
	ECalComponent *comp;
	icalcomponent *toplevel_comp, *subcomp;
	icalcomponent_kind kind;
	gboolean hasImport = FALSE;
	gboolean relativeAlarm = FALSE;

	g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError);

	if (!cal_backend_wcap_is_online (wcap))
		return GNOME_Evolution_Calendar_RepositoryOffline;
	if (!cal_backend_wcap_get_connection (wcap))
		return cal_backend_wcap_get_open_error (wcap);

	toplevel_comp = icalparser_parse_string (calobj);
	if (!toplevel_comp)
		return GNOME_Evolution_Calendar_InvalidObject;
	
	kind = icalcomponent_isa (toplevel_comp);
	if (kind == ICAL_VEVENT_COMPONENT) {
		icalcomponent *icalcomp;
		icalproperty *prop;
	
		/* Create a temporary toplevel component and put the VEVENT
		   in it, to simplify the code below. */
		icalcomp = toplevel_comp;
		prop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
		while (prop) {
			const char *x_name;

			x_name = icalproperty_get_x_name (prop);

			if (!strcmp (x_name, "X-NSCP-GSE-COMPONENT-STATE")) {
				icalcomponent_remove_property (icalcomp, prop);
				break;			
			}

			prop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
		}
		toplevel_comp = e_cal_util_new_top_level ();
		icalcomponent_add_component (toplevel_comp, icalcomp);
	} else if (kind != ICAL_VCALENDAR_COMPONENT) {
		/* We don't support this type of component */
		icalcomponent_free (toplevel_comp);
		return GNOME_Evolution_Calendar_InvalidObject;
	}

	subcomp = icalcomponent_get_first_component (toplevel_comp, ICAL_ANY_COMPONENT);
	comp = e_cal_component_new ();
	while (subcomp) {
		/* We ignore anything except VEVENT and VJOURNAL
		   components. */
		icalcomponent_kind child_kind = icalcomponent_isa (subcomp);
		if (child_kind == ICAL_VEVENT_COMPONENT) {
			icalcomponent *return_icalcomp, *return_subcomp;
			const char *uid;
			gboolean allday, expand;
			guint error_code;			
			SunOneMethod method;
			
			e_cal_component_set_icalcomponent (comp, subcomp);

			/* recurrences need special process */
			process_modified_recurrence (wcap, comp, mod);

			/* Fix up the component for the server */
			process_events_component_to_server (wcap, comp, &relativeAlarm, &allday);

			if (cal_backend_wcap_need_import (wcap, comp)) {
				result = cal_backend_wcap_handle_import (wcap, comp, mod, toplevel_comp, subcomp, child_kind);
				subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_ANY_COMPONENT);
				hasImport = TRUE;
				continue;
			}

			if (mod == CALOBJ_MOD_ALL)
				expand = TRUE;
			else
				expand = FALSE;

			e_cal_component_get_uid (comp, &uid);
			
			/* Unmangle the uid so its the right uid */
			if (e_cal_component_is_instance (comp))
				sunone_util_unmangle_uid (comp);
			
			/* Get the uid for verification */
			e_cal_component_get_uid (comp, &uid);
			
			/* Guess the method */
			method = cal_backend_wcap_guess_method (wcap, cal, comp);
			error_code = sunone_connection_storeevents (cal_backend_wcap_get_connection (wcap),
								    cal_backend_wcap_get_calid (wcap), subcomp,
								    method, cal_backend_wcap_to_sunone_mod (mod, comp), 
								    expand, allday, &return_icalcomp,
									cal_backend_wcap_get_account_email (wcap));
			cal_backend_wcap_verify_exists_uid (wcap, uid);

			e_cal_component_set_icalcomponent (comp, NULL);

			if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code)) {
				result = cal_backend_wcap_result_from_error (error_code);
				subcomp = icalcomponent_get_next_component (toplevel_comp,
									    ICAL_ANY_COMPONENT);
				continue;
			}

			return_subcomp = icalcomponent_get_first_component (return_icalcomp,
									    child_kind);
			while (return_subcomp) {
				cal_backend_wcap_add_component (wcap, return_subcomp, TRUE, old_object, new_object);
				return_subcomp = icalcomponent_get_next_component (return_icalcomp,
										   child_kind);
			}

			icalcomponent_free (return_icalcomp);
			
		}
		subcomp = icalcomponent_get_next_component (toplevel_comp,
							    ICAL_ANY_COMPONENT);
	}

	g_object_unref (G_OBJECT (comp));
	icalcomponent_free (toplevel_comp);

	if (hasImport || relativeAlarm)
		cal_backend_wcap_poll_cb (wcap);
	else
		cal_backend_wcap_write_cache (wcap);

	return result;
}

static ECalBackendSyncStatus
cal_backend_wcap_events_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid)
{
	ECalBackendSyncStatus result = GNOME_Evolution_Calendar_Success;
	CalObjModType mod = CALOBJ_MOD_ALL;

	g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);

	d(printf("cal_backend_wcap_events_create_object(%p, %s)\n", backend, *calobj));

	result = cal_backend_wcap_events_update_objects (backend, cal, *calobj, mod, NULL, NULL);
/*
	*calobj = NULL;
*/

	return result;
}

static ECalBackendSyncStatus
cal_backend_wcap_events_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj,
			CalObjModType mod, char **old_object, char **new_object)
{
	ECalBackendSyncStatus result = GNOME_Evolution_Calendar_Success;

	g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);

	d(printf("cal_backend_wcap_events_modify_object(%p, %s)\n", backend, calobj));

	result = cal_backend_wcap_events_update_objects (backend, cal, calobj, mod, old_object, new_object);

	return result;
}

static ECalBackendSyncStatus
cal_backend_wcap_events_remove_object (ECalBackendSync *backend, EDataCal *cal, const char *uid,
			const char *rid, CalObjModType mod, char **old_object, char **object)
{
	CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend);
	ECalComponent *comp;
	icalcomponent *icalcomp;
	ECalBackendSyncStatus status;
	const char *unmangled_uid;
	guint error_code;
	char *calobj;
	char *ridstr;
	
	d(printf("cal_backend_wcap_evnets_remove_object(%p, %s, %s)\n", backend, uid, rid));

	if (!cal_backend_wcap_is_online (wcap))
		return GNOME_Evolution_Calendar_RepositoryOffline;
	if (!cal_backend_wcap_get_connection (wcap))
		return cal_backend_wcap_get_open_error (wcap);

	status = E_CAL_BACKEND_SYNC_CLASS (parent_class)->get_object_sync (backend, cal, uid, rid, &calobj);
	if (status != GNOME_Evolution_Calendar_Success)
		return status;

	icalcomp = icalcomponent_new_from_string (calobj);
	if (!icalcomp) {
		g_free (calobj);
		return GNOME_Evolution_Calendar_InvalidObject;
	}

	comp = e_cal_component_new ();
	e_cal_component_set_icalcomponent (comp, icalcomp);

	sunone_util_unmangle_uid (comp);
	e_cal_component_get_uid (comp, &unmangled_uid);
	ridstr = cal_backend_wcap_get_rid_string (comp);
	
	error_code = sunone_connection_deleteevents_by_id (
			cal_backend_wcap_get_connection (wcap),
			cal_backend_wcap_get_calid (wcap),
			unmangled_uid, ridstr,
			cal_backend_wcap_to_sunone_mod (mod, comp));
	g_free (ridstr);

	g_object_unref (G_OBJECT (comp));

	if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code)) {
		g_free (calobj);
		return cal_backend_wcap_result_from_error (error_code);
	}

	cal_backend_wcap_remove_component (wcap, uid, mod, old_object);
	g_free (calobj);

	cal_backend_wcap_write_cache (wcap);

	return GNOME_Evolution_Calendar_Success;
}

static void
get_fb_info (SunOneConnection *so_cnc, const char *user, const char *calid, time_t start, time_t end, GList **freebusy)
{
	icalcomponent *icalcomp, *fb;
	icaltimezone *utc_zone;
	guint error_code;
	const char *user_calid;

	if (!so_cnc)
		return; 

	utc_zone = icaltimezone_get_utc_timezone ();

	if (strchr (user, '@')) {
		/*if user is account's email, change it to calid*/
		CalBackendWCAP *wcap = NULL;
		char *key = g_strconcat (calid, ":calendar", NULL);
		const char *user_mail;

		if (!strncasecmp (user, "mailto:", 7)) 
				user_mail  = user + 7;
		else
				user_mail = user;

		wcap = sunone_connection_get_wcap (so_cnc, key);
		g_free (key);

		if(wcap && !strcasecmp (cal_backend_wcap_get_account_email (wcap), user_mail))
			user_calid = cal_backend_wcap_get_calid (wcap);
		else
			user_calid = user_mail;
	} else
		user_calid = user;

	error_code = sunone_connection_get_freebusy (so_cnc, user_calid,
				icaltime_from_timet_with_zone (start, FALSE, utc_zone),
				icaltime_from_timet_with_zone (end, FALSE, utc_zone),
				&icalcomp);

	if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code))
		return;

	fb = icalcomponent_get_first_component (icalcomp, ICAL_VFREEBUSY_COMPONENT);
	if (fb) {
		icalproperty *prop;

		prop = icalproperty_new_organizer (user);
		if (prop != NULL)
			icalcomponent_add_property (fb, prop);

		*freebusy = g_list_append (*freebusy,
				icalcomponent_as_ical_string (fb));
	}

	icalcomponent_free (icalcomp);
}

static ECalBackendSyncStatus
cal_backend_wcap_events_get_freebusy (ECalBackendSync *backend, EDataCal *cal, GList *users,
			time_t start, time_t end, GList **freebusy)
{
	GList *l;
	CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend);

	g_return_val_if_fail (freebusy != 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_freebusy(%p)\n", backend));

	if (!cal_backend_wcap_is_online (wcap))
		return GNOME_Evolution_Calendar_RepositoryOffline;
	if (!cal_backend_wcap_get_connection (wcap))
		return cal_backend_wcap_get_open_error (wcap);

	if (!users) {
		get_fb_info (cal_backend_wcap_get_connection (wcap), cal_backend_wcap_get_calid (wcap), cal_backend_wcap_get_calid (wcap), start, end, freebusy);
	} else {
		for (l = users; l != NULL; l = l->next)
			get_fb_info (cal_backend_wcap_get_connection (wcap), l->data, cal_backend_wcap_get_calid (wcap), start, end, freebusy);
	}

	return GNOME_Evolution_Calendar_Success;
}

static ECalBackendSyncStatus
cal_backend_wcap_events_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
{
	ECalBackendSyncStatus result = GNOME_Evolution_Calendar_Success;
	CalBackendWCAP *wcap = CAL_BACKEND_WCAP (backend);
	icalcomponent *toplevel_comp;
	icalcomponent_kind kind;
	guint error_code;

	g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);

	d(printf("cal_backend_wcap_receive_objects(%p, %p, %s)\n", backend, cal, calobj));

	g_return_val_if_fail (IS_CAL_BACKEND_WCAP (wcap), GNOME_Evolution_Calendar_OtherError);

	if (!cal_backend_wcap_is_online (wcap))
		return GNOME_Evolution_Calendar_RepositoryOffline;
	if (!cal_backend_wcap_get_connection (wcap))
		return cal_backend_wcap_get_open_error (wcap);

	toplevel_comp = icalparser_parse_string (calobj);
	if (!toplevel_comp)
		return GNOME_Evolution_Calendar_InvalidObject;

	kind = icalcomponent_isa (toplevel_comp);
	if (kind == ICAL_VEVENT_COMPONENT) {
		icalcomponent *icalcomp;

		/* Create a temporary toplevel component and put the VEVENT
			in it, to simplify the code below. */
		icalcomp = toplevel_comp;
		toplevel_comp = e_cal_util_new_top_level ();
		icalcomponent_add_component (toplevel_comp, icalcomp);
	} else if (kind != ICAL_VCALENDAR_COMPONENT){
		/* We don't support this type of component */
		icalcomponent_free (toplevel_comp);
		return GNOME_Evolution_Calendar_InvalidObject;
	}

	error_code = sunone_connection_import (cal_backend_wcap_get_connection (wcap), cal_backend_wcap_get_calid (wcap), toplevel_comp);
	if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code))
		result = cal_backend_wcap_result_from_error (error_code);
	cal_backend_wcap_poll_cb (wcap);

	icalcomponent_free (toplevel_comp);

	return result;
}

static void
e_cal_backend_wcap_events_dispose(GObject *object)
{
	if (G_OBJECT_CLASS (parent_class)->dispose)
		(*G_OBJECT_CLASS (parent_class)->dispose) (object);
}

static void
e_cal_backend_wcap_events_finalize(GObject *object)
{
	if (G_OBJECT_CLASS (parent_class)->finalize)
		(*G_OBJECT_CLASS (parent_class)->finalize) (object);
}

static void
cal_backend_wcap_events_class_init (CalBackendWCAPEventsClass *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);

	sync_class->create_object_sync = cal_backend_wcap_events_create_object;
	sync_class->modify_object_sync = cal_backend_wcap_events_modify_object;
	sync_class->remove_object_sync = cal_backend_wcap_events_remove_object;
	sync_class->get_freebusy_sync = cal_backend_wcap_events_get_freebusy;
	sync_class->receive_objects_sync = cal_backend_wcap_events_receive_objects;

	object_class->dispose = e_cal_backend_wcap_events_dispose;
	object_class->finalize = e_cal_backend_wcap_events_finalize;
}

static void
cal_backend_wcap_events_init (CalBackendWCAPEvents *klass)
{
}
