Index: libedataserver/e-iconv.c
===================================================================
--- libedataserver/e-iconv.c	(revision 409)
+++ libedataserver/e-iconv.c	(working copy)
@@ -26,6 +26,8 @@
 #include <config.h>
 #endif
 
+#define ICONV_10646 "iso-10646" 
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
Index: addressbook/libebook-dbus/e-address-western.h
===================================================================
--- addressbook/libebook-dbus/e-address-western.h	(revision 409)
+++ addressbook/libebook-dbus/e-address-western.h	(working copy)
@@ -1 +1,25 @@
-link ../libebook-orbit/./e-address-western.h
\ No newline at end of file
+#ifndef __E_ADDRESS_WESTERN_H__
+#define __E_ADDRESS_WESTERN_H__
+
+G_BEGIN_DECLS
+
+typedef struct {
+	
+	/* Public */
+	char *po_box;
+	char *extended;  /* I'm not sure what this is. */
+	char *street;
+	char *locality;  /* For example, the city or town. */
+	char *region;	/* The state or province. */
+	char *postal_code;
+	char *country;
+} EAddressWestern;
+
+EAddressWestern *e_address_western_parse (const char *address);
+void e_address_western_free (EAddressWestern *eaw);
+
+G_END_DECLS
+
+#endif  /* ! __E_ADDRESS_WESTERN_H__ */
+
+
Index: addressbook/libebook-dbus/e-destination.c
===================================================================
--- addressbook/libebook-dbus/e-destination.c	(revision 409)
+++ addressbook/libebook-dbus/e-destination.c	(working copy)
@@ -39,17 +39,13 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <string.h>
-#include "e-book.h"
+#include <libebook/e-book.h>
 #include <libedataserver/e-data-server-marshal.h>
 
 #include <glib.h>
 #include <libxml/xmlmemory.h>
 #include <glib/gi18n-lib.h>
-#if ENABLE_CAMEL
 #include <camel/camel-internet-address.h>
-#else
-#include <ucamel/camel-internet-address.h>
-#endif
 
 #define d(x)
 
@@ -1126,34 +1122,34 @@
 	
 	g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
 	
-	dest_node = xmlNewNode (NULL, (xmlChar*)"destination");
+	dest_node = xmlNewNode (NULL, "destination");
 	
 	str = e_destination_get_name (dest);
 	if (str)
-		xmlNewTextChild (dest_node, NULL, (xmlChar*)"name", (xmlChar*)str);
+		xmlNewTextChild (dest_node, NULL, "name", str);
 	
 	if (!e_destination_is_evolution_list (dest)) {
 		str = e_destination_get_email (dest);
 		if (str)
-			xmlNewTextChild (dest_node, NULL, (xmlChar*)"email", (xmlChar*)str);
+			xmlNewTextChild (dest_node, NULL, "email", str);
 	} else {
 		GList *iter = dest->priv->list_dests;
 		
 		while (iter) {
 			EDestination *list_dest = E_DESTINATION (iter->data);
-			xmlNodePtr list_node = xmlNewNode (NULL, (xmlChar*)"list_entry");
+			xmlNodePtr list_node = xmlNewNode (NULL, "list_entry");
 
 			str = e_destination_get_name (list_dest);
 			if (str) {
-				xmlChar *escaped = xmlEncodeEntitiesReentrant (NULL, (xmlChar*)str);
-				xmlNewTextChild (list_node, NULL, (xmlChar*)"name", escaped);
+				char *escaped = xmlEncodeEntitiesReentrant (NULL, str);
+				xmlNewTextChild (list_node, NULL, "name", escaped);
 				xmlFree (escaped);
 			}
 			
 			str = e_destination_get_email (list_dest);
 			if (str) {
-				xmlChar *escaped = xmlEncodeEntitiesReentrant (NULL, (xmlChar*)str);
-				xmlNewTextChild (list_node, NULL, (xmlChar*)"email", escaped);
+				char *escaped = xmlEncodeEntitiesReentrant (NULL, str);
+				xmlNewTextChild (list_node, NULL, "email", str);
 				xmlFree (escaped);
 			}
 			
@@ -1162,15 +1158,15 @@
 			iter = g_list_next (iter);
 		}
 		
-		xmlNewProp (dest_node, (xmlChar*)"is_list", (xmlChar*)"yes");
-		xmlNewProp (dest_node, (xmlChar*)"show_addresses", 
-			    e_destination_list_show_addresses (dest) ? (xmlChar*)"yes" : (xmlChar*)"no");
+		xmlNewProp (dest_node, "is_list", "yes");
+		xmlNewProp (dest_node, "show_addresses", 
+			    e_destination_list_show_addresses (dest) ? "yes" : "no");
 	}
 	
 	str = e_destination_get_source_uid (dest);
 	if (str) {
-		xmlChar *escaped = xmlEncodeEntitiesReentrant (NULL, (xmlChar*)str);
-		xmlNewTextChild (dest_node, NULL, (xmlChar*)"source_uid", escaped);
+		char *escaped = xmlEncodeEntitiesReentrant (NULL, str);
+		xmlNewTextChild (dest_node, NULL, "source_uid", str);
 		xmlFree (escaped);
 	}
 	
@@ -1178,15 +1174,15 @@
 	if (str) {
 		char buf[16];
 		
-		xmlNodePtr uri_node = xmlNewTextChild (dest_node, NULL, (xmlChar*)"card_uid", (xmlChar*)str);
+		xmlNodePtr uri_node = xmlNewTextChild (dest_node, NULL, "card_uid", str);
 		g_snprintf (buf, 16, "%d", e_destination_get_email_num (dest));
-		xmlNewProp (uri_node, (xmlChar*)"email_num", (xmlChar*)buf);
+		xmlNewProp (uri_node, "email_num", buf);
 	}
 	
-	xmlNewProp (dest_node, (xmlChar*)"html_mail", e_destination_get_html_mail_pref (dest) ? (xmlChar*)"yes" : (xmlChar*)"no");
+	xmlNewProp (dest_node, "html_mail", e_destination_get_html_mail_pref (dest) ? "yes" : "no");
 	
-	xmlNewProp (dest_node, (xmlChar*)"auto_recipient",
-		    e_destination_is_auto_recipient (dest) ? (xmlChar*)"yes" : (xmlChar*)"no");
+	xmlNewProp (dest_node, "auto_recipient",
+		    e_destination_is_auto_recipient (dest) ? "yes" : "no");
 	
 	return dest_node;
 }
@@ -1214,28 +1210,28 @@
 	g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE);
 	g_return_val_if_fail (node != NULL, FALSE);
 	
-	if (strcmp ((char*)node->name, "destination"))
+	if (strcmp (node->name, "destination"))
 		return FALSE;
 	
-	tmp = (char*)xmlGetProp (node, (xmlChar*)"html_mail");
+	tmp = xmlGetProp (node, "html_mail");
 	if (tmp) {
 		html_mail = !strcmp (tmp, "yes");
 		xmlFree (tmp);
 	}
 	
-	tmp = (char*)xmlGetProp (node, (xmlChar*)"is_list");
+	tmp = xmlGetProp (node, "is_list");
 	if (tmp) {
 		is_list = !strcmp (tmp, "yes");
 		xmlFree (tmp);
 	}
 	
-	tmp = (char*)xmlGetProp (node, (xmlChar*)"show_addresses");
+	tmp = xmlGetProp (node, "show_addresses");
 	if (tmp) {
 		show_addr = !strcmp (tmp, "yes");
 		xmlFree (tmp);
 	}
 	
-	tmp = (char*)xmlGetProp (node, (xmlChar*)"auto_recipient");
+	tmp = xmlGetProp (node, "auto_recipient");
 	if (tmp) {
 		auto_recip = !strcmp (tmp, "yes");
 		xmlFree (tmp);
@@ -1243,28 +1239,28 @@
 	
 	node = node->xmlChildrenNode;
 	while (node) {
-		if (!strcmp ((char*)node->name, "name")) {
-			tmp = (char*)xmlNodeGetContent (node);
+		if (!strcmp (node->name, "name")) {
+			tmp = xmlNodeGetContent (node);
 			g_free (name);
 			name = g_strdup (tmp);
 			xmlFree (tmp);
-		} else if (!is_list && !strcmp ((char*)node->name, "email")) {
-			tmp = (char*)xmlNodeGetContent (node);
+		} else if (!is_list && !strcmp (node->name, "email")) {
+			tmp = xmlNodeGetContent (node);
 			g_free (email);
 			email = g_strdup (tmp);
 			xmlFree (tmp);
-		} else if (is_list && !strcmp ((char*)node->name, "list_entry")) {
+		} else if (is_list && !strcmp (node->name, "list_entry")) {
 			xmlNodePtr subnode = node->xmlChildrenNode;
 			char *list_name = NULL, *list_email = NULL;
 			
 			while (subnode) {
-				if (!strcmp ((char*)subnode->name, "name")) {
-					tmp = (char*)xmlNodeGetContent (subnode);
+				if (!strcmp (subnode->name, "name")) {
+					tmp = xmlNodeGetContent (subnode);
 					g_free (list_name);
 					list_name = g_strdup (tmp);
 					xmlFree (tmp);
-				} else if (!strcmp ((char*)subnode->name, "email")) {
-					tmp = (char*)xmlNodeGetContent (subnode);
+				} else if (!strcmp (subnode->name, "email")) {
+					tmp = xmlNodeGetContent (subnode);
 					g_free (list_email);
 					list_email = g_strdup (tmp);
 					xmlFree (tmp);
@@ -1286,18 +1282,18 @@
 				
 				list_dests = g_list_append (list_dests, list_dest);
 			}
-		} else if (!strcmp ((char*)node->name, "source_uid")) {
-			tmp = (char*)xmlNodeGetContent (node);
+		} else if (!strcmp (node->name, "source_uid")) {
+			tmp = xmlNodeGetContent (node);
 			g_free (source_uid);
 			source_uid = g_strdup (tmp);
 			xmlFree (tmp);
-		} else if (!strcmp ((char*)node->name, "card_uid")) {
-			tmp = (char*)xmlNodeGetContent (node);
+		} else if (!strcmp (node->name, "card_uid")) {
+			tmp = xmlNodeGetContent (node);
 			g_free (card_uid);
 			card_uid = g_strdup (tmp);
 			xmlFree (tmp);
 			
-			tmp = (char*)xmlGetProp (node, (xmlChar*)"email_num");
+			tmp = xmlGetProp (node, "email_num");
 			email_num = atoi (tmp);
 			xmlFree (tmp);
 		}
@@ -1345,7 +1341,7 @@
 	if (xml_in == NULL || size <= 0) 
 		return NULL;
 	
-	xml = g_strndup ((char*)xml_in, size);
+	xml = g_strndup (xml_in, size);
 	r = w = xml;
 	
 	while (*r) {
@@ -1393,7 +1389,7 @@
 	if (dest_node == NULL)
 		return NULL;
 	
-	dest_doc = xmlNewDoc ((xmlChar*)XML_DEFAULT_VERSION);
+	dest_doc = xmlNewDoc (XML_DEFAULT_VERSION);
 	xmlDocSetRootElement (dest_doc, dest_node);
 	
 	xmlDocDumpMemory (dest_doc, &buffer, &size);
@@ -1455,8 +1451,8 @@
 	if (destv == NULL || *destv == NULL)
 		return NULL;
 	
-	destv_doc  = xmlNewDoc ((xmlChar*)XML_DEFAULT_VERSION);
-	destv_node = xmlNewNode (NULL, (xmlChar*)"destinations");
+	destv_doc  = xmlNewDoc (XML_DEFAULT_VERSION);
+	destv_node = xmlNewNode (NULL, "destinations");
 	xmlDocSetRootElement (destv_doc, destv_node);
 	
 	for (i = 0; destv[i]; i++) {
@@ -1502,7 +1498,7 @@
 	
 	node = destv_doc->xmlRootNode;
 	
-	if (strcmp ((char*)node->name, "destinations"))
+	if (strcmp (node->name, "destinations"))
 		goto finished;
 	
 	node = node->xmlChildrenNode;
Index: addressbook/libebook-dbus/e-name-western-tables.h
===================================================================
--- addressbook/libebook-dbus/e-name-western-tables.h	(revision 409)
+++ addressbook/libebook-dbus/e-name-western-tables.h	(working copy)
@@ -1 +1,272 @@
-link ../libebook-orbit/./e-name-western-tables.h
\ No newline at end of file
+/* This file is generated by gen-western-table.py. DO NOT EDIT */
+static const char western_pfx_table[] = {
+  "mister\0"
+  "miss.\0"
+  "mr.\0"
+  "mrs.\0"
+  "ms.\0"
+  "miss\0"
+  "mr\0"
+  "mrs\0"
+  "ms\0"
+  "sir\0"
+  "professor\0"
+  "prof.\0"
+  "dr\0"
+  "dr.\0"
+  "doctor\0"
+  "judge\0"
+  "justice\0"
+  "chief justice\0"
+  "congressman\0"
+  "congresswoman\0"
+  "commander\0"
+  "lieutenant\0"
+  "lt.\0"
+  "colonel\0"
+  "col.\0"
+  "major\0"
+  "maj.\0"
+  "general\0"
+  "gen.\0"
+  "admiral\0"
+  "admr.\0"
+  "sergeant\0"
+  "sgt.\0"
+  "lord\0"
+  "lady\0"
+  "baron\0"
+  "baroness\0"
+  "duke\0"
+  "duchess\0"
+  "king\0"
+  "queen\0"
+  "prince\0"
+  "princess\0"
+  "the most honorable\0"
+  "the honorable\0"
+  "the reverend\0"
+  "his holiness\0"
+  "his eminence\0"
+  "his majesty\0"
+  "her majesty\0"
+  "his grace\0"
+  "her grace\0"
+  "president\0"
+  "vice president\0"
+  "secretary\0"
+  "undersecretary\0"
+  "consul\0"
+  "ambassador\0"
+  "senator\0"
+  "saint\0"
+  "st.\0"
+  "pastor\0"
+  "deacon\0"
+  "father\0"
+  "bishop\0"
+  "archbishop\0"
+  "cardinal\0"
+  "pope\0"
+  "reverend\0"
+  "rev.\0"
+  "rabbi\0"
+  "monsieur\0"
+  "m.\0"
+  "mademoiselle\0"
+  "melle\0"
+  "madame\0"
+  "mme\0"
+  "professeur\0"
+  "dauphin\0"
+  "dauphine\0"
+  "herr\0"
+  "frau\0"
+  "fraulein\0"
+  "herr doktor\0"
+  "doktor frau\0"
+  "doktor frau doktor\0"
+  "frau doktor\0"
+  "senor\0"
+  "senora\0"
+  "sra.\0"
+  "senorita\0"
+  "srita.\0"
+};
+static const guint western_pfx_index[] = {
+  0,
+  7,
+  13,
+  17,
+  22,
+  26,
+  31,
+  34,
+  38,
+  41,
+  45,
+  55,
+  61,
+  64,
+  68,
+  75,
+  81,
+  89,
+  103,
+  115,
+  129,
+  139,
+  150,
+  154,
+  162,
+  167,
+  173,
+  178,
+  186,
+  191,
+  199,
+  205,
+  214,
+  219,
+  224,
+  229,
+  235,
+  244,
+  249,
+  257,
+  262,
+  268,
+  275,
+  284,
+  303,
+  317,
+  330,
+  343,
+  356,
+  368,
+  380,
+  390,
+  400,
+  410,
+  425,
+  435,
+  450,
+  457,
+  468,
+  476,
+  482,
+  486,
+  493,
+  500,
+  507,
+  514,
+  525,
+  534,
+  539,
+  548,
+  553,
+  559,
+  568,
+  571,
+  584,
+  590,
+  597,
+  601,
+  612,
+  620,
+  629,
+  634,
+  639,
+  648,
+  660,
+  672,
+  691,
+  703,
+  709,
+  716,
+  721,
+  730,
+};
+
+static const char western_sfx_table[] = {
+  "junior\0"
+  "senior\0"
+  "jr\0"
+  "sr\0"
+  "I\0"
+  "II\0"
+  "III\0"
+  "IV\0"
+  "V\0"
+  "VI\0"
+  "VII\0"
+  "VIII\0"
+  "IX\0"
+  "X\0"
+  "XI\0"
+  "XII\0"
+  "XIII\0"
+  "XIV\0"
+  "XV\0"
+  "XVI\0"
+  "XVII\0"
+  "XVIII\0"
+  "XIX\0"
+  "XX\0"
+  "XXI\0"
+  "XXII\0"
+  "phd\0"
+  "ms\0"
+  "md\0"
+  "esq\0"
+  "esq.\0"
+  "esquire\0"
+};
+static const guint western_sfx_index[] = {
+  0,
+  7,
+  14,
+  17,
+  20,
+  22,
+  25,
+  29,
+  32,
+  34,
+  37,
+  41,
+  46,
+  49,
+  51,
+  54,
+  58,
+  63,
+  67,
+  70,
+  74,
+  79,
+  85,
+  89,
+  92,
+  96,
+  101,
+  105,
+  108,
+  111,
+  115,
+  120,
+};
+
+static const char western_complex_last_table[] = {
+  "van\0"
+  "von\0"
+  "de\0"
+  "di\0"
+};
+static const guint western_complex_last_index[] = {
+  0,
+  4,
+  8,
+  11,
+};
+
Index: addressbook/libebook-dbus/e-destination.h
===================================================================
--- addressbook/libebook-dbus/e-destination.h	(revision 409)
+++ addressbook/libebook-dbus/e-destination.h	(working copy)
@@ -1 +1,127 @@
-link ../libebook-orbit/e-destination.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * e-destination.h
+ *
+ * Copyright (C) 2001-2004 Ximian, Inc.
+ *
+ * Authors: Jon Trowbridge <trow@ximian.com>
+ *          Chris Toshok <toshok@ximian.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ * 
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA.
+ */
+
+#ifndef __E_DESTINATION_H__
+#define __E_DESTINATION_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libebook/e-contact.h>
+#include <libebook/e-book.h>
+#include <libxml/tree.h>
+
+#define E_TYPE_DESTINATION           (e_destination_get_type ())
+#define E_DESTINATION(o)             (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_DESTINATION, EDestination))
+#define E_DESTINATION_CLASS(k)       (G_TYPE_CHECK_CLASS_CAST ((k), E_TYPE_DESTINATION, EDestinationClass))
+#define E_IS_DESTINATION(o)          (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_DESTINATION))
+#define E_IS_DESTINATION_CLASS(k)    (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_DESTINATION))
+#define E_DESTINATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_DESTINATION, EDestinationClass))
+
+typedef struct _EDestination EDestination;
+typedef struct _EDestinationClass EDestinationClass;
+
+struct _EDestinationPrivate;
+
+struct _EDestination {
+	GObject object;
+
+	struct _EDestinationPrivate *priv;
+};
+
+struct _EDestinationClass {
+	GObjectClass parent_class;
+
+	/* Signals */
+	void (* changed) (EDestination *destination);
+
+	/* Padding for future expansion */
+	void (*_ebook_reserved1) (void);
+	void (*_ebook_reserved2) (void);
+	void (*_ebook_reserved3) (void);
+	void (*_ebook_reserved4) (void);
+};
+
+GType e_destination_get_type (void);
+
+
+EDestination  *e_destination_new                (void);
+EDestination  *e_destination_copy               (const EDestination *);
+
+gboolean       e_destination_empty              (const EDestination *);
+gboolean       e_destination_equal              (const EDestination *a, const EDestination *b);
+
+/* for use with EDestinations that wrap a particular contact */
+void           e_destination_set_contact        (EDestination *, EContact *contact, int email_num);
+void           e_destination_set_contact_uid    (EDestination *dest, const char *uid, gint email_num);
+void           e_destination_set_book           (EDestination *, EBook *book);
+EContact      *e_destination_get_contact        (const EDestination *);
+const char    *e_destination_get_source_uid     (const EDestination *);
+const char    *e_destination_get_contact_uid    (const EDestination *);
+int            e_destination_get_email_num      (const EDestination *);
+
+/* for use with EDestinations built up from strings (not corresponding to contacts in a user's address books) */
+void           e_destination_set_name           (EDestination *, const char *name);
+void           e_destination_set_email          (EDestination *, const char *email);
+const char    *e_destination_get_name           (const EDestination *);  /* "Jane Smith" */
+const char    *e_destination_get_email          (const EDestination *);  /* "jane@assbarn.com" */
+const char    *e_destination_get_address        (const EDestination *);  /* "Jane Smith <jane@assbarn.com>" (or a comma-sep set of such for a list) */
+
+gboolean       e_destination_is_evolution_list   (const EDestination *);
+gboolean       e_destination_list_show_addresses (const EDestination *);
+const GList   *e_destination_list_get_dests      (const EDestination *);
+
+/* If true, they want HTML mail. */
+void           e_destination_set_html_mail_pref (EDestination *dest, gboolean flag);
+gboolean       e_destination_get_html_mail_pref (const EDestination *);
+
+/* used by the evolution composer to manage automatic recipients
+
+   XXX should probably be implemented using a more neutral/extensible
+   approach instead of a hardcoded evolution-only flag. */
+gboolean       e_destination_is_auto_recipient  (const EDestination *);
+void           e_destination_set_auto_recipient (EDestination *, gboolean value);
+
+/* parse out an EDestination (name/email, not contact) from a free form string. */
+void           e_destination_set_raw            (EDestination *, const char *free_form_string);
+
+/* generate a plain-text representation of an EDestination* or EDestination** */
+const char    *e_destination_get_textrep        (const EDestination *, gboolean include_email);  /* "Jane Smith" or "jane@assbarn.com" */
+char          *e_destination_get_textrepv       (EDestination **);
+
+/* XML export/import routines. */
+char          *e_destination_export             (const EDestination *);
+char          *e_destination_exportv            (EDestination **);
+EDestination  *e_destination_import             (const char *str);
+EDestination **e_destination_importv            (const char *str);
+
+/* EVCard "export" routines */
+void          e_destination_export_to_vcard_attribute   (EDestination *dest, EVCardAttribute *attr);
+
+void           e_destination_freev              (EDestination **);
+
+#endif /* __E_DESTINATION_H__ */
+
Index: addressbook/libebook-dbus/e-name-western-tables.h.in
===================================================================
--- addressbook/libebook-dbus/e-name-western-tables.h.in	(revision 409)
+++ addressbook/libebook-dbus/e-name-western-tables.h.in	(working copy)
@@ -1 +1,133 @@
-link ../libebook-orbit/e-name-western-tables.h.in
\ No newline at end of file
+western_pfx
+mister
+miss.
+mr.
+mrs.
+ms.
+miss
+mr
+mrs
+ms
+sir
+professor
+prof.
+dr
+dr.
+doctor
+judge
+justice
+chief justice
+congressman
+congresswoman
+commander
+lieutenant
+lt.
+colonel
+col.
+major
+maj.
+general
+gen.
+admiral
+admr.
+sergeant
+sgt.
+lord
+lady
+baron
+baroness
+duke
+duchess
+king
+queen
+prince
+princess
+the most honorable
+the honorable
+the reverend
+his holiness
+his eminence
+his majesty
+her majesty
+his grace
+her grace
+president
+vice president
+secretary
+undersecretary
+consul
+ambassador
+senator
+saint
+st.
+pastor
+deacon
+father
+bishop
+archbishop
+cardinal
+pope
+reverend
+rev.
+rabbi
+monsieur
+m.
+mademoiselle
+melle
+madame
+mme
+professeur
+dauphin
+dauphine
+herr
+frau
+fraulein
+herr doktor
+doktor frau
+doktor frau doktor
+frau doktor
+senor
+senora
+sra.
+senorita
+srita.
+
+western_sfx
+junior
+senior
+jr
+sr
+I
+II
+III
+IV
+V
+VI
+VII
+VIII
+IX
+X
+XI
+XII
+XIII
+XIV
+XV
+XVI
+XVII
+XVIII
+XIX
+XX
+XXI
+XXII
+phd
+ms
+md
+esq
+esq.
+esquire
+
+western_complex_last
+van
+von
+de
+di
Index: addressbook/libebook-dbus/e-contact.c
===================================================================
--- addressbook/libebook-dbus/e-contact.c	(revision 409)
+++ addressbook/libebook-dbus/e-contact.c	(working copy)
@@ -1 +1,2066 @@
-link ../libebook-orbit/./e-contact.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-contact.c
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser 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: Chris Toshok (toshok@ximian.com)
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "e-contact.h"
+#include "e-book.h"
+#include "e-name-western.h"
+
+#define d(x)
+
+struct _EContactPrivate {
+	char *cached_strings [E_CONTACT_FIELD_LAST];
+};
+
+#define E_CONTACT_FIELD_TYPE_STRING       0x00000001   /* used for simple single valued attributes */
+/*E_CONTACT_FIELD_TYPE_FLOAT*/
+#define E_CONTACT_FIELD_TYPE_LIST         0x00000002   /* used for multivalued single attributes - the elements are of type char* */
+#define E_CONTACT_FIELD_TYPE_MULTI        0x00000004   /* used for multivalued attributes - the elements are of type EVCardAttribute */
+#define E_CONTACT_FIELD_TYPE_GETSET       0x00000008   /* used for attributes that need custom handling for getting/setting */
+#define E_CONTACT_FIELD_TYPE_STRUCT       0x00000010   /* used for structured types (N and ADR properties, in particular) */
+#define E_CONTACT_FIELD_TYPE_BOOLEAN      0x00000020   /* used for boolean types (WANTS_HTML) */
+
+#define E_CONTACT_FIELD_TYPE_SYNTHETIC    0x10000000   /* used when there isn't a corresponding vcard field (such as email_1) */
+#define E_CONTACT_FIELD_TYPE_LIST_ELEM    0x20000000   /* used when a synthetic attribute is a numbered list element */
+#define E_CONTACT_FIELD_TYPE_MULTI_ELEM   0x40000000   /* used when we're looking for the nth attribute where more than 1 can be present in the vcard */
+#define E_CONTACT_FIELD_TYPE_ATTR_TYPE    0x80000000   /* used when a synthetic attribute is flagged with a TYPE= that we'll be looking for */
+
+typedef struct {
+	guint32 t;
+
+	EContactField field_id;
+	const char *vcard_field_name;
+	const char *field_name;      /* non translated */
+	const char *pretty_name;     /* translated */
+	
+	gboolean read_only;
+
+	int list_elem;
+	const char *attr_type1;
+	const char *attr_type2;
+
+	void* (*struct_getter)(EContact *contact, EVCardAttribute *attribute);
+	void (*struct_setter)(EContact *contact, EVCardAttribute *attribute, void *data);
+
+	GType (*boxed_type_getter) (void);
+} EContactFieldInfo;
+
+static void* photo_getter (EContact *contact, EVCardAttribute *attr);
+static void photo_setter (EContact *contact, EVCardAttribute *attr, void *data);
+static void* fn_getter (EContact *contact, EVCardAttribute *attr);
+static void fn_setter (EContact *contact, EVCardAttribute *attr, void *data);
+static void* n_getter (EContact *contact, EVCardAttribute *attr);
+static void n_setter (EContact *contact, EVCardAttribute *attr, void *data);
+static void* adr_getter (EContact *contact, EVCardAttribute *attr);
+static void adr_setter (EContact *contact, EVCardAttribute *attr, void *data);
+static void* date_getter (EContact *contact, EVCardAttribute *attr);
+static void date_setter (EContact *contact, EVCardAttribute *attr, void *data);
+static void* cert_getter (EContact *contact, EVCardAttribute *attr);
+static void cert_setter (EContact *contact, EVCardAttribute *attr, void *data);
+
+#define STRING_FIELD(id,vc,n,pn,ro)  { E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro) }
+#define BOOLEAN_FIELD(id,vc,n,pn,ro)  { E_CONTACT_FIELD_TYPE_BOOLEAN, (id), (vc), (n), (pn), (ro) }
+#define LIST_FIELD(id,vc,n,pn,ro)      { E_CONTACT_FIELD_TYPE_LIST, (id), (vc), (n), (pn), (ro) }
+#define MULTI_LIST_FIELD(id,vc,n,pn,ro) { E_CONTACT_FIELD_TYPE_MULTI, (id), (vc), (n), (pn), (ro) }
+#define GETSET_FIELD(id,vc,n,pn,ro,get,set)    { E_CONTACT_FIELD_TYPE_STRING | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), -1, NULL, NULL, (get), (set) }
+#define STRUCT_FIELD(id,vc,n,pn,ro,get,set,ty)    { E_CONTACT_FIELD_TYPE_STRUCT | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), -1, NULL, NULL, (get), (set), (ty) }
+#define SYNTH_STR_FIELD(id,n,pn,ro)  { E_CONTACT_FIELD_TYPE_STRING | E_CONTACT_FIELD_TYPE_SYNTHETIC, (id), NULL, (n), (pn), (ro) }
+#define LIST_ELEM_STR_FIELD(id,vc,n,pn,ro,nm) { E_CONTACT_FIELD_TYPE_LIST_ELEM | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nm) }
+#define MULTI_ELEM_STR_FIELD(id,vc,n,pn,ro,nm) { E_CONTACT_FIELD_TYPE_MULTI_ELEM | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nm) }
+#define ATTR_TYPE_STR_FIELD(id,vc,n,pn,ro,at1,nth) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nth), (at1), NULL }
+#define ATTR_TYPE_GETSET_FIELD(id,vc,n,pn,ro,at1,nth,get,set) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), (nth), (at1), NULL, (get), (set) }
+#define ATTR2_TYPE_STR_FIELD(id,vc,n,pn,ro,at1,at2,nth) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nth), (at1), (at2) }
+#define ATTR_TYPE_STRUCT_FIELD(id,vc,n,pn,ro,at,get,set,ty) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_GETSET | E_CONTACT_FIELD_TYPE_STRUCT, (id), (vc), (n), (pn), (ro), 0, (at), NULL, (get), (set), (ty) }
+
+/* This *must* be kept in the same order as the EContactField enum */
+static const EContactFieldInfo field_info[] = {
+	{0,}, /* Dummy row as EContactField starts from 1 */
+ 	STRING_FIELD (E_CONTACT_UID,        EVC_UID,       "id",         N_("Unique ID"),  FALSE),
+	STRING_FIELD (E_CONTACT_FILE_AS,    EVC_X_FILE_AS, "file-as",    N_("File Under"),    FALSE),
+	STRING_FIELD (E_CONTACT_BOOK_URI, EVC_X_BOOK_URI, "book-uri", N_("Book URI"), FALSE),
+
+	/* Name fields */
+	GETSET_FIELD        (E_CONTACT_FULL_NAME,   EVC_FN,       "full-name",   N_("Full Name"),   FALSE, fn_getter, fn_setter),
+	LIST_ELEM_STR_FIELD (E_CONTACT_GIVEN_NAME,  EVC_N,        "given-name",  N_("Given Name"),  FALSE, 1),
+	LIST_ELEM_STR_FIELD (E_CONTACT_FAMILY_NAME, EVC_N,        "family-name", N_("Family Name"), FALSE, 0),
+	STRING_FIELD        (E_CONTACT_NICKNAME,    EVC_NICKNAME, "nickname",    N_("Nickname"),    FALSE),
+
+	/* Email fields */
+	MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_1,    EVC_EMAIL,        "email-1",    N_("Email 1"),         FALSE, 0),
+	MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_2,    EVC_EMAIL,        "email-2",    N_("Email 2"),         FALSE, 1),
+	MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_3,    EVC_EMAIL,        "email-3",    N_("Email 3"),         FALSE, 2),
+	MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_4,    EVC_EMAIL,        "email-4",    N_("Email 4"),         FALSE, 3),
+
+	STRING_FIELD         (E_CONTACT_MAILER,     EVC_MAILER,       "mailer",     N_("Mailer"),          FALSE),
+
+	/* Address Labels */
+	ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_HOME,  EVC_LABEL, "address-label-home",  N_("Home Address Label"),  FALSE, "HOME", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_WORK,  EVC_LABEL, "address-label-work",  N_("Work Address Label"),  FALSE, "WORK", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_OTHER, EVC_LABEL, "address-label-other", N_("Other Address Label"), FALSE, "OTHER", 0),
+
+	/* Phone fields */
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_ASSISTANT,    EVC_TEL, "assistant-phone",   N_("Assistant Phone"),  FALSE, EVC_X_ASSISTANT, 0),
+	ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS,     EVC_TEL, "business-phone",    N_("Business Phone"),   FALSE, "WORK", "VOICE",         0),
+	ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS_2,   EVC_TEL, "business-phone-2",  N_("Business Phone 2"), FALSE, "WORK", "VOICE",         1),
+	ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS_FAX, EVC_TEL, "business-fax",      N_("Business Fax"),     FALSE, "WORK", "FAX",           0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_CALLBACK,     EVC_TEL, "callback-phone",    N_("Callback Phone"),   FALSE, EVC_X_CALLBACK,  0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_CAR,          EVC_TEL, "car-phone",         N_("Car Phone"),        FALSE, "CAR",                   0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_COMPANY,      EVC_TEL, "company-phone",     N_("Company Phone"),    FALSE, EVC_X_COMPANY,   0),
+	ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME,         EVC_TEL, "home-phone",        N_("Home Phone"),       FALSE, "HOME", "VOICE",         0),
+	ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME_2,       EVC_TEL, "home-phone-2",      N_("Home Phone 2"),     FALSE, "HOME", "VOICE",         1),
+	ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME_FAX,     EVC_TEL, "home-fax",          N_("Home Fax"),         FALSE, "HOME", "FAX",           0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_ISDN,         EVC_TEL, "isdn-phone",        N_("ISDN"),             FALSE, "ISDN",                  0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_MOBILE,       EVC_TEL, "mobile-phone",      N_("Mobile Phone"),     FALSE, "CELL",                  0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_OTHER,        EVC_TEL, "other-phone",       N_("Other Phone"),      FALSE, "VOICE",                 0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_OTHER_FAX,    EVC_TEL, "other-fax",         N_("Other Fax"),        FALSE, "FAX",                   0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_PAGER,        EVC_TEL, "pager",             N_("Pager"),            FALSE, "PAGER",                 0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_PRIMARY,      EVC_TEL, "primary-phone",     N_("Primary Phone"),    FALSE, "PREF",                  0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_RADIO,        EVC_TEL, "radio",             N_("Radio"),            FALSE, EVC_X_RADIO,     0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_TELEX,        EVC_TEL, "telex",             N_("Telex"),            FALSE, EVC_X_TELEX,     0),
+	ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_TTYTDD,       EVC_TEL, "tty",               N_("TTY"),              FALSE, EVC_X_TTYTDD,    0),
+	
+	/* Organizational fields */
+	LIST_ELEM_STR_FIELD (E_CONTACT_ORG,      EVC_ORG, "org",      N_("Organization"),        FALSE, 0),
+	LIST_ELEM_STR_FIELD (E_CONTACT_ORG_UNIT, EVC_ORG, "org-unit", N_("Organizational Unit"), FALSE, 1),
+	LIST_ELEM_STR_FIELD (E_CONTACT_OFFICE,   EVC_ORG, "office",   N_("Office"),              FALSE, 2),
+	STRING_FIELD    (E_CONTACT_TITLE,     EVC_TITLE,       "title",     N_("Title"),           FALSE),
+	STRING_FIELD    (E_CONTACT_ROLE,      EVC_ROLE,        "role",      N_("Role"),            FALSE),
+	STRING_FIELD    (E_CONTACT_MANAGER,   EVC_X_MANAGER,   "manager",   N_("Manager"),         FALSE),
+	STRING_FIELD    (E_CONTACT_ASSISTANT, EVC_X_ASSISTANT, "assistant", N_("Assistant"),       FALSE),
+
+	/* Web fields */
+	STRING_FIELD (E_CONTACT_HOMEPAGE_URL, EVC_URL,         "homepage-url", N_("Homepage URL"), FALSE),
+	STRING_FIELD (E_CONTACT_BLOG_URL,     EVC_X_BLOG_URL,  "blog-url",     N_("Weblog URL"),   FALSE),
+
+	/* Contact categories */
+	SYNTH_STR_FIELD (E_CONTACT_CATEGORIES,                    "categories",    N_("Categories"),    FALSE),
+
+	/* Collaboration fields */
+	STRING_FIELD (E_CONTACT_CALENDAR_URI, EVC_CALURI,      "caluri",     N_("Calendar URI"),  FALSE),
+	STRING_FIELD (E_CONTACT_FREEBUSY_URL, EVC_FBURL,       "fburl",       N_("Free/Busy URL"), FALSE),
+	STRING_FIELD (E_CONTACT_ICS_CALENDAR, EVC_ICSCALENDAR, "icscalendar", N_("ICS Calendar"),  FALSE),
+	STRING_FIELD (E_CONTACT_VIDEO_URL,    EVC_X_VIDEO_URL, "video-url",    N_("Video Conferencing URL"),   FALSE),
+
+	/* Misc fields */
+	STRING_FIELD (E_CONTACT_SPOUSE, EVC_X_SPOUSE,    "spouse", N_("Spouse's Name"), FALSE),
+	STRING_FIELD (E_CONTACT_NOTE,   EVC_NOTE,        "note",   N_("Note"),          FALSE),
+
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_1,    EVC_X_AIM,    "im-aim-home-1",    N_("AIM Home Screen Name 1"),    FALSE, "HOME", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_2,    EVC_X_AIM,    "im-aim-home-2",    N_("AIM Home Screen Name 2"),    FALSE, "HOME", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_3,    EVC_X_AIM,    "im-aim-home-3",    N_("AIM Home Screen Name 3"),    FALSE, "HOME", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_1,    EVC_X_AIM,    "im-aim-work-1",    N_("AIM Work Screen Name 1"),    FALSE, "WORK", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_2,    EVC_X_AIM,    "im-aim-work-2",    N_("AIM Work Screen Name 2"),    FALSE, "WORK", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_3,    EVC_X_AIM,    "im-aim-work-3",    N_("AIM Work Screen Name 3"),    FALSE, "WORK", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_1, EVC_X_GROUPWISE, "im-groupwise-home-1", N_("GroupWise Home Screen Name 1"),    FALSE, "HOME", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_2, EVC_X_GROUPWISE, "im-groupwise-home-2", N_("GroupWise Home Screen Name 2"),    FALSE, "HOME", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_3, EVC_X_GROUPWISE, "im-groupwise-home-3", N_("GroupWise Home Screen Name 3"),    FALSE, "HOME", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_1, EVC_X_GROUPWISE, "im-groupwise-work-1", N_("GroupWise Work Screen Name 1"),    FALSE, "WORK", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_2, EVC_X_GROUPWISE, "im-groupwise-work-2", N_("GroupWise Work Screen Name 2"),    FALSE, "WORK", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_3, EVC_X_GROUPWISE, "im-groupwise-work-3", N_("GroupWise Work Screen Name 3"),    FALSE, "WORK", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_1, EVC_X_JABBER, "im-jabber-home-1", N_("Jabber Home Id 1"),          FALSE, "HOME", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_2, EVC_X_JABBER, "im-jabber-home-2", N_("Jabber Home Id 2"),          FALSE, "HOME", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_3, EVC_X_JABBER, "im-jabber-home-3", N_("Jabber Home Id 3"),          FALSE, "HOME", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_1, EVC_X_JABBER, "im-jabber-work-1", N_("Jabber Work Id 1"),          FALSE, "WORK", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_2, EVC_X_JABBER, "im-jabber-work-3", N_("Jabber Work Id 2"),          FALSE, "WORK", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_3, EVC_X_JABBER, "im-jabber-work-2", N_("Jabber Work Id 3"),          FALSE, "WORK", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_1,  EVC_X_YAHOO,  "im-yahoo-home-1",  N_("Yahoo! Home Screen Name 1"), FALSE, "HOME", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_2,  EVC_X_YAHOO,  "im-yahoo-home-2",  N_("Yahoo! Home Screen Name 2"), FALSE, "HOME", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_3,  EVC_X_YAHOO,  "im-yahoo-home-3",  N_("Yahoo! Home Screen Name 3"), FALSE, "HOME", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_1,  EVC_X_YAHOO,  "im-yahoo-work-1",  N_("Yahoo! Work Screen Name 1"), FALSE, "WORK", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_2,  EVC_X_YAHOO,  "im-yahoo-work-2",  N_("Yahoo! Work Screen Name 2"), FALSE, "WORK", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_3,  EVC_X_YAHOO,  "im-yahoo-work-3",  N_("Yahoo! Work Screen Name 3"), FALSE, "WORK", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_1,    EVC_X_MSN,    "im-msn-home-1",    N_("MSN Home Screen Name 1"),    FALSE, "HOME", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_2,    EVC_X_MSN,    "im-msn-home-2",    N_("MSN Home Screen Name 2"),    FALSE, "HOME", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_3,    EVC_X_MSN,    "im-msn-home-3",    N_("MSN Home Screen Name 3"),    FALSE, "HOME", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_1,    EVC_X_MSN,    "im-msn-work-1",    N_("MSN Work Screen Name 1"),    FALSE, "WORK", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_2,    EVC_X_MSN,    "im-msn-work-2",    N_("MSN Work Screen Name 2"),    FALSE, "WORK", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_3,    EVC_X_MSN,    "im-msn-work-3",    N_("MSN Work Screen Name 3"),    FALSE, "WORK", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_1,    EVC_X_ICQ,    "im-icq-home-1",    N_("ICQ Home Id 1"),             FALSE, "HOME", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_2,    EVC_X_ICQ,    "im-icq-home-2",    N_("ICQ Home Id 2"),             FALSE, "HOME", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_3,    EVC_X_ICQ,    "im-icq-home-3",    N_("ICQ Home Id 3"),             FALSE, "HOME", 2),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_1,    EVC_X_ICQ,    "im-icq-work-1",    N_("ICQ Work Id 1"),             FALSE, "WORK", 0),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_2,    EVC_X_ICQ,    "im-icq-work-2",    N_("ICQ Work Id 2"),             FALSE, "WORK", 1),
+	ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_3,    EVC_X_ICQ,    "im-icq-work-3",    N_("ICQ Work Id 3"),             FALSE, "WORK", 2),
+
+	/* Last modified time */
+	STRING_FIELD (E_CONTACT_REV, EVC_REV, "Rev", N_("Last Revision"), FALSE),
+	SYNTH_STR_FIELD     (E_CONTACT_NAME_OR_ORG,               "name-or-org", N_("Name or Org"), TRUE),
+
+	/* Address fields */
+	MULTI_LIST_FIELD       (E_CONTACT_ADDRESS,       EVC_ADR, "address",       N_("Address List"),  FALSE),
+	ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_HOME,  EVC_ADR, "address-home",  N_("Home Address"),  FALSE, "HOME",  adr_getter, adr_setter, e_contact_address_get_type),
+	ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_WORK,  EVC_ADR, "address-work",  N_("Work Address"),  FALSE, "WORK",  adr_getter, adr_setter, e_contact_address_get_type),
+	ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_OTHER, EVC_ADR, "address-other", N_("Other Address"), FALSE, "OTHER", adr_getter, adr_setter, e_contact_address_get_type),
+
+	/* Contact categories */
+	LIST_FIELD      (E_CONTACT_CATEGORY_LIST, EVC_CATEGORIES, "category-list", N_("Category List"), FALSE),
+
+	/* Photo/Logo */
+	STRUCT_FIELD    (E_CONTACT_PHOTO, EVC_PHOTO, "photo", N_("Photo"), FALSE, photo_getter, photo_setter, e_contact_photo_get_type),
+	STRUCT_FIELD    (E_CONTACT_LOGO,  EVC_LOGO,  "logo",  N_("Logo"),  FALSE, photo_getter, photo_setter, e_contact_photo_get_type),
+
+	STRUCT_FIELD        (E_CONTACT_NAME,        EVC_N,        "name",        N_("Name"),        FALSE, n_getter, n_setter, e_contact_name_get_type),
+	MULTI_LIST_FIELD     (E_CONTACT_EMAIL,      EVC_EMAIL,        "email",      N_("Email List"),      FALSE),
+
+	/* Instant messaging fields */
+	MULTI_LIST_FIELD (E_CONTACT_IM_AIM,       EVC_X_AIM,       "im-aim",       N_("AIM Screen Name List"),    FALSE),
+	MULTI_LIST_FIELD (E_CONTACT_IM_GROUPWISE, EVC_X_GROUPWISE, "im-groupwise", N_("GroupWise Id List"),       FALSE),
+	MULTI_LIST_FIELD (E_CONTACT_IM_JABBER, 	  EVC_X_JABBER,    "im-jabber",    N_("Jabber Id List"),          FALSE),
+	MULTI_LIST_FIELD (E_CONTACT_IM_YAHOO,  	  EVC_X_YAHOO,     "im-yahoo",     N_("Yahoo! Screen Name List"), FALSE),
+	MULTI_LIST_FIELD (E_CONTACT_IM_MSN,    	  EVC_X_MSN,       "im-msn",       N_("MSN Screen Name List"),    FALSE),
+	MULTI_LIST_FIELD (E_CONTACT_IM_ICQ,    	  EVC_X_ICQ,       "im-icq",       N_("ICQ Id List"),             FALSE),
+
+	BOOLEAN_FIELD        (E_CONTACT_WANTS_HTML, EVC_X_WANTS_HTML, "wants-html", N_("Wants HTML Mail"), FALSE),
+
+	BOOLEAN_FIELD (E_CONTACT_IS_LIST,             EVC_X_LIST, "list", N_("List"), FALSE),
+	BOOLEAN_FIELD (E_CONTACT_LIST_SHOW_ADDRESSES, EVC_X_LIST_SHOW_ADDRESSES, "list-show-addresses", N_("List Show Addresses"), FALSE),
+
+	STRUCT_FIELD (E_CONTACT_BIRTH_DATE,  EVC_BDAY,          "birth-date",  N_("Birth Date"), FALSE, date_getter, date_setter, e_contact_date_get_type),
+	STRUCT_FIELD (E_CONTACT_ANNIVERSARY, EVC_X_ANNIVERSARY, "anniversary", N_("Anniversary"), FALSE, date_getter, date_setter, e_contact_date_get_type),
+
+	/* Security fields */
+	ATTR_TYPE_STRUCT_FIELD (E_CONTACT_X509_CERT,  EVC_KEY, "x509Cert",  N_("X.509 Certificate"), FALSE, "X509", cert_getter, cert_setter, e_contact_cert_get_type), 
+
+	MULTI_LIST_FIELD (E_CONTACT_OSSO_CONTACT_STATE,       EVC_X_OSSO_CONTACT_STATE,       "osso-contact-state",       N_("Contact State"),    FALSE),
+};
+
+#undef LIST_ELEM_STR_FIELD
+#undef STRING_FIELD
+#undef SYNTH_STR_FIELD
+#undef LIST_FIELD
+#undef GETSET_FIELD
+
+static GObjectClass *parent_class;
+
+static void e_contact_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void e_contact_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+
+static void
+e_contact_dispose (GObject *object)
+{
+	EContact *ec = E_CONTACT (object);
+
+	if (ec->priv) {
+		g_free (ec->priv);
+		ec->priv = NULL;
+	}
+
+	if (G_OBJECT_CLASS (parent_class)->dispose)
+		G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+e_contact_class_init (EContactClass *klass)
+{
+	GObjectClass *object_class;
+	int i;
+
+	object_class = G_OBJECT_CLASS(klass);
+
+	parent_class = g_type_class_ref (E_TYPE_VCARD);
+
+	object_class->dispose = e_contact_dispose;
+	object_class->set_property = e_contact_set_property;
+	object_class->get_property = e_contact_get_property;
+
+	for (i = E_CONTACT_FIELD_FIRST; i < E_CONTACT_FIELD_LAST; i++) {
+		GParamSpec *pspec = NULL;
+
+		/* Verify the table is correctly ordered */
+		g_assert (i == field_info[i].field_id);
+		
+		if (field_info[i].t & E_CONTACT_FIELD_TYPE_STRING)
+			pspec = g_param_spec_string (field_info[i].field_name,
+						     _(field_info[i].pretty_name),
+						     "" /* XXX blurb */,
+						     NULL,
+						     field_info[i].read_only ? E_PARAM_READABLE : E_PARAM_READWRITE);
+		else if (field_info[i].t & E_CONTACT_FIELD_TYPE_BOOLEAN)
+			pspec = g_param_spec_boolean (field_info[i].field_name,
+						      _(field_info[i].pretty_name),
+						      "" /* XXX blurb */,
+						      FALSE,
+						      field_info[i].read_only ? E_PARAM_READABLE : E_PARAM_READWRITE);
+		else if (field_info[i].t & E_CONTACT_FIELD_TYPE_STRUCT)
+			pspec = g_param_spec_boxed (field_info[i].field_name,
+						    _(field_info[i].pretty_name),
+						    "" /* XXX blurb */,
+						    field_info[i].boxed_type_getter(),
+						    field_info[i].read_only ? E_PARAM_READABLE : E_PARAM_READWRITE);
+		else
+			pspec = g_param_spec_pointer (field_info[i].field_name,
+						      _(field_info[i].pretty_name),
+						      "" /* XXX blurb */,
+						      field_info[i].read_only ? E_PARAM_READABLE : E_PARAM_READWRITE);
+
+		g_object_class_install_property (object_class, field_info[i].field_id,
+						 pspec);
+	}
+}
+
+static void
+e_contact_init (EContact *ec)
+{
+	ec->priv = g_new0 (EContactPrivate, 1);
+}
+
+GType
+e_contact_get_type (void)
+{
+	static GType contact_type = 0;
+
+	if (!contact_type) {
+		static const GTypeInfo contact_info =  {
+			sizeof (EContactClass),
+			NULL,           /* base_init */
+			NULL,           /* base_finalize */
+			(GClassInitFunc) e_contact_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (EContact),
+			0,             /* n_preallocs */
+			(GInstanceInitFunc) e_contact_init,
+		};
+
+		contact_type = g_type_register_static (E_TYPE_VCARD, "EContact", &contact_info, 0);
+	}
+
+	return contact_type;
+}
+
+static EVCardAttribute*
+e_contact_get_first_attr (EContact *contact, const char *attr_name)
+{
+	GList *attrs, *l;
+
+	attrs = e_vcard_get_attributes (E_VCARD (contact));
+
+	for (l = attrs; l; l = l->next) {
+		EVCardAttribute *attr = l->data;
+		const char *name;
+
+		name = e_vcard_attribute_get_name (attr);
+
+		if (!strcmp (name, attr_name))
+			return attr;
+	}
+
+	return NULL;
+}
+
+
+
+static void*
+photo_getter (EContact *contact, EVCardAttribute *attr)
+{
+	GList *values;
+
+	if (!attr)
+		return NULL;
+	
+	values = e_vcard_attribute_get_param (attr, EVC_ENCODING);
+	if (values && g_ascii_strcasecmp (values->data, "b") == 0) {
+		values = e_vcard_attribute_get_values_decoded (attr);	
+		if (values && values->data) {
+			GString *s = values->data;
+			EContactPhoto *photo;
+			
+			if (!s->len)
+				return NULL;
+			
+			photo = g_new0 (EContactPhoto, 1);
+			photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
+			photo->data.inlined.length = s->len;
+			photo->data.inlined.data = g_malloc (photo->data.inlined.length);
+			memcpy (photo->data.inlined.data, s->str, photo->data.inlined.length);
+
+			values = e_vcard_attribute_get_param (attr, EVC_TYPE);
+			if (values && values->data)
+				photo->data.inlined.mime_type = g_strdup_printf("image/%s", (char*)values->data);
+			return photo;
+		}
+	}
+
+	values = e_vcard_attribute_get_param (attr, EVC_VALUE);
+	if (values && g_ascii_strcasecmp (values->data, "uri") == 0) {
+		EContactPhoto *photo;
+		photo = g_new0 (EContactPhoto, 1);
+		photo->type = E_CONTACT_PHOTO_TYPE_URI;
+		photo->data.uri = e_vcard_attribute_get_value (attr);
+		return photo;
+	}
+	return NULL;
+}
+
+static void
+photo_setter (EContact *contact, EVCardAttribute *attr, void *data)
+{
+	EContactPhoto *photo = data;
+	char *image_type, *p;
+
+	switch (photo->type) {
+	case E_CONTACT_PHOTO_TYPE_INLINED:
+		g_return_if_fail (photo->data.inlined.length > 0);
+		
+		e_vcard_attribute_add_param_with_value (attr,
+							e_vcard_attribute_param_new (EVC_ENCODING),
+							"b");
+		if (photo->data.inlined.mime_type && (p = strchr (photo->data.inlined.mime_type, '/'))) {
+			image_type = p+1;
+		} else {
+			image_type = "X-EVOLUTION-UNKNOWN";
+		}
+		e_vcard_attribute_add_param_with_value (attr,
+							e_vcard_attribute_param_new (EVC_TYPE),
+							image_type);
+		
+		e_vcard_attribute_add_value_decoded (attr, (char*)photo->data.inlined.data, photo->data.inlined.length);
+		break;
+	case E_CONTACT_PHOTO_TYPE_URI:
+		e_vcard_attribute_add_param_with_value (attr,
+							e_vcard_attribute_param_new (EVC_VALUE),
+							"uri");
+		e_vcard_attribute_add_value (attr, photo->data.uri);
+		break;
+	default:
+		g_warning ("Unknown EContactPhotoType %d", photo->type);
+		break;
+	}
+}
+
+
+static void*
+fn_getter (EContact *contact, EVCardAttribute *attr)
+{
+	if (attr) {
+		GList *p = e_vcard_attribute_get_values (attr);
+
+		return p && p->data ? p->data : "";
+	}
+	else
+		return NULL;
+}
+
+static void
+fn_setter (EContact *contact, EVCardAttribute *attr, void *data)
+{
+	gchar *name_str = data;
+
+	e_vcard_attribute_add_value (attr, name_str);
+
+	attr = e_contact_get_first_attr (contact, EVC_N);
+	if (!attr) {
+		EContactName *name = e_contact_name_from_string ((char*)data);
+
+		attr = e_vcard_attribute_new (NULL, EVC_N);
+		e_vcard_add_attribute (E_VCARD (contact), attr);
+
+		/* call the setter directly */
+		n_setter (contact, attr, name);
+
+		e_contact_name_free (name);
+	}
+}
+
+
+
+static void*
+n_getter (EContact *contact, EVCardAttribute *attr)
+{
+	EContactName *name = g_new0 (EContactName, 1);
+	EVCardAttribute *new_attr;
+	char *name_str;
+
+	if (attr) {
+		GList *p = e_vcard_attribute_get_values (attr);
+
+		name->family     = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		name->given      = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		name->additional = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		name->prefixes   = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		name->suffixes   = g_strdup (p && p->data ? p->data : "");
+	}
+
+	new_attr = e_contact_get_first_attr (contact, EVC_FN);
+	if (!new_attr) {
+		new_attr = e_vcard_attribute_new (NULL, EVC_FN);
+		e_vcard_add_attribute (E_VCARD (contact), new_attr);
+		name_str = e_contact_name_to_string (name);
+		e_vcard_attribute_add_value (new_attr, name_str);
+		g_free (name_str);
+	}
+
+	return name;
+}
+
+static void
+n_setter (EContact *contact, EVCardAttribute *attr, void *data)
+{
+	EContactName *name = data;
+
+	e_vcard_attribute_add_value (attr, name->family);
+	e_vcard_attribute_add_value (attr, name->given);
+	e_vcard_attribute_add_value (attr, name->additional);
+	e_vcard_attribute_add_value (attr, name->prefixes);
+	e_vcard_attribute_add_value (attr, name->suffixes);
+
+	/* now find the attribute for FileAs.  if it's not present, fill it in */
+	attr = e_contact_get_first_attr (contact, EVC_X_FILE_AS);
+	if (!attr) {
+		char *strings[3], **stringptr;
+		char *string;
+		attr = e_vcard_attribute_new (NULL, EVC_X_FILE_AS);
+		e_vcard_add_attribute (E_VCARD (contact), attr);
+
+		stringptr = strings;
+		if (name->family && *name->family)
+			*(stringptr++) = name->family;
+		if (name->given && *name->given)
+			*(stringptr++) = name->given;
+		*stringptr = NULL;
+		string = g_strjoinv(", ", strings);
+
+		e_vcard_attribute_add_value (attr, string);
+		g_free (string);
+	}
+
+}
+
+
+
+static void*
+adr_getter (EContact *contact, EVCardAttribute *attr)
+{
+	if (attr) {
+		GList *p = e_vcard_attribute_get_values (attr);
+		EContactAddress *addr = g_new0 (EContactAddress, 1);
+
+		addr->address_format = g_strdup ("");
+		addr->po       = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		addr->ext      = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		addr->street   = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		addr->locality = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		addr->region   = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		addr->code     = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+		addr->country  = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
+
+		return addr;
+	}
+
+	return NULL;
+}
+
+static void
+adr_setter (EContact *contact, EVCardAttribute *attr, void *data)
+{
+	EContactAddress *addr = data;
+
+	e_vcard_attribute_add_value (attr, addr->po);
+	e_vcard_attribute_add_value (attr, addr->ext);
+	e_vcard_attribute_add_value (attr, addr->street);
+	e_vcard_attribute_add_value (attr, addr->locality);
+	e_vcard_attribute_add_value (attr, addr->region);
+	e_vcard_attribute_add_value (attr, addr->code);
+	e_vcard_attribute_add_value (attr, addr->country);
+}
+
+
+
+static void*
+date_getter (EContact *contact, EVCardAttribute *attr)
+{
+	if (attr) {
+		GList *p = e_vcard_attribute_get_values (attr);
+		EContactDate *date;
+
+		if (p && p->data && ((gchar *) p->data) [0])
+			date = e_contact_date_from_string ((char *) p->data);
+		else
+			date = NULL;
+
+		return date;
+	}
+
+	return NULL;
+}
+
+static void
+date_setter (EContact *contact, EVCardAttribute *attr, void *data)
+{
+	EContactDate *date = data;
+	char *str;
+
+	str = e_contact_date_to_string (date);
+
+	e_vcard_attribute_add_value (attr, str);
+	g_free (str);
+}
+
+
+
+static void*
+cert_getter (EContact *contact, EVCardAttribute *attr)
+{
+	if (attr) {
+		/* the certificate is stored in this vcard.  just
+		   return the data */
+		GList *values = e_vcard_attribute_get_values_decoded (attr);
+
+		if (values && values->data) {
+			GString *s = values->data;
+			EContactCert *cert = g_new0 (EContactCert, 1);
+
+			cert->length = s->len;
+			cert->data = g_malloc (cert->length);
+			memcpy (cert->data, s->str, cert->length);
+
+			return cert;
+		}
+	}
+
+	/* XXX if we stored a fingerprint in the cert we could look it
+	   up via NSS, but that would require the additional NSS dep
+	   here, and we'd have more than one process opening the
+	   certdb, which is bad.  *sigh* */
+	
+	return NULL;
+}
+
+static void
+cert_setter (EContact *contact, EVCardAttribute *attr, void *data)
+{
+	EContactCert *cert = data;
+
+	e_vcard_attribute_add_param_with_value (attr,
+						e_vcard_attribute_param_new (EVC_ENCODING),
+						"b");
+
+	e_vcard_attribute_add_value_decoded (attr, cert->data, cert->length);
+}
+
+
+
+/* Set_arg handler for the contact */
+static void
+e_contact_set_property (GObject *object,
+			guint prop_id,
+			const GValue *value,
+			GParamSpec *pspec)
+{
+	EContact *contact = E_CONTACT (object);
+	const EContactFieldInfo *info = NULL;
+
+	if (prop_id < 1 || prop_id >= E_CONTACT_FIELD_LAST) {
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		return;
+	}
+
+	info = &field_info[prop_id];
+
+	if (info->t & E_CONTACT_FIELD_TYPE_MULTI) {
+		GList *new_values = g_value_get_pointer (value);
+		GList *l;
+
+		/* first we remove all attributes of the type we're
+		   adding, then add new ones based on the values that
+		   are passed in */
+		e_vcard_remove_attributes (E_VCARD (contact), NULL, info->vcard_field_name);
+
+		for (l = new_values; l; l = l->next)
+			e_vcard_add_attribute_with_value (E_VCARD (contact),
+							  e_vcard_attribute_new (NULL, info->vcard_field_name),
+							  (char*)l->data);
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_SYNTHETIC) {
+		if (info->t & E_CONTACT_FIELD_TYPE_MULTI_ELEM) {
+			/* XXX this is kinda broken - we don't insert
+			   insert padding elements if, e.g. the user
+			   sets email 3 when email 1 and 2 don't
+			   exist.  But, if we *did* pad the lists we'd
+			   end up with empty items in the vcard.  I
+			   dunno which is worse. */
+			EVCardAttribute *attr = NULL;
+			gboolean found = FALSE;
+			int num_left = info->list_elem;
+			GList *attrs = e_vcard_get_attributes (E_VCARD (contact));
+			GList *l;
+			const char *sval;
+
+			for (l = attrs; l; l = l->next) {
+				const char *name;
+
+				attr = l->data;
+				name = e_vcard_attribute_get_name (attr);
+
+				if (!strcmp (name, info->vcard_field_name)) {
+					if (num_left-- == 0) {
+						found = TRUE;
+						break;
+					}
+				}
+			}
+
+			sval = g_value_get_string (value);
+			if (sval && *sval) {
+				if (found) {
+					/* we found it, overwrite it */
+					e_vcard_attribute_remove_values (attr);
+				}
+				else {
+					/* we didn't find it - add a new attribute */
+					attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
+					if (!strcmp(info->vcard_field_name, "EMAIL") && 
+					    !info->attr_type1 && 
+					    !info->attr_type2) {
+						/* Add default type */
+						e_vcard_attribute_add_param_with_value ( attr,
+								e_vcard_attribute_param_new (EVC_TYPE),
+								"OTHER");
+					}
+					e_vcard_add_attribute (E_VCARD (contact), attr);
+				}
+
+				e_vcard_attribute_add_value (attr, sval);
+			}
+			else {
+				if (found)
+					e_vcard_remove_attribute (E_VCARD (contact), attr);
+			}
+		}
+		else if (info->t & E_CONTACT_FIELD_TYPE_ATTR_TYPE) {
+			/* XXX this is kinda broken - we don't insert
+			   insert padding elements if, e.g. the user
+			   sets email 3 when email 1 and 2 don't
+			   exist.  But, if we *did* pad the lists we'd
+			   end up with empty items in the vcard.  I
+			   dunno which is worse. */
+			EVCardAttribute *attr = NULL;
+			gboolean found = FALSE;
+			int num_left = info->list_elem;
+			GList *attrs = e_vcard_get_attributes (E_VCARD (contact));
+			GList *l;
+
+			for (l = attrs; l && !found; l = l->next) {
+				const char *name;
+				gboolean found_needed1, found_needed2;
+
+				found_needed1 = (info->attr_type1 == NULL);
+				found_needed2 = (info->attr_type2 == NULL);
+
+				attr = l->data;
+				name = e_vcard_attribute_get_name (attr);
+
+				if (!strcmp (name, info->vcard_field_name)) {
+					GList *params;
+
+					for (params = e_vcard_attribute_get_params (attr); params; params = params->next) {
+						EVCardAttributeParam *param = params->data;
+						const char *name = e_vcard_attribute_param_get_name (param);
+
+						if (!strcmp (name, EVC_TYPE)) {
+							GList *values = e_vcard_attribute_param_get_values (param);
+							if (values && values->data) {
+								gboolean matches = FALSE;
+								if (!found_needed1 && !strcasecmp ((char*)values->data, info->attr_type1)) {
+									found_needed1 = TRUE;
+									matches = TRUE;
+								}
+								else if (!found_needed2 && !strcasecmp ((char*)values->data, info->attr_type2)) {
+									found_needed2 = TRUE;
+									matches = TRUE;
+								}
+								if (!matches) {
+									/* this is to enforce that we find an attribute
+									   with *only* the TYPE='s we need.  This may seem like
+									   an odd restriction but it's the only way at present to
+									   implement the Other Fax and Other Phone attributes. */
+									found_needed1 =
+										found_needed2 = FALSE;
+									break;
+								}
+							}
+						}
+
+						if (found_needed1 && found_needed2) {
+							if (num_left-- == 0) {
+								found = TRUE;
+								break;
+							}
+						}
+					}
+				}
+			}
+
+			if (found) {
+				/* we found it, overwrite it */
+				e_vcard_attribute_remove_values (attr);
+			}
+			else {
+				/* we didn't find it - add a new attribute */
+				attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
+				e_vcard_add_attribute (E_VCARD (contact), attr);
+				if (info->attr_type1)
+					e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_TYPE),
+										info->attr_type1);
+				if (info->attr_type2)
+					e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_TYPE),
+										info->attr_type2);
+			}
+
+			if (info->t & E_CONTACT_FIELD_TYPE_STRUCT || info->t & E_CONTACT_FIELD_TYPE_GETSET) {
+				void *data = info->t & E_CONTACT_FIELD_TYPE_STRUCT ? g_value_get_boxed (value) : (char*)g_value_get_string (value);
+
+				if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
+				    || (data && *(char*)data))
+					info->struct_setter (contact, attr, data);
+				else
+					e_vcard_remove_attribute (E_VCARD (contact), attr);
+			}
+			else {
+				const char *sval = g_value_get_string (value);
+
+				if (sval && *sval)
+					e_vcard_attribute_add_value (attr, sval);
+				else
+					e_vcard_remove_attribute (E_VCARD (contact), attr);
+			}
+		}
+		else if (info->t & E_CONTACT_FIELD_TYPE_LIST_ELEM) {
+			EVCardAttribute *attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+			GList *values;
+			GList *p;
+			const char *sval = g_value_get_string (value);
+
+			if (!attr) {
+				if (!sval || !*sval)
+					return;
+
+				d(printf ("adding new %s\n", info->vcard_field_name));
+
+				attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
+				e_vcard_add_attribute (E_VCARD (contact), attr);
+			}
+			
+			values = e_vcard_attribute_get_values (attr);
+			p = g_list_nth (values, info->list_elem);
+
+			if (p) {
+				g_free (p->data);
+				p->data = g_strdup (g_value_get_string (value));
+			}
+			else {
+				/* there weren't enough elements in the list, pad it */
+				int count = info->list_elem - g_list_length (values);
+
+				while (count--)
+					e_vcard_attribute_add_value (attr, "");
+
+				e_vcard_attribute_add_value (attr, g_value_get_string (value));
+			}
+		}
+		else {
+			switch (info->field_id) {
+			case E_CONTACT_CATEGORIES: {
+				EVCardAttribute *attr = e_contact_get_first_attr (contact, EVC_CATEGORIES);
+				char **split, **s;
+				const char *str;
+
+				if (attr)
+					e_vcard_attribute_remove_values (attr);
+				else {
+					/* we didn't find it - add a new attribute */
+					attr = e_vcard_attribute_new (NULL, EVC_CATEGORIES);
+					e_vcard_add_attribute (E_VCARD (contact), attr);
+				}
+
+				str = g_value_get_string (value);
+				if (str && *str) {
+					split = g_strsplit (str, ",", 0);
+					if (split) {
+						for (s = split; *s; s++) {
+							e_vcard_attribute_add_value (attr, g_strstrip (*s));
+						}
+						g_strfreev (split);
+					} else
+						e_vcard_attribute_add_value (attr, str);
+				}
+				else {
+					d(printf ("removing %s\n", info->vcard_field_name));
+
+					e_vcard_remove_attribute (E_VCARD (contact), attr);
+				}
+				break;
+			}
+			default:
+				g_warning ("unhandled synthetic field 0x%02x", info->field_id);
+				break;
+			}
+		}
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT || info->t & E_CONTACT_FIELD_TYPE_GETSET) {
+		EVCardAttribute *attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+		void *data = info->t & E_CONTACT_FIELD_TYPE_STRUCT ? g_value_get_boxed (value) : (char*)g_value_get_string (value);
+
+		if (attr) {
+			if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
+			    || (data && *(char*)data)) {
+				d(printf ("overwriting existing %s\n", info->vcard_field_name));
+				/* remove all existing values and parameters.
+				   the setter will add the correct ones */
+				e_vcard_attribute_remove_values (attr);
+				e_vcard_attribute_remove_params (attr);
+
+				info->struct_setter (contact, attr, data);
+			}
+			else {
+				d(printf ("removing %s\n", info->vcard_field_name));
+
+				e_vcard_remove_attribute (E_VCARD (contact), attr);
+			}
+		}
+		else if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
+			 || (data && *(char*)data)) {
+			d(printf ("adding new %s\n", info->vcard_field_name));
+			attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
+
+			e_vcard_add_attribute (E_VCARD (contact), attr);
+
+			info->struct_setter (contact, attr, data);
+		}
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
+		EVCardAttribute *attr;
+
+		/* first we search for an attribute we can overwrite */
+		attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+		if (attr) {
+			d(printf ("setting %s to `%s'\n", info->vcard_field_name, g_value_get_string (value)));
+			e_vcard_attribute_remove_values (attr);
+			e_vcard_attribute_add_value (attr, g_value_get_boolean (value) ? "TRUE" : "FALSE");
+		}
+		else {
+			/* and if we don't find one we create a new attribute */
+			e_vcard_add_attribute_with_value (E_VCARD (contact),
+							  e_vcard_attribute_new (NULL, info->vcard_field_name),
+							  g_value_get_boolean (value) ? "TRUE" : "FALSE");
+		}
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
+		EVCardAttribute *attr;
+		const char *sval = g_value_get_string (value);
+
+		/* first we search for an attribute we can overwrite */
+		attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+		if (attr) {
+			d(printf ("setting %s to `%s'\n", info->vcard_field_name, sval));
+			e_vcard_attribute_remove_values (attr);
+			if (sval) {
+				e_vcard_attribute_add_value (attr, sval);
+			}
+			else {
+				d(printf ("removing %s\n", info->vcard_field_name));
+
+				e_vcard_remove_attribute (E_VCARD (contact), attr);
+			}
+
+		}
+		else if (sval) {
+			/* and if we don't find one we create a new attribute */
+			e_vcard_add_attribute_with_value (E_VCARD (contact),
+							  e_vcard_attribute_new (NULL, info->vcard_field_name),
+							  g_value_get_string (value));
+		}
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
+		EVCardAttribute *attr;
+		GList *values, *l;
+
+		values = g_value_get_pointer (value);
+
+		attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+
+		if (attr) {
+			e_vcard_attribute_remove_values (attr);
+
+			if (!values)
+				e_vcard_remove_attribute (E_VCARD (contact), attr);
+		}
+		else if (values) {
+			attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
+			e_vcard_add_attribute (E_VCARD (contact), attr);
+		}
+
+		for (l = values; l != NULL; l = l->next)
+			e_vcard_attribute_add_value (attr, l->data);
+	}
+	else {
+		g_warning ("unhandled attribute `%s'", info->vcard_field_name);
+	}
+}
+
+static EVCardAttribute *
+e_contact_find_attribute_with_types (EContact *contact, const char *attr_name, const char *type_needed1, const char *type_needed2, int nth)
+{
+	GList *l, *attrs;
+	gboolean found_needed1, found_needed2;
+
+	attrs = e_vcard_get_attributes (E_VCARD (contact));
+
+	for (l = attrs; l; l = l->next) {
+		EVCardAttribute *attr = l->data;
+		const char *name;
+
+		found_needed1 = (type_needed1 == NULL);
+		found_needed2 = (type_needed2 == NULL);
+
+		name = e_vcard_attribute_get_name (attr);
+
+		if (!strcmp (name, attr_name)) {
+			GList *params;
+
+			for (params = e_vcard_attribute_get_params (attr); params; params = params->next) {
+				EVCardAttributeParam *param = params->data;
+				const char *name = e_vcard_attribute_param_get_name (param);
+
+				if (!strcmp (name, EVC_TYPE)) {
+					GList *values = e_vcard_attribute_param_get_values (param);
+					if (values && values->data) {
+						gboolean matches = FALSE;
+
+						if (!found_needed1 && !strcasecmp ((char*)values->data, type_needed1)) {
+							found_needed1 = TRUE;
+							matches = TRUE;
+						}
+						else if (!found_needed2 && !strcasecmp ((char*)values->data, type_needed2)) {
+							found_needed2 = TRUE;
+							matches = TRUE;
+						}
+
+						if (!matches) {
+							/* this is to enforce that we find an attribute
+							   with *only* the TYPE='s we need.  This may seem like
+							   an odd restriction but it's the only way at present to
+							   implement the Other Fax and Other Phone attributes. */
+							found_needed1 =
+								found_needed2 = FALSE;
+							break;
+						}
+					}
+				}
+
+				if (found_needed1 && found_needed2) {
+					if (nth-- == 0)
+						return attr;
+					else
+						break;
+				}
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static void
+e_contact_get_property (GObject *object,
+			guint prop_id,
+			GValue *value,
+			GParamSpec *pspec)
+{
+	const EContactFieldInfo *info = NULL;
+	gpointer data;
+	
+	if (prop_id < 1 || prop_id >= E_CONTACT_FIELD_LAST) {
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		g_value_reset (value);
+		return;
+	}
+	
+	info = &field_info[prop_id];
+	data = e_contact_get (E_CONTACT (object), prop_id);
+
+	if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
+		g_value_set_boolean (value, (gboolean)data);
+	} else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
+		g_value_set_pointer (value, data);
+#if 0
+	} else if (info->t & E_CONTACT_FIELD_TYPE_LIST_ELEM && info->t & E_CONTACT_FIELD_TYPE_STRING) {
+		g_value_set_string (value, data);
+	} else if (info->t & E_CONTACT_FIELD_TYPE_MULTI_ELEM && info->t & E_CONTACT_FIELD_TYPE_STRING) {
+		g_value_set_string (value, data);
+	} else if (info->t & E_CONTACT_FIELD_TYPE_ATTR_TYPE) {
+		if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
+			g_value_set_string (value, data);
+		} else {
+			g_value_set_boxed (value, data);
+ 		}
+#endif
+	} else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
+		g_value_take_boxed (value, data);
+	} else if (info->t & E_CONTACT_FIELD_TYPE_GETSET) {
+		if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
+			g_value_set_boxed (value, data);
+		} else {
+			g_value_set_string (value, data);
+ 		}
+	} else if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
+		g_value_set_string (value, data);
+	} else {
+		g_value_set_pointer (value, data);
+ 	}
+}
+
+
+
+/**
+ * e_contact_new:
+ *
+ * Creates a new, blank #EContact.
+ *
+ * Return value: A new #EContact.
+ **/
+EContact*
+e_contact_new (void)
+{
+	return e_contact_new_from_vcard ("");
+}
+
+/**
+ * e_contact_new_from_vcard:
+ * @vcard: a string representing a vcard
+ * 
+ * Creates a new #EContact based on a vcard.
+ *
+ * Return value: A new #EContact.
+ **/
+EContact*
+e_contact_new_from_vcard  (const char *vcard)
+{
+	EContact *contact;
+	const gchar *file_as;
+
+	g_return_val_if_fail (vcard != NULL, NULL);
+
+	contact = g_object_new (E_TYPE_CONTACT, NULL);
+	e_vcard_construct (E_VCARD (contact), vcard);
+
+	/* Generate a FILE_AS field if needed */
+
+	file_as = e_contact_get_const (contact, E_CONTACT_FILE_AS);
+	if (!file_as || !*file_as) {
+		EContactName *name;
+		const gchar *org;
+		gchar *file_as_new = NULL;
+		gchar *strings [4];
+		gchar **strings_p = strings;
+
+		name = e_contact_get (contact, E_CONTACT_NAME);
+		org = e_contact_get_const (contact, E_CONTACT_ORG);
+
+		if (name) {
+			if (name->family && *name->family)
+				*(strings_p++) = name->family;
+			if (name->given && *name->given)
+				*(strings_p++) = name->given;
+
+			if (strings_p != strings) {
+				*strings_p = NULL;
+				file_as_new = g_strjoinv (", ", strings);
+			}
+
+			e_contact_name_free (name);
+		}
+
+		if (!file_as_new && org && *org)
+			file_as_new = g_strdup (org);
+
+		if (file_as_new) {
+			e_contact_set (contact, E_CONTACT_FILE_AS, file_as_new);
+			g_free (file_as_new);
+		}
+	}
+
+	return contact;
+}
+
+/**
+ * e_contact_duplicate:
+ * @contact: an #EContact
+ *
+ * Creates a copy of @contact.
+ *
+ * Return value: A new #EContact identical to @contact.
+ **/
+EContact*
+e_contact_duplicate (EContact *contact)
+{
+	char *vcard;
+	EContact *c;
+
+	g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+
+	vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+	c = e_contact_new_from_vcard (vcard);
+	g_free (vcard);
+
+	return c;
+}
+
+/**
+ * e_contact_field_name:
+ * @field_id: an #EContactField
+ *
+ * Gets the string representation of @field_id.
+ *
+ * Return value: The string representation of @field_id, or %NULL if it doesn't exist.
+ **/
+const char *
+e_contact_field_name (EContactField field_id)
+{
+	g_return_val_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST, "");
+
+	return field_info[field_id].field_name;
+}
+
+/**
+ * e_contact_pretty_name:
+ * @field_id: an #EContactField
+ *
+ * Gets a human-readable, translated string representation
+ * of @field_id.
+ *
+ * Return value: The human-readable representation of @field_id, or %NULL if it doesn't exist.
+ **/
+const char *
+e_contact_pretty_name (EContactField field_id)
+{
+	g_return_val_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST, "");
+
+#ifdef ENABLE_NLS
+	bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+	return _(field_info[field_id].pretty_name);
+}
+
+/**
+ * e_contact_vcard_attribute:
+ * @field_id: an #EContactField
+ *
+ * Gets the vcard attribute corresponding to @field_id, as a string.
+ *
+ * Return value: The vcard attribute corresponding to @field_id, or %NULL if it doesn't exist.
+ **/
+const char*
+e_contact_vcard_attribute  (EContactField field_id)
+{
+	g_return_val_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST, "");
+
+	return field_info[field_id].vcard_field_name;
+}
+
+/**
+ * e_contact_field_id:
+ * @field_name: a string representing a contact field
+ * 
+ * Gets the #EContactField corresponding to the @field_name.
+ *
+ * Return value: An #EContactField corresponding to @field_name, or %0 if it doesn't exist.
+ **/
+EContactField
+e_contact_field_id (const char *field_name)
+{
+	int i;
+
+	for (i = E_CONTACT_FIELD_FIRST; i < E_CONTACT_FIELD_LAST; i ++) {
+		if (!strcmp (field_info[i].field_name, field_name))
+			return field_info[i].field_id;
+	}
+
+	g_warning ("unknown field name `%s'", field_name);
+	return 0;
+}
+
+EContactField
+e_contact_field_id_from_vcard (const char *vcard_field)
+{
+	int i;
+
+	for (i = E_CONTACT_FIELD_FIRST; i < E_CONTACT_FIELD_LAST; i ++) {
+		if (field_info[i].vcard_field_name == NULL)
+			continue;
+		if (field_info[i].t & E_CONTACT_FIELD_TYPE_SYNTHETIC)
+			continue;
+		if (!strcmp (field_info[i].vcard_field_name, vcard_field))
+			return field_info[i].field_id;
+	}
+
+	g_warning ("unknown vCard field `%s'", vcard_field);
+	return 0;
+}
+
+/**
+ * e_contact_get:
+ * @contact: an #EContact
+ * @field_id: an #EContactField
+ *
+ * Gets the value of @contact's field specified by @field_id.
+ *
+ * Return value: Depends on the field's type, owned by the caller.
+ **/
+gpointer
+e_contact_get (EContact *contact, EContactField field_id)
+{
+	const EContactFieldInfo *info = NULL;
+ 
+	g_return_val_if_fail (contact && E_IS_CONTACT (contact), NULL);
+	g_return_val_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST, NULL);
+
+	info = &field_info[field_id];
+
+	if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
+		EVCardAttribute *attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+		gboolean rv = FALSE;
+
+		if (attr) {
+			GList *v = e_vcard_attribute_get_values (attr);
+			rv = v && v->data && !g_ascii_strcasecmp ((char*)v->data, "true");
+			return (gpointer)rv;
+		}
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
+		EVCardAttribute *attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+
+		if (attr) {
+			GList *list = g_list_copy (e_vcard_attribute_get_values (attr));
+			GList *l;
+			for (l = list; l; l = l->next)
+				l->data = g_strdup (l->data);
+			return list;
+		}
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_LIST_ELEM) {
+		if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
+			EVCardAttribute *attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+
+			if (attr) {
+				GList *v;
+				
+				v = e_vcard_attribute_get_values (attr);
+				v = g_list_nth (v, info->list_elem);
+				
+				return v ? g_strdup (v->data) : NULL;
+			}
+		}
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_MULTI_ELEM) {
+		if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
+			GList *attrs, *l;
+			int num_left = info->list_elem;
+
+			attrs = e_vcard_get_attributes (E_VCARD (contact));
+
+			for (l = attrs; l; l = l->next) {
+				EVCardAttribute *attr = l->data;
+				const char *name;
+
+				name = e_vcard_attribute_get_name (attr);
+
+				if (!strcmp (name, info->vcard_field_name)) {
+					if (num_left-- == 0) {
+						GList *v = e_vcard_attribute_get_values (attr);
+
+						return v ? g_strdup (v->data) : NULL;
+					}
+				}
+			}
+		}
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_ATTR_TYPE) {
+		EVCardAttribute *attr = e_contact_find_attribute_with_types (contact, info->vcard_field_name, info->attr_type1, info->attr_type2, info->list_elem);
+
+		if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
+			if (attr) {
+				GList *p = e_vcard_attribute_get_values (attr);
+				return g_strdup (p->data);
+			}
+			else {
+				return NULL;
+			}
+		}
+		else { /* struct */
+			return info->struct_getter (contact, attr);
+		}
+
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
+		EVCardAttribute *attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+		if (attr)
+			return info->struct_getter (contact, attr);
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_GETSET) {
+		EVCardAttribute *attr = e_contact_get_first_attr (contact, info->vcard_field_name);
+		void *rv = NULL;
+
+		if (attr)
+			rv = info->struct_getter (contact, attr);
+
+		if (info->t & E_CONTACT_FIELD_TYPE_STRUCT)
+			return (gpointer)info->boxed_type_getter();
+		else
+			return g_strdup (rv);
+	}
+	else if (info->t & E_CONTACT_FIELD_TYPE_SYNTHETIC) {
+		switch (info->field_id) {
+		case E_CONTACT_NAME_OR_ORG: {
+			const char *str;
+
+			str = e_contact_get_const (contact, E_CONTACT_FILE_AS);
+			if (!str)
+				str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
+			if (!str)
+				str = e_contact_get_const (contact, E_CONTACT_ORG);
+			if (!str) {
+				gboolean is_list = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_IS_LIST));
+
+				if (is_list)
+					str = _("Unnamed List");
+				else
+					str = e_contact_get_const (contact, E_CONTACT_EMAIL_1);
+			}
+
+			return g_strdup (str);
+		}
+		case E_CONTACT_CATEGORIES: {
+			EVCardAttribute *attr = e_contact_get_first_attr (contact, EVC_CATEGORIES);
+			char *rv = NULL;
+
+			if (attr) {
+				GString *str = g_string_new ("");
+				GList *v = e_vcard_attribute_get_values (attr);
+				while (v) {
+					g_string_append (str, (char*)v->data);
+					v = v->next;
+					if (v)
+						g_string_append_c (str, ',');
+				}
+
+				rv = g_string_free (str, FALSE);
+			}
+			return rv;
+			break;
+		}
+		default:
+			g_warning ("unhandled synthetic field 0x%02x", info->field_id);
+			break;
+		}
+	}
+	else {
+		GList *attrs, *l;
+		GList *rv = NULL; /* used for multi attribute lists */
+
+		attrs = e_vcard_get_attributes (E_VCARD (contact));
+
+		for (l = attrs; l; l = l->next) {
+			EVCardAttribute *attr = l->data;
+			const char *name;
+
+			name = e_vcard_attribute_get_name (attr);
+
+			if (!strcmp (name, info->vcard_field_name)) {
+				GList *v;
+				v = e_vcard_attribute_get_values (attr);
+
+				if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
+					return v ? g_strdup (v->data) : NULL;
+				}
+				else {
+					rv = g_list_append (rv, v ? g_strdup (v->data) : NULL);
+				}
+			}
+		}
+		return rv;
+	}
+	return NULL;
+}
+
+static void
+free_const_data (gpointer data, GObject *where_object_was)
+{
+	g_free (data);
+}
+
+/**
+ * e_contact_get_const:
+ * @contact: an #EContact
+ * @field_id: an #EContactField
+ *
+ * Gets the value of @contact's field specified by @field_id, caching
+ * the result so it can be freed later.
+ *
+ * Return value: Depends on the field's type, owned by the #EContact.
+ **/
+gconstpointer
+e_contact_get_const (EContact *contact, EContactField field_id)
+{
+	gboolean is_string = FALSE;
+	gpointer value = NULL;
+
+	g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+	g_return_val_if_fail (field_id >= 1 && field_id <= E_CONTACT_LAST_SIMPLE_STRING, NULL);
+
+	if (field_info [field_id].t & E_CONTACT_FIELD_TYPE_STRING)
+		is_string = TRUE;
+
+	if (is_string)
+		value = contact->priv->cached_strings[field_id];
+
+	if (!value) {
+		value = e_contact_get (contact, field_id);
+		if (is_string && value)
+			contact->priv->cached_strings[field_id] = value;
+
+		if (value)
+			g_object_weak_ref (G_OBJECT (contact), free_const_data, value);
+	}
+
+	return value;
+}
+
+/**
+ * e_contact_set;
+ * @contact: an #EContact
+ * @field_id: an #EContactField
+ * @value: a value whose type depends on the @field_id
+ *
+ * Sets the value of @contact's field specified by @field_id to @value.
+ **/
+void
+e_contact_set (EContact *contact, EContactField field_id, gpointer value)
+{
+	d(printf ("e_contact_set (%p, %d, %p)\n", contact, field_id, value));
+
+	g_return_if_fail (contact && E_IS_CONTACT (contact));
+	g_return_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST);
+
+	/* set the cached slot to NULL so we'll re-get the new string
+	   if e_contact_get_const is called again */
+	contact->priv->cached_strings[field_id] = NULL;
+
+	g_object_set (contact,
+		      e_contact_field_name (field_id), value,
+		      NULL);
+}
+
+/**
+ * e_contact_get_attributes:
+ * @contact: an #EContact
+ * @field_id: an #EContactField
+ *
+ * Gets a list of the vcard attributes for @contact's @field_id.
+ *
+ * Return value: A #GList of pointers to #EVCardAttribute, owned by the caller.
+ **/
+GList*
+e_contact_get_attributes (EContact *contact, EContactField field_id)
+{
+	GList *l = NULL;
+	GList *attrs, *a;
+	const EContactFieldInfo *info = NULL;
+
+	g_return_val_if_fail (contact && E_IS_CONTACT (contact), NULL);
+	g_return_val_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST, NULL);
+
+	info = &field_info[field_id];
+
+	attrs = e_vcard_get_attributes (E_VCARD (contact));
+
+	for (a = attrs; a; a = a->next) {
+		EVCardAttribute *attr = a->data;
+		const char *name;
+
+		name = e_vcard_attribute_get_name (attr);
+
+		if (!strcmp (name, info->vcard_field_name)) {
+			l = g_list_prepend (l, e_vcard_attribute_copy (attr));
+		}
+	}
+
+	return g_list_reverse(l);
+}
+
+/**
+ * e_contact_set_attributes:
+ * @contact: an #EContact
+ * @field_id: an #EContactField
+ * @attributes: a #GList of pointers to #EVCardAttribute
+ *
+ * Sets the vcard attributes for @contact's @field_id.
+ **/
+void
+e_contact_set_attributes (EContact *contact, EContactField field_id, GList *attributes)
+{
+	const EContactFieldInfo *info = NULL;
+	GList *l;
+
+	g_return_if_fail (contact && E_IS_CONTACT (contact));
+	g_return_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST);
+
+	info = &field_info[field_id];
+
+	e_vcard_remove_attributes (E_VCARD (contact), NULL, info->vcard_field_name);
+
+	for (l = attributes; l; l = l->next)
+		e_vcard_add_attribute (E_VCARD (contact),
+				       e_vcard_attribute_copy ((EVCardAttribute*)l->data));
+}
+
+/**
+ * e_contact_name_new:
+ *
+ * Creates a new #EContactName struct.
+ *
+ * Return value: A new #EContactName struct.
+ **/
+EContactName*
+e_contact_name_new ()
+{
+	return g_new0 (EContactName, 1);
+}
+
+/**
+ * e_contact_name_to_string:
+ * @name: an #EContactName
+ *
+ * Generates a string representation of @name.
+ *
+ * Return value: The string representation of @name.
+ **/
+char *
+e_contact_name_to_string(const EContactName *name)
+{
+	char *strings[6], **stringptr = strings;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	if (name->prefixes && *name->prefixes)
+		*(stringptr++) = name->prefixes;
+	if (name->given && *name->given)
+		*(stringptr++) = name->given;
+	if (name->additional && *name->additional)
+		*(stringptr++) = name->additional;
+	if (name->family && *name->family)
+		*(stringptr++) = name->family;
+	if (name->suffixes && *name->suffixes)
+		*(stringptr++) = name->suffixes;
+	*stringptr = NULL;
+	return g_strjoinv(" ", strings);
+}
+
+/**
+ * e_contact_name_from_string:
+ * @name_str: a string representing a contact's full name
+ *
+ * Creates a new #EContactName based on the parsed @name_str.
+ *
+ * Return value: A new #EContactName struct.
+ **/
+EContactName*
+e_contact_name_from_string (const char *name_str)
+{
+	EContactName *name = e_contact_name_new();
+	ENameWestern *western;
+
+	g_return_val_if_fail (name_str != NULL, NULL);
+
+	western = e_name_western_parse (name_str ? name_str : "");
+	
+	name->prefixes   = g_strdup (western->prefix);
+	name->given      = g_strdup (western->first );
+	name->additional = g_strdup (western->middle);
+	name->family     = g_strdup (western->last  );
+	name->suffixes   = g_strdup (western->suffix);
+	
+	e_name_western_free(western);
+	
+	return name;
+}
+
+/**
+ * e_contact_name_copy:
+ * @n: an #EContactName
+ *
+ * Creates a copy of @n.
+ *
+ * Return value: A new #EContactName identical to @n.
+ **/
+EContactName*
+e_contact_name_copy (EContactName *n)
+{
+	EContactName *name;
+
+	g_return_val_if_fail (n != NULL, NULL);
+
+	name = e_contact_name_new();
+	
+	name->prefixes   = g_strdup (n->prefixes);
+	name->given      = g_strdup (n->given);
+	name->additional = g_strdup (n->additional);
+	name->family     = g_strdup (n->family);
+	name->suffixes   = g_strdup (n->suffixes);
+	
+	return name;
+}
+
+/**
+ * e_contact_name_free:
+ * @name: an #EContactName
+ *
+ * Frees @name and its contents.
+ **/
+void
+e_contact_name_free (EContactName *name)
+{
+	if (!name)
+		return;
+
+	g_free (name->family);
+	g_free (name->given);
+	g_free (name->additional);
+	g_free (name->prefixes);
+	g_free (name->suffixes);
+
+	g_free (name);
+}
+
+GType
+e_contact_name_get_type (void)
+{
+	static GType type_id = 0;
+
+	if (!type_id)
+		type_id = g_boxed_type_register_static ("EContactName",
+							(GBoxedCopyFunc) e_contact_name_copy,
+							(GBoxedFreeFunc) e_contact_name_free);
+	return type_id;
+}
+
+/**
+ * e_contact_date_from_string:
+ * @str: a date string in the format YYYY-MM-DD or YYYYMMDD
+ *
+ * Creates a new #EContactDate based on @str.
+ *
+ * Return value: A new #EContactDate struct.
+ **/
+EContactDate*
+e_contact_date_from_string (const char *str)
+{
+	EContactDate* date;
+	int length;
+
+	g_return_val_if_fail (str != NULL, NULL);
+
+	date = e_contact_date_new();
+	length = strlen(str);
+	
+	if (length == 10 ) {
+		date->year = str[0] * 1000 + str[1] * 100 + str[2] * 10 + str[3] - '0' * 1111;
+		date->month = str[5] * 10 + str[6] - '0' * 11;
+		date->day = str[8] * 10 + str[9] - '0' * 11;
+	} else if ( length == 8 ) {
+		date->year = str[0] * 1000 + str[1] * 100 + str[2] * 10 + str[3] - '0' * 1111;
+		date->month = str[4] * 10 + str[5] - '0' * 11;
+		date->day = str[6] * 10 + str[7] - '0' * 11;
+	}
+	
+	return date;
+}
+
+/**
+ * e_contact_date_to_string:
+ * @dt: an #EContactDate
+ *
+ * Generates a date string in the format YYYY-MM-DD based
+ * on the values of @dt.
+ *
+ * Return value: A date string, owned by the caller.
+ **/
+char *
+e_contact_date_to_string (EContactDate *dt)
+{
+	if (dt) 
+		return g_strdup_printf ("%04d-%02d-%02d",
+					CLAMP(dt->year, 1000, 9999),
+					CLAMP(dt->month, 1, 12),
+					CLAMP(dt->day, 1, 31));
+	else
+		return NULL;
+}
+
+/**
+ * e_contact_date_equal:
+ * @dt1: an #EContactDate
+ * @dt2: an #EContactDate
+ *
+ * Checks if @dt1 and @dt2 are the same date.
+ *
+ * Return value: %TRUE if @dt1 and @dt2 are equal, %FALSE otherwise.
+ **/
+gboolean
+e_contact_date_equal (EContactDate *dt1, EContactDate *dt2)
+{
+	if (dt1 && dt2) {
+		return (dt1->year == dt2->year &&
+			dt1->month == dt2->month &&
+			dt1->day == dt2->day);
+	} else
+		return (!!dt1 == !!dt2);
+}
+
+/**
+ * e_contact_date_copy:
+ * @dt: an #EContactDate
+ *
+ * Creates a copy of @dt.
+ *
+ * Return value: A new #EContactDate struct identical to @dt.
+ **/
+static EContactDate *
+e_contact_date_copy (EContactDate *dt)
+{
+	EContactDate *dt2 = e_contact_date_new ();
+	dt2->year = dt->year;
+	dt2->month = dt->month;
+	dt2->day = dt->day;
+
+	return dt2;
+}
+
+/**
+ * e_contact_date_free:
+ * @dt: an #EContactDate
+ *
+ * Frees the @dt struct and its contents.
+ **/
+void
+e_contact_date_free (EContactDate *dt)
+{
+	g_free (dt);
+}
+
+GType
+e_contact_date_get_type (void)
+{
+	static GType type_id = 0;
+
+	if (!type_id)
+		type_id = g_boxed_type_register_static ("EContactDate",
+							(GBoxedCopyFunc) e_contact_date_copy,
+							(GBoxedFreeFunc) e_contact_date_free);
+	return type_id;
+}
+
+/**
+ * e_contact_date_new:
+ * 
+ * Creates a new #EContactDate struct.
+ *
+ * Return value: A new #EContactDate struct.
+ **/
+EContactDate*
+e_contact_date_new (void)
+{
+	return g_new0 (EContactDate, 1);
+}
+
+/**
+ * e_contact_photo_free:
+ * @photo: an #EContactPhoto struct
+ *
+ * Frees the @photo struct and its contents.
+ **/
+void
+e_contact_photo_free (EContactPhoto *photo)
+{
+	if (!photo)
+		return;
+	
+	switch (photo->type) {
+	case E_CONTACT_PHOTO_TYPE_INLINED:
+		g_free (photo->data.inlined.mime_type);
+		g_free (photo->data.inlined.data);
+		break;
+	case E_CONTACT_PHOTO_TYPE_URI:
+		g_free (photo->data.uri);
+		break;
+	default:
+		g_warning ("Unknown EContactPhotoType %d", photo->type);
+		break;
+	}
+
+	g_free (photo);
+}
+
+/**
+ * e_contact_photo_copy:
+ * @photo: an #EContactPhoto
+ *
+ * Creates a copy of @photo.
+ *
+ * Return value: A new #EContactPhoto struct identical to @photo.
+ **/
+static EContactPhoto *
+e_contact_photo_copy (EContactPhoto *photo)
+{
+	EContactPhoto *photo2 = g_new0 (EContactPhoto, 1);
+	switch (photo->type) {
+	case E_CONTACT_PHOTO_TYPE_INLINED:
+		photo2->type = E_CONTACT_PHOTO_TYPE_INLINED;
+		photo2->data.inlined.mime_type = g_strdup (photo->data.inlined.mime_type);
+		photo2->data.inlined.length = photo->data.inlined.length;
+		photo2->data.inlined.data = g_malloc (photo2->data.inlined.length);
+		memcpy (photo2->data.inlined.data, photo->data.inlined.data, photo->data.inlined.length);
+		break;
+	case E_CONTACT_PHOTO_TYPE_URI:
+		photo2->type = E_CONTACT_PHOTO_TYPE_URI;
+		photo2->data.uri = g_strdup (photo->data.uri);
+		break;
+	default:
+		g_warning ("Unknown EContactPhotoType %d", photo->type);
+		break;
+	}
+	return photo2;
+}
+
+GType
+e_contact_photo_get_type (void)
+{
+	static GType type_id = 0;
+
+	if (!type_id)
+		type_id = g_boxed_type_register_static ("EContactPhoto",
+							(GBoxedCopyFunc) e_contact_photo_copy,
+							(GBoxedFreeFunc) e_contact_photo_free);
+	return type_id;
+}
+
+/**
+ * e_contact_address_free:
+ * @address: an #EContactAddress
+ *
+ * Frees the @address struct and its contents.
+ **/
+void
+e_contact_address_free (EContactAddress *address)
+{
+	if (!address)
+		return;
+
+	g_free (address->address_format);
+	g_free (address->po);
+	g_free (address->ext);
+	g_free (address->street);
+	g_free (address->locality);
+	g_free (address->region);
+	g_free (address->code);
+	g_free (address->country);
+
+	g_free (address);
+}
+
+static EContactAddress *
+e_contact_address_copy (EContactAddress *address)
+{
+	EContactAddress *address2 = g_new0 (EContactAddress, 1);
+
+	address2->address_format = g_strdup (address->address_format);
+	address2->po = g_strdup (address->po);
+	address2->ext = g_strdup (address->ext);
+	address2->street = g_strdup (address->street);
+	address2->locality = g_strdup (address->locality);
+	address2->region = g_strdup (address->region);
+	address2->code = g_strdup (address->code);
+	address2->country = g_strdup (address->country);
+
+	return address2;
+}
+
+GType
+e_contact_address_get_type (void)
+{
+	static GType type_id = 0;
+
+	if (!type_id)
+		type_id = g_boxed_type_register_static ("EContactAddress",
+							(GBoxedCopyFunc) e_contact_address_copy,
+							(GBoxedFreeFunc) e_contact_address_free);
+	return type_id;
+}
+
+/**
+ * e_contact_cert_free:
+ * @cert: an #EContactCert
+ *
+ * Frees the @cert struct and its contents.
+ **/
+void
+e_contact_cert_free (EContactCert *cert)
+{
+	if (!cert)
+		return;
+
+	g_free (cert->data);
+	g_free (cert);
+}
+
+static EContactCert *
+e_contact_cert_copy (EContactCert *cert)
+{
+	EContactCert *cert2 = g_new0 (EContactCert, 1);
+	cert2->length = cert->length;
+	cert2->data = g_malloc (cert2->length);
+	memcpy (cert2->data, cert->data, cert->length);
+
+	return cert2;
+}
+
+GType
+e_contact_cert_get_type (void)
+{
+	static GType type_id = 0;
+
+	if (!type_id)
+		type_id = g_boxed_type_register_static ("EContactCert",
+							(GBoxedCopyFunc) e_contact_cert_copy,
+							(GBoxedFreeFunc) e_contact_cert_free);
+	return type_id;
+}
Index: addressbook/libebook-dbus/e-book-query.c
===================================================================
--- addressbook/libebook-dbus/e-book-query.c	(revision 409)
+++ addressbook/libebook-dbus/e-book-query.c	(working copy)
@@ -1 +1,713 @@
-link ../libebook-orbit/./e-book-query.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include <config.h>
+
+#include "e-book-query.h"
+#include <libedataserver/e-sexp.h>
+
+#include <stdarg.h>
+#include <string.h>
+
+typedef enum {
+	E_BOOK_QUERY_TYPE_AND,
+	E_BOOK_QUERY_TYPE_OR,
+	E_BOOK_QUERY_TYPE_NOT,
+	E_BOOK_QUERY_TYPE_FIELD_EXISTS,
+	E_BOOK_QUERY_TYPE_FIELD_TEST,
+	E_BOOK_QUERY_TYPE_ANY_FIELD_CONTAINS
+} EBookQueryType;
+
+struct EBookQuery {
+	EBookQueryType type;
+	int ref_count;
+
+	union {
+		struct {
+			guint          nqs;
+			EBookQuery   **qs;
+		} andor;
+
+		struct {
+			EBookQuery    *q;
+		} not;
+
+		struct {
+			EBookQueryTest test;
+			EContactField  field;
+			char          *value;
+		} field_test;
+
+		struct {
+			EContactField  field;
+			char          *vcard_field;
+		} exist;
+
+		struct {
+			char          *value;
+		} any_field_contains;
+	} query;
+};
+
+static EBookQuery *
+conjoin (EBookQueryType type, int nqs, EBookQuery **qs, gboolean unref)
+{
+	EBookQuery *ret = g_new0 (EBookQuery, 1);
+	int i;
+
+	ret->type = type;
+	ret->query.andor.nqs = nqs;
+	ret->query.andor.qs = g_new (EBookQuery *, nqs);
+	for (i = 0; i < nqs; i++) {
+		ret->query.andor.qs[i] = qs[i];
+		if (!unref)
+			e_book_query_ref (qs[i]);
+	}
+
+	return ret;
+}
+
+/**
+ * e_book_query_and:
+ * @nqs: the number of queries to AND
+ * @qs: pointer to an array of #EBookQuery items
+ * @unref: if %TRUE, the new query takes ownership of the existing queries
+ *
+ * Create a new #EBookQuery which is the logical AND of the queries in #qs.
+ *
+ * Return value: A new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_and (int nqs, EBookQuery **qs, gboolean unref)
+{
+	return conjoin (E_BOOK_QUERY_TYPE_AND, nqs, qs, unref);
+}
+
+/**
+ * e_book_query_or:
+ * @nqs: the number of queries to OR
+ * @qs: pointer to an array of #EBookQuery items
+ * @unref: if #TRUE, the new query takes ownership of the existing queries
+ *
+ * Creates a new #EBookQuery which is the logical OR of the queries in #qs.
+ *
+ * Return value: A new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_or (int nqs, EBookQuery **qs, gboolean unref)
+{
+	return conjoin (E_BOOK_QUERY_TYPE_OR, nqs, qs, unref);
+}
+
+static EBookQuery *
+conjoinv (EBookQueryType type, EBookQuery *q, va_list ap)
+{
+	EBookQuery *ret = g_new0 (EBookQuery, 1);
+	GPtrArray *qs;
+
+	qs = g_ptr_array_new ();
+	while (q) {
+		g_ptr_array_add (qs, q);
+		q = va_arg (ap, EBookQuery *);
+	}
+	va_end (ap);
+
+	ret->type = type;
+	ret->query.andor.nqs = qs->len;
+	ret->query.andor.qs = (EBookQuery **)qs->pdata;
+	g_ptr_array_free (qs, FALSE);
+
+	return ret;
+}
+
+/**
+ * e_book_query_andv:
+ * @q: first #EBookQuery
+ * @Varargs: #NULL terminated list of #EBookQuery pointers
+ *
+ * Creates a new #EBookQuery which is the logical AND of the queries specified.
+ *
+ * Return value: A new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_andv (EBookQuery *q, ...)
+{
+	va_list ap;
+
+	va_start (ap, q);
+	return conjoinv (E_BOOK_QUERY_TYPE_AND, q, ap);
+}
+
+/**
+ * e_book_query_orv:
+ * @q: first #EBookQuery
+ * @Varargs: #NULL terminated list of #EBookQuery pointers
+ *
+ * Creates a new #EBookQuery which is the logical OR of the queries specified.
+ *
+ * Return value: A new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_orv (EBookQuery *q, ...)
+{
+	va_list ap;
+
+	va_start (ap, q);
+	return conjoinv (E_BOOK_QUERY_TYPE_OR, q, ap);
+}
+
+/**
+ * e_book_query_not:
+ * @q: an #EBookQuery
+ * @unref: if #TRUE, the new query takes ownership of the existing queries
+ *
+ * Creates a new #EBookQuery which is the opposite of #q.
+ *
+ * Return value: the new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_not (EBookQuery *q, gboolean unref)
+{
+	EBookQuery *ret = g_new0 (EBookQuery, 1);
+
+	ret->type = E_BOOK_QUERY_TYPE_NOT;
+	ret->query.not.q = q;
+	if (!unref)
+		e_book_query_ref (q);
+
+	return ret;
+}
+
+/**
+ * e_book_query_field_test:
+ * @field: an #EContactField to test
+ * @test: the test to apply
+ * @value: the value to test for
+ *
+ * Creates a new #EBookQuery which tests @field for @value using the test @test.
+ *
+ * Return value: the new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_field_test (EContactField field,
+			 EBookQueryTest test,
+			 const char *value)
+{
+	EBookQuery *ret = g_new0 (EBookQuery, 1);
+
+	ret->type = E_BOOK_QUERY_TYPE_FIELD_TEST;
+	ret->query.field_test.field = field;
+	ret->query.field_test.test = test;
+	ret->query.field_test.value = g_strdup (value);
+
+	return ret;
+}
+
+/**
+ * e_book_query_field_exists:
+ * @field: a #EContactField
+ *
+ * Creates a new #EBookQuery which tests if the field @field exists.
+ * Return value: the new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_field_exists (EContactField field)
+{
+	EBookQuery *ret = g_new0 (EBookQuery, 1);
+
+	ret->type = E_BOOK_QUERY_TYPE_FIELD_EXISTS;
+	ret->query.exist.field = field;
+	ret->query.exist.vcard_field = NULL;
+
+	return ret;
+}
+
+/**
+ * e_book_query_vcard_field_exists:
+ * @field: a field name
+ *
+ * Creates a new #EBookQuery which tests if the field @field exists. @field
+ * should be a vCard field name, such as #FN or #X-MSN.
+ * Return value: the new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_vcard_field_exists (const char *field)
+{
+	EBookQuery *ret = g_new0 (EBookQuery, 1);
+
+	ret->type = E_BOOK_QUERY_TYPE_FIELD_EXISTS;
+	ret->query.exist.field = 0;
+	ret->query.exist.vcard_field = g_strdup (field);
+
+	return ret;
+}
+
+/**
+ * e_book_query_any_field_contains:
+ * @value: a value
+ *
+ * Creates a new #EBookQuery which tests if any field contains @value.
+ *
+ * Return value: the new #EBookQuery
+ **/
+EBookQuery *
+e_book_query_any_field_contains (const char *value)
+{
+	EBookQuery *ret = g_new0 (EBookQuery, 1);
+
+	ret->type = E_BOOK_QUERY_TYPE_ANY_FIELD_CONTAINS;
+	ret->query.any_field_contains.value = g_strdup (value);
+
+	return ret;
+}
+
+/**
+ * e_book_query_unref:
+ * @q: an #EBookQuery
+ *
+ * Decrement the reference count on @q. When the reference count reaches 0, @q
+ * will be freed and any child queries will have e_book_query_unref() called.
+ **/
+void
+e_book_query_unref (EBookQuery *q)
+{
+	int i;
+
+	if (q->ref_count--)
+		return;
+
+	switch (q->type) {
+	case E_BOOK_QUERY_TYPE_AND:
+	case E_BOOK_QUERY_TYPE_OR:
+		for (i = 0; i < q->query.andor.nqs; i++)
+			e_book_query_unref (q->query.andor.qs[i]);
+		g_free (q->query.andor.qs);
+		break;
+
+	case E_BOOK_QUERY_TYPE_NOT:
+		e_book_query_unref (q->query.not.q);
+		break;
+
+	case E_BOOK_QUERY_TYPE_FIELD_TEST:
+		g_free (q->query.field_test.value);
+		break;
+
+	case E_BOOK_QUERY_TYPE_FIELD_EXISTS:
+		g_free (q->query.exist.vcard_field);
+		break;
+
+	case E_BOOK_QUERY_TYPE_ANY_FIELD_CONTAINS:
+		g_free (q->query.any_field_contains.value);
+		break;
+
+	default:
+		break;
+	}
+
+	g_free (q);
+}
+
+/**
+ * e_book_query_ref:
+ * @q: a #EBookQuery
+ *
+ * Increment the reference count on @q.
+ * Return value: @q
+ **/
+EBookQuery *
+e_book_query_ref (EBookQuery *q)
+{
+	q->ref_count++;
+	return q;
+}
+
+static ESExpResult *
+func_and(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	GList **list = data;
+	ESExpResult *r;
+	EBookQuery **qs;
+
+	if (argc > 0) {
+		int i;
+
+		qs = g_new0(EBookQuery*, argc);
+		
+		for (i = 0; i < argc; i ++) {
+			GList *list_head = *list;
+			if (!list_head)
+				break;
+			qs[i] = list_head->data;
+			*list = g_list_delete_link(*list, list_head);
+		}
+
+		*list = g_list_prepend(*list, 
+				       e_book_query_and (argc, qs, TRUE));
+
+		g_free (qs);
+	}
+
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = FALSE;
+
+	return r;
+}
+
+static ESExpResult *
+func_or(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	GList **list = data;
+	ESExpResult *r;
+	EBookQuery **qs;
+
+	if (argc > 0) {
+		int i;
+
+		qs = g_new0(EBookQuery*, argc);
+		
+		for (i = 0; i < argc; i ++) {
+			GList *list_head = *list;
+			if (!list_head)
+				break;
+			qs[i] = list_head->data;
+			*list = g_list_delete_link(*list, list_head);
+		}
+
+		*list = g_list_prepend(*list, 
+				       e_book_query_or (argc, qs, TRUE));
+
+		g_free (qs);
+	}
+
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = FALSE;
+
+	return r;
+}
+
+static ESExpResult *
+func_not(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	GList **list = data;
+	ESExpResult *r;
+
+	/* just replace the head of the list with the NOT of it. */
+	if (argc > 0) {
+		EBookQuery *term = (*list)->data;
+		(*list)->data = e_book_query_not (term, TRUE);
+	}
+
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = FALSE;
+
+	return r;
+}
+
+static ESExpResult *
+func_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	GList **list = data;
+	ESExpResult *r;
+
+	if (argc == 2
+	    && argv[0]->type == ESEXP_RES_STRING
+	    && argv[1]->type == ESEXP_RES_STRING) {
+		char *propname = argv[0]->value.string;
+		char *str = argv[1]->value.string;
+
+		if (!strcmp (propname, "x-evolution-any-field")) {
+			*list = g_list_prepend (*list, e_book_query_any_field_contains (str));
+		}
+		else {
+			EContactField field = e_contact_field_id (propname);
+
+			if (field)
+				*list = g_list_prepend (*list, e_book_query_field_test (field,
+											E_BOOK_QUERY_CONTAINS,
+											str));
+		}
+	}
+
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = FALSE;
+
+	return r;
+}
+
+static ESExpResult *
+func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	GList **list = data;
+	ESExpResult *r;
+
+	if (argc == 2
+	    && argv[0]->type == ESEXP_RES_STRING
+	    && argv[1]->type == ESEXP_RES_STRING) {
+		char *propname = argv[0]->value.string;
+		char *str = argv[1]->value.string;
+		EContactField field = e_contact_field_id (propname);
+
+		if (field)
+			*list = g_list_prepend (*list, e_book_query_field_test (field,
+										E_BOOK_QUERY_IS,
+										str));
+	}
+
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = FALSE;
+
+	return r;
+}
+
+static ESExpResult *
+func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	GList **list = data;
+	ESExpResult *r;
+
+	if (argc == 2
+	    && argv[0]->type == ESEXP_RES_STRING
+	    && argv[1]->type == ESEXP_RES_STRING) {
+		char *propname = argv[0]->value.string;
+		char *str = argv[1]->value.string;
+		EContactField field = e_contact_field_id (propname);
+
+		if (field)
+			*list = g_list_prepend (*list, e_book_query_field_test (field,
+										E_BOOK_QUERY_BEGINS_WITH,
+										str));
+	}
+
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = FALSE;
+
+	return r;
+}
+
+static ESExpResult *
+func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	GList **list = data;
+	ESExpResult *r;
+
+	if (argc == 2
+	    && argv[0]->type == ESEXP_RES_STRING
+	    && argv[1]->type == ESEXP_RES_STRING) {
+		char *propname = argv[0]->value.string;
+		char *str = argv[1]->value.string;
+		EContactField field = e_contact_field_id (propname);
+
+		if (field)
+			*list = g_list_prepend (*list, e_book_query_field_test (field,
+										E_BOOK_QUERY_ENDS_WITH,
+										str));
+	}
+
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = FALSE;
+
+	return r;
+}
+
+static ESExpResult *
+func_exists(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	GList **list = data;
+	ESExpResult *r;
+
+	if (argc == 1
+	    && argv[0]->type == ESEXP_RES_STRING) {
+		char *propname = argv[0]->value.string;
+		EContactField field = e_contact_field_id (propname);
+
+		if (field)
+			*list = g_list_prepend (*list, e_book_query_field_exists (field));
+	}
+
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = FALSE;
+
+	return r;
+}
+
+/* 'builtin' functions */
+static const struct {
+	char *name;
+	ESExpFunc *func;
+	int type;		/* set to 1 if a function can perform shortcut evaluation, or
+				   doesn't execute everything, 0 otherwise */
+} symbols[] = {
+	{ "and", func_and, 0 },
+	{ "or", func_or, 0 },
+	{ "not", func_not, 0 },
+	{ "contains", func_contains, 0 },
+	{ "is", func_is, 0 },
+	{ "beginswith", func_beginswith, 0 },
+	{ "endswith", func_endswith, 0 },
+	{ "exists", func_exists, 0 },
+};
+
+/**
+ * e_book_query_from_string:
+ * @query_string: the query
+ *
+ * Parse @query_string and return a new #EBookQuery representing it.
+ *
+ * Return value: the new #EBookValue
+ **/
+EBookQuery*
+e_book_query_from_string  (const char *query_string)
+{
+	ESExp *sexp;
+	ESExpResult *r;
+	EBookQuery *retval;
+	GList *list = NULL;
+	int i;
+
+	sexp = e_sexp_new();
+
+	for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
+		if (symbols[i].type == 1) {
+			e_sexp_add_ifunction(sexp, 0, symbols[i].name,
+					     (ESExpIFunc *)symbols[i].func, &list);
+		} else {
+			e_sexp_add_function(sexp, 0, symbols[i].name,
+					    symbols[i].func, &list);
+		}
+	}
+
+	e_sexp_input_text(sexp, query_string, strlen(query_string));
+	e_sexp_parse(sexp);
+
+	r = e_sexp_eval(sexp);
+
+	e_sexp_result_free(sexp, r);
+	e_sexp_unref (sexp);
+
+	if (list) {
+		if (list->next) {
+			g_warning ("conversion to EBookQuery");
+			retval = NULL;
+			g_list_foreach (list, (GFunc)e_book_query_unref, NULL);
+		}
+		else {
+			retval = list->data;
+		}
+	}
+	else {
+		g_warning ("conversion to EBookQuery failed");
+		retval = NULL;
+	}
+
+	g_list_free (list);
+	return retval;
+}
+
+/**
+ * e_book_query_to_string:
+ * @q: an #EBookQuery
+ *
+ * Return the string representation of @q.
+ *
+ * Return value: The string form of the query. This string should be freed when
+ * finished with.
+ **/
+char*
+e_book_query_to_string    (EBookQuery *q)
+{
+	GString *str = g_string_new ("(");
+	GString *encoded = g_string_new ("");
+	int i;
+	char *s = NULL;
+
+	switch (q->type) {
+	case E_BOOK_QUERY_TYPE_AND:
+		g_string_append (str, "and ");
+		for (i = 0; i < q->query.andor.nqs; i ++) {
+			s = e_book_query_to_string (q->query.andor.qs[i]);
+			g_string_append (str, s);
+			g_free (s);
+			g_string_append_c (str, ' ');
+		}
+		break;
+	case E_BOOK_QUERY_TYPE_OR:
+		g_string_append (str, "or ");
+		for (i = 0; i < q->query.andor.nqs; i ++) {
+			s = e_book_query_to_string (q->query.andor.qs[i]);
+			g_string_append (str, s);
+			g_free (s);
+			g_string_append_c (str, ' ');
+		}
+		break;
+	case E_BOOK_QUERY_TYPE_NOT:
+		s = e_book_query_to_string (q->query.not.q);
+		g_string_append_printf (str, "not %s", s);
+		g_free (s);
+		break;
+	case E_BOOK_QUERY_TYPE_FIELD_EXISTS:
+		if (q->query.exist.vcard_field) {
+			g_string_append_printf (str, "exists_vcard \"%s\"", q->query.exist.vcard_field);
+		} else {
+			g_string_append_printf (str, "exists \"%s\"", e_contact_field_name (q->query.exist.field));
+		}
+		break;
+	case E_BOOK_QUERY_TYPE_FIELD_TEST:
+		switch (q->query.field_test.test) {
+		case E_BOOK_QUERY_IS: s = "is"; break;
+		case E_BOOK_QUERY_CONTAINS: s = "contains"; break;
+		case E_BOOK_QUERY_BEGINS_WITH: s = "beginswith"; break;
+		case E_BOOK_QUERY_ENDS_WITH: s = "endswith"; break;
+		default:
+			g_assert_not_reached();
+			break;
+		}
+
+		e_sexp_encode_string (encoded, q->query.field_test.value);
+
+		g_string_append_printf (str, "%s \"%s\" %s",
+					s,
+					e_contact_field_name (q->query.field_test.field),
+					encoded->str);
+		break;
+	case E_BOOK_QUERY_TYPE_ANY_FIELD_CONTAINS:
+		g_string_append_printf (str, "contains \"x-evolution-any-field\"");
+		e_sexp_encode_string (str, q->query.any_field_contains.value);
+		break;
+	}
+	 
+
+	g_string_append (str, ")");
+
+	g_string_free (encoded, TRUE);
+
+	return g_string_free (str, FALSE);
+}
+
+GType
+e_book_query_get_type (void)
+{
+	static GType type_id = 0;
+
+	if (!type_id)
+		type_id = g_boxed_type_register_static ("EBookQuery",
+							(GBoxedCopyFunc) e_book_query_copy,
+							(GBoxedFreeFunc) e_book_query_unref);
+	return type_id;
+}
+
+/**
+ * e_book_query_oopy:
+ * @q: an #EBookQuery
+ *
+ * Creates a copy of @q.
+ *
+ * Return value: A new #EBookQuery identical to @q.
+ **/
+EBookQuery*
+e_book_query_copy (EBookQuery *q)
+{
+	char *str = e_book_query_to_string (q);
+	EBookQuery *nq = e_book_query_from_string (str);
+
+	g_free (str);
+	return nq;
+}
Index: addressbook/libebook-dbus/e-vcard.c
===================================================================
--- addressbook/libebook-dbus/e-vcard.c	(revision 409)
+++ addressbook/libebook-dbus/e-vcard.c	(working copy)
@@ -1 +1,2043 @@
-link ../libebook-orbit/./e-vcard.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-vcard.c
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser 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: Chris Toshok (toshok@ximian.com)
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "e-vcard.h"
+
+#define d(x)
+
+#define CRLF "\r\n"
+
+#define FOLD_LINES 0
+
+typedef enum {
+	EVC_ENCODING_RAW,    /* no encoding */
+	EVC_ENCODING_BASE64, /* base64 */
+	EVC_ENCODING_QP      /* quoted-printable */
+} EVCardEncoding;
+
+struct _EVCardPrivate {
+	GList *attributes;
+};
+
+struct _EVCardAttribute {
+	char  *group;
+	char  *name;
+	GList *params; /* EVCardParam */
+	GList *values;
+	GList *decoded_values;
+	EVCardEncoding encoding;
+	gboolean encoding_set;
+};
+
+struct _EVCardAttributeParam {
+	char     *name;
+	GList    *values;  /* GList of char*'s*/
+};
+
+static GObjectClass *parent_class;
+
+static void   _evc_base64_init(void);
+static size_t _evc_base64_encode_step(unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save);
+static size_t _evc_base64_decode_step(unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save);
+static size_t _evc_base64_decode_simple (char *data, size_t len);
+static char  *_evc_base64_encode_simple (const char *data, size_t len);
+
+static void e_vcard_attribute_param_add_value_with_len (EVCardAttributeParam *param, const char *value, int length);
+
+static void
+e_vcard_dispose (GObject *object)
+{
+	EVCard *evc = E_VCARD (object);
+
+	if (evc->priv) {
+
+		g_list_foreach (evc->priv->attributes, (GFunc)e_vcard_attribute_free, NULL);
+		g_list_free (evc->priv->attributes);
+
+		g_free (evc->priv);
+		evc->priv = NULL;
+	}
+
+	if (G_OBJECT_CLASS (parent_class)->dispose)
+		G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+e_vcard_class_init (EVCardClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS(klass);
+
+	parent_class = g_type_class_ref (G_TYPE_OBJECT);
+
+	object_class->dispose = e_vcard_dispose;
+
+	_evc_base64_init();
+}
+
+static void
+e_vcard_init (EVCard *evc)
+{
+	evc->priv = g_new0 (EVCardPrivate, 1);
+}
+
+GType
+e_vcard_get_type (void)
+{
+	static GType vcard_type = 0;
+
+	if (!vcard_type) {
+		static const GTypeInfo vcard_info =  {
+			sizeof (EVCardClass),
+			NULL,           /* base_init */
+			NULL,           /* base_finalize */
+			(GClassInitFunc) e_vcard_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (EVCard),
+			0,             /* n_preallocs */
+			(GInstanceInitFunc) e_vcard_init,
+		};
+
+		vcard_type = g_type_register_static (G_TYPE_OBJECT, "EVCard", &vcard_info, 0);
+	}
+
+	return vcard_type;
+}
+
+static void
+skip_to_next_line (const char **p)
+{
+	const char *lp = *p;
+
+	while (*lp != '\r' && *lp != '\n' && *lp != '\0')
+		lp++;
+
+	if (*lp == '\r') {
+		if (*++lp == '\n')
+			lp++;
+	}
+	
+	*p = lp;
+}
+
+/* skip forward until we hit a character in @s, CRLF, or \0.  leave *p
+   pointing at the character that causes us to stop */
+static void
+skip_until (const char **p, char *s)
+{
+	const char *lp;
+
+	lp = *p;
+
+	while (*lp != '\r' && *lp != '\0') {
+		gboolean s_matches = FALSE;
+		char *ls;
+		for (ls = s; *ls; ls = g_utf8_next_char (ls)) {
+			if (g_utf8_get_char (ls) == g_utf8_get_char (lp)) {
+				s_matches = TRUE;
+				break;
+			}
+		}
+
+		if (s_matches)
+			break;
+		lp = g_utf8_next_char (lp);
+	}
+
+	*p = lp;
+}
+
+#define WRITE_CHUNK \
+	if (chunk_start) { \
+		g_string_append_len (str, chunk_start, lp - chunk_start); \
+		chunk_start = NULL; \
+	}
+
+#define UNFOLD(p)				       \
+	else if (*p == '\r' || *p == '\n') {	       \
+		p++;				       \
+		if (*p == '\n' || *p == '\r') {	       \
+			p++;			       \
+			if (*p == ' ' || *p == '\t') { \
+				p++;		       \
+			} else {		       \
+				break;		       \
+			}			       \
+		} else if (*p == ' ' || *p == '\t') {  \
+			p++;			       \
+		} else {			       \
+			break;			       \
+		}				       \
+	}
+
+#define UNFOLD_CHUNK(p)				       \
+	else if (*p == '\r' || *p == '\n') {	       \
+		WRITE_CHUNK;			       \
+		p++;				       \
+		if (*p == '\n' || *p == '\r') {	       \
+			p++;			       \
+			if (*p == ' ' || *p == '\t') { \
+				p++;		       \
+			} else {		       \
+				break;		       \
+			}			       \
+		} else if (*p == ' ' || *p == '\t') {  \
+			p++;			       \
+		} else {			       \
+			break;			       \
+		}				       \
+	}
+
+
+static void
+read_attribute_value (EVCardAttribute *attr, const char **p, gboolean quoted_printable)
+{
+	const char *lp = *p;
+	GString *str;
+	const char *chunk_start = NULL;
+
+	/* read in the value */
+	str = g_string_sized_new (16);
+	while (*lp) {
+		//g_printerr("(%c)", *lp);
+		if (*lp == '=' && quoted_printable) {
+			char a, b;
+			WRITE_CHUNK;
+			if ((a = *(++lp)) == '\0') break;
+			if ((b = *(++lp)) == '\0') break;
+			else if (isxdigit(a) && isxdigit (b)) {
+				gunichar c;
+
+				a = tolower (a);
+				b = tolower (b);
+
+				c = (((a>='a'?a-'a'+10:a-'0')&0x0f) << 4)
+					| ((b>='a'?b-'a'+10:b-'0')&0x0f);
+				
+				g_string_insert_unichar (str, -1, c);
+			}
+			else 
+				{
+					g_string_append_c (str, a);
+					g_string_append_c (str, b);
+				}
+			
+			lp++;
+		}
+		else if (*lp == '\\') {
+			WRITE_CHUNK;
+			/* convert back to the non-escaped version of
+			   the characters */
+			lp = g_utf8_next_char(lp);
+			if (*lp == '\0') {
+				g_string_append_c (str, '\\');
+				break;
+			}
+			switch (*lp) {
+			case 'n': g_string_append_c (str, '\n'); break;
+			case 'r': g_string_append_c (str, '\r'); break;
+			case ';': g_string_append_c (str, ';'); break;
+			case ',': g_string_append_c (str, ','); break;
+			case '\\': g_string_append_c (str, '\\'); break;
+			default:
+				g_warning ("invalid escape, passing it through");
+				g_string_append_c (str, '\\');
+				g_string_insert_unichar (str, -1, g_utf8_get_char(lp));
+				break;
+			}
+			lp = g_utf8_next_char(lp);
+		}
+		else if ((*lp == ';') ||
+			 (*lp == ',' && !strcmp (attr->name, "CATEGORIES"))) {
+			WRITE_CHUNK;
+			e_vcard_attribute_add_value (attr, str->str);
+			g_string_assign (str, "");
+			lp = g_utf8_next_char(lp);
+		}
+		UNFOLD_CHUNK(lp)
+		else {
+			if (chunk_start == NULL)
+				chunk_start = lp;
+
+			//g_string_insert_unichar (str, -1, g_utf8_get_char (lp));
+			lp = g_utf8_next_char(lp);
+		}
+	}
+	WRITE_CHUNK;
+	if (str) {
+		e_vcard_attribute_add_value (attr, str->str);
+		g_string_free (str, TRUE);
+	}
+
+	*p = lp;
+}
+
+static void
+read_attribute_params (EVCardAttribute *attr, const char **p, gboolean *quoted_printable)
+{
+	const char *lp = *p;
+	GString *str;
+	EVCardAttributeParam *param = NULL;
+	gboolean in_quote = FALSE;
+	str = g_string_sized_new (16);
+	while (*lp != '\0') {
+		if (*lp == '"') {
+			in_quote = !in_quote;
+			lp = g_utf8_next_char (lp);
+		}
+		else if (in_quote || g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_') {
+			g_string_insert_unichar (str, -1, g_utf8_get_char (lp));
+			lp = g_utf8_next_char (lp);
+		}
+		/* accumulate until we hit the '=' or ';'.  If we hit
+		 * a '=' the string contains the parameter name.  if
+		 * we hit a ';' the string contains the parameter
+		 * value and the name is either ENCODING (if value ==
+		 * QUOTED-PRINTABLE) or TYPE (in any other case.)
+		 */
+		else if (*lp == '=') {
+			if (str->len > 0) {
+				param = e_vcard_attribute_param_new (str->str);
+				g_string_assign (str, "");
+				lp = g_utf8_next_char (lp);
+			}
+			else {
+				skip_until (&lp, ":;");
+				if (*lp == '\r') {
+					lp = g_utf8_next_char (lp); /* \n */
+					lp = g_utf8_next_char (lp); /* start of the next line */
+					break;
+				}
+				else if (*lp == ';')
+					lp = g_utf8_next_char (lp);
+			}
+		}
+		else if (*lp == ';' || *lp == ':' || *lp == ',') {
+			gboolean colon = (*lp == ':');
+			gboolean comma = (*lp == ',');
+
+			if (param) {
+				if (str->len > 0) {
+					e_vcard_attribute_param_add_value_with_len (param, str->str, str->len);
+					g_string_assign (str, "");
+					if (!colon)
+						lp = g_utf8_next_char (lp);
+				}
+				else {
+					/* we've got a parameter of the form:
+					 * PARAM=(.*,)?[:;]
+					 * so what we do depends on if there are already values
+					 * for the parameter.  If there are, we just finish
+					 * this parameter and skip past the offending character
+					 * (unless it's the ':'). If there aren't values, we free
+					 * the parameter then skip past the character.
+					 */
+					if (!param->values) {
+						e_vcard_attribute_param_free (param);
+						param = NULL;
+						if (!colon)
+							lp = g_utf8_next_char (lp);
+					}
+				}
+
+				if (param
+				    && !strcmp (param->name, "ENCODING")
+				    && !g_ascii_strcasecmp (param->values->data, "quoted-printable")) {
+					*quoted_printable = TRUE;
+					e_vcard_attribute_param_free (param);
+					param = NULL;
+				}
+			}
+			else {
+				if (str->len > 0) {
+					char *param_name;
+					if (!g_ascii_strcasecmp (str->str,
+								 "quoted-printable")) {
+						param_name = "ENCODING";
+						*quoted_printable = TRUE;
+					}
+					/* apple's broken addressbook app outputs naked BASE64
+					   parameters, which aren't even vcard 3.0 compliant. */
+					else if (!g_ascii_strcasecmp (str->str,
+								      "base64")) {
+						param_name = "ENCODING";
+						g_string_assign (str, "b");
+					}
+					else {
+						param_name = "TYPE";
+					}
+
+					if (param_name) {
+						param = e_vcard_attribute_param_new (param_name);
+						e_vcard_attribute_param_add_value_with_len (param, str->str, str->len);
+					}
+					g_string_assign (str, "");
+					if (!colon)
+						lp = g_utf8_next_char (lp);
+				}
+				else {
+					/* we've got an attribute with a truly empty
+					   attribute parameter.  So it's of the form:
+					   
+					   ATTR;[PARAM=value;]*;[PARAM=value;]*:
+
+					   (note the extra ';')
+
+					   the only thing to do here is, well.. nothing.
+					   we skip over the character if it's not a colon,
+					   and the rest is handled for us: We'll either
+					   continue through the loop again if we hit a ';',
+					   or we'll break out correct below if it was a ':' */
+					if (!colon)
+						lp = g_utf8_next_char (lp);
+				}
+			}
+			if (param && !comma) {
+				e_vcard_attribute_add_param (attr, param);
+				param = NULL;
+			}
+			if (colon)
+				break;
+		}
+		UNFOLD (lp)
+		else {
+			g_warning ("invalid character found in parameter spec");
+			g_string_assign (str, "");
+			skip_until (&lp, ":;");
+			
+			if (*lp == '\r') {
+				lp = g_utf8_next_char (lp); /* \n */
+				lp = g_utf8_next_char (lp); /* start of the next line */
+				break;
+			}
+		}
+	}
+
+	if (str)
+		g_string_free (str, TRUE);
+
+	attr->params = g_list_reverse (attr->params);
+	
+	*p = lp;
+}
+
+/* reads an entire attribute from the input buffer, leaving p pointing
+   at the start of the next line (past the \r\n) */
+static EVCardAttribute*
+read_attribute (const char **p)
+{
+	char *attr_group = NULL;
+	char *attr_name = NULL;
+	EVCardAttribute *attr = NULL;
+	GString *str;
+	const char *lp = *p;
+	char c;
+	gboolean is_qp = FALSE;
+	gunichar uc;
+
+	/* first read in the group/name */
+	str = g_string_sized_new (16);
+	while ((c = *lp)) {
+		if (c == ':' || c == ';') {
+			if (str->len != 0) {
+				/* we've got a name, break out to the value/attribute parsing */
+				attr_name = g_string_free (str, FALSE);
+				break;
+			}
+			else {
+				/* a line of the form:
+				 * (group.)?[:;]
+				 *
+				 * since we don't have an attribute
+				 * name, skip to the end of the line
+				 * and try again.
+				 */
+				g_string_free (str, TRUE);
+				*p = lp;
+				skip_to_next_line(p);
+				goto lose;
+			}
+		}
+		else if (c == '.') {
+			if (attr_group) {
+				g_warning ("extra `.' in attribute specification.  ignoring extra group `%s'",
+					   str->str);
+				g_string_assign (str, "");
+			}
+			if (str->len != 0) {
+				attr_group = g_string_free (str, FALSE);
+				g_string_assign (str, "");
+			}
+		}
+		else if (g_unichar_isalnum ((uc = g_utf8_get_char (lp))) ||c == '-' || c == '_') {
+			g_string_insert_unichar (str, -1, g_unichar_toupper (uc));
+		}
+		UNFOLD (lp)
+		else {
+			g_warning ("invalid character '%c' found in attribute group/name", c);
+			g_string_free (str, TRUE);
+			*p = lp;
+			skip_to_next_line(p);
+			goto lose;
+		}
+
+		lp = g_utf8_next_char(lp);
+	}
+
+	if (!attr_name) {
+		goto lose;
+	}
+
+	attr = e_vcard_attribute_new (attr_group, attr_name);
+	g_free (attr_group);
+	g_free (attr_name);
+
+	if (*lp == ';') {
+		/* skip past the ';' */
+		lp = g_utf8_next_char(lp);
+		read_attribute_params (attr, &lp, &is_qp);
+		if (is_qp)
+			attr->encoding = EVC_ENCODING_RAW;
+	}
+	if (*lp == ':') {
+		/* skip past the ':' */
+		lp = g_utf8_next_char(lp);
+		read_attribute_value (attr, &lp, is_qp);
+	}
+
+	*p = lp;
+
+	if (!attr->values) {
+		goto lose;
+	}
+
+	return attr;
+ lose:
+	if (attr)
+		e_vcard_attribute_free (attr);
+	return NULL;
+}
+
+static void
+parse (EVCard *evc, const char *str)
+{
+	EVCardAttribute *attr;
+
+	if (str == NULL)
+		return;
+	
+	attr = read_attribute (&str);
+	if (!attr || attr->group || strcmp (attr->name, "BEGIN")) {
+		g_warning ("vcard began without a BEGIN:VCARD\n");
+	}
+	if (attr && !strcmp (attr->name, "BEGIN"))
+		e_vcard_attribute_free (attr);
+	else if (attr)
+		e_vcard_add_attribute (evc, attr);
+	
+	while ((attr = read_attribute (&str))) {
+		if (strcmp (attr->name, "END") != 0)
+			e_vcard_add_attribute (evc, attr);
+		else
+			break;
+	}
+
+	if (!attr || attr->group || strcmp (attr->name, "END"))
+		g_warning ("vcard ended without END:VCARD\n");
+	
+	if (attr && !strcmp (attr->name, "END"))
+		e_vcard_attribute_free (attr);
+
+	evc->priv->attributes = g_list_reverse (evc->priv->attributes);
+}
+
+/**
+ * e_vcard_escape_string:
+ * @s: the string to escape
+ *
+ * Escapes a string according to RFC2426, section 5.
+ *
+ * Return value: A newly allocated, escaped string.
+ **/
+char*
+e_vcard_escape_string (const char *s)
+{
+	GString *str;
+	const char *p;
+
+	str = g_string_new ("");
+
+	/* Escape a string as described in RFC2426, section 5 */
+	for (p = s; p && *p; p++) {
+		switch (*p) {
+		case '\n':
+			g_string_append (str, "\\n");
+			break;
+		case '\r':
+			if (*(p+1) == '\n')
+				p++;
+			g_string_append (str, "\\n");
+			break;
+		case ';':
+			g_string_append (str, "\\;");
+			break;
+		case ',':
+			g_string_append (str, "\\,");
+			break;
+		case '\\':
+			g_string_append (str, "\\\\");
+			break;
+		default:
+			g_string_append_c (str, *p);
+			break;
+		}
+	}
+
+	return g_string_free (str, FALSE);
+}
+
+/**
+ * e_vcard_unescape_string:
+ * @s: the string to unescape
+ *
+ * Unescapes a string according to RFC2426, section 5.
+ *
+ * Return value: A newly allocated, unescaped string.
+ **/
+char*
+e_vcard_unescape_string (const char *s)
+{
+	GString *str;
+	const char *p;
+
+	g_return_val_if_fail (s != NULL, NULL);
+
+	str = g_string_new ("");
+
+	/* Unescape a string as described in RFC2426, section 5 */
+	for (p = s; *p; p++) {
+		if (*p == '\\') {
+			p++;
+			if (*p == '\0') {
+				g_string_append_c (str, '\\');
+				break;
+			}
+			switch (*p) {
+			case 'n':  g_string_append_c (str, '\n'); break;
+			case 'r':  g_string_append_c (str, '\r'); break;
+			case ';':  g_string_append_c (str, ';'); break;
+			case ',':  g_string_append_c (str, ','); break;
+			case '\\': g_string_append_c (str, '\\'); break;
+			default:
+				g_warning ("invalid escape, passing it through");
+				g_string_append_c (str, '\\');
+				g_string_append_unichar (str, g_utf8_get_char(p));
+				break;
+			}
+		}
+	}
+
+	return g_string_free (str, FALSE);
+}
+
+void
+e_vcard_construct (EVCard *evc, const char *str)
+{
+	g_return_if_fail (E_IS_VCARD (evc));
+	g_return_if_fail (str != NULL);
+
+	if (*str)
+		parse (evc, str);
+}
+
+/**
+ * e_vcard_new:
+ * 
+ * Creates a new, blank #EVCard.
+ *
+ * Return value: A new, blank #EVCard.
+ **/
+EVCard *
+e_vcard_new ()
+{
+	return e_vcard_new_from_string ("");
+}
+
+/**
+ * e_vcard_new_from_string:
+ * @str: a string representation of the vcard to create
+ *
+ * Creates a new #EVCard from the passed-in string
+ * representation.
+ *
+ * Return value: A new #EVCard.
+ **/
+EVCard *
+e_vcard_new_from_string (const char *str)
+{
+	EVCard *evc;
+
+	g_return_val_if_fail (str != NULL, NULL);
+
+	evc = g_object_new (E_TYPE_VCARD, NULL);
+
+	e_vcard_construct (evc, str);
+
+	return evc;
+}
+
+static char*
+e_vcard_to_string_vcard_21  (EVCard *evc)
+{
+	g_warning ("need to implement e_vcard_to_string_vcard_21");
+	return g_strdup ("");
+}
+
+static char*
+e_vcard_to_string_vcard_30 (EVCard *evc)
+{
+	GList *l;
+	GList *v;
+
+	GString *str;
+
+	str = g_string_new ("BEGIN:VCARD" CRLF);
+
+	/* we hardcode the version (since we're outputting to a
+	   specific version) and ignore any version attributes the
+	   vcard might contain */
+	g_string_append (str, "VERSION:3.0" CRLF);
+
+	for (l = evc->priv->attributes; l; l = l->next) {
+		GList *p;
+		EVCardAttribute *attr = l->data;
+		GString *attr_str;
+
+		if (!strcmp (attr->name, "VERSION"))
+			continue;
+
+		attr_str = g_string_new ("");
+
+		/* From rfc2425, 5.8.2
+		 *
+		 * contentline  = [group "."] name *(";" param) ":" value CRLF
+		 */
+
+		if (attr->group) {
+			g_string_append (attr_str, attr->group);
+			g_string_append_c (attr_str, '.');
+		}
+		g_string_append (attr_str, attr->name);
+
+		/* handle the parameters */
+		for (p = attr->params; p; p = p->next) {
+			EVCardAttributeParam *param = p->data;
+			/* 5.8.2:
+			 * param        = param-name "=" param-value *("," param-value)
+			 */
+			g_string_append_c (attr_str, ';');
+			g_string_append (attr_str, param->name);
+			if (param->values) {
+				g_string_append_c (attr_str, '=');
+				for (v = param->values; v; v = v->next) {
+					char *value = v->data;
+					char *p = value;
+					gboolean quotes = FALSE;
+					while (*p) {
+						if (!g_unichar_isalnum (g_utf8_get_char (p))) {
+							quotes = TRUE;
+							break;
+						}
+						p = g_utf8_next_char (p);
+					}
+					if (quotes)
+						g_string_append_c (attr_str, '"');
+					g_string_append (attr_str, value);
+					if (quotes)
+						g_string_append_c (attr_str, '"');
+					if (v->next)
+						g_string_append_c (attr_str, ',');
+				}
+			}
+		}
+
+		g_string_append_c (attr_str, ':');
+
+		for (v = attr->values; v; v = v->next) {
+			char *value = v->data;
+			char *escaped_value = NULL;
+
+			escaped_value = e_vcard_escape_string (value);
+
+			g_string_append (attr_str, escaped_value);
+			if (v->next) {
+				/* XXX toshok - i hate you, rfc 2426.
+				   why doesn't CATEGORIES use a ; like
+				   a normal list attribute? */
+				if (!strcmp (attr->name, "CATEGORIES"))
+					g_string_append_c (attr_str, ',');
+				else
+					g_string_append_c (attr_str, ';');
+			}
+
+			g_free (escaped_value);
+		}
+
+		/* 5.8.2:
+		 * When generating a content line, lines longer than 75
+		 * characters SHOULD be folded
+		 */
+#if FOLD_LINES
+		l = 0;
+		do {
+			
+			if ((g_utf8_strlen (attr_str->str, -1) -l) > 75) {
+				char *p;
+
+				l += 75;
+				p = g_utf8_offset_to_pointer (attr_str->str, l);
+			
+				g_string_insert_len (attr_str, (p - attr_str->str), CRLF " ", sizeof (CRLF " ") - 1);
+			}
+			else
+				break;
+		} while (l < g_utf8_strlen (attr_str->str, -1));
+#endif
+
+		g_string_append (attr_str, CRLF);
+
+		g_string_append (str, attr_str->str);
+		g_string_free (attr_str, TRUE);
+	}
+
+	g_string_append (str, "END:VCARD");
+
+	return g_string_free (str, FALSE);
+}
+
+/**
+ * e_vcard_to_string:
+ * @evc: the #EVCard to export
+ * @format: the format to export to
+ *
+ * Exports @evc to a string representation, specified
+ * by the @format argument.
+ *
+ * Return value: A newly allocated string representing the vcard.
+ **/
+char*
+e_vcard_to_string (EVCard *evc, EVCardFormat format)
+{
+	g_return_val_if_fail (E_IS_VCARD (evc), NULL);
+
+	switch (format) {
+	case EVC_FORMAT_VCARD_21:
+		return e_vcard_to_string_vcard_21 (evc);
+	case EVC_FORMAT_VCARD_30:
+		return e_vcard_to_string_vcard_30 (evc);
+	default:
+		g_warning ("invalid format specifier passed to e_vcard_to_string");
+		return g_strdup ("");
+	}
+}
+
+/**
+ * e_vcard_dump_structure:
+ * @evc: the #EVCard to dump
+ *
+ * Prints a dump of @evc's structure to stdout. Used for
+ * debugging.
+ **/
+void
+e_vcard_dump_structure (EVCard *evc)
+{
+	GList *a;
+	GList *v;
+	int i;
+
+	g_return_if_fail (E_IS_VCARD (evc));
+
+	printf ("vCard\n");
+	for (a = evc->priv->attributes; a; a = a->next) {
+		GList *p;
+		EVCardAttribute *attr = a->data;
+		printf ("+-- %s\n", attr->name);
+		if (attr->params) {
+			printf ("    +- params=\n");
+
+			for (p = attr->params, i = 0; p; p = p->next, i++) {
+				EVCardAttributeParam *param = p->data;
+				printf ("    |   [%d] = %s", i,param->name);
+				printf ("(");
+				for (v = param->values; v; v = v->next) {
+					char *value = e_vcard_escape_string ((char*)v->data);
+					printf ("%s", value);
+					if (v->next)
+						printf (",");
+					g_free (value);
+				}
+
+				printf (")\n");
+			}
+		}
+		printf ("    +- values=\n");
+		for (v = attr->values, i = 0; v; v = v->next, i++) {
+			printf ("        [%d] = `%s'\n", i, (char*)v->data);
+		}
+	}
+}
+
+/**
+ * e_vcard_attribute_new:
+ * @attr_group: a group name
+ * @attr_name: an attribute name
+ *
+ * Creates a new #EVCardAttribute with the specified group and
+ * attribute names.
+ *
+ * Return value: A new #EVCardAttribute.
+ **/
+EVCardAttribute*
+e_vcard_attribute_new (const char *attr_group, const char *attr_name)
+{
+	EVCardAttribute *attr;
+
+	attr = g_slice_new0 (EVCardAttribute);
+
+	attr->group = g_strdup (attr_group);
+	attr->name = g_ascii_strup (attr_name, -1);
+
+	return attr;
+}
+
+/**
+ * e_vcard_attribute_free:
+ * @attr: attribute to free
+ *
+ * Frees an attribute, its values and its parameters.
+ **/
+void
+e_vcard_attribute_free (EVCardAttribute *attr)
+{
+	g_return_if_fail (attr != NULL);
+
+	g_free (attr->group);
+	g_free (attr->name);
+
+	e_vcard_attribute_remove_values (attr);
+
+	e_vcard_attribute_remove_params (attr);
+
+	g_slice_free (EVCardAttribute, attr);
+}
+
+/**
+ * e_vcard_attribute_copy:
+ * @attr: attribute to copy
+ *
+ * Makes a copy of @attr.
+ *
+ * Return value: A new #EVCardAttribute identical to @attr.
+ **/
+EVCardAttribute*
+e_vcard_attribute_copy (EVCardAttribute *attr)
+{
+	EVCardAttribute *a;
+	GList *p;
+
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	a = e_vcard_attribute_new (e_vcard_attribute_get_group (attr),
+				   e_vcard_attribute_get_name (attr));
+
+	for (p = attr->values; p; p = p->next)
+		e_vcard_attribute_add_value (a, p->data);
+	for (p = attr->params; p; p = p->next)
+		e_vcard_attribute_add_param (a, e_vcard_attribute_param_copy (p->data));
+
+	return a;
+}
+
+/**
+ * e_vcard_remove_attributes:
+ * @evc: vcard object
+ * @attr_group: group name of attributes to be removed
+ * @attr_name: name of the arributes to be removed
+ *
+ * Removes all the attributes with group name and attribute name equal to 
+ * passed in values. If @attr_group is %NULL or an empty string,
+ * it removes all the attributes with passed in name irrespective of
+ * their group names.
+ **/
+void
+e_vcard_remove_attributes (EVCard *evc, const char *attr_group, const char *attr_name)
+{
+	GList *attr;
+
+	g_return_if_fail (E_IS_VCARD (evc));
+	g_return_if_fail (attr_name != NULL);
+
+	attr = evc->priv->attributes;
+	while (attr) {
+		GList *next_attr;
+		EVCardAttribute *a = attr->data;
+
+		next_attr = attr->next;
+
+		if (((!attr_group || *attr_group == '\0') ||
+		     (attr_group && !g_ascii_strcasecmp (attr_group, a->group))) &&
+		    ((!attr_name && !a->name) || !g_ascii_strcasecmp (attr_name, a->name))) {
+
+			/* matches, remove/delete the attribute */
+			evc->priv->attributes = g_list_delete_link (evc->priv->attributes, attr);
+
+			e_vcard_attribute_free (a);
+		}
+
+		attr = next_attr;
+	}
+}
+
+/**
+ * e_vcard_remove_attribute:
+ * @evc: an #EVCard
+ * @attr: an #EVCardAttribute to remove
+ *
+ * Removes @attr from @evc and frees it.
+ **/
+void
+e_vcard_remove_attribute (EVCard *evc, EVCardAttribute *attr)
+{
+	g_return_if_fail (E_IS_VCARD (evc));
+	g_return_if_fail (attr != NULL);
+
+	evc->priv->attributes = g_list_remove (evc->priv->attributes, attr);
+	e_vcard_attribute_free (attr);
+}
+
+/**
+ * e_vcard_add_attribute:
+ * @evc: an #EVCard
+ * @attr: an #EVCardAttribute to add
+ *
+ * Adds @attr to @evc.
+ **/
+void
+e_vcard_add_attribute (EVCard *evc, EVCardAttribute *attr)
+{
+	g_return_if_fail (E_IS_VCARD (evc));
+	g_return_if_fail (attr != NULL);
+
+	evc->priv->attributes = g_list_prepend (evc->priv->attributes, attr);
+}
+
+/**
+ * e_vcard_add_attribute_with_value:
+ * @evcard: an #EVCard
+ * @attr: an #EVCardAttribute to add
+ * @value: a value to assign to the attribute
+ *
+ * Adds @attr to @evcard, setting it to @value.
+ **/
+void
+e_vcard_add_attribute_with_value (EVCard *evcard,
+				  EVCardAttribute *attr, const char *value)
+{
+	g_return_if_fail (E_IS_VCARD (evcard));
+	g_return_if_fail (attr != NULL);
+
+	e_vcard_attribute_add_value (attr, value);
+
+	e_vcard_add_attribute (evcard, attr);
+}
+
+/**
+ * e_vcard_add_attribute_with_values:
+ * @evcard: an @EVCard
+ * @attr: an #EVCardAttribute to add
+ * @...: a %NULL-terminated list of values to assign to the attribute
+ *
+ * Adds @attr to @evcard, assigning the list of values to it.
+ **/
+void
+e_vcard_add_attribute_with_values (EVCard *evcard, EVCardAttribute *attr, ...)
+{
+	va_list ap;
+	char *v;
+
+	g_return_if_fail (E_IS_VCARD (evcard));
+	g_return_if_fail (attr != NULL);
+
+	va_start (ap, attr);
+
+	while ((v = va_arg (ap, char*))) {
+		e_vcard_attribute_add_value (attr, v);
+	}
+
+	va_end (ap);
+
+	e_vcard_add_attribute (evcard, attr);
+}
+
+/**
+ * e_vcard_attribute_add_value:
+ * @attr: an #EVCardAttribute
+ * @value: a string value
+ *
+ * Adds @value to @attr's list of values.
+ **/
+void
+e_vcard_attribute_add_value (EVCardAttribute *attr, const char *value)
+{
+	g_return_if_fail (attr != NULL);
+
+	attr->values = g_list_append (attr->values, g_strdup (value));
+}
+
+/**
+ * e_vcard_attribute_add_value_decoded:
+ * @attr: an #EVCardAttribute
+ * @value: an encoded value
+ * @len: the length of the encoded value, in bytes
+ *
+ * Decodes @value according to the encoding used for @attr, and
+ * adds it to @attr's list of values.
+ **/
+void
+e_vcard_attribute_add_value_decoded (EVCardAttribute *attr, const char *value, int len)
+{
+	g_return_if_fail (attr != NULL);
+
+	switch (attr->encoding) {
+	case EVC_ENCODING_RAW:
+		g_warning ("can't add_value_decoded with an attribute using RAW encoding.  you must set the ENCODING parameter first");
+		break;
+	case EVC_ENCODING_BASE64: {
+		char *b64_data = _evc_base64_encode_simple (value, len);
+		GString *decoded = g_string_new_len (value, len);
+
+		/* make sure the decoded list is up to date */
+		e_vcard_attribute_get_values_decoded (attr);
+
+		d(printf ("base64 encoded value: %s\n", b64_data));
+		d(printf ("original length: %d\n", len));
+
+		attr->values = g_list_append (attr->values, b64_data);
+		attr->decoded_values = g_list_append (attr->decoded_values, decoded);
+		break;
+	}
+	case EVC_ENCODING_QP:
+		g_warning ("need to implement quoted printable decoding");
+		break;
+	}
+}
+
+/**
+ * e_vcard_attribute_add_values:
+ * @attr: an #EVCardAttribute
+ * ...: a %NULL-terminated list of strings
+ *
+ * Adds a list of values to @attr.
+ **/
+void
+e_vcard_attribute_add_values (EVCardAttribute *attr,
+			      ...)
+{
+	va_list ap;
+	char *v;
+
+	g_return_if_fail (attr != NULL);
+
+	va_start (ap, attr);
+
+	while ((v = va_arg (ap, char*))) {
+		e_vcard_attribute_add_value (attr, v);
+	}
+
+	va_end (ap);
+}
+
+static void
+free_gstring (GString *str)
+{
+	g_string_free (str, TRUE);
+}
+
+/**
+ * e_vcard_attribute_remove_values:
+ * @attr: an #EVCardAttribute
+ *
+ * Removes all values from @attr.
+ **/
+void
+e_vcard_attribute_remove_values (EVCardAttribute *attr)
+{
+	g_return_if_fail (attr != NULL);
+
+	g_list_foreach (attr->values, (GFunc)g_free, NULL);
+	g_list_free (attr->values);
+	attr->values = NULL;
+
+	g_list_foreach (attr->decoded_values, (GFunc)free_gstring, NULL);
+	g_list_free (attr->decoded_values);
+	attr->decoded_values = NULL;
+}
+
+void
+e_vcard_attribute_remove_value (EVCardAttribute *attr, const char *s)
+{
+	GList *l;
+
+	g_return_if_fail (attr != NULL);
+	g_return_if_fail (s != NULL);
+
+	l = g_list_find_custom (attr->values, s, (GCompareFunc)strcmp);
+	if (l == NULL) {
+		return;
+	}
+	
+	attr->values = g_list_delete_link (attr->values, l);
+}
+
+/**
+ * e_vcard_attribute_remove_params:
+ * @attr: an #EVCardAttribute
+ *
+ * Removes all parameters from @attr.
+ **/
+void
+e_vcard_attribute_remove_params (EVCardAttribute *attr)
+{
+	g_return_if_fail (attr != NULL);
+
+	g_list_foreach (attr->params, (GFunc)e_vcard_attribute_param_free, NULL);
+	g_list_free (attr->params);
+	attr->params = NULL;
+
+	/* also remove the cached encoding on this attribute */
+	attr->encoding_set = FALSE;
+	attr->encoding = EVC_ENCODING_RAW;
+}
+
+/**
+ * e_vcard_attribute_param_new:
+ * @name: the name of the new parameter
+ *
+ * Creates a new parameter named @name.
+ *
+ * Return value: A new #EVCardAttributeParam.
+ **/
+EVCardAttributeParam*
+e_vcard_attribute_param_new (const char *name)
+{
+	EVCardAttributeParam *param = g_slice_new (EVCardAttributeParam);
+	param->values = NULL;
+	param->name = g_ascii_strup (name, -1);
+
+	return param;
+}
+
+/**
+ * e_vcard_attribute_param_free:
+ * @param: an #EVCardAttributeParam
+ *
+ * Frees @param and its values.
+ **/
+void
+e_vcard_attribute_param_free (EVCardAttributeParam *param)
+{
+	g_return_if_fail (param != NULL);
+
+	g_free (param->name);
+
+	e_vcard_attribute_param_remove_values (param);
+
+	g_slice_free (EVCardAttributeParam, param);
+}
+
+/**
+ * e_vcard_attribute_param_copy:
+ * @param: an #EVCardAttributeParam
+ *
+ * Makes a copy of @param.
+ *
+ * Return value: a new #EVCardAttributeParam identical to @param.
+ **/
+EVCardAttributeParam*
+e_vcard_attribute_param_copy (EVCardAttributeParam *param)
+{
+	EVCardAttributeParam *p;
+	GList *l;
+
+	g_return_val_if_fail (param != NULL, NULL);
+
+	p = e_vcard_attribute_param_new (e_vcard_attribute_param_get_name (param));
+
+	for (l = param->values; l; l = l->next) {
+		e_vcard_attribute_param_add_value (p, l->data);
+	}
+
+	return p;
+}
+
+/**
+ * e_vcard_attribute_add_param:
+ * @attr: an #EVCardAttribute
+ * @param: an #EVCardAttributeParam to add
+ *
+ * Adds @param to @attr's list of parameters.
+ **/
+void
+e_vcard_attribute_add_param (EVCardAttribute *attr,
+			     EVCardAttributeParam *param)
+{
+	g_return_if_fail (attr != NULL);
+	g_return_if_fail (param != NULL);
+
+	attr->params = g_list_prepend (attr->params, param);
+
+	/* we handle our special encoding stuff here */
+
+	if (!strcmp (param->name, EVC_ENCODING)) {
+		if (attr->encoding_set) {
+			g_warning ("ENCODING specified twice");
+			return;
+		}
+
+		if (param->values && param->values->data) {
+			if (!g_ascii_strcasecmp ((char*)param->values->data, "b") ||
+			    !g_ascii_strcasecmp ((char*)param->values->data, "BASE64"))
+				attr->encoding = EVC_ENCODING_BASE64;
+			else if (!g_ascii_strcasecmp ((char*)param->values->data, EVC_QUOTEDPRINTABLE))
+				attr->encoding = EVC_ENCODING_QP;
+			else {
+				g_warning ("Unknown value `%s' for ENCODING parameter.  values will be treated as raw",
+					   (char*)param->values->data);
+			}
+
+			attr->encoding_set = TRUE;
+		}
+		else {
+			g_warning ("ENCODING parameter added with no value");
+		}
+	}
+}
+
+/**
+ * e_vcard_attribute_param_add_value:
+ * @param: an #EVCardAttributeParam
+ * @value: a string value to add
+ *
+ * Adds @value to @param's list of values.
+ **/
+void
+e_vcard_attribute_param_add_value (EVCardAttributeParam *param,
+				   const char *value)
+{
+	g_return_if_fail (param != NULL);
+
+	param->values = g_list_append (param->values, g_strdup (value));
+}
+
+/**
+ * e_vcard_attribute_param_add_value_with_len:
+ * @param: an #EVCardAttributeParam
+ * @value: a string value to add
+ * @length: the length of @string
+ *
+ * Adds @value to @param's list of values.  This function is for internal use
+ * only.
+ **/
+static void
+e_vcard_attribute_param_add_value_with_len (EVCardAttributeParam *param,
+					    const char *value,
+					    int length)
+{
+	g_return_if_fail (param != NULL);
+
+	param->values = g_list_append (param->values, g_strndup (value, length));
+}
+
+/**
+ * e_vcard_attribute_param_add_values:
+ * @param: an #EVCardAttributeParam
+ * ...: a %NULL-terminated list of strings
+ *
+ * Adds a list of values to @param.
+ **/
+void
+e_vcard_attribute_param_add_values (EVCardAttributeParam *param,
+				    ...)
+{
+	va_list ap;
+	char *v;
+
+	g_return_if_fail (param != NULL);
+
+	va_start (ap, param);
+
+	while ((v = va_arg (ap, char*))) {
+		e_vcard_attribute_param_add_value (param, v);
+	}
+
+	va_end (ap);
+}
+
+/**
+ * e_vcard_attribute_add_param_with_value:
+ * @attr: an #EVCardAttribute
+ * @param: an #EVCardAttributeParam
+ * @value: a string value
+ *
+ * Adds @value to @param, then adds @param to @attr.
+ **/
+void
+e_vcard_attribute_add_param_with_value (EVCardAttribute *attr,
+					EVCardAttributeParam *param, const char *value)
+{
+	g_return_if_fail (attr != NULL);
+	g_return_if_fail (param != NULL);
+
+	e_vcard_attribute_param_add_value (param, value);
+
+	e_vcard_attribute_add_param (attr, param);
+}
+
+/**
+ * e_vcard_attribute_add_param_with_values:
+ * @attr: an #EVCardAttribute
+ * @param: an #EVCardAttributeParam
+ * ...: a %NULL-terminated list of strings
+ *
+ * Adds the list of values to @param, then adds @param
+ * to @attr.
+ **/
+void
+e_vcard_attribute_add_param_with_values (EVCardAttribute *attr,
+					 EVCardAttributeParam *param, ...)
+{
+	va_list ap;
+	char *v;
+
+	g_return_if_fail (attr != NULL);
+	g_return_if_fail (param != NULL);
+
+	va_start (ap, param);
+
+	while ((v = va_arg (ap, char*))) {
+		e_vcard_attribute_param_add_value (param, v);
+	}
+
+	va_end (ap);
+
+	e_vcard_attribute_add_param (attr, param);
+}
+
+/**
+ * e_vcard_attribute_param_remove_values:
+ * @param: an #EVCardAttributeParam
+ *
+ * Removes and frees all values from @param.
+ **/
+void
+e_vcard_attribute_param_remove_values (EVCardAttributeParam *param)
+{
+	g_return_if_fail (param != NULL);
+
+	g_list_foreach (param->values, (GFunc)g_free, NULL);
+	g_list_free (param->values);
+	param->values = NULL;
+}
+
+void
+e_vcard_attribute_remove_param_value (EVCardAttribute *attr, const char *param_name, const char *s)
+{
+	GList *l, *params;
+	EVCardAttributeParam *param;
+
+	g_return_if_fail (attr != NULL);
+	g_return_if_fail (param_name != NULL);
+	g_return_if_fail (s != NULL);
+
+	params = e_vcard_attribute_get_params (attr);
+
+	for (l = params; l; l = l->next) {
+		param = l->data;
+		if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (param), param_name) == 0) {
+			goto found;
+		}
+	}
+	return;
+
+ found:
+	l = g_list_find_custom (param->values, s, (GCompareFunc)strcmp);
+	if (l == NULL) {
+		return;
+	}
+	
+	param->values = g_list_delete_link (param->values, l);
+	if (param->values == NULL) {
+		e_vcard_attribute_param_free (param);
+		attr->params = g_list_remove (attr->params, param);
+	}
+}
+
+/**
+ * e_vcard_get_attributes:
+ * @evcard: an #EVCard
+ *
+ * Gets the list of attributes from @evcard. The list and its
+ * contents are owned by @evcard, and must not be freed.
+ *
+ * Return value: A list of attributes of type #EVCardAttribute.
+ **/
+GList*
+e_vcard_get_attributes (EVCard *evcard)
+{
+	g_return_val_if_fail (E_IS_VCARD (evcard), NULL);
+
+	return evcard->priv->attributes;
+}
+
+EVCardAttribute *
+e_vcard_get_attribute (EVCard     *vcard,
+		       const char *name)
+{
+        GList *attrs, *l;
+
+        g_return_val_if_fail (E_IS_VCARD (vcard), NULL);
+        g_return_val_if_fail (name != NULL, NULL);
+
+        attrs = e_vcard_get_attributes (vcard);
+        for (l = attrs; l; l = l->next) {
+                EVCardAttribute *attr;
+		
+                attr = (EVCardAttribute *) l->data;
+                if (strcmp (attr->name, name) == 0)
+                        return attr;
+        }
+
+        return NULL;
+}
+/**
+ * e_vcard_attribute_get_group:
+ * @attr: an #EVCardAttribute
+ *
+ * Gets the group name of @attr.
+ *
+ * Return value: The attribute's group name.
+ **/
+const char*
+e_vcard_attribute_get_group (EVCardAttribute *attr)
+{
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	return attr->group;
+}
+
+/**
+ * e_vcard_attribute_get_name:
+ * @attr: an #EVCardAttribute
+ *
+ * Gets the name of @attr.
+ *
+ * Return value: The attribute's name.
+ **/
+const char*
+e_vcard_attribute_get_name (EVCardAttribute *attr)
+{
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	return attr->name;
+}
+
+/**
+ * e_vcard_attribute_get_values:
+ * @attr: an #EVCardAttribute
+ *
+ * Gets the list of values from @attr. The list and its
+ * contents are owned by @attr, and must not be freed.
+ * 
+ * Return value: A list of string values.
+ **/
+GList*
+e_vcard_attribute_get_values (EVCardAttribute *attr)
+{
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	return attr->values;
+}
+
+/**
+ * e_vcard_attribute_get_values_decoded:
+ * @attr: an #EVCardAttribute
+ *
+ * Gets the list of values from @attr, decoding them if
+ * necessary. The list and its contents are owned by @attr,
+ * and must not be freed.
+ *
+ * Return value: A list of values of type #GString.
+ **/
+GList*
+e_vcard_attribute_get_values_decoded (EVCardAttribute *attr)
+{
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	if (!attr->decoded_values) {
+		GList *l;
+		switch (attr->encoding) {
+		case EVC_ENCODING_RAW:
+			for (l = attr->values; l; l = l->next)
+				attr->decoded_values = g_list_prepend (attr->decoded_values, g_string_new ((char*)l->data));
+			attr->decoded_values = g_list_reverse (attr->decoded_values);
+			break;
+		case EVC_ENCODING_BASE64:
+			for (l = attr->values; l; l = l->next) {
+				char *decoded = g_strdup ((char*)l->data);
+				int len = _evc_base64_decode_simple (decoded, strlen (decoded));
+				attr->decoded_values = g_list_prepend (attr->decoded_values, g_string_new_len (decoded, len));
+				g_free (decoded);
+			}
+			attr->decoded_values = g_list_reverse (attr->decoded_values);
+			break;
+		case EVC_ENCODING_QP:
+			g_warning ("need to implement quoted printable decoding");
+			break;
+		}
+	}
+
+	return attr->decoded_values;
+}
+
+/**
+ * e_vcard_attribute_is_single_valued:
+ * @attr: an #EVCardAttribute
+ *
+ * Checks if @attr has a single value.
+ *
+ * Return value: %TRUE if the attribute has exactly one value, %FALSE otherwise.
+ **/
+gboolean
+e_vcard_attribute_is_single_valued (EVCardAttribute *attr)
+{
+	g_return_val_if_fail (attr != NULL, FALSE);
+
+	if (attr->values == NULL
+	    || attr->values->next != NULL)
+		return FALSE;
+
+	return TRUE;
+}
+
+/**
+ * e_vcard_attribute_get_value:
+ * @attr: an #EVCardAttribute
+ *
+ * Gets the value of a single-valued #EVCardAttribute, @attr.
+ *
+ * Return value: A newly allocated string representing the value.
+ **/
+char*
+e_vcard_attribute_get_value (EVCardAttribute *attr)
+{
+	GList *values;
+
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	values = e_vcard_attribute_get_values (attr);
+
+	if (!e_vcard_attribute_is_single_valued (attr))
+		g_warning ("e_vcard_attribute_get_value called on multivalued attribute %s", attr->name);
+
+	return values ? g_strdup ((char*)values->data) : NULL;
+}
+
+/**
+ * e_vcard_attribute_get_value_decoded:
+ * @attr: an #EVCardAttribute
+ *
+ * Gets the value of a single-valued #EVCardAttribute, @attr, decoding
+ * it if necessary.
+ *
+ * Return value: A newly allocated #GString representing the value.
+ **/
+GString*
+e_vcard_attribute_get_value_decoded (EVCardAttribute *attr)
+{
+	GList *values;
+	GString *str = NULL;
+
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	values = e_vcard_attribute_get_values_decoded (attr);
+
+	if (!e_vcard_attribute_is_single_valued (attr))
+		g_warning ("e_vcard_attribute_get_value_decoded called on multivalued attribute");
+
+	if (values)
+		str = values->data;
+
+	return str ? g_string_new_len (str->str, str->len) : NULL;
+}
+
+/**
+ * e_vcard_attribute_has_type:
+ * @attr: an #EVCardAttribute
+ * @typestr: a string representing the type
+ *
+ * Checks if @attr has an #EVCardAttributeParam of the specified type.
+ *
+ * Return value: %TRUE if such a parameter exists, %FALSE otherwise.
+ **/
+gboolean
+e_vcard_attribute_has_type (EVCardAttribute *attr, const char *typestr)
+{
+	GList *params;
+	GList *p;
+
+	g_return_val_if_fail (attr != NULL, FALSE);
+	g_return_val_if_fail (typestr != NULL, FALSE);
+
+	params = e_vcard_attribute_get_params (attr);
+
+	for (p = params; p; p = p->next) {
+		EVCardAttributeParam *param = p->data;
+
+		if (!g_ascii_strcasecmp (e_vcard_attribute_param_get_name (param), EVC_TYPE)) {
+			GList *values = e_vcard_attribute_param_get_values (param);
+			GList *v;
+
+			for (v = values; v; v = v->next) {
+				if (!g_ascii_strcasecmp ((char*)v->data, typestr))
+					return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+/**
+ * e_vcard_attribute_get_params:
+ * @attr: an #EVCardAttribute
+ * 
+ * Gets the list of parameters from @attr. The list and its
+ * contents are owned by @attr, and must not be freed.
+ *
+ * Return value: A list of elements of type #EVCardAttributeParam.
+ **/
+GList*
+e_vcard_attribute_get_params (EVCardAttribute *attr)
+{
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	return attr->params;
+}
+
+GList *
+e_vcard_attribute_get_param (EVCardAttribute *attr, const char *name)
+{
+	GList *params, *p;
+	
+	g_return_val_if_fail (attr != NULL, FALSE);
+	g_return_val_if_fail (name != NULL, FALSE);
+	
+	params = e_vcard_attribute_get_params (attr);
+
+	for (p = params; p; p = p->next) {
+		EVCardAttributeParam *param = p->data;
+		if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (param), name) == 0) {
+			return e_vcard_attribute_param_get_values (param);
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * e_vcard_attribute_param_get_name:
+ * @param: an #EVCardAttributeParam
+ *
+ * Gets the name of @param.
+ *
+ * Return value: The name of the parameter.
+ **/
+const char*
+e_vcard_attribute_param_get_name (EVCardAttributeParam *param)
+{
+	g_return_val_if_fail (param != NULL, NULL);
+
+	return param->name;
+}
+
+/**
+ * e_vcard_attribute_param_get_values:
+ * @param: an #EVCardAttributeParam
+ * 
+ * Gets the list of values from @param. The list and its
+ * contents are owned by @param, and must not be freed.
+ *
+ * Return value: A list of string elements representing the parameter's values.
+ **/
+GList*
+e_vcard_attribute_param_get_values (EVCardAttributeParam *param)
+{
+	g_return_val_if_fail (param != NULL, NULL);
+
+	return param->values;
+}
+
+
+
+/* encoding/decoding stuff ripped from camel-mime-utils.c */
+
+static char *_evc_base64_alphabet =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static unsigned char _evc_base64_rank[256];
+
+static void
+_evc_base64_init(void)
+{
+	int i;
+
+	memset(_evc_base64_rank, 0xff, sizeof(_evc_base64_rank));
+	for (i=0;i<64;i++) {
+		_evc_base64_rank[(unsigned int)_evc_base64_alphabet[i]] = i;
+	}
+	_evc_base64_rank['='] = 0;
+}
+
+/* call this when finished encoding everything, to
+   flush off the last little bit */
+static size_t
+_evc_base64_encode_close(unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save)
+{
+	int c1, c2;
+	unsigned char *outptr = out;
+
+	if (inlen>0)
+		outptr += _evc_base64_encode_step(in, inlen, break_lines, outptr, state, save);
+
+	c1 = ((unsigned char *)save)[1];
+	c2 = ((unsigned char *)save)[2];
+	
+	d(printf("mode = %d\nc1 = %c\nc2 = %c\n",
+		 (int)((char *)save)[0],
+		 (int)((char *)save)[1],
+		 (int)((char *)save)[2]));
+
+	switch (((char *)save)[0]) {
+	case 2:
+		outptr[2] = _evc_base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
+		g_assert(outptr[2] != 0);
+		goto skip;
+	case 1:
+		outptr[2] = '=';
+	skip:
+		outptr[0] = _evc_base64_alphabet[ c1 >> 2 ];
+		outptr[1] = _evc_base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )];
+		outptr[3] = '=';
+		outptr += 4;
+		break;
+	}
+	if (break_lines)
+		*outptr++ = '\n';
+
+	*save = 0;
+	*state = 0;
+
+	return outptr-out;
+}
+
+/*
+  performs an 'encode step', only encodes blocks of 3 characters to the
+  output at a time, saves left-over state in state and save (initialise to
+  0 on first invocation).
+*/
+static size_t
+_evc_base64_encode_step(unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save)
+{
+	register unsigned char *inptr, *outptr;
+
+	if (len<=0)
+		return 0;
+
+	inptr = in;
+	outptr = out;
+
+	d(printf("we have %d chars, and %d saved chars\n", len, ((char *)save)[0]));
+
+	if (len + ((char *)save)[0] > 2) {
+		unsigned char *inend = in+len-2;
+		register int c1, c2, c3;
+		register int already;
+
+		already = *state;
+
+		switch (((char *)save)[0]) {
+		case 1:	c1 = ((unsigned char *)save)[1]; goto skip1;
+		case 2:	c1 = ((unsigned char *)save)[1];
+			c2 = ((unsigned char *)save)[2]; goto skip2;
+		}
+		
+		/* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
+		while (inptr < inend) {
+			c1 = *inptr++;
+		skip1:
+			c2 = *inptr++;
+		skip2:
+			c3 = *inptr++;
+			*outptr++ = _evc_base64_alphabet[ c1 >> 2 ];
+			*outptr++ = _evc_base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ];
+			*outptr++ = _evc_base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
+			*outptr++ = _evc_base64_alphabet[ c3 & 0x3f ];
+			/* this is a bit ugly ... */
+			if (break_lines && (++already)>=19) {
+				*outptr++='\n';
+				already = 0;
+			}
+		}
+
+		((char *)save)[0] = 0;
+		len = 2-(inptr-inend);
+		*state = already;
+	}
+
+	d(printf("state = %d, len = %d\n",
+		 (int)((char *)save)[0],
+		 len));
+
+	if (len>0) {
+		register char *saveout;
+
+		/* points to the slot for the next char to save */
+		saveout = & (((char *)save)[1]) + ((char *)save)[0];
+
+		/* len can only be 0 1 or 2 */
+		switch(len) {
+		case 2:	*saveout++ = *inptr++;
+		case 1:	*saveout++ = *inptr++;
+		}
+		((char *)save)[0]+=len;
+	}
+
+	d(printf("mode = %d\nc1 = %c\nc2 = %c\n",
+		 (int)((char *)save)[0],
+		 (int)((char *)save)[1],
+		 (int)((char *)save)[2]));
+
+	return outptr-out;
+}
+
+
+/**
+ * base64_decode_step: decode a chunk of base64 encoded data
+ * @in: input stream
+ * @len: max length of data to decode
+ * @out: output stream
+ * @state: holds the number of bits that are stored in @save
+ * @save: leftover bits that have not yet been decoded
+ *
+ * Decodes a chunk of base64 encoded data
+ **/
+static size_t
+_evc_base64_decode_step(unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save)
+{
+	register unsigned char *inptr, *outptr;
+	unsigned char *inend, c;
+	register unsigned int v;
+	int i;
+
+	inend = in+len;
+	outptr = out;
+
+	/* convert 4 base64 bytes to 3 normal bytes */
+	v=*save;
+	i=*state;
+	inptr = in;
+	while (inptr<inend) {
+		c = _evc_base64_rank[*inptr++];
+		if (c != 0xff) {
+			v = (v<<6) | c;
+			i++;
+			if (i==4) {
+				*outptr++ = v>>16;
+				*outptr++ = v>>8;
+				*outptr++ = v;
+				i=0;
+			}
+		}
+	}
+
+	*save = v;
+	*state = i;
+
+	/* quick scan back for '=' on the end somewhere */
+	/* fortunately we can drop 1 output char for each trailing = (upto 2) */
+	i=2;
+	while (inptr>in && i) {
+		inptr--;
+		if (_evc_base64_rank[*inptr] != 0xff) {
+			if (*inptr == '=' && outptr>out)
+				outptr--;
+			i--;
+		}
+	}
+
+	/* if i!= 0 then there is a truncation error! */
+	return outptr-out;
+}
+
+static char *
+_evc_base64_encode_simple (const char *data, size_t len)
+{
+	unsigned char *out;
+	int state = 0, outlen;
+	int save = 0;
+
+	g_return_val_if_fail (data != NULL, NULL);
+
+	out = g_malloc (len * 4 / 3 + 5);
+	outlen = _evc_base64_encode_close ((unsigned char *)data, len, FALSE,
+				      out, &state, &save);
+	out[outlen] = 0;
+	return (char *)out;
+}
+
+static size_t
+_evc_base64_decode_simple (char *data, size_t len)
+{
+	int state = 0;
+	unsigned int save = 0;
+
+	g_return_val_if_fail (data != NULL, 0);
+
+	return _evc_base64_decode_step ((unsigned char *)data, len,
+					(unsigned char *)data, &state, &save);
+}
Index: addressbook/libebook-dbus/e-contact.h
===================================================================
--- addressbook/libebook-dbus/e-contact.h	(revision 409)
+++ addressbook/libebook-dbus/e-contact.h	(working copy)
@@ -1 +1,324 @@
-link ../libebook-orbit/./e-contact.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors:
+ *   Chris Toshok <toshok@ximian.com>
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ */
+
+#ifndef __E_CONTACT_H__
+#define __E_CONTACT_H__
+
+#include <time.h>
+#include <glib-object.h>
+#include <stdio.h>
+#include "e-vcard.h"
+
+G_BEGIN_DECLS
+
+#define E_TYPE_CONTACT            (e_contact_get_type ())
+#define E_CONTACT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CONTACT, EContact))
+#define E_CONTACT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CONTACT, EContactClass))
+#define E_IS_CONTACT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CONTACT))
+#define E_IS_CONTACT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CONTACT))
+#define E_CONTACT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_CONTACT, EContactClass))
+
+#define E_TYPE_CONTACT_DATE       (e_contact_date_get_type ())
+#define E_TYPE_CONTACT_NAME       (e_contact_name_get_type ())
+#define E_TYPE_CONTACT_PHOTO      (e_contact_photo_get_type ())
+#define E_TYPE_CONTACT_CERT       (e_contact_cert_get_type ())
+#define E_TYPE_CONTACT_ADDRESS    (e_contact_address_get_type ())
+
+typedef struct _EContact EContact;
+typedef struct _EContactClass EContactClass;
+typedef struct _EContactPrivate EContactPrivate;
+
+typedef enum {
+
+	E_CONTACT_UID = 1,     	 /* string field */
+	E_CONTACT_FILE_AS,     	 /* string field */
+	E_CONTACT_BOOK_URI,      /* string field */
+
+	/* Name fields */
+	E_CONTACT_FULL_NAME,   	 /* string field */
+	E_CONTACT_GIVEN_NAME,  	 /* synthetic string field */
+	E_CONTACT_FAMILY_NAME, 	 /* synthetic string field */
+	E_CONTACT_NICKNAME,    	 /* string field */
+
+	/* Email fields */
+	E_CONTACT_EMAIL_1,     	 /* synthetic string field */
+	E_CONTACT_EMAIL_2,     	 /* synthetic string field */
+	E_CONTACT_EMAIL_3,     	 /* synthetic string field */
+	E_CONTACT_EMAIL_4,       /* synthetic string field */
+
+	E_CONTACT_MAILER,        /* string field */
+
+	/* Address Labels */
+	E_CONTACT_ADDRESS_LABEL_HOME,  /* synthetic string field */
+	E_CONTACT_ADDRESS_LABEL_WORK,  /* synthetic string field */
+	E_CONTACT_ADDRESS_LABEL_OTHER, /* synthetic string field */
+
+	/* Phone fields */
+	E_CONTACT_PHONE_ASSISTANT,
+	E_CONTACT_PHONE_BUSINESS,
+	E_CONTACT_PHONE_BUSINESS_2,
+	E_CONTACT_PHONE_BUSINESS_FAX,
+	E_CONTACT_PHONE_CALLBACK,
+	E_CONTACT_PHONE_CAR,
+	E_CONTACT_PHONE_COMPANY,
+	E_CONTACT_PHONE_HOME,
+	E_CONTACT_PHONE_HOME_2,
+	E_CONTACT_PHONE_HOME_FAX,
+	E_CONTACT_PHONE_ISDN,
+	E_CONTACT_PHONE_MOBILE,
+	E_CONTACT_PHONE_OTHER,
+	E_CONTACT_PHONE_OTHER_FAX,
+	E_CONTACT_PHONE_PAGER,
+	E_CONTACT_PHONE_PRIMARY,
+	E_CONTACT_PHONE_RADIO,
+	E_CONTACT_PHONE_TELEX,
+	E_CONTACT_PHONE_TTYTDD,
+
+	/* Organizational fields */
+	E_CONTACT_ORG,        	 /* string field */
+	E_CONTACT_ORG_UNIT,   	 /* string field */
+	E_CONTACT_OFFICE,     	 /* string field */
+	E_CONTACT_TITLE,      	 /* string field */
+	E_CONTACT_ROLE,       	 /* string field */
+	E_CONTACT_MANAGER,    	 /* string field */
+	E_CONTACT_ASSISTANT,  	 /* string field */
+
+	/* Web fields */
+	E_CONTACT_HOMEPAGE_URL,  /* string field */
+	E_CONTACT_BLOG_URL,      /* string field */
+
+	/* Contact categories */
+	E_CONTACT_CATEGORIES,    /* string field */
+
+	/* Collaboration fields */
+	E_CONTACT_CALENDAR_URI,  /* string field */
+	E_CONTACT_FREEBUSY_URL,  /* string field */
+	E_CONTACT_ICS_CALENDAR,  /* string field */
+	E_CONTACT_VIDEO_URL,      /* string field */
+
+	/* misc fields */
+	E_CONTACT_SPOUSE,        /* string field */
+	E_CONTACT_NOTE,          /* string field */
+
+	E_CONTACT_IM_AIM_HOME_1,       /* Synthetic string field */
+	E_CONTACT_IM_AIM_HOME_2,       /* Synthetic string field */
+	E_CONTACT_IM_AIM_HOME_3,       /* Synthetic string field */
+	E_CONTACT_IM_AIM_WORK_1,       /* Synthetic string field */
+	E_CONTACT_IM_AIM_WORK_2,       /* Synthetic string field */
+	E_CONTACT_IM_AIM_WORK_3,       /* Synthetic string field */
+	E_CONTACT_IM_GROUPWISE_HOME_1, /* Synthetic string field */
+	E_CONTACT_IM_GROUPWISE_HOME_2, /* Synthetic string field */
+	E_CONTACT_IM_GROUPWISE_HOME_3, /* Synthetic string field */
+	E_CONTACT_IM_GROUPWISE_WORK_1, /* Synthetic string field */
+	E_CONTACT_IM_GROUPWISE_WORK_2, /* Synthetic string field */
+	E_CONTACT_IM_GROUPWISE_WORK_3, /* Synthetic string field */
+	E_CONTACT_IM_JABBER_HOME_1,    /* Synthetic string field */
+	E_CONTACT_IM_JABBER_HOME_2,    /* Synthetic string field */
+	E_CONTACT_IM_JABBER_HOME_3,    /* Synthetic string field */
+	E_CONTACT_IM_JABBER_WORK_1,    /* Synthetic string field */
+	E_CONTACT_IM_JABBER_WORK_2,    /* Synthetic string field */
+	E_CONTACT_IM_JABBER_WORK_3,    /* Synthetic string field */
+	E_CONTACT_IM_YAHOO_HOME_1,     /* Synthetic string field */
+	E_CONTACT_IM_YAHOO_HOME_2,     /* Synthetic string field */
+	E_CONTACT_IM_YAHOO_HOME_3,     /* Synthetic string field */
+	E_CONTACT_IM_YAHOO_WORK_1,     /* Synthetic string field */
+	E_CONTACT_IM_YAHOO_WORK_2,     /* Synthetic string field */
+	E_CONTACT_IM_YAHOO_WORK_3,     /* Synthetic string field */
+	E_CONTACT_IM_MSN_HOME_1,       /* Synthetic string field */
+	E_CONTACT_IM_MSN_HOME_2,       /* Synthetic string field */
+	E_CONTACT_IM_MSN_HOME_3,       /* Synthetic string field */
+	E_CONTACT_IM_MSN_WORK_1,       /* Synthetic string field */
+	E_CONTACT_IM_MSN_WORK_2,       /* Synthetic string field */
+	E_CONTACT_IM_MSN_WORK_3,       /* Synthetic string field */
+	E_CONTACT_IM_ICQ_HOME_1,       /* Synthetic string field */
+	E_CONTACT_IM_ICQ_HOME_2,       /* Synthetic string field */
+	E_CONTACT_IM_ICQ_HOME_3,       /* Synthetic string field */
+	E_CONTACT_IM_ICQ_WORK_1,       /* Synthetic string field */
+	E_CONTACT_IM_ICQ_WORK_2,       /* Synthetic string field */
+	E_CONTACT_IM_ICQ_WORK_3,       /* Synthetic string field */
+
+	/* Convenience field for getting a name from the contact.
+	   Returns the first one of [File-As, Full Name, Org, Email1]
+	   to be set */
+	E_CONTACT_REV,     /* string field to hold  time of last update to this vcard*/
+	E_CONTACT_NAME_OR_ORG,
+
+	/* Address fields */
+	E_CONTACT_ADDRESS,       /* Multi-valued structured (EContactAddress) */
+	E_CONTACT_ADDRESS_HOME,  /* synthetic structured field (EContactAddress) */
+	E_CONTACT_ADDRESS_WORK,  /* synthetic structured field (EContactAddress) */
+	E_CONTACT_ADDRESS_OTHER, /* synthetic structured field (EContactAddress) */
+
+	E_CONTACT_CATEGORY_LIST, /* multi-valued */
+
+	/* Photo/Logo */
+	E_CONTACT_PHOTO,       	 /* structured field (EContactPhoto) */
+	E_CONTACT_LOGO,       	 /* structured field (EContactPhoto) */
+
+	E_CONTACT_NAME,        	 /* structured field (EContactName) */
+	E_CONTACT_EMAIL,       	 /* Multi-valued */
+
+	/* Instant Messaging fields */
+	E_CONTACT_IM_AIM,     	 /* Multi-valued */
+	E_CONTACT_IM_GROUPWISE,  /* Multi-valued */
+	E_CONTACT_IM_JABBER,  	 /* Multi-valued */
+	E_CONTACT_IM_YAHOO,   	 /* Multi-valued */
+	E_CONTACT_IM_MSN,     	 /* Multi-valued */
+	E_CONTACT_IM_ICQ,     	 /* Multi-valued */
+       
+	E_CONTACT_WANTS_HTML,    /* boolean field */
+
+	/* fields used for describing contact lists.  a contact list
+	   is just a contact with _IS_LIST set to true.  the members
+	   are listed in the _EMAIL field. */
+	E_CONTACT_IS_LIST,             /* boolean field */
+	E_CONTACT_LIST_SHOW_ADDRESSES, /* boolean field */
+
+
+	E_CONTACT_BIRTH_DATE,    /* structured field (EContactDate) */
+	E_CONTACT_ANNIVERSARY,   /* structured field (EContactDate) */
+
+	/* Security Fields */
+	E_CONTACT_X509_CERT,     /* structured field (EContactCert) */
+
+	E_CONTACT_OSSO_CONTACT_STATE, /* list of strings */
+
+	E_CONTACT_FIELD_LAST,
+	E_CONTACT_FIELD_FIRST        = E_CONTACT_UID,
+
+	/* useful constants */
+	E_CONTACT_LAST_SIMPLE_STRING = E_CONTACT_NAME_OR_ORG,
+	E_CONTACT_FIRST_PHONE_ID     = E_CONTACT_PHONE_ASSISTANT,
+	E_CONTACT_LAST_PHONE_ID      = E_CONTACT_PHONE_TTYTDD,
+	E_CONTACT_FIRST_EMAIL_ID     = E_CONTACT_EMAIL_1,
+	E_CONTACT_LAST_EMAIL_ID      = E_CONTACT_EMAIL_4,
+	E_CONTACT_FIRST_ADDRESS_ID   = E_CONTACT_ADDRESS_HOME,
+	E_CONTACT_LAST_ADDRESS_ID    = E_CONTACT_ADDRESS_OTHER,
+	E_CONTACT_FIRST_LABEL_ID     = E_CONTACT_ADDRESS_LABEL_HOME,
+	E_CONTACT_LAST_LABEL_ID      = E_CONTACT_ADDRESS_LABEL_OTHER
+
+} EContactField;
+
+typedef struct {
+	char *family;
+	char *given;
+	char *additional;
+ 	char *prefixes;
+	char *suffixes;
+} EContactName;
+
+typedef enum {
+	E_CONTACT_PHOTO_TYPE_INLINED,
+	E_CONTACT_PHOTO_TYPE_URI
+} EContactPhotoType;
+
+typedef struct {
+	EContactPhotoType type;
+	union {
+		struct {
+			char *mime_type;
+			int length;
+			guchar *data;
+		} inlined;
+		char *uri;
+	} data;
+} EContactPhoto;
+
+typedef struct {
+	char *address_format; /* the two letter country code that
+				 determines the format/meaning of the
+				 following fields */
+	char *po;
+	char *ext;
+	char *street;
+	char *locality;
+	char *region;
+	char *code;
+	char *country;
+} EContactAddress;
+
+typedef struct {
+	int year;
+	int month;
+	int day;
+} EContactDate;
+
+typedef struct {
+	int length;
+	char *data;
+} EContactCert;
+
+struct _EContact {
+	EVCard parent;
+	/*< private >*/
+	EContactPrivate *priv;
+};
+
+struct _EContactClass {
+	EVCardClass parent_class;
+
+	/* Padding for future expansion */
+	void (*_ebook_reserved0) (void);
+	void (*_ebook_reserved1) (void);
+	void (*_ebook_reserved2) (void);
+	void (*_ebook_reserved3) (void);
+	void (*_ebook_reserved4) (void);
+};
+
+GType                   e_contact_get_type (void);
+
+EContact*               e_contact_new              (void);
+EContact*               e_contact_new_from_vcard   (const char *vcard);
+
+EContact*               e_contact_duplicate        (EContact *contact);
+
+gpointer                e_contact_get              (EContact *contact, EContactField field_id);
+gconstpointer		e_contact_get_const        (EContact *contact, EContactField field_id);
+void                    e_contact_set              (EContact *contact, EContactField field_id, gpointer value);
+
+/* the following two calls return and take a GList of
+   EVCardAttribute*'s. */
+GList*                  e_contact_get_attributes   (EContact *contact, EContactField field_id);
+void                    e_contact_set_attributes   (EContact *contact, EContactField field_id, GList *attributes);
+
+/* misc functions for structured values */
+GType                   e_contact_date_get_type    (void);
+EContactDate           *e_contact_date_new         (void);
+EContactDate           *e_contact_date_from_string (const char *str);
+char                   *e_contact_date_to_string   (EContactDate *dt);
+gboolean                e_contact_date_equal       (EContactDate *dt1,
+						    EContactDate *dt2);
+void                    e_contact_date_free        (EContactDate *date);
+
+GType                   e_contact_name_get_type    (void);
+EContactName           *e_contact_name_new         (void);
+char                   *e_contact_name_to_string   (const EContactName *name);
+EContactName           *e_contact_name_from_string (const char *name_str);
+EContactName           *e_contact_name_copy        (EContactName *name);
+void                    e_contact_name_free        (EContactName *name);
+
+
+GType                   e_contact_photo_get_type   (void);
+void                    e_contact_photo_free       (EContactPhoto *photo);
+
+GType                   e_contact_cert_get_type    (void);
+void                    e_contact_cert_free        (EContactCert *cert);
+
+GType                   e_contact_address_get_type (void);
+void                    e_contact_address_free     (EContactAddress *address);
+
+
+const char*             e_contact_field_name       (EContactField field_id);
+const char*             e_contact_pretty_name      (EContactField field_id);
+const char*             e_contact_vcard_attribute  (EContactField field_id);
+EContactField           e_contact_field_id         (const char *field_name);
+EContactField           e_contact_field_id_from_vcard (const char *vcard_field);
+
+G_END_DECLS
+
+#endif /* __E_CONTACT_H__ */
Index: addressbook/libebook-dbus/e-book-types.h
===================================================================
--- addressbook/libebook-dbus/e-book-types.h	(revision 409)
+++ addressbook/libebook-dbus/e-book-types.h	(working copy)
@@ -1 +1,76 @@
-link ../libebook-orbit/e-book-types.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * A client-side GObject which exposes the
+ * Evolution:BookListener interface.
+ *
+ * Author:
+ *   Nat Friedman (nat@ximian.com)
+ *
+ * Copyright 2000, Ximian, Inc.
+ */
+
+#ifndef __E_BOOK_TYPES_H__
+#define __E_BOOK_TYPES_H__
+
+#include <glib.h>
+#include <libebook/e-contact.h>
+
+G_BEGIN_DECLS
+
+#define E_BOOK_ERROR e_book_error_quark()
+
+GQuark e_book_error_quark (void) G_GNUC_CONST;
+
+typedef enum {
+	E_BOOK_ERROR_OK,
+	E_BOOK_ERROR_INVALID_ARG,
+	E_BOOK_ERROR_BUSY,
+	E_BOOK_ERROR_REPOSITORY_OFFLINE,
+	E_BOOK_ERROR_NO_SUCH_BOOK,
+	E_BOOK_ERROR_NO_SELF_CONTACT,
+	E_BOOK_ERROR_SOURCE_NOT_LOADED,
+	E_BOOK_ERROR_SOURCE_ALREADY_LOADED,
+	E_BOOK_ERROR_PERMISSION_DENIED,
+	E_BOOK_ERROR_CONTACT_NOT_FOUND,
+	E_BOOK_ERROR_CONTACT_ID_ALREADY_EXISTS,
+	E_BOOK_ERROR_PROTOCOL_NOT_SUPPORTED,
+	E_BOOK_ERROR_CANCELLED,
+	E_BOOK_ERROR_COULD_NOT_CANCEL,
+	E_BOOK_ERROR_AUTHENTICATION_FAILED,
+	E_BOOK_ERROR_AUTHENTICATION_REQUIRED,
+	E_BOOK_ERROR_TLS_NOT_AVAILABLE,
+	E_BOOK_ERROR_CORBA_EXCEPTION,
+	E_BOOK_ERROR_NO_SUCH_SOURCE,
+	E_BOOK_ERROR_OFFLINE_UNAVAILABLE,
+	E_BOOK_ERROR_OTHER_ERROR,
+	E_BOOK_ERROR_INVALID_SERVER_VERSION
+} EBookStatus;
+
+
+typedef enum {
+	E_BOOK_VIEW_STATUS_OK,
+	E_BOOK_VIEW_STATUS_TIME_LIMIT_EXCEEDED,
+	E_BOOK_VIEW_STATUS_SIZE_LIMIT_EXCEEDED,
+	E_BOOK_VIEW_ERROR_INVALID_QUERY,
+	E_BOOK_VIEW_ERROR_QUERY_REFUSED,
+	E_BOOK_VIEW_ERROR_OTHER_ERROR
+} EBookViewStatus;
+
+typedef enum {
+	E_BOOK_CHANGE_CARD_ADDED,
+	E_BOOK_CHANGE_CARD_DELETED,
+	E_BOOK_CHANGE_CARD_MODIFIED
+} EBookChangeType;
+
+typedef struct {
+	EBookChangeType  change_type;
+	EContact        *contact;
+} EBookChange;
+
+/* Convenience defines to save typing */
+#define E_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+#define E_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+
+G_END_DECLS
+
+#endif /* ! __E_BOOK_TYPES_H__ */
Index: addressbook/libebook-dbus/e-book-query.h
===================================================================
--- addressbook/libebook-dbus/e-book-query.h	(revision 409)
+++ addressbook/libebook-dbus/e-book-query.h	(working copy)
@@ -1 +1,57 @@
-link ../libebook-orbit/./e-book-query.h
\ No newline at end of file
+
+#ifndef __E_BOOK_QUERY_H__
+#define __E_BOOK_QUERY_H__
+
+#include "e-contact.h"
+
+G_BEGIN_DECLS
+
+#define E_TYPE_BOOK_QUERY (e_book_query_get_type ())
+
+typedef struct EBookQuery EBookQuery;
+
+typedef enum {
+  E_BOOK_QUERY_IS,
+  E_BOOK_QUERY_CONTAINS,
+  E_BOOK_QUERY_BEGINS_WITH,
+  E_BOOK_QUERY_ENDS_WITH,
+
+  /*
+    Consider these "coming soon". 
+
+    E_BOOK_QUERY_LT,
+    E_BOOK_QUERY_LE,
+    E_BOOK_QUERY_GT,
+    E_BOOK_QUERY_GE,
+    E_BOOK_QUERY_EQ,
+  */
+} EBookQueryTest;
+
+EBookQuery* e_book_query_from_string  (const char *query_string);
+char*       e_book_query_to_string    (EBookQuery *q);
+
+EBookQuery* e_book_query_ref          (EBookQuery *q);
+void        e_book_query_unref        (EBookQuery *q);
+
+EBookQuery* e_book_query_and          (int nqs, EBookQuery **qs, gboolean unref);
+EBookQuery* e_book_query_andv         (EBookQuery *q, ...);
+EBookQuery* e_book_query_or           (int nqs, EBookQuery **qs, gboolean unref);
+EBookQuery* e_book_query_orv          (EBookQuery *q, ...);
+
+EBookQuery* e_book_query_not          (EBookQuery *q, gboolean unref);
+
+EBookQuery* e_book_query_field_exists (EContactField   field);
+EBookQuery *e_book_query_vcard_field_exists (const char *field);
+EBookQuery* e_book_query_field_test   (EContactField   field,
+				       EBookQueryTest     test,
+				       const char        *value);
+
+/* a special any field contains query */
+EBookQuery* e_book_query_any_field_contains (const char  *value);
+
+GType       e_book_query_get_type (void);
+EBookQuery* e_book_query_copy     (EBookQuery *q);
+
+G_END_DECLS
+
+#endif /* __E_BOOK_QUERY_H__ */
Index: addressbook/libebook-dbus/e-vcard.h
===================================================================
--- addressbook/libebook-dbus/e-vcard.h	(revision 409)
+++ addressbook/libebook-dbus/e-vcard.h	(working copy)
@@ -1 +1,204 @@
-link ../libebook-orbit/./e-vcard.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* e-vcard.h
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser 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: Chris Toshok (toshok@ximian.com)
+ */
+
+#ifndef _EVCARD_H
+#define _EVCARD_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EVC_ADR             "ADR"
+#define EVC_BDAY            "BDAY"
+#define EVC_CALURI          "CALURI"
+#define EVC_CATEGORIES      "CATEGORIES"
+#define EVC_EMAIL           "EMAIL"
+#define EVC_ENCODING        "ENCODING"
+#define EVC_FBURL           "FBURL"
+#define EVC_FN              "FN"
+#define EVC_ICSCALENDAR     "ICSCALENDAR" /* XXX should this be X-EVOLUTION-ICSCALENDAR? */
+#define EVC_KEY             "KEY"
+#define EVC_LABEL           "LABEL"
+#define EVC_LOGO            "LOGO"
+#define EVC_MAILER          "MAILER"
+#define EVC_NICKNAME        "NICKNAME"
+#define EVC_N               "N"
+#define EVC_NOTE            "NOTE"
+#define EVC_ORG             "ORG"
+#define EVC_PHOTO           "PHOTO"
+#define EVC_PRODID          "PRODID"
+#define EVC_QUOTEDPRINTABLE "QUOTED-PRINTABLE"
+#define EVC_REV             "REV"
+#define EVC_ROLE            "ROLE"
+#define EVC_TEL             "TEL"
+#define EVC_TITLE           "TITLE"
+#define EVC_TYPE            "TYPE"
+#define EVC_UID             "UID"
+#define EVC_URL             "URL"
+#define EVC_VALUE           "VALUE"
+#define EVC_VERSION         "VERSION"
+
+#define EVC_X_AIM              "X-AIM"
+#define EVC_X_ANNIVERSARY      "X-EVOLUTION-ANNIVERSARY"
+#define EVC_X_ASSISTANT        "X-EVOLUTION-ASSISTANT"
+#define EVC_X_BIRTHDAY         "X-EVOLUTION-BIRTHDAY"
+#define EVC_X_BLOG_URL         "X-EVOLUTION-BLOG-URL"
+#define EVC_X_CALLBACK         "X-EVOLUTION-CALLBACK"
+#define EVC_X_COMPANY          "X-EVOLUTION-COMPANY"
+#define EVC_X_DEST_CONTACT_UID "X-EVOLUTION-DEST-CONTACT-UID"
+#define EVC_X_DEST_EMAIL       "X-EVOLUTION-DEST-EMAIL"
+#define EVC_X_DEST_EMAIL_NUM   "X-EVOLUTION-DEST-EMAIL-NUM"
+#define EVC_X_DEST_HTML_MAIL   "X-EVOLUTION-DEST-HTML-MAIL"
+#define EVC_X_DEST_NAME        "X-EVOLUTION-DEST-NAME"
+#define EVC_X_DEST_SOURCE_UID  "X-EVOLUTION-DEST-SOURCE-UID"
+#define EVC_X_FILE_AS          "X-EVOLUTION-FILE-AS"
+#define EVC_X_ICQ              "X-ICQ"
+#define EVC_X_JABBER           "X-JABBER"
+#define EVC_X_LIST_SHOW_ADDRESSES "X-EVOLUTION-LIST-SHOW_ADDRESSES"
+#define EVC_X_LIST          	"X-EVOLUTION-LIST"
+#define EVC_X_MANAGER       	"X-EVOLUTION-MANAGER"
+#define EVC_X_MSN           	"X-MSN"
+#define EVC_X_RADIO         	"X-EVOLUTION-RADIO"
+#define EVC_X_SPOUSE        	"X-EVOLUTION-SPOUSE"
+#define EVC_X_TELEX         	"X-EVOLUTION-TELEX"
+#define EVC_X_TTYTDD        	"X-EVOLUTION-TTYTDD"
+#define EVC_X_VIDEO_URL     	"X-EVOLUTION-VIDEO-URL"
+#define EVC_X_WANTS_HTML    	"X-MOZILLA-HTML"
+#define EVC_X_YAHOO         	"X-YAHOO"
+#define EVC_X_GROUPWISE     	"X-GROUPWISE"
+#define EVC_X_BOOK_URI     	"X-EVOLUTION-BOOK-URI"
+#define EVC_X_OSSO_CONTACT_STATE "X-OSSO-CONTACT-STATE"
+
+/* Parameter names */
+#define EVC_X_OSSO_BOUND "X-OSSO-BOUND"
+#define EVC_X_OSSO_FIELD_STATE "X-OSSO-FIELD-STATE"
+
+typedef enum {
+	EVC_FORMAT_VCARD_21,
+	EVC_FORMAT_VCARD_30
+} EVCardFormat;
+
+#define E_TYPE_VCARD            (e_vcard_get_type ())
+#define E_VCARD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_VCARD, EVCard))
+#define E_VCARD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_VCARD, EVCardClass))
+#define E_IS_VCARD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_VCARD))
+#define E_IS_VCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_VCARD))
+#define E_VCARD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_VCARD, EVCardClass))
+
+typedef struct _EVCard EVCard;
+typedef struct _EVCardClass EVCardClass;
+typedef struct _EVCardPrivate EVCardPrivate;
+typedef struct _EVCardAttribute EVCardAttribute;
+typedef struct _EVCardAttributeParam EVCardAttributeParam;
+
+struct _EVCard {
+	GObject parent;
+	/*< private >*/
+	EVCardPrivate *priv;
+};
+
+struct _EVCardClass {
+	GObjectClass parent_class;
+
+	/* Padding for future expansion */
+	void (*_ebook_reserved0) (void);
+	void (*_ebook_reserved1) (void);
+	void (*_ebook_reserved2) (void);
+	void (*_ebook_reserved3) (void);
+	void (*_ebook_reserved4) (void);
+};
+
+GType   e_vcard_get_type                     (void);
+
+void    e_vcard_construct                    (EVCard *evc, const char *str);
+EVCard* e_vcard_new                          (void);
+EVCard* e_vcard_new_from_string              (const char *str);
+
+char*   e_vcard_to_string                    (EVCard *evc, EVCardFormat format);
+
+/* mostly for debugging */
+void    e_vcard_dump_structure               (EVCard *evc);
+
+
+/* attributes */
+EVCardAttribute *e_vcard_attribute_new               (const char *attr_group, const char *attr_name);
+void             e_vcard_attribute_free              (EVCardAttribute *attr);
+EVCardAttribute *e_vcard_attribute_copy              (EVCardAttribute *attr);
+void             e_vcard_remove_attributes           (EVCard *evcard, const char *attr_group, const char *attr_name);
+void             e_vcard_remove_attribute            (EVCard *evcard, EVCardAttribute *attr);
+void             e_vcard_add_attribute               (EVCard *evcard, EVCardAttribute *attr);
+void             e_vcard_add_attribute_with_value    (EVCard *evcard, EVCardAttribute *attr, const char *value);
+void             e_vcard_add_attribute_with_values   (EVCard *evcard, EVCardAttribute *attr, ...);
+void             e_vcard_attribute_add_value         (EVCardAttribute *attr, const char *value);
+void             e_vcard_attribute_add_value_decoded (EVCardAttribute *attr, const char *value, int len);
+void             e_vcard_attribute_add_values        (EVCardAttribute *attr, ...);
+void             e_vcard_attribute_remove_value      (EVCardAttribute *attr, const char *s);
+void             e_vcard_attribute_remove_values     (EVCardAttribute *attr);
+void             e_vcard_attribute_remove_params     (EVCardAttribute *attr);
+void             e_vcard_attribute_remove_param_value (EVCardAttribute *attr, const char *param_name, const char *s);
+
+/* attribute parameters */
+EVCardAttributeParam* e_vcard_attribute_param_new             (const char *param_name);
+void                  e_vcard_attribute_param_free            (EVCardAttributeParam *param);
+EVCardAttributeParam* e_vcard_attribute_param_copy            (EVCardAttributeParam *param);
+void                  e_vcard_attribute_add_param             (EVCardAttribute *attr, EVCardAttributeParam *param);
+void                  e_vcard_attribute_add_param_with_value  (EVCardAttribute *attr,
+							       EVCardAttributeParam *param, const char *value);
+void                  e_vcard_attribute_add_param_with_values (EVCardAttribute *attr,
+							       EVCardAttributeParam *param, ...);
+
+void                  e_vcard_attribute_param_add_value       (EVCardAttributeParam *param,
+							       const char *value);
+void                  e_vcard_attribute_param_add_values      (EVCardAttributeParam *param,
+							       ...);
+void                  e_vcard_attribute_param_remove_values   (EVCardAttributeParam *param);
+
+/* EVCard* accessors.  nothing returned from these functions should be
+   freed by the caller. */
+EVCardAttribute *e_vcard_get_attribute (EVCard *vcard, const char *name);
+GList*           e_vcard_get_attributes       (EVCard *evcard);
+const char*      e_vcard_attribute_get_group  (EVCardAttribute *attr);
+const char*      e_vcard_attribute_get_name   (EVCardAttribute *attr);
+GList*           e_vcard_attribute_get_values (EVCardAttribute *attr);  /* GList elements are of type char* */
+GList*           e_vcard_attribute_get_values_decoded (EVCardAttribute *attr); /* GList elements are of type GString* */
+
+/* special accessors for single valued attributes */
+gboolean              e_vcard_attribute_is_single_valued      (EVCardAttribute *attr);
+char*                 e_vcard_attribute_get_value             (EVCardAttribute *attr);
+GString*              e_vcard_attribute_get_value_decoded     (EVCardAttribute *attr);
+
+GList*           e_vcard_attribute_get_params       (EVCardAttribute *attr);
+GList*           e_vcard_attribute_get_param        (EVCardAttribute *attr, const char *name);
+const char*      e_vcard_attribute_param_get_name   (EVCardAttributeParam *param);
+GList*           e_vcard_attribute_param_get_values (EVCardAttributeParam *param);
+
+/* special TYPE= parameter predicate (checks for TYPE=@typestr */
+gboolean         e_vcard_attribute_has_type         (EVCardAttribute *attr, const char *typestr);
+
+/* Utility functions. */
+char*            e_vcard_escape_string (const char *str);
+char*            e_vcard_unescape_string (const char *str);
+
+G_END_DECLS
+
+#endif /* _EVCARD_H */
Index: addressbook/libebook-dbus/e-name-western.c
===================================================================
--- addressbook/libebook-dbus/e-name-western.c	(revision 409)
+++ addressbook/libebook-dbus/e-name-western.c	(working copy)
@@ -1 +1,990 @@
-link ../libebook-orbit/./e-name-western.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * A simple Western name parser.
+ *
+ * <Nat> Jamie, do you know anything about name parsing?
+ * <jwz> Are you going down that rat hole?  Bring a flashlight.
+ *
+ * Authors:
+ *   Nat Friedman <nat@ximian.com>
+ *
+ * Copyright 1999 - 2001, Ximian, Inc.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+ 
+#include "e-name-western.h"
+#include "e-name-western-tables.h"
+
+typedef struct {
+	int prefix_idx;
+	int first_idx;
+	int middle_idx;
+	int nick_idx;
+	int last_idx;
+	int suffix_idx;
+} ENameWesternIdxs;
+
+static int
+e_name_western_str_count_words (const char *str)
+{
+	int word_count;
+	const char *p;
+
+	word_count = 0;
+
+	for (p = str; p != NULL; p = g_utf8_strchr (p, -1, ' ')) {
+		word_count ++;
+		p = g_utf8_next_char (p);
+	}
+
+	return word_count;
+}
+
+static void
+e_name_western_cleanup_string (char **str)
+{
+	char *newstr;
+	char *p;
+
+	if (*str == NULL)
+		return;
+
+	/* skip any spaces and commas at the start of the string */
+	p = *str;
+	while (g_unichar_isspace (g_utf8_get_char(p)) || *p == ',')
+		p = g_utf8_next_char (p);
+
+	/* make the copy we're going to return */
+	newstr = g_strdup (p);
+
+	if ( strlen(newstr) > 0) {
+		/* now search from the back, skipping over any spaces and commas */
+		p = newstr + strlen (newstr);
+		p = g_utf8_prev_char (p);
+		while (g_unichar_isspace (g_utf8_get_char(p)) || *p == ',')
+			p = g_utf8_prev_char (p);
+		/* advance p to after the character that caused us to exit the
+		   previous loop, and end the string. */
+		if ((! g_unichar_isspace (g_utf8_get_char (p))) && *p != ',')
+			p = g_utf8_next_char (p);
+		*p = '\0';
+	}
+
+	g_free (*str);
+	*str = newstr;
+}
+
+static char *
+e_name_western_get_words_at_idx (char *str, int idx, int num_words)
+{
+	GString *words;
+	char *p;
+	int   word_count;
+
+	/*
+	 * Walk to the end of the words.
+	 */
+	words = g_string_new ("");
+	word_count = 0;
+	p = str + idx;
+	while (word_count < num_words && *p != '\0') {
+		while (! g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0') {
+			words = g_string_append_unichar (words, g_utf8_get_char (p));
+			p = g_utf8_next_char (p);
+		}
+
+		while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+			p = g_utf8_next_char (p);
+
+		word_count ++;
+	}
+
+	return g_string_free (words, FALSE);
+}
+
+/*
+ * What the fuck is wrong with glib's MAX macro.
+ */ 
+static int
+e_name_western_max (const int a, const int b)
+{
+	if (a > b)
+		return a;
+
+	return b;
+}
+
+static gboolean
+e_name_western_word_is_suffix (char *word)
+{
+	int i;
+
+	for (i = 0; i < G_N_ELEMENTS (western_sfx_index); i++) {
+		const char *suffix;
+		int length;
+
+		suffix = western_sfx_table + western_sfx_index[i];
+		length = strlen (suffix);
+
+		if (!g_strcasecmp (word, suffix) || 
+		    ( !g_strncasecmp (word, suffix, length) &&
+		      strlen(word) == length + 1 &&
+		      word[length] == '.' ))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static char *
+e_name_western_get_one_prefix_at_str (char *str)
+{
+	char *word;
+	int   i;
+
+	/*
+	 * Check for prefixes from our table.
+	 */
+	for (i = 0; i < G_N_ELEMENTS (western_pfx_index); i++) {
+		int pfx_words;
+		const char *prefix;
+		char *words;
+
+		prefix = western_pfx_table + western_pfx_index[i];
+		pfx_words = e_name_western_str_count_words (prefix);
+		words = e_name_western_get_words_at_idx (str, 0, pfx_words);
+
+		if (! g_strcasecmp (words, prefix))
+			return words;
+
+		g_free (words);
+	}
+
+	/*
+	 * Check for prefixes we don't know about.  These are always a
+	 * sequence of more than one letters followed by a period.
+	 */
+	word = e_name_western_get_words_at_idx (str, 0, 1);
+
+	if (g_utf8_strlen (word, -1) > 2 && 
+	    g_unichar_isalpha (g_utf8_get_char (word)) &&
+	    g_unichar_isalpha (g_utf8_get_char (g_utf8_next_char (word))) &&
+	    word [strlen (word) - 1] == '.')
+		return word;
+
+	g_free (word);
+
+	return NULL;
+}
+
+static char *
+e_name_western_get_prefix_at_str (char *str)
+{
+	char *pfx;
+	char *pfx1;
+	char *pfx2;
+	char *p;
+
+	/* Get the first prefix. */
+	pfx1 = e_name_western_get_one_prefix_at_str (str);
+
+	if (pfx1 == NULL)
+		return NULL;
+
+	/* Check for a second prefix. */
+	p = str + strlen (pfx1);
+	while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+		p = g_utf8_next_char (p);
+
+	pfx2 = e_name_western_get_one_prefix_at_str (p);
+
+	if (pfx2 != NULL) {
+		int pfx_len;
+
+		pfx_len = (p + strlen (pfx2)) - str;
+		pfx = g_malloc0 (pfx_len + 1);
+		strncpy (pfx, str, pfx_len);
+	} else {
+		pfx = g_strdup (pfx1);
+	}
+
+	g_free (pfx1);
+	g_free (pfx2);
+
+	return pfx;
+}
+
+static void
+e_name_western_extract_prefix (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	char *pfx;
+
+	pfx = e_name_western_get_prefix_at_str (name->full);
+
+	if (pfx == NULL)
+		return;
+
+	idxs->prefix_idx = 0;
+	name->prefix     = pfx;
+}
+
+static gboolean
+e_name_western_is_complex_last_beginning (char *word)
+{
+	int i;
+
+	for (i = 0; i < G_N_ELEMENTS (western_complex_last_index); i++) {
+		const char *last = western_complex_last_table + western_complex_last_index[i];
+		if (! g_strcasecmp (word, last))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+e_name_western_extract_first (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	/*
+	 * If there's a prefix, then the first name is right after it.
+	 */
+	if (idxs->prefix_idx != -1) {
+		int   first_idx;
+		char *p;
+
+		first_idx = idxs->prefix_idx + strlen (name->prefix);
+
+		/* Skip past white space. */
+		p = name->full + first_idx;
+		while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+			p = g_utf8_next_char (p);
+
+		if (*p == '\0')
+			return;
+
+		idxs->first_idx = p - name->full;
+		name->first = e_name_western_get_words_at_idx (
+			name->full, idxs->first_idx, 1);
+
+	} else {
+
+		/*
+		 * Otherwise, the first name is probably the first string.
+		 */
+		idxs->first_idx = 0;
+		name->first = e_name_western_get_words_at_idx (
+			name->full, idxs->first_idx, 1);
+	}
+
+	/*
+	 * Check that we didn't just assign the beginning of a
+	 * compound last name to the first name.
+	 */
+	if (name->first != NULL) {
+		if (e_name_western_is_complex_last_beginning (name->first)) {
+			g_free (name->first);
+			name->first = NULL;
+			idxs->first_idx = -1;
+		}
+	}
+}
+
+static void
+e_name_western_extract_middle (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	char *word;
+	char *middle;
+
+	/*
+	 * Middle names can only exist if you have a first name.
+	 */
+	if (idxs->first_idx == -1)
+		return;
+
+	middle = name->full + idxs->first_idx + strlen (name->first);
+	if (*middle == '\0')
+		return;
+
+	middle = g_utf8_next_char (middle);
+	if (*middle == '\0')
+		return;
+	
+	/*
+	 * Search for the first space (or the terminating \0)
+	 */
+	while (g_unichar_isspace (g_utf8_get_char (middle)) &&
+	       *middle != '\0')
+		middle = g_utf8_next_char (middle);
+		
+	if (*middle == '\0')
+		return;
+
+	/*
+	 * Skip past the nickname, if it's there.
+	 */
+	if (*middle == '\"') {
+		if (idxs->nick_idx == -1)
+			return;
+
+		middle = name->full + idxs->nick_idx + strlen (name->nick);
+		middle = g_utf8_next_char (middle);
+		
+		while (g_unichar_isspace (g_utf8_get_char (middle)) &&
+		       *middle != '\0')
+			middle = g_utf8_next_char (middle);
+
+		if (*middle == '\0')
+			return;
+	}
+
+	/*
+	 * Make sure this isn't the beginning of a complex last name.
+	 */
+	word = e_name_western_get_words_at_idx (name->full, middle - name->full, 1);
+	if (e_name_western_is_complex_last_beginning (word)) {
+		g_free (word);
+		return;
+	}
+
+	/*
+	 * Make sure this isn't a suffix.
+	 */
+	e_name_western_cleanup_string (& word);
+	if (e_name_western_word_is_suffix (word)) {
+		g_free (word);
+		return;
+	}
+
+	/*
+	 * Make sure we didn't just grab a cute nickname.
+	 */
+	if (word [0] == '\"') {
+		g_free (word);
+		return;
+	}
+	
+	idxs->middle_idx = middle - name->full;
+	name->middle = word;
+}
+
+static void
+e_name_western_extract_nickname (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	char *nick;
+	int   start_idx;
+	GString *str;
+
+	if (idxs->first_idx == -1)
+		return;
+
+	if (idxs->middle_idx > idxs->first_idx)
+		nick = name->full + idxs->middle_idx + strlen (name->middle);
+	else
+		nick = name->full + idxs->first_idx + strlen (name->first);
+
+	while (*nick != '\"' && *nick != '\0')
+		nick = g_utf8_next_char (nick);
+
+	if (*nick != '\"')
+		return;
+
+	start_idx = nick - name->full;
+
+	/*
+	 * Advance to the next double quote.
+	 */
+	str = g_string_new ("\"");
+	nick = g_utf8_next_char (nick);
+
+	while (*nick != '\"' && *nick != '\0') {
+		str = g_string_append_unichar (str, g_utf8_get_char (nick));
+		nick = g_utf8_next_char (nick);
+	}
+
+	if (*nick == '\0') {
+		g_string_free (str, TRUE);
+		return;
+	}
+	str = g_string_append (str, "\"");
+
+	name->nick = g_string_free (str, FALSE);
+
+	idxs->nick_idx = start_idx;
+}
+
+static int
+e_name_western_last_get_max_idx (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	int max_idx = -1;
+
+	if (name->prefix != NULL)
+		max_idx = e_name_western_max (
+			max_idx, idxs->prefix_idx + strlen (name->prefix));
+
+	if (name->first != NULL)
+		max_idx = e_name_western_max (
+			max_idx, idxs->first_idx + strlen (name->first));
+
+	if (name->middle != NULL)
+		max_idx = e_name_western_max (
+			max_idx, idxs->middle_idx + strlen (name->middle));
+
+	if (name->nick != NULL)
+		max_idx = e_name_western_max (
+			max_idx, idxs->nick_idx + strlen (name->nick));
+
+	return max_idx;
+}
+
+static void
+e_name_western_extract_last (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	char *word;
+	int   idx = -1;
+	char *last;
+
+	idx = e_name_western_last_get_max_idx (name, idxs);
+
+	/*
+	 * In the case where there is no preceding name element, the
+	 * name is either just a first name ("Nat", "John"), is a
+	 * single-element name ("Cher", which we treat as a first
+	 * name), or is just a last name.  The only time we can
+	 * differentiate a last name alone from a single-element name
+	 * or a first name alone is if it's a complex last name ("de
+	 * Icaza", "van Josephsen").  So if there is no preceding name
+	 * element, we check to see whether or not the first part of
+	 * the name is the beginning of a complex name.  If it is,
+	 * we subsume the entire string.  If we accidentally subsume
+	 * the suffix, this will get fixed in the fixup routine.
+	 */
+	if (idx == -1) {
+		word = e_name_western_get_words_at_idx (name->full, 0, 1);
+		if (! e_name_western_is_complex_last_beginning (word)) {
+			g_free (word);
+			return;
+		}
+
+		name->last     = g_strdup (name->full);
+		idxs->last_idx = 0;
+		return;
+	}
+
+	last = name->full + idx;
+
+	/* Skip past the white space. */
+	while (g_unichar_isspace (g_utf8_get_char (last)) && *last != '\0')
+		last = g_utf8_next_char (last);
+
+	if (*last == '\0')
+		return;
+
+	word = e_name_western_get_words_at_idx (name->full, last - name->full, 1);
+	e_name_western_cleanup_string (& word);
+	if (e_name_western_word_is_suffix (word)) {
+		g_free (word);
+		return;
+	}
+	g_free (word);
+
+	/*
+	 * Subsume the rest of the string into the last name.  If we
+	 * accidentally include the prefix, it will get fixed later.
+	 * This is the only way to handle things like "Miguel de Icaza
+	 * Amozorrutia" without dropping data and forcing the user
+	 * to retype it.
+	 */
+	name->last = g_strdup (last);
+	idxs->last_idx = last - name->full;
+}
+
+static char *
+e_name_western_get_preceding_word (char *str, int idx)
+{
+	int   word_len;
+	char *word;
+	char *p;
+
+	p = str + idx;
+
+	while (g_unichar_isspace (g_utf8_get_char (p)) && p > str)
+		p = g_utf8_prev_char (p);
+
+	while (! g_unichar_isspace (g_utf8_get_char (p)) && p > str)
+		p = g_utf8_prev_char (p);
+
+	if (g_unichar_isspace (g_utf8_get_char (p)))
+		p = g_utf8_next_char (p);
+
+	word_len = (str + idx) - p;
+	word = g_malloc0 (word_len + 1);
+	if (word_len > 0)
+		strncpy (word, p, word_len);
+
+	return word;
+}
+
+static char *
+e_name_western_get_suffix_at_str_end (char *str)
+{
+	char *suffix;
+	char *p;
+
+	/*
+	 * Walk backwards till we reach the beginning of the
+	 * (potentially-comma-separated) list of suffixes.
+	 */
+	p = str + strlen (str);
+	while (1) {
+		char *nextp;
+		char *word;
+
+		word = e_name_western_get_preceding_word (str, p - str);
+		nextp = p - strlen (word);
+		if (nextp == str) {
+			g_free (word);
+			break;
+		}
+		nextp = g_utf8_prev_char (nextp);
+		
+		e_name_western_cleanup_string (& word);
+
+		if (e_name_western_word_is_suffix (word)) {
+			p = nextp;
+			g_free (word);
+		} else {
+			g_free (word);
+			break;
+		}
+	}
+
+	if (p == (str + strlen (str)))
+		return NULL;
+
+	suffix = g_strdup (p);
+	e_name_western_cleanup_string (& suffix);
+
+	if (strlen (suffix) == 0) {
+		g_free (suffix);
+		return NULL;
+	}
+
+	return suffix;
+}
+
+static void
+e_name_western_extract_suffix (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	name->suffix = e_name_western_get_suffix_at_str_end (name->full);
+
+	if (name->suffix == NULL)
+		return;
+
+	idxs->suffix_idx = strlen (name->full) - strlen (name->suffix);
+}
+
+static gboolean
+e_name_western_detect_backwards (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	char *comma;
+	char *word;
+
+	comma = g_utf8_strchr (name->full, -1, ',');
+
+	if (comma == NULL)
+		return FALSE;
+
+	/*
+	 * If there's a comma, we need to detect whether it's
+	 * separating the last name from the first or just separating
+	 * suffixes.  So we grab the word which comes before the
+	 * comma and check if it's a suffix.
+	 */
+	word = e_name_western_get_preceding_word (name->full, comma - name->full);
+
+	if (e_name_western_word_is_suffix (word)) {
+		g_free (word);
+		return FALSE;
+	}
+
+	g_free (word);
+	return TRUE;
+}
+
+static void
+e_name_western_reorder_asshole (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	char *prefix;
+	char *last;
+	char *suffix;
+	char *firstmidnick;
+	char *newfull;
+
+	char *comma;
+	char *p;
+
+	if (! e_name_western_detect_backwards (name, idxs))
+		return;
+
+	/*
+	 * Convert
+	 *    <Prefix> <Last name>, <First name> <Middle[+nick] name> <Suffix>
+	 * to
+	 *    <Prefix> <First name> <Middle[+nick] name> <Last name> <Suffix>
+	 */
+	
+	/*
+	 * Grab the prefix from the beginning.
+	 */
+	prefix = e_name_western_get_prefix_at_str (name->full);
+
+	/*
+	 * Everything from the end of the prefix to the comma is the
+	 * last name.
+	 */
+	comma = g_utf8_strchr (name->full, -1, ',');
+	if (comma == NULL)
+		return;
+
+	p = name->full + (prefix == NULL ? 0 : strlen (prefix));
+
+	while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+		p = g_utf8_next_char (p);
+
+	last = g_malloc0 (comma - p + 1);
+	strncpy (last, p, comma - p);
+
+	/*
+	 * Get the suffix off the end.
+	 */
+	suffix = e_name_western_get_suffix_at_str_end (name->full);
+
+	/*
+	 * Firstmidnick is everything from the comma to the beginning
+	 * of the suffix.
+	 */
+	p = g_utf8_next_char (comma);
+
+	while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+		p = g_utf8_next_char (p);
+
+	if (suffix != NULL) {
+		char *q;
+
+		/*
+		 * Point q at the beginning of the suffix.
+		 */
+		q = name->full + strlen (name->full) - strlen (suffix);
+		q = g_utf8_prev_char (q);
+
+		/*
+		 * Walk backwards until we hit the space which
+		 * separates the suffix from firstmidnick.
+		 */
+		while (! g_unichar_isspace (g_utf8_get_char (q)) && q > comma)
+			q = g_utf8_prev_char (q);
+
+		if ((q - p + 1) > 0) {
+			firstmidnick = g_malloc0 (q - p + 1);
+			strncpy (firstmidnick, p, q - p);
+		} else
+			firstmidnick = NULL;
+	} else {
+		firstmidnick = g_strdup (p);
+	}
+
+	/*
+	 * Create our new reordered version of the name.
+	 */
+#define NULLSTR(a) ((a) == NULL ? "" : (a))
+	newfull = g_strdup_printf ("%s %s %s %s", NULLSTR (prefix), NULLSTR (firstmidnick),
+				   NULLSTR (last), NULLSTR (suffix));
+	g_strstrip (newfull);
+	g_free (name->full);
+	name->full = newfull;
+
+
+	g_free (prefix);
+	g_free (firstmidnick);
+	g_free (last);
+	g_free (suffix);
+}
+
+static void
+e_name_western_zap_nil (char **str, int *idx)
+{
+	if (*str == NULL)
+		return;
+
+	if (strlen (*str) != 0)
+		return;
+
+	*idx = -1;
+	g_free (*str);
+	*str = NULL;
+}
+
+#define FINISH_CHECK_MIDDLE_NAME_FOR_CONJUNCTION			\
+	char *last_start = NULL;					\
+	if (name->last)							\
+		last_start = g_utf8_strchr (name->last, -1, ' ');	\
+	if (last_start) {						\
+		char *new_last, *new_first;				\
+									\
+		new_last = g_strdup (g_utf8_next_char (last_start));	\
+		*last_start = '\0';					\
+									\
+		idxs->last_idx += (last_start - name->last) + 1;	\
+									\
+		new_first = g_strdup_printf ("%s %s %s",		\
+					     name->first,		\
+					     name->middle,		\
+					     name->last);		\
+									\
+		g_free (name->first);					\
+		g_free (name->middle);					\
+		g_free (name->last);					\
+									\
+		name->first = new_first;				\
+		name->middle = NULL;					\
+		name->last = new_last;					\
+									\
+		idxs->middle_idx = -1;					\
+	} else {							\
+		char *new_first;					\
+									\
+		new_first = g_strdup_printf ("%s %s %s",		\
+					     name->first,		\
+					     name->middle,		\
+					     name->last);		\
+									\
+		g_free (name->first);					\
+		g_free (name->middle);					\
+		g_free (name->last);					\
+									\
+		name->first = new_first;				\
+		name->middle = NULL;					\
+		name->last = NULL;					\
+		idxs->middle_idx = -1;					\
+		idxs->last_idx = -1;					\
+	}
+
+#define CHECK_MIDDLE_NAME_FOR_CONJUNCTION(conj) \
+	if (idxs->middle_idx != -1 && !strcmp (name->middle, conj)) {	\
+		FINISH_CHECK_MIDDLE_NAME_FOR_CONJUNCTION	\
+	}
+
+#define CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE(conj) \
+	if (idxs->middle_idx != -1 && !strcasecmp (name->middle, conj)) {	\
+		FINISH_CHECK_MIDDLE_NAME_FOR_CONJUNCTION	\
+	}
+
+static void
+e_name_western_fixup (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+	/*
+	 * The middle and last names cannot be the same.
+	 */
+	if (idxs->middle_idx != -1 && idxs->middle_idx == idxs->last_idx) {
+		idxs->middle_idx = -1;
+		g_free (name->middle);
+		name->middle = NULL;
+	}
+
+	/*
+	 * If we have a middle name and no last name, then we mistook
+	 * the last name for the middle name.
+	 */
+	if (idxs->last_idx == -1 && idxs->middle_idx != -1) {
+		idxs->last_idx   = idxs->middle_idx;
+		name->last       = name->middle;
+		name->middle     = NULL;
+		idxs->middle_idx = -1;
+	}
+
+	/*
+	 * Check to see if we accidentally included the suffix in the
+	 * last name.
+	 */
+	if (idxs->suffix_idx != -1 && idxs->last_idx != -1 &&
+	    idxs->suffix_idx < (idxs->last_idx + strlen (name->last))) {
+		char *sfx;
+
+		sfx = name->last + (idxs->suffix_idx - idxs->last_idx);
+		if (sfx != NULL) {
+			char *newlast;
+			char *p;
+
+			p = sfx;
+			p = g_utf8_prev_char (p);
+			while (g_unichar_isspace (g_utf8_get_char (p)) && p > name->last)
+				p = g_utf8_prev_char (p);
+			p = g_utf8_next_char (p);
+
+			newlast = g_malloc0 (p - name->last + 1);
+			strncpy (newlast, name->last, p - name->last);
+			g_free (name->last);
+			name->last = newlast;
+		}
+	}
+
+	/*
+	 * If we have a prefix and a first name, but no last name,
+	 * then we need to assign the first name to the last name.
+	 * This way we get things like "Mr Friedman" correctly.
+	 */
+	if (idxs->first_idx != -1 && idxs->prefix_idx != -1 &&
+	    idxs->last_idx == -1) {
+		name->last      = name->first;
+		idxs->last_idx  = idxs->first_idx;
+		idxs->first_idx = -1;
+		name->first     = NULL;
+	}
+
+	if (idxs->middle_idx != -1) {
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("&");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("*");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("|");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("^");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("&&");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("||");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("+");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("-");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("and");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("or");
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("plus");
+
+		/* Spanish */
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("y");
+
+		/* German */
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("und");
+
+		/* Italian */
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("e");
+
+		/* Czech */
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("a");
+
+		/* Finnish */
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("ja");
+
+		/* French */
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("et");
+
+		/* Russian */
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("\xd0\x98"); /* u+0418 */
+		CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("\xd0\xb8"); /* u+0438 */
+	}
+
+	/*
+	 * Remove stray spaces and commas (although there don't seem
+	 * to be any in the test cases, they might show up later).
+	 */
+	e_name_western_cleanup_string (& name->prefix);
+	e_name_western_cleanup_string (& name->first);
+	e_name_western_cleanup_string (& name->middle);
+	e_name_western_cleanup_string (& name->nick);
+	e_name_western_cleanup_string (& name->last);
+	e_name_western_cleanup_string (& name->suffix);
+
+	/*
+	 * Make zero-length strings just NULL.
+	 */
+	e_name_western_zap_nil (& name->prefix, & idxs->prefix_idx);
+	e_name_western_zap_nil (& name->first,  & idxs->first_idx);
+	e_name_western_zap_nil (& name->middle, & idxs->middle_idx);
+	e_name_western_zap_nil (& name->nick,   & idxs->nick_idx);
+	e_name_western_zap_nil (& name->last,   & idxs->last_idx);
+	e_name_western_zap_nil (& name->suffix, & idxs->suffix_idx);
+}
+
+/**
+ * e_name_western_western_parse_fullname:
+ * @full_name: A string containing a western name.
+ *
+ * Parses @full_name and returns an #ENameWestern struct filled with
+ * the component parts of the name.
+ *
+ * Return value: A new #ENameWestern struct.
+ **/
+ENameWestern *
+e_name_western_parse (const char *full_name)
+{
+	ENameWesternIdxs *idxs;
+	ENameWestern *wname;
+	char *end;
+
+	if (!g_utf8_validate (full_name, -1, (const char **)&end)) {
+		g_warning ("e_name_western_parse passed invalid UTF-8 sequence");
+		*end = '\0';
+	}
+
+	wname = g_new0 (ENameWestern, 1);
+
+	wname->full = g_strdup (full_name);
+
+	idxs = g_new0 (ENameWesternIdxs, 1);
+
+	idxs->prefix_idx = -1;
+	idxs->first_idx  = -1;
+	idxs->middle_idx = -1;
+	idxs->nick_idx   = -1;
+	idxs->last_idx   = -1;
+	idxs->suffix_idx = -1;
+	
+	/*
+	 * An extremely simple algorithm.
+	 *
+	 * The goal here is to get it right 95% of the time for
+	 * Western names.
+	 *
+	 * First we check to see if this is an ass-backwards name
+	 * ("Prefix Last, First Middle Suffix").  These names really
+	 * suck (imagine "Dr von Johnson, Albert Roderick Jr"), so
+	 * we reorder them first and then parse them.
+	 *
+	 * Next, we grab the most obvious assignments for the various
+	 * parts of the name.  Once this is done, we check for stupid
+	 * errors and fix them up.
+	 */
+	e_name_western_reorder_asshole  (wname, idxs);
+
+	e_name_western_extract_prefix   (wname, idxs);
+	e_name_western_extract_first    (wname, idxs);
+	e_name_western_extract_nickname (wname, idxs);
+	e_name_western_extract_middle   (wname, idxs);
+	e_name_western_extract_last     (wname, idxs);
+	e_name_western_extract_suffix   (wname, idxs);
+
+	e_name_western_fixup            (wname, idxs);
+
+	g_free (idxs);
+
+	return wname;
+}
+
+/**
+ * e_name_western_free:
+ * @w: an #ENameWestern struct
+ *
+ * Frees the @w struct and its contents.
+ **/
+void
+e_name_western_free (ENameWestern *w)
+{
+
+	g_free (w->prefix);
+	g_free (w->first);
+	g_free (w->middle);
+	g_free (w->nick);
+	g_free (w->last);
+	g_free (w->suffix);
+	
+	g_free (w->full);
+
+	g_free (w);
+}
Index: addressbook/libebook-dbus/gen-western-table.py
===================================================================
--- addressbook/libebook-dbus/gen-western-table.py	(revision 409)
+++ addressbook/libebook-dbus/gen-western-table.py	(working copy)
@@ -1 +1,39 @@
-link ../libebook-orbit/gen-western-table.py
\ No newline at end of file
+#! /usr/bin/env python
+
+import sys
+
+var = None
+strings = []
+
+def output():
+    print "static const char %s_table[] = {" % var
+    for s in strings:
+        print "  \"%s\\0\"" % s
+    print "};"
+
+    print "static const guint %s_index[] = {" % var
+    index = 0
+    for s in strings:
+        print "  %d," % index
+        index += len(s) + 1
+    print "};\n"
+    
+(S_VAR, S_STRING) = range(0, 2)
+state = S_VAR
+
+print "/* This file is generated by gen-western-table.py. DO NOT EDIT */"
+
+for l in sys.stdin.readlines():
+    l = l.strip()
+    if l == "":
+        state = S_VAR
+        output()
+        var = None
+        strings = []
+    elif state == S_VAR:
+        var = l
+        state = S_STRING
+    elif state == S_STRING:
+        strings.append(l)
+
+output()
Index: addressbook/libebook-dbus/e-name-western.h
===================================================================
--- addressbook/libebook-dbus/e-name-western.h	(revision 409)
+++ addressbook/libebook-dbus/e-name-western.h	(working copy)
@@ -1 +1,25 @@
-link ../libebook-orbit/./e-name-western.h
\ No newline at end of file
+#ifndef __E_NAME_WESTERN_H__
+#define __E_NAME_WESTERN_H__
+
+G_BEGIN_DECLS
+
+typedef struct {
+
+	/* Public */
+	char *prefix;
+	char *first;
+	char *middle;
+	char *nick;
+	char *last;
+	char *suffix;
+
+	/* Private */
+	char *full;
+} ENameWestern;
+
+ENameWestern *e_name_western_parse (const char   *full_name);
+void          e_name_western_free  (ENameWestern *w);
+
+G_END_DECLS
+
+#endif /* ! __E_NAME_WESTERN_H__ */
Index: addressbook/libebook-dbus/e-address-western.c
===================================================================
--- addressbook/libebook-dbus/e-address-western.c	(revision 409)
+++ addressbook/libebook-dbus/e-address-western.c	(working copy)
@@ -1 +1,456 @@
-link ../libebook-orbit/./e-address-western.c
\ No newline at end of file
+/* --------------------------------------------------
+
+ An address parser, yielding fields as per RFC 2426.
+
+ Author:
+   Jesse Pavel (jpavel@ximian.com)
+
+ Copyright 2000, Ximian, Inc.
+   -------------------------------------------------- 
+*/
+
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+
+#include "e-address-western.h"
+#include "libedataserver/e-util.h"
+
+/* These are the keywords that will distinguish the start of an extended
+   address. */
+
+static char *extended_keywords[] = {
+	"apt", "apartment", "suite", NULL
+};
+
+
+
+static gboolean
+e_address_western_is_line_blank (gchar *line)
+{
+	gboolean blank = TRUE;
+	gint cntr;
+
+	/* A blank line consists of whitespace only, or a NULL line. */
+	for (cntr = 0; line[cntr] != '\0'; cntr++ ) {
+		if (!isspace(line[cntr])) {
+			blank = FALSE;
+			break;
+		}
+	}
+
+	return blank;
+}
+
+
+
+/* In the array of lines, `lines', we will erase the line at line_num, and
+ shift the remaining lines, up to line number num_lines, up one position. */
+
+static void
+e_address_western_shift_line (gchar *lines[], gint line_num, gint num_lines)
+{
+	gint cntr;
+
+	if (line_num >= (num_lines - 1)) {
+		/* It is the last line, so simply shift in a NULL. */
+		lines[line_num] = NULL;
+	}
+	else {
+		for (cntr = line_num; cntr < num_lines; cntr++)
+			lines[cntr] = lines[cntr + 1];
+	}
+}
+
+
+static void 
+e_address_western_remove_blank_lines (gchar *lines[], gint *linecntr)
+{
+	gint cntr;
+
+	for (cntr = 0; cntr < *linecntr; cntr++) {
+		if (e_address_western_is_line_blank (lines[cntr])) {
+			/* Delete the blank line, and shift all subsequent lines up
+			   one spot to fill its old spot. */
+			e_address_western_shift_line (lines, cntr, *linecntr);
+
+			/* Since we must check the newly shifted line, let's 
+			  not advance the counter on this next pass. */
+			cntr--;
+
+			/* There is now one less line, total. */
+			*linecntr -= 1;
+		}
+	}
+}
+ 
+
+static gboolean
+e_address_western_is_po_box (gchar *line)
+{
+	gboolean retval = FALSE;
+
+	/* In which phase of processing are we? */
+	enum State { FIRSTCHAR, SECONDCHAR, WHITESPACE } state;
+	
+	
+	/* If the first two letters of the line are `p' and `o', and these
+	 are in turn followed by whitespace before another letter, then I
+	 will deem the line a representation of a PO Box address. */
+	
+	gint cntr;
+
+	state = FIRSTCHAR;
+	for (cntr = 0; line[cntr] != '\0'; cntr++) {
+		if (state == FIRSTCHAR)	{
+			if (isalnum(line[cntr])) {
+				if (tolower(line[cntr]) == 'p')
+					state = SECONDCHAR;
+				else {
+					retval = FALSE;
+					break;
+				}
+			}
+		}
+		else if (state == SECONDCHAR) {
+			if (isalnum (line[cntr])) {
+				if (tolower(line[cntr]) == 'o')
+					state = WHITESPACE;
+				else {
+					retval = FALSE;
+					break;
+				}
+			}
+		}
+		else if (state == WHITESPACE) {
+			if (isspace (line[cntr])) {
+				retval = TRUE;
+				break;
+			}
+			else if (isalnum (line[cntr])) {
+				retval = FALSE;
+				break;
+			}
+		}
+	}
+
+	return retval;
+}
+
+/* A line that contains a comma followed eventually by a number is
+  deemed to be the line in the form of <town, region postal-code>. */
+
+static gboolean
+e_address_western_is_postal (gchar *line)
+{
+	gboolean retval;
+	int cntr;
+	
+	if (strchr (line, ',') == NULL)
+		retval = FALSE;  /* No comma. */
+	else {
+		int index;
+		
+		/* Ensure that the first character after the comma is
+		 a letter. */
+		index = strcspn (line, ",");
+		index++;
+		while (isspace(line[index]))
+			index++;
+		
+		if (!isalpha (line[index]))
+			return FALSE;   /* FIXME: ugly control flow. */
+
+		cntr = strlen(line) - 1;
+
+		/* Go to the character immediately following the last
+		  whitespace character. */
+		while (cntr >= 0 && isspace(line[cntr]))
+			cntr--;	
+		
+		while (cntr >= 0 && !isspace(line[cntr]))
+			cntr--;
+
+		if (cntr == 0)
+			retval = FALSE;
+		else {
+			if (isdigit (line[cntr+1]))
+				retval = TRUE;
+			else
+				retval = FALSE;
+		}
+	}	
+
+	return retval;
+}
+
+static gchar *
+e_address_western_extract_po_box (gchar *line)
+{
+	/* Return everything from the beginning of the line to
+	   the end of the first word that contains a number. */
+	
+	int index;
+
+	index = 0;
+	while (!isdigit(line[index]))
+		index++;
+	
+	while (isgraph(line[index]))
+		index++;
+	
+	return g_strndup (line, index);
+}
+
+static gchar *
+e_address_western_extract_locality (gchar *line)
+{
+	gint index;
+
+	/* Everything before the comma is the locality. */
+	index = strcspn(line, ",");
+
+	if (index == 0)
+		return NULL;
+	else
+		return g_strndup (line, index);
+}
+
+
+/* Whatever resides between the comma and the start of the
+  postal code is deemed to be the region. */
+
+static gchar *
+e_address_western_extract_region (gchar *line)
+{
+	gint start, end, alt_end;
+
+	start = strcspn (line, ",");
+	start++;
+	while (isspace(line[start]))
+		start++;
+	
+	end = strlen(line) - 1;
+	while (end >= 0 && isspace (line[end]))
+		end--;
+
+	alt_end = end;
+
+	while (end >= 0 && !isspace (line[end]))
+		end--;
+
+	while (end >= 0 && isspace (line[end]))
+		end--;
+	end++;
+
+	if (end <= start)
+		end = alt_end;
+	if (end <= start)
+		return g_strdup ("");
+
+	/* Between start and end lie the string. */
+	return g_strndup ( (line+start), end-start);
+}
+
+static gchar *
+e_address_western_extract_postal_code (gchar *line)
+{
+	int start, end;
+
+	end = strlen (line) - 1;
+	while (end >= 0 && isspace(line[end]))
+		end--;
+	
+	start = end;
+	end++;
+
+	while (start >= 0 && !isspace(line[start]))
+		start--;
+	start++;	
+
+	/* Between start and end lie the string. */
+	return g_strndup ( (line+start), end-start);
+}
+
+static void
+e_address_western_extract_street (gchar *line, gchar **street, gchar **extended)
+{
+        const gchar *split = NULL;
+	gint cntr;
+
+	for (cntr = 0; extended_keywords[cntr] != NULL; cntr++) {
+		split = e_util_strstrcase (line, extended_keywords[cntr]);
+		if (split != NULL)
+			break;
+	}
+
+	if (split != NULL) {
+		*street = g_strndup (line, (split - line));
+		*extended = g_strdup (split);
+	}
+	else {
+		*street = g_strdup (line);
+		*extended = NULL;
+	}
+
+}
+
+/**
+ * e_address_western_parse:
+ * @in_address: a string representing a mailing address
+ *
+ * Parses a string representing a mailing address into a
+ * structure of type #EAddressWestern.
+ *
+ * Return value: A new #EAddressWestern structure, or %NULL if the parsing failed.
+ **/
+EAddressWestern *
+e_address_western_parse (const gchar *in_address)
+{
+	gchar **lines;
+	gint linecntr, lineindex;
+	gchar *address;
+	gint cntr;
+	gboolean found_po_box, found_postal;
+
+	EAddressWestern *eaw;
+#if 0
+	gint start, end;  /* To be used to classify address lines. */
+#endif
+
+	if (in_address == NULL)
+		return NULL;
+	
+	eaw = (EAddressWestern *)g_malloc (sizeof(EAddressWestern));
+	eaw->po_box = NULL;
+	eaw->extended = NULL;
+	eaw->street = NULL;
+	eaw->locality = NULL;
+	eaw->region = NULL;
+	eaw->postal_code = NULL;
+	eaw->country = NULL;
+	
+	address = g_strndup (in_address, 2047);
+
+	/* The first thing I'll do is divide the multiline input string
+	into lines. */
+
+	/* ... count the lines. */
+	linecntr = 1;
+	lineindex = 0;
+	while (address[lineindex] != '\0') {
+		if (address[lineindex] == '\n')
+			linecntr++;
+			
+		lineindex++;
+	}
+
+	/* ... tally them. */
+	lines = (gchar **)g_malloc (sizeof(gchar *) * (linecntr+3));
+	lineindex = 0;
+	lines[0] = &address[0];
+	linecntr = 1;
+	while (address[lineindex] != '\0') {
+		if (address[lineindex] == '\n') {
+			lines[linecntr] = &address[lineindex + 1];
+			linecntr++;
+		}
+
+		lineindex++;
+	}
+
+	/* Convert the newlines at the end of each line (except the last,
+	 because it is already NULL terminated) to NULLs. */
+	for (cntr = 0; cntr < (linecntr - 1); cntr++) {
+		char *p;
+		p = strchr (lines[cntr], '\n');
+		if (p)
+			*p = '\0';
+	}
+
+	e_address_western_remove_blank_lines (lines, &linecntr);
+
+	/* Let's just test these functions. */
+	found_po_box = FALSE;
+	found_postal = FALSE;
+
+   	for (cntr = 0; cntr < linecntr; cntr++)  {
+		if (e_address_western_is_po_box (lines[cntr])) {
+			if (eaw->po_box == NULL)
+				eaw->po_box = e_address_western_extract_po_box (lines[cntr]);
+			found_po_box = TRUE;
+		}
+		else if (e_address_western_is_postal (lines[cntr])) {
+			if (eaw->locality == NULL)
+				eaw->locality = e_address_western_extract_locality (lines[cntr]);
+			if (eaw->region == NULL)
+				eaw->region = e_address_western_extract_region (lines[cntr]);
+			if (eaw->postal_code == NULL)
+				eaw->postal_code = e_address_western_extract_postal_code (lines[cntr]);
+			found_postal = TRUE;
+		}
+		else {
+			if (found_postal) {
+				if (eaw->country == NULL)
+					eaw->country = g_strdup (lines[cntr]);
+				else {
+					gchar *temp;
+					temp = g_strconcat (eaw->country, "\n", lines[cntr], NULL);
+					g_free (eaw->country);
+					eaw->country = temp;
+				}
+			}
+			else {
+				if (eaw->street == NULL) {
+					e_address_western_extract_street (lines[cntr], &eaw->street,
+										&eaw->extended );
+				}
+				else {
+					gchar *temp;
+					temp = g_strdup_printf (
+						"%s\n%s",
+						eaw->extended ? eaw->extended: "",
+						lines[cntr]);
+					g_free (eaw->extended);
+					eaw->extended = temp;
+				}
+			}
+		}
+	}			
+	
+	g_free (lines);
+	g_free (address);	
+	
+	return eaw;
+}	
+
+/**
+ * e_address_western_free:
+ * @eaw: an #EAddressWestern
+ *
+ * Frees @eaw and its contents.
+ **/
+void 
+e_address_western_free (EAddressWestern *eaw)
+{
+	if (eaw == NULL)
+		return;
+	
+	if (eaw->po_box != NULL)
+		g_free(eaw->po_box);
+	if (eaw->extended != NULL)
+		g_free(eaw->extended);
+	if (eaw->street != NULL)
+		g_free(eaw->street);
+	if (eaw->locality != NULL)
+		g_free(eaw->locality);
+	if (eaw->region != NULL)
+		g_free(eaw->region);
+	if (eaw->postal_code != NULL)
+		g_free(eaw->postal_code);
+	if (eaw->country != NULL)
+		g_free(eaw->country);
+	
+	g_free (eaw);
+}
+
Index: addressbook/libedata-book-dbus/e-book-backend-summary.h
===================================================================
--- addressbook/libedata-book-dbus/e-book-backend-summary.h	(revision 409)
+++ addressbook/libedata-book-dbus/e-book-backend-summary.h	(working copy)
@@ -1 +1,77 @@
-link ../libedata-book-orbit/e-book-backend-summary.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* 
+ * pas-backend-summary.h
+ * Copyright 2000, 2001, Ximian, Inc.
+ *
+ * Authors:
+ *   Chris Toshok <toshok@ximian.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __E_BOOK_BACKEND_SUMMARY_H__
+#define __E_BOOK_BACKEND_SUMMARY_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libedata-book/e-data-book-types.h>
+#include <libebook/e-contact.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_BACKEND_SUMMARY        (e_book_backend_summary_get_type ())
+#define E_BOOK_BACKEND_SUMMARY(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_BACKEND_SUMMARY, EBookBackendSummary))
+#define E_BOOK_BACKEND_SUMMARY_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), E_BOOK_BACKEND_TYPE, EBookBackendSummaryClass))
+#define E_IS_BACKEND_SUMMARY(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_BACKEND_SUMMARY))
+#define E_IS_BACKEND_SUMMARY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_BACKEND_SUMMARY))
+#define E_BOOK_BACKEND_SUMMARY_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_BACKEND_SUMMARY, EBookBackendSummaryClass))
+
+typedef struct _EBookBackendSummaryPrivate EBookBackendSummaryPrivate;
+
+struct _EBookBackendSummary{
+	GObject parent_object;
+	EBookBackendSummaryPrivate *priv;
+};
+
+struct _EBookBackendSummaryClass{
+	GObjectClass parent_class;
+};
+
+EBookBackendSummary* e_book_backend_summary_new              (const char *summary_path,
+							 int flush_timeout_millis);
+GType              e_book_backend_summary_get_type         (void);
+
+/* returns FALSE if the load fails for any reason (including that the
+   summary is out of date), TRUE if it succeeds */
+gboolean           e_book_backend_summary_load             (EBookBackendSummary *summary);
+/* returns FALSE if the save fails, TRUE if it succeeds (or isn't required due to no changes) */
+gboolean           e_book_backend_summary_save              (EBookBackendSummary *summary);
+
+void               e_book_backend_summary_add_contact       (EBookBackendSummary *summary, EContact *contact);
+void               e_book_backend_summary_remove_contact    (EBookBackendSummary *summary, const char *id);
+gboolean           e_book_backend_summary_check_contact     (EBookBackendSummary *summary, const char *id);
+
+void               e_book_backend_summary_touch             (EBookBackendSummary *summary);
+
+/* returns TRUE if the summary's mtime is >= @t. */
+gboolean           e_book_backend_summary_is_up_to_date     (EBookBackendSummary *summary, time_t t);
+
+gboolean           e_book_backend_summary_is_summary_query  (EBookBackendSummary *summary, const char *query);
+GPtrArray*         e_book_backend_summary_search            (EBookBackendSummary *summary, const char *query);
+char*              e_book_backend_summary_get_summary_vcard (EBookBackendSummary *summary, const char *id);
+
+G_END_DECLS
+
+#endif /* __E_BOOK_BACKEND_SUMMARY_H__ */
Index: addressbook/libedata-book-dbus/e-book-backend-cache.c
===================================================================
--- addressbook/libedata-book-dbus/e-book-backend-cache.c	(revision 409)
+++ addressbook/libedata-book-dbus/e-book-backend-cache.c	(working copy)
@@ -1 +1,500 @@
-link ../libedata-book-orbit/e-book-backend-cache.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* A class to cache address  book conents on local file system
+ *
+ * Copyright (C) 2004 Novell, Inc.
+ *
+ * Authors: Sivaiah Nallagatla <snallagatla@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include "e-book-backend-cache.h"
+#include "e-book-backend-sexp.h"
+
+struct _EBookBackendCachePrivate {
+	char *uri;
+};
+
+/* Property IDs */
+enum {
+	PROP_0,
+	PROP_URI
+};
+
+static GObjectClass *parent_class = NULL;
+
+static char *
+get_filename_from_uri (const char *uri)
+{
+	char *mangled_uri, *filename;
+	int i;
+
+	/* mangle the URI to not contain invalid characters */
+	mangled_uri = g_strdup (uri);
+	for (i = 0; i < strlen (mangled_uri); i++) {
+		switch (mangled_uri[i]) {
+		case ':' :
+		case '/' :
+			mangled_uri[i] = '_';
+		}
+	}
+
+	/* generate the file name */
+	filename = g_build_filename (g_get_home_dir (), ".evolution/cache/addressbook",
+				     mangled_uri, "cache.xml", NULL);
+
+	/* free memory */
+	g_free (mangled_uri);
+
+	return filename;
+}
+
+static void
+e_book_backend_cache_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	EBookBackendCache *cache;
+	EBookBackendCachePrivate *priv;
+	char *cache_file;
+
+	cache = E_BOOK_BACKEND_CACHE (object);
+	priv = cache->priv;
+
+	switch (property_id) {
+	case PROP_URI :
+		cache_file = get_filename_from_uri (g_value_get_string (value));
+		if (!cache_file)
+			break;
+
+		g_object_set (G_OBJECT (cache), "filename", cache_file, NULL);
+		g_free (cache_file);
+
+		if (priv->uri)
+			g_free (priv->uri);
+		priv->uri = g_value_dup_string (value);
+		break;
+	default :
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	}
+}
+
+static void
+e_book_backend_cache_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	EBookBackendCache *cache;
+	EBookBackendCachePrivate *priv;
+
+	cache = E_BOOK_BACKEND_CACHE (object);
+	priv = cache->priv;
+
+	switch (property_id) {
+	case PROP_URI :
+		g_value_set_string (value, priv->uri);
+		break;
+	default :
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	}
+}
+
+
+static void
+e_book_backend_cache_finalize (GObject *object)
+{
+	EBookBackendCache *cache;
+	EBookBackendCachePrivate *priv;
+
+	cache = E_BOOK_BACKEND_CACHE (object);
+	priv = cache->priv;
+
+	if (priv) {
+		if (priv->uri) {
+			g_free (priv->uri);
+			priv->uri = NULL;
+		}
+	
+
+		g_free (priv);
+		cache->priv = NULL;
+	}
+
+	parent_class->finalize (object);
+}
+
+static GObject *
+e_book_backend_cache_constructor (GType type,
+                                 guint n_construct_properties,
+                                 GObjectConstructParam *construct_properties)
+{
+	GObject *obj;
+	const char *uri;
+	char *cache_file;
+	EBookBackendCacheClass *klass;
+	GObjectClass *parent_class;
+
+	/* Invoke parent constructor. */
+	klass = E_BOOK_BACKEND_CACHE_CLASS (g_type_class_peek (E_TYPE_BOOK_BACKEND_CACHE));
+	parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+	obj = parent_class->constructor (type,
+					 n_construct_properties,
+					 construct_properties);
+  
+	/* extract uid */
+	if (!g_ascii_strcasecmp ( g_param_spec_get_name (construct_properties->pspec), "uri")) {
+		uri = g_value_get_string (construct_properties->value);
+		cache_file = get_filename_from_uri (uri);
+		if (cache_file)
+			g_object_set (obj, "filename", cache_file, NULL);
+		g_free (cache_file);
+	}
+
+	return obj;
+}
+
+static void
+e_book_backend_cache_class_init (EBookBackendCacheClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = e_book_backend_cache_finalize;
+	object_class->set_property = e_book_backend_cache_set_property;
+	object_class->get_property = e_book_backend_cache_get_property;
+
+        object_class->constructor = e_book_backend_cache_constructor;
+	g_object_class_install_property (object_class, PROP_URI,
+					 g_param_spec_string ("uri", NULL, NULL, "",
+							      G_PARAM_READABLE | G_PARAM_WRITABLE
+							      | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_book_backend_cache_init (EBookBackendCache *cache)
+{
+	EBookBackendCachePrivate *priv;
+
+	priv = g_new0 (EBookBackendCachePrivate, 1);
+
+	cache->priv = priv;
+
+}
+
+
+GType
+e_book_backend_cache_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		static GTypeInfo info = {
+                        sizeof (EBookBackendCacheClass),
+                        (GBaseInitFunc) NULL,
+                        (GBaseFinalizeFunc) NULL,
+                        (GClassInitFunc) e_book_backend_cache_class_init,
+                        NULL, NULL,
+                        sizeof (EBookBackendCache),
+                        0,
+                        (GInstanceInitFunc) e_book_backend_cache_init,
+                };
+		type = g_type_register_static (E_TYPE_FILE_CACHE, "EBookBackendCache", &info, 0);
+	}
+
+	return type;
+}
+
+/**
+ * e_book_backend_cache_new
+ * @uri: URI of the backend to be cached.
+ *
+ * Creates a new #EBookBackendCache object, which implements a local
+ * cache of #EContact objects, useful for remote backends.
+ *
+ * Return value: A new #EBookBackendCache.
+ */
+EBookBackendCache *
+e_book_backend_cache_new (const char *uri)
+{
+	EBookBackendCache *cache;
+        
+       	cache = g_object_new (E_TYPE_BOOK_BACKEND_CACHE, "uri", uri, NULL);
+
+        return cache;
+}
+
+/**
+ * e_book_backend_cache_get_contact:
+ * @cache: an #EBookBackendCache
+ * @uid: a unique contact ID
+ *
+ * Get a cached contact. Note that the returned #EContact will be
+ * newly created, and must be unreffed by the caller when no longer
+ * needed.
+ *
+ * Return value: A cached #EContact, or %NULL if @uid is not cached.
+ **/
+EContact *
+e_book_backend_cache_get_contact (EBookBackendCache *cache, const char *uid)
+{
+	const char *vcard_str;
+	EContact *contact = NULL;
+
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_CACHE (cache), NULL);
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	vcard_str = e_file_cache_get_object (E_FILE_CACHE (cache), uid);
+	if (vcard_str) {
+		contact = e_contact_new_from_vcard (vcard_str);
+		
+	}
+
+
+	return contact;
+}
+
+/**
+ * e_book_backend_cache_add_contact:
+ * @cache: an #EBookBackendCache
+ * @contact: an #EContact
+ *
+ * Adds @contact to @cache.
+ *
+ * Return value: %TRUE if the contact was cached successfully, %FALSE otherwise.
+ **/
+gboolean
+e_book_backend_cache_add_contact (EBookBackendCache *cache,
+				   EContact *contact)
+{
+	char *vcard_str;
+	const char *uid;
+	gboolean retval;
+	EBookBackendCachePrivate *priv;
+
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_CACHE (cache), FALSE);
+
+
+	priv = cache->priv;
+
+	uid = e_contact_get_const (contact, E_CONTACT_UID);
+	vcard_str = e_vcard_to_string (E_VCARD(contact), EVC_FORMAT_VCARD_30);
+
+	if (e_file_cache_get_object (E_FILE_CACHE (cache), uid))
+		retval = e_file_cache_replace_object (E_FILE_CACHE (cache), uid, vcard_str);
+	else
+		retval = e_file_cache_add_object (E_FILE_CACHE (cache), uid, vcard_str);
+
+	g_free (vcard_str);
+
+	return retval;
+}
+
+/**
+ * e_book_backend_cache_remove_contact:
+ * @cache: an #EBookBackendCache
+ * @uid: a unique contact ID
+ *
+ * Removes the contact identified by @uid from @cache.
+ *
+ * Return value: %TRUE if the contact was found and removed, %FALSE otherwise.
+ **/
+gboolean
+e_book_backend_cache_remove_contact (EBookBackendCache *cache,
+				    const char *uid)
+				      
+{
+	gboolean retval;
+	EBookBackendCachePrivate *priv;
+
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_CACHE (cache), FALSE);
+	g_return_val_if_fail (uid != NULL, FALSE);
+
+	priv = cache->priv;
+
+
+	if (!e_file_cache_get_object (E_FILE_CACHE (cache), uid)) {
+		return FALSE;
+	}
+
+	retval = e_file_cache_remove_object (E_FILE_CACHE (cache), uid);
+
+
+	return retval;
+}
+
+/**
+ * e_book_backend_cache_check_contact:
+ * @cache: an #EBookBackendCache
+ * @uid: a unique contact ID
+ *
+ * Checks if the contact identified by @uid exists in @cache.
+ *
+ * Return value: %TRUE if the cache contains the contact, %FALSE otherwise.
+ **/
+gboolean 
+e_book_backend_cache_check_contact (EBookBackendCache *cache, const char *uid)
+{
+
+	gboolean retval;
+	EBookBackendCachePrivate *priv;
+
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_CACHE (cache), FALSE);
+	g_return_val_if_fail (uid != NULL, FALSE);
+
+	priv = cache->priv;
+
+	retval = FALSE;
+	if (e_file_cache_get_object (E_FILE_CACHE (cache), uid)) 
+		retval = TRUE;
+	return retval;
+}
+
+/**
+ * e_book_backend_cache_get_contacts:
+ * @cache: an #EBookBackendCache
+ * @query: an s-expression
+ *
+ * Returns a list of #EContact elements from @cache matching @query.
+ * When done with the list, the caller must unref the contacts and
+ * free the list.
+ *
+ * Return value: A #GList of pointers to #EContact.
+ **/
+GList *
+e_book_backend_cache_get_contacts (EBookBackendCache *cache, const char *query)
+{
+        char *vcard_str;
+        GSList *l;
+	GList *list = NULL;
+	EContact *contact;
+        EBookBackendSExp *sexp = NULL;
+	const char *uid;
+
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_CACHE (cache), NULL);
+	if (query) {
+		sexp = e_book_backend_sexp_new (query);
+		if (!sexp)
+			return NULL;
+	}
+       
+
+        l = e_file_cache_get_objects (E_FILE_CACHE (cache));
+
+        for ( ; l != NULL; l = g_slist_next (l)) {
+                vcard_str = l->data;
+                if (vcard_str && !strncmp (vcard_str, "BEGIN:VCARD", 11)) {
+                        contact = e_contact_new_from_vcard (vcard_str);
+			uid = e_contact_get_const (contact, E_CONTACT_UID);
+                        if (contact && uid && *uid &&(query && e_book_backend_sexp_match_contact(sexp, contact)))
+				list = g_list_prepend (list, contact);
+                }
+                
+        }
+	if (l) {
+		g_slist_foreach (l, (GFunc) g_object_unref, NULL);
+		g_slist_free (l);
+	}
+	if (sexp)
+		g_object_unref (sexp);
+
+        return g_list_reverse (list);
+}
+
+/**
+ * e_book_backend_cache_search:
+ * @cache: an #EBookBackendCache
+ * @query: an s-expression
+ *
+ * Returns an array of pointers to unique contact ID strings for contacts
+ * in @cache matching @query. When done with the array, the caller must
+ * free the ID strings and the array.
+ *
+ * Return value: A #GPtrArray of pointers to contact ID strings.
+ **/
+GPtrArray *
+e_book_backend_cache_search (EBookBackendCache *cache, const char *query)
+{
+	GList *matching_contacts, *temp;
+	GPtrArray *ptr_array;
+	
+	matching_contacts = e_book_backend_cache_get_contacts (cache, query);
+	ptr_array = g_ptr_array_new ();
+	
+	temp = matching_contacts;
+	for (; matching_contacts != NULL; matching_contacts = g_list_next (matching_contacts)) {
+		g_ptr_array_add (ptr_array, e_contact_get (matching_contacts->data, E_CONTACT_UID));
+		g_object_unref (matching_contacts->data);
+	}
+	g_list_free (temp);
+	
+	return ptr_array;
+}
+
+/**
+ * e_book_backend_cache_exists:
+ * @uri: URI for the cache
+ *
+ * Checks if an #EBookBackendCache exists at @uri.
+ *
+ * Return value: %TRUE if cache exists, %FALSE if not.
+ **/
+gboolean 
+e_book_backend_cache_exists (const char *uri)
+{
+	char *file_name;
+	gboolean exists = FALSE;
+	file_name = get_filename_from_uri (uri);
+	
+	if (file_name && g_file_test (file_name, G_FILE_TEST_EXISTS)) {
+		exists = TRUE;
+		g_free (file_name);
+	}
+	
+	return exists;
+}
+
+/**
+ * e_book_backend_cache_set_populated:
+ * @cache: an #EBookBackendCache
+ *
+ * Flags @cache as being populated - that is, it is up-to-date on the 
+ * contents of the book it's caching.
+ **/
+void
+e_book_backend_cache_set_populated (EBookBackendCache *cache)
+{
+  	g_return_if_fail (E_IS_BOOK_BACKEND_CACHE (cache));
+	e_file_cache_add_object (E_FILE_CACHE (cache), "populated", "TRUE");
+	
+}
+
+/**
+ * e_book_backend_cache_is_populated:
+ * @cache: an #EBookBackendCache
+ *
+ * Checks if @cache is populated.
+ *
+ * Return value: %TRUE if @cache is populated, %FALSE otherwise.
+ **/
+gboolean
+e_book_backend_cache_is_populated (EBookBackendCache *cache)
+{
+  	g_return_val_if_fail (E_IS_BOOK_BACKEND_CACHE (cache), FALSE);
+	if (e_file_cache_get_object (E_FILE_CACHE (cache), "populated"))
+		return TRUE;
+	return FALSE;	
+}
Index: addressbook/libedata-book-dbus/e-book-backend-cache.h
===================================================================
--- addressbook/libedata-book-dbus/e-book-backend-cache.h	(revision 409)
+++ addressbook/libedata-book-dbus/e-book-backend-cache.h	(working copy)
@@ -1 +1,67 @@
-link ../libedata-book-orbit/e-book-backend-cache.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* 
+ *  A class to cache address book conents on local file system
+ * 
+ * Copyright (C) 2004 Novell, Inc.
+ *
+ * Authors: Sivaiah Nallagatla <snallagatla@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_BOOK_BACKEND_CACHE_H
+#define E_BOOK_BACKEND_CACHE_H
+
+#include <libedataserver/e-file-cache.h>
+#include <libebook/e-contact.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_BOOK_BACKEND_CACHE            (e_book_backend_cache_get_type ())
+#define E_BOOK_BACKEND_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_BOOK_BACKEND_CACHE, EBookBackendCache))
+#define E_BOOK_BACKEND_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_BOOK_BACKEND_CACHE, EBookBackendCacheClass))
+#define E_IS_BOOK_BACKEND_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_BOOK_BACKEND_CACHE))
+#define E_IS_BOOK_BACKEND_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_BOOK_BACKEND_CACHE))
+
+typedef struct _EBookBackendCachePrivate EBookBackendCachePrivate;
+
+typedef struct {
+	EFileCache parent;
+	EBookBackendCachePrivate *priv;
+} EBookBackendCache;
+
+typedef struct {
+	EFileCacheClass parent_class;
+} EBookBackendCacheClass;
+
+GType e_book_backend_cache_get_type (void);
+EBookBackendCache* e_book_backend_cache_new (const char *uri);
+EContact* e_book_backend_cache_get_contact (EBookBackendCache *cache, const char *uid);
+gboolean e_book_backend_cache_add_contact (EBookBackendCache *cache,
+					   EContact *contact);
+gboolean e_book_backend_cache_remove_contact (EBookBackendCache *cache,
+					      const char *uid);
+gboolean e_book_backend_cache_check_contact (EBookBackendCache *cache, const char *uid);
+GList*   e_book_backend_cache_get_contacts (EBookBackendCache *cache, const char *query);
+gboolean e_book_backend_cache_exists (const char *uri);
+void     e_book_backend_cache_set_populated (EBookBackendCache *cache);
+gboolean e_book_backend_cache_is_populated (EBookBackendCache *cache);
+GPtrArray* e_book_backend_cache_search (EBookBackendCache *cache, const char *query);
+
+
+
+
+G_END_DECLS
+
+#endif
Index: addressbook/libedata-book-dbus/e-book-backend-factory.h
===================================================================
--- addressbook/libedata-book-dbus/e-book-backend-factory.h	(revision 409)
+++ addressbook/libedata-book-dbus/e-book-backend-factory.h	(working copy)
@@ -1 +1,118 @@
-link ../libedata-book-orbit/e-book-backend-factory.h
\ No newline at end of file
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-book-backend-factory.h
+ *
+ * Copyright (C) 2004  Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser 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: Chris Toshok <toshok@ximian.com>
+ */
+
+#ifndef _E_BOOK_BACKEND_FACTORY_H_
+#define _E_BOOK_BACKEND_FACTORY_H_
+
+#include <glib-object.h>
+#include "e-book-backend.h"
+
+G_BEGIN_DECLS
+
+#define E_TYPE_BOOK_BACKEND_FACTORY        (e_book_backend_factory_get_type ())
+#define E_BOOK_BACKEND_FACTORY(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_BOOK_BACKEND_FACTORY, EBookBackendFactory))
+#define E_BOOK_BACKEND_FACTORY_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), E_TYPE_BOOK_BACKEND_FACTORY, EBookBackendFactoryClass))
+#define E_IS_BOOK_BACKEND_FACTORY(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_BOOK_BACKEND_FACTORY))
+#define E_IS_BOOK_BACKEND_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_BOOK_BACKEND_FACTORY))
+#define E_BOOK_BACKEND_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), E_TYPE_BOOK_BACKEND_FACTORY, EBookBackendFactoryClass))
+
+typedef struct _EBookBackendFactoryPrivate EBookBackendFactoryPrivate;
+
+typedef struct {
+	GObject            parent_object;
+} EBookBackendFactory;
+
+typedef struct {
+	GObjectClass parent_class;
+
+	const char*   (*get_protocol) (EBookBackendFactory *factory);
+	EBookBackend* (*new_backend)  (EBookBackendFactory *factory);
+} EBookBackendFactoryClass;
+
+GType                e_book_backend_factory_get_type             (void);
+
+const char*          e_book_backend_factory_get_protocol         (EBookBackendFactory *factory);
+EBookBackend*        e_book_backend_factory_new_backend          (EBookBackendFactory *factory);
+
+
+/* use this macro for simple, 1 factory modules */
+#define E_BOOK_BACKEND_FACTORY_SIMPLE(p,t,f) \
+typedef struct { \
+	EBookBackendFactory      parent_object; \
+} EBookBackend##t##Factory; \
+\
+typedef struct { \
+	EBookBackendFactoryClass parent_class; \
+} EBookBackend##t##FactoryClass; \
+\
+static void \
+_ ## p ##_factory_instance_init (EBookBackend## t ##Factory *factory) \
+{ \
+} \
+\
+static const char * \
+_ ## p ##_get_protocol (EBookBackendFactory *factory) \
+{ \
+	return #p; \
+} \
+\
+static EBookBackend* \
+_ ## p ##_new_backend (EBookBackendFactory *factory) \
+{ \
+	return (f) (); \
+} \
+\
+static void \
+_ ## p ##_factory_class_init (EBookBackend## t ##FactoryClass *klass) \
+{ \
+	E_BOOK_BACKEND_FACTORY_CLASS (klass)->get_protocol = _ ## p ##_get_protocol; \
+	E_BOOK_BACKEND_FACTORY_CLASS (klass)->new_backend = _ ## p ##_new_backend; \
+} \
+\
+static GType \
+_ ## p ##_factory_get_type (GTypeModule *module) \
+{ \
+	GType type; \
+\
+	GTypeInfo info = { \
+		sizeof (EBookBackend##t##FactoryClass), \
+		NULL, /* base_class_init */ \
+		NULL, /* base_class_finalize */ \
+		(GClassInitFunc)  _ ## p ##_factory_class_init, \
+		NULL, /* class_finalize */ \
+		NULL, /* class_data */ \
+		sizeof (EBookBackend##t##Factory), \
+		0,    /* n_preallocs */ \
+		(GInstanceInitFunc) _ ## p ##_factory_instance_init \
+	}; \
+\
+	type = g_type_module_register_type (module, \
+					    E_TYPE_BOOK_BACKEND_FACTORY, \
+					    "EBookBackend" #t "Factory", \
+					    &info, 0); \
+\
+	return type; \
+}
+
+G_END_DECLS
+
+#endif /* _E_BOOK_BACKEND_FACTORY_H_ */
Index: addressbook/libedata-book-dbus/ximian-vcard.h
===================================================================
--- addressbook/libedata-book-dbus/ximian-vcard.h	(revision 409)
+++ addressbook/libedata-book-dbus/ximian-vcard.h	(working copy)
@@ -1 +1,80 @@
-link ../libedata-book-orbit/ximian-vcard.h
\ No newline at end of file
+#define XIMIAN_VCARD \
+"BEGIN:VCARD\n" \
+"X-EVOLUTION-FILE-AS:Novell Ximian Group\n" \
+"ADR;TYPE=WORK:;Suite 500;8 Cambridge Center;Cambridge;MA;02142;USA\n" \
+"LABEL;TYPE=WORK:8 Cambridge Center, Suite 500\\nCambridge\\, MA\\n02142\\nUSA\n" \
+"TEL;WORK;VOICE:(617) 613-2000\n" \
+"TEL;WORK;FAX:(617) 613-2001\n" \
+"EMAIL;INTERNET:hello@ximian.com\n" \
+"URL:http://www.ximian.com/\n" \
+"ORG:Novell;Ximian Group\n" \
+"PHOTO;ENCODING=b;TYPE=JPEG:/9j/4AAQSkZJRgABAQEARwBHAAD//gAXQ3JlYXRlZCB3aXRo\n" \
+" IFRoZSBHSU1Q/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCM\n" \
+" cHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMj\n" \
+" IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAbgBkAwEiAAIRAQMRAf/EA\n" \
+" BwAAAIDAQEBAQAAAAAAAAAAAAAHBQYIBAMBAv/EAEYQAAEDAwEFBgMEBgQPAAAAAAECAwQABREG\n" \
+" BxIhMWETIkFRcYEUkaEIMkLBFSNSsbLRFmJydRgkMzY3Q0RGgpKTosLh8P/EABsBAQACAwEBAAA\n" \
+" AAAAAAAAAAAAEBQIDBgEH/8QALREAAQMCAwYGAgMAAAAAAAAAAQACAwQREiFRBRMiMUFhMnGBkb\n" \
+" HRBsEUofD/2gAMAwEAAhEDEQA/AH/RRRREVwXe9W2wwFzbpNZixkc1uqwPQeZ6CoHXevLfom1ds\n" \
+" 9h6a6D8PGCsFZHMk+CR4n86yzdbrqfaZqYBSnp0hRPZMoG62ynoOSR1Pua8Lg0XPJegX5Jv6k+0\n" \
+" bBjrWxp22LlkcBIlHs0HqEjiR64peT9umupqyWrhHhpP4WI6eHureNW7Tmw+DGaTI1FJVJdxksM\n" \
+" qKG09CrmfbFMCHpCw2xATDs8JrH4gykq+Z4mqifbMUZsxpd/QUllK53M2SCb2xa+bXvf0gcV0Uw\n" \
+" 0R/DVktH2hdUwlpFxjQrg1490tLPuOH0pvv2qE4jdchx1p8lNAj91Va87OtM3RCt+2Nx3Dyci/q\n" \
+" yPYcD7g1EZ+RR4rSMI9b/S2mhdbhKsmkdtWmNTuNxnXVW2cvgGZRASo+SV8j74PSmOlQUMpORWP\n" \
+" NU7MrjY0rlQFmdDTxOE4cQOo8R1Hyqe2Z7ZJ2m32bXfHnJVpJCUuqO8uP7+Kenh4eVXkFRFUMxx\n" \
+" G4UOSN0Zs4LU1FeEOWxOityYzqHWXEhSFoOQoHkQa963rBFFFFERUdfr1E09Y5d1mr3Y8ZsrV5n\n" \
+" yA6k4A6mpGkL9ojUym0W/TrLmAsGU+AeYBwgfPJ/4RREqrrcb1tJ1oUpBXLmObqUZ7rSByT0SkZ\n" \
+" J8zk1pHQmiLXo+zpbabC3SAp55Q7zyvM9PIUudiGmURbS7fpCMvzFFton8LSTxx6qH0FM7VV9VY\n" \
+" 9MzZ7aQt5tASw3+26ohKB/zEVSVFVvZzGMw02tqe/kpbI8LMR6/C/Xxq9QagfbbP+IW1QQ4Rycf\n" \
+" xncHRAIJ/rEfsmu2a9Fgsl2XIZjtj8bqwgfM1+9L2VFksESAV9o6hG886ebjqjvLWepUSarutdn\n" \
+" MXV+obRcZks/CwCQ5DKMpeBOTxzwzgA9KwfTtfxPOSB5GQUXc9pOjoC+zXe2HV5xiOC6PmkEfWp\n" \
+" xe6tAWghSVDIIOQRXxekNOx4b0WPZYLLTram19mwlJKSMHjjNUzQd2dZM7SNxczcLOsttqVzdYz\n" \
+" 3FewI9iKpK2mjMZdFe7ed9NfT9qZDI4OAd1Vkko50ndoui22kuXq2NBOO9JZSOH9sD9/z86c8gc\n" \
+" DUJNQlaFJUkKSoYII4EVGoKp9PIHt9e6lyRNlZhcqlsJ2guQpydL3F4mO7kw1KP3Fcyj0PEjrnz\n" \
+" rSAIIyOVYfvsJ3TGqlCKpTfYuJfjLHMDOR8jw9q2Foy+o1FpWBckY/XMpUoeRxxHsciu/jeJGB7\n" \
+" eRXPvaWuLT0U/RRRWaxQeVY82x3BVw2oXbJyhgoZR0AQM/UmthK+6fSsWbRQW9pV73x/tZPtwNE\n" \
+" Wj9Nw0WuwwIKQAGI6G/cAZ+tRW0lx5nTEW4Ntqdat9xjy5CEjJLSFZP5H2qaYdCkpUk5BGQa7Ap\n" \
+" DrSm3EpWhYKVJUMgg8wRXz+kqyyTG7VXUsV22Clrfc48+CzMiPIejvIC23EHIUDXNe79b7HbXbh\n" \
+" c5SI8ZvmtZ5nyA5k9BS7d0nfdMPuSdD3JtEZaitdom5Uznx3DzT6cPWkvq/V1611fGW5nZtBCgy\n" \
+" zFbXhtCycE5JxknxPhXR07RUeB3D11H+9lAfwcxmrrqLbxcHpikWGAw1FScByUkqWvrgEBPpxqi\n" \
+" ztdXWdqmNqIIjx7gykJUphJCXAM/eBJ5g4PQCmBZNiDKWEu364uF0jJYh4AT6qUDn2FVu6bPIkT\n" \
+" aTB08xKeMOU2H99eCtKRvZGQMZ7hwceNZxVGzsbmMzIBv5dfNeOjnsCdUwbTtKsV8nJgIccZkqw\n" \
+" lJcThDqvJJz8s4zUtLVzpc2vZZKt+qBIkyUKt0V0ONKSe+7g5SCPDr9Kv0tznXP1cNMyQfxnXBC\n" \
+" tqUyuB3gslftPjJLkGWB3u82o/Ij86bf2e7iqRoxyIpWfhpC0JHQ4V/5GlVtJcBt0RPiXif+00w\n" \
+" Ps5BQtNxP4TJP8Ka6rZZJpW37/Kq68ATlPeiiirBQ0HlWR9t9qVbtpEp/dwiY0h5J8Mgbp/h+ta\n" \
+" 4pM7fdKLumn2rxGbKn4BKl4HEtn73ywD7GiL7o28JuulLbKCsqLKUL/tJ7p+oqyIe4c6RGyzU4g\n" \
+" THLNJc3WpCt9gk8A54j3GPcdaZuoosy82V23QpaYpkEIdeIJKUeIAHieXPkTXA11DuassJsCefY\n" \
+" /SvYZN5FiGZU9edRwLDAXJny2mRukoStQBWQOQHjSjg7PYE7ZmzcZb7cG6KK5CZD6txOCcJQvPg\n" \
+" QAQfAn2q6RNOWi1D9J3R5dwlR2xmZPVv9mlI8ByTj59ar09Lm0jUIQl5Y0zAUMrQSPiXfHHpyz4\n" \
+" D1qTRvMQIieQAQXOtllfIDre/X2WqVmI8Qz6D9q0bP9SO37SrSpW8ZUVXw7q+YcKeSgeRyMZ65q\n" \
+" qammvWTalEv1yjOJtaWfh25CBvBOUkHPlxUeHlyq/MiPCitxorSGWG07qG0DASK45xZlx3GJDaH\n" \
+" WljCkLGQR6VGinY2ofIG8Lri2gOi37hxYG3zC+uT2HY6ZDbyFMrAUlwK7pB5HNRcp7nxqpzdN3G\n" \
+" CFQ7NObTa3nApcaSN/suOe4SDw6VK3O4swojsp9WGmxk9fIDrW4UzWkbt2K/v691vjec8YtZUTa\n" \
+" BL+IuMaIjiWWytXQn/wBD608tgtrVC0W2+tOFSFqd9icD6AVnmFFl6n1AhoAmRPdwcfgR4n2H7q\n" \
+" 2Ppi1N2exRojaQlKEBIHkAK7Gmi3MTWaLn6iTeSF+qmaKKK3rSiuedEanQ3I7qQpC0kEEZzXRRR\n" \
+" FjnaRoSVoq/KcYQv9HOr3mHB/qzz3SenhVi0ftAbnNNwLo6G5iQEodUcJd9fJX760ZqLTkHUdsd\n" \
+" hTWEOtuJwQoVl/XGyS7aakOPwGnJcDORujK0DqPH2qJV0cdUzC/0Oi3QTuhddqY84IuFukwnFFK\n" \
+" JDSmlEcwFDGR86ISI1tgtQ4jYaYaTuoSP/udJS1azvFoAZLnbsp4dm/klPQHmKs0faVEWkfEw32\n" \
+" 1f1CFj8q56XZNSwYG5t7fSt46yB5ucimM5L4c643pXWqU5tCteMpRKUfIIH86ipmvnnAUwoQSf2\n" \
+" 3lZ+g/nWEey5yfCtrquBo8Su0+4sQ46pEp1LTSeZUfoPOlnfr67fZKQlK0QkK/VtficV5nrXOkX\n" \
+" XUk9KQHp0gnghI7qPyAp1bOdkCmH2rneQHHxxQjHdb9OvWr2j2c2Didm74VZVVplGFuQXRsc2fO\n" \
+" Qgb1cmsSXQN1JH+TT4D+dPEAAADkK848duMylppISkDGBXrVkoCKKKKIiiqrrbX9m0JARIua1re\n" \
+" dJDMdoArcI58+AA8zVLsO26RqiS9Gsukpct5lHaKbTLaSrd8wFEZ9s0RN6vGRGZktlDqAoHzFKq\n" \
+" JtomzrPOuzGjZvwEBRTJfckttpbUOae9jJ5cBk8R514Wrbo7e489+3aTlvtQGTIkqElsdm2Mkq4\n" \
+" 4zyPKiKf1Hsj09flKdXEQh4/jR3VfMUvJ/2et1ZMOe8keSgFfyqz2LbfJ1M9IZs2kJsx2O0XnEN\n" \
+" yEAhA4ZwcZ58hxr7ZdtkvUS5SbTo2fJMRsuPkPoSG0jzKsDPPhz4HyoipDewC47+FXFWOjYH51Y\n" \
+" bTsAgtrSqc88/jwWrA+QxUlYtujupZ6oNo0nLlSUtqdKEyW04SMZOVYHiKjP8ACUt5/wB3pX/XT\n" \
+" /KiJnWLQ1nsTSURorad39lIFWZKUoThIAHSlNqDbLP0siKu96MnQ0ygSyVyGzvYxnlnB4jga87F\n" \
+" ttlamXJbs2j50xcZvtXUtyEZCfPB5+gyaIm9RSetm3J68RbhJgaSmPM25vtZaviW09knjxIOM8j\n" \
+" y8q7LHtzstwv/AOhrlBftkkudkFOLS43v5xgqSeHHx5daImrRX5QtK0hSTkGiiLMP2ho8wa1iSn\n" \
+" QoxVRQ20fAKClFQ9eIqq7LLJe7vreG7ZZCoZhqD8iZjustjnnwORkY8c+Wa1ZqbStt1PBMa4MId\n" \
+" Rz7wzg+dL8bEbA1vpa7RtK+CkpdWAfXjRFB7UpCNe6Kdm6NnJft1qluKuUJlvdKznPbYH3hzPXJ\n" \
+" PMGqZsk/zc2gf3G5/Cumc3sRsTO92Rcb3uB3XVjP1r4jYfYGwoN76QsYUEurGR5HjREudhUt2BP\n" \
+" 1TMYID0eyuuoJGRvJII+oq96I2iwtVz7rb7ZZWbalyzyJ9wKUjLsrKEkjH4cE8+Jz049bew+wNb\n" \
+" 3Z76N4YO66sZHlzob2H2Bkktb6CRglLqxkeXOiJZbAv9IMj+7X/wB6ag9lGnEaj17CRJA+BhZmy\n" \
+" lK+6EI44PQq3R6E06W9h9gZVvNb6FYxlLqwcfOhvYhYWt7s99G8MK3XVjI68aIo7UxgbR9IajhQ\n" \
+" 7/Du9yiSF3S3tMNrStlkAAt94DPDI4eJFUvYfNetqNYz4xAfjWZx5skZAUnJHD1FMVrYhYWVbzW\n" \
+" +2ojGUOrBx86EbD7A0FBvfRvDCt11YyPI8aIo23zdP6i2e621TaUJiXCfa1IucFPJt5KVnfHRWS\n" \
+" euPPNZ2YadfkNsspUp1aglCU8yTyrTSNh9gbCgjfSFjCgl1YyPI8al9PbItP2WamUywkuJ5KOVE\n" \
+" emeVEVw02ZH9H4YkEqdDYCifE4oqXbaS02lCRhIGBRRF//Z\n" \
+"END:VCARD"
Index: addressbook/libedata-book-dbus/e-book-backend-sync.c
===================================================================
--- addressbook/libedata-book-dbus/e-book-backend-sync.c	(revision 409)
+++ addressbook/libedata-book-dbus/e-book-backend-sync.c	(working copy)
@@ -1 +1,607 @@
-link ../libedata-book-orbit/e-book-backend-sync.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Author:
+ *   Chris Toshok (toshok@ximian.com)
+ *
+ * Copyright (C) 2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-data-book-marshal.h"
+#include "e-book-backend-sync.h"
+
+struct _EBookBackendSyncPrivate {
+  int mumble;
+};
+
+static GObjectClass *parent_class;
+
+/**
+ * e_book_backend_sync_construct:
+ * @backend: an #EBookBackendSync
+ *
+ * Does nothing.
+ *
+ * Return value: %TRUE.
+ **/
+gboolean
+e_book_backend_sync_construct (EBookBackendSync *backend)
+{
+	return TRUE;
+}
+
+/**
+ * e_book_backend_sync_create_contact:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @vcard: a VCard representation of a contact
+ * @contact: a pointer to a location to store the resulting #EContact
+ *
+ * Creates a new contact with the contents of @vcard in @backend.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_create_contact (EBookBackendSync *backend,
+				    EDataBook *book,
+				    guint32 opid,
+				    const char *vcard,
+				    EContact **contact)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (vcard, GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (contact, GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->create_contact_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->create_contact_sync) (backend, book, opid, vcard, contact);
+}
+
+/**
+ * e_book_backend_sync_remove:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ *
+ * Remove @book's database and storage overhead from the storage
+ * medium. This will delete all contacts in @book.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_remove (EBookBackendSync *backend,
+			    EDataBook *book,
+			    guint32 opid)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->remove_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->remove_sync) (backend, book, opid);
+}
+
+/**
+ * e_book_backend_sync_remove_contacts:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @id_list: a #GList of pointers to unique contact ID strings
+ * @removed_ids: a pointer to a location to store a list of the contacts actually removed
+ *
+ * Removes the contacts specified by @id_list from @backend. The returned list
+ * of removed contacts is in the same format as the passed-in list, and must be
+ * freed by the caller.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_remove_contacts (EBookBackendSync *backend,
+				     EDataBook *book,
+				     guint32 opid,
+				     GList *id_list,
+				     GList **removed_ids)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (id_list, GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (removed_ids, GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->remove_contacts_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->remove_contacts_sync) (backend, book, opid, id_list, removed_ids);
+}
+
+/**
+ * e_book_backend_sync_modify_contact:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @vcard: the string representation of a contact
+ * @contact: a pointer to a location to store the resulting #EContact
+ * 
+ * Modifies the contact specified by the ID embedded in @vcard, to
+ * reflect the full contents of @vcard.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_modify_contact (EBookBackendSync *backend,
+				    EDataBook *book,
+				    guint32 opid,
+				    const char *vcard,
+				    EContact **contact)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (vcard, GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (contact, GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->modify_contact_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->modify_contact_sync) (backend, book, opid, vcard, contact);
+}
+
+/**
+ * e_book_backend_sync_get_contact:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @id: a unique contact ID
+ * @vcard: a pointer to a location to store the resulting VCard string
+ *
+ * Gets a contact from @book.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_get_contact (EBookBackendSync *backend,
+				 EDataBook *book,
+				 guint32 opid,
+				 const char *id,
+				 char **vcard)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (id, GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (vcard, GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_contact_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_contact_sync) (backend, book, opid, id, vcard);
+}
+
+/**
+ * e_book_backend_sync_get_contact_list:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @query: an s-expression of the query to perform
+ * @contacts: a pointer to a location to store the resulting list of VCard strings
+ *
+ * Gets a list of contacts from @book. The list and its elements must be freed
+ * by the caller.
+ * 
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_get_contact_list (EBookBackendSync *backend,
+				      EDataBook *book,
+				      guint32 opid,
+				      const char *query,
+				      GList **contacts)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (query, GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (contacts, GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_contact_list_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_contact_list_sync) (backend, book, opid, query, contacts);
+}
+
+/**
+ * e_book_backend_sync_get_changes:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @change_id: a unique changes ID
+ * @changes: a pointer to a location to store the resulting list of changes
+ *
+ * Gets the changes made to @book since the last call to this function.
+ * The returned list will contain items of CORBA type
+ * #GNOME_Evolution_Addressbook_BookChangeItem.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_get_changes (EBookBackendSync *backend,
+				 EDataBook *book,
+				 guint32 opid,
+				 const char *change_id,
+				 GList **changes)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (change_id, GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (changes, GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_changes_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_changes_sync) (backend, book, opid, change_id, changes);
+}
+
+/**
+ * e_book_backend_sync_authenticate_user:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @user: the user's name
+ * @passwd: the user's password
+ * @auth_method: the authentication method desired
+ *
+ * Authenticates @user against @book.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_authenticate_user (EBookBackendSync *backend,
+				       EDataBook *book,
+				       guint32 opid,
+				       const char *user,
+				       const char *passwd,
+				       const char *auth_method)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (user && passwd && auth_method, GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->authenticate_user_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->authenticate_user_sync) (backend, book, opid, user, passwd, auth_method);
+}
+
+/**
+ * e_book_backend_sync_get_required_fields:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @fields: a pointer to a location to store the fields
+ *
+ * Gets a list of the fields required for all contacts in @book. The
+ * fields are represented by strings from #e_contact_field_name. The list
+ * and its contents must be freed by the caller.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_get_required_fields (EBookBackendSync *backend,
+					  EDataBook *book,
+					  guint32 opid,
+					  GList **fields)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (fields, GNOME_Evolution_Addressbook_OtherError);
+	
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_required_fields_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_required_fields_sync) (backend, book, opid, fields);
+}
+
+/**
+ * e_book_backend_sync_get_supported_fields:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @fields: a pointer to a location to store the fields
+ *
+ * Gets a list of the fields supported for contacts in @book. Other fields
+ * may not be stored. The fields are represented by strings from #e_contact_field_name.
+ * The list and its contents must be freed by the caller.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_get_supported_fields (EBookBackendSync *backend,
+					  EDataBook *book,
+					  guint32 opid,
+					  GList **fields)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (fields, GNOME_Evolution_Addressbook_OtherError);
+	
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_supported_fields_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_supported_fields_sync) (backend, book, opid, fields);
+}
+
+/**
+ * e_book_backend_sync_get_supported_auth_methods:
+ * @backend: an #EBookBackendSync
+ * @book: an #EDataBook
+ * @opid: the unique ID of the operation
+ * @methods: a pointer to a location to store the methods
+ *
+ * Gets a list of the authentication methods supported by @book. The
+ * methods are represented by strings. The list and its contents must
+ * be freed by the caller.
+ *
+ * Return value: An #EBookBackendSyncStatus indicating the outcome of the operation.
+ **/
+EBookBackendSyncStatus
+e_book_backend_sync_get_supported_auth_methods (EBookBackendSync *backend,
+						EDataBook *book,
+						guint32 opid,
+						GList **methods)
+{
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (E_IS_DATA_BOOK (book), GNOME_Evolution_Addressbook_OtherError);
+	g_return_val_if_fail (methods, GNOME_Evolution_Addressbook_OtherError);
+
+	g_assert (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_supported_auth_methods_sync);
+
+	return (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->get_supported_auth_methods_sync) (backend, book, opid, methods);
+}
+
+static void
+_e_book_backend_remove (EBookBackend *backend,
+			EDataBook    *book,
+			guint32       opid)
+{
+	EBookBackendSyncStatus status;
+
+	status = e_book_backend_sync_remove (E_BOOK_BACKEND_SYNC (backend), book, opid);
+
+	e_data_book_respond_remove (book, opid, status);
+}
+
+static void
+_e_book_backend_create_contact (EBookBackend *backend,
+				EDataBook    *book,
+				guint32       opid,
+				const char   *vcard)
+{
+	EBookBackendSyncStatus status;
+	EContact *contact = NULL;
+
+	status = e_book_backend_sync_create_contact (E_BOOK_BACKEND_SYNC (backend), book, opid, vcard, &contact);
+
+	e_data_book_respond_create (book, opid, status, contact);
+
+	if (contact)
+		g_object_unref (contact);
+}
+
+static void
+_e_book_backend_remove_contacts (EBookBackend *backend,
+				 EDataBook    *book,
+				 guint32       opid,
+				 GList        *id_list)
+{
+	EBookBackendSyncStatus status;
+	GList *ids = NULL;
+
+	status = e_book_backend_sync_remove_contacts (E_BOOK_BACKEND_SYNC (backend), book, opid, id_list, &ids);
+
+	e_data_book_respond_remove_contacts (book, opid, status, ids);
+
+	if (ids)
+		g_list_free (ids);
+}
+
+static void
+_e_book_backend_modify_contact (EBookBackend *backend,
+				EDataBook    *book,
+				guint32       opid,
+				const char   *vcard)
+{
+	EBookBackendSyncStatus status;
+	EContact *contact = NULL;
+
+	status = e_book_backend_sync_modify_contact (E_BOOK_BACKEND_SYNC (backend), book, opid, vcard, &contact);
+
+	e_data_book_respond_modify (book, opid, status, contact);
+
+	if (contact)
+		g_object_unref (contact);
+}
+
+static void
+_e_book_backend_get_contact (EBookBackend *backend,
+			     EDataBook    *book,
+			     guint32       opid,
+			     const char   *id)
+{
+	EBookBackendSyncStatus status;
+	char *vcard = NULL;
+
+	status = e_book_backend_sync_get_contact (E_BOOK_BACKEND_SYNC (backend), book, opid, id, &vcard);
+
+	e_data_book_respond_get_contact (book, opid, status, vcard);
+
+	if (vcard)
+		g_free (vcard);
+}
+
+static void
+_e_book_backend_get_contact_list (EBookBackend *backend,
+				  EDataBook    *book,
+				  guint32       opid,
+				  const char   *query)
+{
+	EBookBackendSyncStatus status;
+	GList *cards = NULL;
+
+	status = e_book_backend_sync_get_contact_list (E_BOOK_BACKEND_SYNC (backend), book, opid, query, &cards);
+
+	e_data_book_respond_get_contact_list (book, opid, status, cards);
+}
+
+static void
+_e_book_backend_get_changes (EBookBackend *backend,
+			     EDataBook    *book,
+			     guint32       opid,
+			     const char   *change_id)
+{
+	EBookBackendSyncStatus status;
+	GList *changes = NULL;
+
+	status = e_book_backend_sync_get_changes (E_BOOK_BACKEND_SYNC (backend), book, opid, change_id, &changes);
+
+	e_data_book_respond_get_changes (book, opid, status, changes);
+}
+
+static void
+_e_book_backend_authenticate_user (EBookBackend *backend,
+				   EDataBook    *book,
+				   guint32       opid,
+				   const char   *user,
+				   const char   *passwd,
+				   const char   *auth_method)
+{
+	EBookBackendSyncStatus status;
+
+	status = e_book_backend_sync_authenticate_user (E_BOOK_BACKEND_SYNC (backend), book, opid, user, passwd, auth_method);
+
+	e_data_book_respond_authenticate_user (book, opid, status);
+}
+
+static void
+_e_book_backend_get_required_fields (EBookBackend *backend,
+				      EDataBook    *book,
+				      guint32       opid)
+{
+	EBookBackendSyncStatus status;
+	GList *fields = NULL;
+
+	status = e_book_backend_sync_get_required_fields (E_BOOK_BACKEND_SYNC (backend), book, opid, &fields);
+
+	e_data_book_respond_get_required_fields (book, opid, status, fields);
+
+	if (fields) {
+		g_list_foreach (fields, (GFunc)g_free, NULL);
+		g_list_free (fields);
+	}
+}
+
+static void
+_e_book_backend_get_supported_fields (EBookBackend *backend,
+				      EDataBook    *book,
+				      guint32       opid)
+{
+	EBookBackendSyncStatus status;
+	GList *fields = NULL;
+
+	status = e_book_backend_sync_get_supported_fields (E_BOOK_BACKEND_SYNC (backend), book, opid, &fields);
+
+	e_data_book_respond_get_supported_fields (book, opid, status, fields);
+
+	if (fields) {
+		g_list_foreach (fields, (GFunc)g_free, NULL);
+		g_list_free (fields);
+	}
+}
+
+static void
+_e_book_backend_get_supported_auth_methods (EBookBackend *backend,
+					    EDataBook    *book,
+					    guint32       opid)
+{
+	EBookBackendSyncStatus status;
+	GList *methods = NULL;
+
+	status = e_book_backend_sync_get_supported_auth_methods (E_BOOK_BACKEND_SYNC (backend), book, opid, &methods);
+
+	e_data_book_respond_get_supported_auth_methods (book, opid, status, methods);
+
+	if (methods) {
+		g_list_foreach (methods, (GFunc)g_free, NULL);
+		g_list_free (methods);
+	}
+}
+
+static void
+e_book_backend_sync_init (EBookBackendSync *backend)
+{
+	EBookBackendSyncPrivate *priv;
+
+	priv          = g_new0 (EBookBackendSyncPrivate, 1);
+
+	backend->priv = priv;
+}
+
+static void
+e_book_backend_sync_dispose (GObject *object)
+{
+	EBookBackendSync *backend;
+
+	backend = E_BOOK_BACKEND_SYNC (object);
+
+	if (backend->priv) {
+		g_free (backend->priv);
+
+		backend->priv = NULL;
+	}
+
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+e_book_backend_sync_class_init (EBookBackendSyncClass *klass)
+{
+	GObjectClass *object_class;
+	EBookBackendClass *backend_class = E_BOOK_BACKEND_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class = (GObjectClass *) klass;
+
+	backend_class->remove = _e_book_backend_remove;
+	backend_class->create_contact = _e_book_backend_create_contact;
+	backend_class->remove_contacts = _e_book_backend_remove_contacts;
+	backend_class->modify_contact = _e_book_backend_modify_contact;
+	backend_class->get_contact = _e_book_backend_get_contact;
+	backend_class->get_contact_list = _e_book_backend_get_contact_list;
+	backend_class->get_changes = _e_book_backend_get_changes;
+	backend_class->authenticate_user = _e_book_backend_authenticate_user;
+	backend_class->get_required_fields = _e_book_backend_get_required_fields;
+	backend_class->get_supported_fields = _e_book_backend_get_supported_fields;
+	backend_class->get_supported_auth_methods = _e_book_backend_get_supported_auth_methods;
+
+	object_class->dispose = e_book_backend_sync_dispose;
+}
+
+/**
+ * e_book_backend_get_type:
+ */
+GType
+e_book_backend_sync_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo info = {
+			sizeof (EBookBackendSyncClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc)  e_book_backend_sync_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (EBookBackendSync),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) e_book_backend_sync_init
+		};
+
+		type = g_type_register_static (E_TYPE_BOOK_BACKEND, "EBookBackendSync", &info, 0);
+	}
+
+	return type;
+}
Index: addressbook/libedata-book-dbus/e-book-backend-sexp.c
===================================================================
--- addressbook/libedata-book-dbus/e-book-backend-sexp.c	(revision 409)
+++ addressbook/libedata-book-dbus/e-book-backend-sexp.c	(working copy)
@@ -1 +1,655 @@
-link ../libedata-book-orbit/e-book-backend-sexp.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* 
+ * pas-backend-card-sexp.c
+ * Copyright 1999, 2000, 2001, Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <libedataserver/e-sexp.h>
+#include <libedataserver/e-util.h>
+#include "e-book-backend-sexp.h"
+
+static GObjectClass *parent_class;
+
+typedef struct _SearchContext SearchContext;
+
+struct _EBookBackendSExpPrivate {
+	ESExp *search_sexp;
+	SearchContext *search_context;
+};
+
+struct _SearchContext {
+	EContact *contact;
+};
+
+static gboolean
+compare_im (EContact *contact, const char *str,
+	    char *(*compare)(const char*, const char*),
+	    EContactField im_field)
+{
+	GList    *aims, *l;
+	gboolean  found_it = FALSE;
+
+	aims = e_contact_get (contact, im_field);
+
+	for (l = aims; l != NULL; l = l->next) {
+		char *im = (char *) l->data;
+
+		if (im && compare (im, str)) {
+			found_it = TRUE;
+			break;
+		}
+	}
+	
+	g_list_foreach (aims, (GFunc)g_free, NULL);
+	g_list_free (aims);
+
+	return found_it;
+}
+
+static gboolean
+compare_im_aim (EContact *contact, const char *str,
+		char *(*compare)(const char*, const char*))
+{
+	return compare_im (contact, str, compare, E_CONTACT_IM_AIM);
+}
+
+static gboolean
+compare_im_msn (EContact *contact, const char *str,
+		char *(*compare)(const char*, const char*))
+{
+	return compare_im (contact, str, compare, E_CONTACT_IM_MSN);
+}
+
+static gboolean
+compare_im_icq (EContact *contact, const char *str,
+		char *(*compare)(const char*, const char*))
+{
+	return compare_im (contact, str, compare, E_CONTACT_IM_ICQ);
+}
+
+static gboolean
+compare_im_yahoo (EContact *contact, const char *str,
+		  char *(*compare)(const char*, const char*))
+{
+	return compare_im (contact, str, compare, E_CONTACT_IM_YAHOO);
+}
+
+static gboolean
+compare_im_jabber (EContact *contact, const char *str,
+		   char *(*compare)(const char*, const char*))
+{
+	return compare_im (contact, str, compare, E_CONTACT_IM_JABBER);
+}
+
+static gboolean
+compare_im_groupwise (EContact *contact, const char *str,
+		      char *(*compare)(const char*, const char*))
+{
+	return compare_im (contact, str, compare, E_CONTACT_IM_GROUPWISE);
+}
+
+static gboolean
+compare_email (EContact *contact, const char *str,
+	       char *(*compare)(const char*, const char*))
+{
+	int i;
+
+	for (i = E_CONTACT_EMAIL_1; i <= E_CONTACT_EMAIL_4; i ++) {
+		const char *email = e_contact_get_const (contact, i);
+
+		if (email && compare(email, str))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+compare_phone (EContact *contact, const char *str,
+	       char *(*compare)(const char*, const char*))
+{
+	int i;
+	gboolean rv = FALSE;
+
+	for (i = E_CONTACT_FIRST_PHONE_ID; i <= E_CONTACT_LAST_PHONE_ID; i ++) {
+		char *phone = e_contact_get (contact, i);
+
+		rv = phone && compare(phone, str);
+		g_free (phone);
+
+		if (rv)
+			break;
+	}
+
+	return rv;
+}
+
+static gboolean
+compare_name (EContact *contact, const char *str,
+	      char *(*compare)(const char*, const char*))
+{
+	const char *name;
+
+	name = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
+	if (name && compare (name, str))
+		return TRUE;
+
+	name = e_contact_get_const (contact, E_CONTACT_FAMILY_NAME);
+	if (name && compare (name, str))
+		return TRUE;
+	
+	name = e_contact_get_const (contact, E_CONTACT_GIVEN_NAME);
+	if (name && compare (name, str))
+		return TRUE;
+
+	name = e_contact_get_const (contact, E_CONTACT_NICKNAME);
+	if (name && compare (name, str))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean
+compare_address (EContact *contact, const char *str,
+		 char *(*compare)(const char*, const char*))
+{
+	
+	int i;
+	gboolean rv = FALSE;
+
+	for (i = E_CONTACT_FIRST_ADDRESS_ID; i <= E_CONTACT_LAST_ADDRESS_ID; i ++) {
+		EContactAddress *address = e_contact_get (contact, i);
+		if (address) {
+			rv =  (address->po && compare(address->po, str)) ||
+				(address->street && compare(address->street, str)) ||
+				(address->ext && compare(address->ext, str)) ||
+				(address->locality && compare(address->locality, str)) ||
+				(address->region && compare(address->region, str)) || 
+				(address->code && compare(address->code, str)) ||
+				(address->country && compare(address->country, str));
+			
+			e_contact_address_free (address);
+		
+			if (rv)
+				break;
+		}
+	}
+
+	return rv;
+	
+}
+
+static gboolean
+compare_category (EContact *contact, const char *str,
+		  char *(*compare)(const char*, const char*))
+{
+	GList *categories;
+	GList *iterator;
+	gboolean ret_val = FALSE;
+
+	categories = e_contact_get (contact, E_CONTACT_CATEGORY_LIST);
+
+	for (iterator = categories; iterator; iterator = iterator->next) {
+		const char *category = iterator->data;
+
+		if (compare(category, str)) {
+			ret_val = TRUE;
+			break;
+		}
+	}
+
+	g_list_foreach (categories, (GFunc)g_free, NULL);
+	g_list_free (categories);
+
+	return ret_val;
+}
+
+enum prop_type {
+	PROP_TYPE_NORMAL,
+	PROP_TYPE_LIST
+};
+static struct prop_info {
+	EContactField field_id;
+	const char *query_prop;
+	enum prop_type prop_type;
+	gboolean (*list_compare)(EContact *contact, const char *str,
+				 char *(*compare)(const char*, const char*));
+
+} prop_info_table[] = {
+#define NORMAL_PROP(f,q) {f, q, PROP_TYPE_NORMAL, NULL}
+#define LIST_PROP(q,c) {0, q, PROP_TYPE_LIST, c}
+
+	/* query prop,   type,              list compare function */
+	NORMAL_PROP ( E_CONTACT_FILE_AS, "file-as" ),
+	NORMAL_PROP ( E_CONTACT_UID, "id" ),
+	LIST_PROP ( "full-name", compare_name), /* not really a list, but we need to compare both full and surname */
+	NORMAL_PROP ( E_CONTACT_HOMEPAGE_URL, "url"),
+	NORMAL_PROP ( E_CONTACT_BLOG_URL, "blog-url"),
+	NORMAL_PROP ( E_CONTACT_CALENDAR_URI, "calurl"),
+	NORMAL_PROP ( E_CONTACT_FREEBUSY_URL, "fburl"),
+	NORMAL_PROP ( E_CONTACT_ICS_CALENDAR, "icscalendar"),
+	NORMAL_PROP ( E_CONTACT_VIDEO_URL, "video-url"),
+
+	NORMAL_PROP ( E_CONTACT_MAILER, "mailer"),
+	NORMAL_PROP ( E_CONTACT_ORG, "org"),
+	NORMAL_PROP ( E_CONTACT_ORG_UNIT, "org-unit"),
+	NORMAL_PROP ( E_CONTACT_OFFICE, "office"),
+	NORMAL_PROP ( E_CONTACT_TITLE, "title"),
+	NORMAL_PROP ( E_CONTACT_ROLE, "role"),
+	NORMAL_PROP ( E_CONTACT_MANAGER, "manager"),
+	NORMAL_PROP ( E_CONTACT_ASSISTANT, "assistant"),
+	NORMAL_PROP ( E_CONTACT_NICKNAME, "nickname"),
+	NORMAL_PROP ( E_CONTACT_SPOUSE, "spouse" ),
+	NORMAL_PROP ( E_CONTACT_NOTE, "note"),
+	LIST_PROP ( "im-aim",    compare_im_aim ),
+	LIST_PROP ( "im-msn",    compare_im_msn ),
+	LIST_PROP ( "im-icq",    compare_im_icq ),
+	LIST_PROP ( "im-jabber", compare_im_jabber ),
+	LIST_PROP ( "im-yahoo",  compare_im_yahoo ),
+	LIST_PROP ( "im-groupwise", compare_im_groupwise ),
+	LIST_PROP ( "email",     compare_email ),
+	LIST_PROP ( "phone",     compare_phone ),
+	LIST_PROP ( "address",   compare_address ),
+	LIST_PROP ( "category-list",  compare_category ),
+};
+static int num_prop_infos = sizeof(prop_info_table) / sizeof(prop_info_table[0]);
+
+static ESExpResult *
+entry_compare(SearchContext *ctx, struct _ESExp *f,
+	      int argc, struct _ESExpResult **argv,
+	      char *(*compare)(const char*, const char*))
+{
+	ESExpResult *r;
+	int truth = FALSE;
+
+	if (argc == 2
+	    && argv[0]->type == ESEXP_RES_STRING
+	    && argv[1]->type == ESEXP_RES_STRING) {
+		char *propname;
+		struct prop_info *info = NULL;
+		int i;
+		gboolean any_field;
+
+		propname = argv[0]->value.string;
+
+		any_field = !strcmp(propname, "x-evolution-any-field");
+		for (i = 0; i < num_prop_infos; i ++) {
+			if (any_field
+			    || !strcmp (prop_info_table[i].query_prop, propname)) {
+				info = &prop_info_table[i];
+		
+				if (any_field && info->field_id == E_CONTACT_UID) {
+					/* We need to skip UID from any field contains search
+					 * any-field search should be supported for the 
+					 * visible fields only.
+					 */
+					truth = FALSE;
+				}
+				else if (info->prop_type == PROP_TYPE_NORMAL) {
+					const char *prop = NULL;
+					/* straight string property matches */
+					
+					prop = e_contact_get_const (ctx->contact, info->field_id);
+
+					if (prop && compare(prop, argv[1]->value.string)) {
+						truth = TRUE;
+					}
+					if ((!prop) && compare("", argv[1]->value.string)) {
+						truth = TRUE;
+					}
+				}
+				else if (info->prop_type == PROP_TYPE_LIST) {
+					/* the special searches that match any of the list elements */
+					truth = info->list_compare (ctx->contact, argv[1]->value.string, compare);
+				}
+
+				/* if we're looking at all fields and find a match,
+				   or if we're just looking at this one field,
+				   break. */
+				if ((any_field && truth)
+				    || !any_field)
+					break;
+			}
+		}
+		
+	}
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = truth;
+
+	return r;
+}
+
+static ESExpResult *
+func_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+
+	return entry_compare (ctx, f, argc, argv, (char *(*)(const char*, const char*)) e_util_utf8_strstrcase);
+}
+
+static char *
+is_helper (const char *s1, const char *s2)
+{
+	if (!strcasecmp(s1, s2))
+		return (char*)s1;
+	else
+		return NULL;
+}
+
+static ESExpResult *
+func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+
+	return entry_compare (ctx, f, argc, argv, is_helper);
+}
+
+static char *
+endswith_helper (const char *s1, const char *s2)
+{
+	char *p;
+	if ((p = (char*) e_util_utf8_strstrcase(s1, s2))
+	    && (strlen(p) == strlen(s2)))
+		return p;
+	else
+		return NULL;
+}
+
+static ESExpResult *
+func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+
+	return entry_compare (ctx, f, argc, argv, endswith_helper);
+}
+
+static char *
+beginswith_helper (const char *s1, const char *s2)
+{
+	char *p;
+	if ((p = (char*) e_util_utf8_strstrcase(s1, s2))
+	    && (p == s1))
+		return p;
+	else
+		return NULL;
+}
+
+static ESExpResult *
+func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+
+	return entry_compare (ctx, f, argc, argv, beginswith_helper);
+}
+
+static ESExpResult *
+func_exists(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	ESExpResult *r;
+	int truth = FALSE;
+
+	if (argc == 1
+	    && argv[0]->type == ESEXP_RES_STRING) {
+		char *propname;
+		struct prop_info *info = NULL;
+		int i;
+
+		propname = argv[0]->value.string;
+
+		for (i = 0; i < num_prop_infos; i ++) {
+			if (!strcmp (prop_info_table[i].query_prop, propname)) {
+				info = &prop_info_table[i];
+				
+				if (info->prop_type == PROP_TYPE_NORMAL) {
+					const char *prop = NULL;
+					/* searches where the query's property
+					   maps directly to an ecard property */
+					
+					prop = e_contact_get_const (ctx->contact, info->field_id);
+
+					if (prop && *prop)
+						truth = TRUE;
+				}
+				else if (info->prop_type == PROP_TYPE_LIST) {
+				/* the special searches that match any of the list elements */
+					truth = info->list_compare (ctx->contact, "", (char *(*)(const char*, const char*)) e_util_utf8_strstrcase);
+				}
+
+				break;
+			}
+		}
+		
+	}
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = truth;
+
+	return r;
+}
+
+static ESExpResult *
+func_exists_vcard(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	ESExpResult *r;
+	int truth = FALSE;
+
+	if (argc == 1 && argv[0]->type == ESEXP_RES_STRING) {
+		const char *attr_name;
+		EVCardAttribute *attr;
+		GList *values;
+		char *s;
+
+		attr_name = argv[0]->value.string;
+		attr = e_vcard_get_attribute (E_VCARD (ctx->contact), attr_name);
+		if (attr) {
+			values = e_vcard_attribute_get_values (attr);
+			if (g_list_length (values) > 0) {
+				s = values->data;
+				if (s[0] != '\0') {
+					truth = TRUE;
+				}
+			}
+		}
+	}
+	
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = truth;
+
+	return r;
+}
+
+/* 'builtin' functions */
+static struct {
+	char *name;
+	ESExpFunc *func;
+	int type;		/* set to 1 if a function can perform shortcut evaluation, or
+				   doesn't execute everything, 0 otherwise */
+} symbols[] = {
+	{ "contains", func_contains, 0 },
+	{ "is", func_is, 0 },
+	{ "beginswith", func_beginswith, 0 },
+	{ "endswith", func_endswith, 0 },
+	{ "exists", func_exists, 0 },
+	{ "exists_vcard", func_exists_vcard, 0 },
+};
+
+/**
+ * e_book_backend_sexp_match_contact:
+ * @sexp: an #EBookBackendSExp
+ * @contact: an #EContact
+ *
+ * Checks if @contact matches @sexp.
+ *
+ * Return value: %TRUE if the contact matches, %FALSE otherwise.
+ **/
+gboolean
+e_book_backend_sexp_match_contact (EBookBackendSExp *sexp, EContact *contact)
+{
+	ESExpResult *r;
+	gboolean retval;
+
+	if (!contact) {
+		g_warning ("null EContact passed to e_book_backend_sexp_match_contact");
+		return FALSE;
+	}
+
+	sexp->priv->search_context->contact = g_object_ref (contact);
+
+	r = e_sexp_eval(sexp->priv->search_sexp);
+
+	retval = (r && r->type == ESEXP_RES_BOOL && r->value.bool);
+
+	g_object_unref(sexp->priv->search_context->contact);
+
+	e_sexp_result_free(sexp->priv->search_sexp, r);
+
+	return retval;
+}
+
+/**
+ * e_book_backend_sexp_match_vcard:
+ * @sexp: an #EBookBackendSExp
+ * @vcard: a VCard string
+ *
+ * Checks if @vcard matches @sexp.
+ *
+ * Return value: %TRUE if the VCard matches, %FALSE otherwise.
+ **/
+gboolean
+e_book_backend_sexp_match_vcard (EBookBackendSExp *sexp, const char *vcard)
+{
+	EContact *contact;
+	gboolean retval;
+
+	contact = e_contact_new_from_vcard (vcard);
+
+	retval = e_book_backend_sexp_match_contact (sexp, contact);
+
+	g_object_unref(contact);
+
+	return retval;
+}
+
+
+
+/**
+ * e_book_backend_sexp_new:
+ * @text: an s-expression to parse
+ *
+ * Creates a new #EBookBackendSExp from @text.
+ *
+ * Return value: A new #EBookBackendSExp.
+ **/
+EBookBackendSExp *
+e_book_backend_sexp_new (const char *text)
+{
+	EBookBackendSExp *sexp = g_object_new (E_TYPE_BACKEND_SEXP, NULL);
+	int esexp_error;
+	int i;
+
+	sexp->priv->search_sexp = e_sexp_new();
+
+	for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
+		if (symbols[i].type == 1) {
+			e_sexp_add_ifunction(sexp->priv->search_sexp, 0, symbols[i].name,
+					     (ESExpIFunc *)symbols[i].func, sexp->priv->search_context);
+		}
+		else {
+			e_sexp_add_function(sexp->priv->search_sexp, 0, symbols[i].name,
+					    symbols[i].func, sexp->priv->search_context);
+		}
+	}
+
+	e_sexp_input_text(sexp->priv->search_sexp, text, strlen(text));
+	esexp_error = e_sexp_parse(sexp->priv->search_sexp);
+
+	if (esexp_error == -1) {
+		g_object_unref (sexp);
+		sexp = NULL;
+	}
+
+	return sexp;
+}
+
+static void
+e_book_backend_sexp_dispose (GObject *object)
+{
+	EBookBackendSExp *sexp = E_BOOK_BACKEND_SEXP (object);
+
+	if (sexp->priv) {
+		e_sexp_unref(sexp->priv->search_sexp);
+
+		g_free (sexp->priv->search_context);
+		g_free (sexp->priv);
+		sexp->priv = NULL;
+	}
+
+	if (G_OBJECT_CLASS (parent_class)->dispose)
+		G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+e_book_backend_sexp_class_init (EBookBackendSExpClass *klass)
+{
+	GObjectClass  *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* Set the virtual methods. */
+
+	object_class->dispose = e_book_backend_sexp_dispose;
+}
+
+static void
+e_book_backend_sexp_init (EBookBackendSExp *sexp)
+{
+	EBookBackendSExpPrivate *priv;
+
+	priv             = g_new0 (EBookBackendSExpPrivate, 1);
+
+	sexp->priv = priv;
+	priv->search_context = g_new (SearchContext, 1);
+}
+
+/**
+ * e_book_backend_sexp_get_type:
+ */
+GType
+e_book_backend_sexp_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo info = {
+			sizeof (EBookBackendSExpClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc)  e_book_backend_sexp_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (EBookBackendSExp),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) e_book_backend_sexp_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT, "EBookBackendSExp", &info, 0);
+	}
+
+	return type;
+}
Index: addressbook/libedata-book-dbus/e-book-backend-sexp.h
===================================================================
--- addressbook/libedata-book-dbus/e-book-backend-sexp.h	(revision 409)
+++ addressbook/libedata-book-dbus/e-book-backend-sexp.h	(working copy)
@@ -1 +1,60 @@
-link ../libedata-book-orbit/e-book-backend-sexp.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* 
+ * pas-backend-card-sexp.h
+ * Copyright 2000, 2001, Ximian, Inc.
+ *
+ * Authors:
+ *   Chris Lahey <clahey@ximian.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __E_BOOK_BACKEND_SEXP_H__
+#define __E_BOOK_BACKEND_SEXP_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libebook/e-contact.h>
+#include <libedata-book/e-data-book-types.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_BACKEND_SEXP        (e_book_backend_sexp_get_type ())
+#define E_BOOK_BACKEND_SEXP(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_BACKEND_SEXP, EBookBackendSExp))
+#define E_BOOK_BACKEND_SEXP_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), E_BOOK_BACKEND_TYPE, EBookBackendSExpClass))
+#define E_IS_BACKEND_SEXP(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_BACKEND_SEXP))
+#define E_IS_BACKEND_SEXP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_BACKEND_SEXP))
+#define E_BOOK_BACKEND_SEXP_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_BACKEND_SEXP, EBookBackendSExpClass))
+
+typedef struct _EBookBackendSExpPrivate EBookBackendSExpPrivate;
+
+struct _EBookBackendSExp {
+	GObject parent_object;
+	EBookBackendSExpPrivate *priv;
+};
+
+struct _EBookBackendSExpClass {
+	GObjectClass parent_class;
+};
+
+EBookBackendSExp *e_book_backend_sexp_new      (const char *text);
+GType               e_book_backend_sexp_get_type (void);
+
+gboolean            e_book_backend_sexp_match_vcard (EBookBackendSExp *sexp, const char *vcard);
+gboolean            e_book_backend_sexp_match_contact (EBookBackendSExp *sexp, EContact *contact);
+
+G_END_DECLS
+
+#endif /* __E_BOOK_BACKEND_SEXP_H__ */
Index: COPYING
===================================================================
--- COPYING	(revision 409)
+++ COPYING	(working copy)
@@ -1,26 +1,23 @@
-		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
- Copyright (C) 1991 Free Software Foundation, Inc.
-    		    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
-[This is the first released version of the library GPL.  It is
- numbered 2 because it goes with version 2 of the ordinary GPL.]
-
 			    Preamble
 
   The licenses for most software are designed to take away your
 freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
 
-  This license, the Library General Public License, applies to some
-specially designated Free Software Foundation software, and to any
-other libraries whose authors decide to use it.  You can use it for
-your libraries, too.
-
   When we speak of free software, we are referring to freedom, not
 price.  Our General Public Licenses are designed to make sure that you
 have the freedom to distribute copies of free software (and charge for
@@ -30,347 +27,195 @@
 
   To protect your rights, we need to make restrictions that forbid
 anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if
-you distribute copies of the library, or if you modify it.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
 
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link a program with the library, you must provide
-complete object files to the recipients so that they can relink them
-with the library, after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
 
-  Our method of protecting your rights has two steps: (1) copyright
-the library, and (2) offer you this license which gives you legal
-permission to copy, distribute and/or modify the library.
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
 
-  Also, for each distributor's protection, we want to make certain
+  Also, for each author's protection and ours, we want to make certain
 that everyone understands that there is no warranty for this free
-library.  If the library is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original
-version, so that any problems introduced by others will not reflect on
-the original authors' reputations.
-
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
   Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that companies distributing free
-software will individually obtain patent licenses, thus in effect
-transforming the program into proprietary software.  To prevent this,
-we have made it clear that any patent must be licensed for everyone's
-free use or not licensed at all.
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
 
-  Most GNU software, including some libraries, is covered by the ordinary
-GNU General Public License, which was designed for utility programs.  This
-license, the GNU Library General Public License, applies to certain
-designated libraries.  This license is quite different from the ordinary
-one; be sure to read it in full, and don't assume that anything in it is
-the same as in the ordinary license.
-
-  The reason we have a separate public license for some libraries is that
-they blur the distinction we usually make between modifying or adding to a
-program and simply using it.  Linking a program with a library, without
-changing the library, is in some sense simply using the library, and is
-analogous to running a utility program or application program.  However, in
-a textual and legal sense, the linked executable is a combined work, a
-derivative of the original library, and the ordinary General Public License
-treats it as such.
-
-  Because of this blurred distinction, using the ordinary General
-Public License for libraries did not effectively promote software
-sharing, because most developers did not use the libraries.  We
-concluded that weaker conditions might promote sharing better.
-
-  However, unrestricted linking of non-free programs would deprive the
-users of those programs of all benefit from the free status of the
-libraries themselves.  This Library General Public License is intended to
-permit developers of non-free programs to use free libraries, while
-preserving your freedom as a user of such programs to change the free
-libraries that are incorporated in them.  (We have not seen how to achieve
-this as regards changes in header files, but we have achieved it as regards
-changes in the actual functions of the Library.)  The hope is that this
-will lead to faster development of free libraries.
-
   The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, while the latter only
-works together with the library.
-
-  Note that it is possible for a library to be covered by the ordinary
-General Public License rather than by this special one.
+modification follow.
 
-		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
-  0. This License Agreement applies to any software library which
-contains a notice placed by the copyright holder or other authorized
-party saying it may be distributed under the terms of this Library
-General Public License (also called "this License").  Each licensee is
-addressed as "you".
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
 
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
 
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
 
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
 
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-  
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
 distribute such modifications or work under the terms of Section 1
 above, provided that you also meet all of these conditions:
 
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
+    a) You must cause the modified files to carry prominent notices
     stating that you changed the files and the date of any change.
 
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
 
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
 These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
+identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
 themselves, then this License, and its terms, do not apply to those
 sections when you distribute them as separate works.  But when you
 distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
+on the Program, the distribution of the whole must be on the terms of
 this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
+entire whole, and thus to each and every part regardless of who wrote it.
 
 Thus, it is not the intent of this section to claim rights or contest
 your rights to work written entirely by you; rather, the intent is to
 exercise the right to control the distribution of derivative or
-collective works based on the Library.
+collective works based on the Program.
 
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
 a storage or distribution medium does not bring the other work under
 the scope of this License.
 
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
 
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
 
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
 
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
 
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
 
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
 
-  6. As an exception to the Sections above, you may also compile or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
 
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    c) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    d) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the source code distributed need not include anything that is normally
-distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
+  5. You are not required to accept this License, since you have not
 signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
+distribute the Program or its derivative works.  These actions are
 prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
 all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
+the Program or works based on it.
 
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
 restrictions on the recipients' exercise of the rights granted herein.
 You are not responsible for enforcing compliance by third parties to
 this License.
-
-  11. If, as a consequence of a court judgment or allegation of patent
+
+  7. If, as a consequence of a court judgment or allegation of patent
 infringement or for any other reason (not limited to patent issues),
 conditions are imposed on you (whether by court order, agreement or
 otherwise) that contradict the conditions of this License, they do not
 excuse you from the conditions of this License.  If you cannot
 distribute so as to satisfy simultaneously your obligations under this
 License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
 all those who receive copies directly or indirectly through you, then
 the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
+refrain entirely from distribution of the Program.
 
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
 
 It is not the purpose of this section to induce you to infringe any
 patents or other property right claims or to contest validity of any
 such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
+integrity of the free software distribution system, which is
 implemented by public license practices.  Many people have made
 generous contributions to the wide range of software distributed
 through that system in reliance on consistent application of that
@@ -380,103 +225,116 @@
 
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
+
+  8. If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
 
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Library General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
 
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
 
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
 			    NO WARRANTY
 
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
 
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
 
 		     END OF TERMS AND CONDITIONS
 
-           How to Apply These Terms to Your New Libraries
+	    How to Apply These Terms to Your New Programs
 
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
 
-  To apply these terms, attach the following notices to the library.  It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
 
-    <one line to give the library's name and a brief idea of what it does.>
+    <one line to give the program's name and a brief idea of what it does.>
     Copyright (C) <year>  <name of author>
 
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library 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 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 library is distributed in the hope that it will be useful,
+    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
-    Library General Public License for more details.
+    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 Library General Public
-    License along with this library; if not, write to the 
-    Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
-    Boston, MA  02111-1307  USA.
+    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
 
+
 Also add information on how to contact you by electronic and paper mail.
 
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
 You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
+school, if any, to sign a "copyright disclaimer" for the program, if
 necessary.  Here is a sample; alter the names:
 
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
 
-  <signature of Ty Coon>, 1 April 1990
+  <signature of Ty Coon>, 1 April 1989
   Ty Coon, President of Vice
 
-That's all there is to it!
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: configure.in
===================================================================
--- configure.in	(revision 409)
+++ configure.in	(working copy)
@@ -1104,7 +1104,7 @@
 	fi
 fi
 
-GLIB_REQUIRED="2.9.1"
+GLIB_REQUIRED="2.8.0"
 LIBBONOBO_REQUIRED="2.4.2"
 ORBIT_REQUIRED="2.9.8"
 
Index: calendar/libical/src/libicalss/icalssyacc.h
===================================================================
--- calendar/libical/src/libicalss/icalssyacc.h	(revision 409)
+++ calendar/libical/src/libicalss/icalssyacc.h	(working copy)
@@ -1,7 +1,7 @@
-/* A Bison parser, made by GNU Bison 1.875d.  */
+/* A Bison parser, made by GNU Bison 1.875a.  */
 
 /* Skeleton parser for Yacc-like parsing with Bison,
-   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, 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
@@ -78,7 +78,7 @@
 typedef union YYSTYPE {
 	char* v_string;
 } YYSTYPE;
-/* Line 1285 of yacc.c.  */
+/* Line 1240 of yacc.c.  */
 #line 83 "icalssyacc.h"
 # define yystype YYSTYPE /* obsolescent; will be withdrawn */
 # define YYSTYPE_IS_DECLARED 1
Index: calendar/libical/INSTALL
===================================================================
--- calendar/libical/INSTALL	(revision 409)
+++ calendar/libical/INSTALL	(working copy)
@@ -1,4 +1,4 @@
-Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
 Foundation, Inc.
 
    This file is free documentation; the Free Software Foundation gives
Index: calendar/libecal-dbus/e-cal-recur.c
===================================================================
--- calendar/libecal-dbus/e-cal-recur.c	(revision 409)
+++ calendar/libecal-dbus/e-cal-recur.c	(working copy)
@@ -1 +1,4067 @@
-link ../libecal/e-cal-recur.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Evolution calendar recurrence rule functions
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Author: Damon Chaplin <damon@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include "e-cal-recur.h"
+#include "e-cal-time-util.h"
+
+
+/*
+ * Introduction to The Recurrence Generation Functions:
+ *
+ * Note: This is pretty complicated. See the iCalendar spec (RFC 2445) for
+ *       the specification of the recurrence rules and lots of examples
+ *	 (sections 4.3.10 & 4.8.5). We also want to support the older
+ *	 vCalendar spec, though this should be easy since it is basically a
+ *	 subset of iCalendar.
+ *
+ * o An iCalendar event can have any number of recurrence rules specifying
+ *   occurrences of the event, as well as dates & times of specific
+ *   occurrences. It can also have any number of recurrence rules and
+ *   specific dates & times specifying exceptions to the occurrences.
+ *   So we first merge all the occurrences generated, eliminating any
+ *   duplicates, then we generate all the exceptions and remove these to
+ *   form the final set of occurrences.
+ *
+ * o There are 7 frequencies of occurrences: YEARLY, MONTHLY, WEEKLY, DAILY,
+ *   HOURLY, MINUTELY & SECONDLY. The 'interval' property specifies the
+ *   multiples of the frequency which we step by. We generate a 'set' of
+ *   occurrences for each period defined by the frequency & interval.
+ *   So for a YEARLY frequency with an interval of 3, we generate a set of
+ *   occurrences for every 3rd year. We use complete years here -  any
+ *   generated occurrences that occur before the event's start (or after its
+ *   end) are just discarded.
+ *
+ * o There are 8 frequency modifiers: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY,
+ *   BYDAY, BYHOUR, BYMINUTE & BYSECOND. These can either add extra occurrences
+ *   or filter out occurrences. For example 'FREQ=YEARLY;BYMONTH=1,2' produces
+ *   2 occurrences for each year rather than the default 1. And
+ *   'FREQ=DAILY;BYMONTH=1' filters out all occurrences except those in Jan.
+ *   If the modifier works on periods which are less than the recurrence
+ *   frequency, then extra occurrences are added, otherwise occurrences are
+ *   filtered. So we have 2 functions for each modifier - one to expand events
+ *   and the other to filter. We use a table of functions for each frequency
+ *   which points to the appropriate function to use for each modifier.
+ *
+ * o Any number of frequency modifiers can be used in a recurrence rule.
+ *   (Though the iCalendar spec says that BYWEEKNO can only be used in a YEARLY
+ *   rule, and some modifiers aren't appropriate for some frequencies - e.g.
+ *   BYMONTHDAY is not really useful in a WEEKLY frequency, and BYYEARDAY is
+ *   not useful in a MONTHLY or WEEKLY frequency).
+ *   The frequency modifiers are applied in the order given above. The first 5
+ *   modifier rules (BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY & BYDAY) all
+ *   produce the days on which the occurrences take place, and so we have to
+ *   compute some of these in parallel rather than sequentially, or we may end
+ *   up with too many days.
+ *
+ * o Note that some expansion functions may produce days which are invalid,
+ *   e.g. 31st September, 30th Feb. These invalid days are removed before the
+ *   BYHOUR, BYMINUTE & BYSECOND modifier functions are applied.
+ *
+ * o After the set of occurrences for the frequency interval are generated,
+ *   the BYSETPOS property is used to select which of the occurrences are
+ *   finally output. If BYSETPOS is not specified then all the occurrences are
+ *   output.
+ *
+ *
+ * FIXME: I think there are a few errors in this code:
+ *
+ *  1) I'm not sure it should be generating events in parallel like it says
+ *     above. That needs to be checked.
+ *
+ *  2) I didn't think about timezone changes when implementing this. I just
+ *     assumed all the occurrences of the event would be in local time.
+ *     But when clocks go back or forwards due to daylight-saving time, some
+ *     special handling may be needed, especially for the shorter frequencies.
+ *     e.g. for a MINUTELY frequency it should probably iterate over all the
+ *     minutes before and after clocks go back (i.e. some may be the same local
+ *     time but have different UTC offsets). For longer frequencies, if an
+ *     occurrence lands on the overlapping or non-existant time when clocks
+ *     go back/forward, then it may need to choose which of the times to use
+ *     or move the time forward or something. I'm not sure this is clear in the
+ *     spec.
+ */
+
+/* This is the maximum year we will go up to (inclusive). Since we use time_t
+   values we can't go past 2037 anyway, and some of our VTIMEZONEs may stop
+   at 2037 as well. */
+#define MAX_YEAR	2037
+
+/* Define this for some debugging output. */
+#if 0
+#define CAL_OBJ_DEBUG	1
+#endif
+
+/* We will use icalrecurrencetype instead of this eventually. */
+typedef struct {
+	icalrecurrencetype_frequency freq;
+
+	int            interval;
+
+	/* Specifies the end of the recurrence, inclusive. No occurrences are
+	   generated after this date. If it is 0, the event recurs forever. */
+	time_t         enddate;
+
+	/* WKST property - the week start day: 0 = Monday to 6 = Sunday. */
+	gint	       week_start_day;
+
+
+	/* NOTE: I've used GList's here, but it doesn't matter if we use
+	   other data structures like arrays. The code should be easy to
+	   change. So long as it is easy to see if the modifier is set. */
+
+	/* For BYMONTH modifier. A list of GINT_TO_POINTERs, 0-11. */
+	GList	      *bymonth;
+
+	/* For BYWEEKNO modifier. A list of GINT_TO_POINTERs, [+-]1-53. */
+	GList	      *byweekno;
+
+	/* For BYYEARDAY modifier. A list of GINT_TO_POINTERs, [+-]1-366. */
+	GList	      *byyearday;
+
+	/* For BYMONTHDAY modifier. A list of GINT_TO_POINTERs, [+-]1-31. */
+	GList	      *bymonthday;
+
+	/* For BYDAY modifier. A list of GINT_TO_POINTERs, in pairs.
+	   The first of each pair is the weekday, 0 = Monday to 6 = Sunday.
+	   The second of each pair is the week number [+-]0-53. */
+	GList	      *byday;
+
+	/* For BYHOUR modifier. A list of GINT_TO_POINTERs, 0-23. */
+	GList	      *byhour;
+
+	/* For BYMINUTE modifier. A list of GINT_TO_POINTERs, 0-59. */
+	GList	      *byminute;
+
+	/* For BYSECOND modifier. A list of GINT_TO_POINTERs, 0-60. */
+	GList	      *bysecond;
+
+	/* For BYSETPOS modifier. A list of GINT_TO_POINTERs, +ve or -ve. */
+	GList	      *bysetpos;
+} ECalRecurrence;
+
+/* This is what we use to pass to all the filter functions. */
+typedef struct _RecurData RecurData;
+struct _RecurData {
+	ECalRecurrence *recur;
+
+	/* This is used for the WEEKLY frequency. It is the offset from the
+	   week_start_day. */
+	gint weekday_offset;
+
+	/* This is used for fast lookup in BYMONTH filtering. */
+	guint8 months[12];
+
+	/* This is used for fast lookup in BYYEARDAY filtering. */
+	guint8 yeardays[367], neg_yeardays[367]; /* Days are 1 - 366. */
+
+	/* This is used for fast lookup in BYMONTHDAY filtering. */
+	guint8 monthdays[32], neg_monthdays[32]; /* Days are 1 to 31. */
+
+	/* This is used for fast lookup in BYDAY filtering. */
+	guint8 weekdays[7];
+
+	/* This is used for fast lookup in BYHOUR filtering. */
+	guint8 hours[24];
+
+	/* This is used for fast lookup in BYMINUTE filtering. */
+	guint8 minutes[60];
+
+	/* This is used for fast lookup in BYSECOND filtering. */
+	guint8 seconds[62];
+};
+
+/* This is what we use to represent a date & time. */
+typedef struct _CalObjTime CalObjTime;
+struct _CalObjTime {
+	guint16 year;
+	guint8 month;		/* 0 - 11 */
+	guint8 day;		/* 1 - 31 */
+	guint8 hour;		/* 0 - 23 */
+	guint8 minute;		/* 0 - 59 */
+	guint8 second;		/* 0 - 59 (maybe up to 61 for leap seconds) */
+	guint8 flags;		/* The meaning of this depends on where the
+				   CalObjTime is used. In most cases this is
+				   set to TRUE to indicate that this is an
+				   RDATE with an end or a duration set.
+				   In the exceptions code, this is set to TRUE
+				   to indicate that this is an EXDATE with a
+				   DATE value. */
+};
+
+/* This is what we use to represent specific recurrence dates.
+   Note that we assume it starts with a CalObjTime when sorting. */
+typedef struct _CalObjRecurrenceDate CalObjRecurrenceDate;
+struct _CalObjRecurrenceDate {
+	CalObjTime start;
+	ECalComponentPeriod *period;
+};
+
+/* The paramter we use to store the enddate in RRULE and EXRULE properties. */
+#define EVOLUTION_END_DATE_PARAMETER	"X-EVOLUTION-ENDDATE"
+
+typedef gboolean (*CalObjFindStartFn) (CalObjTime *event_start,
+				       CalObjTime *event_end,
+				       RecurData  *recur_data,
+				       CalObjTime *interval_start,
+				       CalObjTime *interval_end,
+				       CalObjTime *cotime);
+typedef gboolean (*CalObjFindNextFn)  (CalObjTime *cotime,
+				       CalObjTime *event_end,
+				       RecurData  *recur_data,
+				       CalObjTime *interval_end);
+typedef GArray*	 (*CalObjFilterFn)    (RecurData  *recur_data,
+				       GArray     *occs);
+
+typedef struct _ECalRecurVTable ECalRecurVTable;
+struct _ECalRecurVTable {
+	CalObjFindStartFn find_start_position;
+	CalObjFindNextFn find_next_position;
+
+	CalObjFilterFn bymonth_filter;
+	CalObjFilterFn byweekno_filter;
+	CalObjFilterFn byyearday_filter;
+	CalObjFilterFn bymonthday_filter;
+	CalObjFilterFn byday_filter;
+	CalObjFilterFn byhour_filter;
+	CalObjFilterFn byminute_filter;
+	CalObjFilterFn bysecond_filter;
+};
+
+
+/* This is used to specify which parts of the CalObjTime to compare in
+   cal_obj_time_compare(). */
+typedef enum {
+	CALOBJ_YEAR,
+	CALOBJ_MONTH,
+	CALOBJ_DAY,
+	CALOBJ_HOUR,
+	CALOBJ_MINUTE,
+	CALOBJ_SECOND
+} CalObjTimeComparison;
+
+static void e_cal_recur_generate_instances_of_rule (ECalComponent	*comp,
+						  icalproperty	*prop,
+						  time_t	 start,
+						  time_t	 end,
+						  ECalRecurInstanceFn cb,
+						  gpointer       cb_data,
+						  ECalRecurResolveTimezoneFn  tz_cb,
+						  gpointer	 tz_cb_data,
+						  icaltimezone	*default_timezone);
+
+static ECalRecurrence * e_cal_recur_from_icalproperty (icalproperty *prop,
+						    gboolean exception,
+						    icaltimezone *zone,
+						    gboolean convert_end_date);
+static gint e_cal_recur_ical_weekday_to_weekday	(enum icalrecurrencetype_weekday day);
+static void	e_cal_recur_free			(ECalRecurrence	*r);
+
+
+static gboolean cal_object_get_rdate_end	(CalObjTime	*occ,
+						 GArray		*rdate_periods);
+static void	cal_object_compute_duration	(CalObjTime	*start,
+						 CalObjTime	*end,
+						 gint		*days,
+						 gint		*seconds);
+
+static gboolean generate_instances_for_chunk	(ECalComponent		*comp,
+						 time_t			 comp_dtstart,
+						 icaltimezone		*zone,
+						 GSList			*rrules,
+						 GSList			*rdates,
+						 GSList			*exrules,
+						 GSList			*exdates,
+						 gboolean		 single_rule,
+						 CalObjTime		*event_start,
+						 time_t			 interval_start,
+						 CalObjTime		*chunk_start,
+						 CalObjTime		*chunk_end,
+						 gint			 duration_days,
+						 gint			 duration_seconds,
+						 gboolean		 convert_end_date,
+						 ECalRecurInstanceFn	 cb,
+						 gpointer		 cb_data);
+
+static GArray* cal_obj_expand_recurrence	(CalObjTime	  *event_start,
+						 icaltimezone	  *zone,
+						 ECalRecurrence	  *recur,
+						 CalObjTime	  *interval_start,
+						 CalObjTime	  *interval_end,
+						 gboolean	  *finished);
+
+static GArray*	cal_obj_generate_set_yearly	(RecurData	*recur_data,
+						 ECalRecurVTable *vtable,
+						 CalObjTime	*occ);
+static GArray*	cal_obj_generate_set_monthly	(RecurData	*recur_data,
+						 ECalRecurVTable *vtable,
+						 CalObjTime	*occ);
+static GArray*	cal_obj_generate_set_default	(RecurData	*recur_data,
+						 ECalRecurVTable *vtable,
+						 CalObjTime	*occ);
+
+
+static ECalRecurVTable* cal_obj_get_vtable	(icalrecurrencetype_frequency recur_type);
+static void	cal_obj_initialize_recur_data	(RecurData	*recur_data,
+						 ECalRecurrence	*recur,
+						 CalObjTime	*event_start);
+static void	cal_obj_sort_occurrences	(GArray		*occs);
+static gint	cal_obj_time_compare_func	(const void	*arg1,
+						 const void	*arg2);
+static void	cal_obj_remove_duplicates_and_invalid_dates (GArray	*occs);
+static void	cal_obj_remove_exceptions	(GArray		*occs,
+						 GArray		*ex_occs);
+static GArray*	cal_obj_bysetpos_filter		(ECalRecurrence	*recur,
+						 GArray		*occs);
+
+
+static gboolean cal_obj_yearly_find_start_position (CalObjTime *event_start,
+						    CalObjTime *event_end,
+						    RecurData  *recur_data,
+						    CalObjTime *interval_start,
+						    CalObjTime *interval_end,
+						    CalObjTime *cotime);
+static gboolean cal_obj_yearly_find_next_position  (CalObjTime *cotime,
+						    CalObjTime *event_end,
+						    RecurData  *recur_data,
+						    CalObjTime *interval_end);
+
+static gboolean cal_obj_monthly_find_start_position (CalObjTime *event_start,
+						     CalObjTime *event_end,
+						     RecurData  *recur_data,
+						     CalObjTime *interval_start,
+						     CalObjTime *interval_end,
+						     CalObjTime *cotime);
+static gboolean cal_obj_monthly_find_next_position  (CalObjTime *cotime,
+						     CalObjTime *event_end,
+						     RecurData  *recur_data,
+						     CalObjTime *interval_end);
+
+static gboolean cal_obj_weekly_find_start_position (CalObjTime *event_start,
+						    CalObjTime *event_end,
+						    RecurData  *recur_data,
+						    CalObjTime *interval_start,
+						    CalObjTime *interval_end,
+						    CalObjTime *cotime);
+static gboolean cal_obj_weekly_find_next_position  (CalObjTime *cotime,
+						    CalObjTime *event_end,
+						    RecurData  *recur_data,
+						    CalObjTime *interval_end);
+
+static gboolean cal_obj_daily_find_start_position  (CalObjTime *event_start,
+						    CalObjTime *event_end,
+						    RecurData  *recur_data,
+						    CalObjTime *interval_start,
+						    CalObjTime *interval_end,
+						    CalObjTime *cotime);
+static gboolean cal_obj_daily_find_next_position   (CalObjTime *cotime,
+						    CalObjTime *event_end,
+						    RecurData  *recur_data,
+						    CalObjTime *interval_end);
+
+static gboolean cal_obj_hourly_find_start_position (CalObjTime *event_start,
+						    CalObjTime *event_end,
+						    RecurData  *recur_data,
+						    CalObjTime *interval_start,
+						    CalObjTime *interval_end,
+						    CalObjTime *cotime);
+static gboolean cal_obj_hourly_find_next_position  (CalObjTime *cotime,
+						    CalObjTime *event_end,
+						    RecurData  *recur_data,
+						    CalObjTime *interval_end);
+
+static gboolean cal_obj_minutely_find_start_position (CalObjTime *event_start,
+						      CalObjTime *event_end,
+						      RecurData  *recur_data,
+						      CalObjTime *interval_start,
+						      CalObjTime *interval_end,
+						      CalObjTime *cotime);
+static gboolean cal_obj_minutely_find_next_position  (CalObjTime *cotime,
+						      CalObjTime *event_end,
+						      RecurData  *recur_data,
+						      CalObjTime *interval_end);
+
+static gboolean cal_obj_secondly_find_start_position (CalObjTime *event_start,
+						      CalObjTime *event_end,
+						      RecurData  *recur_data,
+						      CalObjTime *interval_start,
+						      CalObjTime *interval_end,
+						      CalObjTime *cotime);
+static gboolean cal_obj_secondly_find_next_position  (CalObjTime *cotime,
+						      CalObjTime *event_end,
+						      RecurData  *recur_data,
+						      CalObjTime *interval_end);
+
+static GArray* cal_obj_bymonth_expand		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_bymonth_filter		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byweekno_expand		(RecurData  *recur_data,
+						 GArray     *occs);
+#if 0
+/* This isn't used at present. */
+static GArray* cal_obj_byweekno_filter		(RecurData  *recur_data,
+						 GArray     *occs);
+#endif
+static GArray* cal_obj_byyearday_expand		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byyearday_filter		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_bymonthday_expand	(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_bymonthday_filter	(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byday_expand_yearly	(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byday_expand_monthly	(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byday_expand_weekly	(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byday_filter		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byhour_expand		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byhour_filter		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byminute_expand		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_byminute_filter		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_bysecond_expand		(RecurData  *recur_data,
+						 GArray     *occs);
+static GArray* cal_obj_bysecond_filter		(RecurData  *recur_data,
+						 GArray     *occs);
+
+static void cal_obj_time_add_months		(CalObjTime *cotime,
+						 gint	     months);
+static void cal_obj_time_add_days		(CalObjTime *cotime,
+						 gint	     days);
+static void cal_obj_time_add_hours		(CalObjTime *cotime,
+						 gint	     hours);
+static void cal_obj_time_add_minutes		(CalObjTime *cotime,
+						 gint	     minutes);
+static void cal_obj_time_add_seconds		(CalObjTime *cotime,
+						 gint	     seconds);
+static gint cal_obj_time_compare		(CalObjTime *cotime1,
+						 CalObjTime *cotime2,
+						 CalObjTimeComparison type);
+static gint cal_obj_time_weekday		(CalObjTime *cotime);
+static gint cal_obj_time_weekday_offset		(CalObjTime *cotime,
+						 ECalRecurrence *recur);
+static gint cal_obj_time_day_of_year		(CalObjTime *cotime);
+static void cal_obj_time_find_first_week	(CalObjTime *cotime,
+						 RecurData  *recur_data);
+static void cal_object_time_from_time		(CalObjTime *cotime,
+						 time_t      t,
+						 icaltimezone *zone);
+static gint cal_obj_date_only_compare_func	(const void *arg1,
+						 const void *arg2);
+
+
+
+static gboolean e_cal_recur_ensure_end_dates	(ECalComponent	*comp,
+						 gboolean	 refresh,
+						 ECalRecurResolveTimezoneFn tz_cb,
+						 gpointer	 tz_cb_data);
+static gboolean e_cal_recur_ensure_rule_end_date	(ECalComponent	*comp,
+						 icalproperty	*prop,
+						 gboolean	 exception,
+						 gboolean	 refresh,
+						 ECalRecurResolveTimezoneFn tz_cb,
+						 gpointer	 tz_cb_data);
+static gboolean e_cal_recur_ensure_rule_end_date_cb	(ECalComponent	*comp,
+							 time_t		 instance_start,
+							 time_t		 instance_end,
+							 gpointer	 data);
+static time_t e_cal_recur_get_rule_end_date	(icalproperty	*prop,
+						 icaltimezone	*default_timezone);
+static void e_cal_recur_set_rule_end_date		(icalproperty	*prop,
+						 time_t		 end_date);
+
+
+#ifdef CAL_OBJ_DEBUG
+static char* cal_obj_time_to_string		(CalObjTime	*cotime);
+#endif
+
+
+ECalRecurVTable cal_obj_yearly_vtable = {
+	cal_obj_yearly_find_start_position,
+	cal_obj_yearly_find_next_position,
+
+	cal_obj_bymonth_expand,
+	cal_obj_byweekno_expand,
+	cal_obj_byyearday_expand,
+	cal_obj_bymonthday_expand,
+	cal_obj_byday_expand_yearly,
+	cal_obj_byhour_expand,
+	cal_obj_byminute_expand,
+	cal_obj_bysecond_expand
+};
+
+ECalRecurVTable cal_obj_monthly_vtable = {
+	cal_obj_monthly_find_start_position,
+	cal_obj_monthly_find_next_position,
+
+	cal_obj_bymonth_filter,
+	NULL, /* BYWEEKNO is only applicable to YEARLY frequency. */
+	NULL, /* BYYEARDAY is not useful in a MONTHLY frequency. */
+	cal_obj_bymonthday_expand,
+	cal_obj_byday_expand_monthly,
+	cal_obj_byhour_expand,
+	cal_obj_byminute_expand,
+	cal_obj_bysecond_expand
+};
+
+ECalRecurVTable cal_obj_weekly_vtable = {
+	cal_obj_weekly_find_start_position,
+	cal_obj_weekly_find_next_position,
+
+	cal_obj_bymonth_filter,
+	NULL, /* BYWEEKNO is only applicable to YEARLY frequency. */
+	NULL, /* BYYEARDAY is not useful in a WEEKLY frequency. */
+	NULL, /* BYMONTHDAY is not useful in a WEEKLY frequency. */
+	cal_obj_byday_expand_weekly,
+	cal_obj_byhour_expand,
+	cal_obj_byminute_expand,
+	cal_obj_bysecond_expand
+};
+
+ECalRecurVTable cal_obj_daily_vtable = {
+	cal_obj_daily_find_start_position,
+	cal_obj_daily_find_next_position,
+
+	cal_obj_bymonth_filter,
+	NULL, /* BYWEEKNO is only applicable to YEARLY frequency. */
+	cal_obj_byyearday_filter,
+	cal_obj_bymonthday_filter,
+	cal_obj_byday_filter,
+	cal_obj_byhour_expand,
+	cal_obj_byminute_expand,
+	cal_obj_bysecond_expand
+};
+
+ECalRecurVTable cal_obj_hourly_vtable = {
+	cal_obj_hourly_find_start_position,
+	cal_obj_hourly_find_next_position,
+
+	cal_obj_bymonth_filter,
+	NULL, /* BYWEEKNO is only applicable to YEARLY frequency. */
+	cal_obj_byyearday_filter,
+	cal_obj_bymonthday_filter,
+	cal_obj_byday_filter,
+	cal_obj_byhour_filter,
+	cal_obj_byminute_expand,
+	cal_obj_bysecond_expand
+};
+
+ECalRecurVTable cal_obj_minutely_vtable = {
+	cal_obj_minutely_find_start_position,
+	cal_obj_minutely_find_next_position,
+
+	cal_obj_bymonth_filter,
+	NULL, /* BYWEEKNO is only applicable to YEARLY frequency. */
+	cal_obj_byyearday_filter,
+	cal_obj_bymonthday_filter,
+	cal_obj_byday_filter,
+	cal_obj_byhour_filter,
+	cal_obj_byminute_filter,
+	cal_obj_bysecond_expand
+};
+
+ECalRecurVTable cal_obj_secondly_vtable = {
+	cal_obj_secondly_find_start_position,
+	cal_obj_secondly_find_next_position,
+
+	cal_obj_bymonth_filter,
+	NULL, /* BYWEEKNO is only applicable to YEARLY frequency. */
+	cal_obj_byyearday_filter,
+	cal_obj_bymonthday_filter,
+	cal_obj_byday_filter,
+	cal_obj_byhour_filter,
+	cal_obj_byminute_filter,
+	cal_obj_bysecond_filter
+};
+
+/**
+ * e_cal_recur_generate_instances:
+ * @comp: A calendar component object.
+ * @start: Range start time.
+ * @end: Range end time.
+ * @cb: Callback function.
+ * @cb_data: Closure data for the callback function.
+ * @tz_cb: Callback for retrieving timezones.
+ * @tz_cb_data: Closure data for the timezone callback.
+ * @default_timezone: Default timezone to use when a timezone cannot be
+ * found.
+ *
+ * Calls the given callback function for each occurrence of the event that
+ * intersects the range between the given @start and @end times (the end time is
+ * not included). Note that the occurrences may start before the given start
+ * time.
+ *
+ * If the callback routine returns FALSE the occurrence generation stops.
+ *
+ * Both start and end can be -1, in which case we start at the events first
+ * instance and continue until it ends, or forever if it has no enddate.
+ *
+ * The tz_cb is used to resolve references to timezones. It is passed a TZID
+ * and should return the icaltimezone* corresponding to that TZID. We need to
+ * do this as we access timezones in different ways on the client & server.
+ *
+ * The default_timezone argument is used for DTSTART or DTEND properties that
+ * are DATE values or do not have a TZID (i.e. floating times).
+ */
+void
+e_cal_recur_generate_instances (ECalComponent		*comp,
+			      time_t			 start,
+			      time_t			 end,
+			      ECalRecurInstanceFn	 cb,
+			      gpointer                   cb_data,
+			      ECalRecurResolveTimezoneFn  tz_cb,
+			      gpointer			 tz_cb_data,
+			      icaltimezone		*default_timezone)
+{
+#if 0
+	g_print ("In e_cal_recur_generate_instances comp: %p\n", comp);
+	g_print ("  start: %li - %s", start, ctime (&start));
+	g_print ("  end  : %li - %s", end, ctime (&end));
+#endif
+	e_cal_recur_generate_instances_of_rule (comp, NULL, start, end,
+						cb, cb_data, tz_cb, tz_cb_data,
+						default_timezone);
+}
+
+
+/*
+ * Calls the given callback function for each occurrence of the given
+ * recurrence rule between the given start and end times. If the rule is NULL
+ * it uses all the rules from the component.
+ *
+ * If the callback routine returns FALSE the occurrence generation stops.
+ *
+ * The use of the specific rule is for determining the end of a rule when
+ * COUNT is set. The callback will count instances and store the enddate
+ * when COUNT is reached.
+ *
+ * Both start and end can be -1, in which case we start at the events first
+ * instance and continue until it ends, or forever if it has no enddate.
+ */
+static void
+e_cal_recur_generate_instances_of_rule (ECalComponent	 *comp,
+					icalproperty	 *prop,
+					time_t		  start,
+					time_t		  end,
+					ECalRecurInstanceFn  cb,
+					gpointer            cb_data,
+					ECalRecurResolveTimezoneFn  tz_cb,
+					gpointer	            tz_cb_data,
+					icaltimezone	 *default_timezone)
+{
+	ECalComponentDateTime dtstart, dtend;
+	time_t dtstart_time, dtend_time;
+	GSList *rrules = NULL, *rdates = NULL, elem;
+	GSList *exrules = NULL, *exdates = NULL;
+	CalObjTime interval_start, interval_end, event_start, event_end;
+	CalObjTime chunk_start, chunk_end;
+	gint days, seconds, year;
+	gboolean single_rule, convert_end_date = FALSE;
+	icaltimezone *start_zone = NULL, *end_zone = NULL;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (cb != NULL);
+	g_return_if_fail (tz_cb != NULL);
+	g_return_if_fail (start >= -1);
+	g_return_if_fail (end >= -1);
+
+	/* Get dtstart, dtend, recurrences, and exceptions. Note that
+	   cal_component_get_dtend() will convert a DURATION property to a
+	   DTEND so we don't need to worry about that. */
+
+	e_cal_component_get_dtstart (comp, &dtstart);
+	e_cal_component_get_dtend (comp, &dtend);
+
+	if (!dtstart.value) {
+		g_message ("e_cal_recur_generate_instances_of_rule(): bogus "
+			   "component, does not have DTSTART.  Skipping...");
+		goto out;
+	}
+
+	/* For DATE-TIME values with a TZID, we use the supplied callback to
+	   resolve the TZID. For DATE values and DATE-TIME values without a
+	   TZID (i.e. floating times) we use the default timezone. */
+	if (dtstart.tzid && !dtstart.value->is_date) {
+		start_zone = (*tz_cb) (dtstart.tzid, tz_cb_data);
+		if (!start_zone)
+			start_zone = default_timezone;
+	} else {
+		start_zone = default_timezone;
+
+		/* Flag that we need to convert the saved ENDDATE property
+		   to the default timezone. */
+		convert_end_date = TRUE;
+	}
+
+	dtstart_time = icaltime_as_timet_with_zone (*dtstart.value,
+						    start_zone);
+	if (start == -1)
+		start = dtstart_time;
+
+	if (dtend.value) {
+		/* If both DTSTART and DTEND are DATE values, and they are the
+		   same day, we add 1 day to DTEND. This means that most
+		   events created with the old Evolution behavior will still
+		   work OK. I'm not sure what Outlook does in this case. */
+		if (dtstart.value->is_date && dtend.value->is_date) {
+			if (icaltime_compare_date_only (*dtstart.value,
+							*dtend.value) == 0) {
+				icaltime_adjust (dtend.value, 1, 0, 0, 0);
+			}
+		}
+	} else {
+		/* If there is no DTEND, then if DTSTART is a DATE-TIME value
+		   we use the same time (so we have a single point in time).
+		   If DTSTART is a DATE value we add 1 day. */
+		dtend.value = g_new (struct icaltimetype, 1);
+		*dtend.value = *dtstart.value;
+
+		if (dtstart.value->is_date) {
+			icaltime_adjust (dtend.value, 1, 0, 0, 0);
+		}
+	}
+
+	if (dtend.tzid && !dtend.value->is_date) {
+		end_zone = (*tz_cb) (dtend.tzid, tz_cb_data);
+		if (!end_zone)
+			end_zone = default_timezone;
+	} else {
+		end_zone = default_timezone;
+	}
+
+	/* We don't do this any more, since Outlook assumes that the DTEND
+	   date is not included. */
+#if 0
+	/* If DTEND is a DATE value, we add 1 day to it so that it includes
+	   the entire day. */
+	if (dtend.value->is_date) {
+		dtend.value->hour = 0;
+		dtend.value->minute = 0;
+		dtend.value->second = 0;
+		icaltime_adjust (dtend.value, 1, 0, 0, 0);
+	}
+#endif
+	dtend_time = icaltime_as_timet_with_zone (*dtend.value, end_zone);
+
+	/* If there is no recurrence, just call the callback if the event
+	   intersects the given interval. */
+	if (!(e_cal_component_has_recurrences (comp)
+	      || e_cal_component_has_exceptions (comp))) {
+		if ((end == -1 || dtstart_time < end) && dtend_time > start) {
+			(* cb) (comp, dtstart_time, dtend_time, cb_data);
+		}
+
+		goto out;
+	}
+
+	/* If a specific recurrence rule is being used, set up a simple list,
+	   else get the recurrence rules from the component. */
+	if (prop) {
+		single_rule = TRUE;
+
+		elem.data = prop;
+		elem.next = NULL;
+		rrules = &elem;
+	} else if (e_cal_component_is_instance (comp)) {
+		single_rule = FALSE;
+	} else {
+		single_rule = FALSE;
+
+		/* Make sure all the enddates for the rules are set. */
+		e_cal_recur_ensure_end_dates (comp, FALSE, tz_cb, tz_cb_data);
+
+		e_cal_component_get_rrule_property_list (comp, &rrules);
+		e_cal_component_get_rdate_list (comp, &rdates);
+		e_cal_component_get_exrule_property_list (comp, &exrules);
+		e_cal_component_get_exdate_list (comp, &exdates);
+	}
+
+	/* Convert the interval start & end to CalObjTime. Note that if end
+	   is -1 interval_end won't be set, so don't use it!
+	   Also note that we use end - 1 since we want the interval to be
+	   inclusive as it makes the code simpler. We do all calculation
+	   in the timezone of the DTSTART. */
+	cal_object_time_from_time (&interval_start, start, start_zone);
+	if (end != -1)
+		cal_object_time_from_time (&interval_end, end - 1, start_zone);
+
+	cal_object_time_from_time (&event_start, dtstart_time, start_zone);
+	cal_object_time_from_time (&event_end, dtend_time, start_zone);
+	
+	/* Calculate the duration of the event, which we use for all
+	   occurrences. We can't just subtract start from end since that may
+	   be affected by daylight-saving time. So we want a value of days
+	   + seconds. */
+	cal_object_compute_duration (&event_start, &event_end,
+				     &days, &seconds);
+
+	/* Take off the duration from interval_start, so we get occurrences
+	   that start just before the start time but overlap it. But only do
+	   that if the interval is after the event's start time. */
+	if (start > dtstart_time) {
+		cal_obj_time_add_days (&interval_start, -days);
+		cal_obj_time_add_seconds (&interval_start, -seconds);
+	}
+
+	/* Expand the recurrence for each year between start & end, or until
+	   the callback returns 0 if end is 0. We do a year at a time to
+	   give the callback function a chance to break out of the loop, and
+	   so we don't get into problems with infinite recurrences. Since we
+	   have to work on complete sets of occurrences, if there is a yearly
+	   frequency it wouldn't make sense to break it into smaller chunks,
+	   since we would then be calculating the same sets several times.
+	   Though this does mean that we sometimes do a lot more work than
+	   is necessary, e.g. if COUNT is set to something quite low. */
+	for (year = interval_start.year;
+	     (end == -1 || year <= interval_end.year) && year <= MAX_YEAR;
+	     year++) {
+		chunk_start = interval_start;
+		chunk_start.year = year;
+		if (end != -1)
+			chunk_end = interval_end;
+		chunk_end.year = year;
+
+		if (year != interval_start.year) {
+			chunk_start.month  = 0;
+			chunk_start.day    = 1;
+			chunk_start.hour   = 0;
+			chunk_start.minute = 0;
+			chunk_start.second = 0;
+		}
+		if (end == -1 || year != interval_end.year) {
+			chunk_end.month  = 11;
+			chunk_end.day    = 31;
+			chunk_end.hour   = 23;
+			chunk_end.minute = 59;
+			chunk_end.second = 61;
+			chunk_end.flags  = FALSE;
+		}
+
+		if (!generate_instances_for_chunk (comp, dtstart_time,
+						   start_zone,
+						   rrules, rdates,
+						   exrules, exdates,
+						   single_rule,
+						   &event_start,
+						   start,
+						   &chunk_start, &chunk_end,
+						   days, seconds,
+						   convert_end_date,
+						   cb, cb_data))
+			break;
+	}
+
+	if (!prop) {
+		e_cal_component_free_period_list (rdates);
+		e_cal_component_free_exdate_list (exdates);
+	}
+
+ out:
+	e_cal_component_free_datetime (&dtstart);
+	e_cal_component_free_datetime (&dtend);
+}
+
+/* Builds a list of GINT_TO_POINTER() elements out of a short array from a
+ * struct icalrecurrencetype.
+ */
+static GList *
+array_to_list (short *array, int max_elements)
+{
+	GList *l;
+	int i;
+
+	l = NULL;
+
+	for (i = 0; i < max_elements && array[i] != ICAL_RECURRENCE_ARRAY_MAX; i++)
+		l = g_list_prepend (l, GINT_TO_POINTER ((int) (array[i])));
+	return g_list_reverse (l);
+}
+
+/**
+ * e_cal_recur_from_icalproperty:
+ * @prop: An RRULE or EXRULE #icalproperty.
+ * @exception: TRUE if this is an EXRULE rather than an RRULE.
+ * @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.
+ * 
+ * Converts an #icalproperty to a #ECalRecurrence.  This should be
+ * freed using the e_cal_recur_free() function.
+ * 
+ * Return value: #ECalRecurrence structure.
+ **/
+static ECalRecurrence *
+e_cal_recur_from_icalproperty (icalproperty *prop, gboolean exception,
+			     icaltimezone *zone, gboolean convert_end_date)
+{
+	struct icalrecurrencetype ir;
+	ECalRecurrence *r;
+	gint max_elements, i;
+	GList *elem;
+
+	g_return_val_if_fail (prop != NULL, NULL);
+
+	r = g_new (ECalRecurrence, 1);
+
+	if (exception)
+		ir = icalproperty_get_exrule (prop);
+	else
+		ir = icalproperty_get_rrule (prop);
+
+	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->week_start_day = e_cal_recur_ical_weekday_to_weekday (ir.week_start);
+
+	r->bymonth = array_to_list (ir.by_month,
+				    sizeof (ir.by_month) / sizeof (ir.by_month[0]));
+	for (elem = r->bymonth; elem; elem = elem->next) {
+		/* We need to convert from 1-12 to 0-11, i.e. subtract 1. */
+		int month = GPOINTER_TO_INT (elem->data) - 1;
+		elem->data = GINT_TO_POINTER (month);
+	}
+
+	r->byweekno = array_to_list (ir.by_week_no,
+				     sizeof (ir.by_week_no) / sizeof (ir.by_week_no[0]));
+
+	r->byyearday = array_to_list (ir.by_year_day,
+				      sizeof (ir.by_year_day) / sizeof (ir.by_year_day[0]));
+
+	r->bymonthday = array_to_list (ir.by_month_day,
+				       sizeof (ir.by_month_day) / sizeof (ir.by_month_day[0]));
+
+	/* FIXME: libical only supports 8 values, out of possible 107 * 7. */
+	r->byday = NULL;
+	max_elements = sizeof (ir.by_day) / sizeof (ir.by_day[0]);
+	for (i = 0; i < max_elements && ir.by_day[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
+		enum icalrecurrencetype_weekday day;
+		gint weeknum, weekday;
+
+		day = icalrecurrencetype_day_day_of_week (ir.by_day[i]);
+		weeknum = icalrecurrencetype_day_position (ir.by_day[i]);
+
+		weekday = e_cal_recur_ical_weekday_to_weekday (day);
+
+		r->byday = g_list_prepend (r->byday,
+					   GINT_TO_POINTER (weeknum));
+		r->byday = g_list_prepend (r->byday,
+					   GINT_TO_POINTER (weekday));
+	}
+
+	r->byhour = array_to_list (ir.by_hour,
+				   sizeof (ir.by_hour) / sizeof (ir.by_hour[0]));
+
+	r->byminute = array_to_list (ir.by_minute,
+				     sizeof (ir.by_minute) / sizeof (ir.by_minute[0]));
+
+	r->bysecond = array_to_list (ir.by_second,
+				     sizeof (ir.by_second) / sizeof (ir.by_second[0]));
+
+	r->bysetpos = array_to_list (ir.by_set_pos,
+				     sizeof (ir.by_set_pos) / sizeof (ir.by_set_pos[0]));
+
+	return r;
+}
+
+
+static gint
+e_cal_recur_ical_weekday_to_weekday	(enum icalrecurrencetype_weekday day)
+{
+	gint weekday;
+
+	switch (day) {
+	case ICAL_NO_WEEKDAY:		/* Monday is the default in RFC2445. */
+	case ICAL_MONDAY_WEEKDAY:
+		weekday = 0;
+		break;
+	case ICAL_TUESDAY_WEEKDAY:
+		weekday = 1;
+		break;
+	case ICAL_WEDNESDAY_WEEKDAY:
+		weekday = 2;
+		break;
+	case ICAL_THURSDAY_WEEKDAY:
+		weekday = 3;
+		break;
+	case ICAL_FRIDAY_WEEKDAY:
+		weekday = 4;
+		break;
+	case ICAL_SATURDAY_WEEKDAY:
+		weekday = 5;
+		break;
+	case ICAL_SUNDAY_WEEKDAY:
+		weekday = 6;
+		break;
+	default:
+		g_warning ("e_cal_recur_ical_weekday_to_weekday(): Unknown week day %d",
+			   day);
+		weekday = 0;
+	}
+
+	return weekday;
+}
+
+
+/**
+ * e_cal_recur_free:
+ * @r: A #ECalRecurrence structure.
+ * 
+ * Frees a #ECalRecurrence structure.
+ **/
+static void
+e_cal_recur_free (ECalRecurrence *r)
+{
+	g_return_if_fail (r != NULL);
+
+	g_list_free (r->bymonth);
+	g_list_free (r->byweekno);
+	g_list_free (r->byyearday);
+	g_list_free (r->bymonthday);
+	g_list_free (r->byday);
+	g_list_free (r->byhour);
+	g_list_free (r->byminute);
+	g_list_free (r->bysecond);
+	g_list_free (r->bysetpos);
+
+	g_free (r);
+}
+
+/* Generates one year's worth of recurrence instances.  Returns TRUE if all the
+ * callback invocations returned TRUE, or FALSE when any one of them returns
+ * FALSE, i.e. meaning that the instance generation should be stopped.
+ *
+ * This should only output instances whose start time is between chunk_start
+ * and chunk_end (inclusive), or we may generate duplicates when we do the next
+ * chunk. (This applies mainly to weekly recurrences, since weeks can span 2
+ * years.)
+ *
+ * It should also only output instances that are on or after the event's
+ * DTSTART property and that intersect the required interval, between
+ * interval_start and interval_end.
+ */
+static gboolean
+generate_instances_for_chunk (ECalComponent	*comp,
+			      time_t             comp_dtstart,
+			      icaltimezone	*zone,
+			      GSList		*rrules,
+			      GSList		*rdates,
+			      GSList		*exrules,
+			      GSList		*exdates,
+			      gboolean		 single_rule,
+			      CalObjTime	*event_start,
+			      time_t		 interval_start,
+			      CalObjTime	*chunk_start,
+			      CalObjTime	*chunk_end,
+			      gint		 duration_days,
+			      gint		 duration_seconds,
+			      gboolean		 convert_end_date,
+			      ECalRecurInstanceFn cb,
+			      gpointer           cb_data)
+{
+	GArray *occs, *ex_occs, *tmp_occs, *rdate_periods;
+	CalObjTime cotime, *occ;
+	GSList *elem;
+	gint i;
+	time_t start_time, end_time;
+	struct icaltimetype start_tt, end_tt;
+	gboolean cb_status = TRUE, rule_finished, finished = TRUE;
+
+#if 0
+	g_print ("In generate_instances_for_chunk rrules: %p\n"
+		 "  %i/%i/%i %02i:%02i:%02i - %i/%i/%i %02i:%02i:%02i\n",
+		 rrules,
+		 chunk_start->day, chunk_start->month + 1,
+		 chunk_start->year, chunk_start->hour,
+		 chunk_start->minute, chunk_start->second,
+		 chunk_end->day, chunk_end->month + 1,
+		 chunk_end->year, chunk_end->hour,
+		 chunk_end->minute, chunk_end->second);
+#endif
+
+	occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+	ex_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+	rdate_periods = g_array_new (FALSE, FALSE,
+				     sizeof (CalObjRecurrenceDate));
+
+	/* The original DTSTART property is included in the occurrence set,
+	   but not if we are just generating occurrences for a single rule. */
+	if (!single_rule) {
+		/* We add it if it is in this chunk. If it is after this chunk
+		   we set finished to FALSE, since we know we aren't finished
+		   yet. */
+		if (cal_obj_time_compare_func (event_start, chunk_end) >= 0)
+			finished = FALSE;
+		else if (cal_obj_time_compare_func (event_start, chunk_start) >= 0)
+			g_array_append_vals (occs, event_start, 1);
+	}
+	
+	/* Expand each of the recurrence rules. */
+	for (elem = rrules; elem; elem = elem->next) {
+		icalproperty *prop;
+		ECalRecurrence *r;
+
+		prop = elem->data;
+		r = e_cal_recur_from_icalproperty (prop, FALSE, zone,
+						 convert_end_date);
+
+		tmp_occs = cal_obj_expand_recurrence (event_start, zone, r,
+						      chunk_start,
+						      chunk_end,
+						      &rule_finished);
+		e_cal_recur_free (r);
+
+		/* If any of the rules return FALSE for finished, we know we
+		   have to carry on so we set finished to FALSE. */
+		if (!rule_finished)
+			finished = FALSE;
+
+		g_array_append_vals (occs, tmp_occs->data, tmp_occs->len);
+		g_array_free (tmp_occs, TRUE);
+	}
+
+	/* Add on specific RDATE occurrence dates. If they have an end time
+	   or duration set, flag them as RDATEs, and store a pointer to the
+	   period in the rdate_periods array. Otherwise we can just treat them
+	   as normal occurrences. */
+	for (elem = rdates; elem; elem = elem->next) {
+		ECalComponentPeriod *p;
+		CalObjRecurrenceDate rdate;
+
+		p = elem->data;
+
+		/* 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. */
+		cotime.year     = p->start.year;
+		cotime.month    = p->start.month - 1;
+		cotime.day      = p->start.day;
+		cotime.hour     = p->start.hour;
+		cotime.minute   = p->start.minute;
+		cotime.second   = p->start.second;
+		cotime.flags    = FALSE;
+
+		/* If the rdate is after the current chunk we set finished
+		   to FALSE, and we skip it. */
+		if (cal_obj_time_compare_func (&cotime, chunk_end) >= 0) {
+			finished = FALSE;
+			continue;
+		}
+
+		/* Check if the end date or duration is set. If it is we need
+		   to store it so we can get it later. (libical seems to set
+		   second to -1 to denote an unset time. See icalvalue.c)
+		   FIXME. */
+		if (p->type != E_CAL_COMPONENT_PERIOD_DATETIME
+		    || p->u.end.second != -1) {
+			cotime.flags = TRUE;
+
+			rdate.start = cotime;
+			rdate.period = p;
+			g_array_append_val (rdate_periods, rdate);
+		}
+
+		g_array_append_val (occs, cotime);
+	}
+
+	/* Expand each of the exception rules. */
+	for (elem = exrules; elem; elem = elem->next) {
+		icalproperty *prop;
+		ECalRecurrence *r;
+
+		prop = elem->data;
+		r = e_cal_recur_from_icalproperty (prop, FALSE, zone,
+						 convert_end_date);
+
+		tmp_occs = cal_obj_expand_recurrence (event_start, zone, r,
+						      chunk_start,
+						      chunk_end,
+						      &rule_finished);
+		e_cal_recur_free (r);
+
+		g_array_append_vals (ex_occs, tmp_occs->data, tmp_occs->len);
+		g_array_free (tmp_occs, TRUE);
+	}
+
+	/* Add on specific exception dates. */
+	for (elem = exdates; elem; elem = elem->next) {
+		ECalComponentDateTime *cdt;
+
+		cdt = elem->data;
+
+		/* FIXME: We currently assume EXDATEs are in the same timezone
+		   as DTSTART. We should get the EXDATE timezone and convert
+		   to the DTSTART timezone first. */
+		cotime.year     = cdt->value->year;
+		cotime.month    = cdt->value->month - 1;
+		cotime.day      = cdt->value->day;
+
+		/* If the EXDATE has a DATE value, set the time to the start
+		   of the day and set flags to TRUE so we know to skip all
+		   occurrences on that date. */
+		if (cdt->value->is_date) {
+			cotime.hour     = 0;
+			cotime.minute   = 0;
+			cotime.second   = 0;
+			cotime.flags    = TRUE;
+		} else {
+			cotime.hour     = cdt->value->hour;
+			cotime.minute   = cdt->value->minute;
+			cotime.second   = cdt->value->second;
+			cotime.flags    = FALSE;
+		}
+
+		g_array_append_val (ex_occs, cotime);
+	}
+
+
+	/* Sort all the arrays. */
+	cal_obj_sort_occurrences (occs);
+	cal_obj_sort_occurrences (ex_occs);
+
+	qsort (rdate_periods->data, rdate_periods->len,
+	       sizeof (CalObjRecurrenceDate), cal_obj_time_compare_func);
+
+	/* Create the final array, by removing the exceptions from the
+	   occurrences, and removing any duplicates. */
+	cal_obj_remove_exceptions (occs, ex_occs);
+
+	/* Call the callback for each occurrence. If it returns 0 we break
+	   out of the loop. */
+	for (i = 0; i < occs->len; i++) {
+		/* Convert each CalObjTime into a start & end time_t, and
+		   check it is within the bounds of the event & interval. */
+		occ = &g_array_index (occs, CalObjTime, i);
+#if 0
+		g_print ("Checking occurrence: %s\n",
+			 cal_obj_time_to_string (occ));
+#endif
+		start_tt = icaltime_null_time ();
+		start_tt.year   = occ->year;
+		start_tt.month  = occ->month + 1;
+		start_tt.day    = occ->day;
+		start_tt.hour   = occ->hour;
+		start_tt.minute = occ->minute;
+		start_tt.second = occ->second;
+		start_time = icaltime_as_timet_with_zone (start_tt, zone);
+
+		if (start_time == -1) {
+			g_warning ("time_t out of range");
+			finished = TRUE;
+			break;
+		}
+
+		/* Check to ensure that the start time is at or after the
+		   event's DTSTART time, and that it is inside the chunk that
+		   we are currently working on. (Note that the chunk_end time
+		   is never after the interval end time, so this also tests
+		   that we don't go past the end of the required interval). */
+		if (start_time < comp_dtstart
+		    || cal_obj_time_compare_func (occ, chunk_start) < 0
+		    || cal_obj_time_compare_func (occ, chunk_end) > 0) {
+#if 0
+			g_print ("  start time invalid\n");
+#endif
+			continue;
+		}
+
+		if (occ->flags) {
+			/* If it is an RDATE, we see if the end date or
+			   duration was set. If not, we use the same duration
+			   as the original occurrence. */
+			if (!cal_object_get_rdate_end (occ, rdate_periods)) {
+				cal_obj_time_add_days (occ, duration_days);
+				cal_obj_time_add_seconds (occ,
+							  duration_seconds);
+			}
+		} else {
+			cal_obj_time_add_days (occ, duration_days);
+			cal_obj_time_add_seconds (occ, duration_seconds);
+		}
+
+		end_tt = icaltime_null_time ();
+		end_tt.year   = occ->year;
+		end_tt.month  = occ->month + 1;
+		end_tt.day    = occ->day;
+		end_tt.hour   = occ->hour;
+		end_tt.minute = occ->minute;
+		end_tt.second = occ->second;
+		end_time = icaltime_as_timet_with_zone (end_tt, zone);
+
+		if (end_time == -1) {
+			g_warning ("time_t out of range");
+			finished = TRUE;
+			break;
+		}
+
+		/* Check that the end time is after the interval start, so we
+		   know that it intersects the required interval. */
+		if (end_time <= interval_start) {
+#if 0
+			g_print ("  end time invalid\n");
+#endif
+			continue;
+		}
+
+		cb_status = (*cb) (comp, start_time, end_time, cb_data);
+		if (!cb_status)
+			break;
+	}
+
+	g_array_free (occs, TRUE);
+	g_array_free (ex_occs, TRUE);
+	g_array_free (rdate_periods, TRUE);
+
+	/* We return TRUE (i.e. carry on) only if the callback has always
+	   returned TRUE and we know that we have more occurrences to generate
+	   (i.e. finished is FALSE). */
+	return cb_status && !finished;
+}
+
+
+/* This looks up the occurrence time in the sorted rdate_periods array, and
+   tries to compute the end time of the occurrence. If no end time or duration
+   is set it returns FALSE and the default duration will be used. */
+static gboolean
+cal_object_get_rdate_end	(CalObjTime	*occ,
+				 GArray		*rdate_periods)
+{
+	CalObjRecurrenceDate *rdate = NULL;
+	ECalComponentPeriod *p;
+	gint lower, upper, middle, cmp = 0;
+
+	lower = 0;
+	upper = rdate_periods->len;
+
+	while (lower < upper) {
+		middle = (lower + upper) >> 1;
+	  
+		rdate = &g_array_index (rdate_periods, CalObjRecurrenceDate,
+					middle);
+
+		cmp = cal_obj_time_compare_func (occ, &rdate->start);
+	  
+		if (cmp == 0)
+			break;
+		else if (cmp < 0)
+			upper = middle;
+		else
+			lower = middle + 1;
+	}
+
+	/* This should never happen. */
+	if (cmp == 0) {
+		g_warning ("Recurrence date not found");
+		return FALSE;
+	}
+
+	p = rdate->period;
+	if (p->type == E_CAL_COMPONENT_PERIOD_DATETIME) {
+		/* 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. */
+		occ->year     = p->u.end.year;
+		occ->month    = p->u.end.month - 1;
+		occ->day      = p->u.end.day;
+		occ->hour     = p->u.end.hour;
+		occ->minute   = p->u.end.minute;
+		occ->second   = p->u.end.second;
+		occ->flags    = FALSE;
+	} else {
+		cal_obj_time_add_days (occ, p->u.duration.weeks * 7
+				       + p->u.duration.days);
+		cal_obj_time_add_hours (occ, p->u.duration.hours);
+		cal_obj_time_add_minutes (occ, p->u.duration.minutes);
+		cal_obj_time_add_seconds (occ, p->u.duration.seconds);
+	}
+
+	return TRUE;
+}
+
+
+static void
+cal_object_compute_duration (CalObjTime *start,
+			     CalObjTime *end,
+			     gint	*days,
+			     gint	*seconds)
+{
+	GDate start_date, end_date;
+	gint start_seconds, end_seconds;
+
+	g_date_clear (&start_date, 1);
+	g_date_clear (&end_date, 1);
+	g_date_set_dmy (&start_date, start->day, start->month + 1,
+			start->year);
+	g_date_set_dmy (&end_date, end->day, end->month + 1,
+			end->year);
+
+	*days = g_date_get_julian (&end_date) - g_date_get_julian (&start_date);
+	start_seconds = start->hour * 3600 + start->minute * 60
+		+ start->second;
+	end_seconds = end->hour * 3600 + end->minute * 60 + end->second;
+
+	*seconds = end_seconds - start_seconds;
+	if (*seconds < 0) {
+		*days = *days - 1;
+		*seconds += 24 * 60 * 60;
+	}
+}
+
+
+/* Returns an unsorted GArray of CalObjTime's resulting from expanding the
+   given recurrence rule within the given interval. Note that it doesn't
+   clip the generated occurrences to the interval, i.e. if the interval
+   starts part way through the year this function still returns all the
+   occurrences for the year. Clipping is done later.
+   The finished flag is set to FALSE if there are more occurrences to generate
+   after the given interval.*/
+static GArray*
+cal_obj_expand_recurrence		(CalObjTime	  *event_start,
+					 icaltimezone	  *zone,
+					 ECalRecurrence	  *recur,
+					 CalObjTime	  *interval_start,
+					 CalObjTime	  *interval_end,
+					 gboolean	  *finished)
+{
+	ECalRecurVTable *vtable;
+	CalObjTime *event_end = NULL, event_end_cotime;
+	RecurData recur_data;
+	CalObjTime occ, *cotime;
+	GArray *all_occs, *occs;
+	gint len;
+
+	/* This is the resulting array of CalObjTime elements. */
+	all_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	*finished = TRUE;
+
+	vtable = cal_obj_get_vtable (recur->freq);
+	if (!vtable)
+		return all_occs;
+
+	/* Calculate some useful data such as some fast lookup tables. */
+	cal_obj_initialize_recur_data (&recur_data, recur, event_start);
+
+	/* Compute the event_end, if the recur's enddate is set. */
+	if (recur->enddate > 0) {
+		cal_object_time_from_time (&event_end_cotime,
+					   recur->enddate, zone);
+		event_end = &event_end_cotime;
+
+		/* If the enddate is before the requested interval return. */
+		if (cal_obj_time_compare_func (event_end, interval_start) < 0)
+			return all_occs;
+	}
+
+	/* Set finished to FALSE if we know there will be more occurrences to
+	   do after this interval. */
+	if (!interval_end || !event_end
+	    || cal_obj_time_compare_func (event_end, interval_end) > 0)
+		*finished = FALSE;
+
+	/* Get the first period based on the frequency and the interval that
+	   intersects the interval between start and end. */
+	if ((*vtable->find_start_position) (event_start, event_end,
+					    &recur_data,
+					    interval_start, interval_end,
+					    &occ))
+		return all_occs;
+
+	/* Loop until the event ends or we go past the end of the required
+	   interval. */
+	for (;;) {
+		/* Generate the set of occurrences for this period. */
+		switch (recur->freq) {
+		case ICAL_YEARLY_RECURRENCE:
+			occs = cal_obj_generate_set_yearly (&recur_data,
+							    vtable, &occ);
+			break;
+		case ICAL_MONTHLY_RECURRENCE:
+			occs = cal_obj_generate_set_monthly (&recur_data,
+							     vtable, &occ);
+			break;
+		default:
+			occs = cal_obj_generate_set_default (&recur_data,
+							     vtable, &occ);
+			break;
+		}
+
+		/* Sort the occurrences and remove duplicates. */
+		cal_obj_sort_occurrences (occs);
+		cal_obj_remove_duplicates_and_invalid_dates (occs);
+
+		/* Apply the BYSETPOS property. */
+		occs = cal_obj_bysetpos_filter (recur, occs);
+
+		/* Remove any occs after event_end. */
+		len = occs->len - 1;
+		if (event_end) {
+			while (len >= 0) {
+				cotime = &g_array_index (occs, CalObjTime,
+							 len);
+				if (cal_obj_time_compare_func (cotime,
+							       event_end) <= 0)
+					break;
+				len--;
+			}
+		}
+
+		/* Add the occurrences onto the main array. */
+		if (len >= 0)
+			g_array_append_vals (all_occs, occs->data, len + 1);
+
+ 		g_array_free (occs, TRUE);
+
+		/* Skip to the next period, or exit the loop if finished. */
+		if ((*vtable->find_next_position) (&occ, event_end,
+						   &recur_data, interval_end))
+			break;
+	}
+
+	return all_occs;
+}
+
+
+static GArray*
+cal_obj_generate_set_yearly	(RecurData *recur_data,
+				 ECalRecurVTable *vtable,
+				 CalObjTime *occ)
+{
+	ECalRecurrence *recur = recur_data->recur;
+	GArray *occs_arrays[4], *occs, *occs2;
+	gint num_occs_arrays = 0, i;
+
+	/* This is a bit complicated, since the iCalendar spec says that
+	   several BYxxx modifiers can be used simultaneously. So we have to
+	   be quite careful when determining the days of the occurrences.
+	   The BYHOUR, BYMINUTE & BYSECOND modifiers are no problem at all.
+
+	   The modifiers we have to worry about are: BYMONTH, BYWEEKNO,
+	   BYYEARDAY, BYMONTHDAY & BYDAY. We can't do these sequentially
+	   since each filter will mess up the results of the previous one.
+	   But they aren't all completely independant, e.g. BYMONTHDAY and
+	   BYDAY are related to BYMONTH, and BYDAY is related to BYWEEKNO.
+
+	   BYDAY & BYMONTHDAY can also be applied independently, which makes
+	   it worse. So we assume that if BYMONTH or BYWEEKNO is used, then
+	   the BYDAY modifier applies to those, else it is applied
+	   independantly.
+
+	   We expand the occurrences in parallel into the occs_arrays[] array,
+	   and then merge them all into one GArray before expanding BYHOUR,
+	   BYMINUTE & BYSECOND. */
+
+	if (recur->bymonth) {
+		occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+		g_array_append_vals (occs, occ, 1);
+
+		occs = (*vtable->bymonth_filter) (recur_data, occs);
+
+		/* If BYMONTHDAY & BYDAY are both set we need to expand them
+		   in parallel and add the results. */
+		if (recur->bymonthday && recur->byday) {
+			/* Copy the occs array. */
+			occs2 = g_array_new (FALSE, FALSE,
+					     sizeof (CalObjTime));
+			g_array_append_vals (occs2, occs->data, occs->len);
+
+			occs = (*vtable->bymonthday_filter) (recur_data, occs);
+			/* Note that we explicitly call the monthly version
+			   of the BYDAY expansion filter. */
+			occs2 = cal_obj_byday_expand_monthly (recur_data,
+							      occs2);
+
+			/* Add the 2 resulting arrays together. */
+			g_array_append_vals (occs, occs2->data, occs2->len);
+			g_array_free (occs2, TRUE);
+		} else {
+			occs = (*vtable->bymonthday_filter) (recur_data, occs);
+			/* Note that we explicitly call the monthly version
+			   of the BYDAY expansion filter. */
+			occs = cal_obj_byday_expand_monthly (recur_data, occs);
+		}
+
+		occs_arrays[num_occs_arrays++] = occs;
+	}
+
+	if (recur->byweekno) {
+		occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+		g_array_append_vals (occs, occ, 1);
+
+		occs = (*vtable->byweekno_filter) (recur_data, occs);
+		/* Note that we explicitly call the weekly version of the
+		   BYDAY expansion filter. */
+		occs = cal_obj_byday_expand_weekly (recur_data, occs);
+
+		occs_arrays[num_occs_arrays++] = occs;
+	}
+
+	if (recur->byyearday) {
+		occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+		g_array_append_vals (occs, occ, 1);
+
+		occs = (*vtable->byyearday_filter) (recur_data, occs);
+
+		occs_arrays[num_occs_arrays++] = occs;
+	}
+
+	/* If BYMONTHDAY is set, and BYMONTH is not set, we need to
+	   expand BYMONTHDAY independantly. */
+	if (recur->bymonthday && !recur->bymonth) {
+		occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+		g_array_append_vals (occs, occ, 1);
+
+		occs = (*vtable->bymonthday_filter) (recur_data, occs);
+
+		occs_arrays[num_occs_arrays++] = occs;
+	}
+
+	/* If BYDAY is set, and BYMONTH and BYWEEKNO are not set, we need to
+	   expand BYDAY independantly. */
+	if (recur->byday && !recur->bymonth && !recur->byweekno) {
+		occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+		g_array_append_vals (occs, occ, 1);
+
+		occs = (*vtable->byday_filter) (recur_data, occs);
+
+		occs_arrays[num_occs_arrays++] = occs;
+	}
+
+	/* Add all the arrays together. If no filters were used we just
+	   create an array with one element. */
+	if (num_occs_arrays > 0) {
+		occs = occs_arrays[0];
+		for (i = 1; i < num_occs_arrays; i++) {
+			occs2 = occs_arrays[i];
+			g_array_append_vals (occs, occs2->data, occs2->len);
+			g_array_free (occs2, TRUE);
+		}
+	} else {
+		occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+		g_array_append_vals (occs, occ, 1);
+	}
+
+	/* Now expand BYHOUR, BYMINUTE & BYSECOND. */
+	occs = (*vtable->byhour_filter) (recur_data, occs);
+	occs = (*vtable->byminute_filter) (recur_data, occs);
+	occs = (*vtable->bysecond_filter) (recur_data, occs);
+
+	return occs;
+}
+
+
+static GArray*
+cal_obj_generate_set_monthly	(RecurData *recur_data,
+				 ECalRecurVTable *vtable,
+				 CalObjTime *occ)
+{
+	GArray *occs, *occs2;
+
+	/* We start with just the one time in each set. */
+	occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+	g_array_append_vals (occs, occ, 1);
+
+	occs = (*vtable->bymonth_filter) (recur_data, occs);
+
+	/* We need to combine the output of BYMONTHDAY & BYDAY, by doing them
+	   in parallel rather than sequentially. If we did them sequentially
+	   then we would lose the occurrences generated by BYMONTHDAY, and
+	   instead have repetitions of the occurrences from BYDAY. */
+	if (recur_data->recur->bymonthday && recur_data->recur->byday) {
+		occs2 = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+		g_array_append_vals (occs2, occs->data, occs->len);
+
+		occs = (*vtable->bymonthday_filter) (recur_data, occs);
+		occs2 = (*vtable->byday_filter) (recur_data, occs2);
+
+		g_array_append_vals (occs, occs2->data, occs2->len);
+		g_array_free (occs2, TRUE);
+	} else {
+		occs = (*vtable->bymonthday_filter) (recur_data, occs);
+		occs = (*vtable->byday_filter) (recur_data, occs);
+	}
+
+	occs = (*vtable->byhour_filter) (recur_data, occs);
+	occs = (*vtable->byminute_filter) (recur_data, occs);
+	occs = (*vtable->bysecond_filter) (recur_data, occs);
+
+	return occs;
+}
+
+
+static GArray*
+cal_obj_generate_set_default	(RecurData *recur_data,
+				 ECalRecurVTable *vtable,
+				 CalObjTime *occ)
+{
+	GArray *occs;
+
+#if 0
+	g_print ("Generating set for %i/%i/%i %02i:%02i:%02i\n",
+		 occ->day, occ->month + 1, occ->year, occ->hour, occ->minute,
+		 occ->second);
+#endif
+
+	/* We start with just the one time in the set. */
+	occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+	g_array_append_vals (occs, occ, 1);
+
+	occs = (*vtable->bymonth_filter) (recur_data, occs);
+	if (vtable->byweekno_filter)
+		occs = (*vtable->byweekno_filter) (recur_data, occs);
+	if (vtable->byyearday_filter)
+		occs = (*vtable->byyearday_filter) (recur_data, occs);
+	if (vtable->bymonthday_filter)
+		occs = (*vtable->bymonthday_filter) (recur_data, occs);
+	occs = (*vtable->byday_filter) (recur_data, occs);
+
+	occs = (*vtable->byhour_filter) (recur_data, occs);
+	occs = (*vtable->byminute_filter) (recur_data, occs);
+	occs = (*vtable->bysecond_filter) (recur_data, occs);
+
+	return occs;
+}
+
+
+
+/* Returns the function table corresponding to the recurrence frequency. */
+static ECalRecurVTable* cal_obj_get_vtable (icalrecurrencetype_frequency recur_type)
+{
+	ECalRecurVTable* vtable;
+
+	switch (recur_type) {
+	case ICAL_YEARLY_RECURRENCE:
+		vtable = &cal_obj_yearly_vtable;
+		break;
+	case ICAL_MONTHLY_RECURRENCE:
+		vtable = &cal_obj_monthly_vtable;
+		break;
+	case ICAL_WEEKLY_RECURRENCE:
+		vtable = &cal_obj_weekly_vtable;
+		break;
+	case ICAL_DAILY_RECURRENCE:
+		vtable = &cal_obj_daily_vtable;
+		break;
+	case ICAL_HOURLY_RECURRENCE:
+		vtable = &cal_obj_hourly_vtable;
+		break;
+	case ICAL_MINUTELY_RECURRENCE:
+		vtable = &cal_obj_minutely_vtable;
+		break;
+	case ICAL_SECONDLY_RECURRENCE:
+		vtable = &cal_obj_secondly_vtable;
+		break;
+	default:
+		g_warning ("Unknown recurrence frequency");
+		vtable = NULL;
+	}
+
+	return vtable;
+}
+
+
+/* This creates a number of fast lookup tables used when filtering with the
+   modifier properties BYMONTH, BYYEARDAY etc. */
+static void
+cal_obj_initialize_recur_data (RecurData  *recur_data,
+			       ECalRecurrence *recur,
+			       CalObjTime *event_start)
+{
+	GList *elem;
+	gint month, yearday, monthday, weekday, week_num, hour, minute, second;
+
+	/* Clear the entire RecurData. */
+	memset (recur_data, 0, sizeof (RecurData));
+
+	recur_data->recur = recur;
+
+	/* Set the weekday, used for the WEEKLY frequency and the BYWEEKNO
+	   modifier. */
+	recur_data->weekday_offset = cal_obj_time_weekday_offset (event_start,
+								  recur);
+
+	/* Create an array of months from bymonths for fast lookup. */
+	elem = recur->bymonth;
+	while (elem) {
+		month = GPOINTER_TO_INT (elem->data);
+		recur_data->months[month] = 1;
+		elem = elem->next;
+	}
+
+	/* Create an array of yeardays from byyearday for fast lookup.
+	   We create a second array to handle the negative values. The first
+	   element there corresponds to the last day of the year. */
+	elem = recur->byyearday;
+	while (elem) {
+		yearday = GPOINTER_TO_INT (elem->data);
+		if (yearday >= 0)
+			recur_data->yeardays[yearday] = 1;
+		else
+			recur_data->neg_yeardays[-yearday] = 1;
+		elem = elem->next;
+	}
+
+	/* Create an array of monthdays from bymonthday for fast lookup.
+	   We create a second array to handle the negative values. The first
+	   element there corresponds to the last day of the month. */
+	elem = recur->bymonthday;
+	while (elem) {
+		monthday = GPOINTER_TO_INT (elem->data);
+		if (monthday >= 0)
+			recur_data->monthdays[monthday] = 1;
+		else
+			recur_data->neg_monthdays[-monthday] = 1;
+		elem = elem->next;
+	}
+
+	/* Create an array of weekdays from byday for fast lookup. */
+	elem = recur->byday;
+	while (elem) {
+		weekday = GPOINTER_TO_INT (elem->data);
+		elem = elem->next;
+		/* The week number is not used when filtering. */
+		week_num = GPOINTER_TO_INT (elem->data);
+		elem = elem->next;
+
+		recur_data->weekdays[weekday] = 1;
+	}
+
+	/* Create an array of hours from byhour for fast lookup. */
+	elem = recur->byhour;
+	while (elem) {
+		hour = GPOINTER_TO_INT (elem->data);
+		recur_data->hours[hour] = 1;
+		elem = elem->next;
+	}
+
+	/* Create an array of minutes from byminutes for fast lookup. */
+	elem = recur->byminute;
+	while (elem) {
+		minute = GPOINTER_TO_INT (elem->data);
+		recur_data->minutes[minute] = 1;
+		elem = elem->next;
+	}
+
+	/* Create an array of seconds from byseconds for fast lookup. */
+	elem = recur->bysecond;
+	while (elem) {
+		second = GPOINTER_TO_INT (elem->data);
+		recur_data->seconds[second] = 1;
+		elem = elem->next;
+	}
+}
+
+
+static void
+cal_obj_sort_occurrences (GArray *occs)
+{
+	qsort (occs->data, occs->len, sizeof (CalObjTime),
+	       cal_obj_time_compare_func);
+}
+
+
+static void
+cal_obj_remove_duplicates_and_invalid_dates (GArray *occs)
+{
+	static const int days_in_month[12] = {
+		31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+	};
+
+	CalObjTime *occ, *prev_occ = NULL;
+	gint len, i, j = 0, year, month, days;
+	gboolean keep_occ;
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		keep_occ = TRUE;
+
+		if (prev_occ && cal_obj_time_compare_func (occ,
+							   prev_occ) == 0)
+			keep_occ = FALSE;
+
+		year = occ->year;
+		month = occ->month;
+		days = days_in_month[occ->month];
+		/* If it is february and a leap year, add a day. */
+		if (month == 1 && (year % 4 == 0
+				   && (year % 100 != 0
+				       || year % 400 == 0)))
+			days++;
+
+		if (occ->day > days) {
+			/* move occurrence to the last day of the month */
+			occ->day = days;
+		}
+
+		if (keep_occ) {
+			if (i != j)
+				g_array_index (occs, CalObjTime, j)
+					= g_array_index (occs, CalObjTime, i);
+			j++;
+		}
+
+		prev_occ = occ;
+	}
+
+	g_array_set_size (occs, j);
+}
+
+
+/* Removes the exceptions from the ex_occs array from the occurrences in the
+   occs array, and removes any duplicates. Both arrays are sorted. */
+static void
+cal_obj_remove_exceptions (GArray *occs,
+			   GArray *ex_occs)
+{
+	CalObjTime *occ, *prev_occ = NULL, *ex_occ = NULL, *last_occ_kept;
+	gint i, j = 0, cmp, ex_index, occs_len, ex_occs_len;
+	gboolean keep_occ, current_time_is_exception = FALSE;
+
+	if (occs->len == 0)
+		return;
+
+	ex_index = 0;
+	occs_len = occs->len;
+	ex_occs_len = ex_occs->len;
+
+	if (ex_occs_len > 0)
+		ex_occ = &g_array_index (ex_occs, CalObjTime, ex_index);
+
+	for (i = 0; i < occs_len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		keep_occ = TRUE;
+
+		/* If the occurrence is a duplicate of the previous one, skip
+		   it. */
+		if (prev_occ
+		    && cal_obj_time_compare_func (occ, prev_occ) == 0) {
+			keep_occ = FALSE;
+
+			/* If this occurrence is an RDATE with an end or
+			   duration set, and the previous occurrence in the
+			   array was kept, set the RDATE flag of the last one,
+			   so we still use the end date or duration. */
+			if (occ->flags && !current_time_is_exception) {
+				last_occ_kept = &g_array_index (occs,
+								CalObjTime,
+								j - 1);
+				last_occ_kept->flags = TRUE;
+			}
+		} else {
+			/* We've found a new occurrence time. Reset the flag
+			   to indicate that it hasn't been found in the
+			   exceptions array (yet). */
+			current_time_is_exception = FALSE;
+
+			if (ex_occ) {
+				/* Step through the exceptions until we come
+				   to one that matches or follows this
+				   occurrence. */
+				while (ex_occ) {
+					/* If the exception is an EXDATE with
+					   a DATE value, we only have to
+					   compare the date. */
+					if (ex_occ->flags)
+						cmp = cal_obj_date_only_compare_func (ex_occ, occ);
+					else
+						cmp = cal_obj_time_compare_func (ex_occ, occ);
+
+					if (cmp > 0)
+						break;
+
+					/* Move to the next exception, or set
+					   ex_occ to NULL when we reach the
+					   end of array. */
+					ex_index++;
+					if (ex_index < ex_occs_len)
+						ex_occ = &g_array_index (ex_occs, CalObjTime, ex_index);
+					else
+						ex_occ = NULL;
+
+					/* If the exception did match this
+					   occurrence we remove it, and set the
+					   flag to indicate that the current
+					   time is an exception. */
+					if (cmp == 0) {
+						current_time_is_exception = TRUE;
+						keep_occ = FALSE;
+						break;
+					}
+				}
+			}
+		}
+
+		if (keep_occ) {
+			/* We are keeping this occurrence, so we move it to
+			   the next free space, unless its position hasn't
+			   changed (i.e. all previous occurrences were also
+			   kept). */
+			if (i != j)
+				g_array_index (occs, CalObjTime, j)
+					= g_array_index (occs, CalObjTime, i);
+			j++;
+		}
+
+		prev_occ = occ;
+	}
+
+	g_array_set_size (occs, j);
+}
+
+
+
+static GArray*
+cal_obj_bysetpos_filter (ECalRecurrence *recur,
+			 GArray	    *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	GList *elem;
+	gint len, pos;
+
+	/* If BYSETPOS has not been specified, or the array is empty, just
+	   return the array. */
+	elem = recur->bysetpos;
+	if (!elem || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	/* Iterate over the indices given in bysetpos, adding the corresponding
+	   element from occs to new_occs. */
+	len = occs->len;
+	while (elem) {
+		pos = GPOINTER_TO_INT (elem->data);
+
+		/* Negative values count back from the end of the array. */
+		if (pos < 0)
+			pos += len;
+		/* Positive values need to be decremented since the array is
+		   0-based. */
+		else
+			pos--;
+
+		if (pos >= 0 && pos < len) {
+			occ = &g_array_index (occs, CalObjTime, pos);
+			g_array_append_vals (new_occs, occ, 1);
+		}
+		elem = elem->next;
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+
+
+/* Finds the first year from the event_start, counting in multiples of the
+   recurrence interval, that intersects the given interval. It returns TRUE
+   if there is no intersection. */
+static gboolean
+cal_obj_yearly_find_start_position (CalObjTime *event_start,
+				    CalObjTime *event_end,
+				    RecurData  *recur_data,
+				    CalObjTime *interval_start,
+				    CalObjTime *interval_end,
+				    CalObjTime *cotime)
+{
+	*cotime = *event_start;
+
+	/* Move on to the next interval, if the event starts before the
+	   given interval. */
+	if (cotime->year < interval_start->year) {
+		gint years = interval_start->year - cotime->year
+			+ recur_data->recur->interval - 1;
+		years -= years % recur_data->recur->interval;
+		/* NOTE: The day may now be invalid, e.g. 29th Feb. */
+		cotime->year += years;
+	}
+
+	if ((event_end && cotime->year > event_end->year)
+	    || (interval_end && cotime->year > interval_end->year))
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_yearly_find_next_position (CalObjTime *cotime,
+				   CalObjTime *event_end,
+				   RecurData  *recur_data,
+				   CalObjTime *interval_end)
+{
+	/* NOTE: The day may now be invalid, e.g. 29th Feb. */
+	cotime->year += recur_data->recur->interval;
+
+	if ((event_end && cotime->year > event_end->year)
+	    || (interval_end && cotime->year > interval_end->year))
+		return TRUE;
+
+	return FALSE;
+}
+
+
+
+static gboolean
+cal_obj_monthly_find_start_position (CalObjTime *event_start,
+				     CalObjTime *event_end,
+				     RecurData  *recur_data,
+				     CalObjTime *interval_start,
+				     CalObjTime *interval_end,
+				     CalObjTime *cotime)
+{
+	*cotime = *event_start;
+
+	/* Move on to the next interval, if the event starts before the
+	   given interval. */
+	if (cal_obj_time_compare (cotime, interval_start, CALOBJ_MONTH) < 0) {
+		gint months = (interval_start->year - cotime->year) * 12
+			+ interval_start->month - cotime->month
+			+ recur_data->recur->interval - 1;
+		months -= months % recur_data->recur->interval;
+		/* NOTE: The day may now be invalid, e.g. 31st Sep. */
+		cal_obj_time_add_months (cotime, months);
+	}
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_MONTH) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_MONTH) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_monthly_find_next_position (CalObjTime *cotime,
+				    CalObjTime *event_end,
+				    RecurData  *recur_data,
+				    CalObjTime *interval_end)
+{
+	/* NOTE: The day may now be invalid, e.g. 31st Sep. */
+	cal_obj_time_add_months (cotime, recur_data->recur->interval);
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_MONTH) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_MONTH) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+
+static gboolean
+cal_obj_weekly_find_start_position (CalObjTime *event_start,
+				    CalObjTime *event_end,
+				    RecurData  *recur_data,
+				    CalObjTime *interval_start,
+				    CalObjTime *interval_end,
+				    CalObjTime *cotime)
+{
+	GDate event_start_date, interval_start_date;
+	guint32 event_start_julian, interval_start_julian;
+	gint interval_start_weekday_offset;
+	CalObjTime week_start;
+
+	if (event_end && cal_obj_time_compare (event_end, interval_start,
+					       CALOBJ_DAY) < 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (event_start, interval_end,
+						  CALOBJ_DAY) > 0)
+		return TRUE;
+
+	*cotime = *event_start;
+
+	/* Convert the event start and interval start to GDates, so we can
+	   easily find the number of days between them. */
+	g_date_clear (&event_start_date, 1);
+	g_date_set_dmy (&event_start_date, event_start->day,
+			event_start->month + 1, event_start->year);
+	g_date_clear (&interval_start_date, 1);
+	g_date_set_dmy (&interval_start_date, interval_start->day,
+			interval_start->month + 1, interval_start->year);
+
+	/* Calculate the start of the weeks corresponding to the event start
+	   and interval start. */
+	event_start_julian = g_date_get_julian (&event_start_date);
+	event_start_julian -= recur_data->weekday_offset;
+
+	interval_start_julian = g_date_get_julian (&interval_start_date);
+	interval_start_weekday_offset = cal_obj_time_weekday_offset (interval_start, recur_data->recur);
+	interval_start_julian -= interval_start_weekday_offset;
+
+	/* We want to find the first full week using the recurrence interval
+	   that intersects the given interval dates. */
+	if (event_start_julian < interval_start_julian) {
+		gint weeks = (interval_start_julian - event_start_julian) / 7;
+		weeks += recur_data->recur->interval - 1;
+		weeks -= weeks % recur_data->recur->interval;
+		cal_obj_time_add_days (cotime, weeks * 7);
+	}
+
+	week_start = *cotime;
+	cal_obj_time_add_days (&week_start, -recur_data->weekday_offset);
+
+	if (event_end && cal_obj_time_compare (&week_start, event_end,
+					       CALOBJ_DAY) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (&week_start, interval_end,
+						  CALOBJ_DAY) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_weekly_find_next_position (CalObjTime *cotime,
+				   CalObjTime *event_end,
+				   RecurData  *recur_data,
+				   CalObjTime *interval_end)
+{
+	CalObjTime week_start;
+
+	cal_obj_time_add_days (cotime, recur_data->recur->interval * 7);
+
+	/* Return TRUE if the start of this week is after the event finishes
+	   or is after the end of the required interval. */
+	week_start = *cotime;
+	cal_obj_time_add_days (&week_start, -recur_data->weekday_offset);
+
+#ifdef CAL_OBJ_DEBUG
+	g_print ("Next  day: %s\n", cal_obj_time_to_string (cotime));
+	g_print ("Week Start: %s\n", cal_obj_time_to_string (&week_start));
+#endif
+
+	if (event_end && cal_obj_time_compare (&week_start, event_end,
+					       CALOBJ_DAY) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (&week_start, interval_end,
+						  CALOBJ_DAY) > 0) {
+#ifdef CAL_OBJ_DEBUG
+		g_print ("Interval end reached: %s\n",
+			 cal_obj_time_to_string (interval_end));
+#endif
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_daily_find_start_position  (CalObjTime *event_start,
+				    CalObjTime *event_end,
+				    RecurData  *recur_data,
+				    CalObjTime *interval_start,
+				    CalObjTime *interval_end,
+				    CalObjTime *cotime)
+{
+	GDate event_start_date, interval_start_date;
+	guint32 event_start_julian, interval_start_julian, days;
+
+	if (interval_end && cal_obj_time_compare (event_start, interval_end,
+						  CALOBJ_DAY) > 0)
+		return TRUE;
+	if (event_end && cal_obj_time_compare (event_end, interval_start,
+					       CALOBJ_DAY) < 0)
+		return TRUE;
+
+	*cotime = *event_start;
+
+	/* Convert the event start and interval start to GDates, so we can
+	   easily find the number of days between them. */
+	g_date_clear (&event_start_date, 1);
+	g_date_set_dmy (&event_start_date, event_start->day,
+			event_start->month + 1, event_start->year);
+	g_date_clear (&interval_start_date, 1);
+	g_date_set_dmy (&interval_start_date, interval_start->day,
+			interval_start->month + 1, interval_start->year);
+
+	event_start_julian = g_date_get_julian (&event_start_date);
+	interval_start_julian = g_date_get_julian (&interval_start_date);
+
+	if (event_start_julian < interval_start_julian) {
+		days = interval_start_julian - event_start_julian
+			+ recur_data->recur->interval - 1;
+		days -= days % recur_data->recur->interval;
+		cal_obj_time_add_days (cotime, days);
+	}
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_DAY) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_DAY) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_daily_find_next_position  (CalObjTime *cotime,
+				   CalObjTime *event_end,
+				   RecurData  *recur_data,
+				   CalObjTime *interval_end)
+{
+	cal_obj_time_add_days (cotime, recur_data->recur->interval);
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_DAY) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_DAY) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_hourly_find_start_position (CalObjTime *event_start,
+				    CalObjTime *event_end,
+				    RecurData  *recur_data,
+				    CalObjTime *interval_start,
+				    CalObjTime *interval_end,
+				    CalObjTime *cotime)
+{
+	GDate event_start_date, interval_start_date;
+	guint32 event_start_julian, interval_start_julian, hours;
+
+	if (interval_end && cal_obj_time_compare (event_start, interval_end,
+						  CALOBJ_HOUR) > 0)
+		return TRUE;
+	if (event_end && cal_obj_time_compare (event_end, interval_start,
+					       CALOBJ_HOUR) < 0)
+		return TRUE;
+
+	*cotime = *event_start;
+
+	if (cal_obj_time_compare (event_start, interval_start,
+				  CALOBJ_HOUR) < 0) {
+		/* Convert the event start and interval start to GDates, so we
+		   can easily find the number of days between them. */
+		g_date_clear (&event_start_date, 1);
+		g_date_set_dmy (&event_start_date, event_start->day,
+				event_start->month + 1, event_start->year);
+		g_date_clear (&interval_start_date, 1);
+		g_date_set_dmy (&interval_start_date, interval_start->day,
+				interval_start->month + 1,
+				interval_start->year);
+
+		event_start_julian = g_date_get_julian (&event_start_date);
+		interval_start_julian = g_date_get_julian (&interval_start_date);
+
+		hours = (interval_start_julian - event_start_julian) * 24;
+		hours += interval_start->hour - event_start->hour;
+		hours += recur_data->recur->interval - 1;
+		hours -= hours % recur_data->recur->interval;
+		cal_obj_time_add_hours (cotime, hours);
+	}
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_HOUR) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_HOUR) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_hourly_find_next_position (CalObjTime *cotime,
+				   CalObjTime *event_end,
+				   RecurData  *recur_data,
+				   CalObjTime *interval_end)
+{
+	cal_obj_time_add_hours (cotime, recur_data->recur->interval);
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_HOUR) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_HOUR) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_minutely_find_start_position (CalObjTime *event_start,
+				      CalObjTime *event_end,
+				      RecurData  *recur_data,
+				      CalObjTime *interval_start,
+				      CalObjTime *interval_end,
+				      CalObjTime *cotime)
+{
+	GDate event_start_date, interval_start_date;
+	guint32 event_start_julian, interval_start_julian, minutes;
+
+	if (interval_end && cal_obj_time_compare (event_start, interval_end,
+						  CALOBJ_MINUTE) > 0)
+		return TRUE;
+	if (event_end && cal_obj_time_compare (event_end, interval_start,
+					       CALOBJ_MINUTE) < 0)
+		return TRUE;
+
+	*cotime = *event_start;
+
+	if (cal_obj_time_compare (event_start, interval_start,
+				  CALOBJ_MINUTE) < 0) {
+		/* Convert the event start and interval start to GDates, so we
+		   can easily find the number of days between them. */
+		g_date_clear (&event_start_date, 1);
+		g_date_set_dmy (&event_start_date, event_start->day,
+				event_start->month + 1, event_start->year);
+		g_date_clear (&interval_start_date, 1);
+		g_date_set_dmy (&interval_start_date, interval_start->day,
+				interval_start->month + 1,
+				interval_start->year);
+
+		event_start_julian = g_date_get_julian (&event_start_date);
+		interval_start_julian = g_date_get_julian (&interval_start_date);
+
+		minutes = (interval_start_julian - event_start_julian)
+			* 24 * 60;
+		minutes += (interval_start->hour - event_start->hour) * 24;
+		minutes += interval_start->minute - event_start->minute;
+		minutes += recur_data->recur->interval - 1;
+		minutes -= minutes % recur_data->recur->interval;
+		cal_obj_time_add_minutes (cotime, minutes);
+	}
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_MINUTE) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_MINUTE) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_minutely_find_next_position (CalObjTime *cotime,
+				     CalObjTime *event_end,
+				     RecurData  *recur_data,
+				     CalObjTime *interval_end)
+{
+	cal_obj_time_add_minutes (cotime, recur_data->recur->interval);
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_MINUTE) > 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_MINUTE) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_secondly_find_start_position (CalObjTime *event_start,
+				      CalObjTime *event_end,
+				      RecurData  *recur_data,
+				      CalObjTime *interval_start,
+				      CalObjTime *interval_end,
+				      CalObjTime *cotime)
+{
+	GDate event_start_date, interval_start_date;
+	guint32 event_start_julian, interval_start_julian, seconds;
+
+	if (interval_end && cal_obj_time_compare (event_start, interval_end,
+						  CALOBJ_SECOND) > 0)
+		return TRUE;
+	if (event_end && cal_obj_time_compare (event_end, interval_start,
+					       CALOBJ_SECOND) < 0)
+		return TRUE;
+
+	*cotime = *event_start;
+
+	if (cal_obj_time_compare (event_start, interval_start,
+				  CALOBJ_SECOND) < 0) {
+		/* Convert the event start and interval start to GDates, so we
+		   can easily find the number of days between them. */
+		g_date_clear (&event_start_date, 1);
+		g_date_set_dmy (&event_start_date, event_start->day,
+				event_start->month + 1, event_start->year);
+		g_date_clear (&interval_start_date, 1);
+		g_date_set_dmy (&interval_start_date, interval_start->day,
+				interval_start->month + 1,
+				interval_start->year);
+
+		event_start_julian = g_date_get_julian (&event_start_date);
+		interval_start_julian = g_date_get_julian (&interval_start_date);
+
+		seconds = (interval_start_julian - event_start_julian)
+			* 24 * 60 * 60;
+		seconds += (interval_start->hour - event_start->hour)
+			* 24 * 60;
+		seconds += (interval_start->minute - event_start->minute) * 60;
+		seconds += interval_start->second - event_start->second;
+		seconds += recur_data->recur->interval - 1;
+		seconds -= seconds % recur_data->recur->interval;
+		cal_obj_time_add_seconds (cotime, seconds);
+	}
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_SECOND) >= 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_SECOND) >= 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+static gboolean
+cal_obj_secondly_find_next_position (CalObjTime *cotime,
+				     CalObjTime *event_end,
+				     RecurData  *recur_data,
+				     CalObjTime *interval_end)
+{
+	cal_obj_time_add_seconds (cotime, recur_data->recur->interval);
+
+	if (event_end && cal_obj_time_compare (cotime, event_end,
+					       CALOBJ_SECOND) >= 0)
+		return TRUE;
+	if (interval_end && cal_obj_time_compare (cotime, interval_end,
+						  CALOBJ_SECOND) >= 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+
+
+
+
+/* If the BYMONTH rule is specified it expands each occurrence in occs, by
+   using each of the months in the bymonth list. */
+static GArray*
+cal_obj_bymonth_expand		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	GList *elem;
+	gint len, i;
+
+	/* If BYMONTH has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->bymonth || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		elem = recur_data->recur->bymonth;
+		while (elem) {
+			/* NOTE: The day may now be invalid, e.g. 31st Feb. */
+			occ->month = GPOINTER_TO_INT (elem->data);
+			g_array_append_vals (new_occs, occ, 1);
+			elem = elem->next;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+/* If the BYMONTH rule is specified it filters out all occurrences in occs
+   which do not match one of the months in the bymonth list. */
+static GArray*
+cal_obj_bymonth_filter		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	gint len, i;
+
+	/* If BYMONTH has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->bymonth || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		if (recur_data->months[occ->month])
+			g_array_append_vals (new_occs, occ, 1);
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+
+static GArray*
+cal_obj_byweekno_expand		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ, year_start_cotime, year_end_cotime, cotime;
+	GList *elem;
+	gint len, i, weekno;
+
+	/* If BYWEEKNO has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byweekno || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		/* Find the day that would correspond to week 1 (note that
+		   week 1 is the first week starting from the specified week
+		   start day that has 4 days in the new year). */
+		year_start_cotime = *occ;
+		cal_obj_time_find_first_week (&year_start_cotime,
+					      recur_data);
+
+		/* Find the day that would correspond to week 1 of the next
+		   year, which we use for -ve week numbers. */
+		year_end_cotime = *occ;
+		year_end_cotime.year++;
+		cal_obj_time_find_first_week (&year_end_cotime,
+					      recur_data);
+
+		/* Now iterate over the week numbers in byweekno, generating a
+		   new occurrence for each one. */
+		elem = recur_data->recur->byweekno;
+		while (elem) {
+			weekno = GPOINTER_TO_INT (elem->data);
+			if (weekno > 0) {
+				cotime = year_start_cotime;
+				cal_obj_time_add_days (&cotime,
+						       (weekno - 1) * 7);
+			} else {
+				cotime = year_end_cotime;
+				cal_obj_time_add_days (&cotime, weekno * 7);
+			}
+
+			/* Skip occurrences if they fall outside the year. */
+			if (cotime.year == occ->year)
+				g_array_append_val (new_occs, cotime);
+			elem = elem->next;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+#if 0
+/* This isn't used at present. */
+static GArray*
+cal_obj_byweekno_filter		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+
+	return occs;
+}
+#endif
+
+
+static GArray*
+cal_obj_byyearday_expand	(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ, year_start_cotime, year_end_cotime, cotime;
+	GList *elem;
+	gint len, i, dayno;
+
+	/* If BYYEARDAY has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byyearday || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		/* Find the day that would correspond to day 1. */
+		year_start_cotime = *occ;
+		year_start_cotime.month = 0;
+		year_start_cotime.day = 1;
+
+		/* Find the day that would correspond to day 1 of the next
+		   year, which we use for -ve day numbers. */
+		year_end_cotime = *occ;
+		year_end_cotime.year++;
+		year_end_cotime.month = 0;
+		year_end_cotime.day = 1;
+
+		/* Now iterate over the day numbers in byyearday, generating a
+		   new occurrence for each one. */
+		elem = recur_data->recur->byyearday;
+		while (elem) {
+			dayno = GPOINTER_TO_INT (elem->data);
+			if (dayno > 0) {
+				cotime = year_start_cotime;
+				cal_obj_time_add_days (&cotime, dayno - 1);
+			} else {
+				cotime = year_end_cotime;
+				cal_obj_time_add_days (&cotime, dayno);
+			}
+
+			/* Skip occurrences if they fall outside the year. */
+			if (cotime.year == occ->year)
+				g_array_append_val (new_occs, cotime);
+			elem = elem->next;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+/* Note: occs must not contain invalid dates, e.g. 31st September. */
+static GArray*
+cal_obj_byyearday_filter	(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	gint yearday, len, i, days_in_year;
+
+	/* If BYYEARDAY has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byyearday || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		yearday = cal_obj_time_day_of_year (occ);
+		if (recur_data->yeardays[yearday]) {
+			g_array_append_vals (new_occs, occ, 1);
+		} else {
+			days_in_year = g_date_is_leap_year (occ->year)
+				? 366 : 365;
+			if (recur_data->neg_yeardays[days_in_year + 1
+						    - yearday])
+				g_array_append_vals (new_occs, occ, 1);
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+
+static GArray*
+cal_obj_bymonthday_expand	(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ, month_start_cotime, month_end_cotime, cotime;
+	GList *elem;
+	gint len, i, dayno;
+
+	/* If BYMONTHDAY has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->bymonthday || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		/* Find the day that would correspond to day 1. */
+		month_start_cotime = *occ;
+		month_start_cotime.day = 1;
+
+		/* Find the day that would correspond to day 1 of the next
+		   month, which we use for -ve day numbers. */
+		month_end_cotime = *occ;
+		month_end_cotime.month++;
+		month_end_cotime.day = 1;
+
+		/* Now iterate over the day numbers in bymonthday, generating a
+		   new occurrence for each one. */
+		elem = recur_data->recur->bymonthday;
+		while (elem) {
+			dayno = GPOINTER_TO_INT (elem->data);
+			if (dayno > 0) {
+				cotime = month_start_cotime;
+				cal_obj_time_add_days (&cotime, dayno - 1);
+			} else {
+				cotime = month_end_cotime;
+				cal_obj_time_add_days (&cotime, dayno);
+			}
+			if (cotime.month == occ->month) {
+				g_array_append_val (new_occs, cotime);
+			} else {
+				/* set to last day in month */
+				cotime.month = occ->month;
+				cotime.day = time_days_in_month (occ->year, occ->month);
+				g_array_append_val (new_occs, cotime);
+			}
+				
+			elem = elem->next;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+static GArray*
+cal_obj_bymonthday_filter	(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	gint len, i, days_in_month;
+
+	/* If BYMONTHDAY has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->bymonthday || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		if (recur_data->monthdays[occ->day]) {
+			g_array_append_vals (new_occs, occ, 1);
+		} else {
+			days_in_month = time_days_in_month (occ->year,
+							    occ->month);
+			if (recur_data->neg_monthdays[days_in_month + 1
+						     - occ->day])
+				g_array_append_vals (new_occs, occ, 1);
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+
+static GArray*
+cal_obj_byday_expand_yearly	(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	GList *elem;
+	gint len, i, weekday, week_num;
+	gint first_weekday, last_weekday, offset;
+	guint16 year;
+
+	/* If BYDAY has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byday || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		elem = recur_data->recur->byday;
+		while (elem) {
+			weekday = GPOINTER_TO_INT (elem->data);
+			elem = elem->next;
+			week_num = GPOINTER_TO_INT (elem->data);
+			elem = elem->next;
+
+			year = occ->year;
+			if (week_num == 0) {
+				/* Expand to every Mon/Tue/etc. in the year. */
+				occ->month = 0;
+				occ->day = 1;
+				first_weekday = cal_obj_time_weekday (occ);
+				offset = (weekday + 7 - first_weekday) % 7;
+				cal_obj_time_add_days (occ, offset);
+
+				while (occ->year == year) {
+					g_array_append_vals (new_occs, occ, 1);
+					cal_obj_time_add_days (occ, 7);
+				}
+
+			} else if (week_num > 0) {
+				/* Add the nth Mon/Tue/etc. in the year. */
+				occ->month = 0;
+				occ->day = 1;
+				first_weekday = cal_obj_time_weekday (occ);
+				offset = (weekday + 7 - first_weekday) % 7;
+				offset += (week_num - 1) * 7;
+				cal_obj_time_add_days (occ, offset);
+				if (occ->year == year)
+					g_array_append_vals (new_occs, occ, 1);
+
+			} else {
+				/* Add the -nth Mon/Tue/etc. in the year. */
+				occ->month = 11;
+				occ->day = 31;
+				last_weekday = cal_obj_time_weekday (occ);
+				offset = (last_weekday + 7 - weekday) % 7;
+				offset += (week_num - 1) * 7;
+				cal_obj_time_add_days (occ, -offset);
+				if (occ->year == year)
+					g_array_append_vals (new_occs, occ, 1);
+			}
+
+			/* Reset the year, as we may have gone past the end. */
+			occ->year = year;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+static GArray*
+cal_obj_byday_expand_monthly	(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	GList *elem;
+	gint len, i, weekday, week_num;
+	gint first_weekday, last_weekday, offset;
+	guint16 year;
+	guint8 month;
+
+	/* If BYDAY has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byday || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		elem = recur_data->recur->byday;
+		while (elem) {
+			weekday = GPOINTER_TO_INT (elem->data);
+			elem = elem->next;
+			week_num = GPOINTER_TO_INT (elem->data);
+			elem = elem->next;
+
+			year = occ->year;
+			month = occ->month;
+			if (week_num == 0) {
+				/* Expand to every Mon/Tue/etc. in the month.*/
+				occ->day = 1;
+				first_weekday = cal_obj_time_weekday (occ);
+				offset = (weekday + 7 - first_weekday) % 7;
+				cal_obj_time_add_days (occ, offset);
+
+				while (occ->year == year
+				       && occ->month == month) {
+					g_array_append_vals (new_occs, occ, 1);
+					cal_obj_time_add_days (occ, 7);
+				}
+
+			} else if (week_num > 0) {
+				/* Add the nth Mon/Tue/etc. in the month. */
+				occ->day = 1;
+				first_weekday = cal_obj_time_weekday (occ);
+				offset = (weekday + 7 - first_weekday) % 7;
+				offset += (week_num - 1) * 7;
+				cal_obj_time_add_days (occ, offset);
+				if (occ->year == year && occ->month == month)
+					g_array_append_vals (new_occs, occ, 1);
+
+			} else {
+				/* Add the -nth Mon/Tue/etc. in the month. */
+				occ->day = time_days_in_month (occ->year,
+							       occ->month);
+				last_weekday = cal_obj_time_weekday (occ);
+
+				/* This calculates the number of days to step
+				   backwards from the last day of the month
+				   to the weekday we want. */
+				offset = (last_weekday + 7 - weekday) % 7;
+
+				/* This adds on the weeks. */
+				offset += (-week_num - 1) * 7;
+
+				cal_obj_time_add_days (occ, -offset);
+				if (occ->year == year && occ->month == month)
+					g_array_append_vals (new_occs, occ, 1);
+			}
+
+			/* Reset the year & month, as we may have gone past
+			   the end. */
+			occ->year = year;
+			occ->month = month;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+/* Note: occs must not contain invalid dates, e.g. 31st September. */
+static GArray*
+cal_obj_byday_expand_weekly	(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	GList *elem;
+	gint len, i, weekday, week_num;
+	gint weekday_offset, new_weekday_offset;
+
+	/* If BYDAY has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byday || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		elem = recur_data->recur->byday;
+		while (elem) {
+			weekday = GPOINTER_TO_INT (elem->data);
+			elem = elem->next;
+
+			/* FIXME: Currently we just ignore this, but maybe we
+			   should skip all elements where week_num != 0.
+			   The spec isn't clear about this. */
+			week_num = GPOINTER_TO_INT (elem->data);
+			elem = elem->next;
+
+			weekday_offset = cal_obj_time_weekday_offset (occ, recur_data->recur);
+			new_weekday_offset = (weekday + 7 - recur_data->recur->week_start_day) % 7;
+			cal_obj_time_add_days (occ, new_weekday_offset - weekday_offset);
+			g_array_append_vals (new_occs, occ, 1);
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+/* Note: occs must not contain invalid dates, e.g. 31st September. */
+static GArray*
+cal_obj_byday_filter		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	gint len, i, weekday;
+
+	/* If BYDAY has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byday || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		weekday = cal_obj_time_weekday (occ);
+
+		/* See if the weekday on its own is set. */
+		if (recur_data->weekdays[weekday])
+			g_array_append_vals (new_occs, occ, 1);
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+
+/* If the BYHOUR rule is specified it expands each occurrence in occs, by
+   using each of the hours in the byhour list. */
+static GArray*
+cal_obj_byhour_expand		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	GList *elem;
+	gint len, i;
+
+	/* If BYHOUR has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byhour || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		elem = recur_data->recur->byhour;
+		while (elem) {
+			occ->hour = GPOINTER_TO_INT (elem->data);
+			g_array_append_vals (new_occs, occ, 1);
+			elem = elem->next;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+/* If the BYHOUR rule is specified it filters out all occurrences in occs
+   which do not match one of the hours in the byhour list. */
+static GArray*
+cal_obj_byhour_filter		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	gint len, i;
+
+	/* If BYHOUR has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byhour || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		if (recur_data->hours[occ->hour])
+			g_array_append_vals (new_occs, occ, 1);
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+
+/* If the BYMINUTE rule is specified it expands each occurrence in occs, by
+   using each of the minutes in the byminute list. */
+static GArray*
+cal_obj_byminute_expand		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	GList *elem;
+	gint len, i;
+
+	/* If BYMINUTE has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byminute || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		elem = recur_data->recur->byminute;
+		while (elem) {
+			occ->minute = GPOINTER_TO_INT (elem->data);
+			g_array_append_vals (new_occs, occ, 1);
+			elem = elem->next;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+/* If the BYMINUTE rule is specified it filters out all occurrences in occs
+   which do not match one of the minutes in the byminute list. */
+static GArray*
+cal_obj_byminute_filter		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	gint len, i;
+
+	/* If BYMINUTE has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->byminute || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		if (recur_data->minutes[occ->minute])
+			g_array_append_vals (new_occs, occ, 1);
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+
+/* If the BYSECOND rule is specified it expands each occurrence in occs, by
+   using each of the seconds in the bysecond list. */
+static GArray*
+cal_obj_bysecond_expand		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	GList *elem;
+	gint len, i;
+
+	/* If BYSECOND has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->bysecond || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+
+		elem = recur_data->recur->bysecond;
+		while (elem) {
+			occ->second = GPOINTER_TO_INT (elem->data);
+			g_array_append_vals (new_occs, occ, 1);
+			elem = elem->next;
+		}
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+/* If the BYSECOND rule is specified it filters out all occurrences in occs
+   which do not match one of the seconds in the bysecond list. */
+static GArray*
+cal_obj_bysecond_filter		(RecurData  *recur_data,
+				 GArray     *occs)
+{
+	GArray *new_occs;
+	CalObjTime *occ;
+	gint len, i;
+
+	/* If BYSECOND has not been specified, or the array is empty, just
+	   return the array. */
+	if (!recur_data->recur->bysecond || occs->len == 0)
+		return occs;
+
+	new_occs = g_array_new (FALSE, FALSE, sizeof (CalObjTime));
+
+	len = occs->len;
+	for (i = 0; i < len; i++) {
+		occ = &g_array_index (occs, CalObjTime, i);
+		if (recur_data->seconds[occ->second])
+			g_array_append_vals (new_occs, occ, 1);
+	}
+
+	g_array_free (occs, TRUE);
+
+	return new_occs;
+}
+
+
+
+
+
+/* Adds a positive or negative number of months to the given CalObjTime,
+   updating the year appropriately so we end up with a valid month.
+   Note that the day may be invalid, e.g. 30th Feb. */
+static void
+cal_obj_time_add_months		(CalObjTime *cotime,
+				 gint	     months)
+{
+	guint month, years;
+
+	/* We use a guint to avoid overflow on the guint8. */
+	month = cotime->month + months;
+	cotime->month = month % 12;
+	if (month > 0) {
+		cotime->year += month / 12;
+	} else {
+		years = month / 12;
+		if (cotime->month != 0) {
+			cotime->month += 12;
+			years -= 1;
+		}
+		cotime->year += years;
+	}
+}
+
+
+/* Adds a positive or negative number of days to the given CalObjTime,
+   updating the month and year appropriately so we end up with a valid day. */
+static void
+cal_obj_time_add_days		(CalObjTime *cotime,
+				 gint	     days)
+{
+	gint day, days_in_month;
+
+	/* We use a guint to avoid overflow on the guint8. */
+	day = cotime->day;
+	day += days;
+
+	if (days >= 0) {
+		for (;;) {
+			days_in_month = time_days_in_month (cotime->year,
+							    cotime->month);
+			if (day <= days_in_month)
+				break;
+
+			cotime->month++;
+			if (cotime->month >= 12) {
+				cotime->year++;
+				cotime->month = 0;
+			}
+
+			day -= days_in_month;
+		}
+
+		cotime->day = (guint8) day;
+	} else {
+		while (day <= 0) {
+			if (cotime->month == 0) {
+				cotime->year--;
+				cotime->month = 11;
+			} else {
+				cotime->month--;
+			}
+
+			days_in_month = time_days_in_month (cotime->year,
+							    cotime->month);
+			day += days_in_month;
+		}
+
+		cotime->day = (guint8) day;
+	}
+}
+
+
+/* Adds a positive or negative number of hours to the given CalObjTime,
+   updating the day, month & year appropriately so we end up with a valid
+   time. */
+static void
+cal_obj_time_add_hours		(CalObjTime *cotime,
+				 gint	     hours)
+{
+	gint hour, days;
+
+	/* We use a gint to avoid overflow on the guint8. */
+	hour = cotime->hour + hours;
+	cotime->hour = hour % 24;
+	if (hour >= 0) {
+		if (hour >= 24)
+			cal_obj_time_add_days (cotime, hour / 24);
+	} else {
+		days = hour / 24;
+		if (cotime->hour != 0) {
+			cotime->hour += 24;
+			days -= 1;
+		}
+		cal_obj_time_add_days (cotime, days);
+	}
+}
+
+
+/* Adds a positive or negative number of minutes to the given CalObjTime,
+   updating the rest of the CalObjTime appropriately. */
+static void
+cal_obj_time_add_minutes	(CalObjTime *cotime,
+				 gint	     minutes)
+{
+	gint minute, hours;
+
+	/* We use a gint to avoid overflow on the guint8. */
+	minute = cotime->minute + minutes;
+	cotime->minute = minute % 60;
+	if (minute >= 0) {
+		if (minute >= 60)
+			cal_obj_time_add_hours (cotime, minute / 60);
+	} else {
+		hours = minute / 60;
+		if (cotime->minute != 0) {
+			cotime->minute += 60;
+			hours -= 1;
+		}
+		cal_obj_time_add_hours (cotime, hours);
+	}
+}
+
+
+/* Adds a positive or negative number of seconds to the given CalObjTime,
+   updating the rest of the CalObjTime appropriately. */
+static void
+cal_obj_time_add_seconds	(CalObjTime *cotime,
+				 gint	     seconds)
+{
+	gint second, minutes;
+
+	/* We use a gint to avoid overflow on the guint8. */
+	second = cotime->second + seconds;
+	cotime->second = second % 60;
+	if (second >= 0) {
+		if (second >= 60)
+			cal_obj_time_add_minutes (cotime, second / 60);
+	} else {
+		minutes = second / 60;
+		if (cotime->second != 0) {
+			cotime->second += 60;
+			minutes -= 1;
+		}
+		cal_obj_time_add_minutes (cotime, minutes);
+	}
+}
+
+
+/* Compares 2 CalObjTimes. Returns -1 if the cotime1 is before cotime2, 0 if
+   they are the same, or 1 if cotime1 is after cotime2. The comparison type
+   specifies which parts of the times we are interested in, e.g. if CALOBJ_DAY
+   is used we only want to know if the days are different. */
+static gint
+cal_obj_time_compare		(CalObjTime *cotime1,
+				 CalObjTime *cotime2,
+				 CalObjTimeComparison type)
+{
+	if (cotime1->year < cotime2->year)
+		return -1;
+	if (cotime1->year > cotime2->year)
+		return 1;
+
+	if (type == CALOBJ_YEAR)
+		return 0;
+
+	if (cotime1->month < cotime2->month)
+		return -1;
+	if (cotime1->month > cotime2->month)
+		return 1;
+
+	if (type == CALOBJ_MONTH)
+		return 0;
+
+	if (cotime1->day < cotime2->day)
+		return -1;
+	if (cotime1->day > cotime2->day)
+		return 1;
+
+	if (type == CALOBJ_DAY)
+		return 0;
+
+	if (cotime1->hour < cotime2->hour)
+		return -1;
+	if (cotime1->hour > cotime2->hour)
+		return 1;
+
+	if (type == CALOBJ_HOUR)
+		return 0;
+
+	if (cotime1->minute < cotime2->minute)
+		return -1;
+	if (cotime1->minute > cotime2->minute)
+		return 1;
+
+	if (type == CALOBJ_MINUTE)
+		return 0;
+
+	if (cotime1->second < cotime2->second)
+		return -1;
+	if (cotime1->second > cotime2->second)
+		return 1;
+
+	return 0;
+}
+
+
+/* This is the same as the above function, but without the comparison type.
+   It is used for qsort(). */
+static gint
+cal_obj_time_compare_func (const void *arg1,
+			   const void *arg2)
+{
+	CalObjTime *cotime1, *cotime2;
+	gint retval;
+
+	cotime1 = (CalObjTime*) arg1;
+	cotime2 = (CalObjTime*) arg2;
+
+	if (cotime1->year < cotime2->year)
+		retval = -1;
+	else if (cotime1->year > cotime2->year)
+		retval = 1;
+
+	else if (cotime1->month < cotime2->month)
+		retval = -1;
+	else if (cotime1->month > cotime2->month)
+		retval = 1;
+
+	else if (cotime1->day < cotime2->day)
+		retval = -1;
+	else if (cotime1->day > cotime2->day)
+		retval = 1;
+
+	else if (cotime1->hour < cotime2->hour)
+		retval = -1;
+	else if (cotime1->hour > cotime2->hour)
+		retval = 1;
+
+	else if (cotime1->minute < cotime2->minute)
+		retval = -1;
+	else if (cotime1->minute > cotime2->minute)
+		retval = 1;
+
+	else if (cotime1->second < cotime2->second)
+		retval = -1;
+	else if (cotime1->second > cotime2->second)
+		retval = 1;
+
+	else
+		retval = 0;
+
+#if 0
+	g_print ("%s - ", cal_obj_time_to_string (cotime1));
+	g_print ("%s : %i\n", cal_obj_time_to_string (cotime2), retval);
+#endif
+
+	return retval;
+}
+
+
+static gint
+cal_obj_date_only_compare_func (const void *arg1,
+				const void *arg2)
+{
+	CalObjTime *cotime1, *cotime2;
+
+	cotime1 = (CalObjTime*) arg1;
+	cotime2 = (CalObjTime*) arg2;
+
+	if (cotime1->year < cotime2->year)
+		return -1;
+	if (cotime1->year > cotime2->year)
+		return 1;
+
+	if (cotime1->month < cotime2->month)
+		return -1;
+	if (cotime1->month > cotime2->month)
+		return 1;
+
+	if (cotime1->day < cotime2->day)
+		return -1;
+	if (cotime1->day > cotime2->day)
+		return 1;
+
+	return 0;
+}
+
+
+/* Returns the weekday of the given CalObjTime, from 0 (Mon) - 6 (Sun). */
+static gint
+cal_obj_time_weekday		(CalObjTime *cotime)
+{
+	GDate date;
+	gint weekday;
+
+	g_date_clear (&date, 1);
+	g_date_set_dmy (&date, cotime->day, cotime->month + 1, cotime->year);
+
+	/* This results in a value of 0 (Monday) - 6 (Sunday). */
+	weekday = g_date_get_weekday (&date) - 1;
+
+	return weekday;
+}
+
+
+/* Returns the weekday of the given CalObjTime, from 0 - 6. The week start
+   day is Monday by default, but can be set in the recurrence rule. */
+static gint
+cal_obj_time_weekday_offset	(CalObjTime *cotime,
+				 ECalRecurrence *recur)
+{
+	GDate date;
+	gint weekday, offset;
+
+	g_date_clear (&date, 1);
+	g_date_set_dmy (&date, cotime->day, cotime->month + 1, cotime->year);
+
+	/* This results in a value of 0 (Monday) - 6 (Sunday). */
+	weekday = g_date_get_weekday (&date) - 1;
+
+	/* This calculates the offset of our day from the start of the week.
+	   We just add on a week (to avoid any possible negative values) and
+	   then subtract the specified week start day, then convert it into a
+	   value from 0-6. */
+	offset = (weekday + 7 - recur->week_start_day) % 7;
+
+	return offset;
+}
+
+
+/* Returns the day of the year of the given CalObjTime, from 1 - 366. */
+static gint
+cal_obj_time_day_of_year		(CalObjTime *cotime)
+{
+	GDate date;
+
+	g_date_clear (&date, 1);
+	g_date_set_dmy (&date, cotime->day, cotime->month + 1, cotime->year);
+
+	return g_date_get_day_of_year (&date);
+}
+
+
+/* Finds the first week in the given CalObjTime's year, using the same weekday
+   as the event start day (i.e. from the RecurData).
+   The first week of the year is the first week starting from the specified
+   week start day that has 4 days in the new year. It may be in the previous
+   year. */
+static void
+cal_obj_time_find_first_week	(CalObjTime *cotime,
+				 RecurData  *recur_data)
+{
+	GDate date;
+	gint weekday, week_start_day, first_full_week_start_offset, offset;
+
+	/* Find out the weekday of the 1st of the year, 0 (Mon) - 6 (Sun). */
+	g_date_clear (&date, 1);
+	g_date_set_dmy (&date, 1, 1, cotime->year);
+	weekday = g_date_get_weekday (&date) - 1;
+
+	/* Calculate the first day of the year that starts a new week, i.e. the
+	   first week_start_day after weekday, using 0 = 1st Jan.
+	   e.g. if the 1st Jan is a Tuesday (1) and week_start_day is a
+	   Monday (0), the result will be (0 + 7 - 1) % 7 = 6 (7th Jan). */
+	week_start_day = recur_data->recur->week_start_day;
+	first_full_week_start_offset = (week_start_day + 7 - weekday) % 7;
+
+	/* Now see if we have to move backwards 1 week, i.e. if the week
+	   starts on or after Jan 5th (since the previous week has 4 days in
+	   this year and so will be the first week of the year). */
+	if (first_full_week_start_offset >= 4)
+		first_full_week_start_offset -= 7;
+
+	/* Now add the days to get to the event's weekday. */
+	offset = first_full_week_start_offset + recur_data->weekday_offset;
+
+	/* Now move the cotime to the appropriate day. */
+	cotime->month = 0;
+	cotime->day = 1;
+	cal_obj_time_add_days (cotime, offset);
+}
+
+
+static void
+cal_object_time_from_time	(CalObjTime	*cotime,
+				 time_t		 t,
+				 icaltimezone	*zone)
+{
+	struct icaltimetype tt;
+
+	if (zone)
+		tt = icaltime_from_timet_with_zone (t, FALSE, zone);
+	else
+		tt = icaltime_from_timet (t, FALSE);
+
+	cotime->year     = tt.year;
+	cotime->month    = tt.month - 1;
+	cotime->day      = tt.day;
+	cotime->hour     = tt.hour;
+	cotime->minute   = tt.minute;
+	cotime->second   = tt.second;
+	cotime->flags    = FALSE;
+}
+
+
+/* Debugging function to convert a CalObjTime to a string. It uses a static
+   buffer so beware. */
+#ifdef CAL_OBJ_DEBUG
+static char*
+cal_obj_time_to_string		(CalObjTime	*cotime)
+{
+	static char buffer[20];
+	char *weekdays[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
+			     "   " };
+	gint weekday;
+
+	weekday = cal_obj_time_weekday (cotime);
+
+	sprintf (buffer, "%s %02i/%02i/%04i %02i:%02i:%02i",
+		 weekdays[weekday],
+		 cotime->day, cotime->month + 1, cotime->year,
+		 cotime->hour, cotime->minute, cotime->second);
+	return buffer;
+}
+#endif
+
+
+/* This recalculates the end dates for recurrence & exception rules which use
+   the COUNT property. If refresh is TRUE it will recalculate all enddates
+   for rules which use COUNT. If refresh is FALSE, it will only calculate
+   the enddate if it hasn't already been set. It returns TRUE if the component
+   was changed, i.e. if the component should be saved at some point.
+   We store the enddate in the "X-EVOLUTION-ENDDATE" parameter of the RRULE
+   or EXRULE. */
+static gboolean
+e_cal_recur_ensure_end_dates (ECalComponent	*comp,
+			    gboolean		 refresh,
+			    ECalRecurResolveTimezoneFn  tz_cb,
+			    gpointer		 tz_cb_data)
+{
+	GSList *rrules, *exrules, *elem;
+	gboolean changed = FALSE;
+
+	/* Do the RRULEs. */
+	e_cal_component_get_rrule_property_list (comp, &rrules);
+	for (elem = rrules; elem; elem = elem->next) {
+		changed |= e_cal_recur_ensure_rule_end_date (comp, elem->data,
+							   FALSE, refresh,
+							   tz_cb, tz_cb_data);
+	}
+
+	/* Do the EXRULEs. */
+	e_cal_component_get_exrule_property_list (comp, &exrules);
+	for (elem = exrules; elem; elem = elem->next) {
+		changed |= e_cal_recur_ensure_rule_end_date (comp, elem->data,
+							   TRUE, refresh,
+							   tz_cb, tz_cb_data);
+	}
+
+	return changed;
+}
+
+
+typedef struct _ECalRecurEnsureEndDateData ECalRecurEnsureEndDateData;
+struct _ECalRecurEnsureEndDateData {
+	gint count;
+	gint instances;
+	time_t end_date;
+};
+
+
+static gboolean
+e_cal_recur_ensure_rule_end_date (ECalComponent			*comp,
+				icalproperty			*prop,
+				gboolean			 exception,
+				gboolean			 refresh,
+				ECalRecurResolveTimezoneFn	 tz_cb,
+				gpointer			 tz_cb_data)
+{
+	struct icalrecurrencetype rule;
+	ECalRecurEnsureEndDateData cb_data;
+
+	if (exception)
+		rule = icalproperty_get_exrule (prop);
+	else
+		rule = icalproperty_get_rrule (prop);
+
+	/* If the rule doesn't use COUNT just return. */
+	if (rule.count == 0)
+		return FALSE;
+
+	/* If refresh is FALSE, we check if the enddate is already set, and
+	   if it is we just return. */
+	if (!refresh) {
+		if (e_cal_recur_get_rule_end_date (prop, NULL) != -1)
+			return FALSE;
+	}
+
+	/* Calculate the end date. Note that we initialize end_date to 0, so
+	   if the RULE doesn't generate COUNT instances we save a time_t of 0.
+	   Also note that we use the UTC timezone as the default timezone.
+	   In get_end_date() if the DTSTART is a DATE or floating time, we will
+	   convert the ENDDATE to the current timezone. */
+	cb_data.count = rule.count;
+	cb_data.instances = 0;
+	cb_data.end_date = 0;
+	e_cal_recur_generate_instances_of_rule (comp, prop, -1, -1,
+					      e_cal_recur_ensure_rule_end_date_cb,
+					      &cb_data, tz_cb, tz_cb_data,
+					      icaltimezone_get_utc_timezone ());
+
+	/* Store the end date in the "X-EVOLUTION-ENDDATE" parameter of the
+	   rule. */
+	e_cal_recur_set_rule_end_date (prop, cb_data.end_date);
+		
+	return TRUE;
+}
+
+
+static gboolean
+e_cal_recur_ensure_rule_end_date_cb	(ECalComponent	*comp,
+					 time_t		 instance_start,
+					 time_t		 instance_end,
+					 gpointer	 data)
+{
+	ECalRecurEnsureEndDateData *cb_data;
+
+	cb_data = (ECalRecurEnsureEndDateData*) data;
+
+	cb_data->instances++;
+
+	if (cb_data->instances == cb_data->count) {
+		cb_data->end_date = instance_start;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/* If default_timezone is set, the saved ENDDATE parameter is assumed to be
+   in that timezone. This is used when the DTSTART is a DATE or floating
+   value, since the RRULE end date will change depending on the timezone that
+   it is evaluated in. */
+static time_t
+e_cal_recur_get_rule_end_date	(icalproperty	*prop,
+				 icaltimezone	*default_timezone)
+{
+	icalparameter *param;
+	const char *xname, *xvalue;
+	icalvalue *value;
+	struct icaltimetype icaltime;
+	icaltimezone *zone;
+
+	param = icalproperty_get_first_parameter (prop, ICAL_X_PARAMETER);
+	while (param) {
+		xname = icalparameter_get_xname (param);
+		if (xname && !strcmp (xname, EVOLUTION_END_DATE_PARAMETER)) {
+			xvalue = icalparameter_get_x (param);
+			value = icalvalue_new_from_string (ICAL_DATETIME_VALUE,
+							   xvalue);
+			if (value) {
+				icaltime = icalvalue_get_datetime (value);
+				icalvalue_free (value);
+
+				zone = default_timezone ? default_timezone : 
+					icaltimezone_get_utc_timezone ();
+				return icaltime_as_timet_with_zone (icaltime,
+								    zone);
+			}
+		}
+
+		param = icalproperty_get_next_parameter (prop,
+							 ICAL_X_PARAMETER);
+	}
+
+	return -1;
+}
+
+
+static void
+e_cal_recur_set_rule_end_date	(icalproperty	*prop,
+				 time_t		 end_date)
+{
+	icalparameter *param;
+	icalvalue *value;
+	icaltimezone *utc_zone;
+	struct icaltimetype icaltime;
+	const char *end_date_string, *xname;
+
+	/* We save the value as a UTC DATE-TIME. */
+	utc_zone = icaltimezone_get_utc_timezone ();
+	icaltime = icaltime_from_timet_with_zone (end_date, FALSE, utc_zone);
+	value = icalvalue_new_datetime (icaltime);
+	end_date_string = icalvalue_as_ical_string (value);
+	icalvalue_free (value);
+
+	/* If we already have an X-EVOLUTION-ENDDATE parameter, set the value
+	   to the new date-time. */
+	param = icalproperty_get_first_parameter (prop, ICAL_X_PARAMETER);
+	while (param) {
+		xname = icalparameter_get_xname (param);
+		if (xname && !strcmp (xname, EVOLUTION_END_DATE_PARAMETER)) {
+			icalparameter_set_x (param, end_date_string);
+			return;
+		}
+		param = icalproperty_get_next_parameter (prop, ICAL_X_PARAMETER);
+	}
+
+	/* Create a new X-EVOLUTION-ENDDATE and add it to the property. */
+	param = icalparameter_new_x (end_date_string);
+	icalparameter_set_xname (param, EVOLUTION_END_DATE_PARAMETER);
+	icalproperty_add_parameter (prop, param);
+}
+
+#ifdef G_OS_WIN32
+#undef e_cal_recur_nth
+static
+#endif
+const const char *e_cal_recur_nth[31] = {
+	N_("1st"),
+	N_("2nd"),
+	N_("3rd"),
+	N_("4th"),
+	N_("5th"),
+	N_("6th"),
+	N_("7th"),
+	N_("8th"),
+	N_("9th"),
+	N_("10th"),
+	N_("11th"),
+	N_("12th"),
+	N_("13th"),
+	N_("14th"),
+	N_("15th"),
+	N_("16th"),
+	N_("17th"),
+	N_("18th"),
+	N_("19th"),
+	N_("20th"),
+	N_("21st"),
+	N_("22nd"),
+	N_("23rd"),
+	N_("24th"),
+	N_("25th"),
+	N_("26th"),
+	N_("27th"),
+	N_("28th"),
+	N_("29th"),
+	N_("30th"),
+	N_("31st")
+};
+
+#ifdef G_OS_WIN32
+
+const char **
+e_cal_get_recur_nth (void)
+{
+	return e_cal_recur_nth;
+}
+
+#endif
Index: calendar/libecal-dbus/e-cal-util.h
===================================================================
--- calendar/libecal-dbus/e-cal-util.h	(revision 409)
+++ calendar/libecal-dbus/e-cal-util.h	(working copy)
@@ -1 +1,143 @@
-link ../libecal/e-cal-util.h
\ No newline at end of file
+/* Evolution calendar utilities and types
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Author: Federico Mena-Quintero <federico@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_CAL_UTIL_H
+#define E_CAL_UTIL_H
+
+#include <libical/ical.h>
+#include <time.h>
+#include <glib.h>
+#include <libecal/e-cal-component.h>
+#include <libecal/e-cal-recur.h>
+
+G_BEGIN_DECLS
+
+
+
+/* Instance of a calendar object.  This can be an actual occurrence, a
+ * recurrence, or an alarm trigger of a `real' calendar object.
+ */
+typedef struct {
+	char *uid;			/* UID of the object */
+	time_t start;			/* Start time of instance */
+	time_t end;			/* End time of instance */
+} CalObjInstance;
+
+void cal_obj_instance_list_free (GList *list);
+
+/* Used for modifying objects */
+typedef enum {
+	CALOBJ_MOD_THIS          = 1 << 0,
+	CALOBJ_MOD_THISANDPRIOR  = 1 << 1,
+	CALOBJ_MOD_THISANDFUTURE = 1 << 2,
+	CALOBJ_MOD_ALL           = 0x07
+} CalObjModType;
+
+/* Used for mode stuff */
+typedef enum {
+	CAL_MODE_INVALID = -1,
+	CAL_MODE_LOCAL   = 1 << 0,
+	CAL_MODE_REMOTE  = 1 << 1,
+	CAL_MODE_ANY     = 0x07
+} CalMode;
+
+#define cal_mode_to_corba(mode) \
+	(mode == CAL_MODE_LOCAL   ? GNOME_Evolution_Calendar_MODE_LOCAL  : \
+	 mode == CAL_MODE_REMOTE  ? GNOME_Evolution_Calendar_MODE_REMOTE : \
+	 GNOME_Evolution_Calendar_MODE_ANY)
+
+void cal_obj_uid_list_free (GList *list);
+
+icalcomponent *e_cal_util_new_top_level (void);
+icalcomponent *e_cal_util_new_component (icalcomponent_kind kind);
+
+icalcomponent *e_cal_util_parse_ics_string (const char *string);
+icalcomponent *e_cal_util_parse_ics_file (const char *filename);
+
+ECalComponentAlarms *e_cal_util_generate_alarms_for_comp (ECalComponent *comp,
+						       time_t start,
+						       time_t end,
+						       ECalComponentAlarmAction *omit,
+						       ECalRecurResolveTimezoneFn resolve_tzid,
+						       gpointer user_data,
+						       icaltimezone *default_timezone);
+int e_cal_util_generate_alarms_for_list (GList *comps,
+				       time_t start,
+				       time_t end,
+				       ECalComponentAlarmAction *omit,
+				       GSList **comp_alarms,
+				       ECalRecurResolveTimezoneFn resolve_tzid,
+				       gpointer user_data,
+				       icaltimezone *default_timezone);
+
+icaltimezone *e_cal_util_resolve_tzid (const char *tzid, gpointer data);
+
+char *e_cal_util_priority_to_string (int priority);
+int e_cal_util_priority_from_string (const char *string);
+
+void e_cal_util_add_timezones_from_component (icalcomponent *vcal_comp,
+					    icalcomponent *icalcomp);
+
+gboolean e_cal_util_component_is_instance (icalcomponent *icalcomp);
+gboolean e_cal_util_component_has_alarms (icalcomponent *icalcomp);
+gboolean e_cal_util_component_has_organizer (icalcomponent *icalcomp);
+gboolean e_cal_util_component_has_recurrences (icalcomponent *icalcomp);
+gboolean e_cal_util_component_has_rdates (icalcomponent *icalcomp);
+gboolean e_cal_util_component_has_rrules (icalcomponent *icalcomp);
+gboolean e_cal_util_component_has_attendee (icalcomponent *icalcomp);
+gboolean e_cal_util_event_dates_match (icalcomponent *icalcomp1, icalcomponent *icalcomp2);
+/* The static capabilities to be supported by backends */
+#define CAL_STATIC_CAPABILITY_NO_ALARM_REPEAT             "no-alarm-repeat"
+#define CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS             "no-audio-alarms"
+#define CAL_STATIC_CAPABILITY_NO_DISPLAY_ALARMS           "no-display-alarms"
+#define CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS             "no-email-alarms"
+#define CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS         "no-procedure-alarms"
+#define CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT          "no-task-assignment"
+#define CAL_STATIC_CAPABILITY_NO_THISANDFUTURE            "no-thisandfuture"
+#define CAL_STATIC_CAPABILITY_NO_THISANDPRIOR             "no-thisandprior"
+#define CAL_STATIC_CAPABILITY_NO_TRANSPARENCY             "no-transparency"
+#define CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY              "one-alarm-only"
+#define CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ATTEND       "organizer-must-attend"
+#define CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS "organizer-not-email-address"
+#define CAL_STATIC_CAPABILITY_REMOVE_ALARMS               "remove-alarms"
+#define CAL_STATIC_CAPABILITY_SAVE_SCHEDULES              "save-schedules"
+#define CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK	  "no-conv-to-assign-task"
+#define CAL_STATIC_CAPABILITY_NO_CONV_TO_RECUR		  "no-conv-to-recur"
+#define CAL_STATIC_CAPABILITY_NO_GEN_OPTIONS		  "no-general-options"
+#define CAL_STATIC_CAPABILITY_REQ_SEND_OPTIONS		  "require-send-options"
+#define CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER       "recurrences-no-master-object"
+#define CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ACCEPT      "organizer-must-accept"
+#define CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED	 "delegate-support"
+#define CAL_STATIC_CAPABILITY_NO_ORGANIZER		 "no-organizer"
+#define CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY		 "delegate-to-many"
+#define CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING     "has-unaccepted-meeting"
+
+/* Recurrent events. Management for instances */
+icalcomponent *e_cal_util_construct_instance (icalcomponent *icalcomp,
+					    struct icaltimetype rid);
+void           e_cal_util_remove_instances (icalcomponent *icalcomp,
+					  struct icaltimetype rid,
+					  CalObjModType mod);
+
+G_END_DECLS
+
+#endif
+
Index: calendar/libecal-dbus/e-cal-recur.h
===================================================================
--- calendar/libecal-dbus/e-cal-recur.h	(revision 409)
+++ calendar/libecal-dbus/e-cal-recur.h	(working copy)
@@ -1 +1,58 @@
-link ../libecal/e-cal-recur.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Evolution calendar recurrence rule functions
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Author: Damon Chaplin <damon@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_CAL_RECUR_H
+#define E_CAL_RECUR_H
+
+#include <glib.h>
+#include <libecal/e-cal-component.h>
+
+G_BEGIN_DECLS
+
+typedef gboolean (* ECalRecurInstanceFn) (ECalComponent *comp,
+					 time_t        instance_start,
+					 time_t        instance_end,
+					 gpointer      data);
+
+typedef icaltimezone* (* ECalRecurResolveTimezoneFn)	(const char   *tzid,
+							 gpointer      data);
+
+void	e_cal_recur_generate_instances	(ECalComponent		*comp,
+					 time_t			 start,
+					 time_t			 end,
+					 ECalRecurInstanceFn	 cb,
+					 gpointer                cb_data,
+					 ECalRecurResolveTimezoneFn tz_cb,
+					 gpointer		   tz_cb_data,
+					 icaltimezone		*default_timezone);
+
+/* Localized nth-day-of-month strings. (Use with _() ) */
+#ifdef G_OS_WIN32
+extern const char **e_cal_get_recur_nth (void);
+#define e_cal_recur_nth (e_cal_get_recur_nth ())
+#else
+extern const char *e_cal_recur_nth[31];
+#endif
+
+G_END_DECLS
+
+#endif
Index: calendar/libecal-dbus/e-cal-component.c
===================================================================
--- calendar/libecal-dbus/e-cal-component.c	(revision 409)
+++ calendar/libecal-dbus/e-cal-component.c	(working copy)
@@ -1 +1,5925 @@
-link ../libecal/e-cal-component.c
\ No newline at end of file
+/* Evolution calendar - iCalendar component object
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Author: Federico Mena-Quintero <federico@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include "e-cal-component.h"
+#include "e-cal-time-util.h"
+
+
+
+#ifdef G_OS_WIN32
+#define getgid() 0
+#define getppid() 0
+#endif
+
+/* Extension property for alarm components so that we can reference them by UID */
+#define EVOLUTION_ALARM_UID_PROPERTY "X-EVOLUTION-ALARM-UID"
+
+
+struct attendee {
+	icalproperty *prop;
+	icalparameter *cutype_param;
+	icalparameter *member_param;
+	icalparameter *role_param;
+	icalparameter *partstat_param;
+	icalparameter *rsvp_param;
+	icalparameter *delto_param;
+	icalparameter *delfrom_param;
+	icalparameter *sentby_param;
+	icalparameter *cn_param;
+	icalparameter *language_param;
+};
+
+struct attachment {
+	icalproperty *prop;
+	icalattach *attach;
+};
+
+struct text {
+	icalproperty *prop;
+	icalparameter *altrep_param;
+};
+
+struct datetime {
+	icalproperty *prop;
+	icalparameter *tzid_param;
+};
+
+struct organizer {
+	icalproperty *prop;
+	icalparameter *sentby_param;
+	icalparameter *cn_param;
+	icalparameter *language_param;
+};
+
+struct period {
+	icalproperty *prop;
+	icalparameter *value_param;
+};
+
+struct recur_id {
+	struct datetime recur_time;
+	
+	icalparameter *range_param;
+};
+
+/* Private part of the CalComponent structure */
+struct _ECalComponentPrivate {
+	/* The icalcomponent we wrap */
+	icalcomponent *icalcomp;
+
+	/* Properties */
+
+	icalproperty *uid;
+
+	icalproperty *status;
+	GSList *attendee_list;
+	
+	icalproperty *categories;
+
+	icalproperty *classification;
+
+	GSList *comment_list; /* list of struct text */
+
+	icalproperty *completed;
+
+	GSList *contact_list; /* list of struct text */
+
+	icalproperty *created;
+
+	GSList *description_list; /* list of struct text */
+
+	struct datetime dtstart;
+	struct datetime dtend;
+
+	icalproperty *dtstamp;
+
+	/* The DURATION property can be used instead of the VEVENT DTEND or
+	   the VTODO DUE dates. We do not use it directly ourselves, but we
+	   must be able to handle it from incoming data. If a DTEND or DUE
+	   is requested, we convert the DURATION if necessary. If DTEND or
+	   DUE is set, we remove any DURATION. */
+	icalproperty *duration;
+
+	struct datetime due;
+
+	GSList *exdate_list; /* list of struct datetime */
+	GSList *exrule_list; /* list of icalproperty objects */
+
+	struct organizer organizer;
+	
+	icalproperty *geo;
+	icalproperty *last_modified;
+	icalproperty *percent;
+	icalproperty *priority;
+
+	struct recur_id recur_id;
+	
+	GSList *rdate_list; /* list of struct period */
+
+	GSList *rrule_list; /* list of icalproperty objects */
+
+	icalproperty *sequence;
+
+	struct {
+		icalproperty *prop;
+		icalparameter *altrep_param;
+	} summary;
+
+	icalproperty *transparency;
+	icalproperty *url;
+	icalproperty *location;
+
+	GSList *attachment_list;
+
+	/* Subcomponents */
+
+	GHashTable *alarm_uid_hash;
+
+	/* Whether we should increment the sequence number when piping the
+	 * object over the wire.
+	 */
+	guint need_sequence_inc : 1;
+};
+
+/* Private structure for alarms */
+struct _ECalComponentAlarm {
+	/* Alarm icalcomponent we wrap */
+	icalcomponent *icalcomp;
+
+	/* Our extension UID property */
+	icalproperty *uid;
+
+	/* Properties */
+
+	icalproperty *action;
+	icalproperty *attach; /* FIXME: see scan_alarm_property() below */
+
+	struct {
+		icalproperty *prop;
+		icalparameter *altrep_param;
+	} description;
+
+	icalproperty *duration;
+	icalproperty *repeat;
+	icalproperty *trigger;
+
+	GSList *attendee_list;
+};
+
+
+
+static void e_cal_component_class_init (ECalComponentClass *klass);
+static void e_cal_component_init (ECalComponent *comp, ECalComponentClass *klass);
+static void e_cal_component_finalize (GObject *object);
+
+static GObjectClass *parent_class;
+
+
+
+/**
+ * e_cal_component_get_type:
+ *
+ * Registers the #ECalComponent class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the #ECalComponent class.
+ **/
+GType
+e_cal_component_get_type (void)
+{
+	static GType e_cal_component_type = 0;
+
+	if (!e_cal_component_type) {
+		static GTypeInfo info = {
+                        sizeof (ECalComponentClass),
+                        (GBaseInitFunc) NULL,
+                        (GBaseFinalizeFunc) NULL,
+                        (GClassInitFunc) e_cal_component_class_init,
+                        NULL, NULL,
+                        sizeof (ECalComponent),
+                        0,
+                        (GInstanceInitFunc) e_cal_component_init
+                };
+		e_cal_component_type = g_type_register_static (G_TYPE_OBJECT, "ECalComponent", &info, 0);
+	}
+
+	return e_cal_component_type;
+}
+
+/* Class initialization function for the calendar component object */
+static void
+e_cal_component_class_init (ECalComponentClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = (GObjectClass *) klass;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = e_cal_component_finalize;
+}
+
+/* Object initialization function for the calendar component object */
+static void
+e_cal_component_init (ECalComponent *comp, ECalComponentClass *klass)
+{
+	ECalComponentPrivate *priv;
+
+	priv = g_new0 (ECalComponentPrivate, 1);
+	comp->priv = priv;
+
+	priv->alarm_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+/* Does a simple g_free() of the elements of a GSList and then frees the list
+ * itself.  Returns NULL.
+ */
+static GSList *
+free_slist (GSList *slist)
+{
+	GSList *l;
+
+	for (l = slist; l; l = l->next)
+		g_free (l->data);
+
+	g_slist_free (slist);
+	return NULL;
+}
+
+/* Used from g_hash_table_foreach_remove() to free the alarm UIDs hash table.
+ * We do not need to do anything to individual elements since we were storing
+ * the UID pointers inside the icalproperties themselves.
+ */
+static gboolean
+free_alarm_cb (gpointer key, gpointer value, gpointer data)
+{
+	return TRUE;
+}
+
+/* Frees the internal icalcomponent only if it does not have a parent.  If it
+ * does, it means we don't own it and we shouldn't free it.
+ */
+static void
+free_icalcomponent (ECalComponent *comp, gboolean free)
+{
+	ECalComponentPrivate *priv;
+	GSList *l;
+	
+	priv = comp->priv;
+
+	if (!priv->icalcomp)
+		return;
+
+	/* Free the icalcomponent */
+
+	if (free && icalcomponent_get_parent (priv->icalcomp) == NULL) {
+		icalcomponent_free (priv->icalcomp);
+		priv->icalcomp = NULL;
+	}
+	
+	/* Free the mappings */
+
+	priv->uid = NULL;
+
+	priv->status = NULL;
+
+	for (l = priv->attachment_list; l != NULL; l = l->next)
+		g_free (l->data);
+	g_slist_free (priv->attachment_list);
+	priv->attachment_list = NULL;
+
+	for (l = priv->attendee_list; l != NULL; l = l->next)
+		g_free (l->data);
+	g_slist_free (priv->attendee_list);
+	priv->attendee_list = NULL;
+	
+	priv->categories = NULL;
+
+	priv->classification = NULL;
+	priv->comment_list = NULL;
+	priv->completed = NULL;
+	priv->contact_list = NULL;
+	priv->created = NULL;
+
+	priv->description_list = free_slist (priv->description_list);
+
+	priv->dtend.prop = NULL;
+	priv->dtend.tzid_param = NULL;
+
+	priv->dtstamp = NULL;
+
+	priv->dtstart.prop = NULL;
+	priv->dtstart.tzid_param = NULL;
+
+	priv->due.prop = NULL;
+	priv->due.tzid_param = NULL;
+
+	priv->duration = NULL;
+
+	priv->exdate_list = free_slist (priv->exdate_list);
+
+	g_slist_free (priv->exrule_list);
+	priv->exrule_list = NULL;
+
+	priv->geo = NULL;
+	priv->last_modified = NULL;
+	priv->percent = NULL;
+	priv->priority = NULL;
+
+	priv->rdate_list = free_slist (priv->rdate_list);
+
+	g_slist_free (priv->rrule_list);
+	priv->rrule_list = NULL;
+
+	priv->sequence = NULL;
+
+	priv->summary.prop = NULL;
+	priv->summary.altrep_param = NULL;
+
+	priv->transparency = NULL;
+	priv->url = NULL;
+	priv->location = NULL;
+
+	/* Free the subcomponents */
+
+	g_hash_table_foreach_remove (priv->alarm_uid_hash, free_alarm_cb, NULL);
+
+	/* Clean up */
+
+	priv->need_sequence_inc = FALSE;
+}
+
+/* Finalize handler for the calendar component object */
+static void
+e_cal_component_finalize (GObject *object)
+{
+	ECalComponent *comp;
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (object));
+
+	comp = E_CAL_COMPONENT (object);
+	priv = comp->priv;
+
+	free_icalcomponent (comp, TRUE);
+	g_hash_table_destroy (priv->alarm_uid_hash);
+	priv->alarm_uid_hash = NULL;
+
+	g_free (priv);
+	comp->priv = NULL;
+
+	if (G_OBJECT_CLASS (parent_class)->finalize)
+		(* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+
+/**
+ * e_cal_component_gen_uid:
+ *
+ * Generates a unique identifier suitable for calendar components.
+ *
+ * Return value: A unique identifier string.  Every time this function is called
+ * a different string is returned.
+ **/
+char *
+e_cal_component_gen_uid (void)
+{
+        char *iso, *ret;
+	static char *hostname;
+	time_t t = time (NULL);
+	static int serial;
+
+	if (!hostname) {
+#ifndef G_OS_WIN32
+		static char buffer [512];
+
+		if ((gethostname (buffer, sizeof (buffer) - 1) == 0) &&
+		    (buffer [0] != 0))
+			hostname = buffer;
+		else
+			hostname = "localhost";
+#else
+		hostname = g_get_host_name ();
+#endif
+	}
+
+	iso = isodate_from_time_t (t);
+	ret = g_strdup_printf ("%s-%d-%d-%d-%d@%s",
+			       iso,
+			       getpid (),
+			       getgid (),
+			       getppid (),
+			       serial++,
+			       hostname);
+	g_free (iso);
+
+	return ret;
+}
+
+/**
+ * e_cal_component_new:
+ *
+ * Creates a new empty calendar component object.  Once created, you should set it from an
+ * existing #icalcomponent structure by using e_cal_component_set_icalcomponent() or with a
+ * new empty component type by using e_cal_component_set_new_vtype().
+ *
+ * Return value: A newly-created calendar component object.
+ **/
+ECalComponent *
+e_cal_component_new (void)
+{
+	return E_CAL_COMPONENT (g_object_new (E_TYPE_CAL_COMPONENT, NULL));
+}
+
+/**
+ * e_cal_component_new_from_string:
+ * @calobj: A string representation of an iCalendar component.
+ *
+ * Creates a new calendar component object from the given iCalendar string.
+ *
+ * Return value: A calendar component representing the given iCalendar string on
+ * success, NULL if there was an error.
+ **/
+ECalComponent *
+e_cal_component_new_from_string (const char *calobj)
+{
+	ECalComponent *comp;
+	icalcomponent *icalcomp;
+
+	g_return_val_if_fail (calobj != NULL, NULL);
+
+	icalcomp = icalparser_parse_string (calobj);
+	if (!icalcomp)
+		return NULL;
+
+	comp = e_cal_component_new ();
+	if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
+		icalcomponent_free (icalcomp);
+		g_object_unref (comp);
+
+		return NULL;
+	}
+
+	return comp;
+}
+
+/**
+ * e_cal_component_clone:
+ * @comp: A calendar component object.
+ *
+ * Creates a new calendar component object by copying the information from
+ * another one.
+ *
+ * Return value: A newly-created calendar component with the same values as the
+ * original one.
+ **/
+ECalComponent *
+e_cal_component_clone (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+	ECalComponent *new_comp;
+	icalcomponent *new_icalcomp;
+
+	g_return_val_if_fail (comp != NULL, NULL);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->need_sequence_inc == FALSE, NULL);
+
+	new_comp = e_cal_component_new ();
+
+	if (priv->icalcomp) {
+		new_icalcomp = icalcomponent_new_clone (priv->icalcomp);
+		e_cal_component_set_icalcomponent (new_comp, new_icalcomp);
+	}
+
+	return new_comp;
+}
+
+/* Scans an attachment property */
+static void
+scan_attachment (GSList **attachment_list, icalproperty *prop) 
+{
+	struct attachment *attachment;
+	
+	attachment = g_new (struct attachment, 1);
+	attachment->prop = prop;
+
+	attachment->attach = icalproperty_get_attach (prop);
+
+	*attachment_list = g_slist_append (*attachment_list, attachment);
+}
+
+/* Scans an attendee property */
+static void
+scan_attendee (GSList **attendee_list, icalproperty *prop) 
+{
+	struct attendee *attendee;
+	
+	attendee = g_new (struct attendee, 1);
+	attendee->prop = prop;
+
+	attendee->cutype_param = icalproperty_get_first_parameter (prop, ICAL_CUTYPE_PARAMETER);
+	attendee->member_param = icalproperty_get_first_parameter (prop, ICAL_MEMBER_PARAMETER);
+	attendee->role_param = icalproperty_get_first_parameter (prop, ICAL_ROLE_PARAMETER);
+	attendee->partstat_param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
+	attendee->rsvp_param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
+	attendee->delto_param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDTO_PARAMETER);
+	attendee->delfrom_param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER);
+	attendee->sentby_param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
+	attendee->cn_param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+	attendee->language_param = icalproperty_get_first_parameter (prop, ICAL_LANGUAGE_PARAMETER);
+	
+	*attendee_list = g_slist_append (*attendee_list, attendee);
+}
+
+/* Scans a date/time and timezone pair property */
+static void
+scan_datetime (ECalComponent *comp, struct datetime *datetime, icalproperty *prop)
+{
+	ECalComponentPrivate *priv;
+
+	priv = comp->priv;
+
+	datetime->prop = prop;
+	datetime->tzid_param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
+}
+
+/* Scans an exception date property */
+static void
+scan_exdate (ECalComponent *comp, icalproperty *prop)
+{
+	ECalComponentPrivate *priv;
+	struct datetime *dt;
+
+	priv = comp->priv;
+
+	dt = g_new (struct datetime, 1);
+	dt->prop = prop;
+	dt->tzid_param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
+
+	priv->exdate_list = g_slist_append (priv->exdate_list, dt);
+}
+
+/* Scans and attendee property */
+static void
+scan_organizer (ECalComponent *comp, struct organizer *organizer, icalproperty *prop) 
+{
+	organizer->prop = prop;
+
+	organizer->sentby_param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
+	organizer->cn_param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+	organizer->language_param = icalproperty_get_first_parameter (prop, ICAL_LANGUAGE_PARAMETER);
+}
+
+/* Scans an icalperiodtype property */
+static void
+scan_period (ECalComponent *comp, GSList **list, icalproperty *prop)
+{
+	struct period *period;
+
+	period = g_new (struct period, 1);
+	period->prop = prop;
+	period->value_param = icalproperty_get_first_parameter (prop, ICAL_VALUE_PARAMETER);
+
+	*list = g_slist_append (*list, period);
+}
+
+
+/* Scans an icalrecurtype property */
+static void
+scan_recur_id (ECalComponent *comp, struct recur_id *recur_id, icalproperty *prop)
+{
+	scan_datetime (comp, &recur_id->recur_time, prop);
+
+	recur_id->range_param = icalproperty_get_first_parameter (prop, ICAL_RANGE_PARAMETER);
+}
+
+/* Scans an icalrecurtype property */
+static void
+scan_recur (ECalComponent *comp, GSList **list, icalproperty *prop)
+{
+	*list = g_slist_append (*list, prop);
+}
+
+/* Scans the summary property */
+static void
+scan_summary (ECalComponent *comp, icalproperty *prop)
+{
+	ECalComponentPrivate *priv;
+
+	priv = comp->priv;
+
+	priv->summary.prop = prop;
+	priv->summary.altrep_param = icalproperty_get_first_parameter (prop, ICAL_ALTREP_PARAMETER);
+}
+
+/* Scans a text (i.e. text + altrep) property */
+static void
+scan_text (ECalComponent *comp, GSList **text_list, icalproperty *prop)
+{
+	struct text *text;
+
+	text = g_new (struct text, 1);
+	text->prop = prop;
+	text->altrep_param = icalproperty_get_first_parameter (prop, ICAL_ALTREP_PARAMETER);
+
+	*text_list = g_slist_append (*text_list, text);
+}
+
+/* Scans an icalproperty and adds its mapping to the component */
+static void
+scan_property (ECalComponent *comp, icalproperty *prop)
+{
+	ECalComponentPrivate *priv;
+	icalproperty_kind kind;
+
+	priv = comp->priv;
+
+	kind = icalproperty_isa (prop);
+
+	switch (kind) {
+	case ICAL_STATUS_PROPERTY:
+		priv->status = prop;
+		break;
+
+	case ICAL_ATTACH_PROPERTY:
+		scan_attachment (&priv->attachment_list, prop);
+		break;
+
+	case ICAL_ATTENDEE_PROPERTY:
+		scan_attendee (&priv->attendee_list, prop);
+		break;
+
+	case ICAL_CATEGORIES_PROPERTY:
+		priv->categories = prop;
+		break;
+
+	case ICAL_CLASS_PROPERTY:
+		priv->classification = prop;
+		break;
+
+	case ICAL_COMMENT_PROPERTY:
+		scan_text (comp, &priv->comment_list, prop);
+		break;
+
+	case ICAL_COMPLETED_PROPERTY:
+		priv->completed = prop;
+		break;
+
+	case ICAL_CONTACT_PROPERTY:
+		scan_text (comp, &priv->contact_list, prop);
+		break;
+
+	case ICAL_CREATED_PROPERTY:
+		priv->created = prop;
+		break;
+
+	case ICAL_DESCRIPTION_PROPERTY:
+		scan_text (comp, &priv->description_list, prop);
+		break;
+
+	case ICAL_DTEND_PROPERTY:
+		scan_datetime (comp, &priv->dtend, prop);
+		break;
+
+	case ICAL_DTSTAMP_PROPERTY:
+		priv->dtstamp = prop;
+		break;
+
+	case ICAL_DTSTART_PROPERTY:
+		scan_datetime (comp, &priv->dtstart, prop);
+		break;
+
+	case ICAL_DUE_PROPERTY:
+		scan_datetime (comp, &priv->due, prop);
+		break;
+
+	case ICAL_DURATION_PROPERTY:
+		priv->duration = prop;
+		break;
+
+	case ICAL_EXDATE_PROPERTY:
+		scan_exdate (comp, prop);
+		break;
+
+	case ICAL_EXRULE_PROPERTY:
+		scan_recur (comp, &priv->exrule_list, prop);
+		break;
+
+	case ICAL_GEO_PROPERTY:
+		priv->geo = prop;
+		break;
+
+	case ICAL_LASTMODIFIED_PROPERTY:
+		priv->last_modified = prop;
+		break;
+
+	case ICAL_ORGANIZER_PROPERTY:
+		scan_organizer (comp, &priv->organizer, prop);
+		break;
+
+	case ICAL_PERCENTCOMPLETE_PROPERTY:
+		priv->percent = prop;
+		break;
+
+	case ICAL_PRIORITY_PROPERTY:
+		priv->priority = prop;
+		break;
+
+	case ICAL_RECURRENCEID_PROPERTY:
+		scan_recur_id (comp, &priv->recur_id, prop);
+		break;
+
+	case ICAL_RDATE_PROPERTY:
+		scan_period (comp, &priv->rdate_list, prop);
+		break;
+
+	case ICAL_RRULE_PROPERTY:
+		scan_recur (comp, &priv->rrule_list, prop);
+		break;
+
+	case ICAL_SEQUENCE_PROPERTY:
+		priv->sequence = prop;
+		break;
+
+	case ICAL_SUMMARY_PROPERTY:
+		scan_summary (comp, prop);
+		break;
+
+	case ICAL_TRANSP_PROPERTY:
+		priv->transparency = prop;
+		break;
+
+	case ICAL_UID_PROPERTY:
+		priv->uid = prop;
+		break;
+
+	case ICAL_URL_PROPERTY:
+		priv->url = prop;
+		break;
+
+	case ICAL_LOCATION_PROPERTY :
+		priv->location = prop;
+		break;
+
+	default:
+		break;
+	}
+}
+
+/* Gets our alarm UID string from a property that is known to contain it */
+static const char *
+alarm_uid_from_prop (icalproperty *prop)
+{
+	const char *xstr;
+
+	g_assert (icalproperty_isa (prop) == ICAL_X_PROPERTY);
+
+	xstr = icalproperty_get_x (prop);
+	g_assert (xstr != NULL);
+
+	return xstr;
+}
+
+/* Sets our alarm UID extension property on an alarm component.  Returns a
+ * pointer to the UID string inside the property itself.
+ */
+static const char *
+set_alarm_uid (icalcomponent *alarm, const char *auid)
+{
+	icalproperty *prop;
+	const char *inprop_auid;
+
+	/* Create the new property */
+
+	prop = icalproperty_new_x ((char *) auid);
+	icalproperty_set_x_name (prop, EVOLUTION_ALARM_UID_PROPERTY);
+
+	icalcomponent_add_property (alarm, prop);
+
+	inprop_auid = alarm_uid_from_prop (prop);
+	return inprop_auid;
+}
+
+/* Removes any alarm UID extension properties from an alarm subcomponent */
+static void
+remove_alarm_uid (icalcomponent *alarm)
+{
+	icalproperty *prop;
+	GSList *list, *l;
+
+	list = NULL;
+
+	for (prop = icalcomponent_get_first_property (alarm, ICAL_X_PROPERTY);
+	     prop;
+	     prop = icalcomponent_get_next_property (alarm, ICAL_X_PROPERTY)) {
+		const char *xname;
+
+		xname = icalproperty_get_x_name (prop);
+		g_assert (xname != NULL);
+
+		if (strcmp (xname, EVOLUTION_ALARM_UID_PROPERTY) == 0)
+			list = g_slist_prepend (list, prop);
+	}
+
+	for (l = list; l; l = l->next) {
+		prop = l->data;
+		icalcomponent_remove_property (alarm, prop);
+		icalproperty_free (prop);
+	}
+
+	g_slist_free (list);
+}
+
+/* Adds an alarm subcomponent to the calendar component's mapping table.  The
+ * actual UID with which it gets added may not be the same as the specified one;
+ * this function will change it if the table already had an alarm subcomponent
+ * with the specified UID.  Returns the actual UID used.
+ */
+static const char *
+add_alarm (ECalComponent *comp, icalcomponent *alarm, const char *auid)
+{
+	ECalComponentPrivate *priv;
+	icalcomponent *old_alarm;
+
+	priv = comp->priv;
+
+	/* First we see if we already have an alarm with the requested UID.  In
+	 * that case, we need to change the new UID to something else.  This
+	 * should never happen, but who knows.
+	 */
+
+	old_alarm = g_hash_table_lookup (priv->alarm_uid_hash, auid);
+	if (old_alarm != NULL) {
+		char *new_auid;
+
+		g_message ("add_alarm(): Got alarm with duplicated UID `%s', changing it...", auid);
+
+		remove_alarm_uid (alarm);
+
+		new_auid = e_cal_component_gen_uid ();
+		auid = set_alarm_uid (alarm, new_auid);
+		g_free (new_auid);
+	}
+
+	g_hash_table_insert (priv->alarm_uid_hash, (char *) auid, alarm);
+	return auid;
+}
+
+/* Scans an alarm subcomponent, adds an UID extension property to it (so that we
+ * can reference alarms by unique IDs), and adds its mapping to the component.  */
+static void
+scan_alarm (ECalComponent *comp, icalcomponent *alarm)
+{
+	ECalComponentPrivate *priv;
+	icalproperty *prop;
+	const char *auid;
+	char *new_auid;
+
+	priv = comp->priv;
+
+	for (prop = icalcomponent_get_first_property (alarm, ICAL_X_PROPERTY);
+	     prop;
+	     prop = icalcomponent_get_next_property (alarm, ICAL_X_PROPERTY)) {
+		const char *xname;
+
+		xname = icalproperty_get_x_name (prop);
+		g_assert (xname != NULL);
+
+		if (strcmp (xname, EVOLUTION_ALARM_UID_PROPERTY) == 0) {
+			auid = alarm_uid_from_prop (prop);
+			add_alarm (comp, alarm, auid);
+			return;
+		}
+	}
+
+	/* The component has no alarm UID property, so we create one. */
+
+	new_auid = e_cal_component_gen_uid ();
+	auid = set_alarm_uid (alarm, new_auid);
+	g_free (new_auid);
+
+	add_alarm (comp, alarm, auid);
+}
+
+/* Scans an icalcomponent for its properties so that we can provide
+ * random-access to them.  It also builds a hash table of the component's alarm
+ * subcomponents.
+ */
+static void
+scan_icalcomponent (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+	icalproperty *prop;
+	icalcompiter iter;
+
+	priv = comp->priv;
+
+	g_assert (priv->icalcomp != NULL);
+
+	/* Scan properties */
+
+	for (prop = icalcomponent_get_first_property (priv->icalcomp, ICAL_ANY_PROPERTY);
+	     prop;
+	     prop = icalcomponent_get_next_property (priv->icalcomp, ICAL_ANY_PROPERTY))
+		scan_property (comp, prop);
+
+	/* Scan subcomponents */
+
+	for (iter = icalcomponent_begin_component (priv->icalcomp, ICAL_VALARM_COMPONENT);
+	     icalcompiter_deref (&iter) != NULL;
+	     icalcompiter_next (&iter)) {
+		icalcomponent *subcomp;
+
+		subcomp = icalcompiter_deref (&iter);
+		scan_alarm (comp, subcomp);
+	}
+}
+
+/* Ensures that the mandatory calendar component properties (uid, dtstamp) do
+ * exist.  If they don't exist, it creates them automatically.
+ */
+static void
+ensure_mandatory_properties (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	priv = comp->priv;
+	g_assert (priv->icalcomp != NULL);
+
+	if (!priv->uid) {
+		char *uid;
+
+		uid = e_cal_component_gen_uid ();
+		priv->uid = icalproperty_new_uid (uid);
+		g_free (uid);
+
+		icalcomponent_add_property (priv->icalcomp, priv->uid);
+	}
+
+	if (!priv->dtstamp) {
+		struct icaltimetype t;
+
+		t = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+
+		priv->dtstamp = icalproperty_new_dtstamp (t);
+		icalcomponent_add_property (priv->icalcomp, priv->dtstamp);
+	}
+}
+
+/**
+ * e_cal_component_set_new_vtype:
+ * @comp: A calendar component object.
+ * @type: Type of calendar component to create.
+ *
+ * Clears any existing component data from a calendar component object and
+ * creates a new #icalcomponent of the specified type for it.  The only property
+ * that will be set in the new component will be its unique identifier.
+ **/
+void
+e_cal_component_set_new_vtype (ECalComponent *comp, ECalComponentVType type)
+{
+	ECalComponentPrivate *priv;
+	icalcomponent *icalcomp;
+	icalcomponent_kind kind;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+
+	free_icalcomponent (comp, TRUE);
+
+	if (type == E_CAL_COMPONENT_NO_TYPE)
+		return;
+
+	/* Figure out the kind and create the icalcomponent */
+
+	switch (type) {
+	case E_CAL_COMPONENT_EVENT:
+		kind = ICAL_VEVENT_COMPONENT;
+		break;
+
+	case E_CAL_COMPONENT_TODO:
+		kind = ICAL_VTODO_COMPONENT;
+		break;
+
+	case E_CAL_COMPONENT_JOURNAL:
+		kind = ICAL_VJOURNAL_COMPONENT;
+		break;
+
+	case E_CAL_COMPONENT_FREEBUSY:
+		kind = ICAL_VFREEBUSY_COMPONENT;
+		break;
+
+	case E_CAL_COMPONENT_TIMEZONE:
+		kind = ICAL_VTIMEZONE_COMPONENT;
+		break;
+
+	default:
+		g_assert_not_reached ();
+		kind = ICAL_NO_COMPONENT;
+	}
+
+	icalcomp = icalcomponent_new (kind);
+	if (!icalcomp) {
+		g_message ("e_cal_component_set_new_vtype(): Could not create the icalcomponent!");
+		return;
+	}
+
+	/* Scan the component to build our mapping table */
+
+	priv->icalcomp = icalcomp;
+	scan_icalcomponent (comp);
+
+	/* Add missing stuff */
+
+	ensure_mandatory_properties (comp);
+}
+
+/**
+ * e_cal_component_set_icalcomponent:
+ * @comp: A calendar component object.
+ * @icalcomp: An #icalcomponent.
+ *
+ * Sets the contents of a calendar component object from an #icalcomponent
+ * structure.  If the @comp already had an #icalcomponent set into it, it will
+ * will be freed automatically if the #icalcomponent does not have a parent
+ * component itself.
+ *
+ * Supported component types are VEVENT, VTODO, VJOURNAL, VFREEBUSY, and VTIMEZONE.
+ *
+ * Return value: TRUE on success, FALSE if @icalcomp is an unsupported component
+ * type.
+ **/
+gboolean
+e_cal_component_set_icalcomponent (ECalComponent *comp, icalcomponent *icalcomp)
+{
+	ECalComponentPrivate *priv;
+	icalcomponent_kind kind;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+
+	if (priv->icalcomp == icalcomp)
+		return TRUE;
+
+	free_icalcomponent (comp, TRUE);
+
+	if (!icalcomp) {
+		priv->icalcomp = NULL;
+		return TRUE;
+	}
+
+	kind = icalcomponent_isa (icalcomp);
+
+	if (!(kind == ICAL_VEVENT_COMPONENT
+	      || kind == ICAL_VTODO_COMPONENT
+	      || kind == ICAL_VJOURNAL_COMPONENT
+	      || kind == ICAL_VFREEBUSY_COMPONENT
+	      || kind == ICAL_VTIMEZONE_COMPONENT))
+		return FALSE;
+
+	priv->icalcomp = icalcomp;
+
+	scan_icalcomponent (comp);
+	ensure_mandatory_properties (comp);
+
+	return TRUE;
+}
+
+/**
+ * e_cal_component_get_icalcomponent:
+ * @comp: A calendar component object.
+ *
+ * Queries the #icalcomponent structure that a calendar component object is
+ * wrapping.
+ *
+ * Return value: An #icalcomponent structure, or NULL if the @comp has no
+ * #icalcomponent set to it.
+ **/
+icalcomponent *
+e_cal_component_get_icalcomponent (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, NULL);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->need_sequence_inc == FALSE, NULL);
+
+	return priv->icalcomp;
+}
+
+/**
+ * e_cal_component_rescan:
+ * @comp: A calendar component object.
+ *
+ * Rescans the #icalcomponent being wrapped by the given calendar component. This
+ * would replace any value that was changed in the wrapped #icalcomponent.
+ */
+void
+e_cal_component_rescan (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+
+	/* Clear everything out */
+	free_icalcomponent (comp, FALSE);
+
+	/* Rescan */
+	scan_icalcomponent (comp);
+	ensure_mandatory_properties (comp);
+}
+
+/**
+ * e_cal_component_strip_errors:
+ * @comp: A calendar component object.
+ *
+ * Strips all error messages from the calendar component. Those error messages are
+ * added to the iCalendar string representation whenever an invalid is used for
+ * one of its fields.
+ */
+void
+e_cal_component_strip_errors (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+	
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+
+	icalcomponent_strip_errors (priv->icalcomp);
+}
+
+/**
+ * e_cal_component_get_vtype:
+ * @comp: A calendar component object.
+ *
+ * Queries the type of a calendar component object.
+ *
+ * Return value: The type of the component, as defined by RFC 2445.
+ **/
+ECalComponentVType
+e_cal_component_get_vtype (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+	icalcomponent_kind kind;
+
+	g_return_val_if_fail (comp != NULL, E_CAL_COMPONENT_NO_TYPE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), E_CAL_COMPONENT_NO_TYPE);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, E_CAL_COMPONENT_NO_TYPE);
+
+	kind = icalcomponent_isa (priv->icalcomp);
+	switch (kind) {
+	case ICAL_VEVENT_COMPONENT:
+		return E_CAL_COMPONENT_EVENT;
+
+	case ICAL_VTODO_COMPONENT:
+		return E_CAL_COMPONENT_TODO;
+
+	case ICAL_VJOURNAL_COMPONENT:
+		return E_CAL_COMPONENT_JOURNAL;
+
+	case ICAL_VFREEBUSY_COMPONENT:
+		return E_CAL_COMPONENT_FREEBUSY;
+
+	case ICAL_VTIMEZONE_COMPONENT:
+		return E_CAL_COMPONENT_TIMEZONE;
+
+	default:
+		/* We should have been loaded with a supported type! */
+		g_assert_not_reached ();
+		return E_CAL_COMPONENT_NO_TYPE;
+	}
+}
+
+/**
+ * e_cal_component_get_as_string:
+ * @comp: A calendar component.
+ *
+ * Gets the iCalendar string representation of a calendar component.  You should
+ * call e_cal_component_commit_sequence() before this function to ensure that the
+ * component's sequence number is consistent with the state of the object.
+ *
+ * Return value: String representation of the calendar component according to
+ * RFC 2445.
+ **/
+char *
+e_cal_component_get_as_string (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+	char *str, *buf;
+
+	g_return_val_if_fail (comp != NULL, NULL);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, NULL);
+
+	/* Ensure that the user has committed the new SEQUENCE */
+	g_return_val_if_fail (priv->need_sequence_inc == FALSE, NULL);
+
+	/* We dup the string; libical owns that memory */
+
+	str = icalcomponent_as_ical_string (priv->icalcomp);
+
+	if (str)
+		buf = g_strdup (str);
+	else
+		buf = NULL;
+
+	return buf;
+}
+
+/* Used from g_hash_table_foreach(); ensures that an alarm subcomponent
+ * has the mandatory properties it needs.
+ */
+static void
+ensure_alarm_properties_cb (gpointer key, gpointer value, gpointer data)
+{
+	ECalComponent *comp;
+	ECalComponentPrivate *priv;
+	icalcomponent *alarm;
+	icalproperty *prop;
+	enum icalproperty_action action;
+	const char *str;
+
+	alarm = value;
+
+	comp = E_CAL_COMPONENT (data);
+	priv = comp->priv;
+
+	prop = icalcomponent_get_first_property (alarm, ICAL_ACTION_PROPERTY);
+	if (!prop)
+		return;
+
+	action = icalproperty_get_action (prop);
+
+	switch (action) {
+	case ICAL_ACTION_DISPLAY:
+		/* Ensure we have a DESCRIPTION property */
+		prop = icalcomponent_get_first_property (alarm, ICAL_DESCRIPTION_PROPERTY);
+		if (prop) {
+			if (priv->summary.prop) {
+				icalproperty *xprop;
+
+				xprop = icalcomponent_get_first_property (alarm, ICAL_X_PROPERTY);
+				while (xprop) {
+					str = icalproperty_get_x_name (xprop);
+					if (!strcmp (str, "X-EVOLUTION-NEEDS-DESCRIPTION")) {
+						icalproperty_set_description (prop, icalproperty_get_summary(priv->summary.prop));
+
+						icalcomponent_remove_property (alarm, xprop);
+						icalproperty_free (xprop);
+						break;
+					}
+
+					xprop = icalcomponent_get_next_property (alarm, ICAL_X_PROPERTY);
+				}
+
+				break;
+			}
+		}
+
+		if (!priv->summary.prop) {
+			str = _("Untitled appointment");
+
+			/* add the X-EVOLUTION-NEEDS-DESCRIPTION property */
+			prop = icalproperty_new_x ("1");
+			icalproperty_set_x_name (prop, "X-EVOLUTION-NEEDS-DESCRIPTION");
+			icalcomponent_add_property (alarm, prop);
+		} else
+			str = icalproperty_get_summary (priv->summary.prop);
+
+		prop = icalproperty_new_description (str);
+		icalcomponent_add_property (alarm, prop);
+
+		break;
+
+	default:
+		break;
+		/* FIXME: add other action types here */
+	}
+}
+
+/* Ensures that alarm subcomponents have the mandatory properties they need,
+ * even when clients may not have set them properly.
+ */
+static void
+ensure_alarm_properties (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	priv = comp->priv;
+
+	g_hash_table_foreach (priv->alarm_uid_hash, ensure_alarm_properties_cb, comp);
+}
+
+/**
+ * e_cal_component_commit_sequence:
+ * @comp: A calendar component object.
+ *
+ * Increments the sequence number property in a calendar component object if it
+ * needs it.  This needs to be done when any of a number of properties listed in
+ * RFC 2445 change values, such as the start and end dates of a component.
+ *
+ * This function must be called before calling e_cal_component_get_as_string() to
+ * ensure that the component is fully consistent.
+ **/
+void
+e_cal_component_commit_sequence (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	ensure_alarm_properties (comp);
+
+	if (!priv->need_sequence_inc)
+		return;
+
+	if (priv->sequence) {
+		int seq;
+
+		seq = icalproperty_get_sequence (priv->sequence);
+		icalproperty_set_sequence (priv->sequence, seq + 1);
+	} else {
+		/* The component had no SEQUENCE property, so assume that the
+		 * default would have been zero.  Since it needed incrementing
+		 * anyways, we use a value of 1 here.
+		 */
+		priv->sequence = icalproperty_new_sequence (1);
+		icalcomponent_add_property (priv->icalcomp, priv->sequence);
+	}
+
+	priv->need_sequence_inc = FALSE;
+}
+
+/**
+ * e_cal_component_abort_sequence:
+ * @comp: A calendar component object.
+ *
+ * Aborts the sequence change needed in the given calendar component, which
+ * means it will not require a sequence commit (via #e_cal_component_commit_sequence)
+ * even if the changes done require a sequence increment.
+ */
+void
+e_cal_component_abort_sequence (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+
+	priv->need_sequence_inc = FALSE;
+}
+
+/**
+ * e_cal_component_get_uid:
+ * @comp: A calendar component object.
+ * @uid: Return value for the UID string.
+ *
+ * Queries the unique identifier of a calendar component object.
+ **/
+void
+e_cal_component_get_uid (ECalComponent *comp, const char **uid)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (uid != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	/* This MUST exist, since we ensured that it did */
+	g_assert (priv->uid != NULL);
+
+	*uid = icalproperty_get_uid (priv->uid);
+}
+
+/**
+ * e_cal_component_set_uid:
+ * @comp: A calendar component object.
+ * @uid: Unique identifier.
+ *
+ * Sets the unique identifier string of a calendar component object.
+ **/
+void
+e_cal_component_set_uid (ECalComponent *comp, const char *uid)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (uid != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	/* This MUST exist, since we ensured that it did */
+	g_assert (priv->uid != NULL);
+
+	icalproperty_set_uid (priv->uid, (char *) uid);
+}
+
+/* Gets a text list value */
+static void
+get_attachment_list (GSList *attachment_list, GSList **al)
+{
+	GSList *l;
+
+	*al = NULL;
+
+	if (!attachment_list)
+		return;
+
+	for (l = attachment_list; l; l = l->next) {
+		struct attachment *attachment;
+		const char *data;
+
+		attachment = l->data;
+		g_assert (attachment->attach != NULL);
+
+		if (icalattach_get_is_url (attachment->attach)) {
+			/* FIXME : this ref count is screwed up
+			 * These structures are being leaked.
+			 */   
+			icalattach_ref (attachment->attach);
+			data = icalattach_get_url (attachment->attach);
+		}
+		else
+			data = NULL;
+		*al = g_slist_prepend (*al, (char *)data);
+	}
+
+	*al = g_slist_reverse (*al);
+}
+
+
+static void
+set_attachment_list (icalcomponent *icalcomp,
+		   GSList **attachment_list,
+		   GSList *al)
+{
+	GSList *l;
+
+	/* Remove old attachments */
+
+	if (*attachment_list) {
+		for (l = *attachment_list; l; l = l->next) {
+			struct attachment *attachment;
+
+			attachment = l->data;
+			g_assert (attachment->prop != NULL);
+			g_assert (attachment->attach != NULL);
+
+			icalcomponent_remove_property (icalcomp, attachment->prop);
+			icalproperty_free (attachment->prop);
+			g_free (attachment);
+		}
+
+		g_slist_free (*attachment_list);
+		*attachment_list = NULL;
+	}
+	/* Add in new attachments */
+
+	for (l = al; l; l = l->next) {
+		struct attachment *attachment;
+
+		attachment = g_new0 (struct attachment, 1);
+		attachment->attach = icalattach_new_from_url ((char *) l->data); 	
+		attachment->prop = icalproperty_new_attach (attachment->attach);
+		icalcomponent_add_property (icalcomp, attachment->prop);
+
+		*attachment_list = g_slist_prepend (*attachment_list, attachment);
+	}
+
+	*attachment_list = g_slist_reverse (*attachment_list);
+}
+
+
+/**
+ * e_cal_component_get_attachment_list: 
+ * @comp: A calendar component object. 
+ * @attachment_list: Return list of URLS to attachments.
+ * 
+ * Queries the attachment properties of the calendar component object. When done,
+ * the @attachment_list should be freed by calling #g_slist_free.
+ **/
+void
+e_cal_component_get_attachment_list (ECalComponent *comp, GSList **attachment_list)
+{
+	ECalComponentPrivate *priv;
+	
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (attachment_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_attachment_list (priv->attachment_list, attachment_list);
+}
+
+/**
+ * e_cal_component_set_attachment_list:
+ * @comp: A calendar component object. 
+ * @attachment_list: list of urls to attachment pointers.
+ *
+ * This currently handles only attachments that are urls
+ * in the file system - not inline binaries.
+ *
+ * Sets the attachments of a calendar component object
+ **/
+void
+e_cal_component_set_attachment_list (ECalComponent *comp, GSList *attachment_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_attachment_list (priv->icalcomp, &priv->attachment_list, attachment_list);
+}
+
+/**
+ * e_cal_component_has_attachments:
+ * @comp: A calendar component object.
+ *
+ * Queries the component to see if it has attachments.
+ *
+ * Return value: TRUE if there are attachments, FALSE otherwise.
+ */
+gboolean
+e_cal_component_has_attachments (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+
+	if (g_slist_length (priv->attachment_list) > 0)
+		return TRUE;
+	
+	return FALSE;
+}
+
+
+int 
+e_cal_component_get_num_attachments (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, 0);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), 0);
+
+	priv = comp->priv;
+
+	return g_slist_length (priv->attachment_list) > 0;
+	
+}
+
+/**
+ * e_cal_component_get_categories:
+ * @comp: A calendar component object.
+ * @categories: Return holder for the categories.
+ *
+ * Queries the categories of the given calendar component. The categories
+ * are returned in the @categories argument, which, on success, will contain
+ * a comma-separated list of all categories set in the component.
+ **/
+void
+e_cal_component_get_categories (ECalComponent *comp, const char **categories)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (categories != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (priv->categories)
+		*categories = icalproperty_get_categories (priv->categories);
+	else
+		*categories = NULL;
+}
+
+/**
+ * e_cal_component_set_categories:
+ * @comp: A calendar component object.
+ * @categories: Comma-separated list of categories.
+ *
+ * Sets the list of categories for a calendar component.
+ **/
+void
+e_cal_component_set_categories (ECalComponent *comp, const char *categories)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!categories || !(*categories)) {
+		if (priv->categories) {
+			icalcomponent_remove_property (priv->icalcomp, priv->categories);
+			icalproperty_free (priv->categories);
+			priv->url = NULL;
+		}
+
+		return;
+	}
+
+	if (priv->categories)
+		icalproperty_set_categories (priv->categories, (char *) categories);
+	else {
+		priv->categories = icalproperty_new_categories ((char *) categories);
+		icalcomponent_add_property (priv->icalcomp, priv->categories);
+	}
+}
+
+
+/**
+ * e_cal_component_get_categories_list:
+ * @comp: A calendar component object.
+ * @categ_list: Return value for the list of strings, where each string is a
+ * category. This should be freed using e_cal_component_free_categories_list().
+ *
+ * Queries the list of categories of a calendar component object.  Each element
+ * in the returned categ_list is a string with the corresponding category.
+ **/
+void
+e_cal_component_get_categories_list (ECalComponent *comp, GSList **categ_list)
+{
+	ECalComponentPrivate *priv;
+	const char *categories;
+	const char *p;
+	const char *cat_start;
+	char *str;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (categ_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!priv->categories) {
+		*categ_list = NULL;
+		return;
+	}
+
+	categories = icalproperty_get_categories (priv->categories);
+	g_assert (categories != NULL);
+
+	cat_start = categories;
+	*categ_list = NULL;
+
+	for (p = categories; *p; p++)
+		if (*p == ',') {
+			str = g_strndup (cat_start, p - cat_start);
+			*categ_list = g_slist_prepend (*categ_list, str);
+
+			cat_start = p + 1;
+		}
+
+	str = g_strndup (cat_start, p - cat_start);
+	*categ_list = g_slist_prepend (*categ_list, str);
+
+	*categ_list = g_slist_reverse (*categ_list);
+}
+
+/* Creates a comma-delimited string of categories */
+static char *
+stringify_categories (GSList *categ_list)
+{
+	GString *s;
+	GSList *l;
+	char *str;
+
+	s = g_string_new (NULL);
+
+	for (l = categ_list; l; l = l->next) {
+		g_string_append (s, l->data);
+
+		if (l->next != NULL)
+			g_string_append (s, ",");
+	}
+
+	str = s->str;
+	g_string_free (s, FALSE);
+
+	return str;
+}
+
+/**
+ * e_cal_component_set_categories_list:
+ * @comp: A calendar component object.
+ * @categ_list: List of strings, one for each category.
+ *
+ * Sets the list of categories of a calendar component object.
+ **/
+void
+e_cal_component_set_categories_list (ECalComponent *comp, GSList *categ_list)
+{
+	ECalComponentPrivate *priv;
+	char *categories_str;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!categ_list) {
+		if (priv->categories) {
+			icalcomponent_remove_property (priv->icalcomp, priv->categories);
+			icalproperty_free (priv->categories);
+		}
+
+		return;
+	}
+
+	/* Create a single string of categories */
+	categories_str = stringify_categories (categ_list);
+
+	/* Set the categories */
+	priv->categories = icalproperty_new_categories (categories_str);
+	g_free (categories_str);
+
+	icalcomponent_add_property (priv->icalcomp, priv->categories);
+}
+
+/**
+ * e_cal_component_get_classification:
+ * @comp: A calendar component object.
+ * @classif: Return value for the classification.
+ *
+ * Queries the classification of a calendar component object.  If the
+ * classification property is not set on this component, this function returns
+ * #E_CAL_COMPONENT_CLASS_NONE.
+ **/
+void
+e_cal_component_get_classification (ECalComponent *comp, ECalComponentClassification *classif)
+{
+	ECalComponentPrivate *priv;
+	icalproperty_class class;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (classif != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!priv->classification) {
+		*classif = E_CAL_COMPONENT_CLASS_NONE;
+		return;
+	}
+
+	class = icalproperty_get_class (priv->classification);
+
+	switch (class)
+	{
+	case ICAL_CLASS_PUBLIC:
+	  *classif = E_CAL_COMPONENT_CLASS_PUBLIC;
+	  break;
+	case ICAL_CLASS_PRIVATE:
+	  *classif = E_CAL_COMPONENT_CLASS_PRIVATE;
+	  break;
+	case ICAL_CLASS_CONFIDENTIAL:
+	  *classif = E_CAL_COMPONENT_CLASS_CONFIDENTIAL;
+	  break;
+	default:
+	  *classif = E_CAL_COMPONENT_CLASS_UNKNOWN;
+	  break;
+	}
+}
+
+/**
+ * e_cal_component_set_classification:
+ * @comp: A calendar component object.
+ * @classif: Classification to use.
+ *
+ * Sets the classification property of a calendar component object.  To unset
+ * the property, specify E_CAL_COMPONENT_CLASS_NONE for @classif.
+ **/
+void
+e_cal_component_set_classification (ECalComponent *comp, ECalComponentClassification classif)
+{
+	ECalComponentPrivate *priv;
+	icalproperty_class class;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (classif != E_CAL_COMPONENT_CLASS_UNKNOWN);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (classif == E_CAL_COMPONENT_CLASS_NONE) {
+		if (priv->classification) {
+			icalcomponent_remove_property (priv->icalcomp, priv->classification);
+			icalproperty_free (priv->classification);
+			priv->classification = NULL;
+		}
+
+		return;
+	}
+
+	switch (classif) {
+	case E_CAL_COMPONENT_CLASS_PUBLIC:
+	  class = ICAL_CLASS_PUBLIC;
+		break;
+
+	case E_CAL_COMPONENT_CLASS_PRIVATE:
+	  class = ICAL_CLASS_PRIVATE;
+		break;
+
+	case E_CAL_COMPONENT_CLASS_CONFIDENTIAL:
+	  class = ICAL_CLASS_CONFIDENTIAL;
+		break;
+
+	default:
+		g_assert_not_reached ();
+		class = ICAL_CLASS_NONE;
+	}
+
+	if (priv->classification)
+		icalproperty_set_class (priv->classification, class);
+	else {
+		priv->classification = icalproperty_new_class (class);
+		icalcomponent_add_property (priv->icalcomp, priv->classification);
+	}
+}
+
+/* Gets a text list value */
+static void
+get_text_list (GSList *text_list,
+	       const char *(* get_prop_func) (const icalproperty *prop),
+	       GSList **tl)
+{
+	GSList *l;
+
+	*tl = NULL;
+
+	if (!text_list)
+		return;
+
+	for (l = text_list; l; l = l->next) {
+		struct text *text;
+		ECalComponentText *t;
+
+		text = l->data;
+		g_assert (text->prop != NULL);
+
+		t = g_new (ECalComponentText, 1);
+		t->value = (* get_prop_func) (text->prop);
+
+		if (text->altrep_param)
+			t->altrep = icalparameter_get_altrep (text->altrep_param);
+		else
+			t->altrep = NULL;
+
+		*tl = g_slist_prepend (*tl, t);
+	}
+
+	*tl = g_slist_reverse (*tl);
+}
+
+/* Sets a text list value */
+static void
+set_text_list (ECalComponent *comp,
+	       icalproperty *(* new_prop_func) (const char *value),
+	       GSList **text_list,
+	       GSList *tl)
+{
+	ECalComponentPrivate *priv;
+	GSList *l;
+
+	priv = comp->priv;
+
+	/* Remove old texts */
+
+	for (l = *text_list; l; l = l->next) {
+		struct text *text;
+
+		text = l->data;
+		g_assert (text->prop != NULL);
+
+		icalcomponent_remove_property (priv->icalcomp, text->prop);
+		icalproperty_free (text->prop);
+		g_free (text);
+	}
+
+	g_slist_free (*text_list);
+	*text_list = NULL;
+
+	/* Add in new texts */
+
+	for (l = tl; l; l = l->next) {
+		ECalComponentText *t;
+		struct text *text;
+
+		t = l->data;
+		g_return_if_fail (t->value != NULL);
+
+		text = g_new (struct text, 1);
+
+		text->prop = (* new_prop_func) ((char *) t->value);
+		icalcomponent_add_property (priv->icalcomp, text->prop);
+
+		if (t->altrep) {
+			text->altrep_param = icalparameter_new_altrep ((char *) t->altrep);
+			icalproperty_add_parameter (text->prop, text->altrep_param);
+		} else
+			text->altrep_param = NULL;
+
+		*text_list = g_slist_prepend (*text_list, text);
+	}
+
+	*text_list = g_slist_reverse (*text_list);
+}
+
+/**
+ * e_cal_component_get_comment_list:
+ * @comp: A calendar component object.
+ * @text_list: Return value for the comment properties and their parameters, as
+ * a list of #ECalComponentText structures.  This should be freed using the
+ * e_cal_component_free_text_list() function.
+ *
+ * Queries the comments of a calendar component object.  The comment property can
+ * appear several times inside a calendar component, and so a list of
+ * #ECalComponentText is returned.
+ **/
+void
+e_cal_component_get_comment_list (ECalComponent *comp, GSList **text_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (text_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_text_list (priv->comment_list, icalproperty_get_comment, text_list);
+}
+
+/**
+ * e_cal_component_set_comment_list:
+ * @comp: A calendar component object.
+ * @text_list: List of #ECalComponentText structures.
+ *
+ * Sets the comments of a calendar component object.  The comment property can
+ * appear several times inside a calendar component, and so a list of
+ * #ECalComponentText structures is used.
+ **/
+void
+e_cal_component_set_comment_list (ECalComponent *comp, GSList *text_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_text_list (comp, icalproperty_new_comment, &priv->comment_list, text_list);
+}
+
+/**
+ * e_cal_component_get_contact_list:
+ * @comp: A calendar component object.
+ * @text_list: Return value for the contact properties and their parameters, as
+ * a list of #ECalComponentText structures.  This should be freed using the
+ * e_cal_component_free_text_list() function.
+ *
+ * Queries the contact of a calendar component object.  The contact property can
+ * appear several times inside a calendar component, and so a list of
+ * #ECalComponentText is returned.
+ **/
+void
+e_cal_component_get_contact_list (ECalComponent *comp, GSList **text_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (text_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_text_list (priv->contact_list, icalproperty_get_contact, text_list);
+}
+
+/**
+ * e_cal_component_set_contact_list:
+ * @comp: A calendar component object.
+ * @text_list: List of #ECalComponentText structures.
+ *
+ * Sets the contact of a calendar component object.  The contact property can
+ * appear several times inside a calendar component, and so a list of
+ * #ECalComponentText structures is used.
+ **/
+void
+e_cal_component_set_contact_list (ECalComponent *comp, GSList *text_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_text_list (comp, icalproperty_new_contact, &priv->contact_list, text_list);
+}
+
+/* Gets a struct icaltimetype value */
+static void
+get_icaltimetype (icalproperty *prop,
+		  struct icaltimetype (* get_prop_func) (const icalproperty *prop),
+		  struct icaltimetype **t)
+{
+	if (!prop) {
+		*t = NULL;
+		return;
+	}
+
+	*t = g_new (struct icaltimetype, 1);
+	**t = (* get_prop_func) (prop);
+}
+
+/* Sets a struct icaltimetype value */
+static void
+set_icaltimetype (ECalComponent *comp, icalproperty **prop,
+		  icalproperty *(* prop_new_func) (struct icaltimetype v),
+		  void (* prop_set_func) (icalproperty *prop, struct icaltimetype v),
+		  struct icaltimetype *t)
+{
+	ECalComponentPrivate *priv;
+
+	priv = comp->priv;
+
+	if (!t) {
+		if (*prop) {
+			icalcomponent_remove_property (priv->icalcomp, *prop);
+			icalproperty_free (*prop);
+			*prop = NULL;
+		}
+
+		return;
+	}
+
+	if (*prop)
+		(* prop_set_func) (*prop, *t);
+	else {
+		*prop = (* prop_new_func) (*t);
+		icalcomponent_add_property (priv->icalcomp, *prop);
+	}
+}
+
+/**
+ * e_cal_component_get_completed:
+ * @comp: A calendar component object.
+ * @t: Return value for the completion date.  This should be freed using the
+ * e_cal_component_free_icaltimetype() function.
+ *
+ * Queries the date at which a calendar compoment object was completed.
+ **/
+void
+e_cal_component_get_completed (ECalComponent *comp, struct icaltimetype **t)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (t != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_icaltimetype (priv->completed, icalproperty_get_completed, t);
+}
+
+/**
+ * e_cal_component_set_completed:
+ * @comp: A calendar component object.
+ * @t: Value for the completion date.
+ *
+ * Sets the date at which a calendar component object was completed.
+ **/
+void
+e_cal_component_set_completed (ECalComponent *comp, struct icaltimetype *t)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_icaltimetype (comp, &priv->completed,
+			  icalproperty_new_completed,
+			  icalproperty_set_completed,
+			  t);
+}
+
+
+/**
+ * e_cal_component_get_created:
+ * @comp: A calendar component object.
+ * @t: Return value for the creation date.  This should be freed using the
+ * e_cal_component_free_icaltimetype() function.
+ *
+ * Queries the date in which a calendar component object was created in the
+ * calendar store.
+ **/
+void
+e_cal_component_get_created (ECalComponent *comp, struct icaltimetype **t)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (t != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_icaltimetype (priv->created, icalproperty_get_created, t);
+}
+
+/**
+ * e_cal_component_set_created:
+ * @comp: A calendar component object.
+ * @t: Value for the creation date.
+ *
+ * Sets the date in which a calendar component object is created in the calendar
+ * store.  This should only be used inside a calendar store application, i.e.
+ * not by calendar user agents.
+ **/
+void
+e_cal_component_set_created (ECalComponent *comp, struct icaltimetype *t)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_icaltimetype (comp, &priv->created,
+			  icalproperty_new_created,
+			  icalproperty_set_created,
+			  t);
+}
+
+/**
+ * e_cal_component_get_description_list:
+ * @comp: A calendar component object.
+ * @text_list: Return value for the description properties and their parameters,
+ * as a list of #ECalComponentText structures.  This should be freed using the
+ * e_cal_component_free_text_list() function.
+ *
+ * Queries the description of a calendar component object.  Journal components
+ * may have more than one description, and as such this function returns a list
+ * of #ECalComponentText structures.  All other types of components can have at
+ * most one description.
+ **/
+void
+e_cal_component_get_description_list (ECalComponent *comp, GSList **text_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (text_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_text_list (priv->description_list, icalproperty_get_description, text_list);
+}
+
+/**
+ * e_cal_component_set_description_list:
+ * @comp: A calendar component object.
+ * @text_list: List of #ECalComponentSummary structures.
+ *
+ * Sets the description of a calendar component object.  Journal components may
+ * have more than one description, and as such this function takes in a list of
+ * #ECalComponentDescription structures.  All other types of components can have
+ * at most one description.
+ **/
+void
+e_cal_component_set_description_list (ECalComponent *comp, GSList *text_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_text_list (comp, icalproperty_new_description, &priv->description_list, text_list);
+}
+
+/* Gets a date/time and timezone pair */
+static void
+get_datetime (struct datetime *datetime,
+	      struct icaltimetype (* get_prop_func) (const icalproperty *prop),
+	      ECalComponentDateTime *dt)
+{
+	if (datetime->prop) {
+		dt->value = g_new (struct icaltimetype, 1);
+		*dt->value = (* get_prop_func) (datetime->prop);
+	} else
+		dt->value = NULL;
+
+	/* If the icaltimetype has is_utc set, we set "UTC" as the TZID.
+	   This makes the timezone code simpler. */
+	if (datetime->tzid_param)
+		dt->tzid = g_strdup (icalparameter_get_tzid (datetime->tzid_param));
+	else if (dt->value && dt->value->is_utc)
+		dt->tzid = g_strdup ("UTC");
+	else
+		dt->tzid = NULL;
+}
+
+/* Sets a date/time and timezone pair */
+static void
+set_datetime (ECalComponent *comp, struct datetime *datetime,
+	      icalproperty *(* prop_new_func) (struct icaltimetype v),
+	      void (* prop_set_func) (icalproperty * prop, struct icaltimetype v),
+	      ECalComponentDateTime *dt)
+{
+	ECalComponentPrivate *priv;
+
+	priv = comp->priv;
+
+	/* If we are setting the property to NULL (i.e. removing it), then
+	   we remove it if it exists. */
+	if (!dt) {
+		if (datetime->prop) {
+			icalcomponent_remove_property (priv->icalcomp, datetime->prop);
+			icalproperty_free (datetime->prop);
+
+			datetime->prop = NULL;
+			datetime->tzid_param = NULL;
+		}
+
+		return;
+	}
+
+	g_return_if_fail (dt->value != NULL);
+
+	/* If the TZID is set to "UTC", we set the is_utc flag. */
+	if (dt->tzid && !strcmp (dt->tzid, "UTC"))
+		dt->value->is_utc = 1;
+	else
+		dt->value->is_utc = 0;
+
+	if (datetime->prop) {
+		(* prop_set_func) (datetime->prop, *dt->value);
+	} else {
+		datetime->prop = (* prop_new_func) (*dt->value);
+		icalcomponent_add_property (priv->icalcomp, datetime->prop);
+	}
+
+	/* If the TZID is set to "UTC", we don't want to save the TZID. */
+	if (dt->tzid && strcmp (dt->tzid, "UTC")) {
+		g_assert (datetime->prop != NULL);
+
+		if (datetime->tzid_param) {
+			icalparameter_set_tzid (datetime->tzid_param, (char *) dt->tzid);
+		} else {
+			datetime->tzid_param = icalparameter_new_tzid ((char *) dt->tzid);
+			icalproperty_add_parameter (datetime->prop, datetime->tzid_param);
+		}
+	} else if (datetime->tzid_param) {
+		icalproperty_remove_parameter (datetime->prop, ICAL_TZID_PARAMETER);
+		datetime->tzid_param = NULL;
+	}
+}
+
+
+/* This tries to get the DTSTART + DURATION for a VEVENT or VTODO. In a
+   VEVENT this is used for the DTEND if no DTEND exists, In a VTOTO it is
+   used for the DUE date if DUE doesn't exist. */
+static void
+e_cal_component_get_start_plus_duration (ECalComponent *comp,
+					 ECalComponentDateTime *dt)
+{
+	ECalComponentPrivate *priv;
+	struct icaldurationtype duration;
+
+	priv = comp->priv;
+
+	if (!priv->duration)
+		return;
+
+	/* Get the DTSTART time. */
+	get_datetime (&priv->dtstart, icalproperty_get_dtstart, dt);
+	if (!dt->value)
+		return;
+
+	duration = icalproperty_get_duration (priv->duration);
+
+	/* The DURATION shouldn't be negative, but just return DTSTART if it
+	   is, i.e. assume it is 0. */
+	if (duration.is_neg)
+		return;
+
+	/* If DTSTART is a DATE value, then we need to check if the DURATION
+	   includes any hours, minutes or seconds. If it does, we need to
+	   make the DTEND/DUE a DATE-TIME value. */
+	duration.days += duration.weeks * 7;
+	if (dt->value->is_date) {
+		if (duration.hours != 0 || duration.minutes != 0
+		    || duration.seconds != 0) {
+			dt->value->is_date = 0;
+		}
+	}
+
+	/* Add on the DURATION. */
+	icaltime_adjust (dt->value, duration.days, duration.hours,
+			 duration.minutes, duration.seconds);
+}
+
+
+/**
+ * e_cal_component_get_dtend:
+ * @comp: A calendar component object.
+ * @dt: Return value for the date/time end.  This should be freed with the
+ * e_cal_component_free_datetime() function.
+ *
+ * Queries the date/time end of a calendar component object.
+ **/
+void
+e_cal_component_get_dtend (ECalComponent *comp, ECalComponentDateTime *dt)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (dt != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_datetime (&priv->dtend, icalproperty_get_dtend, dt);
+
+	/* If we don't have a DTEND property, then we try to get DTSTART
+	   + DURATION. */
+	if (!dt->value)
+		e_cal_component_get_start_plus_duration (comp, dt);
+}
+
+/**
+ * e_cal_component_set_dtend:
+ * @comp: A calendar component object.
+ * @dt: End date/time.
+ *
+ * Sets the date/time end property of a calendar component object.
+ **/
+void
+e_cal_component_set_dtend (ECalComponent *comp, ECalComponentDateTime *dt)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_datetime (comp, &priv->dtend,
+		      icalproperty_new_dtend,
+		      icalproperty_set_dtend,
+		      dt);
+
+	/* Make sure we remove any existing DURATION property, as it can't be
+	   used with a DTEND. If DTEND is set to NULL, i.e. removed, we also
+	   want to remove any DURATION. */
+	if (priv->duration) {
+		icalcomponent_remove_property (priv->icalcomp, priv->duration);
+		icalproperty_free (priv->duration);
+		priv->duration = NULL;
+	}
+
+	priv->need_sequence_inc = TRUE;
+}
+
+/**
+ * e_cal_component_get_dtstamp:
+ * @comp: A calendar component object.
+ * @t: A value for the date/timestamp.
+ *
+ * Queries the date/timestamp property of a calendar component object, which is
+ * the last time at which the object was modified by a calendar user agent.
+ **/
+void
+e_cal_component_get_dtstamp (ECalComponent *comp, struct icaltimetype *t)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (t != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	/* This MUST exist, since we ensured that it did */
+	g_assert (priv->dtstamp != NULL);
+
+	*t = icalproperty_get_dtstamp (priv->dtstamp);
+}
+
+/**
+ * e_cal_component_set_dtstamp:
+ * @comp: A calendar component object.
+ * @t: Date/timestamp value.
+ *
+ * Sets the date/timestamp of a calendar component object.  This should be
+ * called whenever a calendar user agent makes a change to a component's
+ * properties.
+ **/
+void
+e_cal_component_set_dtstamp (ECalComponent *comp, struct icaltimetype *t)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (t != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	/* This MUST exist, since we ensured that it did */
+	g_assert (priv->dtstamp != NULL);
+
+	icalproperty_set_dtstamp (priv->dtstamp, *t);
+}
+
+/**
+ * e_cal_component_get_dtstart:
+ * @comp: A calendar component object.
+ * @dt: Return value for the date/time start.  This should be freed with the
+ * e_cal_component_free_datetime() function.
+ *
+ * Queries the date/time start of a calendar component object.
+ **/
+void
+e_cal_component_get_dtstart (ECalComponent *comp, ECalComponentDateTime *dt)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (dt != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_datetime (&priv->dtstart, icalproperty_get_dtstart, dt);
+}
+
+/**
+ * e_cal_component_set_dtstart:
+ * @comp: A calendar component object.
+ * @dt: Start date/time.
+ *
+ * Sets the date/time start property of a calendar component object.
+ **/
+void
+e_cal_component_set_dtstart (ECalComponent *comp, ECalComponentDateTime *dt)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_datetime (comp, &priv->dtstart,
+		      icalproperty_new_dtstart,
+		      icalproperty_set_dtstart,
+		      dt);
+
+	priv->need_sequence_inc = TRUE;
+}
+
+/**
+ * e_cal_component_get_due:
+ * @comp: A calendar component object.
+ * @dt: Return value for the due date/time.  This should be freed with the
+ * e_cal_component_free_datetime() function.
+ *
+ * Queries the due date/time of a calendar component object.
+ **/
+void
+e_cal_component_get_due (ECalComponent *comp, ECalComponentDateTime *dt)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (dt != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_datetime (&priv->due, icalproperty_get_due, dt);
+
+	/* If we don't have a DTEND property, then we try to get DTSTART
+	   + DURATION. */
+	if (!dt->value)
+		e_cal_component_get_start_plus_duration (comp, dt);
+}
+
+/**
+ * e_cal_component_set_due:
+ * @comp: A calendar component object.
+ * @dt: End date/time.
+ *
+ * Sets the due date/time property of a calendar component object.
+ **/
+void
+e_cal_component_set_due (ECalComponent *comp, ECalComponentDateTime *dt)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_datetime (comp, &priv->due,
+		      icalproperty_new_due,
+		      icalproperty_set_due,
+		      dt);
+
+	/* Make sure we remove any existing DURATION property, as it can't be
+	   used with a DTEND. If DTEND is set to NULL, i.e. removed, we also
+	   want to remove any DURATION. */
+	if (priv->duration) {
+		icalcomponent_remove_property (priv->icalcomp, priv->duration);
+		icalproperty_free (priv->duration);
+		priv->duration = NULL;
+	}
+
+	priv->need_sequence_inc = TRUE;
+}
+
+/* Builds a list of ECalComponentPeriod structures based on a list of icalproperties */
+static void
+get_period_list (GSList *period_list,
+		 struct icaldatetimeperiodtype (* get_prop_func) (const icalproperty *prop),
+		 GSList **list)
+{
+	GSList *l;
+
+	*list = NULL;
+
+	if (!period_list)
+		return;
+
+	for (l = period_list; l; l = l->next) {
+		struct period *period;
+		ECalComponentPeriod *p;
+		struct icaldatetimeperiodtype ip;
+
+		period = l->data;
+		g_assert (period->prop != NULL);
+
+		p = g_new (ECalComponentPeriod, 1);
+
+		/* Get value parameter */
+
+		if (period->value_param) {
+			icalparameter_value value_type;
+
+			value_type = icalparameter_get_value (period->value_param);
+
+			if (value_type == ICAL_VALUE_DATE || value_type == ICAL_VALUE_DATETIME)
+				p->type = E_CAL_COMPONENT_PERIOD_DATETIME;
+			else if (value_type == ICAL_VALUE_DURATION)
+				p->type = E_CAL_COMPONENT_PERIOD_DURATION;
+			else {
+				g_message ("get_period_list(): Unknown value for period %d; "
+					   "using DATETIME", value_type);
+				p->type = E_CAL_COMPONENT_PERIOD_DATETIME;
+			}
+		} else
+			p->type = E_CAL_COMPONENT_PERIOD_DATETIME;
+
+		/* Get start and end/duration */
+
+		ip = (* get_prop_func) (period->prop);
+
+		p->start = ip.period.start;
+
+		if (p->type == E_CAL_COMPONENT_PERIOD_DATETIME)
+			p->u.end = ip.period.end;
+		else if (p->type == E_CAL_COMPONENT_PERIOD_DURATION)
+			p->u.duration = ip.period.duration;
+		else
+			g_assert_not_reached ();
+
+		/* Put in list */
+
+		*list = g_slist_prepend (*list, p);
+	}
+
+	*list = g_slist_reverse (*list);
+}
+
+/* Sets a period list value */
+static void
+set_period_list (ECalComponent *comp,
+		 icalproperty *(* new_prop_func) (struct icaldatetimeperiodtype period),
+		 GSList **period_list,
+		 GSList *pl)
+{
+	ECalComponentPrivate *priv;
+	GSList *l;
+
+	priv = comp->priv;
+
+	/* Remove old periods */
+
+	for (l = *period_list; l; l = l->next) {
+		struct period *period;
+
+		period = l->data;
+		g_assert (period->prop != NULL);
+
+		icalcomponent_remove_property (priv->icalcomp, period->prop);
+		icalproperty_free (period->prop);
+		g_free (period);
+	}
+
+	g_slist_free (*period_list);
+	*period_list = NULL;
+
+	/* Add in new periods */
+
+	for (l = pl; l; l = l->next) {
+		ECalComponentPeriod *p;
+		struct period *period;
+		struct icaldatetimeperiodtype ip;
+		icalparameter_value value_type;
+
+		g_assert (l->data != NULL);
+		p = l->data;
+
+		/* Create libical value */
+
+		ip.period.start = p->start;
+
+		if (p->type == E_CAL_COMPONENT_PERIOD_DATETIME) {
+			value_type = ICAL_VALUE_DATETIME;
+			ip.period.end = p->u.end;
+		} else if (p->type == E_CAL_COMPONENT_PERIOD_DURATION) {
+			value_type = ICAL_VALUE_DURATION;
+			ip.period.duration = p->u.duration;
+		} else {
+			g_assert_not_reached ();
+			return;
+		}
+
+		/* Create property */
+
+		period = g_new (struct period, 1);
+
+		period->prop = (* new_prop_func) (ip);
+		period->value_param = icalparameter_new_value (value_type);
+		icalproperty_add_parameter (period->prop, period->value_param);
+
+		/* Add to list */
+
+		*period_list = g_slist_prepend (*period_list, period);
+	}
+
+	*period_list = g_slist_reverse (*period_list);
+}
+
+/**
+ * e_cal_component_get_exdate_list:
+ * @comp: A calendar component object.
+ * @exdate_list: Return value for the list of exception dates, as a list of
+ * #ECalComponentDateTime structures.  This should be freed using the
+ * e_cal_component_free_exdate_list() function.
+ *
+ * Queries the list of exception date properties in a calendar component object.
+ **/
+void
+e_cal_component_get_exdate_list (ECalComponent *comp, GSList **exdate_list)
+{
+	ECalComponentPrivate *priv;
+	GSList *l;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (exdate_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	*exdate_list = NULL;
+
+	for (l = priv->exdate_list; l; l = l->next) {
+		struct datetime *dt;
+		ECalComponentDateTime *cdt;
+
+		dt = l->data;
+
+		cdt = g_new (ECalComponentDateTime, 1);
+		cdt->value = g_new (struct icaltimetype, 1);
+
+		*cdt->value = icalproperty_get_exdate (dt->prop);
+
+		if (dt->tzid_param)
+			cdt->tzid = g_strdup (icalparameter_get_tzid (dt->tzid_param));
+		else
+			cdt->tzid = NULL;
+
+		*exdate_list = g_slist_prepend (*exdate_list, cdt);
+	}
+
+	*exdate_list = g_slist_reverse (*exdate_list);
+}
+
+/**
+ * e_cal_component_set_exdate_list:
+ * @comp: A calendar component object.
+ * @exdate_list: List of #ECalComponentDateTime structures.
+ *
+ * Sets the list of exception dates in a calendar component object.
+ **/
+void
+e_cal_component_set_exdate_list (ECalComponent *comp, GSList *exdate_list)
+{
+	ECalComponentPrivate *priv;
+	GSList *l;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	/* Remove old exception dates */
+
+	for (l = priv->exdate_list; l; l = l->next) {
+		struct datetime *dt;
+
+		dt = l->data;
+
+		/* Removing the DATE or DATE-TIME property will also remove
+		   any TZID parameter. */
+		icalcomponent_remove_property (priv->icalcomp, dt->prop);
+		icalproperty_free (dt->prop);
+		g_free (dt);
+	}
+
+	g_slist_free (priv->exdate_list);
+	priv->exdate_list = NULL;
+
+	/* Add in new exception dates */
+
+	for (l = exdate_list; l; l = l->next) {
+		ECalComponentDateTime *cdt;
+		struct datetime *dt;
+
+		g_assert (l->data != NULL);
+		cdt = l->data;
+
+		g_assert (cdt->value != NULL);
+
+		dt = g_new (struct datetime, 1);
+		dt->prop = icalproperty_new_exdate (*cdt->value);
+
+		if (cdt->tzid) {
+			dt->tzid_param = icalparameter_new_tzid ((char *) cdt->tzid);
+			icalproperty_add_parameter (dt->prop, dt->tzid_param);
+		} else
+			dt->tzid_param = NULL;
+
+		icalcomponent_add_property (priv->icalcomp, dt->prop);
+		priv->exdate_list = g_slist_prepend (priv->exdate_list, dt);
+	}
+
+	priv->exdate_list = g_slist_reverse (priv->exdate_list);
+
+	priv->need_sequence_inc = TRUE;
+}
+
+/**
+ * e_cal_component_has_exdates:
+ * @comp: A calendar component object.
+ *
+ * Queries whether a calendar component object has any exception dates defined
+ * for it.
+ *
+ * Return value: TRUE if the component has exception dates, FALSE otherwise.
+ **/
+gboolean
+e_cal_component_has_exdates (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, FALSE);
+
+	return (priv->exdate_list != NULL);
+}
+
+/* Gets a list of recurrence rules */
+static void
+get_recur_list (GSList *recur_list,
+		struct icalrecurrencetype (* get_prop_func) (const icalproperty *prop),
+		GSList **list)
+{
+	GSList *l;
+
+	*list = NULL;
+
+	for (l = recur_list; l; l = l->next) {
+		icalproperty *prop;
+		struct icalrecurrencetype *r;
+
+		prop = l->data;
+
+		r = g_new (struct icalrecurrencetype, 1);
+		*r = (* get_prop_func) (prop);
+
+		*list = g_slist_prepend (*list, r);
+	}
+
+	*list = g_slist_reverse (*list);
+}
+
+/* Sets a list of recurrence rules */
+static void
+set_recur_list (ECalComponent *comp,
+		icalproperty *(* new_prop_func) (struct icalrecurrencetype recur),
+		GSList **recur_list,
+		GSList *rl)
+{
+	ECalComponentPrivate *priv;
+	GSList *l;
+
+	priv = comp->priv;
+
+	/* Remove old recurrences */
+
+	for (l = *recur_list; l; l = l->next) {
+		icalproperty *prop;
+
+		prop = l->data;
+		icalcomponent_remove_property (priv->icalcomp, prop);
+		icalproperty_free (prop);
+	}
+
+	g_slist_free (*recur_list);
+	*recur_list = NULL;
+
+	/* Add in new recurrences */
+
+	for (l = rl; l; l = l->next) {
+		icalproperty *prop;
+		struct icalrecurrencetype *recur;
+
+		g_assert (l->data != NULL);
+		recur = l->data;
+
+		prop = (* new_prop_func) (*recur);
+		icalcomponent_add_property (priv->icalcomp, prop);
+
+		*recur_list = g_slist_prepend (*recur_list, prop);
+	}
+
+	*recur_list = g_slist_reverse (*recur_list);
+}
+
+/**
+ * e_cal_component_get_exrule_list:
+ * @comp: A calendar component object.
+ * @recur_list: List of exception rules as struct #icalrecurrencetype
+ * structures.  This should be freed using the e_cal_component_free_recur_list()
+ * function.
+ *
+ * Queries the list of exception rule properties of a calendar component
+ * object.
+ **/
+void
+e_cal_component_get_exrule_list (ECalComponent *comp, GSList **recur_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (recur_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_recur_list (priv->exrule_list, icalproperty_get_exrule, recur_list);
+}
+
+/**
+ * e_cal_component_get_exrule_property_list:
+ * @comp: A calendar component object.
+ * @recur_list: Returns a list of exception rule properties.
+ *
+ * Queries the list of exception rule properties of a calendar component object.
+ **/
+void
+e_cal_component_get_exrule_property_list (ECalComponent *comp, GSList **recur_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (recur_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	*recur_list = priv->exrule_list;
+}
+
+/**
+ * e_cal_component_set_exrule_list:
+ * @comp: A calendar component object.
+ * @recur_list: List of struct #icalrecurrencetype structures.
+ *
+ * Sets the list of exception rules in a calendar component object.
+ **/
+void
+e_cal_component_set_exrule_list (ECalComponent *comp, GSList *recur_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_recur_list (comp, icalproperty_new_exrule, &priv->exrule_list, recur_list);
+
+	priv->need_sequence_inc = TRUE;
+}
+
+/**
+ * e_cal_component_has_exrules:
+ * @comp: A calendar component object.
+ *
+ * Queries whether a calendar component object has any exception rules defined
+ * for it.
+ *
+ * Return value: TRUE if the component has exception rules, FALSE otherwise.
+ **/
+gboolean
+e_cal_component_has_exrules (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, FALSE);
+
+	return (priv->exrule_list != NULL);
+}
+
+/**
+ * e_cal_component_has_exceptions:
+ * @comp: A calendar component object
+ *
+ * Queries whether a calendar component object has any exception dates
+ * or exception rules.
+ *
+ * Return value: TRUE if the component has exceptions, FALSE otherwise.
+ **/
+gboolean
+e_cal_component_has_exceptions (ECalComponent *comp)
+{
+	return e_cal_component_has_exdates (comp) || e_cal_component_has_exrules (comp);
+}
+
+/**
+ * e_cal_component_get_geo:
+ * @comp: A calendar component object.
+ * @geo: Return value for the geographic position property.  This should be
+ * freed using the e_cal_component_free_geo() function.
+ *
+ * Gets the geographic position property of a calendar component object.
+ **/
+void
+e_cal_component_get_geo (ECalComponent *comp, struct icalgeotype **geo)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (geo != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (priv->geo) {
+		*geo = g_new (struct icalgeotype, 1);
+		**geo = icalproperty_get_geo (priv->geo);
+	} else
+		*geo = NULL;
+}
+
+/**
+ * e_cal_component_set_geo:
+ * @comp: A calendar component object.
+ * @geo: Value for the geographic position property.
+ *
+ * Sets the geographic position property on a calendar component object.
+ **/
+void
+e_cal_component_set_geo (ECalComponent *comp, struct icalgeotype *geo)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!geo) {
+		if (priv->geo) {
+			icalcomponent_remove_property (priv->icalcomp, priv->geo);
+			icalproperty_free (priv->geo);
+			priv->geo = NULL;
+		}
+
+		return;
+	}
+
+	if (priv->geo)
+		icalproperty_set_geo (priv->geo, *geo);
+	else {
+		priv->geo = icalproperty_new_geo (*geo);
+		icalcomponent_add_property (priv->icalcomp, priv->geo);
+	}
+}
+
+/**
+ * e_cal_component_get_last_modified:
+ * @comp: A calendar component object.
+ * @t: Return value for the last modified time value.
+ *
+ * Queries the time at which a calendar component object was last modified in
+ * the calendar store.
+ **/
+void
+e_cal_component_get_last_modified (ECalComponent *comp, struct icaltimetype **t)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (t != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_icaltimetype (priv->last_modified, icalproperty_get_lastmodified, t);
+}
+
+/**
+ * e_cal_component_set_last_modified:
+ * @comp: A calendar component object.
+ * @t: Value for the last time modified.
+ *
+ * Sets the time at which a calendar component object was last stored in the
+ * calendar store.  This should not be called by plain calendar user agents.
+ **/
+void
+e_cal_component_set_last_modified (ECalComponent *comp, struct icaltimetype *t)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_icaltimetype (comp, &priv->last_modified,
+			  icalproperty_new_lastmodified,
+			  icalproperty_set_lastmodified,
+			  t);
+}
+
+/**
+ * e_cal_component_get_organizer:
+ * @comp:  A calendar component object
+ * @organizer: A value for the organizer
+ * 
+ * Queries the organizer property of a calendar component object
+ **/
+void
+e_cal_component_get_organizer (ECalComponent *comp, ECalComponentOrganizer *organizer)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (organizer != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (priv->organizer.prop)
+		organizer->value = icalproperty_get_organizer (priv->organizer.prop);
+	else
+		organizer->value = NULL;
+
+	if (priv->organizer.sentby_param)
+		organizer->sentby = icalparameter_get_sentby (priv->organizer.sentby_param);
+	else
+		organizer->sentby = NULL;
+
+	if (priv->organizer.cn_param)
+		organizer->cn = icalparameter_get_sentby (priv->organizer.cn_param);
+	else
+		organizer->cn = NULL;
+
+	if (priv->organizer.language_param)
+		organizer->language = icalparameter_get_sentby (priv->organizer.language_param);
+	else
+		organizer->language = NULL;
+
+}
+
+/**
+ * e_cal_component_set_organizer:
+ * @comp:  A calendar component object.
+ * @organizer: Value for the organizer property
+ * 
+ * Sets the organizer of a calendar component object
+ **/
+void
+e_cal_component_set_organizer (ECalComponent *comp, ECalComponentOrganizer *organizer)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!organizer) {
+		if (priv->organizer.prop) {
+			icalcomponent_remove_property (priv->icalcomp, priv->organizer.prop);
+			icalproperty_free (priv->organizer.prop);
+
+			priv->organizer.prop = NULL;
+			priv->organizer.sentby_param = NULL;
+			priv->organizer.cn_param = NULL;
+			priv->organizer.language_param = NULL;
+		}
+
+		return;
+	}
+
+	g_return_if_fail (organizer->value != NULL);
+
+	if (priv->organizer.prop)
+		icalproperty_set_organizer (priv->organizer.prop, (char *) organizer->value);
+	else {
+		priv->organizer.prop = icalproperty_new_organizer ((char *) organizer->value);
+		icalcomponent_add_property (priv->icalcomp, priv->organizer.prop);
+	}
+
+	if (organizer->sentby) {
+		g_assert (priv->organizer.prop != NULL);
+
+		if (priv->organizer.sentby_param)
+			icalparameter_set_sentby (priv->organizer.sentby_param,
+						  (char *) organizer->sentby);
+		else {
+			priv->organizer.sentby_param = icalparameter_new_sentby (
+				(char *) organizer->sentby);
+			icalproperty_add_parameter (priv->organizer.prop,
+						    priv->organizer.sentby_param);
+		}
+	} else if (priv->organizer.sentby_param) {
+		icalproperty_remove_parameter (priv->organizer.prop, ICAL_SENTBY_PARAMETER);
+		priv->organizer.sentby_param = NULL;
+	}
+
+	if (organizer->cn) {
+		g_assert (priv->organizer.prop != NULL);
+
+		if (priv->organizer.cn_param)
+			icalparameter_set_cn (priv->organizer.cn_param,
+						  (char *) organizer->cn);
+		else {
+			priv->organizer.cn_param = icalparameter_new_cn (
+				(char *) organizer->cn);
+			icalproperty_add_parameter (priv->organizer.prop,
+						    priv->organizer.cn_param);
+		}
+	} else if (priv->organizer.cn_param) {
+		icalproperty_remove_parameter (priv->organizer.prop, ICAL_CN_PARAMETER);
+		priv->organizer.cn_param = NULL;
+	}
+
+	if (organizer->language) {
+		g_assert (priv->organizer.prop != NULL);
+
+		if (priv->organizer.language_param)
+			icalparameter_set_language (priv->organizer.language_param,
+						  (char *) organizer->language);
+		else {
+			priv->organizer.language_param = icalparameter_new_language (
+				(char *) organizer->language);
+			icalproperty_add_parameter (priv->organizer.prop,
+						    priv->organizer.language_param);
+		}
+	} else if (priv->organizer.language_param) {
+		icalproperty_remove_parameter (priv->organizer.prop, ICAL_LANGUAGE_PARAMETER);
+		priv->organizer.language_param = NULL;
+	}
+
+
+}
+
+
+/**
+ * e_cal_component_has_organizer:
+ * @comp: A calendar component object.
+ * 
+ * Check whether a calendar component object has an organizer or not.
+ * 
+ * Return value: TRUE if there is an organizer, FALSE otherwise.
+ **/
+gboolean
+e_cal_component_has_organizer (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+
+	return priv->organizer.prop != NULL;
+}
+		
+/**
+ * e_cal_component_get_percent:
+ * @comp: A calendar component object.
+ * @percent: Return value for the percent-complete property.  This should be
+ * freed using the e_cal_component_free_percent() function.
+ *
+ * Queries the percent-complete property of a calendar component object.
+ **/
+void
+e_cal_component_get_percent (ECalComponent *comp, int **percent)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (percent != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (priv->percent) {
+		*percent = g_new (int, 1);
+		**percent = icalproperty_get_percentcomplete (priv->percent);
+	} else
+		*percent = NULL;
+}
+
+/**
+ * e_cal_component_set_percent:
+ * @comp: A calendar component object.
+ * @percent: Value for the percent-complete property.
+ *
+ * Sets the percent-complete property of a calendar component object.
+ **/
+void
+e_cal_component_set_percent (ECalComponent *comp, int *percent)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!percent) {
+		if (priv->percent) {
+			icalcomponent_remove_property (priv->icalcomp, priv->percent);
+			icalproperty_free (priv->percent);
+			priv->percent = NULL;
+		}
+
+		return;
+	}
+
+	g_return_if_fail (*percent >= 0 && *percent <= 100);
+
+	if (priv->percent)
+		icalproperty_set_percentcomplete (priv->percent, *percent);
+	else {
+		priv->percent = icalproperty_new_percentcomplete (*percent);
+		icalcomponent_add_property (priv->icalcomp, priv->percent);
+	}
+}
+
+/**
+ * e_cal_component_get_priority:
+ * @comp: A calendar component object.
+ * @priority: Return value for the priority property.  This should be freed using
+ * the e_cal_component_free_priority() function.
+ *
+ * Queries the priority property of a calendar component object.
+ **/
+void
+e_cal_component_get_priority (ECalComponent *comp, int **priority)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (priority != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (priv->priority) {
+		*priority = g_new (int, 1);
+		**priority = icalproperty_get_priority (priv->priority);
+	} else
+		*priority = NULL;
+}
+
+/**
+ * e_cal_component_set_priority:
+ * @comp: A calendar component object.
+ * @priority: Value for the priority property.
+ *
+ * Sets the priority property of a calendar component object.
+ **/
+void
+e_cal_component_set_priority (ECalComponent *comp, int *priority)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!priority) {
+		if (priv->priority) {
+			icalcomponent_remove_property (priv->icalcomp, priv->priority);
+			icalproperty_free (priv->priority);
+			priv->priority = NULL;
+		}
+
+		return;
+	}
+
+	g_return_if_fail (*priority >= 0 && *priority <= 9);
+
+	if (priv->priority)
+		icalproperty_set_priority (priv->priority, *priority);
+	else {
+		priv->priority = icalproperty_new_priority (*priority);
+		icalcomponent_add_property (priv->icalcomp, priv->priority);
+	}
+}
+
+/**
+ * e_cal_component_get_recurid:
+ * @comp: A calendar component object.
+ * @recur_id: Return value for the recurrence id property
+ * 
+ * Queries the recurrence id property of a calendar component object.
+ **/
+void 
+e_cal_component_get_recurid (ECalComponent *comp, ECalComponentRange *recur_id)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (recur_id != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_datetime (&priv->recur_id.recur_time, 
+		      icalproperty_get_recurrenceid, 
+		      &recur_id->datetime);
+}
+
+/**
+ * e_cal_component_get_recurid_as_string:
+ * @comp: A calendar component object.
+ *
+ * Gets the recurrence ID property as a string.
+ *
+ * Return value: the recurrence ID as a string.
+ */
+const char *
+e_cal_component_get_recurid_as_string (ECalComponent *comp)
+{
+        ECalComponentRange range;
+        struct icaltimetype tt;
+
+	if (!e_cal_component_is_instance (comp))
+		return NULL;
+
+        e_cal_component_get_recurid (comp, &range);
+        if (!range.datetime.value)
+                return "0";
+        tt = *range.datetime.value;
+        e_cal_component_free_range (&range);
+                                                                                   
+        return icaltime_is_valid_time (tt) && !icaltime_is_null_time (tt) ?
+                icaltime_as_ical_string (tt) : "0";
+}
+
+/**
+ * e_cal_component_set_recurid:
+ * @comp: A calendar component object.
+ * @recur_id: Value for the recurrence id property.
+ * 
+ * Sets the recurrence id property of a calendar component object.
+ **/
+void
+e_cal_component_set_recurid (ECalComponent *comp, ECalComponentRange *recur_id)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+	
+	set_datetime (comp, &priv->recur_id.recur_time,
+		      icalproperty_new_recurrenceid,
+		      icalproperty_set_recurrenceid,
+		      recur_id ? &recur_id->datetime : NULL);
+}
+
+/**
+ * e_cal_component_get_rdate_list:
+ * @comp: A calendar component object.
+ * @period_list: Return value for the list of recurrence dates, as a list of
+ * #ECalComponentPeriod structures.  This should be freed using the
+ * e_cal_component_free_period_list() function.
+ *
+ * Queries the list of recurrence date properties in a calendar component
+ * object.
+ **/
+void
+e_cal_component_get_rdate_list (ECalComponent *comp, GSList **period_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (period_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_period_list (priv->rdate_list, icalproperty_get_rdate, period_list);
+}
+
+/**
+ * e_cal_component_set_rdate_list:
+ * @comp: A calendar component object.
+ * @period_list: List of #ECalComponentPeriod structures.
+ *
+ * Sets the list of recurrence dates in a calendar component object.
+ **/
+void
+e_cal_component_set_rdate_list (ECalComponent *comp, GSList *period_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_period_list (comp, icalproperty_new_rdate, &priv->rdate_list, period_list);
+
+	priv->need_sequence_inc = TRUE;
+}
+
+/**
+ * e_cal_component_has_rdates:
+ * @comp: A calendar component object.
+ *
+ * Queries whether a calendar component object has any recurrence dates defined
+ * for it.
+ *
+ * Return value: TRUE if the component has recurrence dates, FALSE otherwise.
+ **/
+gboolean
+e_cal_component_has_rdates (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, FALSE);
+
+	return (priv->rdate_list != NULL);
+}
+
+/**
+ * e_cal_component_get_rrule_list:
+ * @comp: A calendar component object.
+ * @recur_list: List of recurrence rules as struct #icalrecurrencetype
+ * structures.  This should be freed using the e_cal_component_free_recur_list()
+ * function.
+ *
+ * Queries the list of recurrence rule properties of a calendar component
+ * object.
+ **/
+void
+e_cal_component_get_rrule_list (ECalComponent *comp, GSList **recur_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (recur_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_recur_list (priv->rrule_list, icalproperty_get_rrule, recur_list);
+}
+
+/**
+ * e_cal_component_get_rrule_property_list:
+ * @comp: A calendar component object.
+ * @recur_list: Returns a list of recurrence rule properties.
+ *
+ * Queries a list of recurrence rule properties of a calendar component object.
+ **/
+void
+e_cal_component_get_rrule_property_list (ECalComponent *comp, GSList **recur_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (recur_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	*recur_list = priv->rrule_list;
+}
+
+/**
+ * e_cal_component_set_rrule_list:
+ * @comp: A calendar component object.
+ * @recur_list: List of struct #icalrecurrencetype structures.
+ *
+ * Sets the list of recurrence rules in a calendar component object.
+ **/
+void
+e_cal_component_set_rrule_list (ECalComponent *comp, GSList *recur_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_recur_list (comp, icalproperty_new_rrule, &priv->rrule_list, recur_list);
+
+	priv->need_sequence_inc = TRUE;
+}
+
+/**
+ * e_cal_component_has_rrules:
+ * @comp: A calendar component object.
+ *
+ * Queries whether a calendar component object has any recurrence rules defined
+ * for it.
+ *
+ * Return value: TRUE if the component has recurrence rules, FALSE otherwise.
+ **/
+gboolean
+e_cal_component_has_rrules (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, FALSE);
+
+	return (priv->rrule_list != NULL);
+}
+
+/**
+ * e_cal_component_has_recurrences:
+ * @comp: A calendar component object
+ *
+ * Queries whether a calendar component object has any recurrence dates or
+ * recurrence rules.
+ *
+ * Return value: TRUE if the component has recurrences, FALSE otherwise.
+ **/
+gboolean
+e_cal_component_has_recurrences (ECalComponent *comp)
+{
+	return e_cal_component_has_rdates (comp) || e_cal_component_has_rrules (comp);
+}
+
+/* Counts the elements in the by_xxx fields of an icalrecurrencetype */
+static int
+count_by_xxx (short *field, int max_elements)
+{
+	int i;
+
+	for (i = 0; i < max_elements; i++)
+		if (field[i] == ICAL_RECURRENCE_ARRAY_MAX)
+			break;
+
+	return i;
+}
+
+/**
+ * e_cal_component_has_simple_recurrence:
+ * @comp: A calendar component object.
+ *
+ * Checks whether the given calendar component object has simple recurrence
+ * rules or more complicated ones.
+ *
+ + Return value: TRUE if it has a simple recurrence rule, FALSE otherwise.
+ */
+gboolean
+e_cal_component_has_simple_recurrence (ECalComponent *comp)
+{
+	GSList *rrule_list;
+	struct icalrecurrencetype *r;
+	int n_by_second, n_by_minute, n_by_hour;
+	int n_by_day, n_by_month_day, n_by_year_day;
+	int n_by_week_no, n_by_month, n_by_set_pos;
+	int len, i;
+	gboolean simple = FALSE;
+
+	if (!e_cal_component_has_recurrences (comp))
+		return TRUE;
+	
+	e_cal_component_get_rrule_list (comp, &rrule_list);
+	len = g_slist_length (rrule_list);
+	if (len > 1
+	    || e_cal_component_has_rdates (comp)
+	    || e_cal_component_has_exrules (comp))
+		goto cleanup;
+
+	/* Down to one rule, so test that one */
+	r = rrule_list->data;
+
+	/* Any funky frequency? */
+	if (r->freq == ICAL_SECONDLY_RECURRENCE
+	    || r->freq == ICAL_MINUTELY_RECURRENCE
+	    || r->freq == ICAL_HOURLY_RECURRENCE)
+		goto cleanup;
+
+	/* Any funky BY_* */
+#define N_HAS_BY(field) (count_by_xxx (field, sizeof (field) / sizeof (field[0])))
+
+	n_by_second = N_HAS_BY (r->by_second);
+	n_by_minute = N_HAS_BY (r->by_minute);
+	n_by_hour = N_HAS_BY (r->by_hour);
+	n_by_day = N_HAS_BY (r->by_day);
+	n_by_month_day = N_HAS_BY (r->by_month_day);
+	n_by_year_day = N_HAS_BY (r->by_year_day);
+	n_by_week_no = N_HAS_BY (r->by_week_no);
+	n_by_month = N_HAS_BY (r->by_month);
+	n_by_set_pos = N_HAS_BY (r->by_set_pos);
+
+	if (n_by_second != 0
+	    || n_by_minute != 0
+	    || n_by_hour != 0)
+		goto cleanup;	
+
+	switch (r->freq) {
+	case ICAL_DAILY_RECURRENCE:
+		if (n_by_day != 0
+		    || n_by_month_day != 0
+		    || n_by_year_day != 0
+		    || n_by_week_no != 0
+		    || n_by_month != 0
+		    || n_by_set_pos != 0)
+			goto cleanup;
+
+		simple = TRUE;
+		break;
+
+	case ICAL_WEEKLY_RECURRENCE:
+		if (n_by_month_day != 0
+		    || n_by_year_day != 0
+		    || n_by_week_no != 0
+		    || n_by_month != 0
+		    || n_by_set_pos != 0)
+			goto cleanup;
+
+		for (i = 0; i < 8 && r->by_day[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
+			int pos;
+			pos = icalrecurrencetype_day_position (r->by_day[i]);
+
+			if (pos != 0)
+				goto cleanup;
+		}
+		
+		simple = TRUE;
+		break;
+
+	case ICAL_MONTHLY_RECURRENCE:
+		if (n_by_year_day != 0
+		    || n_by_week_no != 0
+		    || n_by_month != 0
+		    || n_by_set_pos > 1)
+			goto cleanup;
+
+		if (n_by_month_day == 1) {
+			int nth;
+
+			if (n_by_set_pos != 0)
+				goto cleanup;
+
+			nth = r->by_month_day[0];
+			if (nth < 1 && nth != -1)
+				goto cleanup;
+			
+			simple = TRUE;
+			
+		} else if (n_by_day == 1) {
+			enum icalrecurrencetype_weekday weekday;
+			int pos;
+
+			/* Outlook 2000 uses BYDAY=TU;BYSETPOS=2, and will not
+			   accept BYDAY=2TU. So we now use the same as Outlook
+			   by default. */
+
+			weekday = icalrecurrencetype_day_day_of_week (r->by_day[0]);
+			pos = icalrecurrencetype_day_position (r->by_day[0]);
+
+			if (pos == 0) {
+				if (n_by_set_pos != 1)
+					goto cleanup;
+				pos = r->by_set_pos[0];
+			} else if (pos < 0) {
+				goto cleanup;
+			}
+
+			switch (weekday) {
+			case ICAL_MONDAY_WEEKDAY:
+			case ICAL_TUESDAY_WEEKDAY:
+			case ICAL_WEDNESDAY_WEEKDAY:
+			case ICAL_THURSDAY_WEEKDAY:
+			case ICAL_FRIDAY_WEEKDAY:
+			case ICAL_SATURDAY_WEEKDAY:
+			case ICAL_SUNDAY_WEEKDAY:
+				break;
+
+			default:
+				goto cleanup;
+			}
+		} else {
+			goto cleanup;
+		}
+		
+		simple = TRUE;
+		break;
+
+	case ICAL_YEARLY_RECURRENCE:
+		if (n_by_day != 0
+		    || n_by_month_day != 0
+		    || n_by_year_day != 0
+		    || n_by_week_no != 0
+		    || n_by_month != 0
+		    || n_by_set_pos != 0)
+			goto cleanup;
+		
+		simple = TRUE;
+		break;
+
+	default:
+		goto cleanup;
+	}
+
+ cleanup:
+	e_cal_component_free_recur_list (rrule_list);
+
+	return simple;
+}
+
+/**
+ * e_cal_component_is_instance:
+ * @comp: A calendar component object.
+ *
+ * Checks whether a calendar component object is an instance of a recurring
+ * event.
+ *
+ * Return value: TRUE if it is an instance, FALSE if not.
+ */
+gboolean 
+e_cal_component_is_instance (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+
+	return !(priv->recur_id.recur_time.prop == NULL);
+}
+
+/**
+ * e_cal_component_get_sequence:
+ * @comp: A calendar component object.
+ * @sequence: Return value for the sequence number.  This should be freed using
+ * e_cal_component_free_sequence().
+ *
+ * Queries the sequence number of a calendar component object.
+ **/
+void
+e_cal_component_get_sequence (ECalComponent *comp, int **sequence)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (sequence != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!priv->sequence) {
+		*sequence = NULL;
+		return;
+	}
+
+	*sequence = g_new (int, 1);
+	**sequence = icalproperty_get_sequence (priv->sequence);
+}
+
+/**
+ * e_cal_component_set_sequence:
+ * @comp: A calendar component object.
+ * @sequence: Sequence number value.
+ *
+ * Sets the sequence number of a calendar component object.  Normally this
+ * function should not be called, since the sequence number is incremented
+ * automatically at the proper times.
+ **/
+void
+e_cal_component_set_sequence (ECalComponent *comp, int *sequence)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	priv->need_sequence_inc = FALSE;
+
+	if (!sequence) {
+		if (priv->sequence) {
+			icalcomponent_remove_property (priv->icalcomp, priv->sequence);
+			icalproperty_free (priv->sequence);
+			priv->sequence = NULL;
+		}
+
+		return;
+	}
+
+	if (priv->sequence)
+		icalproperty_set_sequence (priv->sequence, *sequence);
+	else {
+		priv->sequence = icalproperty_new_sequence (*sequence);
+		icalcomponent_add_property (priv->icalcomp, priv->sequence);
+	}
+}
+
+/**
+ * e_cal_component_get_status:
+ * @comp: A calendar component object.
+ * @status: Return value for the status value.  It is set to #ICAL_STATUS_NONE
+ * if the component has no status property.
+ *
+ * Queries the status property of a calendar component object.
+ **/
+void
+e_cal_component_get_status (ECalComponent *comp, icalproperty_status *status)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (status != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!priv->status) {
+		*status = ICAL_STATUS_NONE;
+		return;
+	}
+
+	*status = icalproperty_get_status (priv->status);
+}
+
+/**
+ * e_cal_component_set_status:
+ * @comp: A calendar component object.
+ * @status: Status value.  You should use #ICAL_STATUS_NONE if you want to unset
+ * this property.
+ *
+ * Sets the status property of a calendar component object.
+ **/
+void
+e_cal_component_set_status (ECalComponent *comp, icalproperty_status status)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	priv->need_sequence_inc = TRUE;
+
+	if (status == ICAL_STATUS_NONE) {
+		if (priv->status) {
+			icalcomponent_remove_property (priv->icalcomp, priv->status);
+			icalproperty_free (priv->status);
+			priv->status = NULL;
+		}
+
+		return;
+	}
+
+	if (priv->status) {
+		icalproperty_set_status (priv->status, status);
+	} else {
+		priv->status = icalproperty_new_status (status);
+		icalcomponent_add_property (priv->icalcomp, priv->status);
+	}
+}
+
+/**
+ * e_cal_component_get_summary:
+ * @comp: A calendar component object.
+ * @summary: Return value for the summary property and its parameters.
+ *
+ * Queries the summary of a calendar component object.
+ **/
+void
+e_cal_component_get_summary (ECalComponent *comp, ECalComponentText *summary)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (summary != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (priv->summary.prop)
+		summary->value = icalproperty_get_summary (priv->summary.prop);
+	else
+		summary->value = NULL;
+
+	if (priv->summary.altrep_param)
+		summary->altrep = icalparameter_get_altrep (priv->summary.altrep_param);
+	else
+		summary->altrep = NULL;
+}
+
+typedef struct {
+	const char *old_summary;
+	const char *new_summary;
+} SetAlarmDescriptionData;
+
+static void
+set_alarm_description_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	icalcomponent *alarm;
+	icalproperty *icalprop, *desc_prop;
+	SetAlarmDescriptionData *sadd;
+	gboolean changed = FALSE;
+	const char *old_summary = NULL;
+
+	alarm = value;
+	sadd = user_data;
+
+	/* set the new description on the alarm */
+	desc_prop = icalcomponent_get_first_property (alarm, ICAL_DESCRIPTION_PROPERTY);
+	if (desc_prop)
+		old_summary = icalproperty_get_description (desc_prop);
+	else
+		desc_prop = icalproperty_new_description (sadd->new_summary);
+
+	/* remove the X-EVOLUTION-NEEDS_DESCRIPTION property */
+	icalprop = icalcomponent_get_first_property (alarm, ICAL_X_PROPERTY);
+	while (icalprop) {
+		const char *x_name;
+
+		x_name = icalproperty_get_x_name (icalprop);
+		if (!strcmp (x_name, "X-EVOLUTION-NEEDS-DESCRIPTION")) {
+			icalcomponent_remove_property (alarm, icalprop);
+			icalproperty_free (icalprop);
+
+			icalproperty_set_description (desc_prop, sadd->new_summary);
+			changed = TRUE;
+			break;
+		}
+
+		icalprop = icalcomponent_get_next_property (alarm, ICAL_X_PROPERTY);
+	}
+
+	if (!changed) {
+		if (!strcmp (old_summary ? old_summary : "", sadd->old_summary ? sadd->old_summary : "")) {
+			icalproperty_set_description (desc_prop, sadd->new_summary);
+		}
+	}
+}
+
+/**
+ * e_cal_component_set_summary:
+ * @comp: A calendar component object.
+ * @summary: Summary property and its parameters.
+ *
+ * Sets the summary of a calendar component object.
+ **/
+void
+e_cal_component_set_summary (ECalComponent *comp, ECalComponentText *summary)
+{
+	ECalComponentPrivate *priv;
+	SetAlarmDescriptionData sadd;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!summary) {
+		if (priv->summary.prop) {
+			icalcomponent_remove_property (priv->icalcomp, priv->summary.prop);
+			icalproperty_free (priv->summary.prop);
+
+			priv->summary.prop = NULL;
+			priv->summary.altrep_param = NULL;
+		}
+
+		return;
+	}
+
+	g_return_if_fail (summary->value != NULL);
+
+	if (priv->summary.prop) {
+		sadd.old_summary = icalproperty_get_summary (priv->summary.prop);
+		icalproperty_set_summary (priv->summary.prop, (char *) summary->value);
+	} else {
+		sadd.old_summary = NULL;
+		priv->summary.prop = icalproperty_new_summary ((char *) summary->value);
+		icalcomponent_add_property (priv->icalcomp, priv->summary.prop);
+	}
+
+	if (summary->altrep) {
+		g_assert (priv->summary.prop != NULL);
+
+		if (priv->summary.altrep_param)
+			icalparameter_set_altrep (priv->summary.altrep_param,
+						  (char *) summary->altrep);
+		else {
+			priv->summary.altrep_param = icalparameter_new_altrep (
+				(char *) summary->altrep);
+			icalproperty_add_parameter (priv->summary.prop,
+						    priv->summary.altrep_param);
+		}
+	} else if (priv->summary.altrep_param) {
+ 		icalproperty_remove_parameter (priv->summary.prop, ICAL_ALTREP_PARAMETER);
+ 		priv->summary.altrep_param = NULL; 
+	}
+
+	/* look for alarms that need a description */
+	sadd.new_summary = summary->value;
+	g_hash_table_foreach (priv->alarm_uid_hash, set_alarm_description_cb, &sadd);
+}
+
+/**
+ * e_cal_component_get_transparency:
+ * @comp: A calendar component object.
+ * @transp: Return value for the time transparency.
+ *
+ * Queries the time transparency of a calendar component object.
+ **/
+void
+e_cal_component_get_transparency (ECalComponent *comp, ECalComponentTransparency *transp)
+{
+	ECalComponentPrivate *priv;
+	icalproperty_transp ical_transp;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (transp != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!priv->transparency) {
+		*transp = E_CAL_COMPONENT_TRANSP_NONE;
+		return;
+	}
+
+	ical_transp = icalproperty_get_transp (priv->transparency);
+
+	switch (ical_transp)
+	{
+	case ICAL_TRANSP_TRANSPARENT:
+	case ICAL_TRANSP_TRANSPARENTNOCONFLICT:
+	  *transp = E_CAL_COMPONENT_TRANSP_TRANSPARENT;
+	  break;
+
+	case ICAL_TRANSP_OPAQUE:
+	case ICAL_TRANSP_OPAQUENOCONFLICT:
+	  *transp = E_CAL_COMPONENT_TRANSP_OPAQUE;
+	  break;
+
+	default:
+	  *transp = E_CAL_COMPONENT_TRANSP_UNKNOWN;
+	  break;
+	}
+}
+
+/**
+ * e_cal_component_set_transparency:
+ * @comp: A calendar component object.
+ * @transp: Time transparency value.
+ *
+ * Sets the time transparency of a calendar component object.
+ **/
+void
+e_cal_component_set_transparency (ECalComponent *comp, ECalComponentTransparency transp)
+{
+	ECalComponentPrivate *priv;
+	icalproperty_transp ical_transp;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (transp != E_CAL_COMPONENT_TRANSP_UNKNOWN);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+
+	if (transp == E_CAL_COMPONENT_TRANSP_NONE) {
+		if (priv->transparency) {
+			icalcomponent_remove_property (priv->icalcomp, priv->transparency);
+			icalproperty_free (priv->transparency);
+			priv->transparency = NULL;
+		}
+
+		return;
+	}
+
+	switch (transp) {
+	case E_CAL_COMPONENT_TRANSP_TRANSPARENT:
+	  ical_transp = ICAL_TRANSP_TRANSPARENT;
+		break;
+
+	case E_CAL_COMPONENT_TRANSP_OPAQUE:
+	  ical_transp = ICAL_TRANSP_OPAQUE;
+		break;
+
+	default:
+		g_assert_not_reached ();
+		ical_transp = ICAL_TRANSP_NONE;
+	}
+
+	if (priv->transparency)
+		icalproperty_set_transp (priv->transparency, ical_transp);
+	else {
+		priv->transparency = icalproperty_new_transp (ical_transp);
+		icalcomponent_add_property (priv->icalcomp, priv->transparency);
+	}
+}
+
+/**
+ * e_cal_component_get_url:
+ * @comp: A calendar component object.
+ * @url: Return value for the URL.
+ *
+ * Queries the uniform resource locator property of a calendar component object.
+ **/
+void
+e_cal_component_get_url (ECalComponent *comp, const char **url)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (url != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (priv->url)
+		*url = icalproperty_get_url (priv->url);
+	else
+		*url = NULL;
+}
+
+/**
+ * e_cal_component_set_url:
+ * @comp: A calendar component object.
+ * @url: URL value.
+ *
+ * Sets the uniform resource locator property of a calendar component object.
+ **/
+void
+e_cal_component_set_url (ECalComponent *comp, const char *url)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!url || !(*url)) {
+		if (priv->url) {
+			icalcomponent_remove_property (priv->icalcomp, priv->url);
+			icalproperty_free (priv->url);
+			priv->url = NULL;
+		}
+
+		return;
+	}
+
+	if (priv->url)
+		icalproperty_set_url (priv->url, (char *) url);
+	else {
+		priv->url = icalproperty_new_url ((char *) url);
+		icalcomponent_add_property (priv->icalcomp, priv->url);
+	}
+}
+
+/* Gets a text list value */
+static void
+get_attendee_list (GSList *attendee_list, GSList **al)
+{
+	GSList *l;
+
+	*al = NULL;
+
+	if (!attendee_list)
+		return;
+
+	for (l = attendee_list; l; l = l->next) {
+		struct attendee *attendee;
+		ECalComponentAttendee *a;
+
+		attendee = l->data;
+		g_assert (attendee->prop != NULL);
+
+		a = g_new0 (ECalComponentAttendee, 1);
+		a->value = icalproperty_get_attendee (attendee->prop);
+
+		if (attendee->member_param)
+			a->member = icalparameter_get_member (attendee->member_param);		
+		if (attendee->cutype_param)
+			a->cutype = icalparameter_get_cutype (attendee->cutype_param);
+		else
+			a->cutype = ICAL_CUTYPE_UNKNOWN;
+		if (attendee->role_param)
+			a->role = icalparameter_get_role (attendee->role_param);
+		else
+			a->role = ICAL_ROLE_REQPARTICIPANT;
+		if (attendee->partstat_param)
+			a->status = icalparameter_get_partstat (attendee->partstat_param);
+		else
+			a->status = ICAL_PARTSTAT_NEEDSACTION;
+		if (attendee->rsvp_param && icalparameter_get_rsvp (attendee->rsvp_param) == ICAL_RSVP_TRUE)
+			a->rsvp = TRUE;
+		else
+			a->rsvp = FALSE;
+		if (attendee->delfrom_param)
+			a->delfrom = icalparameter_get_delegatedfrom (attendee->delfrom_param);
+		if (attendee->delto_param)
+			a->delto = icalparameter_get_delegatedto (attendee->delto_param);
+		if (attendee->sentby_param)
+			a->sentby = icalparameter_get_sentby (attendee->sentby_param);
+		if (attendee->cn_param)
+			a->cn = icalparameter_get_cn (attendee->cn_param);
+		if (attendee->language_param)
+			a->language = icalparameter_get_language (attendee->language_param);
+
+		*al = g_slist_prepend (*al, a);
+	}
+
+	*al = g_slist_reverse (*al);
+}
+
+
+/* Sets a text list value */
+static void
+set_attendee_list (icalcomponent *icalcomp,
+		   GSList **attendee_list,
+		   GSList *al)
+{
+	GSList *l;
+
+	/* Remove old attendees */
+
+	for (l = *attendee_list; l; l = l->next) {
+		struct attendee *attendee;
+
+		attendee = l->data;
+		g_assert (attendee->prop != NULL);
+
+		icalcomponent_remove_property (icalcomp, attendee->prop);
+		icalproperty_free (attendee->prop);
+		g_free (attendee);
+	}
+
+	g_slist_free (*attendee_list);
+	*attendee_list = NULL;
+
+	/* Add in new attendees */
+
+	for (l = al; l; l = l->next) {
+		ECalComponentAttendee *a;
+		struct attendee *attendee;
+
+		a = l->data;
+		g_return_if_fail (a->value != NULL);
+
+		attendee = g_new0 (struct attendee, 1);
+
+		attendee->prop = icalproperty_new_attendee (a->value);
+		icalcomponent_add_property (icalcomp, attendee->prop);
+
+		if (a->member) {
+			attendee->member_param = icalparameter_new_member (a->member);
+			icalproperty_add_parameter (attendee->prop, attendee->member_param);
+		}
+
+		attendee->cutype_param = icalparameter_new_cutype (a->cutype);
+		icalproperty_add_parameter (attendee->prop, attendee->cutype_param);
+
+		attendee->role_param = icalparameter_new_role (a->role);
+		icalproperty_add_parameter (attendee->prop, attendee->role_param);
+
+		attendee->partstat_param = icalparameter_new_partstat (a->status);
+		icalproperty_add_parameter (attendee->prop, attendee->partstat_param);
+
+		if (a->rsvp)
+			attendee->rsvp_param = icalparameter_new_rsvp (ICAL_RSVP_TRUE);
+		else
+			attendee->rsvp_param = icalparameter_new_rsvp (ICAL_RSVP_FALSE);
+		icalproperty_add_parameter (attendee->prop, attendee->rsvp_param);
+	
+		if (a->delfrom) {
+			attendee->delfrom_param = icalparameter_new_delegatedfrom (a->delfrom);
+			icalproperty_add_parameter (attendee->prop, attendee->delfrom_param);
+		}
+		if (a->delto) {
+			attendee->delto_param = icalparameter_new_delegatedto (a->delto);
+			icalproperty_add_parameter (attendee->prop, attendee->delto_param);
+		}
+		if (a->sentby) {
+			attendee->sentby_param = icalparameter_new_sentby (a->sentby);
+			icalproperty_add_parameter (attendee->prop, attendee->sentby_param);
+		}
+		if (a->cn) {
+			attendee->cn_param = icalparameter_new_cn (a->cn);
+			icalproperty_add_parameter (attendee->prop, attendee->cn_param);
+		}
+		if (a->language) {
+			attendee->language_param = icalparameter_new_language (a->language);
+			icalproperty_add_parameter (attendee->prop, attendee->language_param);
+		}
+
+		*attendee_list = g_slist_prepend (*attendee_list, attendee);
+	}
+
+	*attendee_list = g_slist_reverse (*attendee_list);
+}
+
+/**
+ * e_cal_component_get_attendee_list: 
+ * @comp: A calendar component object. 
+ * @attendee_list: Return value for the attendee property.
+ * This should be freed using the e_cal_component_free_attendee_list ()
+ * function.
+ * 
+ * Queries the attendee properties of the calendar component object
+ **/
+void
+e_cal_component_get_attendee_list (ECalComponent *comp, GSList **attendee_list)
+{
+	ECalComponentPrivate *priv;
+	
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (attendee_list != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	get_attendee_list (priv->attendee_list, attendee_list);
+}
+
+/**
+ * e_cal_component_set_attendee_list:
+ * @comp: A calendar component object. 
+ * @attendee_list: Values for attendee properties
+ * 
+ * Sets the attendees of a calendar component object
+ **/
+void
+e_cal_component_set_attendee_list (ECalComponent *comp, GSList *attendee_list)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	set_attendee_list (priv->icalcomp, &priv->attendee_list, attendee_list);
+}
+
+/**
+ * e_cal_component_has_attendees:
+ * @comp: A calendar component object.
+ *
+ * Queries a calendar component object for the existence of attendees.
+ *
+ * Return value: TRUE if there are attendees, FALSE if not.
+ */
+gboolean
+e_cal_component_has_attendees (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+
+	if (g_slist_length (priv->attendee_list) > 0)
+		return TRUE;
+	
+	return FALSE;
+}
+
+/**
+ * e_cal_component_get_location:
+ * @comp: A calendar component object
+ * @location: Return value for the location.
+ * 
+ * Queries the location property of a calendar component object.
+ **/
+void
+e_cal_component_get_location (ECalComponent *comp, const char **location)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (location != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (priv->location)
+		*location = icalproperty_get_location (priv->location);
+	else
+		*location = NULL;
+}
+
+/**
+ * e_cal_component_set_location:
+ * @comp: A calendar component object.
+ * @location: Location value.
+ * 
+ * Sets the location property of a calendar component object.
+ **/
+void
+e_cal_component_set_location (ECalComponent *comp, const char *location)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	if (!location || !(*location)) {
+		if (priv->location) {
+			icalcomponent_remove_property (priv->icalcomp, priv->location);
+			icalproperty_free (priv->location);
+			priv->location = NULL;
+		}
+
+		return;
+	}
+
+	if (priv->location)
+		icalproperty_set_location (priv->location, (char *) location);
+	else {
+		priv->location = icalproperty_new_location ((char *) location);
+		icalcomponent_add_property (priv->icalcomp, priv->location);
+	}
+}
+
+
+
+/**
+ * e_cal_component_free_categories_list:
+ * @categ_list: List of category strings.
+ *
+ * Frees a list of category strings.
+ **/
+void
+e_cal_component_free_categories_list (GSList *categ_list)
+{
+	GSList *l;
+
+	for (l = categ_list; l; l = l->next)
+		g_free (l->data);
+
+	g_slist_free (categ_list);
+}
+
+/**
+ * e_cal_component_free_datetime:
+ * @dt: A date/time structure.
+ *
+ * Frees a date/time structure.
+ **/
+void
+e_cal_component_free_datetime (ECalComponentDateTime *dt)
+{
+	g_return_if_fail (dt != NULL);
+
+	g_free (dt->value);
+	g_free ((char*)dt->tzid);
+}
+
+/**
+ * e_cal_component_free_range:
+ * @range: A #ECalComponentRange.
+ *
+ * Frees an #ECalComponentRange structure.
+ */
+void 
+e_cal_component_free_range (ECalComponentRange *range)
+{
+	g_return_if_fail (range != NULL);
+
+	e_cal_component_free_datetime (&range->datetime);
+}
+
+/**
+ * e_cal_component_free_exdate_list:
+ * @exdate_list: List of #ECalComponentDateTime structures.
+ *
+ * Frees a list of #ECalComponentDateTime structures as returned by the
+ * e_cal_component_get_exdate_list() function.
+ **/
+void
+e_cal_component_free_exdate_list (GSList *exdate_list)
+{
+	GSList *l;
+
+	for (l = exdate_list; l; l = l->next) {
+		ECalComponentDateTime *cdt;
+
+		g_assert (l->data != NULL);
+		cdt = l->data;
+
+		g_assert (cdt->value != NULL);
+		g_free (cdt->value);
+		g_free ((char*)cdt->tzid);
+
+		g_free (cdt);
+	}
+
+	g_slist_free (exdate_list);
+}
+
+/**
+ * e_cal_component_free_geo:
+ * @geo: An #icalgeotype structure.
+ *
+ * Frees a struct #icalgeotype structure as returned by the calendar component
+ * functions.
+ **/
+void
+e_cal_component_free_geo (struct icalgeotype *geo)
+{
+	g_return_if_fail (geo != NULL);
+
+	g_free (geo);
+}
+
+/**
+ * e_cal_component_free_icaltimetype:
+ * @t: An #icaltimetype structure.
+ *
+ * Frees a struct #icaltimetype value as returned by the calendar component
+ * functions.
+ **/
+void
+e_cal_component_free_icaltimetype (struct icaltimetype *t)
+{
+	g_return_if_fail (t != NULL);
+
+	g_free (t);
+}
+
+/**
+ * e_cal_component_free_percent:
+ * @percent: Percent value.
+ *
+ * Frees a percent value as returned by the e_cal_component_get_percent()
+ * function.
+ **/
+void
+e_cal_component_free_percent (int *percent)
+{
+	g_return_if_fail (percent != NULL);
+
+	g_free (percent);
+}
+
+/**
+ * e_cal_component_free_priority:
+ * @priority: Priority value.
+ *
+ * Frees a priority value as returned by the e_cal_component_get_priority()
+ * function.
+ **/
+void
+e_cal_component_free_priority (int *priority)
+{
+	g_return_if_fail (priority != NULL);
+
+	g_free (priority);
+}
+
+/**
+ * e_cal_component_free_period_list:
+ * @period_list: List of #ECalComponentPeriod structures.
+ *
+ * Frees a list of #ECalComponentPeriod structures.
+ **/
+void
+e_cal_component_free_period_list (GSList *period_list)
+{
+	GSList *l;
+
+	for (l = period_list; l; l = l->next) {
+		ECalComponentPeriod *period;
+
+		g_assert (l->data != NULL);
+
+		period = l->data;
+		g_free (period);
+	}
+
+	g_slist_free (period_list);
+}
+
+/**
+ * e_cal_component_free_recur_list:
+ * @recur_list: List of struct #icalrecurrencetype structures.
+ *
+ * Frees a list of struct #icalrecurrencetype structures.
+ **/
+void
+e_cal_component_free_recur_list (GSList *recur_list)
+{
+	GSList *l;
+
+	for (l = recur_list; l; l = l->next) {
+		struct icalrecurrencetype *r;
+
+		g_assert (l->data != NULL);
+		r = l->data;
+
+		g_free (r);
+	}
+
+	g_slist_free (recur_list);
+}
+
+/**
+ * e_cal_component_free_sequence:
+ * @sequence: Sequence number value.
+ *
+ * Frees a sequence number value.
+ **/
+void
+e_cal_component_free_sequence (int *sequence)
+{
+	g_return_if_fail (sequence != NULL);
+
+	g_free (sequence);
+}
+
+/**
+ * e_cal_component_free_text_list:
+ * @text_list: List of #ECalComponentText structures.
+ *
+ * Frees a list of #ECalComponentText structures.  This function should only be
+ * used to free lists of text values as returned by the other getter functions
+ * of #ECalComponent.
+ **/
+void
+e_cal_component_free_text_list (GSList *text_list)
+{
+	GSList *l;
+
+	for (l = text_list; l; l = l->next) {
+		ECalComponentText *text;
+
+		g_assert (l->data != NULL);
+
+		text = l->data;
+		g_return_if_fail (text != NULL);
+		g_free (text);
+	}
+
+	g_slist_free (text_list);
+}
+
+/**
+ * e_cal_component_free_attendee_list:
+ * @attendee_list:  List of attendees.
+ * 
+ * Frees a list of #ECalComponentAttendee structures.
+ * 
+ **/
+void
+e_cal_component_free_attendee_list (GSList *attendee_list)
+{
+	GSList *l;
+
+	for (l = attendee_list; l; l = l->next) {
+		ECalComponentAttendee *attendee;
+
+		g_assert (l->data != NULL);
+
+		attendee = l->data;
+		g_return_if_fail (attendee != NULL);
+		g_free (attendee);
+	}
+
+	g_slist_free (attendee_list);
+}
+
+
+
+/**
+ * e_cal_component_has_alarms:
+ * @comp: A calendar component object.
+ *
+ * Checks whether the component has any alarms.
+ *
+ * Return value: TRUE if the component has any alarms.
+ **/
+gboolean
+e_cal_component_has_alarms (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_val_if_fail (comp != NULL, FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, FALSE);
+
+	return g_hash_table_size (priv->alarm_uid_hash) != 0;
+}
+
+/**
+ * e_cal_component_add_alarm:
+ * @comp: A calendar component.
+ * @alarm: An alarm.
+ * 
+ * Adds an alarm subcomponent to a calendar component.  You should have created
+ * the @alarm by using e_cal_component_alarm_new(); it is invalid to use a
+ * #ECalComponentAlarm structure that came from e_cal_component_get_alarm().  After
+ * adding the alarm, the @alarm structure is no longer valid because the
+ * internal structures may change and you should get rid of it by using
+ * e_cal_component_alarm_free().
+ **/
+void
+e_cal_component_add_alarm (ECalComponent *comp, ECalComponentAlarm *alarm)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (alarm != NULL);
+
+	priv = comp->priv;
+
+	add_alarm (comp, alarm->icalcomp, icalproperty_get_x (alarm->uid));
+	icalcomponent_add_component (priv->icalcomp, alarm->icalcomp);
+}
+
+/**
+ * e_cal_component_remove_alarm:
+ * @comp: A calendar component.
+ * @auid: UID of the alarm to remove.
+ * 
+ * Removes an alarm subcomponent from a calendar component.  If the alarm that
+ * corresponds to the specified @auid had been fetched with
+ * e_cal_component_get_alarm(), then those alarm structures will be invalid; you
+ * should get rid of them with e_cal_component_alarm_free() before using this
+ * function.
+ **/
+void
+e_cal_component_remove_alarm (ECalComponent *comp, const char *auid)
+{
+	ECalComponentPrivate *priv;
+	icalcomponent *alarm;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+	g_return_if_fail (auid != NULL);
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	alarm = g_hash_table_lookup (priv->alarm_uid_hash, auid);
+	if (!alarm)
+		return;
+
+	g_hash_table_remove (priv->alarm_uid_hash, auid);
+	icalcomponent_remove_component (priv->icalcomp, alarm);
+	icalcomponent_free (alarm);
+}
+
+static gboolean
+for_each_remove_all_alarms (gpointer key, gpointer value, gpointer data)
+{
+	ECalComponent *comp = E_CAL_COMPONENT (data);
+	ECalComponentPrivate *priv;
+	icalcomponent *alarm = value;
+	
+	priv = comp->priv;
+	
+	icalcomponent_remove_component (priv->icalcomp, alarm);
+	icalcomponent_free (alarm);
+
+	return TRUE;	
+}
+
+/**
+ * e_cal_component_remove_all_alarms:
+ * @comp: A calendar component
+ * 
+ * Remove all alarms from the calendar component
+ **/
+void
+e_cal_component_remove_all_alarms (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+
+	g_return_if_fail (comp != NULL);
+	g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+	priv = comp->priv;
+	g_return_if_fail (priv->icalcomp != NULL);
+
+	g_hash_table_foreach_remove (priv->alarm_uid_hash, for_each_remove_all_alarms, comp);
+}
+
+
+/* Scans an icalproperty from a calendar component and adds its mapping to our
+ * own alarm structure.
+ */
+static void
+scan_alarm_property (ECalComponentAlarm *alarm, icalproperty *prop)
+{
+	icalproperty_kind kind;
+	const char *xname;
+
+	kind = icalproperty_isa (prop);
+
+	switch (kind) {
+	case ICAL_ACTION_PROPERTY:
+		alarm->action = prop;
+		break;
+
+	case ICAL_ATTACH_PROPERTY:
+		/* FIXME: mail alarms may have any number of these, not just one */
+		alarm->attach = prop;
+		break;
+
+	case ICAL_DESCRIPTION_PROPERTY:
+		alarm->description.prop = prop;
+		alarm->description.altrep_param = icalproperty_get_first_parameter (
+			prop, ICAL_ALTREP_PARAMETER);
+		break;
+
+	case ICAL_DURATION_PROPERTY:
+		alarm->duration = prop;
+		break;
+
+	case ICAL_REPEAT_PROPERTY:
+		alarm->repeat = prop;
+		break;
+
+	case ICAL_TRIGGER_PROPERTY:
+		alarm->trigger = prop;
+		break;
+
+	case ICAL_ATTENDEE_PROPERTY:
+		scan_attendee (&alarm->attendee_list, prop);
+		break;
+		
+	case ICAL_X_PROPERTY:
+		xname = icalproperty_get_x_name (prop);
+		g_assert (xname != NULL);
+
+		if (strcmp (xname, EVOLUTION_ALARM_UID_PROPERTY) == 0)
+			alarm->uid = prop;
+
+		break;
+
+	default:
+		break;
+	}
+}
+
+/* Creates a ECalComponentAlarm from a libical alarm subcomponent */
+static ECalComponentAlarm *
+make_alarm (icalcomponent *subcomp)
+{
+	ECalComponentAlarm *alarm;
+	icalproperty *prop;
+
+	alarm = g_new (ECalComponentAlarm, 1);
+
+	alarm->icalcomp = subcomp;
+	alarm->uid = NULL;
+
+	alarm->action = NULL;
+	alarm->attach = NULL;
+	alarm->description.prop = NULL;
+	alarm->description.altrep_param = NULL;
+	alarm->duration = NULL;
+	alarm->repeat = NULL;
+	alarm->trigger = NULL;
+	alarm->attendee_list = NULL;
+	
+	for (prop = icalcomponent_get_first_property (subcomp, ICAL_ANY_PROPERTY);
+	     prop;
+	     prop = icalcomponent_get_next_property (subcomp, ICAL_ANY_PROPERTY))
+		scan_alarm_property (alarm, prop);
+
+	g_assert (alarm->uid != NULL);
+
+	return alarm;
+}
+
+/**
+ * e_cal_component_get_alarm_uids:
+ * @comp: A calendar component.
+ *
+ * Builds a list of the unique identifiers of the alarm subcomponents inside a
+ * calendar component.
+ *
+ * Return value: List of unique identifiers for alarms.  This should be freed
+ * using cal_obj_uid_list_free().
+ **/
+GList *
+e_cal_component_get_alarm_uids (ECalComponent *comp)
+{
+	ECalComponentPrivate *priv;
+	icalcompiter iter;
+	GList *l;
+
+	g_return_val_if_fail (comp != NULL, NULL);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, NULL);
+
+	l = NULL;
+	for (iter = icalcomponent_begin_component (priv->icalcomp, ICAL_VALARM_COMPONENT);
+	     icalcompiter_deref (&iter) != NULL;
+	     icalcompiter_next (&iter)) {
+		icalcomponent *subcomp;
+		icalproperty *prop;
+		
+		subcomp = icalcompiter_deref (&iter);
+		for (prop = icalcomponent_get_first_property (subcomp, ICAL_X_PROPERTY);
+		     prop;
+		     prop = icalcomponent_get_next_property (subcomp, ICAL_X_PROPERTY)) {
+			const char *xname;
+			
+			xname = icalproperty_get_x_name (prop);
+			g_assert (xname != NULL);
+			
+			if (strcmp (xname, EVOLUTION_ALARM_UID_PROPERTY) == 0) {
+				const char *auid;
+				
+				auid = alarm_uid_from_prop (prop);
+				l = g_list_append (l, g_strdup (auid));
+			}
+		}
+	}
+
+	return l;
+}
+
+/**
+ * e_cal_component_get_alarm:
+ * @comp: A calendar component.
+ * @auid: Unique identifier for the sought alarm subcomponent.
+ *
+ * Queries a particular alarm subcomponent of a calendar component.
+ *
+ * Return value: The alarm subcomponent that corresponds to the specified @auid,
+ * or #NULL if no alarm exists with that UID.  This should be freed using
+ * e_cal_component_alarm_free().
+ **/
+ECalComponentAlarm *
+e_cal_component_get_alarm (ECalComponent *comp, const char *auid)
+{
+	ECalComponentPrivate *priv;
+	icalcomponent *alarm;
+
+	g_return_val_if_fail (comp != NULL, NULL);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
+
+	priv = comp->priv;
+	g_return_val_if_fail (priv->icalcomp != NULL, NULL);
+
+	g_return_val_if_fail (auid != NULL, NULL);
+
+	alarm = g_hash_table_lookup (priv->alarm_uid_hash, auid);
+
+	if (alarm)
+		return make_alarm (alarm);
+	else
+		return NULL;
+}
+
+/**
+ * e_cal_component_alarms_free:
+ * @alarms: Component alarms structure.
+ *
+ * Frees a #ECalComponentAlarms structure.
+ **/
+void
+e_cal_component_alarms_free (ECalComponentAlarms *alarms)
+{
+	GSList *l;
+
+	g_return_if_fail (alarms != NULL);
+
+	g_assert (alarms->comp != NULL);
+	g_object_unref (G_OBJECT (alarms->comp));
+
+	for (l = alarms->alarms; l; l = l->next) {
+		ECalComponentAlarmInstance *instance;
+
+		instance = l->data;
+		g_assert (instance != NULL);
+		g_free (instance);
+	}
+
+	g_slist_free (alarms->alarms);
+	g_free (alarms);
+}
+
+/**
+ * e_cal_component_alarm_new:
+ *
+ * Create a new alarm object.
+ *
+ * Return value: a new alarm component
+ **/
+ECalComponentAlarm *
+e_cal_component_alarm_new (void)
+{
+	ECalComponentAlarm *alarm;
+	char *new_auid ;
+
+	alarm = g_new (ECalComponentAlarm, 1);
+
+	alarm->icalcomp = icalcomponent_new (ICAL_VALARM_COMPONENT);
+
+	new_auid = e_cal_component_gen_uid ();
+	alarm->uid = icalproperty_new_x (new_auid);
+	icalproperty_set_x_name (alarm->uid, EVOLUTION_ALARM_UID_PROPERTY);
+	icalcomponent_add_property (alarm->icalcomp, alarm->uid);
+	g_free (new_auid);
+
+	alarm->action = NULL;
+	alarm->attach = NULL;
+	alarm->description.prop = NULL;
+	alarm->description.altrep_param = NULL;
+	alarm->duration = NULL;
+	alarm->repeat = NULL;
+	alarm->trigger = NULL;
+	alarm->attendee_list = NULL;
+	
+	return alarm;
+}
+
+/**
+ * e_cal_component_alarm_clone:
+ * @alarm: An alarm subcomponent.
+ * 
+ * Creates a new alarm subcomponent by copying the information from another one.
+ * 
+ * Return value: A newly-created alarm subcomponent with the same values as the
+ * original one.  Should be freed with e_cal_component_alarm_free().
+ **/
+ECalComponentAlarm *
+e_cal_component_alarm_clone (ECalComponentAlarm *alarm)
+{
+	icalcomponent *icalcomp;
+
+	g_return_val_if_fail (alarm != NULL, NULL);
+
+	icalcomp = icalcomponent_new_clone (alarm->icalcomp);
+	return make_alarm (icalcomp);
+}
+
+/**
+ * e_cal_component_alarm_free:
+ * @alarm: A calendar alarm.
+ *
+ * Frees an alarm structure.
+ **/
+void
+e_cal_component_alarm_free (ECalComponentAlarm *alarm)
+{
+	GSList *l;
+	
+	g_return_if_fail (alarm != NULL);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	if (icalcomponent_get_parent (alarm->icalcomp) == NULL)
+		icalcomponent_free (alarm->icalcomp);
+
+	alarm->icalcomp = NULL;
+	alarm->uid = NULL;
+	alarm->action = NULL;
+	alarm->attach = NULL;
+	alarm->description.prop = NULL;
+	alarm->description.altrep_param = NULL;
+	alarm->duration = NULL;
+	alarm->repeat = NULL;
+	alarm->trigger = NULL;
+
+	for (l = alarm->attendee_list; l != NULL; l = l->next)
+		g_free (l->data);
+	g_slist_free (alarm->attendee_list);
+	alarm->attendee_list = NULL;
+	
+	g_free (alarm);
+}
+
+/**
+ * e_cal_component_alarm_get_uid:
+ * @alarm: An alarm subcomponent.
+ *
+ * Queries the unique identifier of an alarm subcomponent.
+ *
+ * Return value: UID of the alarm.
+ **/
+const char *
+e_cal_component_alarm_get_uid (ECalComponentAlarm *alarm)
+{
+	g_return_val_if_fail (alarm != NULL, NULL);
+
+	return alarm_uid_from_prop (alarm->uid);
+}
+
+/**
+ * e_cal_component_alarm_get_action:
+ * @alarm: An alarm.
+ * @action: Return value for the alarm's action type.
+ *
+ * Queries the action type of an alarm.
+ **/
+void
+e_cal_component_alarm_get_action (ECalComponentAlarm *alarm, ECalComponentAlarmAction *action)
+{
+	enum icalproperty_action ipa;
+
+	g_return_if_fail (alarm != NULL);
+	g_return_if_fail (action != NULL);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	if (!alarm->action) {
+		*action = E_CAL_COMPONENT_ALARM_NONE;
+		return;
+	}
+
+	ipa = icalproperty_get_action (alarm->action);
+
+	switch (ipa) {
+	case ICAL_ACTION_AUDIO:
+		*action = E_CAL_COMPONENT_ALARM_AUDIO;
+		break;
+
+	case ICAL_ACTION_DISPLAY:
+		*action = E_CAL_COMPONENT_ALARM_DISPLAY;
+		break;
+
+	case ICAL_ACTION_EMAIL:
+		*action = E_CAL_COMPONENT_ALARM_EMAIL;
+		break;
+
+	case ICAL_ACTION_PROCEDURE:
+		*action = E_CAL_COMPONENT_ALARM_PROCEDURE;
+		break;
+
+	case ICAL_ACTION_NONE:
+		*action = E_CAL_COMPONENT_ALARM_NONE;
+		break;
+
+	default:
+		*action = E_CAL_COMPONENT_ALARM_UNKNOWN;
+	}
+}
+
+/**
+ * e_cal_component_alarm_set_action:
+ * @alarm: An alarm.
+ * @action: Action type.
+ *
+ * Sets the action type for an alarm.
+ **/
+void
+e_cal_component_alarm_set_action (ECalComponentAlarm *alarm, ECalComponentAlarmAction action)
+{
+	enum icalproperty_action ipa;
+
+	g_return_if_fail (alarm != NULL);
+	g_return_if_fail (action != E_CAL_COMPONENT_ALARM_NONE);
+	g_return_if_fail (action != E_CAL_COMPONENT_ALARM_UNKNOWN);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	switch (action) {
+	case E_CAL_COMPONENT_ALARM_AUDIO:
+		ipa = ICAL_ACTION_AUDIO;
+		break;
+
+	case E_CAL_COMPONENT_ALARM_DISPLAY:
+		ipa = ICAL_ACTION_DISPLAY;
+		break;
+
+	case E_CAL_COMPONENT_ALARM_EMAIL:
+		ipa = ICAL_ACTION_EMAIL;
+		break;
+
+	case E_CAL_COMPONENT_ALARM_PROCEDURE:
+		ipa = ICAL_ACTION_PROCEDURE;
+		break;
+
+	default:
+		g_assert_not_reached ();
+		ipa = ICAL_ACTION_NONE;
+	}
+
+	if (alarm->action)
+		icalproperty_set_action (alarm->action, ipa);
+	else {
+		alarm->action = icalproperty_new_action (ipa);
+		icalcomponent_add_property (alarm->icalcomp, alarm->action);
+	}
+}
+
+/**
+ * e_cal_component_alarm_get_attach:
+ * @alarm: An alarm.
+ * @attach: Return value for the attachment; should be freed using icalattach_unref().
+ * 
+ * Queries the attachment property of an alarm.
+ **/
+void
+e_cal_component_alarm_get_attach (ECalComponentAlarm *alarm, icalattach **attach)
+{
+	g_return_if_fail (alarm != NULL);
+	g_return_if_fail (attach != NULL);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	if (alarm->attach) {
+		*attach = icalproperty_get_attach (alarm->attach);
+		icalattach_ref (*attach);
+	} else
+		*attach = NULL;
+}
+
+/**
+ * e_cal_component_alarm_set_attach:
+ * @alarm: An alarm.
+ * @attach: Attachment property or NULL to remove an existing property.
+ * 
+ * Sets the attachment property of an alarm.
+ **/
+void
+e_cal_component_alarm_set_attach (ECalComponentAlarm *alarm, icalattach *attach)
+{
+	g_return_if_fail (alarm != NULL);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	if (alarm->attach) {
+		icalcomponent_remove_property (alarm->icalcomp, alarm->attach);
+		icalproperty_free (alarm->attach);
+		alarm->attach = NULL;
+	}
+
+	if (attach) {
+		alarm->attach = icalproperty_new_attach (attach);
+		icalcomponent_add_property (alarm->icalcomp, alarm->attach);
+	}
+}
+
+/**
+ * e_cal_component_alarm_get_description:
+ * @alarm: An alarm.
+ * @description: Return value for the description property and its parameters.
+ * 
+ * Queries the description property of an alarm.
+ **/
+void
+e_cal_component_alarm_get_description (ECalComponentAlarm *alarm, ECalComponentText *description)
+{
+	g_return_if_fail (alarm != NULL);
+	g_return_if_fail (description != NULL);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	if (alarm->description.prop)
+		description->value = icalproperty_get_description (alarm->description.prop);
+	else
+		description->value = NULL;
+
+	if (alarm->description.altrep_param)
+		description->altrep = icalparameter_get_altrep (alarm->description.altrep_param);
+	else
+		description->altrep = NULL;
+}
+
+/**
+ * e_cal_component_alarm_set_description:
+ * @alarm: An alarm.
+ * @description: Description property and its parameters, or NULL for no description.
+ * 
+ * Sets the description property of an alarm.
+ **/
+void
+e_cal_component_alarm_set_description (ECalComponentAlarm *alarm, ECalComponentText *description)
+{
+	g_return_if_fail (alarm != NULL);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	if (alarm->description.prop) {
+		icalcomponent_remove_property (alarm->icalcomp, alarm->description.prop);
+		icalproperty_free (alarm->description.prop);
+
+		alarm->description.prop = NULL;
+		alarm->description.altrep_param = NULL;
+	}
+
+	if (!description)
+		return;
+
+	g_return_if_fail (description->value != NULL);
+
+	alarm->description.prop = icalproperty_new_description (description->value);
+	icalcomponent_add_property (alarm->icalcomp, alarm->description.prop);
+
+	if (description->altrep) {
+		alarm->description.altrep_param = icalparameter_new_altrep (
+			(char *) description->altrep);
+		icalproperty_add_parameter (alarm->description.prop,
+					    alarm->description.altrep_param);
+	}
+}
+
+/**
+ * e_cal_component_alarm_get_repeat:
+ * @alarm: An alarm.
+ * @repeat: Return value for the repeat/duration properties.
+ * 
+ * Queries the repeat/duration properties of an alarm.
+ **/
+void
+e_cal_component_alarm_get_repeat (ECalComponentAlarm *alarm, ECalComponentAlarmRepeat *repeat)
+{
+	g_return_if_fail (alarm != NULL);
+	g_return_if_fail (repeat != NULL);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	if (!(alarm->repeat && alarm->duration)) {
+		repeat->repetitions = 0;
+		memset (&repeat->duration, 0, sizeof (repeat->duration));
+		return;
+	}
+
+	repeat->repetitions = icalproperty_get_repeat (alarm->repeat);
+	repeat->duration = icalproperty_get_duration (alarm->duration);
+}
+
+/**
+ * e_cal_component_alarm_set_repeat:
+ * @alarm: An alarm.
+ * @repeat: Repeat/duration values.  To remove any repetitions from the alarm,
+ * set the @repeat.repetitions to 0.
+ * 
+ * Sets the repeat/duration values for an alarm.
+ **/
+void
+e_cal_component_alarm_set_repeat (ECalComponentAlarm *alarm, ECalComponentAlarmRepeat repeat)
+{
+	g_return_if_fail (alarm != NULL);
+	g_return_if_fail (repeat.repetitions >= 0);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	/* Delete old properties */
+
+	if (alarm->repeat) {
+		icalcomponent_remove_property (alarm->icalcomp, alarm->repeat);
+		icalproperty_free (alarm->repeat);
+		alarm->repeat = NULL;
+	}
+
+	if (alarm->duration) {
+		icalcomponent_remove_property (alarm->icalcomp, alarm->duration);
+		icalproperty_free (alarm->duration);
+		alarm->duration = NULL;
+	}
+
+	/* Set the new properties */
+
+	if (repeat.repetitions == 0)
+		return; /* For zero extra repetitions the properties should not exist */
+
+	alarm->repeat = icalproperty_new_repeat (repeat.repetitions);
+	icalcomponent_add_property (alarm->icalcomp, alarm->repeat);
+
+	alarm->duration = icalproperty_new_duration (repeat.duration);
+	icalcomponent_add_property (alarm->icalcomp, alarm->duration);
+}
+
+/**
+ * e_cal_component_alarm_get_trigger:
+ * @alarm: An alarm.
+ * @trigger: Return value for the trigger time.
+ *
+ * Queries the trigger time for an alarm.
+ **/
+void
+e_cal_component_alarm_get_trigger (ECalComponentAlarm *alarm, ECalComponentAlarmTrigger *trigger)
+{
+	icalparameter *param;
+	struct icaltriggertype t;
+	gboolean relative;
+
+	g_return_if_fail (alarm != NULL);
+	g_return_if_fail (trigger != NULL);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	if (!alarm->trigger) {
+		trigger->type = E_CAL_COMPONENT_ALARM_TRIGGER_NONE;
+		return;
+	}
+
+	/* Get trigger type */
+
+	param = icalproperty_get_first_parameter (alarm->trigger, ICAL_VALUE_PARAMETER);
+	if (param) {
+		icalparameter_value value;
+
+		value = icalparameter_get_value (param);
+
+		switch (value) {
+		case ICAL_VALUE_DURATION:
+			relative = TRUE;
+			break;
+
+		case ICAL_VALUE_DATETIME:
+			relative = FALSE;
+			break;
+
+		default:
+			g_message ("e_cal_component_alarm_get_trigger(): Unknown value for trigger "
+				   "value %d; using RELATIVE", value);
+
+			relative = TRUE;
+			break;
+		}
+	} else
+		relative = TRUE;
+
+	/* Get trigger value and the RELATED parameter */
+
+	t = icalproperty_get_trigger (alarm->trigger);
+
+	if (relative) {
+		trigger->u.rel_duration = t.duration;
+
+		param = icalproperty_get_first_parameter (alarm->trigger, ICAL_RELATED_PARAMETER);
+		if (param) {
+			icalparameter_related rel;
+
+			rel = icalparameter_get_related (param);
+
+			switch (rel) {
+			case ICAL_RELATED_START:
+				trigger->type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
+				break;
+
+			case ICAL_RELATED_END:
+				trigger->type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END;
+				break;
+
+			default:
+				g_assert_not_reached ();
+			}
+		} else
+			trigger->type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
+	} else {
+		trigger->u.abs_time = t.time;
+		trigger->type = E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE;
+	}
+}
+
+/**
+ * e_cal_component_alarm_set_trigger:
+ * @alarm: An alarm.
+ * @trigger: Trigger time structure.
+ *
+ * Sets the trigger time of an alarm.
+ **/
+void
+e_cal_component_alarm_set_trigger (ECalComponentAlarm *alarm, ECalComponentAlarmTrigger trigger)
+{
+	struct icaltriggertype t;
+	icalparameter *param;
+	icalparameter_value value_type;
+	icalparameter_related related;
+
+	g_return_if_fail (alarm != NULL);
+	g_return_if_fail (trigger.type != E_CAL_COMPONENT_ALARM_TRIGGER_NONE);
+
+	g_assert (alarm->icalcomp != NULL);
+
+	/* Delete old trigger */
+
+	if (alarm->trigger) {
+		icalcomponent_remove_property (alarm->icalcomp, alarm->trigger);
+		icalproperty_free (alarm->trigger);
+		alarm->trigger = NULL;
+	}
+
+	/* Set the value */
+
+	related = ICAL_RELATED_START; /* Keep GCC happy */
+
+	t.time = icaltime_null_time ();
+	t.duration = icaldurationtype_null_duration ();
+	switch (trigger.type) {
+	case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START:
+		t.duration = trigger.u.rel_duration;
+		value_type = ICAL_VALUE_DURATION;
+		related = ICAL_RELATED_START;
+		break;
+
+	case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END:
+		t.duration = trigger.u.rel_duration;
+		value_type = ICAL_VALUE_DURATION;
+		related = ICAL_RELATED_END;
+		break;
+
+	case E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE:
+		t.time = trigger.u.abs_time;
+		value_type = ICAL_VALUE_DATETIME;
+		break;
+
+	default:
+		g_assert_not_reached ();
+		return;
+	}
+
+	alarm->trigger = icalproperty_new_trigger (t);
+	icalcomponent_add_property (alarm->icalcomp, alarm->trigger);
+
+	/* Value parameters */
+
+	param = icalproperty_get_first_parameter (alarm->trigger, ICAL_VALUE_PARAMETER);
+	if (param)
+		icalparameter_set_value (param, value_type);
+	else {
+		param = icalparameter_new_value (value_type);
+		icalproperty_add_parameter (alarm->trigger, param);
+	}
+
+	/* Related parameter */
+
+	if (trigger.type != E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE) {
+		param = icalproperty_get_first_parameter (alarm->trigger, ICAL_RELATED_PARAMETER);
+
+		if (param)
+			icalparameter_set_related (param, related);
+		else {
+			param = icalparameter_new_related (related);
+			icalproperty_add_parameter (alarm->trigger, param);
+		}
+	}
+}
+
+/**
+ * e_cal_component_alarm_set_attendee_list:
+ * @alarm: An alarm.
+ * @attendee_list: Return value for the list of attendees.
+ *
+ * Gets the list of attendees associated with an alarm.
+ */
+void 
+e_cal_component_alarm_get_attendee_list (ECalComponentAlarm *alarm, GSList **attendee_list)
+{
+	g_return_if_fail (alarm != NULL);
+	
+	get_attendee_list (alarm->attendee_list, attendee_list);
+}
+
+/**
+ * e_cal_component_alarm_set_attendee_list:
+ * @alarm: An alarm.
+ * @attendee_list: List of attendees.
+ *
+ * Sets the list of attendees for an alarm.
+ */
+void 
+e_cal_component_alarm_set_attendee_list (ECalComponentAlarm *alarm, GSList *attendee_list)
+{
+	g_return_if_fail (alarm != NULL);
+
+	set_attendee_list (alarm->icalcomp, &alarm->attendee_list, attendee_list);
+}
+
+/**
+ * e_cal_component_alarm_has_attendees:
+ * @alarm: An alarm.
+ *
+ * Queries an alarm to see if it has attendees associated with it.
+ *
+ * Return value: TRUE if there are attendees in the alarm, FALSE if not.
+ */
+gboolean 
+e_cal_component_alarm_has_attendees (ECalComponentAlarm *alarm)
+{
+
+	g_return_val_if_fail (alarm != NULL, FALSE);
+
+	if (g_slist_length (alarm->attendee_list) > 0)
+		return TRUE;
+	
+	return FALSE;	
+}
+
+
+/**
+ * e_cal_component_alarm_get_icalcomponent
+ * @alarm: An alarm.
+ *
+ * Get the icalcomponent associated with the given #ECalComponentAlarm.
+ *
+ * Returns: the icalcomponent.
+ */
+icalcomponent *
+e_cal_component_alarm_get_icalcomponent (ECalComponentAlarm *alarm)
+{
+	g_return_val_if_fail (alarm != NULL, NULL);
+	return alarm->icalcomp;
+}
+
+/* Returns TRUE if both strings match, i.e. they are both NULL or the
+   strings are equal. */
+static gboolean
+e_cal_component_strings_match	(const gchar	*string1,
+				 const gchar	*string2)
+{
+	if (string1 == NULL || string2 == NULL)
+		return (string1 == string2) ? TRUE : FALSE;
+
+	if (!strcmp (string1, string2))
+		return TRUE;
+
+	return FALSE;
+}
+
+
+/**
+ * e_cal_component_event_dates_match:
+ * @comp1: A calendar component object.
+ * @comp2: A calendar component object.
+ *
+ * Checks if the DTSTART and DTEND properties of the 2 components match.
+ * Note that the events may have different recurrence properties which are not
+ * taken into account here.
+ *
+ * Returns: TRUE if the DTSTART and DTEND properties of the 2 components match.
+ **/
+gboolean
+e_cal_component_event_dates_match	(ECalComponent *comp1,
+				 ECalComponent *comp2)
+{
+	ECalComponentDateTime comp1_dtstart, comp1_dtend;
+	ECalComponentDateTime comp2_dtstart, comp2_dtend;
+	gboolean retval = TRUE;
+
+	e_cal_component_get_dtstart (comp1, &comp1_dtstart);
+	e_cal_component_get_dtend   (comp1, &comp1_dtend);
+	e_cal_component_get_dtstart (comp2, &comp2_dtstart);
+	e_cal_component_get_dtend   (comp2, &comp2_dtend);
+
+	/* If either value is NULL they must both be NULL to match. */
+	if (comp1_dtstart.value == NULL || comp2_dtstart.value == NULL) {
+		if (comp1_dtstart.value != comp2_dtstart.value) {
+			retval = FALSE;
+			goto out;
+		}
+	} else {
+		if (icaltime_compare (*comp1_dtstart.value,
+				      *comp2_dtstart.value)) {
+			retval = FALSE;
+			goto out;
+		}
+	}
+
+	if (comp1_dtend.value == NULL || comp2_dtend.value == NULL) {
+		if (comp1_dtend.value != comp2_dtend.value) {
+			retval = FALSE;
+			goto out;
+		}
+	} else {
+		if (icaltime_compare (*comp1_dtend.value,
+				      *comp2_dtend.value)) {
+			retval = FALSE;
+			goto out;
+		}
+	}
+
+	/* Now check the timezones. */
+	if (!e_cal_component_strings_match (comp1_dtstart.tzid,
+					  comp2_dtstart.tzid)) {
+		retval = FALSE;
+		goto out;
+	}
+
+	if (!e_cal_component_strings_match (comp1_dtend.tzid,
+					  comp2_dtend.tzid)) {
+		retval = FALSE;
+	}
+
+ out:
+
+	e_cal_component_free_datetime (&comp1_dtstart);
+	e_cal_component_free_datetime (&comp1_dtend);
+	e_cal_component_free_datetime (&comp2_dtstart);
+	e_cal_component_free_datetime (&comp2_dtend);
+
+	return retval;
+}
+
+
Index: calendar/libecal-dbus/e-cal-component.h
===================================================================
--- calendar/libecal-dbus/e-cal-component.h	(revision 409)
+++ calendar/libecal-dbus/e-cal-component.h	(working copy)
@@ -1 +1,457 @@
-link ../libecal/e-cal-component.h
\ No newline at end of file
+/* Evolution calendar - iCalendar component object
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Author: Federico Mena-Quintero <federico@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_CAL_COMPONENT_H
+#define E_CAL_COMPONENT_H
+
+#include <glib/gmacros.h>
+#include <time.h>
+#include <glib-object.h>
+#include <libical/ical.h>
+
+G_BEGIN_DECLS
+
+
+
+#define E_TYPE_CAL_COMPONENT            (e_cal_component_get_type ())
+#define E_CAL_COMPONENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CAL_COMPONENT, ECalComponent))
+#define E_CAL_COMPONENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CAL_COMPONENT,	\
+				       ECalComponentClass))
+#define E_IS_CAL_COMPONENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CAL_COMPONENT))
+#define E_IS_CAL_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CAL_COMPONENT))
+
+/* Types of calendar components to be stored by a ECalComponent, as per RFC 2445.
+ * We don't put the alarm component type here since we store alarms as separate
+ * structures inside the other "real" components.
+ */
+typedef enum {
+	E_CAL_COMPONENT_NO_TYPE,
+	E_CAL_COMPONENT_EVENT,
+	E_CAL_COMPONENT_TODO,
+	E_CAL_COMPONENT_JOURNAL,
+	E_CAL_COMPONENT_FREEBUSY,
+	E_CAL_COMPONENT_TIMEZONE
+} ECalComponentVType;
+
+/* Field identifiers for a calendar component; these are used by the data model
+ * for ETable.
+ *
+ * NOTE: These are also used in the ETable specification, and the column
+ *       numbers are saved in the user settings file. So don't reorder them!
+ */
+typedef enum {
+	E_CAL_COMPONENT_FIELD_CATEGORIES,		/* concatenation of the categories list */
+	E_CAL_COMPONENT_FIELD_CLASSIFICATION,
+	E_CAL_COMPONENT_FIELD_COMPLETED,
+	E_CAL_COMPONENT_FIELD_DTEND,
+	E_CAL_COMPONENT_FIELD_DTSTART,
+	E_CAL_COMPONENT_FIELD_DUE,
+	E_CAL_COMPONENT_FIELD_GEO,
+	E_CAL_COMPONENT_FIELD_PERCENT,
+	E_CAL_COMPONENT_FIELD_PRIORITY,
+	E_CAL_COMPONENT_FIELD_SUMMARY,
+	E_CAL_COMPONENT_FIELD_TRANSPARENCY,
+	E_CAL_COMPONENT_FIELD_URL,
+	E_CAL_COMPONENT_FIELD_HAS_ALARMS,		/* not a real field */
+	E_CAL_COMPONENT_FIELD_ICON,		/* not a real field */
+	E_CAL_COMPONENT_FIELD_COMPLETE,		/* not a real field */
+	E_CAL_COMPONENT_FIELD_RECURRING,		/* not a real field */
+	E_CAL_COMPONENT_FIELD_OVERDUE,		/* not a real field */
+	E_CAL_COMPONENT_FIELD_COLOR,		/* not a real field */
+	E_CAL_COMPONENT_FIELD_STATUS,
+	E_CAL_COMPONENT_FIELD_COMPONENT,		/* not a real field */
+	E_CAL_COMPONENT_FIELD_LOCATION,
+	E_CAL_COMPONENT_FIELD_NUM_FIELDS
+} ECalComponentField;
+
+/* Structures and enumerations to return properties and their parameters */
+
+/* CLASSIFICATION property */
+typedef enum {
+	E_CAL_COMPONENT_CLASS_NONE,
+	E_CAL_COMPONENT_CLASS_PUBLIC,
+	E_CAL_COMPONENT_CLASS_PRIVATE,
+	E_CAL_COMPONENT_CLASS_CONFIDENTIAL,
+	E_CAL_COMPONENT_CLASS_UNKNOWN
+} ECalComponentClassification;
+
+/* Properties that have time and timezone information */
+typedef struct {
+	/* Actual date/time value */
+	struct icaltimetype *value;
+
+	/* Timezone ID */
+	const char *tzid;
+} ECalComponentDateTime;
+
+/* Way in which a period of time is specified */
+typedef enum {
+	E_CAL_COMPONENT_PERIOD_DATETIME,
+	E_CAL_COMPONENT_PERIOD_DURATION
+} ECalComponentPeriodType;
+
+/* Period of time, can have explicit start/end times or start/duration instead */
+typedef struct {
+	ECalComponentPeriodType type;
+
+	struct icaltimetype start;
+
+	union {
+		struct icaltimetype end;
+		struct icaldurationtype duration;
+	} u;
+} ECalComponentPeriod;
+
+/* The type of range */
+typedef enum {
+	E_CAL_COMPONENT_RANGE_SINGLE,
+	E_CAL_COMPONENT_RANGE_THISPRIOR,
+	E_CAL_COMPONENT_RANGE_THISFUTURE
+} ECalComponentRangeType;
+
+typedef struct {
+	ECalComponentRangeType type;
+	
+	ECalComponentDateTime datetime;
+} ECalComponentRange;
+
+/* Text properties */
+typedef struct {
+	/* Description string */
+	const char *value;
+
+	/* Alternate representation URI */
+	const char *altrep;
+} ECalComponentText;
+
+/* Time transparency */
+typedef enum {
+	E_CAL_COMPONENT_TRANSP_NONE,
+	E_CAL_COMPONENT_TRANSP_TRANSPARENT,
+	E_CAL_COMPONENT_TRANSP_OPAQUE,
+	E_CAL_COMPONENT_TRANSP_UNKNOWN
+} ECalComponentTransparency;
+
+/* Organizer & Attendee */	
+typedef struct {
+	const char *value;
+	
+	const char *member;
+	icalparameter_cutype cutype;
+	icalparameter_role role;
+	icalparameter_partstat status;
+	gboolean rsvp;
+	
+	const char *delto;
+	const char *delfrom;
+	const char *sentby;
+	const char *cn;
+	const char *language;
+} ECalComponentAttendee;
+	
+typedef struct {
+	const char *value;
+	const char *sentby;
+	const char *cn;
+	const char *language;
+} ECalComponentOrganizer;
+
+/* Main calendar component object */
+
+typedef struct _ECalComponent ECalComponent;
+typedef struct _ECalComponentClass ECalComponentClass;
+
+typedef struct _ECalComponentPrivate ECalComponentPrivate;
+
+struct _ECalComponent {
+	GObject object;
+
+	/*< private >*/
+	ECalComponentPrivate *priv;
+};
+
+struct _ECalComponentClass {
+	GObjectClass parent_class;
+};
+
+/* Calendar component */
+
+GType e_cal_component_get_type (void);
+
+char *e_cal_component_gen_uid (void);
+
+ECalComponent *e_cal_component_new (void);
+
+ECalComponent *e_cal_component_new_from_string (const char *calobj);
+
+ECalComponent *e_cal_component_clone (ECalComponent *comp);
+
+void e_cal_component_set_new_vtype (ECalComponent *comp, ECalComponentVType type);
+
+gboolean e_cal_component_set_icalcomponent (ECalComponent *comp, icalcomponent *icalcomp);
+icalcomponent *e_cal_component_get_icalcomponent (ECalComponent *comp);
+void e_cal_component_rescan (ECalComponent *comp);
+void e_cal_component_strip_errors (ECalComponent *comp);
+
+ECalComponentVType e_cal_component_get_vtype (ECalComponent *comp);
+
+char *e_cal_component_get_as_string (ECalComponent *comp);
+
+void e_cal_component_commit_sequence (ECalComponent *comp);
+void e_cal_component_abort_sequence (ECalComponent *comp);
+
+void e_cal_component_get_uid (ECalComponent *comp, const char **uid);
+void e_cal_component_set_uid (ECalComponent *comp, const char *uid);
+
+void e_cal_component_get_categories (ECalComponent *comp, const char **categories);
+void e_cal_component_set_categories (ECalComponent *comp, const char *categories);
+void e_cal_component_get_categories_list (ECalComponent *comp, GSList **categ_list);
+void e_cal_component_set_categories_list (ECalComponent *comp, GSList *categ_list);
+
+void e_cal_component_get_classification (ECalComponent *comp, ECalComponentClassification *classif);
+void e_cal_component_set_classification (ECalComponent *comp, ECalComponentClassification classif);
+
+void e_cal_component_get_comment_list (ECalComponent *comp, GSList **text_list);
+void e_cal_component_set_comment_list (ECalComponent *comp, GSList *text_list);
+
+void e_cal_component_get_completed (ECalComponent *comp, struct icaltimetype **t);
+void e_cal_component_set_completed (ECalComponent *comp, struct icaltimetype *t);
+
+void e_cal_component_get_contact_list (ECalComponent *comp, GSList **text_list);
+void e_cal_component_set_contact_list (ECalComponent *comp, GSList *text_list);
+
+void e_cal_component_get_created (ECalComponent *comp, struct icaltimetype **t);
+void e_cal_component_set_created (ECalComponent *comp, struct icaltimetype *t);
+
+void e_cal_component_get_description_list (ECalComponent *comp, GSList **text_list);
+void e_cal_component_set_description_list (ECalComponent *comp, GSList *text_list);
+
+void e_cal_component_get_dtend (ECalComponent *comp, ECalComponentDateTime *dt);
+void e_cal_component_set_dtend (ECalComponent *comp, ECalComponentDateTime *dt);
+
+void e_cal_component_get_dtstamp (ECalComponent *comp, struct icaltimetype *t);
+void e_cal_component_set_dtstamp (ECalComponent *comp, struct icaltimetype *t);
+
+void e_cal_component_get_dtstart (ECalComponent *comp, ECalComponentDateTime *dt);
+void e_cal_component_set_dtstart (ECalComponent *comp, ECalComponentDateTime *dt);
+
+void e_cal_component_get_due (ECalComponent *comp, ECalComponentDateTime *dt);
+void e_cal_component_set_due (ECalComponent *comp, ECalComponentDateTime *dt);
+
+void e_cal_component_get_exdate_list (ECalComponent *comp, GSList **exdate_list);
+void e_cal_component_set_exdate_list (ECalComponent *comp, GSList *exdate_list);
+gboolean e_cal_component_has_exdates (ECalComponent *comp);
+
+void e_cal_component_get_exrule_list (ECalComponent *comp, GSList **recur_list);
+void e_cal_component_get_exrule_property_list (ECalComponent *comp, GSList **recur_list);
+void e_cal_component_set_exrule_list (ECalComponent *comp, GSList *recur_list);
+gboolean e_cal_component_has_exrules (ECalComponent *comp);
+
+gboolean e_cal_component_has_exceptions (ECalComponent *comp);
+
+void e_cal_component_get_geo (ECalComponent *comp, struct icalgeotype **geo);
+void e_cal_component_set_geo (ECalComponent *comp, struct icalgeotype *geo);
+
+void e_cal_component_get_last_modified (ECalComponent *comp, struct icaltimetype **t);
+void e_cal_component_set_last_modified (ECalComponent *comp, struct icaltimetype *t);
+
+void e_cal_component_get_organizer (ECalComponent *comp, ECalComponentOrganizer *organizer);
+void e_cal_component_set_organizer (ECalComponent *comp, ECalComponentOrganizer *organizer);
+gboolean e_cal_component_has_organizer (ECalComponent *comp);
+
+void e_cal_component_get_percent (ECalComponent *comp, int **percent);
+void e_cal_component_set_percent (ECalComponent *comp, int *percent);
+
+void e_cal_component_get_priority (ECalComponent *comp, int **priority);
+void e_cal_component_set_priority (ECalComponent *comp, int *priority);
+
+void e_cal_component_get_recurid (ECalComponent *comp, ECalComponentRange *recur_id);
+const char *e_cal_component_get_recurid_as_string (ECalComponent *comp);
+void e_cal_component_set_recurid (ECalComponent *comp, ECalComponentRange *recur_id);
+
+void e_cal_component_get_rdate_list (ECalComponent *comp, GSList **period_list);
+void e_cal_component_set_rdate_list (ECalComponent *comp, GSList *period_list);
+gboolean e_cal_component_has_rdates (ECalComponent *comp);
+
+void e_cal_component_get_rrule_list (ECalComponent *comp, GSList **recur_list);
+void e_cal_component_get_rrule_property_list (ECalComponent *comp, GSList **recur_list);
+void e_cal_component_set_rrule_list (ECalComponent *comp, GSList *recur_list);
+gboolean e_cal_component_has_rrules (ECalComponent *comp);
+
+gboolean e_cal_component_has_recurrences (ECalComponent *comp);
+gboolean e_cal_component_has_simple_recurrence (ECalComponent *comp);
+gboolean e_cal_component_is_instance (ECalComponent *comp);
+
+void e_cal_component_get_sequence (ECalComponent *comp, int **sequence);
+void e_cal_component_set_sequence (ECalComponent *comp, int *sequence);
+
+void e_cal_component_get_status (ECalComponent *comp, icalproperty_status *status);
+void e_cal_component_set_status (ECalComponent *comp, icalproperty_status status);
+
+void e_cal_component_get_summary (ECalComponent *comp, ECalComponentText *summary);
+void e_cal_component_set_summary (ECalComponent *comp, ECalComponentText *summary);
+
+void e_cal_component_get_transparency (ECalComponent *comp, ECalComponentTransparency *transp);
+void e_cal_component_set_transparency (ECalComponent *comp, ECalComponentTransparency transp);
+
+void e_cal_component_get_url (ECalComponent *comp, const char **url);
+void e_cal_component_set_url (ECalComponent *comp, const char *url);
+
+void e_cal_component_get_attendee_list (ECalComponent *comp, GSList **attendee_list);
+void e_cal_component_set_attendee_list (ECalComponent *comp, GSList *attendee_list);
+gboolean e_cal_component_has_attendees (ECalComponent *comp);
+
+void e_cal_component_get_location (ECalComponent *comp, const char **location);
+void e_cal_component_set_location (ECalComponent *comp, const char *location);
+
+/* Attachment handling */
+void e_cal_component_get_attachment_list (ECalComponent *comp, GSList **attachment_list);
+void e_cal_component_set_attachment_list (ECalComponent *comp, GSList *attachment_list);
+gboolean e_cal_component_has_attachments (ECalComponent *comp);
+int e_cal_component_get_num_attachments (ECalComponent *comp);
+
+
+gboolean e_cal_component_event_dates_match (ECalComponent *comp1, ECalComponent *comp2);
+
+
+/* Functions to free returned values */
+
+void e_cal_component_free_categories_list (GSList *categ_list);
+void e_cal_component_free_datetime (ECalComponentDateTime *dt);
+void e_cal_component_free_range (ECalComponentRange *range);
+void e_cal_component_free_exdate_list (GSList *exdate_list);
+void e_cal_component_free_geo (struct icalgeotype *geo);
+void e_cal_component_free_icaltimetype (struct icaltimetype *t);
+void e_cal_component_free_percent (int *percent);
+void e_cal_component_free_priority (int *priority);
+void e_cal_component_free_period_list (GSList *period_list);
+void e_cal_component_free_recur_list (GSList *recur_list);
+void e_cal_component_free_sequence (int *sequence);
+void e_cal_component_free_text_list (GSList *text_list);
+void e_cal_component_free_attendee_list (GSList *attendee_list);
+
+/* Alarms */
+
+/* Opaque structure used to represent alarm subcomponents */
+typedef struct _ECalComponentAlarm ECalComponentAlarm;
+
+/* An alarm occurrence, i.e. a trigger instance */
+typedef struct {
+	/* UID of the alarm that triggered */
+	const char *auid;
+
+	/* Trigger time, i.e. "5 minutes before the appointment" */
+	time_t trigger;
+
+	/* Actual event occurrence to which this trigger corresponds */
+	time_t occur_start;
+	time_t occur_end;
+} ECalComponentAlarmInstance;
+
+/* Alarm trigger instances for a particular component */
+typedef struct {
+	/* The actual component */
+	ECalComponent *comp;
+
+	/* List of ECalComponentAlarmInstance structures */
+	GSList *alarms;
+} ECalComponentAlarms;
+
+/* Alarm types */
+typedef enum {
+	E_CAL_COMPONENT_ALARM_NONE,
+	E_CAL_COMPONENT_ALARM_AUDIO,
+	E_CAL_COMPONENT_ALARM_DISPLAY,
+	E_CAL_COMPONENT_ALARM_EMAIL,
+	E_CAL_COMPONENT_ALARM_PROCEDURE,
+	E_CAL_COMPONENT_ALARM_UNKNOWN
+} ECalComponentAlarmAction;
+
+/* Whether a trigger is relative to the start or end of an event occurrence, or
+ * whether it is specified to occur at an absolute time.
+ */
+typedef enum {
+	E_CAL_COMPONENT_ALARM_TRIGGER_NONE,
+	E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START,
+	E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END,
+	E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE
+} ECalComponentAlarmTriggerType;
+
+typedef struct {
+	ECalComponentAlarmTriggerType type;
+
+	union {
+		struct icaldurationtype rel_duration;
+		struct icaltimetype abs_time;
+	} u;
+} ECalComponentAlarmTrigger;
+
+typedef struct {
+	/* Number of extra repetitions, zero for none */
+	int repetitions;
+
+	/* Interval between repetitions */
+	struct icaldurationtype duration;
+} ECalComponentAlarmRepeat;
+
+gboolean e_cal_component_has_alarms (ECalComponent *comp);
+void e_cal_component_add_alarm (ECalComponent *comp, ECalComponentAlarm *alarm);
+void e_cal_component_remove_alarm (ECalComponent *comp, const char *auid);
+void e_cal_component_remove_all_alarms (ECalComponent *comp);
+
+GList *e_cal_component_get_alarm_uids (ECalComponent *comp);
+ECalComponentAlarm *e_cal_component_get_alarm (ECalComponent *comp, const char *auid);
+
+void e_cal_component_alarms_free (ECalComponentAlarms *alarms);
+
+/* ECalComponentAlarms */
+ECalComponentAlarm *e_cal_component_alarm_new (void);
+ECalComponentAlarm *e_cal_component_alarm_clone (ECalComponentAlarm *alarm);
+void e_cal_component_alarm_free (ECalComponentAlarm *alarm);
+
+const char *e_cal_component_alarm_get_uid (ECalComponentAlarm *alarm);
+
+void e_cal_component_alarm_get_action (ECalComponentAlarm *alarm, ECalComponentAlarmAction *action);
+void e_cal_component_alarm_set_action (ECalComponentAlarm *alarm, ECalComponentAlarmAction action);
+
+void e_cal_component_alarm_get_attach (ECalComponentAlarm *alarm, icalattach **attach);
+void e_cal_component_alarm_set_attach (ECalComponentAlarm *alarm, icalattach *attach);
+
+void e_cal_component_alarm_get_description (ECalComponentAlarm *alarm, ECalComponentText *description);
+void e_cal_component_alarm_set_description (ECalComponentAlarm *alarm, ECalComponentText *description);
+
+void e_cal_component_alarm_get_repeat (ECalComponentAlarm *alarm, ECalComponentAlarmRepeat *repeat);
+void e_cal_component_alarm_set_repeat (ECalComponentAlarm *alarm, ECalComponentAlarmRepeat repeat);
+
+void e_cal_component_alarm_get_trigger (ECalComponentAlarm *alarm, ECalComponentAlarmTrigger *trigger);
+void e_cal_component_alarm_set_trigger (ECalComponentAlarm *alarm, ECalComponentAlarmTrigger trigger);
+
+void e_cal_component_alarm_get_attendee_list (ECalComponentAlarm *alarm, GSList **attendee_list);
+void e_cal_component_alarm_set_attendee_list (ECalComponentAlarm *alarm, GSList *attendee_list);
+gboolean e_cal_component_alarm_has_attendees (ECalComponentAlarm *alarm);
+
+icalcomponent *e_cal_component_alarm_get_icalcomponent (ECalComponentAlarm *alarm);
+
+
+
+G_END_DECLS
+
+#endif
Index: calendar/libecal-dbus/e-cal-time-util.c
===================================================================
--- calendar/libecal-dbus/e-cal-time-util.c	(revision 409)
+++ calendar/libecal-dbus/e-cal-time-util.c	(working copy)
@@ -1 +1,733 @@
-link ../libecal/e-cal-time-util.c
\ No newline at end of file
+/* Miscellaneous time-related utilities
+ *
+ * Copyright (C) 2000, 2001 Ximian, Inc.
+ *
+ * Authors: Federico Mena <federico@ximian.com>
+ *          Miguel de Icaza <miguel@ximian.com>
+ *          Damon Chaplin <damon@ximian.com>
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#include "e-cal-time-util.h"
+
+
+
+#define REFORMATION_DAY 639787	/* First day of the reformation, counted from 1 Jan 1 */
+#define MISSING_DAYS 11		/* They corrected out 11 days */
+#define THURSDAY 4		/* First day of reformation */
+#define SATURDAY 6		/* Offset value; 1 Jan 1 was a Saturday */
+
+
+/* Number of days in a month, using 0 (Jan) to 11 (Dec). For leap years,
+   add 1 to February (month 1). */
+static const int days_in_month[12] = {
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+
+
+/**************************************************************************
+ * time_t manipulation functions.
+ *
+ * NOTE: these use the Unix timezone functions like mktime() and localtime()
+ * and so should not be used in Evolution. New Evolution code should use
+ * icaltimetype values rather than time_t values wherever possible.
+ **************************************************************************/
+
+/**
+ * time_add_day:
+ * @time: A time_t value.
+ * @days: Number of days to add.
+ *
+ * Adds a day onto the time, using local time.
+ * Note that if clocks go forward due to daylight savings time, there are
+ * some non-existent local times, so the hour may be changed to make it a
+ * valid time. This also means that it may not be wise to keep calling
+ * time_add_day() to step through a certain period - if the hour gets changed
+ * to make it valid time, any further calls to time_add_day() will also return
+ * this hour, which may not be what you want.
+ *
+ * Return value: a time_t value containing @time plus the days added.
+ */
+time_t
+time_add_day (time_t time, int days)
+{
+	struct tm *tm;
+
+	tm = localtime (&time);
+	tm->tm_mday += days;
+	tm->tm_isdst = -1;
+
+	return mktime (tm);
+}
+
+/**
+ * time_add_week:
+ * @time: A time_t value.
+ * @weeks: Number of weeks to add.
+ *
+ * Adds the given number of weeks to a time value.
+ *
+ * Return value: a time_t value containing @time plus the weeks added.
+ */
+time_t
+time_add_week (time_t time, int weeks)
+{
+	return time_add_day (time, weeks * 7);
+}
+
+/**
+ * time_day_begin:
+ * @t: A time_t value.
+ *
+ * Returns the start of the day, according to the local time.
+ *
+ * Return value: the time corresponding to the beginning of the day.
+ */
+time_t
+time_day_begin (time_t t)
+{
+	struct tm tm;
+
+	tm = *localtime (&t);
+	tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+	tm.tm_isdst = -1;
+
+	return mktime (&tm);
+}
+
+/**
+ * time_day_end:
+ * @t: A time_t value.
+ *
+ * Returns the end of the day, according to the local time.
+ *
+ * Return value: the time corresponding to the end of the day.
+ */
+time_t
+time_day_end (time_t t)
+{
+	struct tm tm;
+
+	tm = *localtime (&t);
+	tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+	tm.tm_mday++;
+	tm.tm_isdst = -1;
+
+	return mktime (&tm);
+}
+
+
+/**************************************************************************
+ * time_t manipulation functions, using timezones in libical.
+ *
+ * NOTE: these are only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values rather than
+ * time_t values wherever possible.
+ **************************************************************************/
+
+
+/**
+ * time_add_day_with_zone:
+ * @time: A time_t value.
+ * @days: Number of days to add.
+ * @zone: Timezone to use.
+ *
+ * Adds or subtracts a number of days to/from the given time_t value, using
+ * the given timezone.
+ * NOTE: this function is only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values and
+ * icaltime_adjust() to add or subtract days, hours, minutes & seconds.
+ *
+ * Return value: a time_t value containing @time plus the days added.
+ */
+time_t
+time_add_day_with_zone (time_t time, int days, icaltimezone *zone)
+{
+	struct icaltimetype tt;
+
+	/* Convert to an icaltimetype. */
+	tt = icaltime_from_timet_with_zone (time, FALSE, zone);
+
+	/* Add/subtract the number of days. */
+	icaltime_adjust (&tt, days, 0, 0, 0);
+
+	/* Convert back to a time_t. */
+	return icaltime_as_timet_with_zone (tt, zone);
+}
+
+
+/**
+ * time_add_week_with_zone:
+ * @time: A time_t value.
+ * @weeks: Number of weeks to add.
+ * @zone: Timezone to use.
+ *
+ * Adds or subtracts a number of weeks to/from the given time_t value, using
+ * the given timezone.
+ * NOTE: this function is only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values and
+ * icaltime_adjust() to add or subtract days, hours, minutes & seconds.
+ *
+ * Return value: a time_t value containing @time plus the weeks added.
+ */
+time_t
+time_add_week_with_zone (time_t time, int weeks, icaltimezone *zone)
+{
+	return time_add_day_with_zone (time, weeks * 7, zone);
+}
+
+
+/**
+ * time_add_month_with_zone:
+ * @time: A time_t value.
+ * @months: Number of months to add.
+ * @zone: Timezone to use.
+ *
+ * Adds or subtracts a number of months to/from the given time_t value, using
+ * the given timezone.
+ *
+ * If the day would be off the end of the month (e.g. adding 1 month to
+ * 30th January, would lead to an invalid day, 30th February), it moves it
+ * down to the last day in the month, e.g. 28th Feb (or 29th in a leap year.)
+ *
+ * NOTE: this function is only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values and
+ * icaltime_adjust() to add or subtract days, hours, minutes & seconds.
+ *
+ * Return value: a time_t value containing @time plus the months added.
+ */
+time_t
+time_add_month_with_zone (time_t time, int months, icaltimezone *zone)
+{
+	struct icaltimetype tt;
+	int day, days_in_month;
+
+	/* Convert to an icaltimetype. */
+	tt = icaltime_from_timet_with_zone (time, FALSE, zone);
+
+	/* Add on the number of months. */
+	tt.month += months;
+
+	/* Save the day, and set it to 1, so we don't overflow into the next
+	   month. */
+	day = tt.day;
+	tt.day = 1;
+
+	/* Normalize it, fixing any month overflow. */
+	tt = icaltime_normalize (tt);
+
+	/* If we go past the end of a month, set it to the last day. */
+	days_in_month = time_days_in_month (tt.year, tt.month - 1);
+	if (day > days_in_month)
+		day = days_in_month;
+
+	tt.day = day;
+
+	/* Convert back to a time_t. */
+	return icaltime_as_timet_with_zone (tt, zone);
+}
+
+
+/**
+ * time_year_begin_with_zone:
+ * @time: A time_t value.
+ * @zone: Timezone to use.
+ *
+ * Returns the start of the year containing the given time_t, using the given
+ * timezone.
+ * NOTE: this function is only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values and
+ * icaltime_adjust() to add or subtract days, hours, minutes & seconds.
+ *
+ * Return value: the beginning of the year.
+ */
+time_t
+time_year_begin_with_zone (time_t time, icaltimezone *zone)
+{
+	struct icaltimetype tt;
+
+	/* Convert to an icaltimetype. */
+	tt = icaltime_from_timet_with_zone (time, FALSE, zone);
+
+	/* Set it to the start of the year. */
+	tt.month  = 1;
+	tt.day    = 1;
+	tt.hour   = 0;
+	tt.minute = 0;
+	tt.second = 0;
+
+	/* Convert back to a time_t. */
+	return icaltime_as_timet_with_zone (tt, zone);
+}
+
+
+/**
+ * time_month_begin_with_zone:
+ * @time: A time_t value.
+ * @zone: Timezone to use.
+ *
+ * Returns the start of the month containing the given time_t, using the given
+ * timezone.
+ * NOTE: this function is only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values and
+ * icaltime_adjust() to add or subtract days, hours, minutes & seconds.
+ *
+ * Return value: the beginning of the month.
+ */
+time_t
+time_month_begin_with_zone (time_t time, icaltimezone *zone)
+{
+	struct icaltimetype tt;
+
+	/* Convert to an icaltimetype. */
+	tt = icaltime_from_timet_with_zone (time, FALSE, zone);
+
+	/* Set it to the start of the month. */
+	tt.day    = 1;
+	tt.hour   = 0;
+	tt.minute = 0;
+	tt.second = 0;
+
+	/* Convert back to a time_t. */
+	return icaltime_as_timet_with_zone (tt, zone);
+}
+
+
+/**
+ * time_week_begin_with_zone:
+ * @time: A time_t value.
+ * @week_start_day: Day to use as the starting of the week.
+ * @zone: Timezone to use.
+ *
+ * Returns the start of the week containing the given time_t, using the given
+ * timezone. week_start_day should use the same values as mktime(),
+ * i.e. 0 (Sun) to 6 (Sat).
+ * NOTE: this function is only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values and
+ * icaltime_adjust() to add or subtract days, hours, minutes & seconds.
+ *
+ * Return value: the beginning of the week.
+ */
+time_t
+time_week_begin_with_zone (time_t time, int week_start_day, icaltimezone *zone)
+{
+	struct icaltimetype tt;
+	int weekday, offset;
+
+	/* Convert to an icaltimetype. */
+	tt = icaltime_from_timet_with_zone (time, FALSE, zone);
+
+	/* Get the weekday. */
+	weekday = time_day_of_week (tt.day, tt.month - 1, tt.year);
+
+	/* Calculate the current offset from the week start day. */
+	offset = (weekday + 7 - week_start_day) % 7;
+
+	/* Set it to the start of the month. */
+	tt.day -= offset;
+	tt.hour   = 0;
+	tt.minute = 0;
+	tt.second = 0;
+
+	/* Normalize it, to fix any overflow. */
+	tt = icaltime_normalize (tt);
+
+	/* Convert back to a time_t. */
+	return icaltime_as_timet_with_zone (tt, zone);
+}
+
+
+/**
+ * time_day_begin_with_zone:
+ * @time: A time_t value.
+ * @zone: Timezone to use.
+ *
+ * Returns the start of the day containing the given time_t, using the given
+ * timezone.
+ * NOTE: this function is only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values and
+ * icaltime_adjust() to add or subtract days, hours, minutes & seconds.
+ *
+ * Return value: the beginning of the day.
+ */
+time_t
+time_day_begin_with_zone (time_t time, icaltimezone *zone)
+{
+	struct icaltimetype tt;
+
+	/* Convert to an icaltimetype. */
+	tt = icaltime_from_timet_with_zone (time, FALSE, zone);
+
+	/* Set it to the start of the day. */
+	tt.hour   = 0;
+	tt.minute = 0;
+	tt.second = 0;
+
+	/* Convert back to a time_t. */
+	return icaltime_as_timet_with_zone (tt, zone);
+}
+
+
+/**
+ * time_day_end_with_zone:
+ * @time: A time_t value.
+ * @zone: Timezone to use.
+ *
+ * Returns the end of the day containing the given time_t, using the given
+ * timezone. (The end of the day is the start of the next day.)
+ * NOTE: this function is only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values and
+ * icaltime_adjust() to add or subtract days, hours, minutes & seconds.
+ *
+ * Return value: the end of the day.
+ */
+time_t
+time_day_end_with_zone (time_t time, icaltimezone *zone)
+{
+	struct icaltimetype tt;
+
+	/* Convert to an icaltimetype. */
+	tt = icaltime_from_timet_with_zone (time, FALSE, zone);
+
+	/* Set it to the start of the next day. */
+	tt.day++;
+	tt.hour   = 0;
+	tt.minute = 0;
+	tt.second = 0;
+
+	/* Normalize it, to fix any overflow. */
+	tt = icaltime_normalize (tt);
+
+	/* Convert back to a time_t. */
+	return icaltime_as_timet_with_zone (tt, zone);
+}
+
+/**
+ * time_to_gdate_with_zone:
+ * @date: Destination #GDate value.
+ * @time: A time value.
+ * @zone: Desired timezone for destination @date, or NULL if the UTC timezone
+ * is desired.
+ * 
+ * Converts a time_t value to a #GDate structure using the specified timezone.
+ * This is analogous to g_date_set_time() but takes the timezone into account.
+ **/
+void
+time_to_gdate_with_zone (GDate *date, time_t time, icaltimezone *zone)
+{
+	struct icaltimetype tt;
+
+	g_return_if_fail (date != NULL);
+	g_return_if_fail (time != -1);
+
+	tt = icaltime_from_timet_with_zone (time, FALSE,
+					    zone ? zone : icaltimezone_get_utc_timezone ());
+
+	g_date_set_dmy (date, tt.day, tt.month, tt.year);
+}
+
+
+/**************************************************************************
+ * General time functions.
+ **************************************************************************/
+
+
+/**
+ * time_days_in_month:
+ * @year: The year.
+ * @month: The month.
+ *
+ * Returns the number of days in the month. Year is the normal year, e.g. 2001.
+ * Month is 0 (Jan) to 11 (Dec).
+ *
+ * Return value: number of days in the given month/year.
+ */
+int
+time_days_in_month (int year, int month)
+{
+	int days;
+
+	g_return_val_if_fail (year >= 1900, 0);
+	g_return_val_if_fail ((month >= 0) && (month < 12), 0);
+
+	days = days_in_month[month];
+	if (month == 1 && time_is_leap_year (year))
+		days++;
+
+	return days;
+}
+
+
+/**
+ * time_day_of_year:
+ * @day: The day.
+ * @month: The month.
+ * @year: The year.
+ *
+ * Returns the 1-based day number within the year of the specified date.
+ * Year is the normal year, e.g. 2001. Month is 0 to 11.
+ *
+ * Return value: the day of the year.
+ */
+int
+time_day_of_year (int day, int month, int year)
+{
+	int i;
+
+	for (i = 0; i < month; i++) {
+		day += days_in_month[i];
+
+		if (i == 1 && time_is_leap_year (year))
+			day++;
+	}
+
+	return day;
+}
+
+
+/**
+ * time_day_of_week:
+ * @day: The day.
+ * @month: The month.
+ * @year: The year.
+ *
+ * Returns the day of the week for the specified date, 0 (Sun) to 6 (Sat).
+ * For the days that were removed on the Gregorian reformation, it returns
+ * Thursday. Year is the normal year, e.g. 2001. Month is 0 to 11.
+ *
+ * Return value: the day of the week for the given date.
+ */
+int
+time_day_of_week (int day, int month, int year)
+{
+	int n;
+
+	n = (year - 1) * 365 + time_leap_years_up_to (year - 1)
+	  + time_day_of_year (day, month, year);
+
+	if (n < REFORMATION_DAY)
+		return (n - 1 + SATURDAY) % 7;
+
+	if (n >= (REFORMATION_DAY + MISSING_DAYS))
+		return (n - 1 + SATURDAY - MISSING_DAYS) % 7;
+
+	return THURSDAY;
+}
+
+
+/**
+ * time_is_leap_year:
+ * @year: The year.
+ *
+ * Returns whether the specified year is a leap year. Year is the normal year,
+ * e.g. 2001.
+ *
+ * Return value: TRUE if the year is leap, FALSE if not.
+ */
+gboolean
+time_is_leap_year (int year)
+{
+	if (year <= 1752)
+		return !(year % 4);
+	else
+		return (!(year % 4) && (year % 100)) || !(year % 400);
+}
+
+
+/**
+ * time_leap_years_up_to:
+ * @year: The year.
+ *
+ * Returns the number of leap years since year 1 up to (but not including) the
+ * specified year. Year is the normal year, e.g. 2001.
+ *
+ * Return value: number of leap years.
+ */
+int
+time_leap_years_up_to (int year)
+{
+	/* There is normally a leap year every 4 years, except at the turn of
+	   centuries since 1700. But there is a leap year on centuries since 1700
+	   which are divisible by 400. */
+	return (year / 4
+		- ((year > 1700) ? (year / 100 - 17) : 0)
+		+ ((year > 1600) ? ((year - 1600) / 400) : 0));
+}
+
+
+/**
+ * isodate_from_time_t:
+ * @t: A time value.
+ * 
+ * Creates an ISO 8601 UTC representation from a time value.
+ * 
+ * Return value: String with the ISO 8601 representation of the UTC time.
+ **/
+char *
+isodate_from_time_t (time_t t)
+{
+	gchar *ret;
+
+	ret = g_malloc (17); /* 4+2+2+1+2+2+2+1 + 1 */
+	strftime (ret, 17, "%Y%m%dT%H%M%SZ", gmtime (&t));
+
+	return ret;
+}
+
+/**
+ * time_from_isodate:
+ * @str: Date/time value in ISO 8601 format.
+ * 
+ * Converts an ISO 8601 UTC time string into a time_t value.
+ * 
+ * Return value: Time_t corresponding to the specified ISO string.
+ * Note that we only allow UTC times at present.
+ **/
+time_t
+time_from_isodate (const char *str)
+{
+	struct icaltimetype tt = icaltime_null_time ();
+	icaltimezone *utc_zone;
+	int len, i;
+
+	g_return_val_if_fail (str != NULL, -1);
+
+	/* yyyymmdd[Thhmmss[Z]] */
+
+	len = strlen (str);
+
+	if (!(len == 8 || len == 15 || len == 16))
+		return -1;
+
+	for (i = 0; i < len; i++)
+		if (!((i != 8 && i != 15 && isdigit (str[i]))
+		      || (i == 8 && str[i] == 'T')
+		      || (i == 15 && str[i] == 'Z')))
+			return -1;
+
+#define digit_at(x,y) (x[y] - '0')
+
+	tt.year = digit_at (str, 0) * 1000
+		+ digit_at (str, 1) * 100
+		+ digit_at (str, 2) * 10
+		+ digit_at (str, 3);
+
+	tt.month = digit_at (str, 4) * 10
+		 + digit_at (str, 5);
+
+	tt.day = digit_at (str, 6) * 10
+	       + digit_at (str, 7);
+
+	if (len > 8) {
+		tt.hour = digit_at (str, 9) * 10
+			+ digit_at (str, 10);
+		tt.minute  = digit_at (str, 11) * 10
+			   + digit_at (str, 12);
+		tt.second  = digit_at (str, 13) * 10
+			   + digit_at (str, 14);
+	}
+
+	utc_zone = icaltimezone_get_utc_timezone ();
+
+	return icaltime_as_timet_with_zone (tt, utc_zone);
+}
+
+/**
+ * icaltimetype_to_tm:
+ * @itt: An icaltimetype structure.
+ *
+ * Convers an icaltimetype structure into a GLibc's struct tm.
+ *
+ * Return value: The converted time as a struct tm. All fields will be
+ * set properly except for tm.tm_yday.
+ */
+struct tm
+icaltimetype_to_tm (struct icaltimetype *itt)
+{
+	struct tm tm;
+	
+	memset (&tm, 0, sizeof (struct tm));
+
+	if (!itt->is_date) {
+		tm.tm_sec = itt->second;
+		tm.tm_min = itt->minute;
+		tm.tm_hour = itt->hour;
+	}
+
+	tm.tm_mday = itt->day;
+	tm.tm_mon = itt->month - 1;
+	tm.tm_year = itt->year - 1900;
+	tm.tm_wday = time_day_of_week (itt->day, itt->month - 1, itt->year);
+	tm.tm_isdst = -1;
+	
+	return tm;
+}
+
+/**
+ * icaltimetype_to_tm_with_zone:
+ * @itt: A time value.
+ * @from_zone: Source timezone.
+ * @to_zone: Destination timezone.
+ * 
+ * Converts a time value from one timezone to another, and returns a struct tm
+ * representation of the time.
+ * 
+ * Return value: The converted time as a struct tm. All fields will be
+ * set properly except for tm.tm_yday.
+ **/
+struct tm
+icaltimetype_to_tm_with_zone (struct icaltimetype *itt,
+			      icaltimezone *from_zone,
+			      icaltimezone *to_zone)
+{
+	struct tm tm;
+	struct icaltimetype itt_copy;
+
+	memset (&tm, 0, sizeof (tm));
+	tm.tm_isdst = -1;
+
+	g_return_val_if_fail (itt != NULL, tm);
+
+	itt_copy = *itt;
+
+	icaltimezone_convert_time (&itt_copy, from_zone, to_zone);
+	tm = icaltimetype_to_tm (&itt_copy);
+
+	return tm;
+}
+
+/**
+ * tm_to_icaltimetype:
+ * @tm: A struct tm.
+ * @is_date: Whether the given time is a date only or not.
+ *
+ * Converts a struct tm into an icaltimetype.
+ *
+ * Return value: The converted time as an icaltimetype.
+ */
+struct icaltimetype
+tm_to_icaltimetype (struct tm *tm, gboolean is_date)
+{
+	struct icaltimetype itt;
+
+	memset (&itt, 0, sizeof (struct icaltimetype));
+
+	if (!is_date) {
+		itt.second = tm->tm_sec;
+		itt.minute = tm->tm_min;
+		itt.hour = tm->tm_hour;
+	}
+
+	itt.day = tm->tm_mday;
+	itt.month = tm->tm_mon + 1;
+	itt.year = tm->tm_year+ 1900;
+    
+	itt.is_utc = 0;
+	itt.is_date = is_date; 
+	
+	return itt;
+}
+
Index: calendar/libecal-dbus/e-cal-types.h
===================================================================
--- calendar/libecal-dbus/e-cal-types.h	(revision 409)
+++ calendar/libecal-dbus/e-cal-types.h	(working copy)
@@ -1 +1,75 @@
-link ../libecal/e-cal-types.h
\ No newline at end of file
+/* Evolution calendar utilities and types
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Authors: Federico Mena-Quintero <federico@ximian.com>
+ *          JP Rosevear <jpr@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_CAL_TYPES_H
+#define E_CAL_TYPES_H
+
+#include <libecal/e-cal-component.h>
+
+G_BEGIN_DECLS
+
+
+
+#define E_CALENDAR_ERROR e_calendar_error_quark()
+
+GQuark e_calendar_error_quark (void) G_GNUC_CONST;
+
+typedef enum {
+	E_CAL_CHANGE_ADDED = 1 << 0,
+	E_CAL_CHANGE_MODIFIED = 1 << 1,
+	E_CAL_CHANGE_DELETED = 1 << 2
+} ECalChangeType;
+
+typedef struct 
+{
+	ECalComponent *comp;
+	ECalChangeType type;
+} ECalChange;
+
+typedef enum {
+	E_CALENDAR_STATUS_OK,
+	E_CALENDAR_STATUS_INVALID_ARG,
+	E_CALENDAR_STATUS_BUSY,
+	E_CALENDAR_STATUS_REPOSITORY_OFFLINE,
+	E_CALENDAR_STATUS_NO_SUCH_CALENDAR,
+	E_CALENDAR_STATUS_OBJECT_NOT_FOUND,
+	E_CALENDAR_STATUS_INVALID_OBJECT,
+	E_CALENDAR_STATUS_URI_NOT_LOADED,
+	E_CALENDAR_STATUS_URI_ALREADY_LOADED,
+	E_CALENDAR_STATUS_PERMISSION_DENIED,
+	E_CALENDAR_STATUS_UNKNOWN_USER,
+	E_CALENDAR_STATUS_OBJECT_ID_ALREADY_EXISTS,
+	E_CALENDAR_STATUS_PROTOCOL_NOT_SUPPORTED,
+	E_CALENDAR_STATUS_CANCELLED,
+	E_CALENDAR_STATUS_COULD_NOT_CANCEL,
+	E_CALENDAR_STATUS_AUTHENTICATION_FAILED,
+	E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED,
+	E_CALENDAR_STATUS_CORBA_EXCEPTION,
+	E_CALENDAR_STATUS_OTHER_ERROR,
+	E_CALENDAR_STATUS_INVALID_SERVER_VERSION
+
+} ECalendarStatus;
+
+G_END_DECLS
+
+#endif
+
Index: calendar/libecal-dbus/e-cal-util.c
===================================================================
--- calendar/libecal-dbus/e-cal-util.c	(revision 409)
+++ calendar/libecal-dbus/e-cal-util.c	(working copy)
@@ -1 +1,1128 @@
-link ../libecal/e-cal-util.c
\ No newline at end of file
+/* Evolution calendar utilities and types
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Author: Federico Mena-Quintero <federico@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gstrfuncs.h>
+#include <glib/gi18n-lib.h>
+#include "e-cal-util.h"
+
+
+
+/**
+ * cal_obj_instance_list_free:
+ * @list: List of #CalObjInstance structures.
+ *
+ * Frees a list of #CalObjInstance structures.
+ **/
+void
+cal_obj_instance_list_free (GList *list)
+{
+	CalObjInstance *i;
+	GList *l;
+
+	for (l = list; l; l = l->next) {
+		i = l->data;
+
+		g_assert (i != NULL);
+		g_assert (i->uid != NULL);
+
+		g_free (i->uid);
+		g_free (i);
+	}
+
+	g_list_free (list);
+}
+
+/**
+ * cal_obj_uid_list_free:
+ * @list: List of strings with unique identifiers.
+ *
+ * Frees a list of unique identifiers for calendar objects.
+ **/
+void
+cal_obj_uid_list_free (GList *list)
+{
+	GList *l;
+
+	for (l = list; l; l = l->next) {
+		char *uid;
+
+		uid = l->data;
+
+		g_assert (uid != NULL);
+		g_free (uid);
+	}
+
+	g_list_free (list);
+}
+
+/**
+ * e_cal_util_new_top_level:
+ *
+ * Creates a new VCALENDAR component.
+ *
+ * Return value: the newly created top level component.
+ */
+icalcomponent *
+e_cal_util_new_top_level (void)
+{
+	icalcomponent *icalcomp;
+	icalproperty *prop;
+
+	icalcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT);
+
+	/* RFC 2445, section 4.7.1 */
+	prop = icalproperty_new_calscale ("GREGORIAN");
+	icalcomponent_add_property (icalcomp, prop);
+
+       /* RFC 2445, section 4.7.3 */
+	prop = icalproperty_new_prodid ("-//Ximian//NONSGML Evolution Calendar//EN");
+	icalcomponent_add_property (icalcomp, prop);
+
+	/* RFC 2445, section 4.7.4.  This is the iCalendar spec version, *NOT*
+	 * the product version!  Do not change this!
+	 */
+	prop = icalproperty_new_version ("2.0");
+	icalcomponent_add_property (icalcomp, prop);
+
+	return icalcomp;
+}
+
+/**
+ * e_cal_util_new_component:
+ * @kind: Kind of the component to create.
+ *
+ * Creates a new #icalcomponent of the specified kind.
+ *
+ * Return value: the newly created component.
+ */
+icalcomponent *
+e_cal_util_new_component (icalcomponent_kind kind)
+{
+	icalcomponent *comp;
+	struct icaltimetype dtstamp;
+
+	comp = icalcomponent_new (kind);
+	icalcomponent_set_uid (comp, e_cal_component_gen_uid ());
+	dtstamp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+	icalcomponent_set_dtstamp (comp, dtstamp);
+
+	return comp;
+}
+
+static char *
+read_line (const char *string)
+{
+	char *line;
+	GString *line_str = NULL;
+
+	for (; *string; string++) {
+		if (!line_str)
+			line_str = g_string_new ("");
+
+		line_str = g_string_append_c (line_str, *string);
+		if (*string == '\n')
+			break;
+	}
+
+	line = line_str->str;
+	g_string_free (line_str, FALSE);
+
+	return line;
+}
+
+/**
+ * e_cal_util_parse_ics_string:
+ * @string: iCalendar string to be parsed.
+ *
+ * Parses an iCalendar stirng and returns an #icalcomponent representing that
+ * string. Note that this function deals with multiple VCALENDAR's in the
+ * string, something that Mozilla used to do and which libical does not
+ * support.
+ *
+ * Return value: an #icalcomponent.
+ */
+icalcomponent *
+e_cal_util_parse_ics_string (const char *string)
+{
+	char *s;
+	icalcomponent *icalcomp = NULL;
+
+	g_return_val_if_fail (string != NULL, NULL);
+
+	/* Split string into separated VCALENDAR's, if more than one */
+	if ((s = g_strstr_len (string, strlen (string), "BEGIN:VCALENDAR"))) {
+		GString *comp_str = NULL;
+
+		while (*s) {
+			char *line = read_line (s);
+			if (line) {
+				if (!comp_str)
+					comp_str = g_string_new (line);
+				else
+					comp_str = g_string_append (comp_str, line);
+
+				if (!strncmp (line, "END:VCALENDAR", 13)) {
+					icalcomponent *tmp;
+
+					tmp = icalparser_parse_string (comp_str->str);
+					if (tmp) {
+						if (icalcomp)
+							icalcomponent_merge_component (icalcomp, tmp);
+						else
+							icalcomp = tmp;
+					}
+
+					g_string_free (comp_str, TRUE);
+					comp_str = NULL;
+				}
+
+				s += strlen (line);
+
+				g_free (line);
+			}
+		}
+	} else
+		icalcomp = icalparser_parse_string (string);
+
+	return icalcomp;
+}
+
+static char *
+get_line_fn (char *buf, size_t size, void *file)
+{
+	return fgets (buf, size, file);
+}
+
+/**
+ * e_cal_util_parse_ics_file:
+ * @filename: Name of the file to be parsed.
+ *
+ * Parses the given file, and, if it contains a valid iCalendar object,
+ * parse it and return a corresponding #icalcomponent.
+ *
+ * Return value: an #icalcomponent.
+ */
+icalcomponent *
+e_cal_util_parse_ics_file (const char *filename)
+{
+	icalparser *parser;
+	icalcomponent *icalcomp;
+	FILE *file;
+
+	file = fopen (filename, "r");
+	if (!file)
+		return NULL;
+
+	parser = icalparser_new ();
+	icalparser_set_gen_data (parser, file);
+
+	icalcomp = icalparser_parse (parser, get_line_fn);
+	icalparser_free (parser);
+	fclose (file);
+
+	return icalcomp;
+}
+
+/* Computes the range of time in which recurrences should be generated for a
+ * component in order to compute alarm trigger times.
+ */
+static void
+compute_alarm_range (ECalComponent *comp, GList *alarm_uids, time_t start, time_t end,
+		     time_t *alarm_start, time_t *alarm_end)
+{
+	GList *l;
+	time_t repeat_time;
+
+	*alarm_start = start;
+	*alarm_end = end;
+
+	repeat_time = 0;
+
+	for (l = alarm_uids; l; l = l->next) {
+		const char *auid;
+		ECalComponentAlarm *alarm;
+		ECalComponentAlarmTrigger trigger;
+		struct icaldurationtype *dur;
+		time_t dur_time;
+		ECalComponentAlarmRepeat repeat;
+
+		auid = l->data;
+		alarm = e_cal_component_get_alarm (comp, auid);
+		g_assert (alarm != NULL);
+
+		e_cal_component_alarm_get_trigger (alarm, &trigger);
+		e_cal_component_alarm_get_repeat (alarm, &repeat);
+		e_cal_component_alarm_free (alarm);
+
+		switch (trigger.type) {
+		case E_CAL_COMPONENT_ALARM_TRIGGER_NONE:
+		case E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE:
+			break;
+
+		case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START:
+		case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END:
+			dur = &trigger.u.rel_duration;
+  			dur_time = icaldurationtype_as_int (*dur);
+
+			if (repeat.repetitions != 0) {
+				int rdur;
+
+				rdur = repeat.repetitions * icaldurationtype_as_int (repeat.duration);
+				repeat_time = MAX (repeat_time, rdur);
+			}
+
+			if (dur->is_neg)
+				/* If the duration is negative then dur_time
+				 * will be negative as well; that is why we
+				 * subtract to expand the range.
+				 */
+				*alarm_end = MAX (*alarm_end, end - dur_time);
+			else
+				*alarm_start = MIN (*alarm_start, start - dur_time);
+
+			break;
+
+		default:
+			g_assert_not_reached ();
+		}
+	}
+
+	*alarm_start -= repeat_time;
+
+	g_assert (*alarm_start <= *alarm_end);
+}
+
+/* Closure data to generate alarm occurrences */
+struct alarm_occurrence_data {
+	/* These are the info we have */
+	GList *alarm_uids;
+	time_t start;
+	time_t end;
+	ECalComponentAlarmAction *omit;
+	
+	/* This is what we compute */
+	GSList *triggers;
+	int n_triggers;
+};
+
+static void
+add_trigger (struct alarm_occurrence_data *aod, const char *auid, time_t trigger,
+	     time_t occur_start, time_t occur_end)
+{
+	ECalComponentAlarmInstance *instance;
+
+	instance = g_new (ECalComponentAlarmInstance, 1);
+	instance->auid = auid;
+	instance->trigger = trigger;
+	instance->occur_start = occur_start;
+	instance->occur_end = occur_end;
+
+	aod->triggers = g_slist_prepend (aod->triggers, instance);
+	aod->n_triggers++;
+}
+
+/* Callback used from cal_recur_generate_instances(); generates triggers for all
+ * of a component's RELATIVE alarms.
+ */
+static gboolean
+add_alarm_occurrences_cb (ECalComponent *comp, time_t start, time_t end, gpointer data)
+{
+	struct alarm_occurrence_data *aod;
+	GList *l;
+
+	aod = data;
+
+	for (l = aod->alarm_uids; l; l = l->next) {
+		const char *auid;
+		ECalComponentAlarm *alarm;
+		ECalComponentAlarmAction action;
+		ECalComponentAlarmTrigger trigger;
+		ECalComponentAlarmRepeat repeat;
+		struct icaldurationtype *dur;
+		time_t dur_time;
+		time_t occur_time, trigger_time;
+		int i;
+		
+		auid = l->data;
+		alarm = e_cal_component_get_alarm (comp, auid);
+		g_assert (alarm != NULL);
+
+		e_cal_component_alarm_get_action (alarm, &action);
+		e_cal_component_alarm_get_trigger (alarm, &trigger);
+		e_cal_component_alarm_get_repeat (alarm, &repeat);
+		e_cal_component_alarm_free (alarm);
+
+		for (i = 0; aod->omit[i] != -1; i++) {
+			if (aod->omit[i] == action)
+				break;
+		}
+		if (aod->omit[i] != -1)
+			continue;
+		
+		if (trigger.type != E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START
+		    && trigger.type != E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END)
+			continue;
+
+		dur = &trigger.u.rel_duration;
+  		dur_time = icaldurationtype_as_int (*dur);
+
+		if (trigger.type == E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START)
+			occur_time = start;
+		else
+			occur_time = end;
+
+		/* If dur->is_neg is true then dur_time will already be
+		 * negative.  So we do not need to test for dur->is_neg here; we
+		 * can simply add the dur_time value to the occur_time and get
+		 * the correct result.
+		 */
+
+		trigger_time = occur_time + dur_time;
+
+		/* Add repeating alarms */
+
+		if (repeat.repetitions != 0) {
+			int i;
+			time_t repeat_time;
+
+			repeat_time = icaldurationtype_as_int (repeat.duration);
+
+			for (i = 0; i < repeat.repetitions; i++) {
+				time_t t;
+
+				t = trigger_time + (i + 1) * repeat_time;
+
+				if (t >= aod->start && t < aod->end)
+					add_trigger (aod, auid, t, start, end);
+			}
+		}
+
+		/* Add the trigger itself */
+
+		if (trigger_time >= aod->start && trigger_time < aod->end)
+			add_trigger (aod, auid, trigger_time, start, end);
+	}
+
+	return TRUE;
+}
+
+/* Generates the absolute triggers for a component */
+static void
+generate_absolute_triggers (ECalComponent *comp, struct alarm_occurrence_data *aod,
+			    ECalRecurResolveTimezoneFn resolve_tzid,
+			    gpointer user_data,
+			    icaltimezone *default_timezone)
+{
+	GList *l;
+	ECalComponentDateTime dt_start, dt_end;
+
+	e_cal_component_get_dtstart (comp, &dt_start);
+	e_cal_component_get_dtend (comp, &dt_end);
+
+	for (l = aod->alarm_uids; l; l = l->next) {
+		const char *auid;
+		ECalComponentAlarm *alarm;
+		ECalComponentAlarmAction action;
+		ECalComponentAlarmRepeat repeat;
+		ECalComponentAlarmTrigger trigger;
+		time_t abs_time;
+		time_t occur_start, occur_end;
+		icaltimezone *zone;
+		int i;
+		
+		auid = l->data;
+		alarm = e_cal_component_get_alarm (comp, auid);
+		g_assert (alarm != NULL);
+
+		e_cal_component_alarm_get_action (alarm, &action);
+		e_cal_component_alarm_get_trigger (alarm, &trigger);
+		e_cal_component_alarm_get_repeat (alarm, &repeat);
+		e_cal_component_alarm_free (alarm);
+
+		for (i = 0; aod->omit[i] != -1; i++) {
+			if (aod->omit[i] == action)
+				break;
+		}
+		if (aod->omit[i] != -1)
+			continue;
+
+		if (trigger.type != E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE)
+			continue;
+
+		/* Absolute triggers are always in UTC; see RFC 2445 section 4.8.6.3 */
+		zone = icaltimezone_get_utc_timezone ();
+
+		abs_time = icaltime_as_timet_with_zone (trigger.u.abs_time, zone);
+
+		/* No particular occurrence, so just use the times from the component */
+
+		if (dt_start.value) {
+			if (dt_start.tzid && !dt_start.value->is_date)
+				zone = (* resolve_tzid) (dt_start.tzid, user_data);
+			else
+				zone = default_timezone;
+
+			occur_start = icaltime_as_timet_with_zone (*dt_start.value, zone);
+		} else
+			occur_start = -1;
+
+		if (dt_end.value) {
+			if (dt_end.tzid && !dt_end.value->is_date)
+				zone = (* resolve_tzid) (dt_end.tzid, user_data);
+			else
+				zone = default_timezone;
+
+			occur_end = icaltime_as_timet_with_zone (*dt_end.value, zone);
+		} else
+			occur_end = -1;
+
+		/* Add repeating alarms */
+
+		if (repeat.repetitions != 0) {
+			int i;
+			time_t repeat_time;
+
+			repeat_time = icaldurationtype_as_int (repeat.duration);
+
+			for (i = 0; i < repeat.repetitions; i++) {
+				time_t t;
+
+				t = abs_time + (i + 1) * repeat_time;
+
+				if (t >= aod->start && t < aod->end)
+					add_trigger (aod, auid, t, occur_start, occur_end);
+			}
+		}
+
+		/* Add the trigger itself */
+
+		if (abs_time >= aod->start && abs_time < aod->end)
+			add_trigger (aod, auid, abs_time, occur_start, occur_end);
+	}
+
+	e_cal_component_free_datetime (&dt_start);
+	e_cal_component_free_datetime (&dt_end);
+}
+
+/* Compares two alarm instances; called from g_slist_sort() */
+static gint
+compare_alarm_instance (gconstpointer a, gconstpointer b)
+{
+	const ECalComponentAlarmInstance *aia, *aib;
+
+	aia = a;
+	aib = b;
+
+	if (aia->trigger < aib->trigger)
+		return -1;
+	else if (aia->trigger > aib->trigger)
+		return 1;
+	else
+		return 0;
+}
+
+/**
+ * e_cal_util_generate_alarms_for_comp
+ * @comp: The #ECalComponent to generate alarms from.
+ * @start: Start time.
+ * @end: End time.
+ * @omit: 
+ * @resolve_tzid: Callback for resolving timezones
+ * @user_data: Data to be passed to the resolve_tzid callback
+ * @default_timezone: The timezone used to resolve DATE and floating DATE-TIME
+ * values.
+ *
+ * Generates alarm instances for a calendar component.  Returns the instances
+ * structure, or NULL if no alarm instances occurred in the specified time
+ * range.
+ *
+ * Return value: a list of all the alarms found for the given component on
+ * the given time tange. The list of alarms should be freed by using the
+ * #e_cal_component_free_alarm_list function.
+ */
+ECalComponentAlarms *
+e_cal_util_generate_alarms_for_comp (ECalComponent *comp,
+				     time_t start,
+				     time_t end,
+				     ECalComponentAlarmAction *omit,
+				     ECalRecurResolveTimezoneFn resolve_tzid,
+				     gpointer user_data,
+				     icaltimezone *default_timezone)
+{
+	GList *alarm_uids;
+	time_t alarm_start, alarm_end;
+	struct alarm_occurrence_data aod;
+	ECalComponentAlarms *alarms;
+
+	if (!e_cal_component_has_alarms (comp))
+		return NULL;
+
+	alarm_uids = e_cal_component_get_alarm_uids (comp);
+	compute_alarm_range (comp, alarm_uids, start, end, &alarm_start, &alarm_end);
+
+	aod.alarm_uids = alarm_uids;
+	aod.start = start;
+	aod.end = end;
+	aod.omit = omit;
+	aod.triggers = NULL;
+	aod.n_triggers = 0;
+
+	e_cal_recur_generate_instances (comp, alarm_start, alarm_end,
+					add_alarm_occurrences_cb, &aod,
+					resolve_tzid, user_data,
+					default_timezone);
+
+	/* We add the ABSOLUTE triggers separately */
+	generate_absolute_triggers (comp, &aod, resolve_tzid, user_data, default_timezone);
+
+	if (aod.n_triggers == 0)
+		return NULL;
+
+	/* Create the component alarm instances structure */
+
+	alarms = g_new (ECalComponentAlarms, 1);
+	alarms->comp = comp;
+	g_object_ref (G_OBJECT (alarms->comp));
+	alarms->alarms = g_slist_sort (aod.triggers, compare_alarm_instance);
+
+	return alarms;
+}
+
+/**
+ * e_cal_util_generate_alarms_for_list
+ * @comps: List of #ECalComponent's.
+ * @start: Start time.
+ * @end: End time.
+ * @omit: 
+ * @comp_alarms: List to be returned
+ * @resolve_tzid: Callback for resolving timezones
+ * @user_data: Data to be passed to the resolve_tzid callback
+ * @default_timezone: The timezone used to resolve DATE and floating DATE-TIME
+ * values.
+ *
+ * Iterates through all the components in the @comps list and generates alarm
+ * instances for them; putting them in the @comp_alarms list.
+ *
+ * Return value: the number of elements it added to the list.
+ */
+int
+e_cal_util_generate_alarms_for_list (GList *comps,
+				     time_t start,
+				     time_t end,
+				     ECalComponentAlarmAction *omit,
+				     GSList **comp_alarms,
+				     ECalRecurResolveTimezoneFn resolve_tzid,
+				     gpointer user_data,
+				     icaltimezone *default_timezone)
+{
+	GList *l;
+	int n;
+
+	n = 0;
+
+	for (l = comps; l; l = l->next) {
+		ECalComponent *comp;
+		ECalComponentAlarms *alarms;
+
+		comp = E_CAL_COMPONENT (l->data);
+		alarms = e_cal_util_generate_alarms_for_comp (comp, start, end, omit, resolve_tzid, user_data, default_timezone);
+
+		if (alarms) {
+			*comp_alarms = g_slist_prepend (*comp_alarms, alarms);
+			n++;
+		}
+	}
+
+	return n;
+}
+
+
+/**
+ * e_cal_util_priority_to_string:
+ * @priority: Priority value.
+ *
+ * Converts an iCalendar PRIORITY value to a translated string. Any unknown
+ * priority value (i.e. not 0-9) will be returned as "" (undefined).
+ *
+ * Return value: a string representing the PRIORITY value. This value is a
+ * constant, so it should never be freed.
+ */
+char *
+e_cal_util_priority_to_string (int priority)
+{
+	char *retval;
+
+	if (priority <= 0)
+		retval = "";
+	else if (priority <= 4)
+		retval = _("High");
+	else if (priority == 5)
+		retval = _("Normal");
+	else if (priority <= 9)
+		retval = _("Low");
+	else
+		retval = "";
+
+	return retval;
+}
+
+
+/**
+ * e_cal_util_priority_from_string:
+ * @string: A string representing the PRIORITY value.
+ *
+ * Converts a translated priority string to an iCalendar priority value.
+ *
+ * Return value: the priority (0-9) or -1 if the priority string is not valid.
+*/
+int
+e_cal_util_priority_from_string (const char *string)
+{
+	int priority;
+
+	/* An empty string is the same as 'None'. */
+	if (!string || !string[0] || !g_strcasecmp (string, _("Undefined")))
+		priority = 0;
+	else if (!g_strcasecmp (string, _("High")))
+		priority = 3;
+	else if (!g_strcasecmp (string, _("Normal")))
+		priority = 5;
+	else if (!g_strcasecmp (string, _("Low")))
+		priority = 7;
+	else
+		priority = -1;
+
+	return priority;
+}
+
+/* callback for icalcomponent_foreach_tzid */
+typedef struct {
+	icalcomponent *vcal_comp;
+	icalcomponent *icalcomp;
+} ForeachTzidData;
+
+static void
+add_timezone_cb (icalparameter *param, void *data)
+{
+	icaltimezone *tz;
+	const char *tzid;
+	icalcomponent *vtz_comp;
+	ForeachTzidData *f_data = (ForeachTzidData *) data;
+
+	tzid = icalparameter_get_tzid (param);
+	if (!tzid)
+		return;
+
+	tz = icalcomponent_get_timezone (f_data->vcal_comp, tzid);
+	if (tz)
+		return;
+
+	tz = icalcomponent_get_timezone (f_data->icalcomp, tzid);
+	if (!tz) {
+		tz = icaltimezone_get_builtin_timezone_from_tzid (tzid);
+		if (!tz)
+			return;
+	}
+
+	vtz_comp = icaltimezone_get_component (tz);
+	if (!vtz_comp)
+		return;
+
+	icalcomponent_add_component (f_data->vcal_comp,
+				     icalcomponent_new_clone (vtz_comp));
+}
+
+/**
+ * e_cal_util_add_timezones_from_component:
+ * @vcal_comp: A VCALENDAR component.
+ * @icalcomp: An iCalendar component, of any type.
+ *
+ * Adds VTIMEZONE components to a VCALENDAR for all tzid's
+ * in the given @icalcomp.
+ */
+void
+e_cal_util_add_timezones_from_component (icalcomponent *vcal_comp,
+					 icalcomponent *icalcomp)
+{
+	ForeachTzidData f_data;
+
+	g_return_if_fail (vcal_comp != NULL);
+	g_return_if_fail (icalcomp != NULL);;
+
+	f_data.vcal_comp = vcal_comp;
+	f_data.icalcomp = icalcomp;
+	icalcomponent_foreach_tzid (icalcomp, add_timezone_cb, &f_data);
+}
+
+/**
+ * e_cal_util_component_is_instance:
+ * @icalcomp: An #icalcomponent.
+ *
+ * Checks whether an #icalcomponent is an instance of a recurring appointment or not.
+ *
+ * Return value: TRUE if it is an instance, FALSE if not.
+ */
+gboolean
+e_cal_util_component_is_instance (icalcomponent *icalcomp)
+{
+	icalproperty *prop;
+
+	g_return_val_if_fail (icalcomp != NULL, FALSE);
+
+	prop = icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY);
+	return prop ? TRUE : FALSE;
+}
+
+/**
+ * e_cal_util_component_has_alarms:
+ * @icalcomp: An #icalcomponent.
+ *
+ * Checks whether an #icalcomponent has any alarm.
+ *
+ * Return value: TRUE if it has alarms, FALSE otherwise.
+ */
+gboolean
+e_cal_util_component_has_alarms (icalcomponent *icalcomp)
+{
+	icalcomponent *alarm;
+
+	g_return_val_if_fail (icalcomp != NULL, FALSE);
+
+	alarm = icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT);
+	return alarm ? TRUE : FALSE;
+}
+
+/**
+ * e_cal_util_component_has_organizer:
+ * @icalcomp: An #icalcomponent.
+ *
+ * Checks whether an #icalcomponent has an organizer or not.
+ *
+ * Return value: TRUE if there is an organizer, FALSE if not.
+ */
+gboolean
+e_cal_util_component_has_organizer (icalcomponent *icalcomp)
+{
+	icalproperty *prop;
+
+	g_return_val_if_fail (icalcomp != NULL, FALSE);
+
+	prop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY);
+	return prop ? TRUE : FALSE;
+}
+
+gboolean
+e_cal_util_component_has_attendee (icalcomponent *icalcomp)
+{
+	icalproperty *prop;
+
+	g_return_val_if_fail (icalcomp != NULL, FALSE);
+
+	prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+
+	return prop ? TRUE : FALSE;
+}
+
+/**
+ * e_cal_util_component_has_recurrences:
+ * @icalcomp: An #icalcomponent.
+ *
+ * Checks if an #icalcomponent has recurrence dates or rules.
+ *
+ * Return value: TRUE if there are recurrence dates/rules, FALSE if not.
+ */
+gboolean
+e_cal_util_component_has_recurrences (icalcomponent *icalcomp)
+{
+	g_return_val_if_fail (icalcomp != NULL, FALSE);
+
+	return e_cal_util_component_has_rdates (icalcomp) || e_cal_util_component_has_rrules (icalcomp);
+}
+
+/**
+ * e_cal_util_component_has_rdates:
+ * @icalcomp: An #icalcomponent.
+ *
+ * Checks if an #icalcomponent has recurrence dates.
+ *
+ * Return value: TRUE if there are recurrence dates, FALSE if not.
+ */
+gboolean
+e_cal_util_component_has_rdates (icalcomponent *icalcomp)
+{
+	icalproperty *prop;
+
+	g_return_val_if_fail (icalcomp != NULL, FALSE);
+
+	prop = icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY);
+	return prop ? TRUE : FALSE;
+}
+
+/**
+ * e_cal_util_component_has_rrules:
+ * @icalcomp: An #icalcomponent.
+ *
+ * Checks if an #icalcomponent has recurrence rules.
+ *
+ * Return value: TRUE if there are recurrence rules, FALSE if not.
+ */
+gboolean
+e_cal_util_component_has_rrules (icalcomponent *icalcomp)
+{
+	icalproperty *prop;
+
+	g_return_val_if_fail (icalcomp != NULL, FALSE);
+
+	prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY);
+	return prop ? TRUE : FALSE;
+}
+
+/**
+ * e_cal_util_event_dates_match:
+ * @icalcomp1: An #icalcomponent.
+ * @icalcomp2: An #icalcomponent.
+ *
+ * Compare the dates of two #icalcomponent's to check if they match.
+ *
+ * Return value: TRUE if the dates of both components match, FALSE otherwise.
+ */
+gboolean
+e_cal_util_event_dates_match (icalcomponent *icalcomp1, icalcomponent *icalcomp2)
+{
+	struct icaltimetype c1_dtstart, c1_dtend, c2_dtstart, c2_dtend;
+
+	g_return_val_if_fail (icalcomp1 != NULL, FALSE);
+	g_return_val_if_fail (icalcomp2 != NULL, FALSE);
+
+	c1_dtstart = icalcomponent_get_dtstart (icalcomp1);
+	c1_dtend = icalcomponent_get_dtend (icalcomp1);
+	c2_dtstart = icalcomponent_get_dtstart (icalcomp2);
+	c2_dtend = icalcomponent_get_dtend (icalcomp2);
+
+	/* if either value is NULL, they must both be NULL to match */
+	if (icaltime_is_valid_time (c1_dtstart) || icaltime_is_valid_time (c2_dtstart)) {
+		if (!(icaltime_is_valid_time (c1_dtstart) && icaltime_is_valid_time (c2_dtstart)))
+			return FALSE;
+	} else {
+		if (icaltime_compare (c1_dtstart, c2_dtstart))
+			return FALSE;
+	}
+
+	if (icaltime_is_valid_time (c1_dtend) || icaltime_is_valid_time (c2_dtend)) {
+		if (!(icaltime_is_valid_time (c1_dtend) && icaltime_is_valid_time (c2_dtend)))
+			return FALSE;
+	} else {
+		if (icaltime_compare (c1_dtend, c2_dtend))
+			return FALSE;
+	}
+
+	
+
+	/* now match the timezones */
+	if (!(!c1_dtstart.zone && !c2_dtstart.zone) ||
+	    (c1_dtstart.zone && c2_dtstart.zone &&
+	     !strcmp (icaltimezone_get_tzid ((icaltimezone *) c1_dtstart.zone),
+		      icaltimezone_get_tzid ((icaltimezone *) c2_dtstart.zone))))
+		return FALSE;
+
+	if (!(!c1_dtend.zone && !c2_dtend.zone) ||
+	    (c1_dtend.zone && c2_dtend.zone &&
+	     !strcmp (icaltimezone_get_tzid ((icaltimezone *) c1_dtend.zone),
+		      icaltimezone_get_tzid ((icaltimezone *) c2_dtend.zone))))
+		return FALSE;
+
+	return TRUE;
+}
+
+/* Individual instances management */
+
+struct instance_data {
+	time_t start;
+	gboolean found;
+};
+
+static void
+check_instance (icalcomponent *comp, struct icaltime_span *span, void *data)
+{
+	struct instance_data *instance = data;
+
+	if (span->start == instance->start)
+		instance->found = TRUE;
+}
+
+/**
+ * e_cal_util_construct_instance:
+ * @icalcomp: A recurring #icalcomponent
+ * @rid: The RECURRENCE-ID to construct a component for
+ *
+ * This checks that @rid indicates a valid recurrence of @icalcomp, and
+ * if so, generates a copy of @comp containing a RECURRENCE-ID of @rid.
+ *
+ * Return value: the instance, or %NULL.
+ **/
+icalcomponent *
+e_cal_util_construct_instance (icalcomponent *icalcomp,
+			       struct icaltimetype rid)
+{
+	struct instance_data instance;
+	struct icaltimetype start, end;
+
+	g_return_val_if_fail (icalcomp != NULL, NULL);
+
+	/* Make sure this is really recurring */
+	if (!icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY) &&
+	    !icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY))
+		return NULL;
+
+	/* Make sure the specified instance really exists */
+	start = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ());
+	end = start;
+	icaltime_adjust (&end, 0, 0, 0, 1);
+
+	instance.start = icaltime_as_timet (start);
+	instance.found = FALSE;
+	icalcomponent_foreach_recurrence (icalcomp, start, end,
+					  check_instance, &instance);
+	if (!instance.found)
+		return NULL;
+
+	/* Make the instance */
+	icalcomp = icalcomponent_new_clone (icalcomp);
+	icalcomponent_set_recurrenceid (icalcomp, rid);
+
+	return icalcomp;
+}
+
+static inline gboolean
+time_matches_rid (struct icaltimetype itt, struct icaltimetype rid, CalObjModType mod)
+{
+	int compare;
+
+	compare = icaltime_compare (itt, rid);
+	if (compare == 0)
+		return TRUE;
+	else if (compare < 0 && (mod & CALOBJ_MOD_THISANDPRIOR))
+		return TRUE;
+	else if (compare > 0 && (mod & CALOBJ_MOD_THISANDFUTURE))
+		return TRUE;
+
+	return FALSE;
+}
+
+/**
+ * e_cal_util_remove_instances:
+ * @icalcomp: A (recurring) #icalcomponent
+ * @rid: The base RECURRENCE-ID to remove
+ * @mod: How to interpret @rid
+ *
+ * Removes one or more instances from @comp according to @rid and @mod.
+ *
+ * FIXME: should probably have a return value indicating whether or not
+ * @icalcomp still has any instances
+ **/
+void
+e_cal_util_remove_instances (icalcomponent *icalcomp,
+			     struct icaltimetype rid,
+			     CalObjModType mod)
+{
+	icalproperty *prop;
+	struct icaltimetype itt, recur;
+	struct icalrecurrencetype rule;
+	icalrecur_iterator *iter;
+
+	g_return_if_fail (icalcomp != NULL);
+	g_return_if_fail (mod != CALOBJ_MOD_ALL);
+
+	/* First remove RDATEs and EXDATEs in the indicated range. */
+	for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY);
+	     prop;
+	     prop = icalcomponent_get_next_property (icalcomp, ICAL_RDATE_PROPERTY)) {
+		struct icaldatetimeperiodtype period;
+
+		period = icalproperty_get_rdate (prop);
+		if (time_matches_rid (period.time, rid, mod))
+			icalcomponent_remove_property (icalcomp, prop);
+	}
+	for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY);
+	     prop;
+	     prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) {
+		itt = icalproperty_get_exdate (prop);
+		if (time_matches_rid (itt, rid, mod))
+			icalcomponent_remove_property (icalcomp, prop);
+	}
+
+	/* If we're only removing one instance, just add an EXDATE. */
+	if (mod == CALOBJ_MOD_THIS) {
+		prop = icalproperty_new_exdate (rid);
+		icalcomponent_add_property (icalcomp, prop);
+		return;
+	}
+
+	/* Otherwise, iterate through RRULEs */
+	/* FIXME: this may generate duplicate EXRULEs */
+	for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY);
+	     prop;
+	     prop = icalcomponent_get_next_property (icalcomp, ICAL_RRULE_PROPERTY)) {
+		rule = icalproperty_get_rrule (prop);
+
+		iter = icalrecur_iterator_new (rule, rid);
+		recur = icalrecur_iterator_next (iter);
+
+		if (mod & CALOBJ_MOD_THISANDFUTURE) {
+			/* If there is a recurrence on or after rid,
+			 * use the UNTIL parameter to truncate the rule
+			 * at rid.
+			 */
+			if (!icaltime_is_null_time (recur)) {
+				rule.count = 0;
+				rule.until = rid;
+				icaltime_adjust (&rule.until, 0, 0, 0, -1);
+				icalproperty_set_rrule (prop, rule);
+			}
+		} else {
+			/* (If recur == rid, skip to the next occurrence) */
+			if (icaltime_compare (recur, rid) == 0)
+				recur = icalrecur_iterator_next (iter);
+
+			/* If there is a recurrence after rid, add
+			 * an EXRULE to block instances up to rid.
+			 * Otherwise, just remove the RRULE.
+			 */
+			if (!icaltime_is_null_time (recur)) {
+				rule.count = 0;
+				/* iCalendar says we should just use rid
+				 * here, but Outlook/Exchange handle
+				 * UNTIL incorrectly.
+				 */
+				rule.until = icaltime_add (rid, icalcomponent_get_duration (icalcomp));
+				prop = icalproperty_new_exrule (rule);
+				icalcomponent_add_property (icalcomp, prop);
+			} else
+				icalcomponent_remove_property (icalcomp, prop);
+		}
+
+		icalrecur_iterator_free (iter);
+	}
+}
Index: calendar/libecal-dbus/e-cal-time-util.h
===================================================================
--- calendar/libecal-dbus/e-cal-time-util.h	(revision 409)
+++ calendar/libecal-dbus/e-cal-time-util.h	(working copy)
@@ -1 +1,125 @@
-link ../libecal/e-cal-time-util.h
\ No newline at end of file
+/* Miscellaneous time-related utilities
+ *
+ * Copyright (C) 1998 The Free Software Foundation
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Authors: Federico Mena <federico@ximian.com>
+ *          Miguel de Icaza <miguel@ximian.com>
+ *          Damon Chaplin <damon@ximian.com>
+ */
+
+#ifndef TIMEUTIL_H
+#define TIMEUTIL_H
+
+
+#include <time.h>
+#include <libical/ical.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**************************************************************************
+ * General time functions.
+ **************************************************************************/
+
+/* Returns the number of days in the month. Year is the normal year, e.g. 2001.
+   Month is 0 (Jan) to 11 (Dec). */
+int	time_days_in_month	(int year, int month);
+
+/* Returns the 1-based day number within the year of the specified date.
+   Year is the normal year, e.g. 2001. Month is 0 to 11. */
+int	time_day_of_year	(int day, int month, int year);
+
+/* Returns the day of the week for the specified date, 0 (Sun) to 6 (Sat).
+   For the days that were removed on the Gregorian reformation, it returns
+   Thursday. Year is the normal year, e.g. 2001. Month is 0 to 11. */
+int	time_day_of_week	(int day, int month, int year);
+
+/* Returns whether the specified year is a leap year. Year is the normal year,
+   e.g. 2001. */
+gboolean time_is_leap_year	(int year);
+
+/* Returns the number of leap years since year 1 up to (but not including) the
+   specified year. Year is the normal year, e.g. 2001. */
+int	time_leap_years_up_to	(int year);
+
+/* Convert to or from an ISO 8601 representation of a time, in UTC,
+   e.g. "20010708T183000Z". */
+char   *isodate_from_time_t     (time_t t);
+time_t	time_from_isodate	(const char *str);
+
+
+/**************************************************************************
+ * time_t manipulation functions.
+ *
+ * NOTE: these use the Unix timezone functions like mktime() and localtime()
+ * and so should not be used in Evolution. New Evolution code should use
+ * icaltimetype values rather than time_t values wherever possible.
+ **************************************************************************/
+
+/* Add or subtract a number of days, weeks or months. */
+time_t	time_add_day		(time_t time, int days);
+time_t	time_add_week		(time_t time, int weeks);
+
+/* Returns the beginning or end of the day. */
+time_t	time_day_begin		(time_t t);
+time_t	time_day_end		(time_t t);
+
+
+/**************************************************************************
+ * time_t manipulation functions, using timezones in libical.
+ *
+ * NOTE: these are only here to make the transition to the timezone
+ * functions easier. New code should use icaltimetype values rather than
+ * time_t values wherever possible.
+ **************************************************************************/
+
+/* Adds or subtracts a number of days to/from the given time_t value, using
+   the given timezone. */
+time_t	time_add_day_with_zone (time_t time, int days, icaltimezone *zone);
+
+/* Adds or subtracts a number of weeks to/from the given time_t value, using
+   the given timezone. */
+time_t	time_add_week_with_zone (time_t time, int weeks, icaltimezone *zone);
+
+/* Adds or subtracts a number of months to/from the given time_t value, using
+   the given timezone. */
+time_t	time_add_month_with_zone (time_t time, int months, icaltimezone *zone);
+
+/* Returns the start of the year containing the given time_t, using the given
+   timezone. */
+time_t	time_year_begin_with_zone (time_t time, icaltimezone *zone);
+
+/* Returns the start of the month containing the given time_t, using the given
+   timezone. */
+time_t	time_month_begin_with_zone (time_t time, icaltimezone *zone);
+
+/* Returns the start of the week containing the given time_t, using the given
+   timezone. week_start_day should use the same values as mktime(),
+   i.e. 0 (Sun) to 6 (Sat). */
+time_t	time_week_begin_with_zone (time_t time, int week_start_day,
+				   icaltimezone *zone);
+
+/* Returns the start of the day containing the given time_t, using the given
+   timezone. */
+time_t	time_day_begin_with_zone (time_t time, icaltimezone *zone);
+
+/* Returns the end of the day containing the given time_t, using the given
+   timezone. (The end of the day is the start of the next day.) */
+time_t	time_day_end_with_zone (time_t time, icaltimezone *zone);
+
+void time_to_gdate_with_zone (GDate *date, time_t time, icaltimezone *zone);
+
+/**************************************************************************
+ * struct tm manipulation 
+ **************************************************************************/
+
+struct tm icaltimetype_to_tm (struct icaltimetype *itt);
+struct tm icaltimetype_to_tm_with_zone (struct icaltimetype *itt,
+					icaltimezone *from_zone,
+					icaltimezone *to_zone);
+struct icaltimetype tm_to_icaltimetype (struct tm *tm, gboolean is_date);
+
+G_END_DECLS
+
+#endif
Index: calendar/libedata-cal-dbus/e-cal-backend-cache.c
===================================================================
--- calendar/libedata-cal-dbus/e-cal-backend-cache.c	(revision 409)
+++ calendar/libedata-cal-dbus/e-cal-backend-cache.c	(working copy)
@@ -1 +1,840 @@
-link ../libedata-cal/e-cal-backend-cache.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* Evolution calendar - generic backend class
+ *
+ * Copyright (C) 2003 Novell, Inc.
+ *
+ * Authors: Rodrigo Moya <rodrigo@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <libecal/e-cal-util.h>
+#include "e-cal-backend-cache.h"
+
+struct _ECalBackendCachePrivate {
+	char *uri;
+	GHashTable *timezones;
+};
+
+/* Property IDs */
+enum {
+	PROP_0,
+	PROP_URI
+};
+
+static GObjectClass *parent_class = NULL;
+
+static char *
+get_filename_from_uri (const char *uri)
+{
+	char *mangled_uri, *filename;
+	int i;
+
+	/* mangle the URI to not contain invalid characters */
+	mangled_uri = g_strdup (uri);
+	for (i = 0; i < strlen (mangled_uri); i++) {
+		switch (mangled_uri[i]) {
+		case ':' :
+		case '/' :
+			mangled_uri[i] = '_';
+		}
+	}
+
+	/* generate the file name */
+	filename = g_build_filename (g_get_home_dir (), ".evolution/cache/calendar",
+				     mangled_uri, "cache.xml", NULL);
+
+	/* free memory */
+	g_free (mangled_uri);
+
+	return filename;
+}
+
+static void
+e_cal_backend_cache_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	ECalBackendCache *cache;
+	ECalBackendCachePrivate *priv;
+	char *cache_file;
+
+	cache = E_CAL_BACKEND_CACHE (object);
+	priv = cache->priv;
+
+	switch (property_id) {
+	case PROP_URI :
+		cache_file = get_filename_from_uri (g_value_get_string (value));
+		if (!cache_file)
+			break;
+
+		g_object_set (G_OBJECT (cache), "filename", cache_file, NULL);
+		g_free (cache_file);
+
+		if (priv->uri)
+			g_free (priv->uri);
+		priv->uri = g_value_dup_string (value);
+		break;
+	default :
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	}
+}
+
+static void
+e_cal_backend_cache_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	ECalBackendCache *cache;
+	ECalBackendCachePrivate *priv;
+
+	cache = E_CAL_BACKEND_CACHE (object);
+	priv = cache->priv;
+
+	switch (property_id) {
+	case PROP_URI :
+		g_value_set_string (value, priv->uri);
+		break;
+	default :
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	}
+}
+
+static void
+free_timezone_hash (gpointer key, gpointer value, gpointer user_data)
+{
+	g_free (key);
+	icaltimezone_free (value, 1);
+}
+
+static void
+e_cal_backend_cache_finalize (GObject *object)
+{
+	ECalBackendCache *cache;
+	ECalBackendCachePrivate *priv;
+
+	cache = E_CAL_BACKEND_CACHE (object);
+	priv = cache->priv;
+
+	if (priv) {
+		if (priv->uri) {
+			g_free (priv->uri);
+			priv->uri = NULL;
+		}
+
+		if (priv->timezones) {
+			g_hash_table_foreach (priv->timezones, (GHFunc) free_timezone_hash, NULL);
+			g_hash_table_destroy (priv->timezones);
+			priv->timezones = NULL;
+		}
+
+		g_free (priv);
+		cache->priv = NULL;
+	}
+
+	parent_class->finalize (object);
+}
+
+static GObject *
+e_cal_backend_cache_constructor (GType type,
+                                 guint n_construct_properties,
+                                 GObjectConstructParam *construct_properties)
+{
+	GObject *obj;
+	const char *uri;
+	ECalBackendCacheClass *klass;
+	GObjectClass *parent_class;
+
+	/* Invoke parent constructor. */
+	klass = E_CAL_BACKEND_CACHE_CLASS (g_type_class_peek (E_TYPE_CAL_BACKEND_CACHE));
+	parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+	obj = parent_class->constructor (type,
+					 n_construct_properties,
+					 construct_properties);
+  
+	/* extract uid */
+	if (!g_ascii_strcasecmp ( g_param_spec_get_name (construct_properties->pspec), "uri")) {
+		char *cache_file;
+
+		uri = g_value_get_string (construct_properties->value);
+		cache_file = get_filename_from_uri (uri);
+		g_object_set (obj, "filename", cache_file, NULL);
+		g_free (cache_file);
+	}
+
+	return obj;
+}
+
+static void
+e_cal_backend_cache_class_init (ECalBackendCacheClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = e_cal_backend_cache_finalize;
+	object_class->set_property = e_cal_backend_cache_set_property;
+	object_class->get_property = e_cal_backend_cache_get_property;
+
+        object_class->constructor = e_cal_backend_cache_constructor;
+	g_object_class_install_property (object_class, PROP_URI,
+					 g_param_spec_string ("uri", NULL, NULL, "",
+							      G_PARAM_READABLE | G_PARAM_WRITABLE
+							      | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_cal_backend_cache_init (ECalBackendCache *cache)
+{
+	ECalBackendCachePrivate *priv;
+
+	priv = g_new0 (ECalBackendCachePrivate, 1);
+	priv->timezones = g_hash_table_new (g_str_hash, g_str_equal);
+
+	cache->priv = priv;
+
+}
+
+/**
+ * e_cal_backend_cache_get_type:
+ * @void:
+ *
+ * Registers the #ECalBackendCache class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the #ECalBackendCache class.
+ **/
+GType
+e_cal_backend_cache_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		static GTypeInfo info = {
+                        sizeof (ECalBackendCacheClass),
+                        (GBaseInitFunc) NULL,
+                        (GBaseFinalizeFunc) NULL,
+                        (GClassInitFunc) e_cal_backend_cache_class_init,
+                        NULL, NULL,
+                        sizeof (ECalBackendCache),
+                        0,
+                        (GInstanceInitFunc) e_cal_backend_cache_init,
+                };
+		type = g_type_register_static (E_TYPE_FILE_CACHE, "ECalBackendCache", &info, 0);
+	}
+
+	return type;
+}
+
+/**
+ * e_cal_backend_cache_new
+ * @uri: URI of the backend to be cached.
+ *
+ * Creates a new #ECalBackendCache object, which implements a cache of
+ * calendar/tasks objects, very useful for remote backends.
+ *
+ * Return value: The newly created object.
+ */
+ECalBackendCache *
+e_cal_backend_cache_new (const char *uri)
+{
+	ECalBackendCache *cache;
+        
+       	cache = g_object_new (E_TYPE_CAL_BACKEND_CACHE, "uri", uri, NULL);
+
+        return cache;
+}
+
+static char *
+get_key (const char *uid, const char *rid)
+{
+	GString *real_key;
+	char *retval;
+
+	real_key = g_string_new (uid);
+	if (rid && *rid) {
+		real_key = g_string_append (real_key, "@");
+		real_key = g_string_append (real_key, rid);
+	}
+
+	retval = real_key->str;
+	g_string_free (real_key, FALSE);
+
+	return retval;
+}
+
+/**
+ * e_cal_backend_cache_get_component:
+ * @cache: A %ECalBackendCache object.
+ * @uid: The UID of the component to retrieve.
+ * @rid: Recurrence ID of the specific detached recurrence to retrieve,
+ * or NULL if the whole object is to be retrieved.
+ *
+ * Gets a component from the %ECalBackendCache object.
+ *
+ * Return value: The %ECalComponent representing the component found,
+ * or %NULL if it was not found in the cache.
+ */
+ECalComponent *
+e_cal_backend_cache_get_component (ECalBackendCache *cache, const char *uid, const char *rid)
+{
+	char *real_key;
+	const char *comp_str;
+	icalcomponent *icalcomp;
+	ECalComponent *comp = NULL;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+	g_return_val_if_fail (uid != NULL, NULL);
+
+	real_key = get_key (uid, rid);
+
+	comp_str = e_file_cache_get_object (E_FILE_CACHE (cache), real_key);
+	if (comp_str) {
+		icalcomp = icalparser_parse_string (comp_str);
+		if (icalcomp) {
+			comp = e_cal_component_new ();
+			if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
+				icalcomponent_free (icalcomp);
+				g_object_unref (comp);
+				comp = NULL;
+			}
+		}
+	}
+
+	/* free memory */
+	g_free (real_key);
+
+	return comp;
+}
+
+/**
+ * e_cal_backend_cache_put_component:
+ * @cache: An #ECalBackendCache object.
+ * @comp: Component to put on the cache.
+ *
+ * Puts the given calendar component in the given cache. This will add
+ * the component if it does not exist or replace it if there was a
+ * previous version of it.
+ *
+ * Return value: TRUE if the operation was successful, FALSE otherwise.
+ */
+gboolean
+e_cal_backend_cache_put_component (ECalBackendCache *cache,
+				   ECalComponent *comp)
+{
+	char *real_key, *uid, *comp_str;
+	const char *rid;
+	gboolean retval;
+	ECalBackendCachePrivate *priv;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+	priv = cache->priv;
+
+	e_cal_component_get_uid (comp, (const char **) &uid);
+	if (e_cal_component_is_instance (comp)) {
+		rid = e_cal_component_get_recurid_as_string (comp);
+	} else
+		rid = NULL;
+
+	comp_str = e_cal_component_get_as_string (comp);
+	real_key = get_key (uid, rid);
+
+	if (e_file_cache_get_object (E_FILE_CACHE (cache), real_key))
+		retval = e_file_cache_replace_object (E_FILE_CACHE (cache), real_key, comp_str);
+	else
+		retval = e_file_cache_add_object (E_FILE_CACHE (cache), real_key, comp_str);
+
+	g_free (real_key);
+	g_free (comp_str);
+
+	return retval;
+}
+
+/**
+ * e_cal_backend_cache_remove_component:
+ * @cache: An #ECalBackendCache object.
+ * @uid: UID of the component to remove.
+ * @rid: Recurrence-ID of the component to remove. This is used when removing
+ * detached instances of a recurring appointment.
+ *
+ * Removes a component from the cache.
+ *
+ * Return value: TRUE if the component was removed, FALSE otherwise.
+ */
+gboolean
+e_cal_backend_cache_remove_component (ECalBackendCache *cache,
+				      const char *uid,
+				      const char *rid)
+{
+	char *real_key;
+	gboolean retval;
+	ECalBackendCachePrivate *priv;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
+	g_return_val_if_fail (uid != NULL, FALSE);
+
+	priv = cache->priv;
+
+	real_key = get_key (uid, rid);
+	if (!e_file_cache_get_object (E_FILE_CACHE (cache), real_key)) {
+		g_free (real_key);
+		return FALSE;
+	}
+
+	retval = e_file_cache_remove_object (E_FILE_CACHE (cache), real_key);
+	g_free (real_key);
+
+	return retval;
+}
+
+/**
+ * e_cal_backend_cache_get_components:
+ * @cache: An #ECalBackendCache object.
+ *
+ * Retrieves a list of all the components stored in the cache.
+ *
+ * Return value: A list of all 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 (ECalBackendCache *cache)
+{
+        char *comp_str;
+        GSList *l;
+	GList *list = NULL;
+	icalcomponent *icalcomp;
+	ECalComponent *comp = NULL;
+        
+        /* return null if cache is not a valid Backend Cache.  */
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+        l = e_file_cache_get_objects (E_FILE_CACHE (cache));
+        if (!l)
+                return NULL;
+        for ( ; l != NULL; l = g_slist_next (l)) {
+                comp_str = l->data;
+                if (comp_str) {
+                        icalcomp = icalparser_parse_string (comp_str);
+                        if (icalcomp) {
+				icalcomponent_kind kind;
+
+				kind = icalcomponent_isa (icalcomp);
+				if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT) {
+					comp = e_cal_component_new ();
+					if (e_cal_component_set_icalcomponent (comp, icalcomp))
+						list = g_list_append (list, comp);
+					else {
+						icalcomponent_free (icalcomp);
+						g_object_unref (comp);
+					}
+				} else
+					icalcomponent_free (icalcomp);
+                        }
+                }
+                
+        }
+
+        return list;
+}
+
+/**
+ * e_cal_backend_cache_get_components_by_uid:
+ * @cache: An #ECalBackendCache object.
+ * @uid: ID of the component to retrieve.
+ *
+ * Retrieves a ical components from the cache.
+ *
+ * Return value: The list of calendar components if found, or NULL otherwise.
+ */
+GSList *
+e_cal_backend_cache_get_components_by_uid (ECalBackendCache *cache, const char *uid)
+{
+        char *comp_str;
+        GSList *l;
+	GSList *list = NULL;
+	icalcomponent *icalcomp;
+	ECalComponent *comp = NULL;
+        
+        /* return null if cache is not a valid Backend Cache.  */
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+        l = e_file_cache_get_objects (E_FILE_CACHE (cache));
+        if (!l)
+                return NULL;
+        for ( ; l != NULL; l = g_slist_next (l)) {
+                comp_str = l->data;
+                if (comp_str) {
+                        icalcomp = icalparser_parse_string (comp_str);
+                        if (icalcomp) {
+				icalcomponent_kind kind;
+
+				kind = icalcomponent_isa (icalcomp);
+				if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT) {
+					comp = e_cal_component_new ();
+					if ((e_cal_component_set_icalcomponent (comp, icalcomp)) &&
+						!strcmp (icalcomponent_get_uid (icalcomp), uid)) 
+							list = g_slist_append (list, comp);
+					else {
+						g_object_unref (comp);
+					}
+				} else
+					icalcomponent_free (icalcomp);
+                        }
+                }
+                
+        }
+
+        return list;
+}
+
+/**
+ * e_cal_backend_cache_get_timezone:
+ * @cache: An #ECalBackendCache object.
+ * @tzid: ID of the timezone to retrieve.
+ *
+ * Retrieves a timezone component from the cache.
+ *
+ * Return value: The timezone if found, or NULL otherwise.
+ */
+const icaltimezone *
+e_cal_backend_cache_get_timezone (ECalBackendCache *cache, const char *tzid)
+{
+	icaltimezone *zone;
+	const char *comp_str;
+	ECalBackendCachePrivate *priv;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+	g_return_val_if_fail (tzid != NULL, NULL);
+
+	priv = cache->priv;
+
+	/* we first look for the timezone in the timezones hash table */
+	zone = g_hash_table_lookup (priv->timezones, tzid);
+	if (zone)
+		return (const icaltimezone *) zone;
+
+	/* if not found look for the timezone in the cache */
+	comp_str = e_file_cache_get_object (E_FILE_CACHE (cache), tzid);
+	if (comp_str) {
+		icalcomponent *icalcomp;
+
+		icalcomp = icalparser_parse_string (comp_str);
+		if (icalcomp) {
+			zone = icaltimezone_new ();
+			if (icaltimezone_set_component (zone, icalcomp) == 1)
+				g_hash_table_insert (priv->timezones, g_strdup (tzid), zone);
+			else {
+				icalcomponent_free (icalcomp);
+				icaltimezone_free (zone, 1);
+			}
+		}
+	}
+
+	return (const icaltimezone *) zone;
+}
+
+/**
+ * e_cal_backend_cache_put_timezone:
+ * @cache: An #ECalBackendCache object.
+ * @zone: The timezone to put on the cache.
+ *
+ * Puts the given timezone in the cache, adding it, if it did not exist, or
+ * replacing it, if there was an older version.
+ *
+ * Return value: TRUE if the timezone was put on the cache, FALSE otherwise.
+ */
+gboolean
+e_cal_backend_cache_put_timezone (ECalBackendCache *cache, const icaltimezone *zone)
+{
+	ECalBackendCachePrivate *priv;
+	gpointer orig_key, orig_value;
+	icaltimezone *new_zone;
+	icalcomponent *icalcomp;
+	gboolean retval;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
+	g_return_val_if_fail (zone != NULL, FALSE);
+
+	priv = cache->priv;
+
+	/* add the timezone to the cache file */
+	icalcomp = icaltimezone_get_component ((icaltimezone *)zone);
+	if (!icalcomp)
+		return FALSE;
+
+	if (e_file_cache_get_object (E_FILE_CACHE (cache), icaltimezone_get_tzid ((icaltimezone *)zone))) {
+		retval = e_file_cache_replace_object (E_FILE_CACHE (cache),
+						      icaltimezone_get_tzid ((icaltimezone *)zone),
+						      icalcomponent_as_ical_string (icalcomp));
+	} else {
+		retval = e_file_cache_add_object (E_FILE_CACHE (cache),
+						  icaltimezone_get_tzid ((icaltimezone *)zone),
+						  icalcomponent_as_ical_string (icalcomp));
+	}
+
+	if (!retval)
+		return FALSE;
+
+	/* check if the timezone already exists */
+	if (g_hash_table_lookup_extended (priv->timezones, icaltimezone_get_tzid ((icaltimezone *)zone),
+					  &orig_key, &orig_value)) {
+		/* remove the previous timezone */
+		g_hash_table_remove (priv->timezones, orig_key);
+		g_free (orig_key);
+		icaltimezone_free (orig_value, 1);
+	}
+
+	/* add the timezone to the hash table */
+	new_zone = icaltimezone_new ();
+	icaltimezone_set_component (new_zone, icalcomponent_new_clone (icalcomp));
+	g_hash_table_insert (priv->timezones, g_strdup (icaltimezone_get_tzid (new_zone)), new_zone);
+
+	return TRUE;
+}
+
+/**
+ * e_cal_backend_cache_put_default_timezone:
+ * @cache: An #ECalBackendCache object.
+ * @default_zone: The default timezone.
+ *
+ * Sets the default timezone on the cache.
+ *
+ * Return value: TRUE if the operation was successful, FALSE otherwise.
+ */
+gboolean
+e_cal_backend_cache_put_default_timezone (ECalBackendCache *cache, icaltimezone *default_zone)
+{
+	ECalBackendCachePrivate *priv;
+	icalcomponent *icalcomp;
+	gboolean retval;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
+
+	priv = cache->priv;
+
+	/* add the timezone to the cache file */
+	icalcomp = icaltimezone_get_component (default_zone);
+	if (!icalcomp)
+		return FALSE;
+
+	if (e_file_cache_get_object (E_FILE_CACHE (cache), "default_zone")) {
+		retval = e_file_cache_replace_object (E_FILE_CACHE (cache), "default_zone",
+						      icalcomponent_as_ical_string (icalcomp));
+	} else {
+		retval = e_file_cache_add_object (E_FILE_CACHE (cache),
+						 "default_zone",
+						  icalcomponent_as_ical_string (icalcomp));
+	}
+
+	if (!retval)
+		return FALSE;
+
+	return TRUE;
+
+}
+
+/**
+ * e_cal_backend_cache_get_default_timezone:
+ * @cache: An #ECalBackendCache object.
+ *
+ * Retrieves the default timezone from the cache.
+ *
+ * Return value: The default timezone, or NULL if no default timezone
+ * has been set on the cache.
+ */
+icaltimezone *
+e_cal_backend_cache_get_default_timezone (ECalBackendCache *cache)
+{
+	icaltimezone *zone;
+	const char *comp_str;
+	ECalBackendCachePrivate *priv;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+
+	priv = cache->priv;
+
+	/*  look for the timezone in the cache */
+	comp_str = e_file_cache_get_object (E_FILE_CACHE (cache), "default_zone");
+	if (comp_str) {
+		icalcomponent *icalcomp;
+
+		icalcomp = icalparser_parse_string (comp_str);
+		if (icalcomp) {
+			zone = icaltimezone_new ();
+			if (icaltimezone_set_component (zone, icalcomp) == 1) {
+				return zone;
+			} else {
+				icalcomponent_free (icalcomp);
+				icaltimezone_free (zone, 1);
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * e_cal_backend_cache_remove_timezone:
+ * @cache: An #ECalBackendCache object.
+ * @tzid: ID of the timezone to remove.
+ *
+ * Removes a timezone component from the cache.
+ *
+ * Return value: TRUE if the timezone was removed, FALSE otherwise.
+ */
+gboolean
+e_cal_backend_cache_remove_timezone (ECalBackendCache *cache, const char *tzid)
+{
+	gpointer orig_key, orig_value;
+	ECalBackendCachePrivate *priv;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
+	g_return_val_if_fail (tzid != NULL, FALSE);
+
+	priv = cache->priv;
+
+	if (g_hash_table_lookup_extended (priv->timezones, tzid, &orig_key, &orig_value)) {
+		g_hash_table_remove (priv->timezones, tzid);
+		g_free (orig_key);
+		icaltimezone_free (orig_value, 1);
+	}
+
+	return e_file_cache_remove_object (E_FILE_CACHE (cache), tzid);
+}
+
+/**
+ * e_cal_backend_cache_get_keys:
+ * @cache: An #ECalBackendCache object.
+ *
+ * Gets the list of unique keys in the cache file.
+ *
+ * Return value: A list of all the keys. The items in the list are pointers
+ * to internal data, so should not be freed, only the list should.
+ */
+GSList *
+e_cal_backend_cache_get_keys (ECalBackendCache *cache)
+{
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+        return e_file_cache_get_keys (E_FILE_CACHE (cache));
+}
+
+/**
+ * e_cal_backend_cache_set_marker:
+ * @cache: An #ECalBackendCache object.
+ *
+ * Marks the cache as populated, to discriminate between an empty calendar
+ * and an unpopulated one.
+ */
+void
+e_cal_backend_cache_set_marker (ECalBackendCache *cache)
+{
+	g_return_if_fail (E_IS_CAL_BACKEND_CACHE (cache));
+	e_file_cache_add_object (E_FILE_CACHE (cache), "populated", "TRUE");
+}
+
+/**
+ * e_cal_backend_cache_get_marker:
+ * @cache: An #ECalBackendCache object.
+ *
+ * Gets the marker of the cache. If this field is present, it means the
+ * cache has been populated.
+ *
+ * Return value: The value of the marker or NULL if the cache is still empty.
+ */
+const char *
+e_cal_backend_cache_get_marker (ECalBackendCache *cache)
+{
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+	if (e_file_cache_get_object (E_FILE_CACHE (cache), "populated"))
+		return "";
+	return NULL;
+}
+
+/**
+ * e_cal_backend_cache_put_server_utc_time:
+ * @cache: An #ECalBackendCache object.
+ * @utc_str: UTC string.
+ *
+ * Return value: TRUE if the operation was successful, FALSE otherwise.
+ */
+gboolean
+e_cal_backend_cache_put_server_utc_time (ECalBackendCache *cache, const char *utc_str)
+{
+	gboolean ret_val = FALSE;
+	
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
+
+	if (!(ret_val = e_file_cache_add_object (E_FILE_CACHE (cache), "server_utc_time", utc_str)))
+		ret_val = e_file_cache_replace_object (E_FILE_CACHE (cache), "server_utc_time", utc_str);
+
+	return ret_val;
+}
+
+/**
+ * e_cal_backend_cache_get_server_utc_time:
+ * @cache: An #ECalBackendCache object.
+ *
+ * Return value: The server's UTC string.
+ */
+const char *
+e_cal_backend_cache_get_server_utc_time (ECalBackendCache *cache)
+{
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+	
+       	return	e_file_cache_get_object (E_FILE_CACHE (cache), "server_utc_time");
+}
+
+/**
+ * e_cal_backend_cache_put_key_value:
+ * @cache: An #ECalBackendCache object.
+ * @keyp: The Key parameter to identify uniquely.
+ * @valuep: The value for the keyp parameter.
+ *
+ * Return value: TRUE if the operation was successful, FALSE otherwise.
+ */
+gboolean
+e_cal_backend_cache_put_key_value (ECalBackendCache *cache, const char *key, const char *value)
+{
+	gboolean ret_val = FALSE;
+	
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
+
+	if (value) {
+		e_file_cache_remove_object (E_FILE_CACHE (cache), key);
+		return TRUE;
+	}
+	
+	if (!(ret_val = e_file_cache_add_object (E_FILE_CACHE (cache), key, value)))
+		ret_val = e_file_cache_replace_object (E_FILE_CACHE (cache), key, value);
+
+	return ret_val;
+}
+
+/**
+ * e_cal_backend_cache_get_key_value:
+ * @cache: An #ECalBackendCache object.
+ *
+ * Return value: The value.
+ */
+const char *
+e_cal_backend_cache_get_key_value (ECalBackendCache *cache, const char *key)
+{
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
+	
+       	return	e_file_cache_get_object (E_FILE_CACHE (cache), key);
+}
Index: calendar/libedata-cal-dbus/e-cal-backend-factory.c
===================================================================
--- calendar/libedata-cal-dbus/e-cal-backend-factory.c	(revision 409)
+++ calendar/libedata-cal-dbus/e-cal-backend-factory.c	(working copy)
@@ -1 +1,99 @@
-link ../libedata-cal/e-cal-backend-factory.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *
+ * Author:
+ *   Chris Toshok (toshok@ximian.com)
+ *
+ * Copyright 2004, Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "e-cal-backend-factory.h"
+
+static void
+e_cal_backend_factory_instance_init (ECalBackendFactory *factory)
+{
+}
+
+static void
+e_cal_backend_factory_class_init (ECalBackendFactoryClass *klass)
+{
+}
+
+GType
+e_cal_backend_factory_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo info = {
+			sizeof (ECalBackendFactoryClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc)  e_cal_backend_factory_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (ECalBackend),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) e_cal_backend_factory_instance_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT, "ECalBackendFactory", &info, 0);
+	}
+
+	return type;
+}
+
+/**
+ * e_cal_backend_factory_get_kind:
+ * @factory: An #ECalBackendFactory object.
+ *
+ * Gets the component type of the factory.
+ *
+ * Return value: The kind of factory.
+ */
+icalcomponent_kind
+e_cal_backend_factory_get_kind (ECalBackendFactory *factory)
+{
+	g_return_val_if_fail (E_IS_CAL_BACKEND_FACTORY (factory), 0/*XXX*/);
+
+	return E_CAL_BACKEND_FACTORY_GET_CLASS (factory)->get_kind (factory);
+}
+
+/**
+ * e_cal_backend_factory_get_protocol:
+ * @factory: An #ECalBackendFactory object.
+ *
+ * Gets the protocol used by the factory.
+ *
+ * Return value: The protocol.
+ */
+const char*
+e_cal_backend_factory_get_protocol (ECalBackendFactory *factory)
+{
+	g_return_val_if_fail (E_IS_CAL_BACKEND_FACTORY (factory), NULL);
+
+	return E_CAL_BACKEND_FACTORY_GET_CLASS (factory)->get_protocol (factory);
+}
+
+/**
+ * e_cal_backend_factory_new_backend:
+ * @factory: An #ECalBackendFactory object.
+ * @source: An #ESource.
+ *
+ * Creates a new backend for the given @source.
+ *
+ * Return value: The newly created backend, or NULL if there was an error.
+ */
+ECalBackend*
+e_cal_backend_factory_new_backend (ECalBackendFactory *factory, ESource *source)
+{
+	g_return_val_if_fail (E_IS_CAL_BACKEND_FACTORY (factory), NULL);
+
+	return E_CAL_BACKEND_FACTORY_GET_CLASS (factory)->new_backend (factory, source);
+}
Index: calendar/libedata-cal-dbus/e-cal-backend-cache.h
===================================================================
--- calendar/libedata-cal-dbus/e-cal-backend-cache.h	(revision 409)
+++ calendar/libedata-cal-dbus/e-cal-backend-cache.h	(working copy)
@@ -1 +1,82 @@
-link ../libedata-cal/e-cal-backend-cache.h
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* Evolution calendar - generic backend class
+ *
+ * Copyright (C) 2003 Novell, Inc.
+ *
+ * Authors: Rodrigo Moya <rodrigo@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef E_CAL_BACKEND_CACHE_H
+#define E_CAL_BACKEND_CACHE_H
+
+#include <libedataserver/e-file-cache.h>
+#include <libecal/e-cal-component.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_CAL_BACKEND_CACHE            (e_cal_backend_cache_get_type ())
+#define E_CAL_BACKEND_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CAL_BACKEND_CACHE, ECalBackendCache))
+#define E_CAL_BACKEND_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CAL_BACKEND_CACHE, ECalBackendCacheClass))
+#define E_IS_CAL_BACKEND_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CAL_BACKEND_CACHE))
+#define E_IS_CAL_BACKEND_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CAL_BACKEND_CACHE))
+
+typedef struct _ECalBackendCachePrivate ECalBackendCachePrivate;
+
+typedef struct {
+	EFileCache parent;
+	ECalBackendCachePrivate *priv;
+} ECalBackendCache;
+
+typedef struct {
+	EFileCacheClass parent_class;
+} ECalBackendCacheClass;
+
+GType               e_cal_backend_cache_get_type (void);
+
+ECalBackendCache   *e_cal_backend_cache_new (const char *uri);
+ECalComponent      *e_cal_backend_cache_get_component (ECalBackendCache *cache,
+						       const char *uid,
+						       const char *rid);
+gboolean            e_cal_backend_cache_put_component (ECalBackendCache *cache, ECalComponent *comp);
+gboolean            e_cal_backend_cache_remove_component (ECalBackendCache *cache,
+							  const char *uid,
+							  const char *rid);
+GList              *e_cal_backend_cache_get_components (ECalBackendCache *cache);
+GSList             *e_cal_backend_cache_get_components_by_uid (ECalBackendCache *cache, const char *uid);
+
+
+
+const icaltimezone *e_cal_backend_cache_get_timezone (ECalBackendCache *cache, const char *tzid);
+gboolean            e_cal_backend_cache_put_timezone (ECalBackendCache *cache, const icaltimezone *zone);
+gboolean            e_cal_backend_cache_remove_timezone (ECalBackendCache *cache, const char *tzid);
+
+gboolean            e_cal_backend_cache_put_default_timezone (ECalBackendCache *cache, icaltimezone *default_zone);
+icaltimezone       *e_cal_backend_cache_get_default_timezone (ECalBackendCache *cache);
+
+GSList             *e_cal_backend_cache_get_keys (ECalBackendCache *cache);
+
+const char         *e_cal_backend_cache_get_marker (ECalBackendCache *cache);
+void                e_cal_backend_cache_set_marker (ECalBackendCache *cache);
+
+gboolean e_cal_backend_cache_put_server_utc_time (ECalBackendCache *cache, const char *utc_str);
+const char * e_cal_backend_cache_get_server_utc_time (ECalBackendCache *cache);
+
+gboolean e_cal_backend_cache_put_key_value (ECalBackendCache *cache, const char *key, const char *value);
+const char * e_cal_backend_cache_get_key_value (ECalBackendCache *cache, const char *key);
+
+G_END_DECLS
+
+#endif
Index: calendar/libedata-cal-dbus/e-cal-backend-factory.h
===================================================================
--- calendar/libedata-cal-dbus/e-cal-backend-factory.h	(revision 409)
+++ calendar/libedata-cal-dbus/e-cal-backend-factory.h	(working copy)
@@ -1 +1,60 @@
-link ../libedata-cal/e-cal-backend-factory.h
\ No newline at end of file
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-cal-backend-factory.h
+ *
+ * Copyright (C) 2004  Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser 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: Chris Toshok <toshok@ximian.com>
+ */
+
+#ifndef _E_CAL_BACKEND_FACTORY_H_
+#define _E_CAL_BACKEND_FACTORY_H_
+
+#include <glib-object.h>
+#include "e-cal-backend.h"
+
+G_BEGIN_DECLS
+
+#define E_TYPE_CAL_BACKEND_FACTORY        (e_cal_backend_factory_get_type ())
+#define E_CAL_BACKEND_FACTORY(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_CAL_BACKEND_FACTORY, ECalBackendFactory))
+#define E_CAL_BACKEND_FACTORY_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), E_TYPE_CAL_BACKEND_FACTORY, ECalBackendFactoryClass))
+#define E_IS_CAL_BACKEND_FACTORY(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_CAL_BACKEND_FACTORY))
+#define E_IS_CAL_BACKEND_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_CAL_BACKEND_FACTORY))
+#define E_CAL_BACKEND_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), E_TYPE_CAL_BACKEND_FACTORY, ECalBackendFactoryClass))
+
+typedef struct _ECalBackendFactoryPrivate ECalBackendFactoryPrivate;
+
+typedef struct {
+	GObject            parent_object;
+} ECalBackendFactory;
+
+typedef struct {
+	GObjectClass parent_class;
+
+	icalcomponent_kind (*get_kind)     (ECalBackendFactory *factory);
+	const char*        (*get_protocol) (ECalBackendFactory *factory);
+	ECalBackend*       (*new_backend)  (ECalBackendFactory *factory, ESource *source);
+} ECalBackendFactoryClass;
+
+GType               e_cal_backend_factory_get_type              (void);
+
+icalcomponent_kind  e_cal_backend_factory_get_kind              (ECalBackendFactory *factory);
+const char*         e_cal_backend_factory_get_protocol          (ECalBackendFactory *factory);
+ECalBackend*        e_cal_backend_factory_new_backend           (ECalBackendFactory *factory, ESource *source);
+
+G_END_DECLS
+
+#endif /* _E_CAL_BACKEND_FACTORY_H_ */
Index: calendar/libedata-cal-dbus/e-cal-backend-sexp.c
===================================================================
--- calendar/libedata-cal-dbus/e-cal-backend-sexp.c	(revision 409)
+++ calendar/libedata-cal-dbus/e-cal-backend-sexp.c	(working copy)
@@ -1 +1,1221 @@
-link ../libedata-cal/e-cal-backend-sexp.c
\ No newline at end of file
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* 
+ * cal-backend-card-sexp.c
+ * Copyright 1999, 2000, 2001, Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <libedataserver/e-util.h>
+#include <libecal/e-cal-time-util.h>
+
+#include "e-cal-backend-sexp.h"
+
+static GObjectClass *parent_class;
+
+typedef struct _SearchContext SearchContext;
+
+struct _ECalBackendSExpPrivate {
+	ESExp *search_sexp;
+	char *text;
+	SearchContext *search_context;
+};
+
+struct _SearchContext {
+	ECalComponent *comp;
+	ECalBackend *backend;
+	gboolean occurs;
+};
+
+/**
+ * e_cal_backend_sexp_func_time_now:
+ * @esexp: An #ESExp object.
+ * @argc: Number of arguments.
+ * @argv: The arguments.
+ * @data: Closure data.
+ *
+ * Processes the (time-now) sexp expression.
+ *
+ * Return value: The result of the function.
+ */
+ESExpResult *
+e_cal_backend_sexp_func_time_now (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	ESExpResult *result;
+
+	if (argc != 0) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
+				    "time-now");
+		return NULL;
+	}
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+	result->value.time = time (NULL);
+
+	return result;
+}
+
+/**
+ * e_cal_backend_sexp_func_make_time:
+ * @esexp: An #ESExp object.
+ * @argc: Number of arguments.
+ * @argv: The arguments.
+ * @data: Closure data.
+ *
+ * (make-time ISODATE)
+ * ISODATE - string, ISO 8601 date/time representation
+ *
+ * Constructs a time_t value for the specified date.
+ *
+ * Return value: The result of the function.
+ */
+ESExpResult *
+e_cal_backend_sexp_func_make_time (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	const char *str;
+	time_t t;
+	ESExpResult *result;
+
+	if (argc != 1) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
+				    "make-time");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_STRING) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a string"),
+				    "make-time");
+		return NULL;
+	}
+	str = argv[0]->value.string;
+	if (!str || !*str) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a string"),
+				    "make-time");
+		return NULL;
+	}
+
+	t = time_from_isodate (str);
+	if (t == -1) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be an ISO 8601 "
+					     "date/time string"),
+				    "make-time");
+		return NULL;
+	}
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+	result->value.time = t;
+
+	return result;
+}
+
+/**
+ * e_cal_backend_sexp_func_time_add_day:
+ * @esexp: An #ESExp object.
+ * @argc: Number of arguments.
+ * @argv: The arguments.
+ * @data: Closure data.
+ *
+ * (time-add-day TIME N)
+ * TIME - time_t, base time
+ * N - int, number of days to add
+ *
+ * Adds the specified number of days to a time value.
+ *
+ * FIXME: TIMEZONES - need to use a timezone or daylight saving changes will
+ * make the result incorrect.
+ *
+ * Return value: The result of the function.
+ */
+ESExpResult *
+e_cal_backend_sexp_func_time_add_day (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	ESExpResult *result;
+	time_t t;
+	int n;
+
+	if (argc != 2) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
+				    "time-add-day");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_TIME) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a time_t"),
+				    "time-add-day");
+		return NULL;
+	}
+	t = argv[0]->value.time;
+
+	if (argv[1]->type != ESEXP_RES_INT) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
+					     "argument to be an integer"),
+				    "time-add-day");
+		return NULL;
+	}
+	n = argv[1]->value.number;
+	
+	result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+	result->value.time = time_add_day (t, n);
+
+	return result;
+}
+
+/**
+ * e_cal_backend_sexp_func_time_day_begin:
+ * @esexp: An #ESExp object.
+ * @argc: Number of arguments.
+ * @argv: The arguments.
+ * @data: Closure data.
+ *
+ * (time-day-begin TIME)
+ * TIME - time_t, base time
+ *
+ * Returns the start of the day, according to the local time.
+ *
+ * FIXME: TIMEZONES - this uses the current Unix timezone.
+ *
+ * Return value: The result of the function.
+ */
+ESExpResult *
+e_cal_backend_sexp_func_time_day_begin (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	time_t t;
+	ESExpResult *result;
+
+	if (argc != 1) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
+				    "time-day-begin");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_TIME) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a time_t"),
+				    "time-day-begin");
+		return NULL;
+	}
+	t = argv[0]->value.time;
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+	result->value.time = time_day_begin (t);
+
+	return result;
+}
+
+/**
+ * e_cal_backend_sexp_func_time_day_end:
+ * @esexp: An #ESExp object.
+ * @argc: Number of arguments.
+ * @argv: The arguments.
+ * @data: Closure data.
+ *
+ * (time-day-end TIME)
+ * TIME - time_t, base time
+ *
+ * Returns the end of the day, according to the local time.
+ *
+ * FIXME: TIMEZONES - this uses the current Unix timezone.
+ *
+ * Return value: The result of the function.
+ */
+ESExpResult *
+e_cal_backend_sexp_func_time_day_end (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	time_t t;
+	ESExpResult *result;
+
+	if (argc != 1) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
+				    "time-day-end");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_TIME) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a time_t"),
+				    "time-day-end");
+		return NULL;
+	}
+	t = argv[0]->value.time;
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+	result->value.time = time_day_end (t);
+
+	return result;
+}
+
+/* (uid? UID)
+ *
+ * UID - the uid of the component
+ *
+ * Returns a boolean indicating whether the component has the given UID
+ */
+static ESExpResult *
+func_uid (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	const char *uid = NULL, *arg_uid;
+	gboolean equal;
+	ESExpResult *result;
+
+	/* Check argument types */
+
+	if (argc != 1) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
+				    "uid");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_STRING) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a string"),
+				    "uid");
+		return NULL;
+	}
+
+	arg_uid = argv[0]->value.string;	
+	e_cal_component_get_uid (ctx->comp, &uid);
+
+	if (!arg_uid && !uid)
+		equal = TRUE;
+	else if ((!arg_uid || !uid) && arg_uid != uid)
+		equal = FALSE;
+	else if (e_util_utf8_strstrcase (arg_uid, uid) != NULL && strlen (arg_uid) == strlen (uid))
+		equal = TRUE;
+	else
+		equal = FALSE;
+	
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	result->value.bool = equal;
+
+	return result;
+}
+
+static gboolean
+check_instance_time_range_cb (ECalComponent *comp, time_t instance_start, time_t instance_end, gpointer data)
+{
+	SearchContext *ctx = data;
+
+	/* if we get called, the event has an occurrence in the given time range */
+	ctx->occurs = TRUE;
+
+	return FALSE;
+}
+
+static icaltimezone *
+resolve_tzid_cb (const char *tzid, gpointer user_data)
+{
+	SearchContext *ctx = user_data;
+                                                                                
+        if (!tzid || !tzid[0])
+                return NULL;
+        else if (!strcmp (tzid, "UTC"))
+                return icaltimezone_get_utc_timezone ();
+                                                                                
+        return e_cal_backend_internal_get_timezone (ctx->backend, tzid);
+}
+
+/* (occur-in-time-range? START END)
+ *
+ * START - time_t, start of the time range
+ * END - time_t, end of the time range
+ *
+ * Returns a boolean indicating whether the component has any occurrences in the
+ * specified time range.
+ */
+static ESExpResult *
+func_occur_in_time_range (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	time_t start, end;
+	ESExpResult *result;
+	icaltimezone *default_zone;
+
+	/* Check argument types */
+
+	if (argc != 2) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
+				    "occur-in-time-range");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_TIME) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a time_t"),
+				    "occur-in-time-range");
+		return NULL;
+	}
+	start = argv[0]->value.time;
+
+	if (argv[1]->type != ESEXP_RES_TIME) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
+					     "argument to be a time_t"),
+				    "occur-in-time-range");
+		return NULL;
+	}
+	end = argv[1]->value.time;
+
+	/* See if the object occurs in the specified time range */
+	default_zone = e_cal_backend_internal_get_default_timezone (ctx->backend);
+	if (!default_zone)
+		default_zone = icaltimezone_get_utc_timezone ();
+
+	ctx->occurs = FALSE;
+	e_cal_recur_generate_instances (ctx->comp, start, end,
+					(ECalRecurInstanceFn) check_instance_time_range_cb,
+					ctx, resolve_tzid_cb, ctx,
+					default_zone);
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	result->value.bool = ctx->occurs;
+
+	return result;
+}
+
+/* Returns whether a list of ECalComponentText items matches the specified string */
+static gboolean
+matches_text_list (GSList *text_list, const char *str)
+{
+	GSList *l;
+	gboolean matches;
+
+	matches = FALSE;
+
+	for (l = text_list; l; l = l->next) {
+		ECalComponentText *text;
+
+		text = l->data;
+		g_assert (text->value != NULL);
+
+		if (e_util_utf8_strstrcasedecomp (text->value, str) != NULL) {
+			matches = TRUE;
+			break;
+		}
+	}
+
+	return matches;
+}
+
+/* Returns whether the comments in a component matches the specified string */
+static gboolean
+matches_comment (ECalComponent *comp, const char *str)
+{
+	GSList *list;
+	gboolean matches;
+
+	e_cal_component_get_comment_list (comp, &list);
+	matches = matches_text_list (list, str);
+	e_cal_component_free_text_list (list);
+
+	return matches;
+}
+
+/* Returns whether the description in a component matches the specified string */
+static gboolean
+matches_description (ECalComponent *comp, const char *str)
+{
+	GSList *list;
+	gboolean matches;
+
+	e_cal_component_get_description_list (comp, &list);
+	matches = matches_text_list (list, str);
+	e_cal_component_free_text_list (list);
+
+	return matches;
+}
+
+/* Returns whether the summary in a component matches the specified string */
+static gboolean
+matches_summary (ECalComponent *comp, const char *str)
+{
+	ECalComponentText text;
+
+	e_cal_component_get_summary (comp, &text);
+
+	if (!text.value)
+		return FALSE;
+
+	return e_util_utf8_strstrcasedecomp (text.value, str) != NULL;
+}
+
+/* Returns whether the location in a component matches the specified string */
+static gboolean
+matches_location (ECalComponent *comp, const char *str)
+{
+	const char *location = NULL;
+
+	e_cal_component_get_location (comp, &location);
+
+	if (!location)
+		return FALSE;
+
+	return e_util_utf8_strstrcasedecomp (location, str) != NULL;
+}
+
+/* Returns whether any text field in a component matches the specified string */
+static gboolean
+matches_any (ECalComponent *comp, const char *str)
+{
+	/* As an optimization, and to make life easier for the individual
+	 * predicate functions, see if we are looking for the empty string right
+	 * away.
+	 */
+	if (strlen (str) == 0)
+		return TRUE;
+
+	return (matches_comment (comp, str)
+		|| matches_description (comp, str)
+		|| matches_summary (comp, str)
+		|| matches_location (comp, str));
+}
+
+/* (contains? FIELD STR)
+ *
+ * FIELD - string, name of field to match (any, comment, description, summary, location)
+ * STR - string, match string
+ *
+ * Returns a boolean indicating whether the specified field contains the
+ * specified string.
+ */
+static ESExpResult *
+func_contains (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	const char *field;
+	const char *str;
+	gboolean matches;
+	ESExpResult *result;
+
+	/* Check argument types */
+
+	if (argc != 2) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
+				    "contains");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_STRING) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a string"),
+				    "contains");
+		return NULL;
+	}
+	field = argv[0]->value.string;
+
+	if (argv[1]->type != ESEXP_RES_STRING) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
+					     "argument to be a string"),
+				    "contains");
+		return NULL;
+	}
+	str = argv[1]->value.string;
+
+	/* See if it matches */
+
+	if (strcmp (field, "any") == 0)
+		matches = matches_any (ctx->comp, str);
+	else if (strcmp (field, "comment") == 0)
+		matches = matches_comment (ctx->comp, str);
+	else if (strcmp (field, "description") == 0)
+		matches = matches_description (ctx->comp, str);
+	else if (strcmp (field, "summary") == 0)
+		matches = matches_summary (ctx->comp, str);
+	else if (strcmp (field, "location") == 0)
+		matches = matches_location (ctx->comp, str);
+	else {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be either \"any\", "
+					     "\"summary\", or \"description\", or \"location\""),
+				    "contains");
+		return NULL;
+	}
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	result->value.bool = matches;
+
+	return result;
+}
+
+/* (has-alarms?)
+ *
+ * A boolean value for components that have/dont have alarms.
+ *
+ * Returns: a boolean indicating whether the component has alarms or not.
+ */
+static ESExpResult *
+func_has_alarms (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	ESExpResult *result;
+
+	/* Check argument types */
+
+	if (argc != 0) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
+				    "has-alarms");
+		return NULL;
+	}
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	result->value.bool = e_cal_component_has_alarms (ctx->comp);
+
+	return result;
+}
+
+/* (has-alarms-in-range? START END)
+ *
+ * START - time_t, start of the time range
+ * END - time_t, end of the time range
+ *
+ * Returns: a boolean indicating whether the component has alarms in the given
+ * time range or not.
+ */
+static ESExpResult *
+func_has_alarms_in_range (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	time_t start, end;
+	ESExpResult *result;
+	icaltimezone *default_zone;
+	ECalComponentAlarms *alarms;
+	ECalComponentAlarmAction omit[] = {-1};
+	SearchContext *ctx = data;
+
+	/* Check argument types */
+
+	if (argc != 2) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
+				    "has-alarms-in-range");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_TIME) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a time_t"),
+				    "has-alarms-in-range");
+		return NULL;
+	}
+	start = argv[0]->value.time;
+
+	if (argv[1]->type != ESEXP_RES_TIME) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
+					     "argument to be a time_t"),
+				    "has-alarms-in-range");
+		return NULL;
+	}
+	end = argv[1]->value.time;
+
+	/* See if the object has alarms in the given time range */
+	default_zone = e_cal_backend_internal_get_default_timezone (ctx->backend);
+	if (!default_zone)
+		default_zone = icaltimezone_get_utc_timezone ();
+
+	alarms = e_cal_util_generate_alarms_for_comp (ctx->comp, start, end,
+						      omit, resolve_tzid_cb,
+						      ctx, default_zone);
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	if (alarms) {
+		result->value.bool = TRUE;
+		e_cal_component_alarms_free (alarms);
+	} else
+		result->value.bool = FALSE;
+
+	return result;
+}
+
+/* (has-categories? STR+)
+ * (has-categories? #f)
+ *
+ * STR - At least one string specifying a category
+ * Or you can specify a single #f (boolean false) value for components
+ * that have no categories assigned to them ("unfiled").
+ *
+ * Returns a boolean indicating whether the component has all the specified
+ * categories.
+ */
+static ESExpResult *
+func_has_categories (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	gboolean unfiled;
+	int i;
+	GSList *categories;
+	gboolean matches;
+	ESExpResult *result;
+
+	/* Check argument types */
+
+	if (argc < 1) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects at least one "
+					     "argument"),
+				    "has-categories");
+		return NULL;
+	}
+
+	if (argc == 1 && argv[0]->type == ESEXP_RES_BOOL)
+		unfiled = TRUE;
+	else
+		unfiled = FALSE;
+
+	if (!unfiled)
+		for (i = 0; i < argc; i++)
+			if (argv[i]->type != ESEXP_RES_STRING) {
+				e_sexp_fatal_error (esexp, _("\"%s\" expects "
+							     "all arguments to "
+							     "be strings or "
+							     "one and only one "
+							     "argument to be a "
+							     "boolean false "
+							     "(#f)"),
+						    "has-categories");
+				return NULL;
+			}
+
+	/* Search categories.  First, if there are no categories we return
+	 * whether unfiled components are supposed to match.
+	 */
+
+	e_cal_component_get_categories_list (ctx->comp, &categories);
+	if (!categories) {
+		result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+		result->value.bool = unfiled;
+
+		return result;
+	}
+
+	/* Otherwise, we *do* have categories but unfiled components were
+	 * requested, so this component does not match.
+	 */
+	if (unfiled) {
+		result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+		result->value.bool = FALSE;
+
+		return result;
+	}
+
+	matches = TRUE;
+
+	for (i = 0; i < argc; i++) {
+		const char *sought;
+		GSList *l;
+		gboolean has_category;
+
+		sought = argv[i]->value.string;
+
+		has_category = FALSE;
+
+		for (l = categories; l; l = l->next) {
+			const char *category;
+
+			category = l->data;
+
+			if (strcmp (category, sought) == 0) {
+				has_category = TRUE;
+				break;
+			}
+		}
+
+		if (!has_category) {
+			matches = FALSE;
+			break;
+		}
+	}
+
+	e_cal_component_free_categories_list (categories);
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	result->value.bool = matches;
+
+	return result;
+}
+
+/* (has-recurrences?)
+ *
+ * A boolean value for components that have/dont have recurrences.
+ *
+ * Returns: a boolean indicating whether the component has recurrences or not.
+ */
+static ESExpResult *
+func_has_recurrences (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	ESExpResult *result;
+
+	/* Check argument types */
+
+	if (argc != 0) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
+				    "has-recurrences");
+		return NULL;
+	}
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	result->value.bool = e_cal_component_has_recurrences (ctx->comp);
+
+	return result;
+}
+
+/* (is-completed?)
+ *
+ * Returns a boolean indicating whether the component is completed (i.e. has
+ * a COMPLETED property. This is really only useful for TODO components.
+ */
+static ESExpResult *
+func_is_completed (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	ESExpResult *result;
+	struct icaltimetype *t;
+	gboolean complete = FALSE;
+
+	/* Check argument types */
+
+	if (argc != 0) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
+				    "is-completed");
+		return NULL;
+	}
+
+	e_cal_component_get_completed (ctx->comp, &t);
+	if (t) {
+		complete = TRUE;
+		e_cal_component_free_icaltimetype (t);
+	}
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	result->value.bool = complete;
+
+	return result;
+}
+
+/* (completed-before? TIME)
+ *
+ * TIME - time_t
+ *
+ * Returns a boolean indicating whether the component was completed on or
+ * before the given time (i.e. it checks the COMPLETED property).
+ * This is really only useful for TODO components.
+ */
+static ESExpResult *
+func_completed_before (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+	SearchContext *ctx = data;
+	ESExpResult *result;
+	struct icaltimetype *tt;
+	icaltimezone *zone;
+	gboolean retval = FALSE;
+	time_t before_time, completed_time;
+
+	/* Check argument types */
+
+	if (argc != 1) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
+				    "completed-before");
+		return NULL;
+	}
+
+	if (argv[0]->type != ESEXP_RES_TIME) {
+		e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
+					     "argument to be a time_t"),
+				    "completed-before");
+		return NULL;
+	}
+	before_time = argv[0]->value.time;
+
+	e_cal_component_get_completed (ctx->comp, &tt);
+	if (tt) {
+		/* COMPLETED must be in UTC. */
+		zone = icaltimezone_get_utc_timezone ();
+		completed_time = icaltime_as_timet_with_zone (*tt, zone);
+
+#if 0
+		g_print ("Query Time    : %s", ctime (&before_time));
+		g_print ("Completed Time: %s", ctime (&completed_time));
+#endif
+
+		/* We want to return TRUE if before_time is after
+		   completed_time. */
+		if (difftime (before_time, completed_time) > 0) {
+#if 0
+			g_print ("  Returning TRUE\n");
+#endif
+			retval = TRUE;
+		}
+
+		e_cal_component_free_icaltimetype (tt);
+	}
+
+	result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+	result->value.bool = retval;
+
+	return result;
+}
+
+#if 0
+static struct prop_info {
+	ECardSimpleField field_id;
+	const char *query_prop;
+	const char *ecard_prop;
+#define PROP_TYPE_NORMAL   0x01
+#define PROP_TYPE_LIST     0x02
+#define PROP_TYPE_LISTITEM 0x03
+#define PROP_TYPE_ID 0x04
+	int prop_type;
+	gboolean (*list_compare)(ECardSimple *ecard, const char *str,
+				 char *(*compare)(const char*, const char*));
+
+} prop_info_table[] = {
+#define NORMAL_PROP(f,q,e) {f, q, e, PROP_TYPE_NORMAL, NULL}
+#define ID_PROP {0, "id", NULL, PROP_TYPE_ID, NULL}
+#define LIST_PROP(q,e,c) {0, q, e, PROP_TYPE_LIST, c}
+
+	/* query prop,  ecard prop,   type,              list compare function */
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_FILE_AS, "file_as", "file_as" ),
+	LIST_PROP ( "full_name", "full_name", compare_name), /* not really a list, but we need to compare both full and surname */
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_URL, "url", "url" ),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_MAILER, "mailer", "mailer"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ORG, "org", "org"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ORG_UNIT, "org_unit", "org_unit"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_OFFICE, "office", "office"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_TITLE, "title", "title"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ROLE, "role", "role"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_MANAGER, "manager", "manager"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ASSISTANT, "assistant", "assistant"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_NICKNAME, "nickname", "nickname"),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_SPOUSE, "spouse", "spouse" ),
+	NORMAL_PROP ( E_CARD_SIMPLE_FIELD_NOTE, "note", "note"),
+	ID_PROP,
+	LIST_PROP ( "email", "email", compare_email ),
+	LIST_PROP ( "phone", "phone", compare_phone ),
+	LIST_PROP ( "address", "address", compare_address ),
+	LIST_PROP ( "category", "category", compare_category ),
+	LIST_PROP ( "arbitrary", "arbitrary", compare_arbitrary )
+};
+static int num_prop_infos = sizeof(prop_info_table) / sizeof(prop_info_table[0]);
+
+static ESExpResult *
+entry_compare(SearchContext *ctx, struct _ESExp *f,
+	      int argc, struct _ESExpResult **argv,
+	      char *(*compare)(const char*, const char*))
+{
+	ESExpResult *r;
+	int truth = FALSE;
+
+	if (argc == 2
+	    && argv[0]->type == ESEXP_RES_STRING
+	    && argv[1]->type == ESEXP_RES_STRING) {
+		char *propname;
+		struct prop_info *info = NULL;
+		int i;
+		gboolean any_field;
+
+		propname = argv[0]->value.string;
+
+		any_field = !strcmp(propname, "x-evolution-any-field");
+		for (i = 0; i < num_prop_infos; i ++) {
+			if (any_field
+			    || !strcmp (prop_info_table[i].query_prop, propname)) {
+				info = &prop_info_table[i];
+				
+				if (info->prop_type == PROP_TYPE_NORMAL) {
+					char *prop = NULL;
+					/* searches where the query's property
+					   maps directly to an ecard property */
+					
+					prop = e_card_simple_get (ctx->card, info->field_id);
+
+					if (prop && compare(prop, argv[1]->value.string)) {
+						truth = TRUE;
+					}
+					if ((!prop) && compare("", argv[1]->value.string)) {
+						truth = TRUE;
+					}
+					g_free (prop);
+				} else if (info->prop_type == PROP_TYPE_LIST) {
+				/* the special searches that match any of the list elements */
+					truth = info->list_compare (ctx->card, argv[1]->value.string, compare);
+				} else if (info->prop_type == PROP_TYPE_ID) {
+					const char *prop = NULL;
+					/* searches where the query's property
+					   maps directly to an ecard property */
+					
+					prop = e_card_get_id (ctx->card->card);
+
+					if (prop && compare(prop, argv[1]->value.string)) {
+						truth = TRUE;
+					}
+					if ((!prop) && compare("", argv[1]->value.string)) {
+						truth = TRUE;
+					}
+				}
+
+				/* if we're looking at all fields and find a match,
+				   or if we're just looking at this one field,
+				   break. */
+				if ((any_field && truth)
+				    || !any_field)
+					break;
+			}
+		}
+		
+	}
+	r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+	r->value.bool = truth;
+
+	return r;
+}
+#endif
+
+/* 'builtin' functions */
+static struct {
+	char *name;
+	ESExpFunc *func;
+	int type;		/* set to 1 if a function can perform shortcut evaluation, or
+				   doesn't execute everything, 0 otherwise */
+} symbols[] = {
+	/* Time-related functions */
+	{ "time-now", e_cal_backend_sexp_func_time_now, 0 },
+	{ "make-time", e_cal_backend_sexp_func_make_time, 0 },
+	{ "time-add-day", e_cal_backend_sexp_func_time_add_day, 0 },
+	{ "time-day-begin", e_cal_backend_sexp_func_time_day_begin, 0 },
+	{ "time-day-end", e_cal_backend_sexp_func_time_day_end, 0 },
+
+	/* Component-related functions */
+	{ "uid?", func_uid, 0 },
+	{ "occur-in-time-range?", func_occur_in_time_range, 0 },
+	{ "contains?", func_contains, 0 },
+	{ "has-alarms?", func_has_alarms, 0 },
+	{ "has-alarms-in-range?", func_has_alarms_in_range, 0 },
+	{ "has-recurrences?", func_has_recurrences, 0 },
+	{ "has-categories?", func_has_categories, 0 },
+	{ "is-completed?", func_is_completed, 0 },
+	{ "completed-before?", func_completed_before, 0 }
+};
+
+/**
+ * e_cal_backend_sexp_match_comp:
+ * @sexp: An #ESExp object.
+ * @comp: Component to match against the expression.
+ * @backend: Backend.
+ *
+ * Matches the given ECalComponent against the expression.
+ *
+ * Return value: TRUE if the component matched the expression, FALSE if not.
+ */
+gboolean
+e_cal_backend_sexp_match_comp (ECalBackendSExp *sexp, ECalComponent *comp, ECalBackend *backend)
+{
+	ESExpResult *r;
+	gboolean retval;
+
+	g_return_val_if_fail (E_IS_CAL_BACKEND_SEXP (sexp), FALSE);
+	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+	g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+
+	sexp->priv->search_context->comp = g_object_ref (comp);
+	sexp->priv->search_context->backend = g_object_ref (backend);
+
+	/* if it's not a valid vcard why is it in our db? :) */
+	if (!sexp->priv->search_context->comp)  {
+		g_object_unref (sexp->priv->search_context->backend);
+		return FALSE;
+	}
+	r = e_sexp_eval(sexp->priv->search_sexp);
+
+	retval = (r && r->type == ESEXP_RES_BOOL && r->value.bool);
+
+	g_object_unref (sexp->priv->search_context->comp);
+	g_object_unref (sexp->priv->search_context->backend);
+
+	e_sexp_result_free(sexp->priv->search_sexp, r);
+
+	return retval;
+}
+
+/**
+ * e_cal_backend_sexp_match_object:
+ * @sexp: An #ESExp object.
+ * @object: An iCalendar string.
+ * @backend: A backend.
+ *
+ * Match an iCalendar expression against the expression.
+ *
+ * Return value: TRUE if the object matches the expression, FALSE if not.
+ */
+gboolean
+e_cal_backend_sexp_match_object (ECalBackendSExp *sexp, const char *object, ECalBackend *backend)
+{
+	ECalComponent *comp;
+	icalcomponent *icalcomp;
+	gboolean retval;
+
+	icalcomp = icalcomponent_new_from_string ((char *) object);
+	if (!icalcomp)
+		return FALSE;
+
+	comp = e_cal_component_new ();
+	e_cal_component_set_icalcomponent (comp, icalcomp);
+	
+	retval = e_cal_backend_sexp_match_comp (sexp, comp, backend);
+
+	g_object_unref (comp);
+
+	return retval;
+}
+
+
+
+/**
+ * e_cal_backend_card_sexp_new:
+ * @text: The expression to use.
+ *
+ * Creates a new #EXCalBackendSExp object.
+ *
+ * Return value: The newly created ECalBackendSExp object.
+ */
+ECalBackendSExp *
+e_cal_backend_sexp_new (const char *text)
+{
+	ECalBackendSExp *sexp = g_object_new (E_TYPE_CAL_BACKEND_SEXP, NULL);
+	int esexp_error;
+	int i;
+
+	sexp->priv->search_sexp = e_sexp_new();
+	sexp->priv->text = g_strdup (text);
+
+	for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
+		if (symbols[i].type == 1) {
+			e_sexp_add_ifunction(sexp->priv->search_sexp, 0, symbols[i].name,
+					     (ESExpIFunc *)symbols[i].func, sexp->priv->search_context);
+		} else {
+			e_sexp_add_function(sexp->priv->search_sexp, 0, symbols[i].name,
+					    symbols[i].func, sexp->priv->search_context);
+		}
+	}
+
+	e_sexp_input_text(sexp->priv->search_sexp, text, strlen(text));
+	esexp_error = e_sexp_parse(sexp->priv->search_sexp);
+
+	if (esexp_error == -1) {
+		g_object_unref (sexp);
+		sexp = NULL;
+	}
+
+	return sexp;
+}
+
+/**
+ * e_cal_backend_sexp_text:
+ * @sexp: An #ECalBackendSExp object.
+ *
+ * Retrieve the text expression for the given ECalBackendSExp object.
+ *
+ * Return value: The text expression.
+ */
+const char *
+e_cal_backend_sexp_text (ECalBackendSExp *sexp)
+{
+	ECalBackendSExpPrivate *priv;
+	
+	g_return_val_if_fail (sexp != NULL, NULL);
+	g_return_val_if_fail (E_IS_CAL_BACKEND_SEXP (sexp), NULL);
+
+	priv = sexp->priv;
+
+	return priv->text;
+}
+
+static void
+e_cal_backend_sexp_dispose (GObject *object)
+{
+	ECalBackendSExp *sexp = E_CAL_BACKEND_SEXP (object);
+
+	if (sexp->priv) {
+		e_sexp_unref(sexp->priv->search_sexp);
+
+		g_free (sexp->priv->text);
+
+		g_free (sexp->priv->search_context);
+		g_free (sexp->priv);
+		sexp->priv = NULL;
+	}
+
+	if (G_OBJECT_CLASS (parent_class)->dispose)
+		G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+e_cal_backend_sexp_class_init (ECalBackendSExpClass *klass)
+{
+	GObjectClass  *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* Set the virtual methods. */
+
+	object_class->dispose = e_cal_backend_sexp_dispose;
+}
+
+static void
+e_cal_backend_sexp_init (ECalBackendSExp *sexp)
+{
+	ECalBackendSExpPrivate *priv;
+
+	priv = g_new0 (ECalBackendSExpPrivate, 1);
+
+	sexp->priv = priv;
+	priv->search_context = g_new (SearchContext, 1);
+}
+
+/**
+ * e_cal_backend_sexp_get_type:
+ *
+ * Registers the #ECalBackendSExp class if needed.
+ *
+ * Return value: The unique identifier of the class.
+ */
+GType
+e_cal_backend_sexp_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo info = {
+			sizeof (ECalBackendSExpClass),
+			NULL, /* base_class_init */
+			NULL, /* base_class_finalize */
+			(GClassInitFunc)  e_cal_backend_sexp_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (ECalBackendSExp),
+			0,    /* n_preallocs */
+			(GInstanceInitFunc) e_cal_backend_sexp_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT, "ECalBackendSExp", &info, 0);
+	}
+
+	return type;
+}
Index: calendar/libedata-cal-dbus/e-data-cal-common.h
===================================================================
--- calendar/libedata-cal-dbus/e-data-cal-common.h	(revision 409)
+++ calendar/libedata-cal-dbus/e-data-cal-common.h	(working copy)
@@ -1 +1,47 @@
-link ../libedata-cal/e-data-cal-common.h
\ No newline at end of file
+/* Evolution calendar server - common declarations
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 2000 Ximian, Inc.
+ *
+ * Author: Federico Mena-Quintero <federico@ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CAL_COMMON_H
+#define CAL_COMMON_H
+
+#include <glib/gmacros.h>
+
+G_BEGIN_DECLS
+
+
+
+typedef struct _ECalBackend ECalBackend;
+typedef struct _ECalBackendClass ECalBackendClass;
+
+typedef struct _EDataCal EDataCal;
+typedef struct _EDataCalClass EDataCalClass;
+
+typedef struct _EDataCalView EDataCalView;
+typedef struct _EDataCalViewClass EDataCalViewClass;
+
+typedef struct _ECalBackendSExp ECalBackendSExp;
+typedef struct _ECalBackendSExpClass ECalBackendSExpClass;
+
+
+
+G_END_DECLS
+
+#endif

