[evolution-data-server] Patch from Stanislav Slusny as part of the Google Summer of Code project to optimize eds calendar me



commit 1bec75476162b04acbb7e01fd708638ebfebc334
Author: Stanislav Slusny <slusnys gmail com>
Date:   Mon Sep 6 19:59:53 2010 +0530

    Patch from Stanislav Slusny as part of the Google Summer of
    Code project to optimize eds calendar memory usage and speed.
    Punit Jain <jpunit novell com> worked upon this patch and fixed
    issues to be able to be upstreamed. Thanks to both of them.

 calendar/backends/caldav/e-cal-backend-caldav.c    |   54 ++-
 calendar/backends/file/Makefile.am                 |   19 +
 calendar/backends/file/e-cal-backend-file.c        |  434 +++++++++++-
 .../backends/groupwise/e-cal-backend-groupwise.c   |   56 ++-
 calendar/backends/http/e-cal-backend-http.c        |   59 ++-
 calendar/backends/weather/e-cal-backend-weather.c  |   50 ++-
 calendar/libecal/e-cal-recur.c                     |   94 ++-
 calendar/libecal/e-cal-recur.h                     |    6 +
 calendar/libecal/e-cal-util.c                      |  163 +++++-
 calendar/libecal/e-cal-util.h                      |    7 +
 calendar/libedata-cal/Makefile.am                  |   55 ++-
 calendar/libedata-cal/e-cal-backend-cache.c        |   61 ++-
 calendar/libedata-cal/e-cal-backend-cache.h        |    5 +-
 calendar/libedata-cal/e-cal-backend-file-store.c   |   52 +-
 calendar/libedata-cal/e-cal-backend-intervaltree.c |  765 ++++++++++++++++++++
 calendar/libedata-cal/e-cal-backend-intervaltree.h |   95 +++
 calendar/libedata-cal/e-cal-backend-sexp.c         |   68 ++-
 calendar/libedata-cal/e-cal-backend-sexp.h         |    6 +-
 calendar/libedata-cal/e-cal-backend-store.c        |   94 +++-
 calendar/libedata-cal/e-cal-backend-store.h        |    9 +-
 calendar/libedata-cal/e-data-cal-view.c            |    3 +-
 calendar/libedata-cal/e-data-cal.c                 |    6 +-
 calendar/libedata-cal/test-intervaltree.c          |  431 +++++++++++
 libedataserver/Makefile.am                         |    2 +
 libedataserver/e-debug-log.c                       |  633 ++++++++++++++++
 libedataserver/e-debug-log.h                       |   56 ++
 libedataserver/e-sexp.c                            |  285 ++++++++-
 libedataserver/e-sexp.h                            |    4 +
 28 files changed, 3469 insertions(+), 103 deletions(-)
---
diff --git a/calendar/backends/caldav/e-cal-backend-caldav.c b/calendar/backends/caldav/e-cal-backend-caldav.c
index 505bc12..05d202a 100644
--- a/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -266,6 +266,31 @@ caldav_debug_setup (SoupSession *session)
 	g_object_unref (logger);
 }
 
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+	return (!strcmp (tzid, "UTC"))
+		? icaltimezone_get_utc_timezone ()
+		: icaltimezone_get_builtin_timezone_from_tzid (tzid);
+}
+
+static gboolean 
+put_component_to_store (ECalBackendCalDAV *cbdav,
+			ECalComponent *comp)
+{
+	time_t time_start, time_end;
+	ECalBackendCalDAVPrivate *priv;
+
+	priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
+
+	get_component_occur_times (comp, &time_start, &time_end,
+				   resolve_tzid, NULL,  icaltimezone_get_utc_timezone (),
+				   e_cal_backend_get_kind (E_CAL_BACKEND (cbdav)));
+
+	return e_cal_backend_store_put_component (priv->store, comp, time_start, time_end);
+}
+
+
 static ECalBackendSyncClass *parent_class = NULL;
 
 static icaltimezone *caldav_internal_get_default_timezone (ECalBackend *backend);
@@ -1873,7 +1898,7 @@ synchronize_cache (ECalBackendCalDAV *cbdav, time_t start_time, time_t end_time)
 										g_free (nc_rid);
 									}
 
-									e_cal_backend_store_put_component (priv->store, new_comp);
+									put_component_to_store (cbdav, new_comp);
 
 									if (old_comp == NULL) {
 										gchar *new_str = e_cal_component_get_as_string (new_comp);
@@ -2594,7 +2619,7 @@ put_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp, const gcha
 				if (etag)
 					ecalcomp_set_etag (comp, etag);
 
-				if (e_cal_backend_store_put_component (priv->store, comp))
+				if (put_component_to_store (cbdav, comp))
 					res = TRUE;
 			}
 		}
@@ -2607,7 +2632,7 @@ put_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp, const gcha
 			if (etag)
 				ecalcomp_set_etag (comp, etag);
 
-			res = e_cal_backend_store_put_component (priv->store, comp);
+			res = put_component_to_store (cbdav, comp);
 		}
 	}
 
@@ -3974,6 +3999,8 @@ caldav_get_object_list (ECalBackendSync  *backend,
 	ECalBackend *bkend;
 	gboolean                  do_search;
 	GSList			 *list, *iter;
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time;
 
 	cbdav = E_CAL_BACKEND_CALDAV (backend);
 	priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
@@ -3993,7 +4020,15 @@ caldav_get_object_list (ECalBackendSync  *backend,
 
 	*objects = NULL;
 
-	list = e_cal_backend_store_get_components (priv->store);
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(sexp,
+									    &occur_start,
+									    &occur_end);
+
+	bkend = E_CAL_BACKEND (backend);
+
+	list = prunning_by_time ?
+		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+		: e_cal_backend_store_get_components (priv->store);
 
 	bkend = E_CAL_BACKEND (backend);
 
@@ -4023,7 +4058,8 @@ caldav_start_query (ECalBackend  *backend,
 	gboolean                  do_search;
 	GSList			 *list, *iter;
 	const gchar               *sexp_string;
-
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time;
 	cbdav = E_CAL_BACKEND_CALDAV (backend);
 	priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
 
@@ -4037,11 +4073,17 @@ caldav_start_query (ECalBackend  *backend,
 	} else {
 		do_search = TRUE;
 	}
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(sexp,
+									    &occur_start,
+									    &occur_end);
 
-	list = e_cal_backend_store_get_components (priv->store);
 
 	bkend = E_CAL_BACKEND (backend);
 
+	list = prunning_by_time ?
+		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+		: e_cal_backend_store_get_components (priv->store);
+
 	for (iter = list; iter; iter = g_slist_next (iter)) {
 		ECalComponent *comp = E_CAL_COMPONENT (iter->data);
 
diff --git a/calendar/backends/file/Makefile.am b/calendar/backends/file/Makefile.am
index c58c6a3..316a070 100644
--- a/calendar/backends/file/Makefile.am
+++ b/calendar/backends/file/Makefile.am
@@ -9,6 +9,8 @@ libecalbackendfile_la_CPPFLAGS = \
 	-I$(top_builddir)/calendar			\
 	$(EVOLUTION_CALENDAR_CFLAGS)
 
+noinst_PROGRAMS = test-interval-searches
+
 libecalbackendfile_la_SOURCES =		\
 	e-cal-backend-file-factory.c	\
 	e-cal-backend-file-factory.h	\
@@ -27,7 +29,24 @@ libecalbackendfile_la_LIBADD =							\
 	$(top_builddir)/libedataserver/libedataserver-1.2.la			\
 	$(EVOLUTION_CALENDAR_LIBS)
 
+libecalbackendfile_la_CFLAGS =	$(AM_CFLAGS)
+
 libecalbackendfile_la_LDFLAGS =		\
 	-module -avoid-version $(NO_UNDEFINED)
 
+test_interval_searches_SOURCES = e-cal-backend-file.c
+
+test_interval_searches_LDADD = \
+	$(top_builddir)/calendar/libecal/libecal-1.2.la				\
+	$(top_builddir)/calendar/libedata-cal/libedata-cal-1.2.la		\
+	$(top_builddir)/libedataserver/libedataserver-1.2.la			\
+	$(top_builddir)/calendar/backends/file/libecalbackendfile.la		\
+	$(EVOLUTION_CALENDAR_LIBS)
+
+test_interval_searches_CPPFLAGS = \
+	$(AM_CPPFLAGS)			\
+	-I$(top_builddir)/calendar	\
+	$(EVOLUTION_CALENDAR_CFLAGS)	\
+	-DTEST_QUERY_RESULT=1
+
 -include $(top_srcdir)/git.mk
diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c
index 7d7c511..a78dea2 100644
--- a/calendar/backends/file/e-cal-backend-file.c
+++ b/calendar/backends/file/e-cal-backend-file.c
@@ -33,12 +33,14 @@
 #include <gio/gio.h>
 #include "libedataserver/e-data-server-util.h"
 #include "libedataserver/e-xml-hash-utils.h"
+#include "libedataserver/e-debug-log.h"
 #include <libecal/e-cal-recur.h>
 #include <libecal/e-cal-time-util.h>
 #include <libecal/e-cal-util.h>
 #include <libecal/e-cal-check-timezones.h>
 #include <libedata-cal/e-cal-backend-util.h>
 #include <libedata-cal/e-cal-backend-sexp.h>
+#include <libedata-cal/e-cal-backend-intervaltree.h>
 #include "e-cal-backend-file-events.h"
 
 #ifndef O_BINARY
@@ -85,6 +87,8 @@ struct _ECalBackendFilePrivate {
 	 */
 	GHashTable *comp_uid_hash;
 
+	EIntervalTree *interval_tree;
+
 	GList *comp;
 
 	/* The calendar's default timezone, used for resolving DATE and
@@ -114,7 +118,7 @@ struct _ECalBackendFilePrivate {
 
 
 
-#define d(x)
+#define d(x) x
 
 static void e_cal_backend_file_dispose (GObject *object);
 static void e_cal_backend_file_finalize (GObject *object);
@@ -126,6 +130,12 @@ e_cal_backend_file_add_timezone (ECalBackendSync *backend, EDataCal *cal, const
 
 static void free_refresh_data (ECalBackendFile *cbfile);
 
+static icaltimezone *
+e_cal_backend_file_internal_get_timezone (ECalBackend *backend, const char *tzid);
+
+static icaltimezone *
+e_cal_backend_file_internal_get_default_timezone (ECalBackend *backend);
+
 /* g_hash_table_foreach() callback to destroy a ECalBackendFileObject */
 static void
 free_object_data (gpointer data)
@@ -287,6 +297,9 @@ free_calendar_data (ECalBackendFile *cbfile)
 
 	priv = cbfile->priv;
 
+	e_intervaltree_destroy (priv->interval_tree);
+	priv->interval_tree = NULL;
+
 	free_calendar_components (priv->comp_uid_hash, priv->icalcomp);
 	priv->comp_uid_hash = NULL;
 	priv->icalcomp = NULL;
@@ -439,13 +452,19 @@ static icaltimezone *
 resolve_tzid (const gchar *tzid, gpointer user_data)
 {
 	icalcomponent *vcalendar_comp = user_data;
+	icaltimezone* zone;
 
         if (!tzid || !tzid[0])
                 return NULL;
         else if (!strcmp (tzid, "UTC"))
                 return icaltimezone_get_utc_timezone ();
 
-	return icalcomponent_get_timezone (vcalendar_comp, tzid);
+	zone = icalcomponent_get_timezone (vcalendar_comp, tzid);
+
+	if (!zone)
+		zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
+
+	return zone;
 }
 
 /* Checks if the specified component has a duplicated UID and if so changes it */
@@ -498,6 +517,63 @@ get_rid_icaltime (ECalComponent *comp)
         return tt;
 }
 
+
+/* Adds component to the interval tree
+ */
+static gboolean
+add_component_to_intervaltree (ECalBackendFile *cbfile, ECalComponent *comp)
+{
+	time_t time_start = -1, time_end = -1;
+	ECalBackendFilePrivate *priv;
+
+	g_return_val_if_fail (cbfile != NULL, FALSE);
+	g_return_val_if_fail (comp != NULL, FALSE);
+
+	priv = cbfile->priv;
+
+	get_component_occur_times (comp, &time_start, &time_end,
+				   resolve_tzid, priv->icalcomp, priv->default_zone,
+				   e_cal_backend_get_kind (E_CAL_BACKEND (cbfile)));
+
+	if (time_end != -1 && time_start > time_end)
+		g_print ("Bogus component %s\n", e_cal_component_get_as_string (comp));
+	else
+		e_intervaltree_insert (priv->interval_tree, time_start, time_end, comp);
+
+	return FALSE;
+}
+
+static gboolean 
+remove_component_from_intervaltree (ECalBackendFile *cbfile, ECalComponent *comp)
+{
+	time_t time_start = -1, time_end = -1;
+	const char *uid = NULL;
+	gchar *rid;
+	ECalBackendFilePrivate *priv;
+
+	g_return_val_if_fail (cbfile != NULL, FALSE);
+	g_return_val_if_fail (comp != NULL, FALSE);
+
+	priv = cbfile->priv;
+
+	get_component_occur_times (comp, &time_start, &time_end,
+				   resolve_tzid, priv->icalcomp, priv->default_zone,
+				   e_cal_backend_get_kind (E_CAL_BACKEND (cbfile)));
+
+
+	if (time_end != -1 && time_start > time_end) {
+		g_error ("Bogus component %s\n", e_cal_component_get_as_string (comp));
+		return FALSE;
+	} else {
+		gboolean res;
+		rid = e_cal_component_get_recurid_as_string (comp);
+		e_cal_component_get_uid (comp, &uid);
+		res = e_intervaltree_remove (priv->interval_tree, uid, rid);
+		g_free (rid);
+		return res;
+	}
+}
+
 /* Tries to add an icalcomponent to the file backend.  We only store the objects
  * of the types we support; all others just remain in the toplevel component so
  * that we don't lose them.
@@ -536,6 +612,7 @@ add_component (ECalBackendFile *cbfile, ECalComponent *comp, gboolean add_to_top
 			g_hash_table_insert (priv->comp_uid_hash, g_strdup (uid), obj_data);
 		}
 
+		add_component_to_intervaltree (cbfile, comp);
 		g_hash_table_insert (obj_data->recurrences, rid, comp);
 		obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
 	} else {
@@ -557,6 +634,7 @@ add_component (ECalBackendFile *cbfile, ECalComponent *comp, gboolean add_to_top
 			obj_data->recurrences = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 
 			g_hash_table_insert (priv->comp_uid_hash, g_strdup (uid), obj_data);
+			add_component_to_intervaltree (cbfile, comp);
 		}
 	}
 
@@ -590,6 +668,9 @@ remove_recurrence_cb (gpointer key, gpointer value, gpointer data)
 	icalcomp = e_cal_component_get_icalcomponent (comp);
 	g_assert (icalcomp != NULL);
 
+	if (!remove_component_from_intervaltree (cbfile, comp)) {
+		g_message (G_STRLOC " Could not remove component from interval tree!");
+		}
 	icalcomponent_remove_component (priv->icalcomp, icalcomp);
 
 	/* remove it from our mapping */
@@ -623,6 +704,10 @@ remove_component (ECalBackendFile *cbfile, const gchar *uid, ECalBackendFileObje
 		l = g_list_find (priv->comp, obj_data->full_object);
 		g_assert (l != NULL);
 		priv->comp = g_list_delete_link (priv->comp, l);
+
+		if (!remove_component_from_intervaltree (cbfile, obj_data->full_object)) {
+			g_message (G_STRLOC " Could not remove component from interval tree!");
+		}
 	}
 
 	/* remove the recurrences also */
@@ -911,6 +996,7 @@ open_cal (ECalBackendFile *cbfile, const gchar *uristr, GError **perror)
 	priv->custom_file = g_strdup (uristr);
 
 	priv->comp_uid_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_object_data);
+	priv->interval_tree = e_intervaltree_new ();
 	scan_vcalendar (cbfile);
 
 	prepare_refresh_data (cbfile);
@@ -1058,6 +1144,7 @@ reload_cal (ECalBackendFile *cbfile, const gchar *uristr, GError **perror)
 	priv->icalcomp = icalcomp;
 
 	priv->comp_uid_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_object_data);
+	priv->interval_tree = e_intervaltree_new ();
 	scan_vcalendar (cbfile);
 
 	priv->path = uri_to_path (E_CAL_BACKEND (cbfile));
@@ -1096,6 +1183,7 @@ create_cal (ECalBackendFile *cbfile, const gchar *uristr, GError **perror)
 
 	/* Create our internal data */
 	priv->comp_uid_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_object_data);
+	priv->interval_tree = e_intervaltree_new ();
 
 	priv->path = uri_to_path (E_CAL_BACKEND (cbfile));
 
@@ -1621,6 +1709,33 @@ typedef struct {
 } MatchObjectData;
 
 static void
+match_object_sexp_to_component (gpointer value, gpointer data)
+{
+	ECalComponent* comp = value;
+	MatchObjectData *match_data = data;
+	const gchar *uid;
+	ECalBackendFile *cbfile;
+	ECalBackendFilePrivate *priv;
+	e_cal_component_get_uid (comp, &uid);
+
+	g_return_if_fail (comp != NULL);
+
+	cbfile = E_CAL_BACKEND_FILE (match_data->backend);
+
+	g_return_if_fail (match_data->backend != NULL);
+
+	priv = cbfile->priv;
+
+	g_return_if_fail (priv != NULL);
+
+	if ((!match_data->search_needed) ||
+	    (e_cal_backend_sexp_match_comp (match_data->obj_sexp, comp, match_data->backend))) {
+		match_data->obj_list = g_list_append (match_data->obj_list,
+						      e_cal_component_get_as_string (comp));
+	}
+}
+
+static void
 match_recurrence_sexp (gpointer key, gpointer value, gpointer data)
 {
 	ECalComponent *comp = value;
@@ -1653,6 +1768,7 @@ match_object_sexp (gpointer key, gpointer value, gpointer data)
 			      match_data);
 }
 
+
 /* Get_objects_in_range handler for the file backend */
 static void
 e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, const gchar *sexp, GList **objects, GError **perror)
@@ -1660,7 +1776,9 @@ e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, con
 	ECalBackendFile *cbfile;
 	ECalBackendFilePrivate *priv;
 	MatchObjectData match_data;
-
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time;
+	GList* objs_occuring_in_tw;
 	cbfile = E_CAL_BACKEND_FILE (backend);
 	priv = cbfile->priv;
 
@@ -1682,11 +1800,33 @@ e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, con
 	}
 
 	g_static_rec_mutex_lock (&priv->idle_save_rmutex);
-	g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
+
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(match_data.obj_sexp,
+									    &occur_start,
+									    &occur_end);
+
+	objs_occuring_in_tw =  NULL;
+
+	if (!prunning_by_time) {
+		g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp,
+				      &match_data); 
+	} else {
+		objs_occuring_in_tw = e_intervaltree_search (priv->interval_tree,
+							    occur_start, occur_end);
+
+		g_list_foreach(objs_occuring_in_tw, (GFunc) match_object_sexp_to_component,
+			       &match_data); 
+	}
+
 	g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
 
 	*objects = match_data.obj_list;
 
+	if (objs_occuring_in_tw) {
+		g_list_foreach(objs_occuring_in_tw, (GFunc)g_object_unref, NULL);
+		g_list_free (objs_occuring_in_tw);
+	}
+
 	g_object_unref (match_data.obj_sexp);
 }
 
