Index: calendar/ChangeLog
===================================================================
--- calendar/ChangeLog	(revision 9483)
+++ calendar/ChangeLog	(working copy)
@@ -1,3 +1,24 @@
+2008-09-08  Milan Crha  <mcrha@redhat.com>
+
+	** Fix for bug #537718
+
+	* backends/caldav/e-cal-backend-caldav.c: (status_code_to_result),
+	(xp_object_get_href), (soup_authenticate),
+	(send_and_handle_redirection), (caldav_server_open_calendar),
+	(caldav_server_list_objects), (caldav_server_get_object),
+	(caldav_server_put_object), (caldav_server_delete_object),
+	(synchronize_cache), (initialize_backend), (sanitize_component),
+	(caldav_create_object), (caldav_modify_object),
+	(caldav_remove_object), (process_object), (caldav_add_timezone),
+	(caldav_set_default_zone), (caldav_internal_get_default_timezone),
+	(e_cal_backend_caldav_finalize), (e_cal_backend_caldav_init):
+	Be able to cooperate with servers which uses redirections.
+	Basic support for default timezone information on the backend.
+	Remove instance of the object only when 'rid' has been provided.
+	Set created/last modified times on the component.
+	Do not panic when ETag not returned in the headers, but is known.
+	Leak a bit less.
+
 2008-09-04  Chenthill Palanisamy  <pchenthill@novell.com>
 
 	* libedata-cal/Makefile.am:
Index: calendar/backends/caldav/e-cal-backend-caldav.c
===================================================================
--- calendar/backends/caldav/e-cal-backend-caldav.c	(revision 9483)
+++ calendar/backends/caldav/e-cal-backend-caldav.c	(working copy)
@@ -46,6 +46,8 @@
 
 #include "e-cal-backend-caldav.h"
 
+#define d(x) x
+
 /* in seconds */
 #define DEFAULT_REFRESH_TIME 60
 
@@ -111,6 +113,8 @@ struct _ECalBackendCalDAVPrivate {
 
 	/* object cleanup */
 	gboolean disposed;
+
+	icaltimezone *default_zone;
 };
 
 /* ************************************************************************* */
