/* cal-backend-wcap.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 <gtk/gtk.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <gtk/gtkmain.h>
#include <glib/gi18n-lib.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libical/ical.h>
#include <libedataserver/e-xml-hash-utils.h>
#include <libedataserver/e-data-server-util.h>
#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)) {
					char *rid;

					rid = cal_backend_wcap_get_rid_string (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, rid,
								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, rid,
								cal_backend_wcap_to_sunone_mod (mod, existing));
					g_free (rid);

					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;
}

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 g_strdup ("0");
	tt = *range.datetime.value;
	e_cal_component_free_range (&range);

	if (icaltime_is_valid_time (tt) && !icaltime_is_null_time (tt))
		return icaltime_as_ical_string (tt);
	else
		return g_strdup ("0");
}

gint
cal_backend_wcap_instance_list_compare (gconstpointer a, gconstpointer b)
{
	gint ret;
	char *astr;
	char *bstr;

	astr = cal_backend_wcap_get_rid_string (E_CAL_COMPONENT (a));
	bstr = cal_backend_wcap_get_rid_string (E_CAL_COMPONENT (b));
	ret = strcmp (astr, bstr);
	g_free (astr);
	g_free (bstr);

	return ret;
}

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) = 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);
	g_free (buf);
	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) ||
					sunone_util_has_permissions (priv->props,
							sunone_connection_get_uid (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) ||
					sunone_util_has_permissions (priv->props,
						sunone_connection_get_uid (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 = 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 = 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);
}