@@ -1705,7 +1845,9 @@ e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
 	ECalBackendFile *cbfile;
 	ECalBackendFilePrivate *priv;
 	MatchObjectData match_data;
-
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time;
+	GList* objs_occuring_in_tw;
 	cbfile = E_CAL_BACKEND_FILE (backend);
 	priv = cbfile->priv;
 
@@ -1717,20 +1859,46 @@ e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
 	match_data.obj_list = NULL;
 	match_data.backend = backend;
 	match_data.default_zone = priv->default_zone;
+	match_data.obj_sexp = e_data_cal_view_get_object_sexp (query);
 
 	if (!strcmp (match_data.query, "#t"))
 		match_data.search_needed = FALSE;
 
-	match_data.obj_sexp = e_data_cal_view_get_object_sexp (query);
 	if (!match_data.obj_sexp) {
 		GError *error = EDC_ERROR (InvalidQuery);
 		e_data_cal_view_notify_done (query, error);
 		g_error_free (error);
 		return;
 	}
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(match_data.obj_sexp,
+									    &occur_start,
+									    &occur_end);
+
+	objs_occuring_in_tw = NULL;
 
 	g_static_rec_mutex_lock (&priv->idle_save_rmutex);
-	g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
+
+	if (!prunning_by_time) {
+		/* full scan */
+		g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp,
+				      &match_data); 
+
+		e_debug_log(FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES,  "---;%p;QUERY-ITEMS;%s;%s;%d", query,
+			    e_data_cal_view_get_text (query), G_OBJECT_TYPE_NAME (backend),
+			    g_hash_table_size(priv->comp_uid_hash));
+	} else {
+		/* matches objects in new "interval tree" way */
+		/* events occuring in time window */
+		objs_occuring_in_tw = e_intervaltree_search (priv->interval_tree, occur_start, occur_end);
+
+		g_list_foreach(objs_occuring_in_tw, (GFunc) match_object_sexp_to_component,
+			       &match_data); 
+
+		e_debug_log(FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES,  "---;%p;QUERY-ITEMS;%s;%s;%d", query,
+			    e_data_cal_view_get_text (query), G_OBJECT_TYPE_NAME (backend),
+			    g_list_length (objs_occuring_in_tw));
+	}
+
 	g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
 
 	/* notify listeners of all objects */
@@ -1741,6 +1909,11 @@ e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
 		g_list_foreach (match_data.obj_list, (GFunc) g_free, NULL);
 		g_list_free (match_data.obj_list);
 	}
+
+	if (objs_occuring_in_tw) {
+		g_list_foreach(objs_occuring_in_tw, (GFunc)g_object_unref, NULL);
+		g_list_free (objs_occuring_in_tw);
+	}
 	g_object_unref (match_data.obj_sexp);
 
 	e_data_cal_view_notify_done (query, NULL /* Success */);
@@ -3048,6 +3221,7 @@ e_cal_backend_file_init (ECalBackendFile *cbfile)
 	priv->icalcomp = NULL;
 	priv->comp_uid_hash = NULL;
 	priv->comp = NULL;
+	priv->interval_tree = NULL;
 	priv->custom_file = NULL;
 	priv->refresh_lock = g_mutex_new ();
 
@@ -3236,3 +3410,249 @@ e_cal_backend_file_reload (ECalBackendFile *cbfile, GError **perror)
 	if (err)
 		g_propagate_error (perror, err);
 }
+
+#ifdef TEST_QUERY_RESULT
+#include <glib.h>
+
+static void
+test_query_by_scanning_all_objects (ECalBackendFile* cbfile, const char *sexp, GList **objects)
+{
+	MatchObjectData match_data;
+	ECalBackendFilePrivate *priv;
+
+	priv = cbfile->priv;
+
+	match_data.search_needed = TRUE;
+	match_data.query = sexp;
+	match_data.obj_list = NULL;
+	match_data.default_zone = priv->default_zone;
+	match_data.backend = E_CAL_BACKEND (cbfile);
+
+	if (!strcmp (sexp, "#t"))
+		match_data.search_needed = FALSE;
+
+	match_data.obj_sexp = e_cal_backend_sexp_new (sexp);
+	if (!match_data.obj_sexp)
+		return;
+
+	g_static_rec_mutex_lock (&priv->idle_save_rmutex);
+
+	if (!match_data.obj_sexp)
+	{
+		g_message (G_STRLOC ": Getting object list (%s)", sexp);
+		exit (-1);
+	}
+
+	g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp,
+			&match_data); 
+
+	g_static_rec_mutex_unlock (&priv->idle_save_rmutex);
+
+	*objects = match_data.obj_list;
+
+	g_object_unref (match_data.obj_sexp);
+}
+
+static void
+write_list (GList* list)
+{
+	GList *l;
+
+	for (l = list; l; l = l->next)
+	{
+		const char *str = l->data;
+		ECalComponent *comp = e_cal_component_new_from_string (str);
+		const char *uid;
+		e_cal_component_get_uid (comp, &uid);
+		g_print ("%s\n", uid);
+	}
+}
+
+static void
+get_difference_of_lists (ECalBackendFile* cbfile, GList* smaller, GList* bigger)
+{
+	GList *l, *lsmaller;
+
+	for (l = bigger ; l; l = l->next) {
+		char *str = l->data;
+		const char *uid;
+		ECalComponent *comp = e_cal_component_new_from_string (str);
+		gboolean found = FALSE;
+		e_cal_component_get_uid (comp, &uid);
+
+		for (lsmaller = smaller; lsmaller && !found; lsmaller = lsmaller->next)
+		{
+			char *strsmaller = lsmaller->data;
+			const char *uidsmaller;
+			ECalComponent *compsmaller = e_cal_component_new_from_string (strsmaller);
+			e_cal_component_get_uid (compsmaller, &uidsmaller);
+
+			found = strcmp (uid, uidsmaller) == 0;
+
+			g_object_unref (compsmaller);
+		}
+
+		if (!found)
+		{
+			time_t time_start, time_end;
+			printf ("%s IS MISSING\n", uid);
+
+			get_component_occur_times (comp, &time_start, &time_end,
+						   resolve_tzid, cbfile->priv->icalcomp,
+						   cbfile->priv->default_zone,
+						   e_cal_backend_get_kind (E_CAL_BACKEND (cbfile)));
+
+			d (printf ("start %s\n", asctime(gmtime(&time_start))));
+			d (printf ("end %s\n", asctime(gmtime(&time_end))));
+		}
+
+		g_object_unref (comp);
+	}
+}
+
+static void
+test_query (ECalBackendFile* cbfile, const char* query)
+{
+	GList *objects = NULL, *all_objects = NULL;
+
+	g_return_if_fail (query != NULL);
+
+	d (g_print ("Query %s\n", query));
+
+	test_query_by_scanning_all_objects (cbfile, query, &all_objects);
+	e_cal_backend_file_get_object_list (E_CAL_BACKEND_SYNC (cbfile), NULL, query, &objects, NULL);
+	if (objects == NULL)
+	{
+		g_message (G_STRLOC " failed to get objects\n");
+		exit(0);
+	}
+
+	if (g_list_length (objects) < g_list_length (all_objects) )
+	{
+		g_print ("ERROR\n");
+		get_difference_of_lists (cbfile, objects, all_objects);
+		exit (-1);
+	}
+	else if (g_list_length (objects) > g_list_length (all_objects) )
+	{
+		g_print ("ERROR\n");
+		write_list (all_objects);
+		get_difference_of_lists (cbfile, all_objects, objects);
+		exit (-1);
+	}
+
+
+	g_list_foreach(objects, (GFunc) g_free, NULL);
+	g_list_free (objects);
+	g_list_foreach(all_objects, (GFunc) g_free, NULL);
+	g_list_free (all_objects);
+}
+
+static void
+execute_query (ECalBackendFile* cbfile, const char* query)
+{
+	GList *objects = NULL;
+
+	g_return_if_fail (query != NULL);
+
+	d (g_print ("Query %s\n", query));
+	e_cal_backend_file_get_object_list (E_CAL_BACKEND_SYNC (cbfile), NULL, query, &objects, NULL);
+	if(objects == NULL)
+	{
+		g_message (G_STRLOC " failed to get objects\n");
+		exit(0);
+	}
+
+	g_list_foreach(objects, (GFunc) g_free, NULL);
+	g_list_free (objects);
+}
+
+static gchar *fname = NULL;
+static gboolean only_execute = FALSE;
+static gchar *calendar_fname = NULL;
+
+static GOptionEntry entries[] = 
+{
+  { "test-file", 't', 0, G_OPTION_ARG_STRING, &fname, "File with prepared queries", NULL },
+  { "only-execute", 'e', 0, G_OPTION_ARG_NONE, &only_execute, "Only execute, do not test query", NULL },
+  { "calendar-file", 'c', 0, G_OPTION_ARG_STRING, &calendar_fname, "Path to the calendar.ics file", NULL },
+  { NULL }
+};
+
+int
+main(int argc, char **argv)
+{
+	char * line = NULL;
+	size_t len = 0;
+	ssize_t read;
+	ECalBackendFile* cbfile;
+	int num = 0;
+	GError *error = NULL;
+	GOptionContext *context;
+	FILE* fin = NULL;
+
+	g_type_init ();
+	g_thread_init (NULL);
+
+	context = g_option_context_new ("- test utility for e-d-s file backend");
+	g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+	if (!g_option_context_parse (context, &argc, &argv, &error))
+	{
+		g_print ("option parsing failed: %s\n", error->message);
+		exit (1);
+	}
+
+	calendar_fname = g_strdup("calendar.ics");
+
+	if (!calendar_fname)
+	{
+		g_message (G_STRLOC " Please, use -c parameter");
+		exit (-1);
+	}
+
+	cbfile = g_object_new (E_TYPE_CAL_BACKEND_FILE, NULL);
+	open_cal (cbfile, calendar_fname, NULL);
+	if (cbfile == NULL)
+	{
+		g_message (G_STRLOC " Could not open calendar %s", calendar_fname);
+		exit (-1);
+	}
+
+	if (fname)
+	{
+		fin = fopen (fname, "r");
+
+		if (!fin)
+		{
+			g_message (G_STRLOC " Could not open file %s", fname);
+			goto err0;
+		}
+	}
+	else
+	{
+		g_message (G_STRLOC " Reading from stdin");
+		fin = stdin;
+	}
+
+	while ((read = getline(&line, &len, fin)) != -1) {
+		g_print ("Query %d: %s", num++, line);
+
+		if (only_execute)
+			execute_query(cbfile, line);
+		else
+			test_query(cbfile, line);
+	}
+
+	if (line)
+		free(line);
+
+	if (fname)
+		fclose (fin);
+
+err0:
+	g_object_unref (cbfile);
+
+	return 0;
+}
+#endif
+
diff --git a/calendar/backends/groupwise/e-cal-backend-groupwise.c b/calendar/backends/groupwise/e-cal-backend-groupwise.c
index 00db568..7dbbdc3 100644
--- a/calendar/backends/groupwise/e-cal-backend-groupwise.c
+++ b/calendar/backends/groupwise/e-cal-backend-groupwise.c
@@ -191,6 +191,30 @@ get_element_type (icalcomponent_kind kind)
 
 }
 
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+	return (!strcmp (tzid, "UTC"))
+		? icaltimezone_get_utc_timezone ()
+		: icaltimezone_get_builtin_timezone_from_tzid (tzid);
+}
+
+static void
+put_component_to_store (ECalBackendGroupwise *cbgw,
+			ECalComponent *comp)
+{
+	time_t time_start, time_end;
+	ECalBackendGroupwisePrivate *priv;
+
+	priv = cbgw->priv;
+
+	get_component_occur_times (comp, &time_start, &time_end,
+				   resolve_tzid, NULL, priv->default_zone,
+				   e_cal_backend_get_kind (E_CAL_BACKEND (cbgw)));
+
+	e_cal_backend_store_put_component (priv->store, comp, time_start, time_end);
+}
+
 /* Initialy populate the cache from the server */
 static EGwConnectionStatus
 populate_cache (ECalBackendGroupwise *cbgw)
@@ -302,7 +326,7 @@ populate_cache (ECalBackendGroupwise *cbgw)
 					comp_str = e_cal_component_get_as_string (comp);
 					e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), (const gchar *) comp_str);
 					g_free (comp_str);
-					e_cal_backend_store_put_component (priv->store, comp);
+					put_component_to_store (cbgw, comp);
 					g_object_unref (comp);
 				}
 				g_free (progress_string);
@@ -503,7 +527,7 @@ get_deltas (gpointer handle)
 			g_free (modif_comp_str);
 			g_free (cache_comp_str);
 			cache_comp_str = NULL;
-			e_cal_backend_store_put_component (store, modified_comp);
+			put_component_to_store (cbgw, modified_comp);
 		}
 
 		e_cal_component_free_icaltimetype (tt);
@@ -653,8 +677,7 @@ get_deltas (gpointer handle)
 			comp = e_gw_item_to_cal_component (item, cbgw);
 			if (comp) {
 				e_cal_component_commit_sequence (comp);
-				e_cal_backend_store_put_component (store, comp);
-
+				put_component_to_store (cbgw, comp);
 				if (kind == icalcomponent_isa (e_cal_component_get_icalcomponent (comp))) {
 					tmp = e_cal_component_get_as_string (comp);
 					e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), tmp);
@@ -1344,6 +1367,7 @@ e_cal_backend_groupwise_open (ECalBackendSync *backend, EDataCal *cal, gboolean
 			}
 		}
 
+		e_cal_backend_store_load (priv->store);
 		PRIV_UNLOCK (priv);
 		return;
 	}