@@ -123,7 +127,6 @@ struct _ECalBackendCalDAVPrivate {
 static gboolean caldav_debug_all = FALSE;
 static GHashTable *caldav_debug_table = NULL;
 
-
 static void
 add_debug_key (const char *start, const char *end)
 {
@@ -240,6 +243,10 @@ caldav_debug_setup (SoupSession *session
 
 static ECalBackendSyncClass *parent_class = NULL;
 
+static icaltimezone *caldav_internal_get_default_timezone (ECalBackend *backend);
+static icaltimezone *caldav_internal_get_timezone (ECalBackend *backend, const char *tzid);
+
+
 /* ************************************************************************* */
 /* Misc. utility functions */
 #define X_E_CALDAV "X-EVOLUTION-CALDAV-"
@@ -443,6 +450,7 @@ status_code_to_result (guint status_code
 		break;
 
 	default:
+		d(g_debug ("CalDAV:%s: Unhandled status code %d\n", G_STRFUNC, status_code));
 		result = GNOME_Evolution_Calendar_OtherError;
 	}
 
@@ -568,7 +576,7 @@ xp_object_get_href (xmlXPathObjectPtr re
 	}
 
 	ret = g_strdup (ret);
-	g_debug ("found href: %s", ret);
+	d(g_debug ("found href: %s", ret));
 
 	xmlXPathFreeObject (result);
 	return ret;
@@ -765,10 +773,6 @@ soup_authenticate (SoupSession  *session
 	priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
 
 	soup_auth_authenticate (auth, priv->username, priv->password);
-
-	priv->username = NULL;
-	priv->password = NULL;
-
 }
 
 static gint
@@ -843,6 +847,38 @@ caldav_set_session_proxy(ECalBackendCalD
 /* ************************************************************************* */
 /* direct CalDAV server access functions */
 
+static void
+send_and_handle_redirection (SoupSession *soup_session, SoupMessage *msg, char **new_location)
+{
+	soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
+	soup_session_send_message (soup_session, msg);
+	soup_message_set_flags (msg, 0);
+
+	if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
+		SoupURI *new_uri;
+		const char *new_loc;
+
+		new_loc = soup_message_headers_get (msg->response_headers, "Location");
+		g_return_if_fail (new_loc != NULL);
+
+		new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
+		if (!new_uri) {
+			soup_message_set_status_full (msg,
+						      SOUP_STATUS_MALFORMED,
+						      "Invalid Redirect URL");
+			return;
+		}
+
+		if (new_location)
+			*new_location = g_strdup (new_loc);
+
+		soup_message_set_uri (msg, new_uri);
+		soup_uri_free (new_uri);
+
+		soup_session_send_message (soup_session, msg);
+	}
+}
+
 static char *
 caldav_generate_uri (ECalBackendCalDAV *cbdav, const char *target)
 {
@@ -875,12 +911,14 @@ caldav_server_open_calendar (ECalBackend
 	soup_message_headers_append (message->request_headers,
 				     "User-Agent", "Evolution/" VERSION);
 
-	soup_session_send_message (priv->session, message);
+	send_and_handle_redirection (priv->session, message, NULL);
 
 	if (! SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
+		guint status_code = message->status_code;
+
 		g_object_unref (message);
 
-		return status_code_to_result (message->status_code);
+		return status_code_to_result (status_code);
 	}
 
 	/* parse the dav header, we are intreseted in the
@@ -968,7 +1006,7 @@ caldav_server_list_objects (ECalBackendC
 				  buf->buffer->use);
 
 	/* Send the request now */
-	soup_session_send_message (priv->session, message);
+	send_and_handle_redirection (priv->session, message, NULL);
 
 	/* Clean up the memory */
 	xmlOutputBufferClose (buf);
@@ -976,7 +1014,7 @@ caldav_server_list_objects (ECalBackendC
 
 	/* Check the result */
 	if (message->status_code != 207) {
-		g_warning ("Sever did not response with 207\n");
+		g_warning ("Sever did not response with 207, but with code %d\n", message->status_code);
 		return FALSE;
 	}
 
@@ -1009,7 +1047,7 @@ caldav_server_get_object (ECalBackendCal
 	soup_message_headers_append (message->request_headers,
 				     "User-Agent", "Evolution/" VERSION);
 
-	soup_session_send_message (priv->session, message);
+	send_and_handle_redirection (priv->session, message, NULL);
 
 	if (! SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
 		result = status_code_to_result (message->status_code);
@@ -1029,13 +1067,14 @@ caldav_server_get_object (ECalBackendCal
 
 	hdr = soup_message_headers_get (message->response_headers, "ETag");
 
-	if (hdr == NULL) {
-		g_warning ("UUHH no ETag, now that's bad!");
-		object->etag = NULL;
-	} else {
+	if (hdr != NULL) {
+		g_free (object->etag);
 		object->etag = quote_etag (hdr);
+	} else if (!object->etag) {
+		g_warning ("UUHH no ETag, now that's bad!");
 	}
 
+	g_free (object->cdata);
 	object->cdata = g_strdup (message->response_body->data);
 	g_object_unref (message);
 
@@ -1069,21 +1108,32 @@ caldav_server_put_object (ECalBackendCal
 	 * use the If-Match header to avoid the Lost-update
 	 * problem */
 	if (object->etag == NULL) {
-		soup_message_headers_append (message->request_headers,
-					     "If-None-Match", "*");
+		soup_message_headers_append (message->request_headers, "If-None-Match", "*");
 	} else {
 		soup_message_headers_append (message->request_headers,
 					     "If-Match", object->etag);
 	}
 
 	soup_message_set_request (message,
-			          "text/calendar",
+			          "text/calendar; charset=utf-8",
 				  SOUP_MEMORY_COPY,
 				  object->cdata,
 				  strlen (object->cdata));
 
+	uri = NULL;
+	send_and_handle_redirection (priv->session, message, &uri);
+
+	if (uri) {
+		char *file = strrchr (uri, '/');
 
-	soup_session_send_message (priv->session, message);
+		/* there was a redirect, update href properly */
+		if (file) {
+			g_free (object->href);
+			object->href = soup_uri_encode (file + 1, NULL);
+		}
+
+		g_free (uri);
+	}
 
 	/* FIXME: sepcial case precondition errors ?*/
 	result = status_code_to_result (message->status_code);
@@ -1100,7 +1150,6 @@ caldav_server_put_object (ECalBackendCal
 		g_warning ("Ups no Etag in put response");
 	}
 
-
 	g_object_unref (message);
 	return result;
 }
@@ -1130,7 +1179,7 @@ caldav_server_delete_object (ECalBackend
 					     "If-Match", object->etag);
 	}
 
-	soup_session_send_message (priv->session, message);
+	send_and_handle_redirection (priv->session, message, NULL);
 
 	result = status_code_to_result (message->status_code);
 
@@ -1307,7 +1356,7 @@ synchronize_cache (ECalBackendCalDAV *cb
 	/* remove old (not on server anymore) items from cache */
 	for (citer = cobjs; citer; citer = g_list_next (citer)) {
 		ECalComponent *comp;
-		const char *uid;
+		const char *uid = NULL;
 
 		comp = E_CAL_COMPONENT (citer->data);
 		e_cal_component_get_uid (comp, &uid);
@@ -1356,7 +1405,6 @@ synch_slave_loop (gpointer data)
 		/* Ok here we go, do some real work
 		 * Synch it baby one more time ...
 		 */
-		//d(g_print ("Synch-Slave: Goint to work ...\n")); XXX re-enable output please
 		synchronize_cache (cbdav);
 
 		/* puhh that was hard, get some rest :) */
@@ -1452,6 +1500,7 @@ initialize_backend (ECalBackendCalDAV *c
 	const char		 *os_val;
 	const char               *uri;
 	gsize                     len;
+	const char               *refresh;
 
 	priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
 
@@ -1473,8 +1522,6 @@ initialize_backend (ECalBackendCalDAV *c
 	os_val = e_source_get_property(source, "ssl");
 	uri = e_cal_backend_get_uri (E_CAL_BACKEND (cbdav));
 
-
-
 	if (g_str_has_prefix (uri, "caldav://")) {
 		const char *proto;
 
@@ -1511,6 +1558,9 @@ initialize_backend (ECalBackendCalDAV *c
 
 	}
 
+	refresh = e_source_get_property (source, "refresh");
+	priv->refresh_time.tv_sec  = (refresh && atoi (refresh) > 0) ? (60 * atoi (refresh)) : (DEFAULT_REFRESH_TIME);
+
 	priv->slave_cmd = SLAVE_SHOULD_SLEEP;
 	slave = g_thread_create (synch_slave_loop, cbdav, FALSE, NULL);
 
@@ -1526,7 +1576,6 @@ out:
 	return result;
 }
 
-
 static ECalBackendSyncStatus
 caldav_do_open (ECalBackendSync *backend,
 		EDataCal        *cal,
@@ -1658,6 +1707,52 @@ pack_cobj (ECalBackendCalDAV *cbdav, ECa
 
 }
 
+static void
+sanitize_component (ECalBackend *cb, ECalComponent *comp)
+{
+	ECalComponentDateTime dt;
+	icaltimezone *zone, *default_zone;
+
+	/* Check dtstart, dtend and due's timezone, and convert it to local
+	 * default timezone if the timezone is not in our builtin timezone
+	 * list */
+	e_cal_component_get_dtstart (comp, &dt);
+	if (dt.value && dt.tzid) {
+		zone = caldav_internal_get_timezone (cb, dt.tzid);
+		if (!zone) {
+			default_zone = caldav_internal_get_default_timezone (cb);
+			g_free ((char *)dt.tzid);
+			dt.tzid = g_strdup (icaltimezone_get_tzid (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 && dt.tzid) {
+		zone = caldav_internal_get_timezone (cb, dt.tzid);
+		if (!zone) {
+			default_zone = caldav_internal_get_default_timezone (cb);
+			g_free ((char *)dt.tzid);
+			dt.tzid = g_strdup (icaltimezone_get_tzid (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 && dt.tzid) {
+		zone = caldav_internal_get_timezone (cb, dt.tzid);
+		if (!zone) {
+			default_zone = caldav_internal_get_default_timezone (cb);
+			g_free ((char *)dt.tzid);
+			dt.tzid = g_strdup (icaltimezone_get_tzid (default_zone));
+			e_cal_component_set_due (comp, &dt);
+		}
+	}
+	e_cal_component_free_datetime (&dt);
+	e_cal_component_abort_sequence (comp);
+}
 
 static ECalBackendSyncStatus
 caldav_create_object (ECalBackendSync  *backend,
@@ -1671,6 +1766,7 @@ caldav_create_object (ECalBackendSync  *
 	ECalComponent            *comp;
 	gboolean                  online;
 	char                     *href;
+	struct icaltimetype current;
 
 	cbdav = E_CAL_BACKEND_CALDAV (backend);
 	priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
@@ -1691,17 +1787,28 @@ caldav_create_object (ECalBackendSync  *
 		return GNOME_Evolution_Calendar_InvalidObject;
 	}
 
+	/* Set the created and last modified times on the component */
+	current = icaltime_from_timet (time (NULL), 0);
+	e_cal_component_set_created (comp, &current);
+	e_cal_component_set_last_modified (comp, &current);
+
+	/* sanitize the component*/
+	sanitize_component ((ECalBackend *)cbdav, comp);
+
 	if (online) {
 		CalDAVObject object;
+		const char *id = NULL;
 
 		href = e_cal_component_gen_href (comp);
-
+		
 		object.href  = href;
 		object.etag  = NULL;
 		object.cdata = pack_cobj (cbdav, comp);
 
 		status = caldav_server_put_object (cbdav, &object);
 
+		e_cal_component_get_uid (comp, &id);
+		e_cal_component_set_href (comp, object.href);
 		e_cal_component_set_etag (comp, object.etag);
 		caldav_object_free (&object, FALSE);
 
@@ -1742,6 +1849,7 @@ caldav_modify_object (ECalBackendSync  *
 	ECalComponent            *cache_comp;
 	gboolean                  online;
 	const char		 *uid = NULL;
+	struct icaltimetype current;
 
 	cbdav = E_CAL_BACKEND_CALDAV (backend);
 	priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
@@ -1762,6 +1870,13 @@ caldav_modify_object (ECalBackendSync  *
 		return GNOME_Evolution_Calendar_InvalidObject;
 	}
 
+	/* Set the last modified time on the component */
+	current = icaltime_from_timet (time (NULL), 0);
+	e_cal_component_set_last_modified (comp, &current);
+
+	/* sanitize the component*/
+	sanitize_component ((ECalBackend *)cbdav, comp);
+
 	e_cal_component_get_uid (comp, &uid);
 
 	cache_comp = e_cal_backend_cache_get_component (priv->cache, uid, NULL);
@@ -1779,6 +1894,7 @@ caldav_modify_object (ECalBackendSync  *
 
 		status = caldav_server_put_object (cbdav, &object);
 
+		e_cal_component_set_href (comp, object.href);
 		e_cal_component_set_etag (comp, object.etag);
 		caldav_object_free (&object, FALSE);
 
@@ -1846,7 +1962,7 @@ caldav_remove_object (ECalBackendSync  *
 
 	*old_object = e_cal_component_get_as_string (cache_comp);
 
-	if (mod == CALOBJ_MOD_THIS)
+	if (mod == CALOBJ_MOD_THIS && rid && *rid)
 		e_cal_util_remove_instances (e_cal_component_get_icalcomponent (cache_comp), icaltime_from_string (rid), mod);
 
 	if (online) {
@@ -1856,17 +1972,19 @@ caldav_remove_object (ECalBackendSync  *
 		caldav_object.etag  = e_cal_component_get_etag (cache_comp);
 		caldav_object.cdata = NULL;
 
-		if (mod == CALOBJ_MOD_THIS) {
+		if (mod == CALOBJ_MOD_THIS && rid && *rid) {
 			caldav_object.cdata = pack_cobj (cbdav, cache_comp);
 
 			status = caldav_server_put_object (cbdav, &caldav_object);
+			e_cal_component_set_href (cache_comp, caldav_object.href);
+			e_cal_component_set_etag (cache_comp, caldav_object.etag);
 		} else
 			status = caldav_server_delete_object (cbdav, &caldav_object);
 
 		caldav_object_free (&caldav_object, FALSE);
 	} else {
 		/* mark component as out of synch */
-		if (mod == CALOBJ_MOD_THIS)
+		if (mod == CALOBJ_MOD_THIS && rid && *rid)
 			e_cal_component_set_synch_state (cache_comp, E_CAL_COMPONENT_LOCALLY_MODIFIED);
 		else
 			e_cal_component_set_synch_state (cache_comp, E_CAL_COMPONENT_LOCALLY_DELETED);
@@ -1879,7 +1997,7 @@ caldav_remove_object (ECalBackendSync  *
 
 	/* We should prolly check for cache errors
 	 * but when that happens we are kinda hosed anyway */
-	if (mod == CALOBJ_MOD_THIS) {
+	if (mod == CALOBJ_MOD_THIS && rid && *rid) {
 		e_cal_backend_cache_put_component (priv->cache, cache_comp);
 		*object = e_cal_component_get_as_string (cache_comp);
 	} else
@@ -2005,6 +2123,7 @@ process_object (ECalBackendCalDAV   *cbd
 
 			object.cdata = pack_cobj (cbdav, ecomp);
 			status = caldav_server_put_object (cbdav, &object);
+			e_cal_component_set_href (ecomp, object.href);
 			e_cal_component_set_etag (ecomp, object.etag);
 			caldav_object_free (&object, FALSE);
 		} else {
@@ -2296,8 +2415,7 @@ caldav_add_timezone (ECalBackendSync *ba
 		     EDataCal        *cal,
 		     const char      *tzobj)
 {
-	/* FIXME: implement me! */
-	g_warning ("function not implemented %s", G_STRFUNC);
+	/* Using only libical buildin timezones for now */
 	return GNOME_Evolution_Calendar_Success;
 }
 
@@ -2306,8 +2424,30 @@ caldav_set_default_zone (ECalBackendSync
 			     EDataCal        *cal,
 			     const char      *tzobj)
 {
-	/* FIXME: implement me! */
-	g_warning ("function not implemented %s", G_STRFUNC);
+	icalcomponent *tz_comp;
+	ECalBackendCalDAV *cbdav;
+	ECalBackendCalDAVPrivate *priv;
+	icaltimezone *zone;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (backend), GNOME_Evolution_Calendar_OtherError);
+	g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
+
+	cbdav = E_CAL_BACKEND_CALDAV (backend);
+	priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
+
+	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 != icaltimezone_get_utc_timezone ())
+		icaltimezone_free (priv->default_zone, 1);
+
+	/* Set the default timezone to it. */
+	priv->default_zone = zone;
+
 	return GNOME_Evolution_Calendar_Success;
 }
 
@@ -2519,7 +2659,17 @@ caldav_set_mode (ECalBackend *backend, C
 static icaltimezone *
 caldav_internal_get_default_timezone (ECalBackend *backend)
 {
-	return icaltimezone_get_utc_timezone ();
+	ECalBackendCalDAV *cbdav;
+	ECalBackendCalDAVPrivate *priv;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (backend), NULL);
+
+	cbdav = E_CAL_BACKEND_CALDAV (backend);
+	priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
+
+	g_return_val_if_fail (priv->default_zone != NULL, NULL);
+
+	return priv->default_zone;
 }
 
 static icaltimezone *
@@ -2598,6 +2748,11 @@ e_cal_backend_caldav_finalize (GObject *
 	g_cond_free (priv->cond);
 	g_cond_free (priv->slave_gone_cond);
 
+	if (priv->default_zone && priv->default_zone != icaltimezone_get_utc_timezone ()) {
+		icaltimezone_free (priv->default_zone, 1);
+	}
+	priv->default_zone = NULL;
+
 	if (G_OBJECT_CLASS (parent_class)->finalize)
 		(* G_OBJECT_CLASS (parent_class)->finalize) (object);
 }
@@ -2614,6 +2769,8 @@ e_cal_backend_caldav_init (ECalBackendCa
 	if (G_UNLIKELY (caldav_debug_show (DEBUG_MESSAGE)))
 		caldav_debug_setup (priv->session);
 
+	priv->default_zone = icaltimezone_get_utc_timezone ();
+
 	priv->disposed = FALSE;
 	priv->do_synch = FALSE;
 	priv->loaded   = FALSE;