@@ -1594,6 +1618,8 @@ e_cal_backend_groupwise_get_object_list (ECalBackendSync *backend, EDataCal *cal
         GSList *components, *l;
 	ECalBackendSExp *cbsexp;
 	gboolean search_needed = TRUE;
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time;
 
 	cbgw = E_CAL_BACKEND_GROUPWISE (backend);
 	priv = cbgw->priv;
@@ -1611,8 +1637,15 @@ e_cal_backend_groupwise_get_object_list (ECalBackendSync *backend, EDataCal *cal
 	}
 
 	*objects = NULL;
-	components = e_cal_backend_store_get_components (priv->store);
-        for (l = components; l != NULL; l = l->next) {
+		
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(cbsexp,
+									    &occur_start,
+									    &occur_end);
+	components = prunning_by_time ?
+		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+		: e_cal_backend_store_get_components (priv->store);
+
+	for (l = components; l != NULL; l = l->next) {
                 ECalComponent *comp = E_CAL_COMPONENT (l->data);
 
 		if (e_cal_backend_get_kind (E_CAL_BACKEND (backend)) ==
@@ -1873,7 +1906,7 @@ update_from_server (ECalBackendGroupwise *cbgw, GSList *uid_list, gchar **calobj
 		item = (EGwItem *) tmp->data;
 		e_cal_comp = e_gw_item_to_cal_component (item, cbgw);
 		e_cal_component_commit_sequence (e_cal_comp);
-		e_cal_backend_store_put_component (priv->store, e_cal_comp);
+		put_component_to_store (cbgw, e_cal_comp);
 
 		if (i == 0) {
 			*calobj = e_cal_component_get_as_string (e_cal_comp);
@@ -2089,7 +2122,7 @@ e_cal_backend_groupwise_modify_object (ECalBackendSync *backend, EDataCal *cal,
 				return;
 			}
 
-			e_cal_backend_store_put_component (priv->store, comp);
+			put_component_to_store (cbgw, comp);
 			*new_object = e_cal_component_get_as_string (comp);
 			break;
 		}
@@ -2121,7 +2154,7 @@ e_cal_backend_groupwise_modify_object (ECalBackendSync *backend, EDataCal *cal,
 					g_propagate_error (error, EDC_ERROR_FAILED_STATUS (OtherError, status));
 					return;
 				}
-				e_cal_backend_store_put_component (priv->store, comp);
+				put_component_to_store (cbgw, comp);
 				break;
 			}
 		}
@@ -2145,7 +2178,7 @@ e_cal_backend_groupwise_modify_object (ECalBackendSync *backend, EDataCal *cal,
 
 	case CAL_MODE_LOCAL :
 		/* in offline mode, we just update the cache */
-		e_cal_backend_store_put_component (priv->store, comp);
+		put_component_to_store (cbgw, comp);
 		break;
 	default :
 		break;
@@ -2521,8 +2554,7 @@ receive_object (ECalBackendGroupwise *cbgw, EDataCal *cal, icalcomponent *icalco
 				change_status (component, pstatus, e_gw_connection_get_user_email (priv->cnc));
 				e_cal_component_get_transparency (comp, &transp);
 				e_cal_component_set_transparency (component, transp);
-
-				e_cal_backend_store_put_component (priv->store, component);
+				put_component_to_store (cbgw, comp);
 				comp_str = e_cal_component_get_as_string (component);
 
 				if (found)
diff --git a/calendar/backends/http/e-cal-backend-http.c b/calendar/backends/http/e-cal-backend-http.c
index 738ba8c..2f50c51 100644
--- a/calendar/backends/http/e-cal-backend-http.c
+++ b/calendar/backends/http/e-cal-backend-http.c
@@ -274,6 +274,36 @@ empty_cache (ECalBackendHttp *cbhttp)
 	e_cal_backend_store_clean (priv->store);
 }
 
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+        icalcomponent *vcalendar_comp = user_data;
+
+        if (!tzid || !tzid[0])
+                return NULL;
+        else if (!strcmp (tzid, "UTC"))
+                return icaltimezone_get_utc_timezone ();
+
+        return icalcomponent_get_timezone (vcalendar_comp, tzid);
+}
+
+static void
+put_component_to_store (ECalBackendHttp *cb,
+			ECalComponent *comp)
+{
+	time_t time_start, time_end;
+	ECalBackendHttpPrivate *priv;
+
+	priv = cb->priv;
+
+	get_component_occur_times (comp, &time_start, &time_end,
+				   resolve_tzid, NULL, priv->default_zone,
+				   e_cal_backend_get_kind (E_CAL_BACKEND (cb)));
+
+	e_cal_backend_store_put_component (priv->store, comp, time_start, time_end);
+}
+
+
 static void
 retrieval_done (SoupSession *session, SoupMessage *msg, ECalBackendHttp *cbhttp)
 {
@@ -397,8 +427,7 @@ retrieval_done (SoupSession *session, SoupMessage *msg, ECalBackendHttp *cbhttp)
 				const gchar *uid, *orig_key, *orig_value;
 				gchar *obj;
 
-				e_cal_backend_store_put_component (priv->store, comp);
-
+				put_component_to_store (cbhttp, comp);
 				e_cal_component_get_uid (comp, &uid);
 				/* middle (gpointer) cast only because of 'dereferencing type-punned pointer will break strict-aliasing rules' */
 				if (g_hash_table_lookup_extended (old_cache, uid, (gpointer *)(gpointer)&orig_key, (gpointer *)(gpointer)&orig_value)) {
@@ -907,6 +936,8 @@ e_cal_backend_http_get_object_list (ECalBackendSync *backend, EDataCal *cal, con
 	ECalBackendHttpPrivate *priv;
 	GSList *components, *l;
 	ECalBackendSExp *cbsexp;
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time;
 
 	cbhttp = E_CAL_BACKEND_HTTP (backend);
 	priv = cbhttp->priv;
@@ -920,7 +951,14 @@ e_cal_backend_http_get_object_list (ECalBackendSync *backend, EDataCal *cal, con
 	cbsexp = e_cal_backend_sexp_new (sexp);
 
 	*objects = NULL;
-	components = e_cal_backend_store_get_components (priv->store);
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(cbsexp,
+									    &occur_start,
+									    &occur_end);
+
+	components = prunning_by_time ?
+		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+		: e_cal_backend_store_get_components (priv->store);
+
 	for (l = components; l != NULL; l = g_slist_next (l)) {
 		if (e_cal_backend_sexp_match_comp (cbsexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend))) {
 			*objects = g_list_append (*objects, e_cal_component_get_as_string (l->data));
@@ -941,6 +979,8 @@ e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query)
 	GSList *components, *l;
 	GList *objects = NULL;
 	ECalBackendSExp *cbsexp;
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time; 
 
 	cbhttp = E_CAL_BACKEND_HTTP (backend);
 	priv = cbhttp->priv;
@@ -958,7 +998,14 @@ e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query)
 	cbsexp = e_cal_backend_sexp_new (e_data_cal_view_get_text (query));
 
 	objects = NULL;
-	components = e_cal_backend_store_get_components (priv->store);
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(cbsexp,
+									    &occur_start,
+									    &occur_end);
+
+	components = prunning_by_time ?
+		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+		: e_cal_backend_store_get_components (priv->store);
+
 	for (l = components; l != NULL; l = g_slist_next (l)) {
 		if (e_cal_backend_sexp_match_comp (cbsexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend))) {
 			objects = g_list_append (objects, e_cal_component_get_as_string (l->data));
@@ -976,7 +1023,7 @@ e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query)
 	e_data_cal_view_notify_done (query, NULL /* Success */);
 }
 
-static icaltimezone *
+/***** static icaltimezone *
 resolve_tzid (const gchar *tzid, gpointer user_data)
 {
         icalcomponent *vcalendar_comp = user_data;
@@ -987,7 +1034,7 @@ resolve_tzid (const gchar *tzid, gpointer user_data)
                 return icaltimezone_get_utc_timezone ();
 
         return icalcomponent_get_timezone (vcalendar_comp, tzid);
-}
+} *****/
 
 static gboolean
 free_busy_instance (ECalComponent *comp,
diff --git a/calendar/backends/weather/e-cal-backend-weather.c b/calendar/backends/weather/e-cal-backend-weather.c
index ffc0a41..025b0b6 100644
--- a/calendar/backends/weather/e-cal-backend-weather.c
+++ b/calendar/backends/weather/e-cal-backend-weather.c
@@ -139,6 +139,30 @@ maybe_start_reload_timeout (ECalBackendWeather *cbw)
 
 }
 
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+	return (!strcmp (tzid, "UTC"))
+		? icaltimezone_get_utc_timezone ()
+		: icaltimezone_get_builtin_timezone_from_tzid (tzid);
+}
+
+static void
+put_component_to_store (ECalBackendWeather *cb,
+			ECalComponent *comp)
+{
+	time_t time_start, time_end;
+	ECalBackendWeatherPrivate *priv;
+
+	priv = cb->priv;
+
+	get_component_occur_times (comp, &time_start, &time_end,
+				   resolve_tzid, NULL, priv->default_zone,
+				   e_cal_backend_get_kind (E_CAL_BACKEND (cb)));
+
+	e_cal_backend_store_put_component (priv->store, comp, time_start, time_end);
+}
+
 static void
 finished_retrieval_cb (WeatherInfo *info, ECalBackendWeather *cbw)
 {
@@ -157,6 +181,7 @@ finished_retrieval_cb (WeatherInfo *info, ECalBackendWeather *cbw)
 
 	/* update cache */
 	l = e_cal_backend_store_get_components (priv->store);
+
 	for (; l != NULL; l = g_slist_next (l)) {
 		ECalComponentId *id;
 		gchar *obj;
@@ -181,7 +206,7 @@ finished_retrieval_cb (WeatherInfo *info, ECalBackendWeather *cbw)
 	if (comp) {
 		GSList *forecasts;
 
-		e_cal_backend_store_put_component (priv->store, comp);
+		put_component_to_store (cbw, comp);
 		icomp = e_cal_component_get_icalcomponent (comp);
 		obj = icalcomponent_as_ical_string_r (icomp);
 		e_cal_backend_notify_object_created (E_CAL_BACKEND (cbw), obj);
@@ -199,7 +224,7 @@ finished_retrieval_cb (WeatherInfo *info, ECalBackendWeather *cbw)
 				if (nfo) {
 					comp = create_weather (cbw, nfo, TRUE);
 					if (comp) {
-						e_cal_backend_store_put_component (priv->store, comp);
+						put_component_to_store (cbw, comp);
 						icomp = e_cal_component_get_icalcomponent (comp);
 						obj = icalcomponent_as_ical_string_r (icomp);
 						e_cal_backend_notify_object_created (E_CAL_BACKEND (cbw), obj);
@@ -487,6 +512,7 @@ e_cal_backend_weather_open (ECalBackendSync *backend, EDataCal *cal, gboolean on
 			g_propagate_error (perror, EDC_ERROR_EX (OtherError, _("Could not create cache file")));
 			return;
 		}
+	/* do we require to load this new store*/	
 		e_cal_backend_store_load (priv->store);
 
 		if (priv->default_zone) {
@@ -589,6 +615,8 @@ e_cal_backend_weather_get_object_list (ECalBackendSync *backend, EDataCal *cal,
 	ECalBackendWeatherPrivate *priv = cbw->priv;
 	ECalBackendSExp *sexp = e_cal_backend_sexp_new (sexp_string);
 	GSList *components, *l;
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time;
 
 	if (!sexp) {
 		g_propagate_error (perror, EDC_ERROR (InvalidQuery));
@@ -597,6 +625,14 @@ e_cal_backend_weather_get_object_list (ECalBackendSync *backend, EDataCal *cal,
 
 	*objects = NULL;
 	components = e_cal_backend_store_get_components (priv->store);
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(sexp,
+									    &occur_start,
+									    &occur_end);
+
+	components = prunning_by_time ?
+		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+		: e_cal_backend_store_get_components (priv->store);
+
 	for (l = components; l != NULL; l = g_slist_next (l)) {
 		if (e_cal_backend_sexp_match_comp (sexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend)))
 			*objects = g_list_append (*objects, e_cal_component_get_as_string (l->data));
@@ -717,6 +753,8 @@ static void e_cal_backend_weather_start_query (ECalBackend *backend, EDataCalVie
 	GSList *components, *l;
 	GList *objects;
 	GError *error;
+	time_t occur_start = -1, occur_end = -1;
+	gboolean prunning_by_time;
 
 	cbw = E_CAL_BACKEND_WEATHER (backend);
 	priv = cbw->priv;
@@ -737,9 +775,13 @@ static void e_cal_backend_weather_start_query (ECalBackend *backend, EDataCalVie
 	}
 
 	objects = NULL;
-	components = e_cal_backend_store_get_components (priv->store);
+	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times(sexp, &occur_start, &occur_end);
+	components = prunning_by_time ?
+		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
+		: e_cal_backend_store_get_components (priv->store);
+
 	for (l = components; l != NULL; l = g_slist_next (l)) {
-		if (e_cal_backend_sexp_match_comp (sexp, E_CAL_COMPONENT (l->data), backend))
+	 	if (e_cal_backend_sexp_match_comp (sexp, E_CAL_COMPONENT (l->data), backend))
 			objects = g_list_append (objects, e_cal_component_get_as_string (l->data));
 	}
 
diff --git a/calendar/libecal/e-cal-recur.c b/calendar/libecal/e-cal-recur.c
index 5d80b66..c504faf 100644
--- a/calendar/libecal/e-cal-recur.c
+++ b/calendar/libecal/e-cal-recur.c
@@ -907,6 +907,65 @@ array_to_list (gshort *array, gint max_elements)
 	return g_list_reverse (l);
 }
 
+/** 
+ * e_cal_recur_get_enddate
+ * 
+ * @ir: RRULE or EXRULE recurrence 
+ * @prop: An RRULE or EXRULE #icalproperty. 
+ * @zone: The DTSTART timezone, used for converting the UNTIL property if it
+ * is given as a DATE value.
+ * @convert_end_date: TRUE if the saved end date needs to be converted to the
+ * given @zone timezone. This is needed if the DTSTART is a DATE or floating
+ * time.
+ * 
+ * @return End timepoint of giben event
+ *
+ * Finds out end time of (reccurent) event.
+ */
+time_t
+e_cal_recur_obtain_enddate (struct icalrecurrencetype *ir, icalproperty *prop,
+          icaltimezone *zone, gboolean convert_end_date)
+{
+	time_t enddate = -1;
+
+	g_return_val_if_fail (prop != NULL, 0);
+	g_return_val_if_fail (ir != NULL, 0);
+
+	if (ir->count != 0) {
+		/* If COUNT is set, we use the pre-calculated enddate.
+		Note that this can be 0 if the RULE doesn't actually
+		generate COUNT instances. */
+		enddate = e_cal_recur_get_rule_end_date (prop, convert_end_date ? zone : NULL);
+	} else {
+		if (icaltime_is_null_time (ir->until)) {
+			/* If neither COUNT or UNTIL is set, the event
+			recurs forever. */
+		} else if (ir->until.is_date) {
+			/* If UNTIL is a DATE, we stop at the end of
+			the day, in local time (with the DTSTART timezone).
+			Note that UNTIL is inclusive so we stop before
+			midnight. */
+			ir->until.hour = 23;
+			ir->until.minute = 59;
+			ir->until.second = 59;
+			ir->until.is_date = FALSE;
+
+			enddate = icaltime_as_timet_with_zone (ir->until, zone);
+#if 0
+	g_print ("  until: %li - %s", r->enddate, ctime (&r->enddate));
+#endif
+
+		} else {
+		/* If UNTIL is a DATE-TIME, it must be in UTC. */
+		icaltimezone *utc_zone;
+		utc_zone = icaltimezone_get_utc_timezone ();
+		enddate = icaltime_as_timet_with_zone (ir->until, utc_zone);
+		}
+	}
+
+	return enddate;
+}
+
 /**
  * e_cal_recur_from_icalproperty:
  * @prop: An RRULE or EXRULE #icalproperty.
@@ -943,40 +1002,7 @@ e_cal_recur_from_icalproperty (icalproperty *prop, gboolean exception,
 	r->freq = ir.freq;
 	r->interval = ir.interval;
 
-	if (ir.count != 0) {
-		/* If COUNT is set, we use the pre-calculated enddate.
-		   Note that this can be 0 if the RULE doesn't actually
-		   generate COUNT instances. */
-		r->enddate = e_cal_recur_get_rule_end_date (prop, convert_end_date ? zone : NULL);
-	} else {
-		if (icaltime_is_null_time (ir.until)) {
-			/* If neither COUNT or UNTIL is set, the event
-			   recurs forever. */
-			r->enddate = 0;
-		} else if (ir.until.is_date) {
-			/* If UNTIL is a DATE, we stop at the end of
-			   the day, in local time (with the DTSTART timezone).
-			   Note that UNTIL is inclusive so we stop before
-			   midnight. */
-			ir.until.hour = 23;
-			ir.until.minute = 59;
-			ir.until.second = 59;
-			ir.until.is_date = FALSE;
-
-			r->enddate = icaltime_as_timet_with_zone (ir.until,
-								  zone);
-#if 0
-			g_print ("  until: %li - %s", r->enddate, ctime (&r->enddate));
-#endif
-
-		} else {
-			/* If UNTIL is a DATE-TIME, it must be in UTC. */
-			icaltimezone *utc_zone;
-			utc_zone = icaltimezone_get_utc_timezone ();
-			r->enddate = icaltime_as_timet_with_zone (ir.until,
-								  utc_zone);
-		}
-	}
+  r->enddate = e_cal_recur_obtain_enddate (&ir, prop, zone, convert_end_date);
 
 	r->week_start_day = e_cal_recur_ical_weekday_to_weekday (ir.week_start);
 
diff --git a/calendar/libecal/e-cal-recur.h b/calendar/libecal/e-cal-recur.h
index f80d3c2..7af97c5 100644
--- a/calendar/libecal/e-cal-recur.h
+++ b/calendar/libecal/e-cal-recur.h
@@ -45,6 +45,12 @@ void	e_cal_recur_generate_instances	(ECalComponent		*comp,
 					 gpointer		   tz_cb_data,
 					 icaltimezone		*default_timezone);
 
+time_t
+e_cal_recur_obtain_enddate (struct icalrecurrencetype *ir,
+                            icalproperty *prop,
+                            icaltimezone *zone,
+                            gboolean convert_end_date);
+
 /* Localized nth-day-of-month strings. (Use with _() ) */
 #ifdef G_OS_WIN32
 extern const gchar **e_cal_get_recur_nth (void);
diff --git a/calendar/libecal/e-cal-util.c b/calendar/libecal/e-cal-util.c
index b7e60c1..e7a8720 100644
--- a/calendar/libecal/e-cal-util.c
+++ b/calendar/libecal/e-cal-util.c
@@ -28,7 +28,8 @@
 #include "e-cal-util.h"
 #include "e-cal-system-timezone.h"
 
-
+#define _TIME_MIN	((time_t) 0)		/* Min valid time_t	*/
+#define _TIME_MAX	((time_t) INT_MAX)
 
 /**
  * cal_obj_instance_list_free:
@@ -424,6 +425,7 @@ add_alarm_occurrences_cb (ECalComponent *comp, time_t start, time_t end, gpointe
 	return TRUE;
 }
 
+
 /* Generates the absolute triggers for a component */
 static void
 generate_absolute_triggers (ECalComponent *comp, struct alarm_occurrence_data *aod,
@@ -1166,3 +1168,162 @@ e_cal_util_get_system_timezone (void)
 
 	return zone;
 }
+
+static time_t
+componenttime_to_utc_timet (const ECalComponentDateTime* dt_time,
+			    ECalRecurResolveTimezoneFn tz_cb,
+			    gpointer tz_cb_data,
+			    const icaltimezone *default_zone)
+{
+	time_t timet = -1;
+	icaltimezone *zone = NULL;
+
+	g_return_val_if_fail (dt_time != NULL, -1);
+
+	if (dt_time->value) {
+		if (dt_time->tzid)
+			zone = tz_cb(dt_time->tzid, tz_cb_data);
+
+		// zone = icaltimezone_get_utc_timezone ();
+		timet = icaltime_as_timet_with_zone (*dt_time->value, zone ? zone : default_zone);
+	}
+
+
+	return timet;
+}
+
+/**
+ * Find out, when the component starts and stops. Be careful about recurrences.
+ */
+void
+get_component_occur_times (ECalComponent *comp,
+			   time_t* start,
+			   time_t* end,
+			   ECalRecurResolveTimezoneFn tz_cb,
+			   gpointer tz_cb_data,
+			   const icaltimezone *default_timezone,
+			   icalcomponent_kind kind)
+{
+	struct icalrecurrencetype ir;
+	ECalComponentDateTime dt_start, dt_end;
+	GSList *rrules = NULL, *exrules = NULL, *elem, *rdates = NULL;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (start != NULL);
+	g_return_if_fail (end != NULL);
+
+	/* Get dtstart of the component and convert it to UTC */
+	e_cal_component_get_dtstart (comp, &dt_start);
+
+	if ( (*start = componenttime_to_utc_timet (&dt_start, tz_cb, tz_cb_data, default_timezone)) == -1)
+		*start = _TIME_MIN;
+
+	e_cal_component_free_datetime (&dt_start);
+
+	/* find out end date of component */
+	*end = _TIME_MAX;
+
+	if (kind == ICAL_VTODO_COMPONENT) {
+		/* max from COMPLETED and DUE properties */
+		struct icaltimetype *tt = NULL;
+		time_t completed_time = -1, due_time = -1, max_time;
+		ECalComponentDateTime dt_due;
+
+		e_cal_component_get_completed (comp, &tt);
+		if (tt) {
+			/* COMPLETED must be in UTC. */
+			completed_time = icaltime_as_timet_with_zone (*tt,
+								      icaltimezone_get_utc_timezone());
+			e_cal_component_free_icaltimetype (tt);
+		}
+
+		e_cal_component_get_due (comp, &dt_due);
+		if(dt_due.value != NULL)
+			due_time = componenttime_to_utc_timet (&dt_due, tz_cb, tz_cb_data,
+							       default_timezone);
+
+		e_cal_component_free_datetime (&dt_due);
+
+		max_time = MAX(completed_time, due_time);
+
+		if (max_time != -1)
+			*end = max_time;
+
+	} else {
+		/* ALARMS, EVENTS: DTEND and reccurences */
+
+		if (e_cal_component_has_recurrences (comp)) {
+			/* Do the RRULEs, EXRULEs and RDATEs*/
+			e_cal_component_get_rrule_property_list (comp, &rrules);
+			e_cal_component_get_exrule_property_list (comp, &exrules);
+			e_cal_component_get_rdate_list (comp, &rdates);
+
+			for (elem = rrules; elem; elem = elem->next) {
+				time_t rule_end;
+				icaltimezone *utc_zone;
+				icalproperty *prop = elem->data;
+				ir = icalproperty_get_rrule (prop);
+
+				utc_zone = icaltimezone_get_utc_timezone ();
+				rule_end = e_cal_recur_obtain_enddate (&ir, prop, utc_zone, TRUE);
+
+				if (rule_end == -1) /* repeats forever */
+					*end = -1;
+				else if (rule_end > *end) /* new maximum */
+					*end = rule_end;
+			}
+
+			/* Do the EXRULEs. */
+			for (elem = exrules; elem; elem = elem->next) {
+				icalproperty *prop = elem->data;
+				time_t rule_end;
+				icaltimezone *utc_zone;
+				ir = icalproperty_get_exrule (prop);
+
+				utc_zone = icaltimezone_get_utc_timezone ();
+				rule_end =
+					e_cal_recur_obtain_enddate (&ir, prop, utc_zone, TRUE);
+
+				if (rule_end == -1) /* repeats forever */
+					*end = _TIME_MAX;
+				else if (rule_end > *end)
+					*end = rule_end;
+			}
+
+			/* Do the RDATEs */
+			for (elem = rdates; elem; elem = elem->next) {
+				ECalComponentPeriod *p = elem->data;
+				time_t rdate_end = _TIME_MAX;
+
+				/* FIXME: We currently assume RDATEs are in the same timezone
+				   as DTSTART. We should get the RDATE timezone and convert
+				   to the DTSTART timezone first. */
+
+				/* Check if the end date or duration is set, libical seems to set
+				   second to -1 to denote an unset time */
+				if (p->type != E_CAL_COMPONENT_PERIOD_DATETIME || p->u.end.second != -1)
+					rdate_end = icaltime_as_timet (icaltime_add(p->start, p->u.duration));
+				else
+					rdate_end = icaltime_as_timet (p->u.end);
+
+				if (rdate_end == -1) /* repeats forever */
+					*end = _TIME_MAX;
+				else if (rdate_end > *end)
+					*end = rdate_end;
+			}
+		}
+
+		/* Get dtend of the component and convert it to UTC */
+		e_cal_component_get_dtend (comp, &dt_end);
+
+		if (dt_end.value) {
+			time_t dtend_time = componenttime_to_utc_timet (&dt_end, tz_cb, tz_cb_data, default_timezone);
+
+			if (dtend_time == -1 || (dtend_time > *end))
+				*end = dtend_time;
+		}
+
+		e_cal_component_free_datetime (&dt_end);
+	}
+}
+
diff --git a/calendar/libecal/e-cal-util.h b/calendar/libecal/e-cal-util.h
index c63e76b..5de7400 100644
--- a/calendar/libecal/e-cal-util.h
+++ b/calendar/libecal/e-cal-util.h
@@ -151,6 +151,13 @@ void           e_cal_util_remove_instances (icalcomponent *icalcomp,
 
 gchar *e_cal_util_get_system_timezone_location (void);
 icaltimezone *e_cal_util_get_system_timezone (void);
+void get_component_occur_times (ECalComponent *comp,
+				   time_t* start,
+				   time_t* end,
+				   ECalRecurResolveTimezoneFn tz_cb,
+				   gpointer tz_cb_data,
+				   const icaltimezone *default_timezone,
+				   icalcomponent_kind kind);
 
 G_END_DECLS
 
diff --git a/calendar/libedata-cal/Makefile.am b/calendar/libedata-cal/Makefile.am
index 01527e6..adbe4b2 100644
--- a/calendar/libedata-cal/Makefile.am
+++ b/calendar/libedata-cal/Makefile.am
@@ -7,6 +7,7 @@ ENUM_GENERATED = e-data-cal-enumtypes.h e-data-cal-enumtypes.c
 
 # The libraray
 lib_LTLIBRARIES = libedata-cal-1.2.la
+noinst_PROGRAMS = test-e-sexp test-intervaltree test-intervaltree-coverage
 
 libedata_cal_1_2_la_CPPFLAGS = 			\
 	$(AM_CPPFLAGS)				\
@@ -25,6 +26,7 @@ libedata_cal_1_2_la_SOURCES =		\
 	e-cal-backend.c			\
 	e-cal-backend-cache.c		\
 	e-cal-backend-factory.c		\
+	e-cal-backend-intervaltree.c	\
 	e-cal-backend-sexp.c		\
 	e-cal-backend-sync.c		\
 	e-cal-backend-util.c		\
@@ -41,7 +43,7 @@ libedata_cal_1_2_la_LIBADD =					\
 	$(LIBICAL_LIBS)						\
 	$(EVOLUTION_CALENDAR_LIBS)
 
-libedata_cal_1_2_la_LDFLAGS = 								\
+libedata_cal_1_2_la_LDFLAGS =								\
 	-version-info $(LIBEDATACAL_CURRENT):$(LIBEDATACAL_REVISION):$(LIBEDATACAL_AGE) $(NO_UNDEFINED)
 
 libedata_calincludedir = $(privincludedir)/libedata-cal
@@ -51,6 +53,7 @@ libedata_calinclude_HEADERS = 		\
 	e-cal-backend.h			\
 	e-cal-backend-cache.h		\
 	e-cal-backend-factory.h		\
+	e-cal-backend-intervaltree.h	\
 	e-cal-backend-sync.h		\
 	e-cal-backend-util.h		\
 	e-cal-backend-sexp.h		\
@@ -64,6 +67,56 @@ libedata_calinclude_HEADERS = 		\
 %-$(API_VERSION).pc: %.pc
 	 cp $< $@
 
+test_e_sexp_SOURCES = e-cal-backend-sexp.c e-cal-backend-sexp.h
+test_e_sexp_CPPFLAGS = \
+	$(AM_CPPFLAGS)				\
+	-I$(top_srcdir)/calendar		\
+	-I$(top_builddir)/calendar		\
+	$(EVOLUTION_CALENDAR_CFLAGS)		\
+	-DTESTER=1
+
+test_e_sexp_LDADD = libedata-cal-1.2.la $(E_DATA_SERVER_LIBS)
+
+test_intervaltree_SOURCES = test-intervaltree.c e-cal-backend-intervaltree.c
+
+test_intervaltree_CPPFLAGS = \
+	$(AM_CPPFLAGS)				\
+	-I$(top_srcdir)/calendar		\
+	-I$(top_builddir)/calendar		\
+	$(EVOLUTION_CALENDAR_CFLAGS)
+
+test_intervaltree_LDADD = \
+	$(top_builddir)/calendar/libecal/libecal-1.2.la				\
+	$(top_builddir)/calendar/libedata-cal/libedata-cal-1.2.la		\
+	$(top_builddir)/libedataserver/libedataserver-1.2.la			\
+	$(EVOLUTION_CALENDAR_LIBS)
+
+test_intervaltree_coverage_SOURCES = test-intervaltree.c e-cal-backend-intervaltree.c
+
+test_intervaltree_coverage_CPPFLAGS = \
+	$(AM_CPPFLAGS)				\
+	-I$(top_srcdir)/calendar		\
+	-I$(top_builddir)/calendar		\
+	$(EVOLUTION_CALENDAR_CFLAGS)
+
+test_intervaltree_coverage_LDADD = \
+	$(top_builddir)/calendar/libecal/libecal-1.2.la				\
+	$(top_builddir)/calendar/libedata-cal/libedata-cal-1.2.la		\
+	$(top_builddir)/libedataserver/libedataserver-1.2.la			\
+	$(EVOLUTION_CALENDAR_LIBS)						\
+	-lgcov
+
+.PHONY: coverage
+coverage: 
+	mkdir -p ./coverage
+	lcov --directory . --zerocounters
+	./test-intervaltree-coverage
+	lcov --directory . --capture --output-file ./coverage/*.info
+	genhtml -o ./coverage --legend --num-spaces 2 ./coverage/*.info
+
+clean-local:
+	rm -f *.gcda *.gcno
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libedata-cal-$(API_VERSION).pc
 
diff --git a/calendar/libedata-cal/e-cal-backend-cache.c b/calendar/libedata-cal/e-cal-backend-cache.c
index dd242d4..bbaffb0 100644
--- a/calendar/libedata-cal/e-cal-backend-cache.c
+++ b/calendar/libedata-cal/e-cal-backend-cache.c
@@ -29,6 +29,7 @@
 #include <libecal/e-cal-util.h>
 #include <libedataserver/e-data-server-util.h>
 #include "e-cal-backend-cache.h"
+#include "e-cal-backend-intervaltree.h"
 
 #define E_CAL_BACKEND_CACHE_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -36,6 +37,7 @@
 
 struct _ECalBackendCachePrivate {
 	GHashTable *timezones;
+	EIntervalTree *intervaltree;
 };
 
 G_DEFINE_TYPE (ECalBackendCache, e_cal_backend_cache, E_TYPE_FILE_CACHE)
@@ -46,7 +48,10 @@ e_cal_backend_cache_finalize (GObject *object)
 	ECalBackendCachePrivate *priv;
 
 	priv = E_CAL_BACKEND_CACHE_GET_PRIVATE (object);
-
+	if (priv->intervaltree) {
+		e_intervaltree_destroy (priv->intervaltree);
+		priv->intervaltree = NULL;
+	}
 	g_hash_table_destroy (priv->timezones);
 
 	/* Chain up to parent's finalize() method. */
@@ -79,6 +84,8 @@ e_cal_backend_cache_init (ECalBackendCache *cache)
 		g_str_hash, g_str_equal,
 		(GDestroyNotify) g_free,
 		(GDestroyNotify) timezones_value_destroy);
+
+	cache->priv->intervaltree = e_intervaltree_new ();
 }
 
 /**
@@ -174,12 +181,14 @@ e_cal_backend_cache_get_component (ECalBackendCache *cache, const gchar *uid, co
  */
 gboolean
 e_cal_backend_cache_put_component (ECalBackendCache *cache,
-				   ECalComponent *comp)
+				   ECalComponent *comp,
+				   time_t occurence_start,
+				   time_t occurence_end)
 {
 	gchar *real_key, *uid, *comp_str;
 	gchar *rid;
 	gboolean retval;
-
+	ECalBackendCachePrivate *priv = cache->priv;
 	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
 
@@ -197,6 +206,8 @@ e_cal_backend_cache_put_component (ECalBackendCache *cache,
 	else
 		retval = e_file_cache_add_object (E_FILE_CACHE (cache), real_key, comp_str);
 
+	e_intervaltree_insert (priv->intervaltree, occurence_start, occurence_end, comp);
+
 	g_free (real_key);
 	g_free (comp_str);
 	g_free (rid);
@@ -238,6 +249,9 @@ e_cal_backend_cache_remove_component (ECalBackendCache *cache,
 	retval = e_file_cache_remove_object (E_FILE_CACHE (cache), real_key);
 	g_free (real_key);
 
+	if (retval)
+		retval = e_intervaltree_remove (priv->intervaltree, uid, rid);
+
 	return retval;
 }
 
@@ -291,6 +305,47 @@ e_cal_backend_cache_get_components (ECalBackendCache *cache)
 }
 
 /**
+ * e_cal_backend_cache_get_components_occuring_in_range:
+ * @cache: An #ECalBackendCache object.
+ * @start:
+ * @end:
+ *
+ * Retrieves a list of components stored in the cache, that are occuring
+ * in time range [start, end].
+ *
+ * Return value: A list of the components. Each item in the list is
+ * an #ECalComponent, which should be freed when no longer needed.
+ */
+GList *
+e_cal_backend_cache_get_components_occuring_in_range (ECalBackendCache *cache, time_t start, time_t end)
+{
+        GList *l;
+        GList *list = NULL;
+	icalcomponent *icalcomp;
+	ECalBackendCachePrivate *priv;
+	/* return null if cache is not a valid Backend Cache.  */
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+	priv = cache->priv;
+	if (!(l = e_intervaltree_search (priv->intervaltree, start, end)))
+		return NULL;
+
+	for ( ; l != NULL; l = g_list_next (l)) {
+		ECalComponent *comp = l->data;
+		icalcomp = e_cal_component_get_icalcomponent (comp);
+		if (icalcomp) {
+			icalcomponent_kind kind;
+			kind = icalcomponent_isa (icalcomp);
+			if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT || kind == ICAL_VJOURNAL_COMPONENT) {
+				list = g_list_prepend (list, comp);
+			}
+		}
+	}
+        return list;
+}
+
+
+
+/**
  * e_cal_backend_cache_get_components_by_uid:
  * @cache: An #ECalBackendCache object.
  * @uid: ID of the component to retrieve.
diff --git a/calendar/libedata-cal/e-cal-backend-cache.h b/calendar/libedata-cal/e-cal-backend-cache.h
index d699fd1..ba7202d 100644
--- a/calendar/libedata-cal/e-cal-backend-cache.h
+++ b/calendar/libedata-cal/e-cal-backend-cache.h
@@ -69,7 +69,9 @@ ECalComponent *	e_cal_backend_cache_get_component
 						 const gchar *rid);
 gboolean	e_cal_backend_cache_put_component
 						(ECalBackendCache *cache,
-						 ECalComponent *comp);
+						ECalComponent *compi,
+						time_t occurence_start,
+						time_t occurence_end);
 gboolean	e_cal_backend_cache_remove_component
 						(ECalBackendCache *cache,
 						 const gchar *uid,
@@ -79,6 +81,7 @@ GList *		e_cal_backend_cache_get_components
 GSList *	e_cal_backend_cache_get_components_by_uid
 						(ECalBackendCache *cache,
 						 const gchar *uid);
+GList		   *e_cal_backend_cache_get_components_occuring_in_range (ECalBackendCache *cache, time_t start, time_t end);
 const icaltimezone *
 		e_cal_backend_cache_get_timezone(ECalBackendCache *cache,
 						 const gchar *tzid);
diff --git a/calendar/libedata-cal/e-cal-backend-file-store.c b/calendar/libedata-cal/e-cal-backend-file-store.c
index f1ac17b..bdf8c89 100644
--- a/calendar/libedata-cal/e-cal-backend-file-store.c
+++ b/calendar/libedata-cal/e-cal-backend-file-store.c
@@ -23,6 +23,7 @@
 #include "e-cal-backend-file-store.h"
 #include "libebackend/e-file-cache.h"
 #include <glib/gstdio.h>
+#include "libecal/e-cal-util.c" 
 
 #define CACHE_FILE_NAME "calendar.ics"
 #define KEY_FILE_NAME "keys.xml"
@@ -117,11 +118,9 @@ put_component (ECalBackendFileStore *fstore, ECalComponent *comp)
 		g_warning ("The component does not have a valid uid \n");
 		return FALSE;
 	}
-
+	
 	g_static_rw_lock_writer_lock (&priv->lock);
-
 	obj = g_hash_table_lookup (priv->comp_uid_hash, uid);
-
 	if (obj == NULL) {
 		obj = create_new_full_object ();
 		g_hash_table_insert (priv->comp_uid_hash, g_strdup (uid), obj);
@@ -132,14 +131,13 @@ put_component (ECalBackendFileStore *fstore, ECalComponent *comp)
 			g_object_unref (obj->comp);
 
 		obj->comp = comp;
-		g_object_ref (comp);
 	} else {
 		gchar *rid = e_cal_component_get_recurid_as_string (comp);
 
-		g_object_ref (comp);
 		g_hash_table_insert (obj->recurrences, rid, comp);
 	}
 
+	g_object_ref (comp);
 	g_static_rw_lock_writer_unlock (&priv->lock);
 
 	return TRUE;
@@ -156,7 +154,7 @@ remove_component (ECalBackendFileStore *fstore, const gchar *uid, const gchar *r
 	priv = E_CAL_BACKEND_FILE_STORE_GET_PRIVATE (fstore);
 
 	g_static_rw_lock_writer_lock (&priv->lock);
-
+	
 	obj = g_hash_table_lookup (priv->comp_uid_hash, uid);
 	if (obj == NULL) {
 		ret_val = FALSE;
@@ -173,10 +171,12 @@ remove_component (ECalBackendFileStore *fstore, const gchar *uid, const gchar *r
 
 	if (remove_completely)
 		g_hash_table_remove (priv->comp_uid_hash, uid);
-
+	
 end:
 	g_static_rw_lock_writer_unlock (&priv->lock);
+	
 	return ret_val;
+		
 }
 
 static ECalComponent *
@@ -608,11 +608,36 @@ add_timezone (ECalBackendFileStore *fstore, icalcomponent *vtzcomp)
 	g_static_rw_lock_writer_unlock (&priv->lock);
 }
 
+static icaltimezone *
+resolve_tzid (const char *tzid, gpointer user_data)
+{
+	return (!strcmp (tzid, "UTC"))
+		? icaltimezone_get_utc_timezone ()
+		: icaltimezone_get_builtin_timezone_from_tzid (tzid);
+}
+
+/*static icaltimezone * 
+get_zone (icalcomponent *icalcomp)
+{
+	icalproperty *prop;
+	icaltimezone *zone;
+	const gchar *tzid;
+	prop = icalcomponent_get_first_property (icalcomp, ICAL_TZID_PROPERTY);
+	if (!prop)
+		return NULL;
+	tzid = icalproperty_get_tzid (prop);
+	zone = icalcomponent_get_timezone (icalcomp, tzid);
+	return zone;
+} */
+
+
 static void
-scan_vcalendar (ECalBackendFileStore *fstore, icalcomponent *top_icalcomp)
+scan_vcalendar (ECalBackendStore *store, icalcomponent *top_icalcomp)
 {
+	ECalBackendFileStore *fstore = E_CAL_BACKEND_FILE_STORE (store);
 	ECalBackendFileStorePrivate *priv;
 	icalcompiter iter;
+	time_t time_start, time_end;
 
 	priv = E_CAL_BACKEND_FILE_STORE_GET_PRIVATE (fstore);
 
@@ -622,7 +647,6 @@ scan_vcalendar (ECalBackendFileStore *fstore, icalcomponent *top_icalcomp)
 		icalcomponent *icalcomp;
 		icalcomponent_kind kind;
 		ECalComponent *comp;
-
 		icalcomp = icalcompiter_deref (&iter);
 
 		kind = icalcomponent_isa (icalcomp);
@@ -644,8 +668,10 @@ scan_vcalendar (ECalBackendFileStore *fstore, icalcomponent *top_icalcomp)
 			g_object_unref (comp);
 			continue;
 		}
+		get_component_occur_times (comp, &time_start, &time_end,
+						resolve_tzid, NULL, NULL, kind);
 
-		put_component (fstore, comp);
+		e_cal_backend_store_put_component (store, comp, time_start, time_end);
 
 		g_object_unref (comp);
 	}
@@ -664,7 +690,7 @@ e_cal_backend_file_store_load (ECalBackendStore *store)
 		return FALSE;
 
 	/* Parse keys */
-	priv->keys_cache = e_file_cache_new (priv->key_file_name);
+	//priv->keys_cache = e_file_cache_new (priv->key_file_name);
 
 	/* Parse components */
 	icalcomp = e_cal_util_parse_ics_file (priv->cache_file_name);
@@ -676,8 +702,7 @@ e_cal_backend_file_store_load (ECalBackendStore *store)
 
 		return FALSE;
 	}
-
-	scan_vcalendar (fstore, icalcomp);
+	scan_vcalendar (store, icalcomp);
 	icalcomponent_free (icalcomp);
 
 	return TRUE;
@@ -881,6 +906,7 @@ cal_backend_file_store_constructed (GObject *object)
 	path = e_cal_backend_store_get_path (E_CAL_BACKEND_STORE (object));
 	priv->cache_file_name = g_build_filename (path, CACHE_FILE_NAME, NULL);
 	priv->key_file_name = g_build_filename (path, KEY_FILE_NAME, NULL);
+	priv->keys_cache = e_file_cache_new (priv->key_file_name);
 }
 
 static void
diff --git a/calendar/libedata-cal/e-cal-backend-intervaltree.c b/calendar/libedata-cal/e-cal-backend-intervaltree.c
new file mode 100644
index 0000000..6e5f936
--- /dev/null
+++ b/calendar/libedata-cal/e-cal-backend-intervaltree.c
@@ -0,0 +1,765 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Stanislav Slusny <slusnys gmail com>
+ *
+ *  Copyright 2008
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+
+#include "e-cal-backend-intervaltree.h"
+
+#define d(x) x
+
+#define _TIME_MIN	((time_t) 0)		/* Min valid time_t	*/
+#define _TIME_MAX	((time_t) INT_MAX)	/* Max valid time_t	*/
+#define DIRECTION_GO_LEFT 0
+#define DIRECTION_GO_RIGHT 1
+
+G_DEFINE_TYPE (EIntervalTree, e_intervaltree, G_TYPE_OBJECT)
+
+#define E_INTERVALTREE_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_INTERVALTREE, EIntervalTreePrivate))
+
+EIntervalNode* 
+e_intervaltree_node_next (EIntervalTree *tree, EIntervalNode *x);
+
+struct _EIntervalNode
+{
+	/* start of the interval - the key of the node */
+	time_t start;
+	/* end of the interval */
+	time_t end;
+	/* maximum value of any interval stored in subtree rooted at node */
+	time_t max;
+	/* minimum value of any interval stored in subtree rooted at node */
+	time_t min;
+	/* color of the node (red or black) */
+	gboolean red;
+
+	ECalComponent *comp;
+
+	/* left child */
+	EIntervalNode* left;
+	/* right child */
+	EIntervalNode* right;
+	EIntervalNode* parent;
+};
+
+
+struct _EIntervalTreePrivate
+{
+	EIntervalNode *root;
+	EIntervalNode *nil;
+	GHashTable *id_node_hash;
+	GStaticRecMutex mutex;	
+};
+
+static inline int
+get_direction (EIntervalNode *x, time_t z_start, time_t z_end)
+{
+	if (x->start == z_start)
+		return x->end > z_end;
+
+	if (x->start > z_start)
+		return DIRECTION_GO_LEFT;
+	else
+		return DIRECTION_GO_RIGHT;
+}
+
+static inline gchar *
+component_key(const gchar *uid, const gchar *rid)
+{
+	return g_strdup_printf("%s_%s", uid, rid);
+}
+
+/**
+ * compare_intervals:
+ *
+ * Compares two intervals.
+ *
+ * Returns: 0 if interval overlaps, -1 if first interval ends before
+ * the second starts, 1 otherwise.
+ *
+ **/
+static inline int
+compare_intervals (time_t x_start, time_t x_end, time_t y_start, time_t y_end)
+{
+	/* assumption: x_start <= x_end */
+	/* assumption: y_start <= y_end */
+
+	/* x is left of y */
+	if (x_end < y_start)
+		return -1;
+
+	/* x is right of y */
+	if (y_end < x_start) 
+		return 1;
+
+	/* x and y overlap */
+	return 0;
+}
+
+/**
+ * left_rotate:
+ * @tree: interval tree
+ * @x: Node, where will be applied the operation
+ * 
+ * Carry out left rotation on the node @x in tree @tree.
+ **/
+static void 
+left_rotate (EIntervalTree *tree, EIntervalNode *x)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	EIntervalNode *y;
+	EIntervalNode *nil = priv->nil;
+
+	g_return_if_fail (tree != NULL);
+	g_return_if_fail (x != NULL);
+
+	y = x->right;
+	x->right = y->left;
+
+	if (y->left != nil)
+		y->left->parent = x;
+
+	y->parent = x->parent;   
+
+	/* instead of checking if x->parent is the root as in the book, we */
+	/* count on the root sentinel to implicitly take care of this case */
+	if (x == x->parent->left)
+		x->parent->left = y;
+	else
+		x->parent->right = y;
+
+	y->left = x;
+	x->parent = y;
+
+	/* update max and min field */
+	x->max = MAX (x->left->max, MAX (x->end, x->right->max));
+	y->max = MAX (x->max, MAX (y->end, y->right->max));
+	x->min = MIN (x->left->min, x->start);
+	y->min = MIN (x->min, y->start);
+}
+
+/**
+ * right_rotate:
+ * @tree: interval tree
+ * @y: Node, where will be applied the operation
+ * 
+ * Carry out right rotation on the node @y in tree @tree.
+ **/
+static void
+right_rotate (EIntervalTree *tree, EIntervalNode *y)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	EIntervalNode *x;
+	EIntervalNode *nil = priv->nil;
+
+	g_return_if_fail (tree != NULL);
+	g_return_if_fail (y != NULL);
+
+	x = y->left;
+	y->left = x->right;
+
+	if (nil != x->right)
+		x->right->parent = y;
+
+	x->parent = y->parent;
+
+	if (y == y->parent->left)
+		y->parent->left = x;
+	else
+		y->parent->right = x;
+
+	x->right = y;
+	y->parent = x;
+
+	/* update max and min field */
+	y->max = MAX (y->left->max, MAX (y->right->max, y->end));
+	x->max = MAX (x->left->max, MAX (y->max, x->end));
+	y->min = MIN (y->left->min, y->start);
+	x->min = MIN (x->left->min, x->start);
+}
+
+static void
+fixup_min_max_fields (EIntervalTree *tree, EIntervalNode *node)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	while (node != priv->root)
+	{
+		node->max = MAX (node->end, MAX (node->left->max, node->right->max));
+		node->min = MIN (node->start, node->left->min);
+
+		node = node->parent;
+	}
+}
+
+static void
+binary_tree_insert (EIntervalTree *tree, EIntervalNode *z)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	EIntervalNode *x;
+	EIntervalNode *y;
+	EIntervalNode *nil = priv->nil;
+
+	g_return_if_fail (tree != NULL);
+	g_return_if_fail (z != NULL);
+
+	z->left = z->right = nil;
+	y = priv->root;
+	x = priv->root->left;
+
+	while( x != nil)
+	{
+		y = x;
+
+		if (get_direction (x, z->start, z->end) == DIRECTION_GO_LEFT)
+			x = x->left;
+		else
+			x = x->right;
+	}
+
+	z->parent = y;
+
+	if ( (y == priv->root) || (get_direction (y, z->start, z->end) == DIRECTION_GO_LEFT))
+		y->left = z;
+	else
+		y->right = z;
+
+	/* update min and max fields */
+	y->min = MIN (y->left->min, y->start);
+	y->max = MAX (y->left->max, MAX (y->end, y->right->max));
+}
+
+/**
+ * e_intervaltree_insert:
+ * @tree: interval tree
+ * @key: the key to insert.
+ * @comp: Component
+ * 
+ **/
+gboolean
+e_intervaltree_insert (EIntervalTree *tree, time_t start, time_t end, ECalComponent *comp)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	EIntervalNode *y;
+	EIntervalNode *x;
+	EIntervalNode *newNode;
+	const gchar *uid;
+	gchar *rid;	
+	g_return_val_if_fail (tree != NULL, FALSE);
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	g_static_rec_mutex_lock (&priv->mutex);
+        x = g_new (EIntervalNode, 1); 
+	x->min = x->start = start;
+	x->max = x->end = end;
+	x->comp = g_object_ref (comp);
+
+	binary_tree_insert (tree, x);
+	newNode = x;
+	x->red = TRUE;
+
+	fixup_min_max_fields (tree, x->parent);
+	while (x->parent->red)
+	{ /* use sentinel instead of checking for root */
+		if (x->parent == x->parent->parent->left)
+		{
+			y = x->parent->parent->right;
+
+			if (y->red)
+			{
+				x->parent->red = FALSE;
+				y->red = FALSE;
+				x->parent->parent->red = TRUE;
+				x = x->parent->parent;
+			}
+			else
+			{
+				if (x == x->parent->right)
+				{
+					x = x ->parent;
+					left_rotate (tree, x);
+				}
+
+				x->parent->red = FALSE;
+				x->parent->parent->red = TRUE;
+				right_rotate (tree, x->parent->parent);
+			} 
+		}
+	       	else
+		{ /* case for x->parent == x->parent->parent->right */
+			y = x->parent->parent->left;
+
+			if (y->red)
+			{
+				x->parent->red = FALSE;
+				y->red = FALSE;
+				x->parent->parent->red = TRUE;
+				x = x->parent->parent;
+			}
+			else
+			{
+				if (x == x->parent->left)
+				{
+					x = x->parent;
+					right_rotate (tree, x);
+				}
+
+				x->parent->red = FALSE;
+				x->parent->parent->red = TRUE;
+				left_rotate (tree, x->parent->parent);
+			} 
+		}
+	}
+
+	priv->root->left->red = FALSE;
+	e_cal_component_get_uid (comp, &uid);
+	rid = e_cal_component_get_recurid_as_string (comp);
+	g_hash_table_insert (priv->id_node_hash, component_key(uid, rid), newNode);
+	g_free (rid);
+
+	g_static_rec_mutex_unlock (&priv->mutex);
+
+	return TRUE;
+}
+
+EIntervalNode* 
+e_intervaltree_node_next (EIntervalTree *tree, EIntervalNode *x)
+{
+	EIntervalTreePrivate *priv = tree->priv; 
+	EIntervalNode *y;
+	EIntervalNode *nil = priv->nil;
+	EIntervalNode *root = priv->root;
+
+	g_return_val_if_fail (tree != NULL, NULL);
+	g_return_val_if_fail (x != NULL, NULL);
+	g_return_val_if_fail (x != priv->nil, NULL);
+
+	if (nil != (y = x->right))
+	{ 
+		/* find out minimum of right subtree of x (assignment to y is ok) */
+		while (y->left != nil)
+			y = y->left;
+
+		return y;
+	}
+
+	y = x->parent;
+
+	while (x == y->right)
+	{
+		x = y;
+		y = y->parent;
+	}
+
+	if (y == root)
+		return nil;
+
+	return y;
+}
+
+void e_intervaltree_destroy (EIntervalTree *tree)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	EIntervalNode *node; 
+	GList *stack_start = NULL, *pos;
+
+	g_return_if_fail (tree != NULL);
+
+	stack_start = pos = g_list_insert (stack_start, priv->root->left, -1);
+
+	while (pos != NULL)
+	{
+		node = (EIntervalNode*) pos->data;
+
+		if (node != priv->nil)
+		{
+			pos = g_list_insert (pos, node->left, -1);
+			pos = g_list_insert (pos, node->right, -1);
+
+			g_object_unref (node->comp);
+			g_free (node);
+		}
+
+		pos = pos->next;
+	}
+
+	g_list_free (stack_start);
+	g_object_unref (tree);
+}
+
+static void
+e_intervaltree_fixup_deletion (EIntervalTree *tree, EIntervalNode *x)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	EIntervalNode *root = priv->root->left;
+	EIntervalNode *w;
+
+	while ( (!x->red) && (root != x))
+	{
+		if (x == x->parent->left)
+		{
+			w = x->parent->right;
+
+			if (w->red)
+			{
+				w->red = FALSE;
+				x->parent->red = TRUE;
+				left_rotate (tree, x->parent);
+				w = x->parent->right;
+			}
+
+			if ( (!w->right->red) && (!w->left->red) )
+			{ 
+				w->red = TRUE;
+				x = x->parent;
+			}
+			else
+		       	{
+				if (!w->right->red)
+				{
+					w->left->red = FALSE;
+					w->red = TRUE;
+					right_rotate (tree, w);
+					w = x->parent->right;
+				}
+
+				w->red = x->parent->red;
+				x->parent->red = FALSE;
+				w->right->red = FALSE;
+				left_rotate (tree, x->parent);
+				x = root; /* this is to exit while loop */
+			}
+		} else {
+			w = x->parent->left;
+
+			if (w->red)
+			{
+				w->red = FALSE;
+				x->parent->red = TRUE;
+				right_rotate (tree, x->parent);
+				w = x->parent->left;
+			}
+
+			if ( (!w->right->red) && (!w->left->red) )
+			{ 
+				w->red = TRUE;
+				x = x->parent;
+			}
+			else
+			{
+				if (!w->left->red)
+				{
+					w->right->red = FALSE;
+					w->red = TRUE;
+					left_rotate (tree, w);
+					w = x->parent->left;
+				}
+
+				w->red = x->parent->red;
+				x->parent->red = FALSE;
+				w->left->red = FALSE;
+				right_rotate (tree, x->parent);
+				x = root; /* this is to exit while loop */
+			}
+		}
+	}
+
+	x->red = 0;
+}
+
+/**
+ * e_intervaltree_search:
+ * @tree: interval tree
+ * @start: start of the interval
+ * @end: end of the interval
+ * 
+ * Returns list of nodes that overlaps given interval
+ * or NULL.
+ **/
+GList*
+e_intervaltree_search (EIntervalTree *tree, time_t start, time_t end)
+{
+	EIntervalTreePrivate *priv = tree->priv;	
+	EIntervalNode *node; 
+	GList *list = NULL;
+	GList *stack_start = NULL, *pos;
+
+	g_return_val_if_fail (tree != NULL, NULL);
+
+	g_static_rec_mutex_lock (&priv->mutex);
+	
+	stack_start = pos = g_list_insert (stack_start, priv->root->left, -1);
+
+	while (pos != NULL)
+	{
+		node = (EIntervalNode*) pos->data;
+
+		if (node != priv->nil)
+		{
+			if (compare_intervals (node->start, node->end, start, end) == 0)
+			{
+				list = g_list_insert (list, node->comp, -1);
+				g_object_ref (node->comp);
+			}
+
+			if (compare_intervals (node->left->min, node->left->max, start, end) == 0)
+				pos = g_list_insert (pos, node->left, -1);
+
+			if (compare_intervals (node->right->min, node->right->max, start, end) == 0)
+				pos = g_list_insert (pos, node->right, -1);
+		}
+
+		pos = pos->next;
+	}
+
+	g_list_free (stack_start);
+
+	g_static_rec_mutex_unlock (&priv->mutex);
+
+	return list;
+}
+
+#ifdef E_INTERVALTREE_DEBUG
+static void
+e_intervaltree_node_dump (EIntervalTree *tree, EIntervalNode *node, gint indent)
+{
+	/*
+	char start_time[32] = {0}, end_time[32] = {0};
+	struct tm tm_start_time, tm_end_time;
+
+	localtime_r (&node->start, &tm_start_time);
+	localtime_r (&node->end, &tm_end_time);
+	strftime(start_time, sizeof (start_time), "%Y-%m-%d T%H:%M:%S", &tm_start_time);
+	strftime(end_time, sizeof (end_time), "%Y-%m-%d T%H:%M:%S", &tm_end_time);
+	g_print ("%*s[%s - %s]\n", indent, "", start_time, end_time);
+	*/
+	EIntervalTreePrivate *priv = tree->priv;
+	if (node != priv->nil)
+		g_print ("%*s[%ld - %ld] [%ld - %ld] red %d\n", indent, "", node->start,
+				node->end, node->min, node->max, node->red);
+	else
+	{
+		g_print ("%*s[ - ]\n", indent, ""); 
+		return;
+	}
+
+	e_intervaltree_node_dump (tree, node->left, indent + 2);
+	e_intervaltree_node_dump (tree, node->right, indent + 2);
+}
+
+
+void
+e_intervaltree_dump (EIntervalTree *tree)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	if (priv->root)
+		  e_intervaltree_node_dump (tree, priv->root, 0);
+}
+#endif
+
+
+/**
+  * FIXME
+ **/
+static EIntervalNode*
+e_intervaltree_search_component (EIntervalTree *tree,
+				 const char *searched_uid,
+				 const char *searched_rid)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	g_return_val_if_fail (tree != NULL, NULL);
+	g_return_val_if_fail (searched_uid != NULL, NULL);
+
+	if (!searched_uid)
+	{
+		g_warning ("Searching the interval tree, the component "
+			   " does not have a valid UID skipping it\n");
+
+		return NULL;
+	}
+
+	return g_hash_table_lookup (priv->id_node_hash, component_key(searched_uid, searched_rid));
+}
+
+gboolean
+e_intervaltree_remove (EIntervalTree *tree,
+		       const char *uid,
+		       const char *rid)
+{
+	EIntervalTreePrivate *priv = tree->priv;
+	EIntervalNode *y;
+	EIntervalNode *x;
+	EIntervalNode *z;
+	EIntervalNode *nil = priv->nil;
+	EIntervalNode *root = priv->root;
+
+	g_return_val_if_fail (tree != NULL, FALSE);
+	
+	g_static_rec_mutex_lock (&priv->mutex);	
+
+	z = e_intervaltree_search_component (tree, uid, rid);
+
+	if (!z || z == nil)
+	{
+		g_message (G_STRLOC ": Cannot remove node - could not find node in tree\n");
+		return FALSE;
+	}
+
+	y = ((z->left == nil) || (z->right == nil)) ? z : e_intervaltree_node_next (tree, z);
+	x = (y->left == nil) ? y->right : y->left;
+	/* y is to be spliced out. x is it's only child */
+
+	x->parent = y->parent;
+
+	if (root == x->parent)
+		root->left = x;
+	else
+	{
+		if (y == y->parent->left)
+			y->parent->left = x;
+		else
+			y->parent->right = x;
+	}
+
+	if (y != z)
+	{
+		/* y (the succesor of z) is the node to be spliced out */
+		g_return_val_if_fail (y != priv->nil, FALSE);
+
+		y->max = _TIME_MIN;
+		y->min = _TIME_MAX;
+		y->left = z->left;
+		y->right = z->right;
+		y->parent = z->parent;
+		z->left->parent = z->right->parent = y;
+
+		if (z == z->parent->left)
+			z->parent->left = y; 
+		else
+			z->parent->right = y;
+
+		fixup_min_max_fields (tree, x->parent);
+
+		if (!(y->red))
+		{
+			y->red = z->red;
+			e_intervaltree_fixup_deletion (tree, x);
+		}
+		else
+			y->red = z->red; 
+	}
+	else
+       	{
+		/* z is the node to be spliced out */
+
+		fixup_min_max_fields (tree, x->parent);
+
+		if (!(y->red))
+			e_intervaltree_fixup_deletion (tree, x);
+	}
+
+	g_hash_table_remove (priv->id_node_hash, component_key(uid, rid));
+
+	g_object_unref (z->comp);
+	g_free (z);
+	g_static_rec_mutex_unlock (&priv->mutex);
+
+	return TRUE;
+}
+
+static void
+e_intervaltree_finalize (GObject *object)
+{
+	EIntervalTreePrivate *priv = E_INTERVALTREE_GET_PRIVATE (object);
+	if (priv->root) {
+		g_free (priv->root);
+		priv->root = NULL;
+	}
+	if (priv->nil) {
+		g_free (priv->nil);
+		priv->nil = NULL;
+	}
+	if (priv->id_node_hash) {
+		g_hash_table_destroy (priv->id_node_hash);
+		priv->id_node_hash = NULL;
+	}
+	g_static_rec_mutex_free (&priv->mutex);
+	G_OBJECT_CLASS (e_intervaltree_parent_class)->finalize (object);	
+	
+}
+
+static void
+e_intervaltree_class_init (EIntervalTreeClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	g_type_class_add_private (klass, sizeof (EIntervalTreePrivate));
+	object_class->finalize = e_intervaltree_finalize;
+}
+
+
+static void
+e_intervaltree_init (EIntervalTree *tree)
+{
+	EIntervalTreePrivate *priv;
+	EIntervalNode *root, *nil;
+	priv = g_new0 (EIntervalTreePrivate, 1);
+	tree->priv = priv;
+
+	priv->nil = nil = g_new (EIntervalNode, 1);
+	nil->parent = nil->left = nil->right = nil;
+	nil->red = FALSE;
+	nil->start = nil->end = nil->max = _TIME_MIN;
+	nil->min = _TIME_MAX;
+
+	priv->root = root = g_new (EIntervalNode, 1);
+	root->parent = root->left = root->right = nil;
+	root->red = FALSE;
+	root->start = _TIME_MAX;
+	root->end = 0;
+	root->max = _TIME_MAX;
+	root->min = _TIME_MIN;
+
+	g_static_rec_mutex_init (&priv->mutex);
+	priv->id_node_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+}
+
+/**
+ * e_intervaltree_new:
+ *
+ * Creates a new #EIntervalTree.
+ *
+ * Returns: The newly-created #EIntervalTree.
+ **/
+EIntervalTree*
+e_intervaltree_new (void)
+{
+	EIntervalTree *tree;
+	tree = g_object_new (E_TYPE_INTERVALTREE, NULL); 
+        return tree;
+}
diff --git a/calendar/libedata-cal/e-cal-backend-intervaltree.h b/calendar/libedata-cal/e-cal-backend-intervaltree.h
new file mode 100644
index 0000000..299b7a7
--- /dev/null
+++ b/calendar/libedata-cal/e-cal-backend-intervaltree.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Stanislav Slusny <slusnys gmail com>
+ *
+ *  Copyright 2008
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __E_INTERVALTREE_H__
+#define __E_INTERVALTREE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <glib.h>
+//#include <glib/glist.h>
+// EDS opt
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <libecal/e-cal-component.h>
+#include <libecal/e-cal-recur.h>
+
+#define E_INTERVALTREE_DEBUG 1
+
+#define E_TYPE_INTERVALTREE        (e_intervaltree_get_type ())
+#define E_INTERVALTREE(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_INTERVALTREE, EIntervalTree))
+#define E_INTERVALTREE_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), E_TYPE_INTERVALTREE, EIntervalTreeClass))
+#define E_IS_INTERVALTREE(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_INTERVALTREE))
+#define E_IS_INTERVALTREE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_INTERVALTREE))
+#define E_INTERVALTREE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), E_TYPE_INTERVALTREE, EIntervalTreeClass))
+/* #undef E_INTERVALTREE_DEBUG */
+/*
+ * Implementation of the interval node as described in Introduction to Algorithms
+ * book by Cormen et al, chapter 14.3.
+ *
+ * Basically, the interval tree is the red-black tree, the node key is the start
+ * of the interval.
+ */
+
+typedef struct _EIntervalTree EIntervalTree;
+typedef struct _EIntervalTreeClass EIntervalTreeClass;
+typedef struct _EIntervalTreePrivate EIntervalTreePrivate;
+typedef struct _EIntervalNode EIntervalNode;
+
+struct _EIntervalTree
+{
+	GObject parent;
+	EIntervalTreePrivate *priv;              
+};
+
+struct _EIntervalTreeClass
+{
+	GObjectClass parent;
+};
+
+GType		e_intervaltree_get_type	(void);
+
+EIntervalTree* e_intervaltree_new(void);
+
+gboolean e_intervaltree_insert (EIntervalTree *tree, time_t start, time_t end, ECalComponent *comp);
+
+gboolean e_intervaltree_remove (EIntervalTree *tree, const char *uid, const char *rid);
+
+gpointer e_intervaltree_lookup (EIntervalTree *tree, time_t start, time_t end);
+
+void e_intervaltree_destroy (EIntervalTree *tree);
+
+GList*
+e_intervaltree_search (EIntervalTree *tree, time_t start, time_t end);
+#ifdef E_INTERVALTREE_DEBUG
+void e_intervaltree_dump (EIntervalTree *tree);
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __E_INTERVALTREE_H__ */
diff --git a/calendar/libedata-cal/e-cal-backend-sexp.c b/calendar/libedata-cal/e-cal-backend-sexp.c
index f6fa9a6..7c35881 100644
--- a/calendar/libedata-cal/e-cal-backend-sexp.c
+++ b/calendar/libedata-cal/e-cal-backend-sexp.c
@@ -24,7 +24,7 @@
 
 #include <string.h>
 #include <glib/gi18n-lib.h>
-#include "libedataserver/e-data-server-util.h"
+#include <libedataserver/e-data-server-util.h>
 #include <libecal/e-cal-time-util.h>
 
 #include "e-cal-backend-sexp.h"
@@ -1273,6 +1273,25 @@ static struct {
 };
 
 /**
+ * e_cal_backend_sexp_evaluate_occur_times:
+ * @sexp: An #ESExp object.
+ * @start: Start of the time window will be stored here.
+ * @end: End of the time window will be stored here.
+ *
+ * Determines biggest time window given by expressions "occur-in-range" in sexp.
+ *
+ */
+gboolean
+e_cal_backend_sexp_evaluate_occur_times(ECalBackendSExp *sexp, time_t *start, time_t *end)
+{
+	g_return_val_if_fail (sexp != NULL, FALSE);
+	g_return_val_if_fail (start != NULL, FALSE);
+	g_return_val_if_fail (end != NULL, FALSE);
+
+	return e_sexp_evaluate_occur_times (sexp->priv->search_sexp, start, end);
+}
+
+/**
  * e_cal_backend_sexp_match_comp:
  * @sexp: An #ESExp object.
  * @comp: Component to match against the expression.
@@ -1446,3 +1465,50 @@ e_cal_backend_sexp_init (ECalBackendSExp *sexp)
 	sexp->priv = priv;
 	priv->search_context = g_new (SearchContext, 1);
 }
+#ifdef TESTER
+static void
+test_query (const char* query)
+{
+	ECalBackendSExp *sexp = e_cal_backend_sexp_new (query);
+	time_t start, end;
+
+	gboolean generator = e_cal_backend_sexp_evaluate_occur_times(sexp, &start, &end);
+
+	if (generator) {
+		printf ("%s: %ld - %ld\n", query, start, end);
+	} else {
+		printf ("%s: no time prunning possible\n", query);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	g_type_init();
+
+	/* e_sexp_add_variable(f, 0, "test", NULL); */
+
+	if (argc < 2 || !argv[1])
+	{
+		test_query ("(occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))");
+		test_query ("(due-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))");
+		test_query ("(has-alarms-in-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))");
+		test_query ("(completed-before? (make-time \"20080727T220000Z\") )");
+
+		test_query ("(and (occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\")) #t)");
+		test_query ("(or (occur-in-time-range? (make-time \"20080727T220000Z\")(make-time \"20080907T220000Z\")) #t)");
+
+		test_query ("(and (contains? \"substring\") (has-categories? \"blah\"))");
+		test_query ("(or (occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\")) (contains? \"substring\"))");
+
+		test_query ("(and (and (occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))"
+			    " (or (contains? \"substring\") (has-categories? \"blah\"))) (has-alarms?))");
+
+		test_query ("(or (and (occur-in-time-range? (make-time \"20080727T220000Z\") (make-time \"20080907T220000Z\"))"
+			    " (or (contains? \"substring\") (has-categories? \"blah\"))) (has-alarms?))");
+	}
+	else
+		test_query(argv[1]);
+
+	return 0;
+}
+#endif
diff --git a/calendar/libedata-cal/e-cal-backend-sexp.h b/calendar/libedata-cal/e-cal-backend-sexp.h
index e25e7b6..71ae30e 100644
--- a/calendar/libedata-cal/e-cal-backend-sexp.h
+++ b/calendar/libedata-cal/e-cal-backend-sexp.h
@@ -28,7 +28,7 @@
 #include <glib-object.h>
 #include <libecal/e-cal-component.h>
 #include <libedata-cal/e-cal-backend.h>
-#include "libedataserver/e-sexp.h"
+#include <libedataserver/e-sexp.h>
 
 G_BEGIN_DECLS
 
@@ -70,7 +70,9 @@ ESExpResult *e_cal_backend_sexp_func_make_time      (ESExp *esexp, gint argc, ES
 ESExpResult *e_cal_backend_sexp_func_time_add_day   (ESExp *esexp, gint argc, ESExpResult **argv, gpointer data);
 ESExpResult *e_cal_backend_sexp_func_time_day_begin (ESExp *esexp, gint argc, ESExpResult **argv, gpointer data);
 ESExpResult *e_cal_backend_sexp_func_time_day_end   (ESExp *esexp, gint argc, ESExpResult **argv, gpointer data);
-
+gboolean	e_cal_backend_sexp_evaluate_occur_times	(ECalBackendSExp *sexp, time_t *start, time_t *end);
+gboolean 	e_cal_backend_sexp_is_a_time_function(const char *function_name);
+gboolean	e_cal_backend_sexp_is_a_generator_function(const char *function_name);
 G_END_DECLS
 
 #endif /* __E_CAL_BACKEND_SEXP_H__ */
diff --git a/calendar/libedata-cal/e-cal-backend-store.c b/calendar/libedata-cal/e-cal-backend-store.c
index 443d6aa..3089601 100644
--- a/calendar/libedata-cal/e-cal-backend-store.c
+++ b/calendar/libedata-cal/e-cal-backend-store.c
@@ -20,7 +20,7 @@
  */
 
 #include "e-cal-backend-store.h"
-
+#include "e-cal-backend-intervaltree.h"
 #include <libedataserver/e-data-server-util.h>
 
 #define E_CAL_BACKEND_STORE_GET_PRIVATE(obj) \
@@ -29,6 +29,7 @@
 
 struct _ECalBackendStorePrivate {
 	gchar *path;
+	EIntervalTree *intervaltree;
 	gboolean loaded;
 };
 
@@ -91,6 +92,10 @@ cal_backend_store_finalize (GObject *object)
 	priv = E_CAL_BACKEND_STORE_GET_PRIVATE (object);
 
 	g_free (priv->path);
+	if (priv->intervaltree) {
+		e_intervaltree_destroy (priv->intervaltree);
+		priv->intervaltree = NULL;
+	}
 
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_cal_backend_store_parent_class)->finalize (object);
@@ -123,7 +128,10 @@ e_cal_backend_store_class_init (ECalBackendStoreClass *class)
 static void
 e_cal_backend_store_init (ECalBackendStore *store)
 {
-	store->priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+	ECalBackendStorePrivate *priv;
+	priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+	store->priv = priv;
+	priv->intervaltree = e_intervaltree_new ();
 }
 
 /**
@@ -169,8 +177,14 @@ e_cal_backend_store_load (ECalBackendStore *store)
 gboolean
 e_cal_backend_store_remove (ECalBackendStore *store)
 {
+	ECalBackendStorePrivate *priv;
 	g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), FALSE);
 
+	priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+	/* remove interval tree */
+	e_intervaltree_destroy (priv->intervaltree);
+	priv->intervaltree = NULL;
+
 	return (E_CAL_BACKEND_STORE_GET_CLASS (store))->remove (store);
 }
 
@@ -182,8 +196,16 @@ e_cal_backend_store_remove (ECalBackendStore *store)
 gboolean
 e_cal_backend_store_clean (ECalBackendStore *store)
 {
+	ECalBackendStorePrivate *priv;
 	g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), FALSE);
 
+	priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+
+	if (priv->intervaltree) {
+		e_intervaltree_destroy (priv->intervaltree);
+		priv->intervaltree = e_intervaltree_new ();
+	}	
+
 	return (E_CAL_BACKEND_STORE_GET_CLASS (store))->clean (store);
 }
 
@@ -221,13 +243,20 @@ e_cal_backend_store_has_component (ECalBackendStore *store, const gchar *uid, co
  * Since: 2.28
  **/
 gboolean
-e_cal_backend_store_put_component (ECalBackendStore *store, ECalComponent *comp)
+e_cal_backend_store_put_component (ECalBackendStore *store, ECalComponent *comp, time_t occurence_start, time_t occurence_end)
 {
+	ECalBackendStorePrivate *priv;
 	g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), FALSE);
 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
 
-	return (E_CAL_BACKEND_STORE_GET_CLASS (store))->put_component (store, comp);
-}
+	priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+
+	if ((E_CAL_BACKEND_STORE_GET_CLASS (store))->put_component (store, comp)) {
+		e_intervaltree_insert (priv->intervaltree, occurence_start, occurence_end, comp);
+		return TRUE;
+	}
+	return FALSE;
+}	
 
 /**
  * e_cal_backend_store_remove_component:
@@ -237,10 +266,17 @@ e_cal_backend_store_put_component (ECalBackendStore *store, ECalComponent *comp)
 gboolean
 e_cal_backend_store_remove_component (ECalBackendStore *store, const gchar *uid, const gchar *rid)
 {
+	ECalBackendStorePrivate *priv;
 	g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), FALSE);
 	g_return_val_if_fail (uid != NULL, FALSE);
+	
+	priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
 
-	return (E_CAL_BACKEND_STORE_GET_CLASS (store))->remove_component (store, uid, rid);
+	if ((E_CAL_BACKEND_STORE_GET_CLASS (store))->remove_component (store, uid, rid)) {
+		e_intervaltree_remove (priv->intervaltree, uid, rid);
+		return TRUE;
+	}
+	return FALSE;
 }
 
 /**
@@ -335,11 +371,55 @@ GSList *
 e_cal_backend_store_get_components (ECalBackendStore *store)
 {
 	g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), NULL);
-
 	return (E_CAL_BACKEND_STORE_GET_CLASS (store))->get_components (store);
 }
 
 /**
+ * e_cal_backend_store_get_components_occuring_in_range:
+ * @store: An #ECalBackendStore object.
+ * @start:
+ * @end:
+ *
+ * Retrieves a list of components stored in the store, that are occuring
+ * in time range [start, end].
+ *
+ * Return value: A list of the components. Each item in the list is
+ * an #ECalComponent, which should be freed when no longer needed.
+ */
+GSList *
+e_cal_backend_store_get_components_occuring_in_range (ECalBackendStore *store, time_t start, time_t end)
+{
+	ECalBackendStorePrivate *priv;
+	GList *l;
+	GSList *list = NULL;
+	icalcomponent *icalcomp;
+
+	g_return_val_if_fail (store != NULL, NULL);
+	g_return_val_if_fail (E_IS_CAL_BACKEND_STORE (store), NULL);
+	
+	priv = E_CAL_BACKEND_STORE_GET_PRIVATE (store);
+
+	if (!(l = e_intervaltree_search (priv->intervaltree, start, end)))
+                return NULL;
+
+	for ( ; l != NULL; l = g_list_next (l)) {
+		ECalComponent *comp = l->data;
+		icalcomp = e_cal_component_get_icalcomponent (comp);
+		if (icalcomp) {
+			icalcomponent_kind kind;
+
+			kind = icalcomponent_isa (icalcomp);
+			if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT || kind == ICAL_VJOURNAL_COMPONENT) {
+				list = g_slist_prepend (list, comp);
+			}
+		}
+	}
+
+	return list;
+}
+
+
+/**
  * e_cal_backend_store_get_component_ids:
  *
  * Since: 2.28
diff --git a/calendar/libedata-cal/e-cal-backend-store.h b/calendar/libedata-cal/e-cal-backend-store.h
index 9eeb230..f28b733 100644
--- a/calendar/libedata-cal/e-cal-backend-store.h
+++ b/calendar/libedata-cal/e-cal-backend-store.h
@@ -82,6 +82,7 @@ struct _ECalBackendStoreClass {
 	GSList *	(*get_components_by_uid)(ECalBackendStore *store,
 						 const gchar *uid);
 	GSList *	(*get_components)	(ECalBackendStore *store);
+
 	GSList *	(*get_component_ids)	(ECalBackendStore *store);
 	const icaltimezone *
 			(*get_timezone)		(ECalBackendStore *store,
@@ -115,7 +116,9 @@ ECalComponent *	e_cal_backend_store_get_component
 						 const gchar *rid);
 gboolean	e_cal_backend_store_put_component
 						(ECalBackendStore *store,
-						 ECalComponent *comp);
+						 ECalComponent *comp,
+						 time_t occurence_start,
+						 time_t occurence_end);
 gboolean	e_cal_backend_store_remove_component
 						(ECalBackendStore *store,
 						 const gchar *uid,
@@ -143,6 +146,10 @@ GSList *	e_cal_backend_store_get_components_by_uid
 						 const gchar *uid);
 GSList *	e_cal_backend_store_get_components
 						(ECalBackendStore *store);
+GSList *	e_cal_backend_store_get_components_occuring_in_range 
+						(ECalBackendStore *store, 
+						 time_t start, 
+						 time_t end);
 GSList *	e_cal_backend_store_get_component_ids
 						(ECalBackendStore *store);
 const gchar *	e_cal_backend_store_get_key_value
diff --git a/calendar/libedata-cal/e-data-cal-view.c b/calendar/libedata-cal/e-data-cal-view.c
index bc63d6b..fb61d5a 100644
--- a/calendar/libedata-cal/e-data-cal-view.c
+++ b/calendar/libedata-cal/e-data-cal-view.c
@@ -29,7 +29,7 @@
 #include <glib.h>
 
 #include <glib-object.h>
-
+#include <libedataserver/e-debug-log.h>
 #include "e-cal-backend-sexp.h"
 #include "e-data-cal-view.h"
 #include "e-gdbus-egdbuscalview.h"
@@ -305,6 +305,7 @@ impl_DataCalView_start (EGdbusCalView *object, GDBusMethodInvocation *invocation
 
 	if (!priv->started) {
 		priv->started = TRUE;
+		e_debug_log(FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES, "---;%p;QUERY-START;%s;%s", query, e_data_cal_view_get_text (query), G_OBJECT_TYPE_NAME(priv->backend));
 		e_cal_backend_start_query (priv->backend, query);
 	}
 
diff --git a/calendar/libedata-cal/e-data-cal.c b/calendar/libedata-cal/e-data-cal.c
index ec1fadb..c8246f6 100644
--- a/calendar/libedata-cal/e-data-cal.c
+++ b/calendar/libedata-cal/e-data-cal.c
@@ -31,7 +31,7 @@
 #include <unistd.h>
 
 #include <glib-object.h>
-
+#include <libedataserver/e-debug-log.h>
 #include "e-data-cal.h"
 #include "e-data-cal-enumtypes.h"
 #include "e-gdbus-egdbuscal.h"
@@ -479,12 +479,16 @@ impl_Cal_getQuery (EGdbusCal *object, GDBusMethodInvocation *invocation, const g
 	}
 
 	query = e_data_cal_view_new (cal->priv->backend, obj_sexp);
+	e_debug_log (FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES, "%p;%p;NEW;%s;%s", cal, query, sexp, G_OBJECT_TYPE_NAME (cal->priv->backend));
 	if (!query) {
 		g_object_unref (obj_sexp);
 		e_data_cal_notify_query (cal, invocation, EDC_ERROR (OtherError), NULL);
 		return TRUE;
 	}
 
+	/* log query to evaluate cache performance */
+	e_debug_log (FALSE, E_DEBUG_LOG_DOMAIN_CAL_QUERIES, "%p;%p;REUSED;%s;%s", cal, query, sexp, G_OBJECT_TYPE_NAME (cal->priv->backend));
+
 	path = construct_calview_path ();
 	e_data_cal_view_register_gdbus_object (query, g_dbus_method_invocation_get_connection (invocation), path, &error);
 
diff --git a/calendar/libedata-cal/test-intervaltree.c b/calendar/libedata-cal/test-intervaltree.c
new file mode 100644
index 0000000..8db8275
--- /dev/null
+++ b/calendar/libedata-cal/test-intervaltree.c
@@ -0,0 +1,431 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Stanislav Slusny <slusnys gmail com>
+ *
+ *  Copyright 2008
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <glib.h>
+#include <string.h>
+#include <libecal/e-cal-recur.h>
+#include <libecal/e-cal-component.h>
+#include <libical/icalcomponent.h>
+#include "e-cal-backend-intervaltree.h"
+
+#define NUM_INTERVALS_CLOSED  100
+#define NUM_INTERVALS_OPEN  100
+#define NUM_SEARCHES  500
+#define _TIME_MIN	((time_t) 0)		/* Min valid time_t	*/
+#define _TIME_MAX	((time_t) INT_MAX)	/* Max valid time_t	*/
+
+GRand *myrand = NULL;
+const double pbality_delete = 0.3;
+
+struct _EInterval
+{
+	int start;
+	int end;
+	ECalComponent* comp;
+};
+
+typedef struct _EInterval EInterval;
+
+GList *list = NULL;
+
+static inline int
+compare_intervals (time_t x_start, time_t x_end, time_t y_start, time_t y_end)
+{
+	/* assumption: x_start <= x_end */
+	/* assumption: y_start <= y_end */
+
+	/* x is left of y */
+	if (x_end < y_start)
+		return -1;
+
+	/* x is right of y */
+	if (y_end < x_start)
+		return 1;
+
+	/* x and y overlap */
+	return 0;
+}
+
+static GList*
+search_in_list (GList* l, time_t start, time_t end)
+{
+	GList* result = NULL;
+	EInterval *i, *i_new;
+
+	while (l)
+	{
+		i = (EInterval*) l->data;
+
+		if (compare_intervals (start, end, i->start, i->end) == 0)
+		{
+			i_new = g_new (EInterval, 1);
+
+			i_new->start = i->start;
+			i_new->end = i->end;
+			i_new->comp = i->comp;
+			g_object_ref (i->comp);
+
+			result = g_list_insert (result, i_new, -1);
+		}
+
+		l = l->next;
+	}
+
+	return result;
+}
+
+/*
+ * list1 .... from list
+ * list2 ... from interval tree
+ */
+static gboolean
+compare_interval_lists (GList *list1, GList *list2)
+{
+	gboolean found;
+	GList *l2;
+	EInterval *i1;
+	ECalComponent *c2, *c1;
+
+	found = TRUE;
+
+	while (list1 != NULL && found)
+	{
+		found = FALSE;
+
+		l2 = list2;
+
+		i1 = (EInterval*) list1->data;
+		c1 = i1->comp;
+
+
+		while (!found && l2 != NULL)
+		{
+			c2 = (ECalComponent*) l2->data;
+
+			found = (c1 == c2);
+
+			l2 = l2->next;
+		}
+
+		if (found)
+			list1 = list1->next;
+	}
+
+	if (!found)
+	{
+		i1 = (EInterval*) list1->data;
+
+		g_message (G_STRLOC ": Not found %d - %d\n", i1->start, i1->end);
+	}
+
+	return found;
+}
+
+static ECalComponent*
+create_test_component (time_t start, time_t end)
+{
+	ECalComponent *comp = e_cal_component_new ();
+	ECalComponentText *summary;
+	struct icaltimetype current;
+	e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
+
+	/*
+	ECalComponentDateTime dtstart, dtend;
+	struct icaltimetype time_start, time_end;
+
+	time_start = icaltime_from_timet (start, 0);
+	dtstart.value = icaltime_from_timet (start, 0);
+	dtstart.zone = icaltimezone_get_utc_timezone ();
+
+	dtend.value = icaltime_from_timet (end, 0);
+	dtend.value = icaltimezone_get_utc_timezone ();
+	e_cal_component_set_dtstart (comp, &dtstart);
+	e_cal_component_set_dtend (comp, &dtend);
+	*/
+
+	summary = g_new (ECalComponentText, 1);
+
+	summary->value = g_strdup_printf ("%ld - %ld", start, end);
+	summary->altrep = NULL;
+
+	e_cal_component_set_summary (comp, summary);
+
+	current = icaltime_from_timet (time (NULL), 0);
+	e_cal_component_set_created (comp, &current);
+	e_cal_component_set_last_modified (comp, &current);
+
+	return comp;
+}
+
+static void
+unref_comp (gpointer data, gpointer user_data)
+{
+	EInterval *interval = (EInterval*) data;
+	g_object_unref (interval->comp);
+	g_free(data);
+}
+
+static void
+print_nodes_list (GList *l1)
+{
+	while (l1)
+	{
+		ECalComponent *comp = l1->data;
+		ECalComponentText summary;
+
+		e_cal_component_get_summary (comp, &summary);
+		g_print ("%s\n", summary.value);
+		l1 = l1->next;
+	}
+}
+
+static void
+print_list (GList *l2)
+{
+	while (l2)
+	{
+		EInterval* i = l2->data;
+
+		g_print ("%d - %d\n", i->start, i->end);
+		l2 = l2->next;
+	}
+}
+
+static void
+random_test()
+{
+	/*
+	 * outline:
+	 * 1. create new tree and empty list of intervals
+	 * 2. insert some intervals into tree and list
+	 * 3. do various searches, compare results of both structures
+	 * 4. delete some intervals 
+	 * 5. do various searches, compare results of both structures
+	 * 6. free memory
+	 */
+	int i, start, end;
+	EInterval *interval = NULL;
+	EIntervalTree *tree;
+	GList *l1, *l2, *next;
+	int num_deleted = 0;
+
+	tree = e_intervaltree_new ();
+
+	for (i = 0; i < NUM_INTERVALS_CLOSED; i++)
+	{
+		ECalComponent *comp;
+		start = g_rand_int_range (myrand, 0, 1000);
+		end = g_rand_int_range (myrand, start, 2000);
+		comp = create_test_component (start, end);
+		if (!comp)
+		{
+			g_message (G_STRLOC ": error");
+			exit (-1);
+		}
+
+		interval = g_new (EInterval, 1);
+		interval->start = start;
+		interval->end = end;
+		interval->comp = comp;
+		g_object_ref (comp);
+
+		list = g_list_insert (list, interval, -1);
+
+		e_intervaltree_insert (tree, start, end, comp);
+	}
+
+	/* insert open ended intervals */
+	for (i = 0; i < NUM_INTERVALS_OPEN; i++)
+	{
+		ECalComponent *comp;
+		start = g_rand_int_range (myrand, 0, 1000);
+		comp = create_test_component (start, end);
+		if (!comp)
+		{
+			g_message (G_STRLOC ": error");
+			exit (-1);
+		}
+
+		interval = g_new (EInterval, 1);
+		interval->start = start;
+		interval->end = _TIME_MAX;
+		interval->comp = comp;
+		g_object_ref (comp);
+		list = g_list_insert (list, interval, -1);
+
+		e_intervaltree_insert (tree, start, interval->end, comp);
+		/* g_print ("%d - %d\n", start, interval->end); */
+	}
+
+	g_print ("Number of intervals inserted: %d\n", NUM_INTERVALS_CLOSED + NUM_INTERVALS_OPEN);
+
+	for (i = 0; i < NUM_SEARCHES; i++)
+	{
+		start = g_rand_int_range (myrand, 0, 1000);
+		end = g_rand_int_range (myrand, 2000, _TIME_MAX);
+
+		/*
+		   g_print ("Search for : %d - %d\n", start, end);
+		   */
+
+		l1 = e_intervaltree_search (tree, start, end);
+
+
+		l2 = search_in_list (list, start, end);
+
+		if (!compare_interval_lists (l2, l1))
+		{
+			e_intervaltree_dump (tree);
+			g_message (G_STRLOC "Error");
+			exit(-1);
+		}
+
+		/* g_print ("OK\n"); */
+		g_list_foreach(l1, (GFunc)g_object_unref, NULL);
+		g_list_foreach(l2, (GFunc)unref_comp, NULL);
+		g_list_free (l1);
+		g_list_free (l2);
+	}
+
+	/* open-ended intervals */
+	for (i = 0; i < 20; i++)
+	{
+		start = g_rand_int_range (myrand, 0, 1000);
+		end = _TIME_MAX;
+
+		/*
+		   g_print ("Search for : %d - %d\n", start, end);
+		   */
+
+		l1 = e_intervaltree_search (tree, start, end);
+
+
+		l2 = search_in_list (list, start, end);
+
+		if (!compare_interval_lists (l2, l1))
+		{
+			e_intervaltree_dump (tree);
+			g_message (G_STRLOC "Error");
+			exit(-1);
+		}
+
+		/* g_print ("OK\n"); */
+		g_list_foreach(l1, (GFunc)g_object_unref, NULL);
+		g_list_foreach(l2, (GFunc)unref_comp, NULL);
+		g_list_free (l1);
+		g_list_free (l2);
+	}
+
+	l1 = list;
+
+	while (l1)
+	{
+		/* perhaps we will delete l1 */
+		next = l1->next;
+
+		if (g_rand_double(myrand) < pbality_delete)
+		{
+			ECalComponent *comp;
+			const gchar *uid = NULL;
+			gchar *rid;
+			interval = (EInterval*) l1->data;
+			comp = interval->comp;
+
+			/* delete l1 */
+			/*
+			   g_print ("Deleting interval %d - %d\n", interval->start,
+			   interval->end);
+			   */
+
+			rid = e_cal_component_get_recurid_as_string (comp);
+			e_cal_component_get_uid (comp, &uid);
+			if (!e_intervaltree_remove (tree, uid, rid))
+			{
+				g_free (rid);
+				e_intervaltree_dump (tree);
+				g_print ("Deleting interval %d - %d ERROR\n", interval->start,
+					 interval->end);
+				exit(-1);
+			}
+
+
+			g_free (rid);
+			g_object_unref (interval->comp);
+			g_free (l1->data);
+			list = g_list_delete_link (list, l1);
+			num_deleted++;
+		}
+
+		l1 = next;
+	}
+
+	g_print ("Number of intervals deleted: %d\n", num_deleted);
+
+	for (i = 0; i < NUM_SEARCHES; i++)
+	{
+		start = g_rand_int_range (myrand, 0, 1000);
+		end = g_rand_int_range (myrand, start, 2000);
+
+		/*
+		   g_print ("Search for : %d - %d\n", start, end);
+		   */
+
+		l1 = e_intervaltree_search (tree, start, end);
+
+		/*
+		   g_print ("Results from tree:\n");
+		   print_nodes_list (l1);
+		   */
+
+		l2 = search_in_list (list, start, end);
+
+		if (!compare_interval_lists (l2, l1))
+		{
+			g_print ("ERROR!\n\n");
+			return;
+		}
+
+		g_list_foreach(l1, (GFunc)g_object_unref, NULL);
+		g_list_foreach(l2, (GFunc)unref_comp, NULL);
+		g_list_free (l1);
+		g_list_free (l2);
+
+		/* g_print ("OK\n"); */
+	}
+
+	e_intervaltree_destroy (tree);
+	g_list_foreach(list, (GFunc)unref_comp, NULL);
+	g_list_free (list);
+}
+
+int
+main (int argc, char **argv)
+{
+	g_type_init ();
+
+	myrand = g_rand_new ();
+	random_test();
+	g_print ("Everything OK\n");
+
+	return 0;
+}
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index 556603d..9dd222f 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -27,6 +27,7 @@ libedataserver_1_2_la_SOURCES =		\
 	e-source-group.c		\
 	e-source-list.c			\
 	e-source.c			\
+	e-debug-log.c			\
 	e-time-utils.c			\
 	e-uid.c				\
 	e-url.c				\
@@ -61,6 +62,7 @@ libedataserverinclude_HEADERS =		\
 	e-source-group.h		\
 	e-source-list.h			\
 	e-source.h			\
+	e-debug-log.h			\
 	e-time-utils.h			\
 	e-uid.h				\
 	e-url.h				\
diff --git a/libedataserver/e-debug-log.c b/libedataserver/e-debug-log.c
new file mode 100644
index 0000000..524a455
--- /dev/null
+++ b/libedataserver/e-debug-log.c
@@ -0,0 +1,633 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   e-debug-log.c: Ring buffer for logging debug messages
+
+   Copyright (C) 2006, 2007 Novell, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Federico Mena-Quintero <federico novell com>
+*/
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include "e-debug-log.h"
+
+#define DEFAULT_RING_BUFFER_NUM_LINES 1000
+
+#define KEY_FILE_GROUP		"debug log"
+#define KEY_FILE_DOMAINS_KEY	"enable domains"
+#define KEY_FILE_MAX_LINES_KEY	"max lines"
+
+static GStaticMutex log_mutex = G_STATIC_MUTEX_INIT;
+
+static GHashTable *domains_hash;
+static char **ring_buffer;
+static int ring_buffer_next_index;
+static int ring_buffer_num_lines;
+static int ring_buffer_max_lines = DEFAULT_RING_BUFFER_NUM_LINES;
+
+static GSList *milestones_head;
+static GSList *milestones_tail;
+
+static void
+lock (void)
+{
+	g_static_mutex_lock (&log_mutex);
+}
+
+static void
+unlock (void)
+{
+	g_static_mutex_unlock (&log_mutex);
+}
+
+void
+e_debug_log (gboolean is_milestone, const char *domain, const char *format, ...)
+{
+	va_list args;
+
+	va_start (args, format);
+	e_debug_logv (is_milestone, domain, format, args);
+	va_end (args);
+}
+
+static gboolean
+is_domain_enabled (const char *domain)
+{
+	/* User actions are always logged */
+	if (strcmp (domain, E_DEBUG_LOG_DOMAIN_USER) == 0)
+		return TRUE;
+
+	if (!domains_hash)
+		return FALSE;
+
+	return (g_hash_table_lookup (domains_hash, domain) != NULL);
+}
+
+static void
+ensure_ring (void)
+{
+	if (ring_buffer)
+		return;
+
+	ring_buffer = g_new0 (char *, ring_buffer_max_lines);
+	ring_buffer_next_index = 0;
+	ring_buffer_num_lines = 0;
+}
+
+static void
+add_to_ring (char *str)
+{
+	ensure_ring ();
+
+	g_assert (str != NULL);
+
+	if (ring_buffer_num_lines == ring_buffer_max_lines) {
+		/* We have an overlap, and the ring_buffer_next_index points to
+		 * the "first" item.  Free it to make room for the new item.
+		 */
+
+		g_assert (ring_buffer[ring_buffer_next_index] != NULL);
+		g_free (ring_buffer[ring_buffer_next_index]);
+	} else
+		ring_buffer_num_lines++;
+
+	g_assert (ring_buffer_num_lines <= ring_buffer_max_lines);
+
+	ring_buffer[ring_buffer_next_index] = str;
+
+	ring_buffer_next_index++;
+	if (ring_buffer_next_index == ring_buffer_max_lines) {
+		ring_buffer_next_index = 0;
+		g_assert (ring_buffer_num_lines == ring_buffer_max_lines);
+	}
+}
+
+static void
+add_to_milestones (const char *str)
+{
+	char *str_copy;
+
+	str_copy = g_strdup (str);
+
+	if (milestones_tail) {
+		milestones_tail = g_slist_append (milestones_tail, str_copy);
+		milestones_tail = milestones_tail->next;
+	} else {
+		milestones_head = milestones_tail = g_slist_append (NULL, str_copy);
+	}
+
+	g_assert (milestones_head != NULL && milestones_tail != NULL);
+}
+
+void
+e_debug_logv (gboolean is_milestone, const char *domain, const char *format, va_list args)
+{
+	char *str;
+	char *debug_str;
+	struct timeval tv;
+	struct tm tm;
+
+	lock ();
+
+	if (!(is_milestone || is_domain_enabled (domain)))
+		goto out;
+
+	str = g_strdup_vprintf (format, args);
+	gettimeofday (&tv, NULL);
+
+	tm = *localtime (&tv.tv_sec);
+
+	debug_str = g_strdup_printf ("%p;%04d/%02d/%02d;%02d:%02d:%02d.%04d;(%s);%s",
+				     g_thread_self (),
+				     tm.tm_year + 1900,
+				     tm.tm_mon + 1,
+				     tm.tm_mday,
+				     tm.tm_hour,
+				     tm.tm_min,
+				     tm.tm_sec,
+				     (int) (tv.tv_usec / 100),
+				     domain,
+				     str);
+	g_free (str);
+
+	add_to_ring (debug_str);
+	if (is_milestone)
+		add_to_milestones (debug_str);
+
+ out:
+	unlock ();
+}
+
+gboolean
+e_debug_log_load_configuration (const char *filename, GError **error)
+{
+	GKeyFile *key_file;
+	char **strings;
+	gsize num_strings;
+	int num;
+	GError *my_error;
+
+	g_assert (filename != NULL);
+	g_assert (error == NULL || *error == NULL);
+
+	key_file = g_key_file_new ();
+
+	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error)) {
+		g_key_file_free (key_file);
+		return FALSE;
+	}
+
+	/* Domains */
+
+	my_error = NULL;
+	strings = g_key_file_get_string_list (key_file, KEY_FILE_GROUP, KEY_FILE_DOMAINS_KEY, &num_strings, &my_error);
+	if (my_error)
+		g_error_free (my_error);
+	else {
+		int i;
+
+		for (i = 0; i < num_strings; i++)
+			strings[i] = g_strstrip (strings[i]);
+
+		e_debug_log_enable_domains ((const char **) strings, num_strings);
+		g_strfreev (strings);
+	}
+
+	/* Number of lines */
+
+	my_error = NULL;
+	num = g_key_file_get_integer (key_file, KEY_FILE_GROUP, KEY_FILE_MAX_LINES_KEY, &my_error);
+	if (my_error)
+		g_error_free (my_error);
+	else
+		e_debug_log_set_max_lines (num);
+
+	g_key_file_free (key_file);
+	return TRUE;
+}
+
+void
+e_debug_log_enable_domains (const char **domains, int n_domains)
+{
+	int i;
+
+	g_assert (domains != NULL);
+	g_assert (n_domains >= 0);
+
+	lock ();
+
+	if (!domains_hash)
+		domains_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+	for (i = 0; i < n_domains; i++) {
+		g_assert (domains[i] != NULL);
+
+		if (strcmp (domains[i], E_DEBUG_LOG_DOMAIN_USER) == 0)
+			continue; /* user actions are always enabled */
+
+		if (g_hash_table_lookup (domains_hash, domains[i]) == NULL) {
+			char *domain;
+
+			domain = g_strdup (domains[i]);
+			g_hash_table_insert (domains_hash, domain, domain);
+		}
+	}
+
+	unlock ();
+}
+
+void
+e_debug_log_disable_domains (const char **domains, int n_domains)
+{
+	int i;
+
+	g_assert (domains != NULL);
+	g_assert (n_domains >= 0);
+
+	lock ();
+
+	if (domains_hash) {
+		for (i = 0; i < n_domains; i++) {
+			char *domain;
+
+			g_assert (domains[i] != NULL);
+
+			if (strcmp (domains[i], E_DEBUG_LOG_DOMAIN_USER) == 0)
+				continue; /* user actions are always enabled */
+
+			domain = g_hash_table_lookup (domains_hash, domains[i]);
+			if (domain) {
+				g_hash_table_remove (domains_hash, domain);
+				g_free (domain);
+			}
+		}
+	} /* else, there is nothing to disable */
+
+	unlock ();
+}
+
+gboolean
+e_debug_log_is_domain_enabled (const char *domain)
+{
+	gboolean retval;
+
+	g_assert (domain != NULL);
+
+	lock ();
+	retval = is_domain_enabled (domain);
+	unlock ();
+
+	return retval;
+}
+
+struct domains_dump_closure {
+	char **domains;
+	int num_domains;
+};
+
+static void
+domains_foreach_dump_cb (gpointer key, gpointer value, gpointer data)
+{
+	struct domains_dump_closure *closure;
+	char *domain;
+
+	closure = data;
+	domain = key;
+
+	closure->domains[closure->num_domains] = domain;
+	closure->num_domains++;
+}
+
+static GKeyFile *
+make_key_file_from_configuration (void)
+{
+	GKeyFile *key_file;
+	struct domains_dump_closure closure;
+	int num_domains;
+
+	key_file = g_key_file_new ();
+
+	/* domains */
+
+	if (domains_hash) {
+		num_domains = g_hash_table_size (domains_hash);
+		if (num_domains != 0) {
+			closure.domains = g_new (char *, num_domains);
+			closure.num_domains = 0;
+
+			g_hash_table_foreach (domains_hash, domains_foreach_dump_cb, &closure);
+			g_assert (num_domains == closure.num_domains);
+
+			g_key_file_set_string_list (key_file, KEY_FILE_GROUP, KEY_FILE_DOMAINS_KEY,
+						    (const gchar * const *) closure.domains, closure.num_domains);
+			g_free (closure.domains);
+		}
+	}
+
+	/* max lines */
+
+	g_key_file_set_integer (key_file, KEY_FILE_GROUP, KEY_FILE_MAX_LINES_KEY, ring_buffer_max_lines);
+
+	return key_file;
+}
+
+static gboolean
+write_string (const char *filename, FILE *file, const char *str, GError **error)
+{
+	if (fputs (str, file) == EOF) {
+		int saved_errno;
+
+		saved_errno = errno;
+		g_set_error (error,
+			     G_FILE_ERROR,
+			     g_file_error_from_errno (saved_errno),
+			     "error when writing to log file %s", filename);
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+dump_configuration (const char *filename, FILE *file, GError **error)
+{
+	GKeyFile *key_file;
+	char *data;
+	gsize length;
+	gboolean success;
+
+	if (!write_string (filename, file,
+			   "\n\n"
+			   "This configuration for the debug log can be re-created\n"
+			   "by putting the following in ~/evolution-data-server-debug-log.conf\n"
+			   "(use ';' to separate domain names):\n\n",
+			   error)) {
+		return FALSE;
+	}
+
+	success = FALSE;
+
+	key_file = make_key_file_from_configuration ();
+
+	data = g_key_file_to_data (key_file, &length, error);
+	if (!data)
+		goto out;
+
+	if (!write_string (filename, file, data, error)) {
+		goto out;
+	}
+
+	success = TRUE;
+ out:
+	g_key_file_free (key_file);
+	return success;
+}
+
+static gboolean
+dump_milestones (const char *filename, FILE *file, GError **error)
+{
+	GSList *l;
+
+	if (!write_string (filename, file, "===== BEGIN MILESTONES =====\n", error))
+		return FALSE;
+
+	for (l = milestones_head; l; l = l->next) {
+		const char *str;
+
+		str = l->data;
+		if (!(write_string (filename, file, str, error)
+		      && write_string (filename, file, "\n", error)))
+			return FALSE;
+	}
+
+	if (!write_string (filename, file, "===== END MILESTONES =====\n", error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+dump_ring_buffer (const char *filename, FILE *file, GError **error)
+{
+	int start_index;
+	int i;
+
+	if (!write_string (filename, file, "===== BEGIN RING BUFFER =====\n", error))
+		return FALSE;
+
+	if (ring_buffer_num_lines == ring_buffer_max_lines)
+		start_index = ring_buffer_next_index;
+	else
+		start_index = 0;
+
+	for (i = 0; i < ring_buffer_num_lines; i++) {
+		int idx;
+
+		idx = (start_index + i) % ring_buffer_max_lines;
+
+		if (!(write_string (filename, file, ring_buffer[idx], error)
+		      && write_string (filename, file, "\n", error))) {
+			return FALSE;
+		}
+	}
+
+	if (!write_string (filename, file, "===== END RING BUFFER =====\n", error))
+		return FALSE;
+
+	return TRUE;
+}
+
+gboolean
+e_debug_log_dump (const char *filename, GError **error)
+{
+	FILE *file;
+	gboolean success;
+
+	g_assert (error == NULL || *error == NULL);
+
+	lock ();
+
+	success = FALSE;
+
+	file = fopen (filename, "w");
+	if (!file) {
+		int saved_errno;
+
+		saved_errno = errno;
+		g_set_error (error,
+			     G_FILE_ERROR,
+			     g_file_error_from_errno (saved_errno),
+			     "could not open log file %s", filename);
+		goto out;
+	}
+
+	if (!(dump_milestones (filename, file, error)
+	      && dump_ring_buffer (filename, file, error)
+	      && dump_configuration (filename, file, error))) {
+		goto do_close;
+	}
+
+	success = TRUE;
+
+ do_close:
+
+	if (fclose (file) != 0) {
+		int saved_errno;
+
+		saved_errno = errno;
+
+		if (error && *error) {
+			g_error_free (*error);
+			*error = NULL;
+		}
+
+		g_set_error (error,
+			     G_FILE_ERROR,
+			     g_file_error_from_errno (saved_errno),
+			     "error when closing log file %s", filename);
+		success = FALSE;
+	}
+
+ out:
+
+	unlock ();
+	return success;
+}
+
+gboolean
+e_debug_log_dump_to_dated_file (GError **error)
+{
+	time_t t;
+	struct tm tm;
+	char *basename;
+	char *filename;
+	gboolean retval;
+
+	g_assert (error == NULL || *error == NULL);
+
+	t = time (NULL);
+	tm = *localtime (&t);
+
+	basename = g_strdup_printf ("e-debug-log-%04d-%02d-%02d-%02d-%02d-%02d.txt",
+				    tm.tm_year + 1900,
+				    tm.tm_mon + 1,
+				    tm.tm_mday,
+				    tm.tm_hour,
+				    tm.tm_min,
+				    tm.tm_sec);
+	filename = g_build_filename (g_get_home_dir (), basename, NULL);
+
+	retval = e_debug_log_dump (filename, error);
+
+	g_free (basename);
+	g_free (filename);
+
+	return retval;
+}
+
+void
+e_debug_log_set_max_lines (int num_lines)
+{
+	char **new_buffer;
+	int lines_to_copy;
+
+	g_assert (num_lines > 0);
+
+	lock ();
+
+	if (num_lines == ring_buffer_max_lines)
+		goto out;
+
+	new_buffer = g_new0 (char *, num_lines);
+
+	lines_to_copy = MIN (num_lines, ring_buffer_num_lines);
+
+	if (ring_buffer) {
+		int start_index;
+		int i;
+
+		if (ring_buffer_num_lines == ring_buffer_max_lines)
+			start_index = (ring_buffer_next_index + ring_buffer_max_lines - lines_to_copy) % ring_buffer_max_lines;
+		else
+			start_index = ring_buffer_num_lines - lines_to_copy;
+
+		g_assert (start_index >= 0 && start_index < ring_buffer_max_lines);
+
+		for (i = 0; i < lines_to_copy; i++) {
+			int idx;
+
+			idx = (start_index + i) % ring_buffer_max_lines;
+
+			new_buffer[i] = ring_buffer[idx];
+			ring_buffer[idx] = NULL;
+		}
+
+		for (i = 0; i < ring_buffer_max_lines; i++)
+			g_free (ring_buffer[i]);
+
+		g_free (ring_buffer);
+	}
+
+	ring_buffer = new_buffer;
+	ring_buffer_next_index = lines_to_copy;
+	ring_buffer_num_lines = lines_to_copy;
+	ring_buffer_max_lines = num_lines;
+
+ out:
+
+	unlock ();
+}
+
+int
+e_debug_log_get_max_lines (void)
+{
+	int retval;
+
+	lock ();
+	retval = ring_buffer_max_lines;
+	unlock ();
+
+	return retval;
+}
+
+void
+e_debug_log_clear (void)
+{
+	int i;
+
+	lock ();
+
+	if (!ring_buffer)
+		goto out;
+
+	for (i = 0; i < ring_buffer_max_lines; i++) {
+		g_free (ring_buffer[i]);
+		ring_buffer[i] = NULL;
+	}
+
+	ring_buffer_next_index = 0;
+	ring_buffer_num_lines = 0;
+
+ out:
+	unlock ();
+}
+
diff --git a/libedataserver/e-debug-log.h b/libedataserver/e-debug-log.h
new file mode 100644
index 0000000..9241c12
--- /dev/null
+++ b/libedataserver/e-debug-log.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   e-debug-log.h: Ring buffer for logging debug messages
+ 
+   Copyright (C) 2006, 2007 Novell, Inc.
+  
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+  
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+  
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Federico Mena-Quintero <federico novell com>
+*/
+
+#ifndef E_DEBUG_LOG_H
+#define E_DEBUG_LOG_H
+
+#include <glib.h>
+
+#define E_DEBUG_LOG_DOMAIN_USER		"USER"		/* always enabled */
+#define E_DEBUG_LOG_DOMAIN_GLOG		"GLog"		/* used for GLog messages; don't use it yourself */
+#define E_DEBUG_LOG_DOMAIN_CAL_QUERIES  "CalQueries"    /* used for calendar queries analysis */
+
+void e_debug_log (gboolean is_milestone, const char *domain, const char *format, ...);
+
+void e_debug_logv (gboolean is_milestone, const char *domain, const char *format, va_list args);
+
+gboolean e_debug_log_load_configuration (const char *filename, GError **error);
+
+void e_debug_log_enable_domains (const char **domains, int n_domains);
+void e_debug_log_disable_domains (const char **domains, int n_domains);
+
+gboolean e_debug_log_is_domain_enabled (const char *domain);
+
+gboolean e_debug_log_dump (const char *filename, GError **error);
+
+gboolean e_debug_log_dump_to_dated_file (GError **error);
+
+void e_debug_log_set_max_lines (int num_lines);
+int e_debug_log_get_max_lines (void);
+
+/* For testing only */
+void e_debug_log_clear (void);
+
+#endif /* E_DEBUG_LOG_H */
+
diff --git a/libedataserver/e-sexp.c b/libedataserver/e-sexp.c
index f054ddb..785a199 100644
--- a/libedataserver/e-sexp.c
+++ b/libedataserver/e-sexp.c
@@ -113,6 +113,12 @@ static void parse_dump_term(struct _ESExpTerm *t, gint depth);
 static GObjectClass *parent_class;
 #endif
 
+typedef gboolean (ESGeneratorFunc)(int argc, struct _ESExpResult **argv, struct _ESExpResult *r);
+typedef gboolean (ESOperatorFunc)(int argc, struct _ESExpResult **argv, struct _ESExpResult *r);
+/* FIXME: constant _TIME_MAX used in different files, move it somewhere */
+#define _TIME_MAX	((time_t) INT_MAX)	/* Max valid time_t	*/
+
+
 static const GScannerConfig scanner_config =
 {
 	( (gchar *) " \t\r\n")		/* cset_skip_characters */,
@@ -177,6 +183,9 @@ e_sexp_result_new(struct _ESExp *f, gint type)
 {
 	struct _ESExpResult *r = e_memchunk_alloc0(f->result_chunks);
 	r->type = type;
+	r->occuring_start = 0;
+	r->occuring_end = _TIME_MAX;
+	r->time_generator = FALSE;
 	return r;
 }
 
@@ -703,7 +712,7 @@ e_sexp_term_eval(struct _ESExp *f, struct _ESExpTerm *t)
 		r->value.boolean = t->value.boolean;
 		break;
 	case ESEXP_TERM_TIME:
-		r(printf(" (time_t %d)\n", t->value.time));
+		r(printf(" (time_t %ld)\n", t->value.time));
 		r = e_sexp_result_new (f, ESEXP_RES_TIME);
 		r->value.time = t->value.time;
 		break;
@@ -820,6 +829,236 @@ parse_dump_term(struct _ESExpTerm *t, gint depth)
 }
 #endif
 
+const char *time_functions[] = {
+	"time-now",
+	"make-time",
+	"time-add-day",
+	"time-day-begin",
+	"time-day-end"
+};
+
+static gboolean
+binary_generator (int argc, struct _ESExpResult **argv, struct _ESExpResult *r)
+{
+	g_return_val_if_fail (r != NULL, FALSE);
+	g_return_val_if_fail (argc == 2, FALSE);
+
+	if ((argv[0]->type != ESEXP_RES_TIME) || (argv[1]->type != ESEXP_RES_TIME))
+		return FALSE;
+
+	r->occuring_start = argv[0]->value.time;
+	r->occuring_end = argv[1]->value.time;
+
+	return TRUE;
+}
+
+static gboolean
+unary_generator(int argc, struct _ESExpResult **argv, struct _ESExpResult *r)
+{
+	/* unary generator with end time */
+	g_return_val_if_fail (r != NULL, FALSE);
+	g_return_val_if_fail (argc == 1, FALSE);
+
+	if (argv[0]->type != ESEXP_RES_TIME)
+		return FALSE;
+
+        r->occuring_start = 0;
+	r->occuring_end = argv[0]->value.time;
+
+	return TRUE;
+}
+
+static const struct {
+	char *name;
+	ESGeneratorFunc *func;
+} generators[] = {
+	{"occur-in-time-range?", binary_generator},
+	{"due-in-time-range?", binary_generator},
+	{"has-alarms-in-range?", binary_generator},
+	{"completed-before?", unary_generator},
+};
+
+const int generators_count = sizeof(generators) / sizeof(generators[0]);
+
+static gboolean
+or_operator(int argc, struct _ESExpResult **argv, struct _ESExpResult *r)
+{
+	/*
+	   A          B           A or B
+	   ----       ----        ------
+	   norm(0)    norm(0)     norm(0)
+	   gen(1)     norm(0)     norm(0)
+	   norm(0)    gen(1)      norm(0)
+	   gen(1)     gen(1)      gen*(1)
+	   */
+
+	g_return_val_if_fail (r != NULL, FALSE);
+	g_return_val_if_fail (argc == 2, FALSE);
+
+	if ((r->time_generator = argv[0]->time_generator & argv[1]->time_generator)) {
+		r->occuring_start = MIN(argv[0]->occuring_start, argv[1]->occuring_start);
+		r->occuring_end = MAX(argv[0]->occuring_end, argv[1]->occuring_end);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+and_operator(int argc, struct _ESExpResult **argv, struct _ESExpResult *r)
+{
+	/*
+	   A           B          A and B
+	   ----        ----       ------- -
+	   norm(0)     norm(0)    norm(0)    
+	   gen(1)      norm(0)    gen(1)    
+	   norm(0)     gen(1)     gen(1)     
+	   gen(1)      gen(1)     gen(1)     
+	   */
+
+	g_return_val_if_fail (r != NULL, FALSE);
+	g_return_val_if_fail (argc == 2, FALSE);
+
+	if ((r->time_generator = argv[0]->time_generator | argv[1]->time_generator)) {
+		/* constraint interval */
+		r->occuring_start = MAX(argv[0]->occuring_start, argv[1]->occuring_start);
+		r->occuring_end = MIN(argv[0]->occuring_end, argv[1]->occuring_end);
+	}
+
+	return TRUE;
+}
+
+static const struct {
+	char *name;
+	ESOperatorFunc *func;
+} operators[] = {
+	{"or", or_operator},
+	{"and", and_operator}
+};
+
+const int operators_count = sizeof(operators) / sizeof(operators[0]);
+
+
+static ESOperatorFunc*
+get_operator_function(const char *fname)
+{
+	int i;
+
+	g_return_val_if_fail (fname != NULL, NULL);
+
+	for (i = 0; i < sizeof(operators) / sizeof(operators[0]); i++)
+		if (strcmp(operators[i].name, fname) == 0)
+			return operators[i].func;
+
+	return NULL;
+}
+
+static inline gboolean
+is_time_function(const char *fname)
+{
+	int i;
+
+	g_return_val_if_fail (fname != NULL, FALSE);
+
+	for (i = 0; i < sizeof(time_functions) / sizeof(time_functions[0]); i++)
+		if (strcmp(time_functions[i], fname) == 0)
+			return TRUE;
+
+	return FALSE;
+}
+
+static ESGeneratorFunc*
+get_generator_function(const char *fname)
+{
+	int i;
+
+	g_return_val_if_fail (fname != NULL, NULL);
+
+	for (i = 0; i < sizeof(generators) / sizeof(generators[0]); i++)
+		if (strcmp(generators[i].name, fname) == 0)
+			return generators[i].func;
+
+	return NULL;
+}
+
+/* this must only be called from inside term evaluation callbacks! */
+static struct _ESExpResult *
+e_sexp_term_evaluate_occur_times(struct _ESExp *f, struct _ESExpTerm *t, time_t *start, time_t *end)
+{
+	struct _ESExpResult *r = NULL;
+	int i, argc;
+	struct _ESExpResult **argv;
+	gboolean ok = TRUE;
+
+	g_return_val_if_fail(t != NULL, NULL);
+	g_return_val_if_fail(start != NULL, NULL);
+	g_return_val_if_fail(end != NULL, NULL);
+
+	/*
+	printf("eval term :\n");
+	parse_dump_term(t, 0);
+	*/
+
+	switch (t->type) {
+	case ESEXP_TERM_STRING:
+		r(printf(" (string \"%s\")\n", t->value.string));
+		r = e_sexp_result_new(f, ESEXP_RES_STRING);
+		r->value.string = g_strdup(t->value.string);
+		break;
+	case ESEXP_TERM_IFUNC:
+	case ESEXP_TERM_FUNC:
+		r(printf(" (function \"%s\"\n", t->value.func.sym->name));
+
+		r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+		argc = t->value.func.termcount;
+		argv = alloca(sizeof(argv[0]) * argc);
+
+		for (i=0;i<t->value.func.termcount;i++) {
+			argv[i] = e_sexp_term_evaluate_occur_times (f, t->value.func.terms[i],
+								    start, end);
+		}
+
+		ESGeneratorFunc *generator = NULL;
+		ESOperatorFunc *operator = NULL;
+
+		if (is_time_function(t->value.func.sym->name)) {
+			/* evaluate time */
+			if (t->value.func.sym->f.func)
+				r = t->value.func.sym->f.func(f, t->value.func.termcount,
+					      argv, t->value.func.sym->data);
+		} else if ((generator = get_generator_function(t->value.func.sym->name)) != NULL) {
+			/* evaluate generator function */
+			r->time_generator = TRUE;
+			ok = generator(argc, argv, r);
+		} else if ((operator = get_operator_function(t->value.func.sym->name)) != NULL)
+			/* evaluate operator function */
+			ok = operator(argc, argv, r);
+		else {
+			/* normal function: we need to scan all objects */
+			r->time_generator = FALSE;
+		}
+
+		e_sexp_resultv_free(f, t->value.func.termcount, argv);
+		break;
+
+	case ESEXP_TERM_INT:
+	case ESEXP_TERM_BOOL:
+	case ESEXP_TERM_TIME:
+		break;
+	default:
+		ok = FALSE;
+		break;
+	}
+
+	if (!ok)
+		e_sexp_fatal_error(f, "Error in parse tree");
+
+	if (r==NULL)
+		r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+
+	return r;
+}
+
+
 /*
   PARSER
 */
@@ -1308,6 +1547,45 @@ e_sexp_eval(ESExp *f)
 }
 
 /**
+ * e_cal_backend_sexp_evaluate_occur_times:
+ * @f: An #ESExp object.
+ * @start: Start of the time window will be stored here.
+ * @end: End of the time window will be stored here.
+ *
+ * Determines biggest time window given by expressions "occur-in-range" in sexp.
+ *
+ */
+gboolean
+e_sexp_evaluate_occur_times(ESExp *f, time_t *start, time_t *end)
+{
+	struct _ESExpResult *r;
+	gboolean generator;
+	g_return_val_if_fail (IS_E_SEXP (f), FALSE);
+	g_return_val_if_fail (f->tree != NULL, FALSE);
+	g_return_val_if_fail (start != NULL, FALSE);
+	g_return_val_if_fail (end != NULL, FALSE);
+
+	*start = *end = -1;
+
+	if (setjmp(f->failenv)) {
+		g_warning("Error in execution: %s", f->error);
+		return FALSE;
+	}
+
+	r = e_sexp_term_evaluate_occur_times(f, f->tree, start, end);
+	generator = r->time_generator;
+
+	if (generator) {
+		*start = r->occuring_start;
+		*end = r->occuring_end;
+	}
+
+	e_sexp_result_free(f, r);
+
+	return generator;
+}
+
+/**
  * e_sexp_encode_bool:
  * @s:
  * @state:
@@ -1365,7 +1643,10 @@ gint main(gint argc, gchar **argv)
 
 	e_sexp_add_variable(f, 0, "test", NULL);
 
-	e_sexp_input_text(f, t, strlen(t));
+	if (argc < 2 || !argv[1])
+		return;
+
+	e_sexp_input_text(f, t, t);
 	e_sexp_parse(f);
 
 	if (f->tree) {
diff --git a/libedataserver/e-sexp.h b/libedataserver/e-sexp.h
index 6c4d492..c10dc09 100644
--- a/libedataserver/e-sexp.h
+++ b/libedataserver/e-sexp.h
@@ -57,6 +57,9 @@ struct _ESExpResult {
 		gint boolean;
 		time_t time;
 	} value;
+	gboolean time_generator;
+	time_t occuring_start;
+	time_t occuring_end;
 };
 
 typedef struct _ESExpResult *(ESExpFunc)(struct _ESExp *sexp, gint argc,
@@ -173,6 +176,7 @@ const gchar     *e_sexp_error		(struct _ESExp *f);
 
 ESExpTerm * e_sexp_parse_value(ESExp *f);
 
+gboolean	e_sexp_evaluate_occur_times	(ESExp *f, time_t *start, time_t *end);
 G_END_DECLS
 
 #endif /* _E_SEXP_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]