[libgdata] Bug 613551 — Add gContact namespace



commit aa23ec1cdcaaa5511a5ba713bebdbe0f101bf180
Author: Philip Withnall <philip tecnocode co uk>
Date:   Thu Mar 25 00:43:09 2010 +0000

    Bug 613551 â?? Add gContact namespace
    
    Add support for various elements from the gContact namespace, and implement
    them in #GDataContactsContact. There is now support for contact:
     * nicknames
     * birthdays
     * jots (notes about a contact)
     * relations (with other people, not necessarily family)
     * websites
     * events (anniversaries, important dates, etc.)
     * calendar links
    Helps: bgo#613551

 Makefile.am                                      |   22 +-
 docs/reference/gdata-docs.xml                    |    9 +
 docs/reference/gdata-sections.txt                |  176 ++++++
 gdata/gcontact/gdata-gcontact-calendar.c         |  490 ++++++++++++++++
 gdata/gcontact/gdata-gcontact-calendar.h         |  119 ++++
 gdata/gcontact/gdata-gcontact-event.c            |  446 +++++++++++++++
 gdata/gcontact/gdata-gcontact-event.h            |  102 ++++
 gdata/gcontact/gdata-gcontact-jot.c              |  308 ++++++++++
 gdata/gcontact/gdata-gcontact-jot.h              |  135 +++++
 gdata/gcontact/gdata-gcontact-relation.c         |  406 +++++++++++++
 gdata/gcontact/gdata-gcontact-relation.h         |  246 ++++++++
 gdata/gcontact/gdata-gcontact-website.c          |  469 +++++++++++++++
 gdata/gcontact/gdata-gcontact-website.h          |  167 ++++++
 gdata/gdata-parser.c                             |   25 +
 gdata/gdata-parser.h                             |    1 +
 gdata/gdata.h                                    |    7 +
 gdata/gdata.symbols                              |   65 +++
 gdata/services/contacts/gdata-contacts-contact.c |  665 ++++++++++++++++++++--
 gdata/services/contacts/gdata-contacts-contact.h |   37 ++-
 gdata/tests/contacts.c                           |  317 ++++++++++-
 gdata/tests/general.c                            |  432 ++++++++++++++
 21 files changed, 4591 insertions(+), 53 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d9ad164..01db9e6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -188,6 +188,14 @@ gdatagdinclude_HEADERS = \
 	gdata/gd/gdata-gd-who.h			\
 	gdata/gd/gdata-gd-name.h
 
+gdatagcontactincludedir = $(gdataincludedir)/gcontact
+gdatagcontactinclude_HEADERS = \
+	gdata/gcontact/gdata-gcontact-calendar.h	\
+	gdata/gcontact/gdata-gcontact-event.h		\
+	gdata/gcontact/gdata-gcontact-jot.h		\
+	gdata/gcontact/gdata-gcontact-relation.h	\
+	gdata/gcontact/gdata-gcontact-website.h
+
 gdatamediaincludedir = $(gdataincludedir)/media
 gdata_media_headers = \
 	gdata/media/gdata-media-category.h	\
@@ -289,6 +297,12 @@ gdata_libgdata_la_SOURCES = \
 	gdata/gd/gdata-gd-who.c			\
 	gdata/gd/gdata-gd-name.c		\
 	\
+	gdata/gcontact/gdata-gcontact-calendar.c	\
+	gdata/gcontact/gdata-gcontact-event.c		\
+	gdata/gcontact/gdata-gcontact-jot.c		\
+	gdata/gcontact/gdata-gcontact-relation.c	\
+	gdata/gcontact/gdata-gcontact-website.c		\
+	\
 	gdata/georss/gdata-georss-where.c	\
 	\
 	gdata/media/gdata-media-category.c	\
@@ -375,10 +389,10 @@ gdata/GData-$(GDATA_API_VERSION).gir: Makefile gdata/libgdata.la
 	--library=gdata/libgdata.la $(_gir_libtool) --output $@				\
 	-I$(top_srcdir) -I$(top_srcdir)/gdata						\
 	$(addprefix $(srcdir)/,$(gdatainclude_HEADERS) $(gdataatominclude_HEADERS)	\
-	$(gdatagdinclude_HEADERS) $(gdatamediainclude_HEADERS)				\
-	$(gdatacalendarinclude_HEADERS) $(gdatacontactsinclude_HEADERS)			\
-	$(gdatadocumentsinclude_HEADERS) $(gdatapicasawebinclude_HEADERS)		\
-	$(gdatayoutubeinclude_HEADERS)))
+	$(gdatagdinclude_HEADERS) $(gdatagcontactinclude_HEADERS)			\
+	$(gdatamediainclude_HEADERS) $(gdatacalendarinclude_HEADERS)			\
+	$(gdatacontactsinclude_HEADERS) $(gdatadocumentsinclude_HEADERS)		\
+	$(gdatapicasawebinclude_HEADERS) $(gdatayoutubeinclude_HEADERS)))
 BUILT_GIRSOURCES = gdata/GData-$(GDATA_API_VERSION).gir
 
 girdir = $(datadir)/gir-1.0
diff --git a/docs/reference/gdata-docs.xml b/docs/reference/gdata-docs.xml
index 023ec4c..8dddbe4 100644
--- a/docs/reference/gdata-docs.xml
+++ b/docs/reference/gdata-docs.xml
@@ -67,6 +67,15 @@
 		</chapter>
 
 		<chapter>
+			<title>GContact API</title>
+			<xi:include href="xml/gdata-gcontact-calendar.xml"/>
+			<xi:include href="xml/gdata-gcontact-event.xml"/>
+			<xi:include href="xml/gdata-gcontact-jot.xml"/>
+			<xi:include href="xml/gdata-gcontact-relation.xml"/>
+			<xi:include href="xml/gdata-gcontact-website.xml"/>
+		</chapter>
+
+		<chapter>
 			<title>Media RSS API</title>
 			<xi:include href="xml/gdata-media-category.xml"/>
 			<xi:include href="xml/gdata-media-content.xml"/>
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 28dbcb3..0feadd0 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -318,6 +318,10 @@ GDataContactsContactClass
 gdata_contacts_contact_new
 gdata_contacts_contact_get_name
 gdata_contacts_contact_set_name
+gdata_contacts_contact_get_nickname
+gdata_contacts_contact_set_nickname
+gdata_contacts_contact_get_birthday
+gdata_contacts_contact_set_birthday
 gdata_contacts_contact_get_email_addresses
 gdata_contacts_contact_get_primary_email_address
 gdata_contacts_contact_add_email_address
@@ -338,6 +342,23 @@ gdata_contacts_contact_get_organizations
 gdata_contacts_contact_get_primary_organization
 gdata_contacts_contact_add_organization
 gdata_contacts_contact_remove_all_organizations
+gdata_contacts_contact_add_jot
+gdata_contacts_contact_get_jots
+gdata_contacts_contact_remove_all_jots
+gdata_contacts_contact_add_relation
+gdata_contacts_contact_get_relations
+gdata_contacts_contact_remove_all_relations
+gdata_contacts_contact_add_website
+gdata_contacts_contact_get_websites
+gdata_contacts_contact_get_primary_website
+gdata_contacts_contact_remove_all_websites
+gdata_contacts_contact_add_event
+gdata_contacts_contact_get_events
+gdata_contacts_contact_remove_all_events
+gdata_contacts_contact_add_calendar
+gdata_contacts_contact_get_calendars
+gdata_contacts_contact_get_primary_calendar
+gdata_contacts_contact_remove_all_calendars
 gdata_contacts_contact_get_groups
 gdata_contacts_contact_add_group
 gdata_contacts_contact_remove_group
@@ -1651,3 +1672,158 @@ GDATA_TYPE_GD_NAME
 <SUBSECTION Private>
 GDataGDNamePrivate
 </SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-jot</FILE>
+<TITLE>GDataGContactJot</TITLE>
+GDATA_GCONTACT_JOT_HOME
+GDATA_GCONTACT_JOT_KEYWORDS
+GDATA_GCONTACT_JOT_OTHER
+GDATA_GCONTACT_JOT_USER
+GDATA_GCONTACT_JOT_WORK
+GDataGContactJot
+GDataGContactJotClass
+gdata_gcontact_jot_new
+gdata_gcontact_jot_get_content
+gdata_gcontact_jot_set_content
+gdata_gcontact_jot_get_relation_type
+gdata_gcontact_jot_set_relation_type
+<SUBSECTION Standard>
+gdata_gcontact_jot_get_type
+GDATA_GCONTACT_JOT
+GDATA_GCONTACT_JOT_CLASS
+GDATA_GCONTACT_JOT_GET_CLASS
+GDATA_IS_GCONTACT_JOT
+GDATA_IS_GCONTACT_JOT_CLASS
+GDATA_TYPE_GCONTACT_JOT
+<SUBSECTION Private>
+GDataGContactJotPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-relation</FILE>
+<TITLE>GDataGContactRelation</TITLE>
+GDATA_GCONTACT_RELATION_ASSISTANT
+GDATA_GCONTACT_RELATION_BROTHER
+GDATA_GCONTACT_RELATION_CHILD
+GDATA_GCONTACT_RELATION_DOMESTIC_PARTNER
+GDATA_GCONTACT_RELATION_FATHER
+GDATA_GCONTACT_RELATION_FRIEND
+GDATA_GCONTACT_RELATION_MANAGER
+GDATA_GCONTACT_RELATION_MOTHER
+GDATA_GCONTACT_RELATION_PARENT
+GDATA_GCONTACT_RELATION_PARTNER
+GDATA_GCONTACT_RELATION_REFERRER
+GDATA_GCONTACT_RELATION_RELATIVE
+GDATA_GCONTACT_RELATION_SISTER
+GDATA_GCONTACT_RELATION_SPOUSE
+GDataGContactRelation
+GDataGContactRelationClass
+gdata_gcontact_relation_new
+gdata_gcontact_relation_get_name
+gdata_gcontact_relation_set_name
+gdata_gcontact_relation_get_relation_type
+gdata_gcontact_relation_set_relation_type
+gdata_gcontact_relation_get_label
+gdata_gcontact_relation_set_label
+<SUBSECTION Standard>
+gdata_gcontact_relation_get_type
+GDATA_GCONTACT_RELATION
+GDATA_GCONTACT_RELATION_CLASS
+GDATA_GCONTACT_RELATION_GET_CLASS
+GDATA_IS_GCONTACT_RELATION
+GDATA_IS_GCONTACT_RELATION_CLASS
+GDATA_TYPE_GCONTACT_RELATION
+<SUBSECTION Private>
+GDataGContactRelationPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-website</FILE>
+<TITLE>GDataGContactWebsite</TITLE>
+GDATA_GCONTACT_WEBSITE_HOME_PAGE
+GDATA_GCONTACT_WEBSITE_BLOG
+GDATA_GCONTACT_WEBSITE_PROFILE
+GDATA_GCONTACT_WEBSITE_HOME
+GDATA_GCONTACT_WEBSITE_WORK
+GDATA_GCONTACT_WEBSITE_OTHER
+GDATA_GCONTACT_WEBSITE_FTP
+GDataGContactWebsite
+GDataGContactWebsiteClass
+gdata_gcontact_website_new
+gdata_gcontact_website_compare
+gdata_gcontact_website_get_uri
+gdata_gcontact_website_set_uri
+gdata_gcontact_website_get_relation_type
+gdata_gcontact_website_set_relation_type
+gdata_gcontact_website_get_label
+gdata_gcontact_website_set_label
+gdata_gcontact_website_is_primary
+gdata_gcontact_website_set_is_primary
+<SUBSECTION Standard>
+gdata_gcontact_website_get_type
+GDATA_GCONTACT_WEBSITE
+GDATA_GCONTACT_WEBSITE_CLASS
+GDATA_GCONTACT_WEBSITE_GET_CLASS
+GDATA_IS_GCONTACT_WEBSITE
+GDATA_IS_GCONTACT_WEBSITE_CLASS
+GDATA_TYPE_GCONTACT_WEBSITE
+<SUBSECTION Private>
+GDataGContactWebsitePrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-event</FILE>
+<TITLE>GDataGContactEvent</TITLE>
+GDATA_GCONTACT_EVENT_ANNIVERSARY
+GDATA_GCONTACT_EVENT_OTHER
+GDataGContactEvent
+GDataGContactEventClass
+gdata_gcontact_event_new
+gdata_gcontact_event_get_date
+gdata_gcontact_event_set_date
+gdata_gcontact_event_get_relation_type
+gdata_gcontact_event_set_relation_type
+gdata_gcontact_event_get_label
+gdata_gcontact_event_set_label
+<SUBSECTION Standard>
+gdata_gcontact_event_get_type
+GDATA_GCONTACT_EVENT
+GDATA_GCONTACT_EVENT_CLASS
+GDATA_GCONTACT_EVENT_GET_CLASS
+GDATA_IS_GCONTACT_EVENT
+GDATA_IS_GCONTACT_EVENT_CLASS
+GDATA_TYPE_GCONTACT_EVENT
+<SUBSECTION Private>
+GDataGContactEventPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-gcontact-calendar</FILE>
+<TITLE>GDataGContactCalendar</TITLE>
+GDATA_GCONTACT_CALENDAR_HOME
+GDATA_GCONTACT_CALENDAR_WORK
+GDATA_GCONTACT_CALENDAR_FREE_BUSY
+GDataGContactCalendar
+GDataGContactCalendarClass
+gdata_gcontact_calendar_new
+gdata_gcontact_calendar_compare
+gdata_gcontact_calendar_get_uri
+gdata_gcontact_calendar_set_uri
+gdata_gcontact_calendar_get_relation_type
+gdata_gcontact_calendar_set_relation_type
+gdata_gcontact_calendar_get_label
+gdata_gcontact_calendar_set_label
+gdata_gcontact_calendar_is_primary
+gdata_gcontact_calendar_set_is_primary
+<SUBSECTION Standard>
+gdata_gcontact_calendar_get_type
+GDATA_GCONTACT_CALENDAR
+GDATA_GCONTACT_CALENDAR_CLASS
+GDATA_GCONTACT_CALENDAR_GET_CLASS
+GDATA_IS_GCONTACT_CALENDAR
+GDATA_IS_GCONTACT_CALENDAR_CLASS
+GDATA_TYPE_GCONTACT_CALENDAR
+<SUBSECTION Private>
+GDataGContactCalendarPrivate
+</SECTION>
diff --git a/gdata/gcontact/gdata-gcontact-calendar.c b/gdata/gcontact/gdata-gcontact-calendar.c
new file mode 100644
index 0000000..4e84c44
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-calendar.c
@@ -0,0 +1,490 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-gcontact-calendar
+ * @short_description: gContact calendar element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-calendar.h
+ *
+ * #GDataGContactCalendar represents a "calendarLink" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-calendar.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_calendar_finalize (GObject *object);
+static void gdata_gcontact_calendar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_calendar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactCalendarPrivate {
+	gchar *uri;
+	gchar *relation_type;
+	gchar *label;
+	gboolean is_primary;
+};
+
+enum {
+	PROP_URI = 1,
+	PROP_RELATION_TYPE,
+	PROP_LABEL,
+	PROP_IS_PRIMARY
+};
+
+G_DEFINE_TYPE (GDataGContactCalendar, gdata_gcontact_calendar, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_calendar_class_init (GDataGContactCalendarClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GDataGContactCalendarPrivate));
+
+	gobject_class->get_property = gdata_gcontact_calendar_get_property;
+	gobject_class->set_property = gdata_gcontact_calendar_set_property;
+	gobject_class->finalize = gdata_gcontact_calendar_finalize;
+
+	parsable_class->pre_parse_xml = pre_parse_xml;
+	parsable_class->pre_get_xml = pre_get_xml;
+	parsable_class->get_namespaces = get_namespaces;
+	parsable_class->element_name = "calendarLink";
+	parsable_class->element_namespace = "gContact";
+
+	/**
+	 * GDataGContactCalendar:uri:
+	 *
+	 * The URI of the calendar.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_URI,
+				g_param_spec_string ("uri",
+					"URI", "The URI of the calendar.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactCalendar:relation-type:
+	 *
+	 * A programmatic value that identifies the type of calendar. It is mutually exclusive with #GDataGContactCalendar:label.
+	 * Examples are %GDATA_GCONTACT_CALENDAR_HOME or %GDATA_GCONTACT_CALENDAR_FREE_BUSY.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+				g_param_spec_string ("relation-type",
+					"Relation type", "A programmatic value that identifies the type of calendar.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactCalendar:label:
+	 *
+	 * A free-form string that identifies the type of calendar. It is mutually exclusive with #GDataGContactCalendar:relation-type.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_LABEL,
+				g_param_spec_string ("label",
+					"Label", "A free-form string that identifies the type of calendar.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactCalendar:is-primary:
+	 *
+	 * Indicates which calendar out of a group is primary.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_IS_PRIMARY,
+				g_param_spec_boolean ("is-primary",
+					"Primary?", "Indicates which calendar out of a group is primary.",
+					FALSE,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_calendar_init (GDataGContactCalendar *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_CALENDAR, GDataGContactCalendarPrivate);
+}
+
+static void
+gdata_gcontact_calendar_finalize (GObject *object)
+{
+	GDataGContactCalendarPrivate *priv = GDATA_GCONTACT_CALENDAR (object)->priv;
+
+	g_free (priv->uri);
+	g_free (priv->relation_type);
+	g_free (priv->label);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (gdata_gcontact_calendar_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_calendar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	GDataGContactCalendarPrivate *priv = GDATA_GCONTACT_CALENDAR (object)->priv;
+
+	switch (property_id) {
+		case PROP_URI:
+			g_value_set_string (value, priv->uri);
+			break;
+		case PROP_RELATION_TYPE:
+			g_value_set_string (value, priv->relation_type);
+			break;
+		case PROP_LABEL:
+			g_value_set_string (value, priv->label);
+			break;
+		case PROP_IS_PRIMARY:
+			g_value_set_boolean (value, priv->is_primary);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static void
+gdata_gcontact_calendar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	GDataGContactCalendar *self = GDATA_GCONTACT_CALENDAR (object);
+
+	switch (property_id) {
+		case PROP_URI:
+			gdata_gcontact_calendar_set_uri (self, g_value_get_string (value));
+			break;
+		case PROP_RELATION_TYPE:
+			gdata_gcontact_calendar_set_relation_type (self, g_value_get_string (value));
+			break;
+		case PROP_LABEL:
+			gdata_gcontact_calendar_set_label (self, g_value_get_string (value));
+			break;
+		case PROP_IS_PRIMARY:
+			gdata_gcontact_calendar_set_is_primary (self, g_value_get_boolean (value));
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+	xmlChar *uri, *rel, *label;
+	gboolean primary_bool;
+	GDataGContactCalendarPrivate *priv = GDATA_GCONTACT_CALENDAR (parsable)->priv;
+
+	/* Is it the primary calendar? */
+	if (gdata_parser_boolean_from_property (root_node, "primary", &primary_bool, 0, error) == FALSE)
+		return FALSE;
+
+	uri = xmlGetProp (root_node, (xmlChar*) "href");
+	if (uri == NULL || *uri == '\0') {
+		xmlFree (uri);
+		return gdata_parser_error_required_property_missing (root_node, "href", error);
+	}
+
+	rel = xmlGetProp (root_node, (xmlChar*) "rel");
+	label = xmlGetProp (root_node, (xmlChar*) "label");
+	if ((rel == NULL || *rel == '\0') && (label == NULL || *label == '\0')) {
+		xmlFree (uri);
+		xmlFree (rel);
+		xmlFree (label);
+		return gdata_parser_error_required_property_missing (root_node, "rel", error);
+	} else if (rel != NULL && label != NULL) {
+		/* Can't have both set at once */
+		xmlFree (uri);
+		xmlFree (rel);
+		xmlFree (label);
+		return gdata_parser_error_mutexed_properties (root_node, "rel", "label", error);
+	}
+
+	priv->uri = (gchar*) uri;
+	priv->relation_type = (gchar*) rel;
+	priv->label = (gchar*) label;
+	priv->is_primary = primary_bool;
+
+	return TRUE;
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+	GDataGContactCalendarPrivate *priv = GDATA_GCONTACT_CALENDAR (parsable)->priv;
+
+	gdata_parser_string_append_escaped (xml_string, " href='", priv->uri, "'");
+
+	if (priv->relation_type != NULL)
+		g_string_append_printf (xml_string, " rel='%s'", priv->relation_type);
+	else if (priv->label != NULL)
+		gdata_parser_string_append_escaped (xml_string, " label='", priv->label, "'");
+	else
+		g_assert_not_reached ();
+
+	if (priv->is_primary == TRUE)
+		g_string_append (xml_string, " primary='true'");
+	else
+		g_string_append (xml_string, " primary='false'");
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+	g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008";);
+}
+
+/**
+ * gdata_gcontact_calendar_new:
+ * @uri: the URI of the calendar
+ * @relation_type: the type of calendar, or %NULL
+ * @label: a free-form label for the calendar, or %NULL
+ * @is_primary: %TRUE if this calendar is its owner's primary calendar, %FALSE otherwise
+ *
+ * Creates a new #GDataGContactCalendar. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>gContact specification</ulink>.
+ *
+ * Exactly one of @relation_type and @label should be provided; the other must be %NULL.
+ *
+ * Return value: a new #GDataGContactCalendar; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactCalendar *
+gdata_gcontact_calendar_new (const gchar *uri, const gchar *relation_type, const gchar *label, gboolean is_primary)
+{
+	g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
+	g_return_val_if_fail ((relation_type != NULL && *relation_type != '\0' && label == NULL) ||
+	                      (relation_type == NULL && label != NULL && *label != '\0'), NULL);
+
+	return g_object_new (GDATA_TYPE_GCONTACT_CALENDAR, "uri", uri, "relation-type", relation_type, "label", label, "is-primary", is_primary, NULL);
+}
+
+/**
+ * gdata_gcontact_calendar_compare:
+ * @a: a #GDataGContactCalendar, or %NULL
+ * @b: another #GDataGContactCalendar, or %NULL
+ *
+ * Compares the two calendars in a strcmp() fashion. %NULL values are handled gracefully, with
+ * %0 returned if both @a and @b are %NULL, %-1 if @a is %NULL and %1 if @b is %NULL.
+ *
+ * The comparison of non-%NULL values is done on the basis of the @uri, @relation_type and @label properties of the #GDataGContactCalendar<!-- -->s.
+ *
+ * Return value: %0 if @a equals @b, %-1 or %1 as appropriate otherwise
+ *
+ * Since: 0.7.0
+ **/
+gint
+gdata_gcontact_calendar_compare (const GDataGContactCalendar *a, const GDataGContactCalendar *b)
+{
+	if (a == NULL && b != NULL)
+		return -1;
+	else if (a != NULL && b == NULL)
+		return 1;
+
+	if (a == b)
+		return 0;
+
+	if (g_strcmp0 (a->priv->uri, b->priv->uri) == 0 &&
+	    g_strcmp0 (a->priv->relation_type, b->priv->relation_type) == 0 &&
+	    g_strcmp0 (a->priv->label, b->priv->label) == 0)
+		return 0;
+	return 1;
+}
+
+/**
+ * gdata_gcontact_calendar_get_uri:
+ * @self: a #GDataGContactCalendar
+ *
+ * Gets the #GDataGContactCalendar:uri property.
+ *
+ * Return value: the calendar's URI
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_calendar_get_uri (GDataGContactCalendar *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_CALENDAR (self), NULL);
+	return self->priv->uri;
+}
+
+/**
+ * gdata_gcontact_calendar_set_uri:
+ * @self: a #GDataGContactCalendar
+ * @uri: the new URI for the calendar
+ *
+ * Sets the #GDataGContactCalendar:uri property to @uri.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_calendar_set_uri (GDataGContactCalendar *self, const gchar *uri)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (self));
+	g_return_if_fail (uri != NULL && *uri != '\0');
+
+	g_free (self->priv->uri);
+	self->priv->uri = g_strdup (uri);
+	g_object_notify (G_OBJECT (self), "uri");
+}
+
+/**
+ * gdata_gcontact_calendar_get_relation_type:
+ * @self: a #GDataGContactCalendar
+ *
+ * Gets the #GDataGContactCalendar:relation-type property.
+ *
+ * Return value: the type of the relation, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_calendar_get_relation_type (GDataGContactCalendar *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_CALENDAR (self), NULL);
+	return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_calendar_set_relation_type:
+ * @self: a #GDataGContactCalendar
+ * @relation_type: the new type for the calendar, or %NULL
+ *
+ * Sets the #GDataGContactCalendar:relation-type property to @relation_type,
+ * such as %GDATA_GCONTACT_CALENDAR_HOME or %GDATA_GCONTACT_CALENDAR_FREE_BUSY.
+ *
+ * If @relation_type is %NULL, the relation type will be unset. When the #GDataGContactCalendar is used in a query, however,
+ * exactly one of #GDataGContactCalendar:relation-type and #GDataGContactCalendar:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_calendar_set_relation_type (GDataGContactCalendar *self, const gchar *relation_type)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (self));
+	g_return_if_fail (relation_type == NULL || *relation_type != '\0');
+
+	g_free (self->priv->relation_type);
+	self->priv->relation_type = g_strdup (relation_type);
+	g_object_notify (G_OBJECT (self), "relation-type");
+}
+
+/**
+ * gdata_gcontact_calendar_get_label:
+ * @self: a #GDataGContactCalendar
+ *
+ * Gets the #GDataGContactCalendar:label property.
+ *
+ * Return value: a free-form label for the calendar, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_calendar_get_label (GDataGContactCalendar *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_CALENDAR (self), NULL);
+	return self->priv->label;
+}
+
+/**
+ * gdata_gcontact_calendar_set_label:
+ * @self: a #GDataGContactCalendar
+ * @label: the new free-form label for the calendar, or %NULL
+ *
+ * Sets the #GDataGContactCalendar:label property to @label.
+ *
+ * If @label is %NULL, the label will be unset. When the #GDataGContactCalendar is used in a query, however,
+ * exactly one of #GDataGContactCalendar:relation-type and #GDataGContactCalendar:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_calendar_set_label (GDataGContactCalendar *self, const gchar *label)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (self));
+	g_return_if_fail (label == NULL || *label != '\0');
+
+	g_free (self->priv->label);
+	self->priv->label = g_strdup (label);
+	g_object_notify (G_OBJECT (self), "label");
+}
+
+/**
+ * gdata_gcontact_calendar_is_primary:
+ * @self: a #GDataGContactCalendar
+ *
+ * Gets the #GDataGContactCalendar:is-primary property.
+ *
+ * Return value: %TRUE if this is the contact's primary calendar, %FALSE otherwise
+ *
+ * Since: 0.7.0
+ **/
+gboolean
+gdata_gcontact_calendar_is_primary (GDataGContactCalendar *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_CALENDAR (self), FALSE);
+	return self->priv->is_primary;
+}
+
+/**
+ * gdata_gcontact_calendar_set_is_primary:
+ * @self: a #GDataGContactCalendar
+ * @is_primary: %TRUE if this is the contact's primary calendar, %FALSE otherwise
+ *
+ * Sets the #GDataGContactCalendar:is-primary property to @is_primary.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_calendar_set_is_primary (GDataGContactCalendar *self, gboolean is_primary)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (self));
+
+	self->priv->is_primary = is_primary;
+	g_object_notify (G_OBJECT (self), "is-primary");
+}
diff --git a/gdata/gcontact/gdata-gcontact-calendar.h b/gdata/gcontact/gdata-gcontact-calendar.h
new file mode 100644
index 0000000..cfc6427
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-calendar.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_GCONTACT_CALENDAR_H
+#define GDATA_GCONTACT_CALENDAR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_CALENDAR_WORK:
+ *
+ * The relation type URI for a contact's work calendar.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_CALENDAR_WORK "work"
+
+/**
+ * GDATA_GCONTACT_CALENDAR_HOME:
+ *
+ * The relation type URI for a contact's home calendar.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_CALENDAR_HOME "home"
+
+/**
+ * GDATA_GCONTACT_CALENDAR_FREE_BUSY:
+ *
+ * The relation type URI for a contact's free/busy calendar.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcCalendarLink";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_CALENDAR_FREE_BUSY "free-busy"
+
+#define GDATA_TYPE_GCONTACT_CALENDAR		(gdata_gcontact_calendar_get_type ())
+#define GDATA_GCONTACT_CALENDAR(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_CALENDAR, GDataGContactCalendar))
+#define GDATA_GCONTACT_CALENDAR_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_CALENDAR, GDataGContactCalendarClass))
+#define GDATA_IS_GCONTACT_CALENDAR(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_CALENDAR))
+#define GDATA_IS_GCONTACT_CALENDAR_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_CALENDAR))
+#define GDATA_GCONTACT_CALENDAR_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_CALENDAR, GDataGContactCalendarClass))
+
+typedef struct _GDataGContactCalendarPrivate	GDataGContactCalendarPrivate;
+
+/**
+ * GDataGContactCalendar:
+ *
+ * All the fields in the #GDataGContactCalendar structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	GDataParsable parent;
+	GDataGContactCalendarPrivate *priv;
+} GDataGContactCalendar;
+
+/**
+ * GDataGContactCalendarClass:
+ *
+ * All the fields in the #GDataGContactCalendarClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	/*< private >*/
+	GDataParsableClass parent;
+} GDataGContactCalendarClass;
+
+GType gdata_gcontact_calendar_get_type (void) G_GNUC_CONST;
+
+GDataGContactCalendar *gdata_gcontact_calendar_new (const gchar *uri, const gchar *relation_type,
+                                                    const gchar *label, gboolean is_primary) G_GNUC_WARN_UNUSED_RESULT;
+gint gdata_gcontact_calendar_compare (const GDataGContactCalendar *a, const GDataGContactCalendar *b);
+
+const gchar *gdata_gcontact_calendar_get_uri (GDataGContactCalendar *self);
+void gdata_gcontact_calendar_set_uri (GDataGContactCalendar *self, const gchar *uri);
+
+const gchar *gdata_gcontact_calendar_get_relation_type (GDataGContactCalendar *self);
+void gdata_gcontact_calendar_set_relation_type (GDataGContactCalendar *self, const gchar *relation_type);
+
+const gchar *gdata_gcontact_calendar_get_label (GDataGContactCalendar *self);
+void gdata_gcontact_calendar_set_label (GDataGContactCalendar *self, const gchar *label);
+
+gboolean gdata_gcontact_calendar_is_primary (GDataGContactCalendar *self);
+void gdata_gcontact_calendar_set_is_primary (GDataGContactCalendar *self, gboolean is_primary);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_CALENDAR_H */
diff --git a/gdata/gcontact/gdata-gcontact-event.c b/gdata/gcontact/gdata-gcontact-event.c
new file mode 100644
index 0000000..0d9ae02
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-event.c
@@ -0,0 +1,446 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-gcontact-event
+ * @short_description: gContact event element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-event.h
+ *
+ * #GDataGContactEvent represents a "event" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent";>gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <string.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-event.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_event_finalize (GObject *object);
+static void gdata_gcontact_event_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_event_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static gboolean post_parse_xml (GDataParsable *parsable, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactEventPrivate {
+	GDate date;
+	gchar *relation_type;
+	gchar *label;
+};
+
+enum {
+	PROP_DATE = 1,
+	PROP_RELATION_TYPE,
+	PROP_LABEL
+};
+
+G_DEFINE_TYPE (GDataGContactEvent, gdata_gcontact_event, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_event_class_init (GDataGContactEventClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GDataGContactEventPrivate));
+
+	gobject_class->get_property = gdata_gcontact_event_get_property;
+	gobject_class->set_property = gdata_gcontact_event_set_property;
+	gobject_class->finalize = gdata_gcontact_event_finalize;
+
+	parsable_class->pre_parse_xml = pre_parse_xml;
+	parsable_class->parse_xml = parse_xml;
+	parsable_class->post_parse_xml = post_parse_xml;
+	parsable_class->pre_get_xml = pre_get_xml;
+	parsable_class->get_xml = get_xml;
+	parsable_class->get_namespaces = get_namespaces;
+	parsable_class->element_name = "event";
+	parsable_class->element_namespace = "gContact";
+
+	/**
+	 * GDataGContactEvent:date:
+	 *
+	 * The date of the event.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent";>GContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_DATE,
+				g_param_spec_boxed ("date",
+					"Date", "The date of the event.",
+					G_TYPE_DATE,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactEvent:relation-type:
+	 *
+	 * A programmatic value that identifies the type of event. It is mutually exclusive with #GDataGContactEvent:label.
+	 * Examples are %GDATA_GCONTACT_EVENT_ANNIVERSARY or %GDATA_GCONTACT_EVENT_OTHER.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+				g_param_spec_string ("relation-type",
+					"Relation type", "A programmatic value that identifies the type of website.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactEvent:label:
+	 *
+	 * A simple string value used to name this event. It is mutually exclusive with #GDataGContactEvent:relation-type.
+	 * It allows UIs to display a label such as "Wedding anniversary".
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_LABEL,
+				g_param_spec_string ("label",
+					"Label", "A simple string value used to name this event.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_event_init (GDataGContactEvent *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_EVENT, GDataGContactEventPrivate);
+
+	/* Clear the date to an invalid but sane value */
+	g_date_clear (&(self->priv->date), 1);
+}
+
+static void
+gdata_gcontact_event_finalize (GObject *object)
+{
+	GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (object)->priv;
+
+	g_free (priv->relation_type);
+	g_free (priv->label);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (gdata_gcontact_event_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_event_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (object)->priv;
+
+	switch (property_id) {
+		case PROP_DATE:
+			g_value_set_boxed (value, &(priv->date));
+			break;
+		case PROP_RELATION_TYPE:
+			g_value_set_string (value, priv->relation_type);
+			break;
+		case PROP_LABEL:
+			g_value_set_string (value, priv->label);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static void
+gdata_gcontact_event_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	GDataGContactEvent *self = GDATA_GCONTACT_EVENT (object);
+
+	switch (property_id) {
+		case PROP_DATE:
+			gdata_gcontact_event_set_date (self, g_value_get_boxed (value));
+			break;
+		case PROP_RELATION_TYPE:
+			gdata_gcontact_event_set_relation_type (self, g_value_get_string (value));
+			break;
+		case PROP_LABEL:
+			gdata_gcontact_event_set_label (self, g_value_get_string (value));
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+	xmlChar *rel, *label;
+	GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+	rel = xmlGetProp (root_node, (xmlChar*) "rel");
+	label = xmlGetProp (root_node, (xmlChar*) "label");
+	if ((rel == NULL || *rel == '\0') && (label == NULL || *label == '\0')) {
+		xmlFree (rel);
+		xmlFree (label);
+		return gdata_parser_error_required_property_missing (root_node, "rel", error);
+	} else if (rel != NULL && label != NULL) {
+		/* Can't have both set at once */
+		xmlFree (rel);
+		xmlFree (label);
+		return gdata_parser_error_mutexed_properties (root_node, "rel", "label", error);
+	}
+
+	priv->relation_type = (gchar*) rel;
+	priv->label = (gchar*) label;
+
+	return TRUE;
+}
+
+static gboolean
+parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
+{
+	GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+	if (gdata_parser_is_namespace (node, "http://schemas.google.com/g/2005";) == TRUE && xmlStrcmp (node->name, (xmlChar*) "when") == 0) {
+		xmlChar *start_time;
+		guint year, month, day;
+
+		/* gd:when; note we don't use GDataGDWhen here because gContact:event only uses a limited subset
+		 * of gd:when (i.e. only the startTime property in date format) */
+		if (g_date_valid (&(priv->date)) == TRUE)
+			return gdata_parser_error_duplicate_element (node, error);
+
+		start_time = xmlGetProp (node, (xmlChar*) "startTime");
+		if (start_time == NULL)
+			return gdata_parser_error_required_property_missing (node, "startTime", error);
+
+		/* Try parsing the date format: YYYY-MM-DD */
+		if (strlen ((char*) start_time) == 10 &&
+		    sscanf ((char*) start_time, "%4u-%2u-%2u", &year, &month, &day) == 3 && g_date_valid_dmy (day, month, year) == TRUE) {
+			/* Store the values in the GDate */
+			g_date_set_dmy (&(priv->date), day, month, year);
+			xmlFree (start_time);
+		} else {
+			/* Parsing failed */
+			gdata_parser_error_not_iso8601_format (node, (gchar*) start_time, error);
+			xmlFree (start_time);
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	return GDATA_PARSABLE_CLASS (gdata_gcontact_event_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+}
+
+static gboolean
+post_parse_xml (GDataParsable *parsable, gpointer user_data, GError **error)
+{
+	GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+	/* Check for missing required elements */
+	if (g_date_valid (&(priv->date)) == FALSE)
+		return gdata_parser_error_required_element_missing ("gd:when", "gContact:event", error);
+
+	return TRUE;
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+	GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+	if (priv->relation_type != NULL)
+		g_string_append_printf (xml_string, " rel='%s'", priv->relation_type);
+	else if (priv->label != NULL)
+		gdata_parser_string_append_escaped (xml_string, " label='", priv->label, "'");
+	else
+		g_assert_not_reached ();
+}
+
+static void
+get_xml (GDataParsable *parsable, GString *xml_string)
+{
+	GDataGContactEventPrivate *priv = GDATA_GCONTACT_EVENT (parsable)->priv;
+
+	g_string_append_printf (xml_string, "<gd:when startTime='%04u-%02u-%02u'/>",
+	                        g_date_get_year (&(priv->date)),
+	                        g_date_get_month (&(priv->date)),
+	                        g_date_get_day (&(priv->date)));
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+	g_hash_table_insert (namespaces, (gchar*) "gd", (gchar*) "http://schemas.google.com/g/2005";);
+	g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008";);
+}
+
+/**
+ * gdata_gcontact_event_new:
+ * @date: the date of the event
+ * @relation_type: the relationship between the event and its owner, or %NULL
+ * @label: a human-readable label for the event, or %NULL
+ *
+ * Creates a new #GDataGContactEvent. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent";>gContact specification</ulink>.
+ *
+ * Exactly one of @relation_type and @label should be provided; the other must be %NULL.
+ *
+ * Return value: a new #GDataGContactEvent; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactEvent *
+gdata_gcontact_event_new (const GDate *date, const gchar *relation_type, const gchar *label)
+{
+	g_return_val_if_fail (date != NULL && g_date_valid (date) == TRUE, NULL);
+	g_return_val_if_fail ((relation_type != NULL && *relation_type != '\0' && label == NULL) ||
+	                      (relation_type == NULL && label != NULL && *label != '\0'), NULL);
+
+	return g_object_new (GDATA_TYPE_GCONTACT_EVENT, "date", date, "relation-type", relation_type, "label", label, NULL);
+}
+
+/**
+ * gdata_gcontact_event_get_date:
+ * @self: a #GDataGContactEvent
+ * @date: return location for the date of the event
+ *
+ * Gets the #GDataGContactEvent:date property.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_event_get_date (GDataGContactEvent *self, GDate *date)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_EVENT (self));
+	g_return_if_fail (date != NULL);
+	*date = self->priv->date;
+}
+
+/**
+ * gdata_gcontact_event_set_date:
+ * @self: a #GDataGContactEvent
+ * @date: the new date for the event
+ *
+ * Sets the #GDataGContactEvent:date property to @date.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_event_set_date (GDataGContactEvent *self, const GDate *date)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_EVENT (self));
+	g_return_if_fail (date != NULL && g_date_valid (date) == TRUE);
+
+	self->priv->date = *date;
+	g_object_notify (G_OBJECT (self), "date");
+}
+
+/**
+ * gdata_gcontact_event_get_relation_type:
+ * @self: a #GDataGContactEvent
+ *
+ * Gets the #GDataGContactEvent:relation-type property.
+ *
+ * Return value: the event's relation type, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_event_get_relation_type (GDataGContactEvent *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_EVENT (self), NULL);
+	return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_event_set_relation_type:
+ * @self: a #GDataGContactEvent
+ * @relation_type: the new relation type for the event, or %NULL
+ *
+ * Sets the #GDataGContactEvent:relation-type property to @relation_type
+ * such as %GDATA_GCONTACT_EVENT_ANNIVERSARY or %GDATA_GCONTACT_EVENT_OTHER.
+ *
+ * If @relation_type is %NULL, the relation type will be unset. When the #GDataGContactEvent is used in a query, however,
+ * exactly one of #GDataGContactEvent:relation-type and #GDataGContactEvent:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_event_set_relation_type (GDataGContactEvent *self, const gchar *relation_type)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_EVENT (self));
+	g_return_if_fail (relation_type == NULL || *relation_type != '\0');
+
+	g_free (self->priv->relation_type);
+	self->priv->relation_type = g_strdup (relation_type);
+	g_object_notify (G_OBJECT (self), "relation-type");
+}
+
+/**
+ * gdata_gcontact_event_get_label:
+ * @self: a #GDataGContactEvent
+ *
+ * Gets the #GDataGContactEvent:label property.
+ *
+ * Return value: the event's label, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_event_get_label (GDataGContactEvent *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_EVENT (self), NULL);
+	return self->priv->label;
+}
+
+/**
+ * gdata_gcontact_event_set_label:
+ * @self: a #GDataGContactEvent
+ * @label: the new label for the event, or %NULL
+ *
+ * Sets the #GDataGContactEvent:label property to @label.
+ *
+ * If @label is %NULL, the label will be unset. When the #GDataGContactEvent is used in a query, however,
+ * exactly one of #GDataGContactEvent:relation-type and #GDataGContactEvent:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_event_set_label (GDataGContactEvent *self, const gchar *label)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_EVENT (self));
+	g_return_if_fail (label == NULL || *label != '\0');
+
+	g_free (self->priv->label);
+	self->priv->label = g_strdup (label);
+	g_object_notify (G_OBJECT (self), "label");
+}
diff --git a/gdata/gcontact/gdata-gcontact-event.h b/gdata/gcontact/gdata-gcontact-event.h
new file mode 100644
index 0000000..6a9caf0
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-event.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_GCONTACT_EVENT_H
+#define GDATA_GCONTACT_EVENT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_EVENT_ANNIVERSARY:
+ *
+ * The relation type URI for an anniversary event.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_EVENT_ANNIVERSARY "anniversary"
+
+/**
+ * GDATA_GCONTACT_EVENT_OTHER:
+ *
+ * The relation type URI for a miscellaneous event.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcEvent";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_EVENT_OTHER "other"
+
+#define GDATA_TYPE_GCONTACT_EVENT		(gdata_gcontact_event_get_type ())
+#define GDATA_GCONTACT_EVENT(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_EVENT, GDataGContactEvent))
+#define GDATA_GCONTACT_EVENT_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_EVENT, GDataGContactEventClass))
+#define GDATA_IS_GCONTACT_EVENT(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_EVENT))
+#define GDATA_IS_GCONTACT_EVENT_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_EVENT))
+#define GDATA_GCONTACT_EVENT_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_EVENT, GDataGContactEventClass))
+
+typedef struct _GDataGContactEventPrivate	GDataGContactEventPrivate;
+
+/**
+ * GDataGContactEvent:
+ *
+ * All the fields in the #GDataGContactEvent structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	GDataParsable parent;
+	GDataGContactEventPrivate *priv;
+} GDataGContactEvent;
+
+/**
+ * GDataGContactEventClass:
+ *
+ * All the fields in the #GDataGContactEventClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	/*< private >*/
+	GDataParsableClass parent;
+} GDataGContactEventClass;
+
+GType gdata_gcontact_event_get_type (void) G_GNUC_CONST;
+
+GDataGContactEvent *gdata_gcontact_event_new (const GDate *date, const gchar *relation_type, const gchar *label) G_GNUC_WARN_UNUSED_RESULT;
+
+void gdata_gcontact_event_get_date (GDataGContactEvent *self, GDate *date);
+void gdata_gcontact_event_set_date (GDataGContactEvent *self, const GDate *date);
+
+const gchar *gdata_gcontact_event_get_relation_type (GDataGContactEvent *self);
+void gdata_gcontact_event_set_relation_type (GDataGContactEvent *self, const gchar *relation_type);
+
+const gchar *gdata_gcontact_event_get_label (GDataGContactEvent *self);
+void gdata_gcontact_event_set_label (GDataGContactEvent *self, const gchar *label);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_EVENT_H */
diff --git a/gdata/gcontact/gdata-gcontact-jot.c b/gdata/gcontact/gdata-gcontact-jot.c
new file mode 100644
index 0000000..075856f
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-jot.c
@@ -0,0 +1,308 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-gcontact-jot
+ * @short_description: gContact jot element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-jot.h
+ *
+ * #GDataGContactJot represents a "jot" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-jot.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_jot_finalize (GObject *object);
+static void gdata_gcontact_jot_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_jot_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactJotPrivate {
+	gchar *content;
+	gchar *relation_type;
+};
+
+enum {
+	PROP_CONTENT = 1,
+	PROP_RELATION_TYPE
+};
+
+G_DEFINE_TYPE (GDataGContactJot, gdata_gcontact_jot, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_jot_class_init (GDataGContactJotClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GDataGContactJotPrivate));
+
+	gobject_class->get_property = gdata_gcontact_jot_get_property;
+	gobject_class->set_property = gdata_gcontact_jot_set_property;
+	gobject_class->finalize = gdata_gcontact_jot_finalize;
+
+	parsable_class->pre_parse_xml = pre_parse_xml;
+	parsable_class->parse_xml = parse_xml;
+	parsable_class->pre_get_xml = pre_get_xml;
+	parsable_class->get_xml = get_xml;
+	parsable_class->get_namespaces = get_namespaces;
+	parsable_class->element_name = "jot";
+	parsable_class->element_namespace = "gContact";
+
+	/**
+	 * GDataGContactJot:content:
+	 *
+	 * The content of the jot.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_CONTENT,
+				g_param_spec_string ("content",
+					"Content", "The content of the jot.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactJot:relation-type:
+	 *
+	 * A programmatic value that identifies the type of jot. Examples are %GDATA_GCONTACT_JOT_HOME or %GDATA_GCONTACT_JOT_OTHER.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+				g_param_spec_string ("relation-type",
+					"Relation type", "A programmatic value that identifies the type of jot.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_jot_init (GDataGContactJot *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_JOT, GDataGContactJotPrivate);
+}
+
+static void
+gdata_gcontact_jot_finalize (GObject *object)
+{
+	GDataGContactJotPrivate *priv = GDATA_GCONTACT_JOT (object)->priv;
+
+	g_free (priv->content);
+	g_free (priv->relation_type);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (gdata_gcontact_jot_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_jot_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	GDataGContactJotPrivate *priv = GDATA_GCONTACT_JOT (object)->priv;
+
+	switch (property_id) {
+		case PROP_CONTENT:
+			g_value_set_string (value, priv->content);
+			break;
+		case PROP_RELATION_TYPE:
+			g_value_set_string (value, priv->relation_type);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static void
+gdata_gcontact_jot_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	GDataGContactJot *self = GDATA_GCONTACT_JOT (object);
+
+	switch (property_id) {
+		case PROP_CONTENT:
+			gdata_gcontact_jot_set_content (self, g_value_get_string (value));
+			break;
+		case PROP_RELATION_TYPE:
+			gdata_gcontact_jot_set_relation_type (self, g_value_get_string (value));
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+	xmlChar *rel;
+	GDataGContactJotPrivate *priv = GDATA_GCONTACT_JOT (parsable)->priv;
+
+	rel = xmlGetProp (root_node, (xmlChar*) "rel");
+	if (rel == NULL || *rel == '\0') {
+		xmlFree (rel);
+		return gdata_parser_error_required_property_missing (root_node, "rel", error);
+	}
+
+	priv->content = (gchar*) xmlNodeListGetString (doc, root_node->children, TRUE);
+	priv->relation_type = (gchar*) rel;
+
+	return TRUE;
+}
+
+static gboolean
+parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
+{
+	/* Textual content's handled in pre_parse_xml */
+	if (node->type != XML_ELEMENT_NODE)
+		return TRUE;
+
+	return GDATA_PARSABLE_CLASS (gdata_gcontact_jot_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+	g_string_append_printf (xml_string, " rel='%s'", GDATA_GCONTACT_JOT (parsable)->priv->relation_type);
+}
+
+static void
+get_xml (GDataParsable *parsable, GString *xml_string)
+{
+	gdata_parser_string_append_escaped (xml_string, NULL, GDATA_GCONTACT_JOT (parsable)->priv->content, NULL);
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+	g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008";);
+}
+
+/**
+ * gdata_gcontact_jot_new:
+ * @content: the content of the jot
+ * @relation_type: the relationship between the jot and its owner
+ *
+ * Creates a new #GDataGContactJot. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>gContact specification</ulink>.
+ *
+ * Return value: a new #GDataGContactJot; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactJot *
+gdata_gcontact_jot_new (const gchar *content, const gchar *relation_type)
+{
+	g_return_val_if_fail (content != NULL, NULL);
+	g_return_val_if_fail (relation_type != NULL && *relation_type != '\0', NULL);
+	return g_object_new (GDATA_TYPE_GCONTACT_JOT, "content", content, "relation-type", relation_type, NULL);
+}
+
+/**
+ * gdata_gcontact_jot_get_content:
+ * @self: a #GDataGContactJot
+ *
+ * Gets the #GDataGContactJot:content property.
+ *
+ * Return value: the jot's content
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_jot_get_content (GDataGContactJot *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_JOT (self), NULL);
+	return self->priv->content;
+}
+
+/**
+ * gdata_gcontact_jot_set_content:
+ * @self: a #GDataGContactJot
+ * @content: the new content
+ *
+ * Sets the #GDataGContactJot:content property to @content.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_jot_set_content (GDataGContactJot *self, const gchar *content)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_JOT (self));
+	g_return_if_fail (content != NULL);
+
+	g_free (self->priv->content);
+	self->priv->content = g_strdup (content);
+	g_object_notify (G_OBJECT (self), "content");
+}
+
+/**
+ * gdata_gcontact_jot_get_relation_type:
+ * @self: a #GDataGContactJot
+ *
+ * Gets the #GDataGContactJot:relation-type property.
+ *
+ * Return value: the jot's relation type
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_jot_get_relation_type (GDataGContactJot *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_JOT (self), NULL);
+	return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_jot_set_relation_type:
+ * @self: a #GDataGContactJot
+ * @relation_type: the new relation type for the jot, or %NULL
+ *
+ * Sets the #GDataGContactJot:relation-type property to @relation_type
+ * such as %GDATA_GCONTACT_JOT_HOME or %GDATA_GCONTACT_JOT_OTHER.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_jot_set_relation_type (GDataGContactJot *self, const gchar *relation_type)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_JOT (self));
+	g_return_if_fail (relation_type != NULL && *relation_type != '\0');
+
+	g_free (self->priv->relation_type);
+	self->priv->relation_type = g_strdup (relation_type);
+	g_object_notify (G_OBJECT (self), "relation-type");
+}
diff --git a/gdata/gcontact/gdata-gcontact-jot.h b/gdata/gcontact/gdata-gcontact-jot.h
new file mode 100644
index 0000000..b4e4217
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-jot.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_GCONTACT_JOT_H
+#define GDATA_GCONTACT_JOT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_JOT_HOME:
+ *
+ * The relation type URI for a jot about a contact's home.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_HOME "home"
+
+/**
+ * GDATA_GCONTACT_JOT_WORK:
+ *
+ * The relation type URI for a jot about a contact's work.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_WORK "work"
+
+/**
+ * GDATA_GCONTACT_JOT_OTHER:
+ *
+ * The relation type URI for a jot about an other facet of a contact.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_OTHER "other"
+
+/**
+ * GDATA_GCONTACT_JOT_KEYWORDS:
+ *
+ * The relation type URI for a jot with keywords about a contact.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_KEYWORDS "keywords"
+
+/**
+ * GDATA_GCONTACT_JOT_USER:
+ *
+ * The relation type URI for a jot about the relationship between a contact and the user.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_JOT_USER "user"
+
+#define GDATA_TYPE_GCONTACT_JOT		(gdata_gcontact_jot_get_type ())
+#define GDATA_GCONTACT_JOT(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_JOT, GDataGContactJot))
+#define GDATA_GCONTACT_JOT_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_JOT, GDataGContactJotClass))
+#define GDATA_IS_GCONTACT_JOT(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_JOT))
+#define GDATA_IS_GCONTACT_JOT_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_JOT))
+#define GDATA_GCONTACT_JOT_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_JOT, GDataGContactJotClass))
+
+typedef struct _GDataGContactJotPrivate	GDataGContactJotPrivate;
+
+/**
+ * GDataGContactJot:
+ *
+ * All the fields in the #GDataGContactJot structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	GDataParsable parent;
+	GDataGContactJotPrivate *priv;
+} GDataGContactJot;
+
+/**
+ * GDataGContactJotClass:
+ *
+ * All the fields in the #GDataGContactJotClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	/*< private >*/
+	GDataParsableClass parent;
+} GDataGContactJotClass;
+
+GType gdata_gcontact_jot_get_type (void) G_GNUC_CONST;
+
+GDataGContactJot *gdata_gcontact_jot_new (const gchar *content, const gchar *relation_type) G_GNUC_WARN_UNUSED_RESULT;
+
+const gchar *gdata_gcontact_jot_get_content (GDataGContactJot *self);
+void gdata_gcontact_jot_set_content (GDataGContactJot *self, const gchar *content);
+
+const gchar *gdata_gcontact_jot_get_relation_type (GDataGContactJot *self);
+void gdata_gcontact_jot_set_relation_type (GDataGContactJot *self, const gchar *relation_type);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_JOT_H */
diff --git a/gdata/gcontact/gdata-gcontact-relation.c b/gdata/gcontact/gdata-gcontact-relation.c
new file mode 100644
index 0000000..024e63f
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-relation.c
@@ -0,0 +1,406 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-gcontact-relation
+ * @short_description: gContact relation element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-relation.h
+ *
+ * #GDataGContactRelation represents a "relation" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcJot";>gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-relation.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_relation_finalize (GObject *object);
+static void gdata_gcontact_relation_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_relation_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactRelationPrivate {
+	gchar *name;
+	gchar *relation_type;
+	gchar *label;
+};
+
+enum {
+	PROP_NAME = 1,
+	PROP_RELATION_TYPE,
+	PROP_LABEL
+};
+
+G_DEFINE_TYPE (GDataGContactRelation, gdata_gcontact_relation, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_relation_class_init (GDataGContactRelationClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GDataGContactRelationPrivate));
+
+	gobject_class->get_property = gdata_gcontact_relation_get_property;
+	gobject_class->set_property = gdata_gcontact_relation_set_property;
+	gobject_class->finalize = gdata_gcontact_relation_finalize;
+
+	parsable_class->pre_parse_xml = pre_parse_xml;
+	parsable_class->parse_xml = parse_xml;
+	parsable_class->pre_get_xml = pre_get_xml;
+	parsable_class->get_xml = get_xml;
+	parsable_class->get_namespaces = get_namespaces;
+	parsable_class->element_name = "relation";
+	parsable_class->element_namespace = "gContact";
+
+	/**
+	 * GDataGContactRelation:name:
+	 *
+	 * The name of the relation. It need not be a full name, and there need not be a contact representing the name.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_NAME,
+				g_param_spec_string ("name",
+					"Name", "The name of the relation.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactRelation:relation-type:
+	 *
+	 * A programmatic value that identifies the type of relation. It is mutually exclusive with #GDataGContactRelation:label.
+	 * Examples are %GDATA_GCONTACT_RELATION_MANAGER or %GDATA_GCONTACT_RELATION_CHILD.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+				g_param_spec_string ("relation-type",
+					"Relation type", "A programmatic value that identifies the type of relation.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactRelation:label:
+	 *
+	 * A free-form string that identifies the type of relation. It is mutually exclusive with #GDataGContactRelation:relation-type.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_LABEL,
+				g_param_spec_string ("label",
+					"Label", "A free-form string that identifies the type of relation.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_relation_init (GDataGContactRelation *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_RELATION, GDataGContactRelationPrivate);
+}
+
+static void
+gdata_gcontact_relation_finalize (GObject *object)
+{
+	GDataGContactRelationPrivate *priv = GDATA_GCONTACT_RELATION (object)->priv;
+
+	g_free (priv->name);
+	g_free (priv->relation_type);
+	g_free (priv->label);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (gdata_gcontact_relation_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_relation_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	GDataGContactRelationPrivate *priv = GDATA_GCONTACT_RELATION (object)->priv;
+
+	switch (property_id) {
+		case PROP_NAME:
+			g_value_set_string (value, priv->name);
+			break;
+		case PROP_RELATION_TYPE:
+			g_value_set_string (value, priv->relation_type);
+			break;
+		case PROP_LABEL:
+			g_value_set_string (value, priv->label);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static void
+gdata_gcontact_relation_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	GDataGContactRelation *self = GDATA_GCONTACT_RELATION (object);
+
+	switch (property_id) {
+		case PROP_NAME:
+			gdata_gcontact_relation_set_name (self, g_value_get_string (value));
+			break;
+		case PROP_RELATION_TYPE:
+			gdata_gcontact_relation_set_relation_type (self, g_value_get_string (value));
+			break;
+		case PROP_LABEL:
+			gdata_gcontact_relation_set_label (self, g_value_get_string (value));
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+	xmlChar *rel, *label, *name;
+	GDataGContactRelationPrivate *priv = GDATA_GCONTACT_RELATION (parsable)->priv;
+
+	rel = xmlGetProp (root_node, (xmlChar*) "rel");
+	label = xmlGetProp (root_node, (xmlChar*) "label");
+	if ((rel == NULL || *rel == '\0') && (label == NULL || *label == '\0')) {
+		xmlFree (rel);
+		xmlFree (label);
+		return gdata_parser_error_required_property_missing (root_node, "rel", error);
+	} else if (rel != NULL && label != NULL) {
+		/* Can't have both set at once */
+		xmlFree (rel);
+		xmlFree (label);
+		return gdata_parser_error_mutexed_properties (root_node, "rel", "label", error);
+	}
+
+	/* Get the name */
+	name = xmlNodeListGetString (doc, root_node->children, TRUE);
+	if (name == NULL || *name == '\0') {
+		xmlFree (rel);
+		xmlFree (label);
+		xmlFree (name);
+		return gdata_parser_error_required_content_missing (root_node, error);
+	}
+
+	priv->name = (gchar*) name;
+	priv->relation_type = (gchar*) rel;
+	priv->label = (gchar*) label;
+
+	return TRUE;
+}
+
+static gboolean
+parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
+{
+	/* Textual content's handled in pre_parse_xml */
+	if (node->type != XML_ELEMENT_NODE)
+		return TRUE;
+
+	return GDATA_PARSABLE_CLASS (gdata_gcontact_relation_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+	GDataGContactRelationPrivate *priv = GDATA_GCONTACT_RELATION (parsable)->priv;
+
+	if (priv->relation_type != NULL)
+		g_string_append_printf (xml_string, " rel='%s'", priv->relation_type);
+	else if (priv->label != NULL)
+		gdata_parser_string_append_escaped (xml_string, " label='", priv->label, "'");
+	else
+		g_assert_not_reached ();
+}
+
+static void
+get_xml (GDataParsable *parsable, GString *xml_string)
+{
+	gdata_parser_string_append_escaped (xml_string, NULL, GDATA_GCONTACT_RELATION (parsable)->priv->name, NULL);
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+	g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008";);
+}
+
+/**
+ * gdata_gcontact_relation_new:
+ * @name: the name of the relation
+ * @relation_type: the type of relation, or %NULL
+ * @label: a free-form label for the type of relation, or %NULL
+ *
+ * Creates a new #GDataGContactRelation. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>gContact specification</ulink>.
+ *
+ * Exactly one of @relation_type and @label should be provided; the other must be %NULL.
+ *
+ * Return value: a new #GDataGContactRelation; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactRelation *
+gdata_gcontact_relation_new (const gchar *name, const gchar *relation_type, const gchar *label)
+{
+	g_return_val_if_fail (name != NULL && *name != '\0', NULL);
+	g_return_val_if_fail ((relation_type != NULL && *relation_type != '\0' && label == NULL) ||
+	                      (relation_type == NULL && label != NULL && *label != '\0'), NULL);
+
+	return g_object_new (GDATA_TYPE_GCONTACT_RELATION, "name", name, "relation-type", relation_type, "label", label, NULL);
+}
+
+/**
+ * gdata_gcontact_relation_get_name:
+ * @self: a #GDataGContactRelation
+ *
+ * Gets the #GDataGContactRelation:name property.
+ *
+ * Return value: the relation's name
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_relation_get_name (GDataGContactRelation *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_RELATION (self), NULL);
+	return self->priv->name;
+}
+
+/**
+ * gdata_gcontact_relation_set_name:
+ * @self: a #GDataGContactRelation
+ * @name: the new name for the relation
+ *
+ * Sets the #GDataGContactRelation:name property to @name.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_relation_set_name (GDataGContactRelation *self, const gchar *name)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_RELATION (self));
+	g_return_if_fail (name != NULL && *name != '\0');
+
+	g_free (self->priv->name);
+	self->priv->name = g_strdup (name);
+	g_object_notify (G_OBJECT (self), "name");
+}
+
+/**
+ * gdata_gcontact_relation_get_relation_type:
+ * @self: a #GDataGContactRelation
+ *
+ * Gets the #GDataGContactRelation:relation-type property.
+ *
+ * Return value: the type of the relation, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_relation_get_relation_type (GDataGContactRelation *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_RELATION (self), NULL);
+	return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_relation_set_relation_type:
+ * @self: a #GDataGContactRelation
+ * @relation_type: the new type for the relation, or %NULL
+ *
+ * Sets the #GDataGContactRelation:relation-type property to @relation_type,
+ * such as %GDATA_GCONTACT_RELATION_MANAGER or %GDATA_GCONTACT_RELATION_CHILD.
+ *
+ * If @relation_type is %NULL, the relation type will be unset. When the #GDataGContactRelation is used in a query, however,
+ * exactly one of #GDataGContactRelation:relation-type and #GDataGContactRelation:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_relation_set_relation_type (GDataGContactRelation *self, const gchar *relation_type)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_RELATION (self));
+	g_return_if_fail (relation_type == NULL || *relation_type != '\0');
+
+	g_free (self->priv->relation_type);
+	self->priv->relation_type = g_strdup (relation_type);
+	g_object_notify (G_OBJECT (self), "relation-type");
+}
+
+/**
+ * gdata_gcontact_relation_get_label:
+ * @self: a #GDataGContactRelation
+ *
+ * Gets the #GDataGContactRelation:label property.
+ *
+ * Return value: a free-form label for the type of the relation, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_relation_get_label (GDataGContactRelation *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_RELATION (self), NULL);
+	return self->priv->label;
+}
+
+/**
+ * gdata_gcontact_relation_set_label:
+ * @self: a #GDataGContactRelation
+ * @label: the new free-form type for the relation, or %NULL
+ *
+ * Sets the #GDataGContactRelation:label property to @label.
+ *
+ * If @label is %NULL, the label will be unset. When the #GDataGContactRelation is used in a query, however,
+ * exactly one of #GDataGContactRelation:relation-type and #GDataGContactRelation:label must be %NULL.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_relation_set_label (GDataGContactRelation *self, const gchar *label)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_RELATION (self));
+	g_return_if_fail (label == NULL || *label != '\0');
+
+	g_free (self->priv->label);
+	self->priv->label = g_strdup (label);
+	g_object_notify (G_OBJECT (self), "label");
+}
diff --git a/gdata/gcontact/gdata-gcontact-relation.h b/gdata/gcontact/gdata-gcontact-relation.h
new file mode 100644
index 0000000..efcd004
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-relation.h
@@ -0,0 +1,246 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_GCONTACT_RELATION_H
+#define GDATA_GCONTACT_RELATION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_RELATION_ASSISTANT:
+ *
+ * The relation type URI for a contact's assistant.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_ASSISTANT "assistant"
+
+/**
+ * GDATA_GCONTACT_RELATION_BROTHER:
+ *
+ * The relation type URI for a contact's brother.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_BROTHER "brother"
+
+/**
+ * GDATA_GCONTACT_RELATION_CHILD:
+ *
+ * The relation type URI for a contact's child.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_CHILD "child"
+
+/**
+ * GDATA_GCONTACT_RELATION_DOMESTIC_PARTNER:
+ *
+ * The relation type URI for a contact's domestic partner.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_DOMESTIC_PARTNER "domestic-partner"
+
+/**
+ * GDATA_GCONTACT_RELATION_FATHER:
+ *
+ * The relation type URI for a contact's father.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_FATHER "father"
+
+/**
+ * GDATA_GCONTACT_RELATION_FRIEND:
+ *
+ * The relation type URI for a contact's friend.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_FRIEND "friend"
+
+/**
+ * GDATA_GCONTACT_RELATION_MANAGER:
+ *
+ * The relation type URI for a contact's manager.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_MANAGER "manager"
+
+/**
+ * GDATA_GCONTACT_RELATION_MOTHER:
+ *
+ * The relation type URI for a contact's mother.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_MOTHER "mother"
+
+/**
+ * GDATA_GCONTACT_RELATION_PARENT:
+ *
+ * The relation type URI for a contact's parent.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_PARENT "parent"
+
+/**
+ * GDATA_GCONTACT_RELATION_PARTNER:
+ *
+ * The relation type URI for a contact's business partner.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_PARTNER "partner"
+
+/**
+ * GDATA_GCONTACT_RELATION_REFERRER:
+ *
+ * The relation type URI for a contact's referrer.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_REFERRER "referred-by"
+
+/**
+ * GDATA_GCONTACT_RELATION_RELATIVE:
+ *
+ * The relation type URI for a contact's (general) family relative.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_RELATIVE "relative"
+
+/**
+ * GDATA_GCONTACT_RELATION_SISTER:
+ *
+ * The relation type URI for a contact's sister.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_SISTER "sister"
+
+/**
+ * GDATA_GCONTACT_RELATION_SPOUSE:
+ *
+ * The relation type URI for a contact's spouse.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcRelation";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_RELATION_SPOUSE "spouse"
+
+#define GDATA_TYPE_GCONTACT_RELATION		(gdata_gcontact_relation_get_type ())
+#define GDATA_GCONTACT_RELATION(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_RELATION, GDataGContactRelation))
+#define GDATA_GCONTACT_RELATION_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_RELATION, GDataGContactRelationClass))
+#define GDATA_IS_GCONTACT_RELATION(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_RELATION))
+#define GDATA_IS_GCONTACT_RELATION_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_RELATION))
+#define GDATA_GCONTACT_RELATION_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_RELATION, GDataGContactRelationClass))
+
+typedef struct _GDataGContactRelationPrivate	GDataGContactRelationPrivate;
+
+/**
+ * GDataGContactRelation:
+ *
+ * All the fields in the #GDataGContactRelation structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	GDataParsable parent;
+	GDataGContactRelationPrivate *priv;
+} GDataGContactRelation;
+
+/**
+ * GDataGContactRelationClass:
+ *
+ * All the fields in the #GDataGContactRelationClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	/*< private >*/
+	GDataParsableClass parent;
+} GDataGContactRelationClass;
+
+GType gdata_gcontact_relation_get_type (void) G_GNUC_CONST;
+
+GDataGContactRelation *gdata_gcontact_relation_new (const gchar *name, const gchar *relation_type, const gchar *label) G_GNUC_WARN_UNUSED_RESULT;
+
+const gchar *gdata_gcontact_relation_get_name (GDataGContactRelation *self);
+void gdata_gcontact_relation_set_name (GDataGContactRelation *self, const gchar *name);
+
+const gchar *gdata_gcontact_relation_get_relation_type (GDataGContactRelation *self);
+void gdata_gcontact_relation_set_relation_type (GDataGContactRelation *self, const gchar *relation_type);
+
+const gchar *gdata_gcontact_relation_get_label (GDataGContactRelation *self);
+void gdata_gcontact_relation_set_label (GDataGContactRelation *self, const gchar *label);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_RELATION_H */
diff --git a/gdata/gcontact/gdata-gcontact-website.c b/gdata/gcontact/gdata-gcontact-website.c
new file mode 100644
index 0000000..b9182f2
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-website.c
@@ -0,0 +1,469 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-gcontact-website
+ * @short_description: gContact website element
+ * @stability: Unstable
+ * @include: gdata/gcontact/gdata-gcontact-website.h
+ *
+ * #GDataGContactWebsite represents a "website" element from the
+ * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "gdata-gcontact-website.h"
+#include "gdata-parsable.h"
+#include "gdata-parser.h"
+
+static void gdata_gcontact_website_finalize (GObject *object);
+static void gdata_gcontact_website_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void gdata_gcontact_website_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
+static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
+static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
+
+struct _GDataGContactWebsitePrivate {
+	gchar *uri;
+	gchar *relation_type;
+	gchar *label;
+	gboolean is_primary;
+};
+
+enum {
+	PROP_URI = 1,
+	PROP_RELATION_TYPE,
+	PROP_LABEL,
+	PROP_IS_PRIMARY
+};
+
+G_DEFINE_TYPE (GDataGContactWebsite, gdata_gcontact_website, GDATA_TYPE_PARSABLE)
+
+static void
+gdata_gcontact_website_class_init (GDataGContactWebsiteClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GDataGContactWebsitePrivate));
+
+	gobject_class->get_property = gdata_gcontact_website_get_property;
+	gobject_class->set_property = gdata_gcontact_website_set_property;
+	gobject_class->finalize = gdata_gcontact_website_finalize;
+
+	parsable_class->pre_parse_xml = pre_parse_xml;
+	parsable_class->pre_get_xml = pre_get_xml;
+	parsable_class->get_namespaces = get_namespaces;
+	parsable_class->element_name = "website";
+	parsable_class->element_namespace = "gContact";
+
+	/**
+	 * GDataGContactWebsite:uri:
+	 *
+	 * The URI of the website.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_URI,
+				g_param_spec_string ("uri",
+					"URI", "The URI of the website.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactWebsite:relation-type:
+	 *
+	 * A programmatic value that identifies the type of website. Examples are %GDATA_GCONTACT_WEBSITE_HOME_PAGE or %GDATA_GCONTACT_WEBSITE_FTP.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_RELATION_TYPE,
+				g_param_spec_string ("relation-type",
+					"Relation type", "A programmatic value that identifies the type of website.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactWebsite:label:
+	 *
+	 * A simple string value used to name this website. It allows UIs to display a label such as "Work", "Travel blog", "Personal blog", etc.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_LABEL,
+				g_param_spec_string ("label",
+					"Label", "A simple string value used to name this website.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataGContactWebsite:is-primary:
+	 *
+	 * Indicates which website out of a group is primary.
+	 *
+	 * For more information, see the
+	 * <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>gContact specification</ulink>.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_IS_PRIMARY,
+				g_param_spec_boolean ("is-primary",
+					"Primary?", "Indicates which website out of a group is primary.",
+					FALSE,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_gcontact_website_init (GDataGContactWebsite *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GCONTACT_WEBSITE, GDataGContactWebsitePrivate);
+}
+
+static void
+gdata_gcontact_website_finalize (GObject *object)
+{
+	GDataGContactWebsitePrivate *priv = GDATA_GCONTACT_WEBSITE (object)->priv;
+
+	g_free (priv->uri);
+	g_free (priv->relation_type);
+	g_free (priv->label);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (gdata_gcontact_website_parent_class)->finalize (object);
+}
+
+static void
+gdata_gcontact_website_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	GDataGContactWebsitePrivate *priv = GDATA_GCONTACT_WEBSITE (object)->priv;
+
+	switch (property_id) {
+		case PROP_URI:
+			g_value_set_string (value, priv->uri);
+			break;
+		case PROP_RELATION_TYPE:
+			g_value_set_string (value, priv->relation_type);
+			break;
+		case PROP_LABEL:
+			g_value_set_string (value, priv->label);
+			break;
+		case PROP_IS_PRIMARY:
+			g_value_set_boolean (value, priv->is_primary);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static void
+gdata_gcontact_website_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	GDataGContactWebsite *self = GDATA_GCONTACT_WEBSITE (object);
+
+	switch (property_id) {
+		case PROP_URI:
+			gdata_gcontact_website_set_uri (self, g_value_get_string (value));
+			break;
+		case PROP_RELATION_TYPE:
+			gdata_gcontact_website_set_relation_type (self, g_value_get_string (value));
+			break;
+		case PROP_LABEL:
+			gdata_gcontact_website_set_label (self, g_value_get_string (value));
+			break;
+		case PROP_IS_PRIMARY:
+			gdata_gcontact_website_set_is_primary (self, g_value_get_boolean (value));
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static gboolean
+pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
+{
+	xmlChar *uri, *rel;
+	gboolean primary_bool;
+	GDataGContactWebsitePrivate *priv = GDATA_GCONTACT_WEBSITE (parsable)->priv;
+
+	/* Is it the primary website? */
+	if (gdata_parser_boolean_from_property (root_node, "primary", &primary_bool, 0, error) == FALSE)
+		return FALSE;
+
+	uri = xmlGetProp (root_node, (xmlChar*) "href");
+	if (uri == NULL || *uri == '\0') {
+		xmlFree (uri);
+		return gdata_parser_error_required_property_missing (root_node, "href", error);
+	}
+
+	rel = xmlGetProp (root_node, (xmlChar*) "rel");
+	if (rel == NULL || *rel == '\0') {
+		xmlFree (uri);
+		xmlFree (rel);
+		return gdata_parser_error_required_property_missing (root_node, "rel", error);
+	}
+
+	priv->uri = (gchar*) uri;
+	priv->relation_type = (gchar*) rel;
+	priv->label = (gchar*) xmlGetProp (root_node, (xmlChar*) "label");
+	priv->is_primary = primary_bool;
+
+	return TRUE;
+}
+
+static void
+pre_get_xml (GDataParsable *parsable, GString *xml_string)
+{
+	GDataGContactWebsitePrivate *priv = GDATA_GCONTACT_WEBSITE (parsable)->priv;
+
+	gdata_parser_string_append_escaped (xml_string, " href='", priv->uri, "'");
+	g_string_append_printf (xml_string, " rel='%s'", priv->relation_type);
+
+	if (priv->label != NULL)
+		gdata_parser_string_append_escaped (xml_string, " label='", priv->label, "'");
+
+	if (priv->is_primary == TRUE)
+		g_string_append (xml_string, " primary='true'");
+	else
+		g_string_append (xml_string, " primary='false'");
+}
+
+static void
+get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
+{
+	g_hash_table_insert (namespaces, (gchar*) "gContact", (gchar*) "http://schemas.google.com/contact/2008";);
+}
+
+/**
+ * gdata_gcontact_website_new:
+ * @uri: the website URI
+ * @relation_type: the relationship between the website and its owner
+ * @label: a human-readable label for the website, or %NULL
+ * @is_primary: %TRUE if this website is its owner's primary website, %FALSE otherwise
+ *
+ * Creates a new #GDataGContactWebsite. More information is available in the <ulink type="http"
+ * url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>gContact specification</ulink>.
+ *
+ * Return value: a new #GDataGContactWebsite; unref with g_object_unref()
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactWebsite *
+gdata_gcontact_website_new (const gchar *uri, const gchar *relation_type, const gchar *label, gboolean is_primary)
+{
+	g_return_val_if_fail (uri != NULL && *uri != '\0', NULL);
+	g_return_val_if_fail (relation_type != NULL && *relation_type != '\0', NULL);
+	return g_object_new (GDATA_TYPE_GCONTACT_WEBSITE, "uri", uri, "relation-type", relation_type, "label", label, "is-primary", is_primary, NULL);
+}
+
+/**
+ * gdata_gcontact_website_compare:
+ * @a: a #GDataGContactWebsite, or %NULL
+ * @b: another #GDataGContactWebsite, or %NULL
+ *
+ * Compares the two websites in a strcmp() fashion. %NULL values are handled gracefully, with
+ * %0 returned if both @a and @b are %NULL, %-1 if @a is %NULL and %1 if @b is %NULL.
+ *
+ * The comparison of non-%NULL values is done on the basis of the @uri, @relation_type and @label properties of the #GDataGContactWebsite<!-- -->s.
+ *
+ * Return value: %0 if @a equals @b, %-1 or %1 as appropriate otherwise
+ *
+ * Since: 0.7.0
+ **/
+gint
+gdata_gcontact_website_compare (const GDataGContactWebsite *a, const GDataGContactWebsite *b)
+{
+	if (a == NULL && b != NULL)
+		return -1;
+	else if (a != NULL && b == NULL)
+		return 1;
+
+	if (a == b)
+		return 0;
+
+	if (g_strcmp0 (a->priv->uri, b->priv->uri) == 0 &&
+	    g_strcmp0 (a->priv->relation_type, b->priv->relation_type) == 0 &&
+	    g_strcmp0 (a->priv->label, b->priv->label) == 0)
+		return 0;
+	return 1;
+}
+
+/**
+ * gdata_gcontact_website_get_uri:
+ * @self: a #GDataGContactWebsite
+ *
+ * Gets the #GDataGContactWebsite:uri property.
+ *
+ * Return value: the URI of the website
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_website_get_uri (GDataGContactWebsite *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_WEBSITE (self), NULL);
+	return self->priv->uri;
+}
+
+/**
+ * gdata_gcontact_website_set_uri:
+ * @self: a #GDataGContactWebsite
+ * @uri: the new website URI
+ *
+ * Sets the #GDataGContactWebsite:uri property to @uri.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_website_set_uri (GDataGContactWebsite *self, const gchar *uri)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (self));
+	g_return_if_fail (uri != NULL && *uri != '\0');
+
+	g_free (self->priv->uri);
+	self->priv->uri = g_strdup (uri);
+	g_object_notify (G_OBJECT (self), "uri");
+}
+
+/**
+ * gdata_gcontact_website_get_relation_type:
+ * @self: a #GDataGContactWebsite
+ *
+ * Gets the #GDataGContactWebsite:relation-type property.
+ *
+ * Return value: the website's relation type
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_website_get_relation_type (GDataGContactWebsite *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_WEBSITE (self), NULL);
+	return self->priv->relation_type;
+}
+
+/**
+ * gdata_gcontact_website_set_relation_type:
+ * @self: a #GDataGContactWebsite
+ * @relation_type: the new relation type for the website
+ *
+ * Sets the #GDataGContactWebsite:relation-type property to @relation_type
+ * such as %GDATA_GCONTACT_WEBSITE_HOME_PAGE or %GDATA_GCONTACT_WEBSITE_FTP.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_website_set_relation_type (GDataGContactWebsite *self, const gchar *relation_type)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (self));
+	g_return_if_fail (relation_type != NULL && *relation_type != '\0');
+
+	g_free (self->priv->relation_type);
+	self->priv->relation_type = g_strdup (relation_type);
+	g_object_notify (G_OBJECT (self), "relation-type");
+}
+
+/**
+ * gdata_gcontact_website_get_label:
+ * @self: a #GDataGContactWebsite
+ *
+ * Gets the #GDataGContactWebsite:label property.
+ *
+ * Return value: the website's label, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_gcontact_website_get_label (GDataGContactWebsite *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_WEBSITE (self), NULL);
+	return self->priv->label;
+}
+
+/**
+ * gdata_gcontact_website_set_label:
+ * @self: a #GDataGContactWebsite
+ * @label: the new label for the website, or %NULL
+ *
+ * Sets the #GDataGContactWebsite:label property to @label.
+ *
+ * Set @label to %NULL to unset the property in the website.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_website_set_label (GDataGContactWebsite *self, const gchar *label)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (self));
+
+	g_free (self->priv->label);
+	self->priv->label = g_strdup (label);
+	g_object_notify (G_OBJECT (self), "label");
+}
+
+/**
+ * gdata_gcontact_website_is_primary:
+ * @self: a #GDataGContactWebsite
+ *
+ * Gets the #GDataGContactWebsite:is-primary property.
+ *
+ * Return value: %TRUE if this is the primary website, %FALSE otherwise
+ *
+ * Since: 0.7.0
+ **/
+gboolean
+gdata_gcontact_website_is_primary (GDataGContactWebsite *self)
+{
+	g_return_val_if_fail (GDATA_IS_GCONTACT_WEBSITE (self), FALSE);
+	return self->priv->is_primary;
+}
+
+/**
+ * gdata_gcontact_website_set_is_primary:
+ * @self: a #GDataGContactWebsite
+ * @is_primary: %TRUE if this is the primary website, %FALSE otherwise
+ *
+ * Sets the #GDataGContactWebsite:is-primary property to @is_primary.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_gcontact_website_set_is_primary (GDataGContactWebsite *self, gboolean is_primary)
+{
+	g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (self));
+
+	self->priv->is_primary = is_primary;
+	g_object_notify (G_OBJECT (self), "is-primary");
+}
diff --git a/gdata/gcontact/gdata-gcontact-website.h b/gdata/gcontact/gdata-gcontact-website.h
new file mode 100644
index 0000000..da0d62f
--- /dev/null
+++ b/gdata/gcontact/gdata-gcontact-website.h
@@ -0,0 +1,167 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2010 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client 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 GData Client.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_GCONTACT_WEBSITE_H
+#define GDATA_GCONTACT_WEBSITE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-parsable.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDATA_GCONTACT_WEBSITE_HOME_PAGE:
+ *
+ * The relation type URI for a contact's home page.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_HOME_PAGE "home-page"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_BLOG:
+ *
+ * The relation type URI for a contact's blog.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_BLOG "blog"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_PROFILE:
+ *
+ * The relation type URI for a contact's online profile.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_PROFILE "profile"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_HOME:
+ *
+ * The relation type URI for a contact's home website.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_HOME "home"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_WORK:
+ *
+ * The relation type URI for a contact's work website.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_WORK "work"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_OTHER:
+ *
+ * The relation type URI for a miscellaneous website of the contact.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_OTHER "other"
+
+/**
+ * GDATA_GCONTACT_WEBSITE_FTP:
+ *
+ * The relation type URI for a contact's FTP site.
+ *
+ * For more information, see the <ulink type="http" url="http://code.google.com/apis/contacts/docs/3.0/reference.html#gcWebsite";>
+ * gContact specification</ulink>.
+ *
+ * Since: 0.7.0
+ **/
+#define GDATA_GCONTACT_WEBSITE_FTP "ftp"
+
+#define GDATA_TYPE_GCONTACT_WEBSITE		(gdata_gcontact_website_get_type ())
+#define GDATA_GCONTACT_WEBSITE(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_GCONTACT_WEBSITE, GDataGContactWebsite))
+#define GDATA_GCONTACT_WEBSITE_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_GCONTACT_WEBSITE, GDataGContactWebsiteClass))
+#define GDATA_IS_GCONTACT_WEBSITE(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_GCONTACT_WEBSITE))
+#define GDATA_IS_GCONTACT_WEBSITE_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_GCONTACT_WEBSITE))
+#define GDATA_GCONTACT_WEBSITE_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_GCONTACT_WEBSITE, GDataGContactWebsiteClass))
+
+typedef struct _GDataGContactWebsitePrivate	GDataGContactWebsitePrivate;
+
+/**
+ * GDataGContactWebsite:
+ *
+ * All the fields in the #GDataGContactWebsite structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	GDataParsable parent;
+	GDataGContactWebsitePrivate *priv;
+} GDataGContactWebsite;
+
+/**
+ * GDataGContactWebsiteClass:
+ *
+ * All the fields in the #GDataGContactWebsiteClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.7.0
+ **/
+typedef struct {
+	/*< private >*/
+	GDataParsableClass parent;
+} GDataGContactWebsiteClass;
+
+GType gdata_gcontact_website_get_type (void) G_GNUC_CONST;
+
+GDataGContactWebsite *gdata_gcontact_website_new (const gchar *uri, const gchar *relation_type,
+                                                  const gchar *label, gboolean is_primary) G_GNUC_WARN_UNUSED_RESULT;
+gint gdata_gcontact_website_compare (const GDataGContactWebsite *a, const GDataGContactWebsite *b);
+
+const gchar *gdata_gcontact_website_get_uri (GDataGContactWebsite *self);
+void gdata_gcontact_website_set_uri (GDataGContactWebsite *self, const gchar *uri);
+
+const gchar *gdata_gcontact_website_get_relation_type (GDataGContactWebsite *self);
+void gdata_gcontact_website_set_relation_type (GDataGContactWebsite *self, const gchar *relation_type);
+
+const gchar *gdata_gcontact_website_get_label (GDataGContactWebsite *self);
+void gdata_gcontact_website_set_label (GDataGContactWebsite *self, const gchar *label);
+
+gboolean gdata_gcontact_website_is_primary (GDataGContactWebsite *self);
+void gdata_gcontact_website_set_is_primary (GDataGContactWebsite *self, gboolean is_primary);
+
+G_END_DECLS
+
+#endif /* !GDATA_GCONTACT_WEBSITE_H */
diff --git a/gdata/gdata-parser.c b/gdata/gdata-parser.c
index b580076..e59a517 100644
--- a/gdata/gdata-parser.c
+++ b/gdata/gdata-parser.c
@@ -145,6 +145,31 @@ gdata_parser_error_required_property_missing (xmlNode *element, const gchar *pro
 }
 
 gboolean
+gdata_parser_error_mutexed_properties (xmlNode *element, const gchar *property1_name, const gchar *property2_name, GError **error)
+{
+	gchar *property1_string, *property2_string, *element_string;
+
+	property1_string = g_strdup_printf ("@%s", property1_name);
+	property2_string = g_strdup_printf ("@%s", property2_name);
+	element_string = print_element (element);
+
+	g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
+	             /* Translators: the first two parameters are the names of XML properties of an XML element given in the third
+	              * parameter (including the angle brackets ("<" and ">")).
+	              *
+	              * For example:
+	              *  Values were present for properties @rel and @label of a <entry/gContact:relation> element when only one of the
+	              *  two is allowed. */
+	             _("Values were present for properties %s and %s of a %s element when only one of the two is allowed."),
+	             property1_string, property2_string, element_string);
+	g_free (property1_string);
+	g_free (property2_string);
+	g_free (element_string);
+
+	return FALSE;
+}
+
+gboolean
 gdata_parser_error_required_element_missing (const gchar *element_name, const gchar *parent_element_name, GError **error)
 {
 	/* NOTE: This can't take an xmlNode, since such a node wouldn't exist. */
diff --git a/gdata/gdata-parser.h b/gdata/gdata-parser.h
index 198846b..1e123d6 100644
--- a/gdata/gdata-parser.h
+++ b/gdata/gdata-parser.h
@@ -31,6 +31,7 @@ gboolean gdata_parser_error_not_iso8601_format (xmlNode *element, const gchar *a
 gboolean gdata_parser_error_unknown_property_value (xmlNode *element, const gchar *property_name, const gchar *actual_value, GError **error);
 gboolean gdata_parser_error_unknown_content (xmlNode *element, const gchar *actual_content, GError **error);
 gboolean gdata_parser_error_required_property_missing (xmlNode *element, const gchar *property_name, GError **error);
+gboolean gdata_parser_error_mutexed_properties (xmlNode *element, const gchar *property1_name, const gchar *property2_name, GError **error);
 gboolean gdata_parser_error_required_element_missing (const gchar *element_name, const gchar *parent_element_name, GError **error);
 gboolean gdata_parser_error_duplicate_element (xmlNode *element, GError **error);
 
diff --git a/gdata/gdata.h b/gdata/gdata.h
index c51c0a4..1351d3c 100644
--- a/gdata/gdata.h
+++ b/gdata/gdata.h
@@ -53,6 +53,13 @@
 #include <gdata/gd/gdata-gd-where.h>
 #include <gdata/gd/gdata-gd-who.h>
 
+/* GContact */
+#include <gdata/gcontact/gdata-gcontact-calendar.h>
+#include <gdata/gcontact/gdata-gcontact-event.h>
+#include <gdata/gcontact/gdata-gcontact-jot.h>
+#include <gdata/gcontact/gdata-gcontact-relation.h>
+#include <gdata/gcontact/gdata-gcontact-website.h>
+
 /* Media RSS */
 #include <gdata/media/gdata-media-category.h>
 #include <gdata/media/gdata-media-content.h>
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index 1e8d531..682188a 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -720,3 +720,68 @@ gdata_gd_name_get_full_name
 gdata_gd_name_set_full_name
 gdata_youtube_action_get_type
 gdata_youtube_permission_get_type
+gdata_gcontact_jot_get_type
+gdata_gcontact_jot_new
+gdata_gcontact_jot_get_content
+gdata_gcontact_jot_set_content
+gdata_gcontact_jot_get_relation_type
+gdata_gcontact_jot_set_relation_type
+gdata_contacts_contact_add_jot
+gdata_contacts_contact_get_jots
+gdata_contacts_contact_remove_all_jots
+gdata_contacts_contact_get_nickname
+gdata_contacts_contact_set_nickname
+gdata_contacts_contact_get_birthday
+gdata_contacts_contact_set_birthday
+gdata_gcontact_relation_get_type
+gdata_gcontact_relation_new
+gdata_gcontact_relation_get_name
+gdata_gcontact_relation_set_name
+gdata_gcontact_relation_get_relation_type
+gdata_gcontact_relation_set_relation_type
+gdata_gcontact_relation_get_label
+gdata_gcontact_relation_set_label
+gdata_contacts_contact_add_relation
+gdata_contacts_contact_get_relations
+gdata_contacts_contact_remove_all_relations
+gdata_gcontact_website_get_type
+gdata_gcontact_website_new
+gdata_gcontact_website_compare
+gdata_gcontact_website_get_uri
+gdata_gcontact_website_set_uri
+gdata_gcontact_website_get_relation_type
+gdata_gcontact_website_set_relation_type
+gdata_gcontact_website_get_label
+gdata_gcontact_website_set_label
+gdata_gcontact_website_is_primary
+gdata_gcontact_website_set_is_primary
+gdata_contacts_contact_add_website
+gdata_contacts_contact_get_websites
+gdata_contacts_contact_get_primary_website
+gdata_contacts_contact_remove_all_websites
+gdata_gcontact_event_get_type
+gdata_gcontact_event_new
+gdata_gcontact_event_get_date
+gdata_gcontact_event_set_date
+gdata_gcontact_event_get_relation_type
+gdata_gcontact_event_set_relation_type
+gdata_gcontact_event_get_label
+gdata_gcontact_event_set_label
+gdata_contacts_contact_add_event
+gdata_contacts_contact_get_events
+gdata_contacts_contact_remove_all_events
+gdata_gcontact_calendar_get_type
+gdata_gcontact_calendar_new
+gdata_gcontact_calendar_compare
+gdata_gcontact_calendar_get_uri
+gdata_gcontact_calendar_set_uri
+gdata_gcontact_calendar_get_relation_type
+gdata_gcontact_calendar_set_relation_type
+gdata_gcontact_calendar_get_label
+gdata_gcontact_calendar_set_label
+gdata_gcontact_calendar_is_primary
+gdata_gcontact_calendar_set_is_primary
+gdata_contacts_contact_add_calendar
+gdata_contacts_contact_get_calendars
+gdata_contacts_contact_get_primary_calendar
+gdata_contacts_contact_remove_all_calendars
diff --git a/gdata/services/contacts/gdata-contacts-contact.c b/gdata/services/contacts/gdata-contacts-contact.c
index fda55f2..631b316 100644
--- a/gdata/services/contacts/gdata-contacts-contact.c
+++ b/gdata/services/contacts/gdata-contacts-contact.c
@@ -68,13 +68,24 @@ struct _GDataContactsContactPrivate {
 	GHashTable *groups;
 	gboolean deleted;
 	gchar *photo_etag;
+	GList *jots; /* GDataGContactJot */
+	gchar *nickname;
+	GDate birthday;
+	gboolean birthday_has_year; /* contacts can choose to just give the month and day of their birth */
+	GList *relations; /* GDataGContactRelation */
+	GList *websites; /* GDataGContactWebsite */
+	GList *events; /* GDataGContactEvent */
+	GList *calendars; /* GDataGContactCalendar */
 };
 
 enum {
 	PROP_EDITED = 1,
 	PROP_DELETED,
 	PROP_HAS_PHOTO,
-	PROP_NAME
+	PROP_NAME,
+	PROP_NICKNAME,
+	PROP_BIRTHDAY,
+	PROP_BIRTHDAY_HAS_YEAR
 };
 
 G_DEFINE_TYPE (GDataContactsContact, gdata_contacts_contact, GDATA_TYPE_ENTRY)
@@ -150,6 +161,45 @@ gdata_contacts_contact_class_init (GDataContactsContactClass *klass)
 					"Name", "The contact's name in a structured representation.",
 					GDATA_TYPE_GD_NAME,
 					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataContactsContact:nickname:
+	 *
+	 * The contact's chosen nickname.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_NICKNAME,
+				g_param_spec_string ("nickname",
+					"Nickname", "The contact's chosen nickname.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataContactsContact:birthday:
+	 *
+	 * The contact's birthday.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_BIRTHDAY,
+				g_param_spec_boxed ("birthday",
+					"Birthday", "The contact's birthday.",
+					G_TYPE_DATE,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * GDataContactsContact:birthday-has-year:
+	 *
+	 * Whether the contact's birthday includes the year of their birth.
+	 *
+	 * Since: 0.7.0
+	 **/
+	g_object_class_install_property (gobject_class, PROP_BIRTHDAY_HAS_YEAR,
+				g_param_spec_boolean ("birthday-has-year",
+					"Birthday has year?", "Whether the contact's birthday includes the year of their birth.",
+					FALSE,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 static void notify_full_name_cb (GObject *gobject, GParamSpec *pspec, GDataContactsContact *self);
@@ -188,6 +238,9 @@ gdata_contacts_contact_init (GDataContactsContact *self)
 	g_signal_connect (self, "notify::title", (GCallback) notify_title_cb, self);
 	g_signal_connect (self->priv->name, "notify::full-name", (GCallback) notify_full_name_cb, self);
 
+	/* Initialise the contact's birthday to a sane but invalid date */
+	g_date_clear (&(self->priv->birthday), 1);
+
 	/* Add the "contact" kind category */
 	category = gdata_category_new ("http://schemas.google.com/contact/2008#contact";, "http://schemas.google.com/g/2005#kind";, NULL);
 	gdata_entry_add_category (GDATA_ENTRY (self), category);
@@ -208,6 +261,11 @@ gdata_contacts_contact_dispose (GObject *object)
 	gdata_contacts_contact_remove_all_im_addresses (self);
 	gdata_contacts_contact_remove_all_postal_addresses (self);
 	gdata_contacts_contact_remove_all_phone_numbers (self);
+	gdata_contacts_contact_remove_all_jots (self);
+	gdata_contacts_contact_remove_all_relations (self);
+	gdata_contacts_contact_remove_all_websites (self);
+	gdata_contacts_contact_remove_all_events (self);
+	gdata_contacts_contact_remove_all_calendars (self);
 
 	/* Chain up to the parent class */
 	G_OBJECT_CLASS (gdata_contacts_contact_parent_class)->dispose (object);
@@ -221,6 +279,7 @@ gdata_contacts_contact_finalize (GObject *object)
 	g_hash_table_destroy (priv->extended_properties);
 	g_hash_table_destroy (priv->groups);
 	g_free (priv->photo_etag);
+	g_free (priv->nickname);
 
 	/* Chain up to the parent class */
 	G_OBJECT_CLASS (gdata_contacts_contact_parent_class)->finalize (object);
@@ -244,6 +303,15 @@ gdata_contacts_contact_get_property (GObject *object, guint property_id, GValue
 		case PROP_NAME:
 			g_value_set_object (value, priv->name);
 			break;
+		case PROP_NICKNAME:
+			g_value_set_string (value, priv->nickname);
+			break;
+		case PROP_BIRTHDAY:
+			g_value_set_boxed (value, &(priv->birthday));
+			break;
+		case PROP_BIRTHDAY_HAS_YEAR:
+			g_value_set_boolean (value, priv->birthday_has_year);
+			break;
 		default:
 			/* We don't have any other property... */
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -260,6 +328,15 @@ gdata_contacts_contact_set_property (GObject *object, guint property_id, const G
 		case PROP_NAME:
 			gdata_contacts_contact_set_name (self, g_value_get_object (value));
 			break;
+		case PROP_NICKNAME:
+			gdata_contacts_contact_set_nickname (self, g_value_get_string (value));
+			break;
+		case PROP_BIRTHDAY:
+			gdata_contacts_contact_set_birthday (self, g_value_get_boxed (value), self->priv->birthday_has_year);
+			break;
+		case PROP_BIRTHDAY_HAS_YEAR:
+			gdata_contacts_contact_set_birthday (self, &(self->priv->birthday), g_value_get_boolean (value));
+			break;
 		default:
 			/* We don't have any other property... */
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -267,28 +344,6 @@ gdata_contacts_contact_set_property (GObject *object, guint property_id, const G
 	}
 }
 
-/**
- * gdata_contacts_contact_new:
- * @id: the contact's ID, or %NULL
- *
- * Creates a new #GDataContactsContact with the given ID and default properties.
- *
- * Return value: a new #GDataContactsContact; unref with g_object_unref()
- *
- * Since: 0.2.0
- **/
-GDataContactsContact *
-gdata_contacts_contact_new (const gchar *id)
-{
-	GDataContactsContact *contact = GDATA_CONTACTS_CONTACT (g_object_new (GDATA_TYPE_CONTACTS_CONTACT, "id", id, NULL));
-
-	/* Set the edited property to the current time (creation time). We don't do this in *_init() since that would cause
-	 * setting it from parse_xml() to fail (duplicate element). */
-	g_get_current_time (&(contact->priv->edited));
-
-	return contact;
-}
-
 static gboolean
 parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
 {
@@ -344,22 +399,64 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 		} else {
 			return GDATA_PARSABLE_CLASS (gdata_contacts_contact_parent_class)->parse_xml (parsable, doc, node, user_data, error);
 		}
-	} else if (gdata_parser_is_namespace (node, "http://schemas.google.com/contact/2008";) == TRUE &&
-	           xmlStrcmp (node->name, (xmlChar*) "groupMembershipInfo") == 0) {
-		/* gContact:groupMembershipInfo */
-		xmlChar *href;
-		gboolean deleted_bool;
-
-		href = xmlGetProp (node, (xmlChar*) "href");
-		if (href == NULL)
-			return gdata_parser_error_required_property_missing (node, "href", error);
-
-		/* Has it been deleted? */
-		if (gdata_parser_boolean_from_property (node, "deleted", &deleted_bool, 0, error) == FALSE)
-			return FALSE;
-
-		/* Insert it into the hash table */
-		g_hash_table_insert (self->priv->groups, (gchar*) href, GUINT_TO_POINTER (deleted_bool));
+	} else if (gdata_parser_is_namespace (node, "http://schemas.google.com/contact/2008";) == TRUE) {
+		if (gdata_parser_object_from_element_setter (node, "jot", P_REQUIRED, GDATA_TYPE_GCONTACT_JOT,
+		                                             gdata_contacts_contact_add_jot, self, &success, error) == TRUE ||
+		    gdata_parser_object_from_element_setter (node, "relation", P_REQUIRED, GDATA_TYPE_GCONTACT_RELATION,
+		                                             gdata_contacts_contact_add_relation, self, &success, error) == TRUE ||
+		    gdata_parser_object_from_element_setter (node, "event", P_REQUIRED, GDATA_TYPE_GCONTACT_EVENT,
+		                                             gdata_contacts_contact_add_event, self, &success, error) == TRUE ||
+		    gdata_parser_object_from_element_setter (node, "website", P_REQUIRED, GDATA_TYPE_GCONTACT_WEBSITE,
+		                                             gdata_contacts_contact_add_website, self, &success, error) == TRUE ||
+		    gdata_parser_object_from_element_setter (node, "calendarLink", P_REQUIRED, GDATA_TYPE_GCONTACT_CALENDAR,
+		                                             gdata_contacts_contact_add_calendar, self, &success, error) == TRUE ||
+		    gdata_parser_string_from_element (node, "nickname", P_REQUIRED | P_NO_DUPES, &(self->priv->nickname), &success, error) == TRUE) {
+			return success;
+		} else if (xmlStrcmp (node->name, (xmlChar*) "groupMembershipInfo") == 0) {
+			/* gContact:groupMembershipInfo */
+			xmlChar *href;
+			gboolean deleted_bool;
+
+			href = xmlGetProp (node, (xmlChar*) "href");
+			if (href == NULL)
+				return gdata_parser_error_required_property_missing (node, "href", error);
+
+			/* Has it been deleted? */
+			if (gdata_parser_boolean_from_property (node, "deleted", &deleted_bool, 0, error) == FALSE)
+				return FALSE;
+
+			/* Insert it into the hash table */
+			g_hash_table_insert (self->priv->groups, (gchar*) href, GUINT_TO_POINTER (deleted_bool));
+		} else if (xmlStrcmp (node->name, (xmlChar*) "birthday") == 0) {
+			/* gContact:birthday */
+			xmlChar *birthday;
+			guint length = 0, year = 666, month, day;
+
+			if (g_date_valid (&(self->priv->birthday)) == TRUE)
+				return gdata_parser_error_duplicate_element (node, error);
+
+			birthday = xmlGetProp (node, (xmlChar*) "when");
+			if (birthday == NULL)
+				return gdata_parser_error_required_property_missing (node, "when", error);
+			length = strlen ((char*) birthday);
+
+			/* Try parsing the two possible formats: YYYY-MM-DD and --MM-DD */
+			if (((length == 10 && sscanf ((char*) birthday, "%4u-%2u-%2u", &year, &month, &day) == 3) ||
+			     (length == 7 && sscanf ((char*) birthday, "--%2u-%2u", &month, &day) == 2)) &&
+			    g_date_valid_dmy (day, month, year) == TRUE) {
+				/* Store the values in the GDate */
+				g_date_set_dmy (&(self->priv->birthday), day, month, year);
+				self->priv->birthday_has_year = (length == 10) ? TRUE : FALSE;
+				xmlFree (birthday);
+			} else {
+				/* Parsing failed */
+				gdata_parser_error_not_iso8601_format (node, (gchar*) birthday, error);
+				xmlFree (birthday);
+				return FALSE;
+			}
+		} else {
+			return GDATA_PARSABLE_CLASS (gdata_contacts_contact_parent_class)->parse_xml (parsable, doc, node, user_data, error);
+		}
 	} else {
 		/* If we haven't yet found a photo, check to see if it's a photo <link> element */
 		if (self->priv->photo_etag == NULL && xmlStrcmp (node->name, (xmlChar*) "link") == 0) {
@@ -416,6 +513,11 @@ get_xml (GDataParsable *parsable, GString *xml_string)
 	get_child_xml (priv->phone_numbers, xml_string);
 	get_child_xml (priv->postal_addresses, xml_string);
 	get_child_xml (priv->organizations, xml_string);
+	get_child_xml (priv->jots, xml_string);
+	get_child_xml (priv->relations, xml_string);
+	get_child_xml (priv->websites, xml_string);
+	get_child_xml (priv->events, xml_string);
+	get_child_xml (priv->calendars, xml_string);
 
 	/* Extended properties */
 	g_hash_table_foreach (priv->extended_properties, (GHFunc) get_extended_property_xml_cb, xml_string);
@@ -423,6 +525,24 @@ get_xml (GDataParsable *parsable, GString *xml_string)
 	/* Group membership info */
 	g_hash_table_foreach (priv->groups, (GHFunc) get_group_xml_cb, xml_string);
 
+	/* gContact:nickname */
+	if (priv->nickname != NULL)
+		gdata_parser_string_append_escaped (xml_string, "<gContact:nickname>", priv->nickname, "</gContact:nickname>");
+
+	/* gContact:birthday */
+	if (g_date_valid (&(priv->birthday)) == TRUE) {
+		if (priv->birthday_has_year == TRUE) {
+			g_string_append_printf (xml_string, "<gContact:birthday when='%04u-%02u-%02u'/>",
+			                        g_date_get_year (&(priv->birthday)),
+			                        g_date_get_month (&(priv->birthday)),
+			                        g_date_get_day (&(priv->birthday)));
+		} else {
+			g_string_append_printf (xml_string, "<gContact:birthday when='--%02u-%02u'/>",
+			                        g_date_get_month (&(priv->birthday)),
+			                        g_date_get_day (&(priv->birthday)));
+		}
+	}
+
 	/* TODO:
 	 * - Finish supporting all tags
 	 * - Check things are escaped (or not) as appropriate
@@ -441,6 +561,28 @@ get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
 }
 
 /**
+ * gdata_contacts_contact_new:
+ * @id: the contact's ID, or %NULL
+ *
+ * Creates a new #GDataContactsContact with the given ID and default properties.
+ *
+ * Return value: a new #GDataContactsContact; unref with g_object_unref()
+ *
+ * Since: 0.2.0
+ **/
+GDataContactsContact *
+gdata_contacts_contact_new (const gchar *id)
+{
+	GDataContactsContact *contact = GDATA_CONTACTS_CONTACT (g_object_new (GDATA_TYPE_CONTACTS_CONTACT, "id", id, NULL));
+
+	/* Set the edited property to the current time (creation time). We don't do this in *_init() since that would cause
+	 * setting it from parse_xml() to fail (duplicate element). */
+	g_get_current_time (&(contact->priv->edited));
+
+	return contact;
+}
+
+/**
  * gdata_contacts_contact_get_edited:
  * @self: a #GDataContactsContact
  * @edited: a #GTimeVal
@@ -502,6 +644,100 @@ gdata_contacts_contact_set_name (GDataContactsContact *self, GDataGDName *name)
 }
 
 /**
+ * gdata_contacts_contact_get_nickname:
+ * @self: a #GDataContactsContact
+ *
+ * Gets the #GDataContactsContact:nickname property.
+ *
+ * Return value: the contact's nickname, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+const gchar *
+gdata_contacts_contact_get_nickname (GDataContactsContact *self)
+{
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+	return self->priv->nickname;
+}
+
+/**
+ * gdata_contacts_contact_set_nickname:
+ * @self: a #GDataContactsContact
+ * @nickname: the new nickname, or %NULL
+ *
+ * Sets the #GDataContactsContact:nickname property to @nickname.
+ *
+ * If @nickname is %NULL, the contact's nickname will be removed.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_set_nickname (GDataContactsContact *self, const gchar *nickname)
+{
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+	g_free (self->priv->nickname);
+	self->priv->nickname = g_strdup (nickname);
+	g_object_notify (G_OBJECT (self), "nickname");
+}
+
+/**
+ * gdata_contacts_contact_get_birthday:
+ * @self: a #GDataContactsContact
+ * @birthday: return location for the birthday, or %NULL
+ *
+ * Gets the #GDataContactsContact:birthday and #GDataContactsContact:birthday-has-year properties. If @birthday is non-%NULL,
+ * #GDataContactsContact:birthday is returned in it. The function returns the value of #GDataContactsContact:birthday-has-year,
+ * which specifies whether the year in @birthday is meaningful. Contacts may not have the year of their birth set, in which
+ * case, the function would return %FALSE, and the year in @birthday should be ignored.
+ *
+ * Return value: whether the contact's birthday has the year set
+ *
+ * Since: 0.7.0
+ **/
+gboolean
+gdata_contacts_contact_get_birthday (GDataContactsContact *self, GDate *birthday)
+{
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), FALSE);
+
+	if (birthday != NULL)
+		*birthday = self->priv->birthday;
+	return self->priv->birthday_has_year;
+}
+
+/**
+ * gdata_contacts_contact_set_birthday:
+ * @self: a #GDataContactsContact
+ * @birthday: the new birthday, or %NULL
+ * @birthday_has_year: %TRUE if @birthday's year is relevant, %FALSE otherwise
+ *
+ * Sets the #GDataContactsContact:birthday property to @birthday and the #GDataContactsContact:birthday-has-year property to @birthday_has_year.
+ * See gdata_contacts_contact_get_birthday() for an explanation of the interation between these two properties.
+ *
+ * If @birthday is %NULL, the contact's birthday will be removed.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_set_birthday (GDataContactsContact *self, GDate *birthday, gboolean birthday_has_year)
+{
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+	g_return_if_fail (birthday == NULL || g_date_valid (birthday));
+
+	if (birthday != NULL)
+		self->priv->birthday = *birthday;
+	else
+		g_date_clear (&(self->priv->birthday), 1);
+
+	self->priv->birthday_has_year = birthday_has_year;
+
+	g_object_freeze_notify (G_OBJECT (self));
+	g_object_notify (G_OBJECT (self), "birthday");
+	g_object_notify (G_OBJECT (self), "birthday-has-year");
+	g_object_thaw_notify (G_OBJECT (self));
+}
+
+/**
  * gdata_contacts_contact_add_email_address:
  * @self: a #GDataContactsContact
  * @email_address: a #GDataGDEmailAddress to add
@@ -947,6 +1183,357 @@ gdata_contacts_contact_remove_all_organizations (GDataContactsContact *self)
 }
 
 /**
+ * gdata_contacts_contact_add_jot:
+ * @self: a #GDataContactsContact
+ * @jot: a #GDataGContactJot to add
+ *
+ * Adds a jot to the contact's list of jots and increments its reference count.
+ *
+ * Duplicate jots will be added to the list, and multiple jots with the same relation type can be added to a single contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_jot (GDataContactsContact *self, GDataGContactJot *jot)
+{
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+	g_return_if_fail (GDATA_IS_GCONTACT_JOT (jot));
+
+	self->priv->jots = g_list_append (self->priv->jots, g_object_ref (jot));
+}
+
+/**
+ * gdata_contacts_contact_get_jots:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the jots attached to the contact.
+ *
+ * Return value: a #GList of #GDataGContactJot<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_jots (GDataContactsContact *self)
+{
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+	return self->priv->jots;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_jots:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all jots from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_jots (GDataContactsContact *self)
+{
+	GDataContactsContactPrivate *priv = self->priv;
+
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+	if (priv->jots != NULL) {
+		g_list_foreach (priv->jots, (GFunc) g_object_unref, NULL);
+		g_list_free (priv->jots);
+	}
+	priv->jots = NULL;
+}
+
+/**
+ * gdata_contacts_contact_add_relation:
+ * @self: a #GDataContactsContact
+ * @relation: a #GDataGContactRelation to add
+ *
+ * Adds a relation to the contact's list of relations and increments its reference count.
+ *
+ * Duplicate relations will be added to the list, and multiple relations with the same relation type can be added to a single contact.
+ * Though it may not make sense for some relation types to be repeated, adding them is allowed.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_relation (GDataContactsContact *self, GDataGContactRelation *relation)
+{
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+	g_return_if_fail (GDATA_IS_GCONTACT_RELATION (relation));
+
+	self->priv->relations = g_list_append (self->priv->relations, g_object_ref (relation));
+}
+
+/**
+ * gdata_contacts_contact_get_relations:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the relations of the contact.
+ *
+ * Return value: a #GList of #GDataGContactRelation<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_relations (GDataContactsContact *self)
+{
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+	return self->priv->relations;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_relations:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all relations from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_relations (GDataContactsContact *self)
+{
+	GDataContactsContactPrivate *priv = self->priv;
+
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+	if (priv->relations != NULL) {
+		g_list_foreach (priv->relations, (GFunc) g_object_unref, NULL);
+		g_list_free (priv->relations);
+	}
+	priv->relations = NULL;
+}
+
+/**
+ * gdata_contacts_contact_add_website:
+ * @self: a #GDataContactsContact
+ * @website: a #GDataGContactWebsite to add
+ *
+ * Adds a website to the contact's list of websites and increments its reference count.
+ *
+ * Duplicate websites will not be added to the list, though the same URI may appear in several #GDataGContactWebsite<!-- -->s with different
+ * relation types or labels.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_website (GDataContactsContact *self, GDataGContactWebsite *website)
+{
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+	g_return_if_fail (GDATA_IS_GCONTACT_WEBSITE (website));
+
+	if (g_list_find_custom (self->priv->websites, website, (GCompareFunc) gdata_gcontact_website_compare) == NULL)
+		self->priv->websites = g_list_append (self->priv->websites, g_object_ref (website));
+}
+
+/**
+ * gdata_contacts_contact_get_websites:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the websites of the contact.
+ *
+ * Return value: a #GList of #GDataGContactWebsite<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_websites (GDataContactsContact *self)
+{
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+	return self->priv->websites;
+}
+
+/**
+ * gdata_contacts_contact_get_primary_website:
+ * @self: a #GDataContactsContact
+ *
+ * Gets the contact's primary website, if one exists.
+ *
+ * Return value: a #GDataGContactWebsite, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactWebsite *
+gdata_contacts_contact_get_primary_website (GDataContactsContact *self)
+{
+	GList *i;
+
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+
+	for (i = self->priv->websites; i != NULL; i = i->next) {
+		if (gdata_gcontact_website_is_primary (GDATA_GCONTACT_WEBSITE (i->data)) == TRUE)
+			return GDATA_GCONTACT_WEBSITE (i->data);
+	}
+
+	return NULL;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_websites:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all websites from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_websites (GDataContactsContact *self)
+{
+	GDataContactsContactPrivate *priv = self->priv;
+
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+	if (priv->websites != NULL) {
+		g_list_foreach (priv->websites, (GFunc) g_object_unref, NULL);
+		g_list_free (priv->websites);
+	}
+	priv->websites = NULL;
+}
+
+/**
+ * gdata_contacts_contact_add_event:
+ * @self: a #GDataContactsContact
+ * @event: a #GDataGContactEvent to add
+ *
+ * Adds an event to the contact's list of events and increments its reference count.
+ *
+ * Duplicate events will be added to the list, and multiple events with the same event type can be added to a single contact.
+ * Though it may not make sense for some event types to be repeated, adding them is allowed.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_event (GDataContactsContact *self, GDataGContactEvent *event)
+{
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+	g_return_if_fail (GDATA_IS_GCONTACT_EVENT (event));
+
+	self->priv->events = g_list_append (self->priv->events, g_object_ref (event));
+}
+
+/**
+ * gdata_contacts_contact_get_events:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the events of the contact.
+ *
+ * Return value: a #GList of #GDataGContactEvent<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_events (GDataContactsContact *self)
+{
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+	return self->priv->events;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_events:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all events from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_events (GDataContactsContact *self)
+{
+	GDataContactsContactPrivate *priv = self->priv;
+
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+	if (priv->events != NULL) {
+		g_list_foreach (priv->events, (GFunc) g_object_unref, NULL);
+		g_list_free (priv->events);
+	}
+	priv->events = NULL;
+}
+
+/**
+ * gdata_contacts_contact_add_calendar:
+ * @self: a #GDataContactsContact
+ * @calendar: a #GDataGContactCalendar to add
+ *
+ * Adds a calendar to the contact's list of calendars and increments its reference count.
+ *
+ * Duplicate calendars will not be added to the list, though the same URI may appear in several #GDataGContactCalendar<!-- -->s with different
+ * relation types or labels.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_add_calendar (GDataContactsContact *self, GDataGContactCalendar *calendar)
+{
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+	g_return_if_fail (GDATA_IS_GCONTACT_CALENDAR (calendar));
+
+	if (g_list_find_custom (self->priv->calendars, calendar, (GCompareFunc) gdata_gcontact_calendar_compare) == NULL)
+		self->priv->calendars = g_list_append (self->priv->calendars, g_object_ref (calendar));
+}
+
+/**
+ * gdata_contacts_contact_get_calendars:
+ * @self: a #GDataContactsContact
+ *
+ * Gets a list of the calendars of the contact.
+ *
+ * Return value: a #GList of #GDataGContactCalendar<!-- -->s, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GList *
+gdata_contacts_contact_get_calendars (GDataContactsContact *self)
+{
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+	return self->priv->calendars;
+}
+
+/**
+ * gdata_contacts_contact_get_primary_calendar:
+ * @self: a #GDataContactsContact
+ *
+ * Gets the contact's primary calendar, if one exists.
+ *
+ * Return value: a #GDataGContactCalendar, or %NULL
+ *
+ * Since: 0.7.0
+ **/
+GDataGContactCalendar *
+gdata_contacts_contact_get_primary_calendar (GDataContactsContact *self)
+{
+	GList *i;
+
+	g_return_val_if_fail (GDATA_IS_CONTACTS_CONTACT (self), NULL);
+
+	for (i = self->priv->calendars; i != NULL; i = i->next) {
+		if (gdata_gcontact_calendar_is_primary (GDATA_GCONTACT_CALENDAR (i->data)) == TRUE)
+			return GDATA_GCONTACT_CALENDAR (i->data);
+	}
+
+	return NULL;
+}
+
+/**
+ * gdata_contacts_contact_remove_all_calendars:
+ * @self: a #GDataContactsContact
+ *
+ * Removes all calendars from the contact.
+ *
+ * Since: 0.7.0
+ **/
+void
+gdata_contacts_contact_remove_all_calendars (GDataContactsContact *self)
+{
+	GDataContactsContactPrivate *priv = self->priv;
+
+	g_return_if_fail (GDATA_IS_CONTACTS_CONTACT (self));
+
+	if (priv->calendars != NULL) {
+		g_list_foreach (priv->calendars, (GFunc) g_object_unref, NULL);
+		g_list_free (priv->calendars);
+	}
+	priv->calendars = NULL;
+}
+
+/**
  * gdata_contacts_contact_get_extended_property:
  * @self: a #GDataContactsContact
  * @name: the property name; an arbitrary, unique string
diff --git a/gdata/services/contacts/gdata-contacts-contact.h b/gdata/services/contacts/gdata-contacts-contact.h
index 8c384c0..03227ec 100644
--- a/gdata/services/contacts/gdata-contacts-contact.h
+++ b/gdata/services/contacts/gdata-contacts-contact.h
@@ -30,6 +30,11 @@
 #include <gdata/gd/gdata-gd-organization.h>
 #include <gdata/gd/gdata-gd-phone-number.h>
 #include <gdata/gd/gdata-gd-postal-address.h>
+#include <gdata/gcontact/gdata-gcontact-calendar.h>
+#include <gdata/gcontact/gdata-gcontact-event.h>
+#include <gdata/gcontact/gdata-gcontact-jot.h>
+#include <gdata/gcontact/gdata-gcontact-relation.h>
+#include <gdata/gcontact/gdata-gcontact-website.h>
 
 G_BEGIN_DECLS
 
@@ -76,6 +81,12 @@ gboolean gdata_contacts_contact_is_deleted (GDataContactsContact *self);
 GDataGDName *gdata_contacts_contact_get_name (GDataContactsContact *self);
 void gdata_contacts_contact_set_name (GDataContactsContact *self, GDataGDName *name);
 
+const gchar *gdata_contacts_contact_get_nickname (GDataContactsContact *self);
+void gdata_contacts_contact_set_nickname (GDataContactsContact *self, const gchar *nickname);
+
+gboolean gdata_contacts_contact_get_birthday (GDataContactsContact *self, GDate *birthday);
+void gdata_contacts_contact_set_birthday (GDataContactsContact *self, GDate *birthday, gboolean birthday_has_year);
+
 void gdata_contacts_contact_add_email_address (GDataContactsContact *self, GDataGDEmailAddress *email_address);
 GList *gdata_contacts_contact_get_email_addresses (GDataContactsContact *self);
 GDataGDEmailAddress *gdata_contacts_contact_get_primary_email_address (GDataContactsContact *self);
@@ -101,6 +112,28 @@ GList *gdata_contacts_contact_get_organizations (GDataContactsContact *self);
 GDataGDOrganization *gdata_contacts_contact_get_primary_organization (GDataContactsContact *self);
 void gdata_contacts_contact_remove_all_organizations (GDataContactsContact *self);
 
+void gdata_contacts_contact_add_jot (GDataContactsContact *self, GDataGContactJot *jot);
+GList *gdata_contacts_contact_get_jots (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_jots (GDataContactsContact *self);
+
+void gdata_contacts_contact_add_relation (GDataContactsContact *self, GDataGContactRelation *relation);
+GList *gdata_contacts_contact_get_relations (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_relations (GDataContactsContact *self);
+
+void gdata_contacts_contact_add_website (GDataContactsContact *self, GDataGContactWebsite *website);
+GList *gdata_contacts_contact_get_websites (GDataContactsContact *self);
+GDataGContactWebsite *gdata_contacts_contact_get_primary_website (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_websites (GDataContactsContact *self);
+
+void gdata_contacts_contact_add_event (GDataContactsContact *self, GDataGContactEvent *event);
+GList *gdata_contacts_contact_get_events (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_events (GDataContactsContact *self);
+
+void gdata_contacts_contact_add_calendar (GDataContactsContact *self, GDataGContactCalendar *calendar);
+GList *gdata_contacts_contact_get_calendars (GDataContactsContact *self);
+GDataGContactCalendar *gdata_contacts_contact_get_primary_calendar (GDataContactsContact *self);
+void gdata_contacts_contact_remove_all_calendars (GDataContactsContact *self);
+
 const gchar *gdata_contacts_contact_get_extended_property (GDataContactsContact *self, const gchar *name);
 GHashTable *gdata_contacts_contact_get_extended_properties (GDataContactsContact *self);
 gboolean gdata_contacts_contact_set_extended_property (GDataContactsContact *self, const gchar *name, const gchar *value);
@@ -114,9 +147,9 @@ GList *gdata_contacts_contact_get_groups (GDataContactsContact *self) G_GNUC_WAR
 
 gboolean gdata_contacts_contact_has_photo (GDataContactsContact *self);
 gchar *gdata_contacts_contact_get_photo (GDataContactsContact *self, GDataContactsService *service, gsize *length, gchar **content_type,
-					  GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
+                                         GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
 gboolean gdata_contacts_contact_set_photo (GDataContactsContact *self, GDataService *service, const gchar *data, gsize length,
-					   GCancellable *cancellable, GError **error);
+                                           GCancellable *cancellable, GError **error);
 
 G_END_DECLS
 
diff --git a/gdata/tests/contacts.c b/gdata/tests/contacts.c
index 0b4e6d3..cae7bab 100644
--- a/gdata/tests/contacts.c
+++ b/gdata/tests/contacts.c
@@ -153,17 +153,23 @@ static void
 test_insert_simple (gconstpointer service)
 {
 	GDataContactsContact *contact, *new_contact;
-	GDataCategory *category;
 	GDataGDName *name, *name2;
 	GDataGDEmailAddress *email_address1, *email_address2;
 	GDataGDPhoneNumber *phone_number1, *phone_number2;
 	GDataGDIMAddress *im_address;
+	GDataGDOrganization *org;
 	GDataGDPostalAddress *postal_address;
-	gchar *xml;
+	GDataGContactJot *jot;
+	GDataGContactRelation *relation;
+	GDataGContactWebsite *website;
+	GDataGContactEvent *event;
+	GDataGContactCalendar *calendar;
+	gchar *xml, *nickname;
 	GList *list;
+	GDate date, *date2;
 	GHashTable *properties;
 	GTimeVal *edited, creation_time;
-	gboolean deleted, has_photo;
+	gboolean deleted, has_photo, birthday_has_year;
 	GError *error = NULL;
 
 	contact = gdata_contacts_contact_new (NULL);
@@ -186,32 +192,61 @@ test_insert_simple (gconstpointer service)
 	g_object_unref (name2);
 	g_assert_cmpstr (gdata_entry_get_title (GDATA_ENTRY (contact)), ==, "John Smith");
 
+	gdata_contacts_contact_set_nickname (contact, "Big J");
+	g_date_set_dmy (&date, 1, 1, 1900);
+	gdata_contacts_contact_set_birthday (contact, &date, FALSE);
 	gdata_entry_set_content (GDATA_ENTRY (contact), "Notes");
-	/* TODO: Have it add this category automatically? Same for GDataCalendarEvent */
-	category = gdata_category_new ("http://schemas.google.com/contact/2008#contact";, "http://schemas.google.com/g/2005#kind";, NULL);
-	gdata_entry_add_category (GDATA_ENTRY (contact), category);
-	g_object_unref (category);
+
 	email_address1 = gdata_gd_email_address_new ("liz gmail com", "http://schemas.google.com/g/2005#work";, NULL, FALSE);
 	gdata_contacts_contact_add_email_address (contact, email_address1);
 	g_object_unref (email_address1);
+
 	email_address2 = gdata_gd_email_address_new ("liz example org", "http://schemas.google.com/g/2005#home";, NULL, FALSE);
 	gdata_contacts_contact_add_email_address (contact, email_address2);
 	g_object_unref (email_address2);
+
 	phone_number1 = gdata_gd_phone_number_new ("(206)555-1212", "http://schemas.google.com/g/2005#work";, NULL, NULL, TRUE);
 	gdata_contacts_contact_add_phone_number (contact, phone_number1);
 	g_object_unref (phone_number1);
+
 	phone_number2 = gdata_gd_phone_number_new ("(206)555-1213", "http://schemas.google.com/g/2005#home";, NULL, NULL, FALSE);
 	gdata_contacts_contact_add_phone_number (contact, phone_number2);
 	g_object_unref (phone_number2);
+
 	im_address = gdata_gd_im_address_new ("liz gmail com", "http://schemas.google.com/g/2005#GOOGLE_TALK";, "http://schemas.google.com/g/2005#home";,
 					      NULL, FALSE);
 	gdata_contacts_contact_add_im_address (contact, im_address);
 	g_object_unref (im_address);
+
 	postal_address = gdata_gd_postal_address_new ("http://schemas.google.com/g/2005#work";, NULL, TRUE);
 	gdata_gd_postal_address_set_street (postal_address, "1600 Amphitheatre Pkwy Mountain View");
 	gdata_contacts_contact_add_postal_address (contact, postal_address);
 	g_object_unref (postal_address);
 
+	org = gdata_gd_organization_new ("OrgCorp", "President", "http://schemas.google.com/g/2005#work";, NULL, FALSE);
+	gdata_contacts_contact_add_organization (contact, org);
+	g_object_unref (org);
+
+	jot = gdata_gcontact_jot_new ("This is a jot.", GDATA_GCONTACT_JOT_OTHER);
+	gdata_contacts_contact_add_jot (contact, jot);
+	g_object_unref (jot);
+
+	relation = gdata_gcontact_relation_new ("Brian Haddock", GDATA_GCONTACT_RELATION_FRIEND, NULL);
+	gdata_contacts_contact_add_relation (contact, relation);
+	g_object_unref (relation);
+
+	website = gdata_gcontact_website_new ("http://example.com/";, GDATA_GCONTACT_WEBSITE_PROFILE, NULL, TRUE);
+	gdata_contacts_contact_add_website (contact, website);
+	g_object_unref (website);
+
+	event = gdata_gcontact_event_new (&date, GDATA_GCONTACT_EVENT_ANNIVERSARY, NULL);
+	gdata_contacts_contact_add_event (contact, event);
+	g_object_unref (event);
+
+	calendar = gdata_gcontact_calendar_new ("http://calendar.example.com/";, GDATA_GCONTACT_CALENDAR_HOME, NULL, TRUE);
+	gdata_contacts_contact_add_calendar (contact, calendar);
+	g_object_unref (calendar);
+
 	/* Add some extended properties */
 	g_assert (gdata_contacts_contact_set_extended_property (contact, "TITLE", NULL) == TRUE);
 	g_assert (gdata_contacts_contact_set_extended_property (contact, "ROLE", "") == TRUE);
@@ -223,6 +258,9 @@ test_insert_simple (gconstpointer service)
 	              "deleted", &deleted,
 	              "has-photo", &has_photo,
 	              "name", &name,
+	              "nickname", &nickname,
+	              "birthday", &date2,
+	              "birthday-has-year", &birthday_has_year,
 	              NULL);
 
 	g_assert_cmpint (edited->tv_sec, ==, creation_time.tv_sec);
@@ -230,8 +268,15 @@ test_insert_simple (gconstpointer service)
 	g_assert (deleted == FALSE);
 	g_assert (has_photo == FALSE);
 	g_assert (name2 == name);
+	g_assert_cmpstr (nickname, ==, "Big J");
+	g_assert (g_date_valid (date2) == TRUE);
+	g_assert_cmpuint (g_date_get_month (date2), ==, 1);
+	g_assert_cmpuint (g_date_get_day (date2), ==, 1);
+	g_assert (birthday_has_year == FALSE);
 
 	g_object_unref (name2);
+	g_free (date2);
+	g_free (nickname);
 
 	/* Check the XML */
 	xml = gdata_parsable_get_xml (GDATA_PARSABLE (contact));
@@ -257,7 +302,18 @@ test_insert_simple (gconstpointer service)
 				"<gd:structuredPostalAddress rel='http://schemas.google.com/g/2005#work' primary='true'>"
 					"<gd:street>1600 Amphitheatre Pkwy Mountain View</gd:street>"
 				"</gd:structuredPostalAddress>"
+				"<gd:organization rel='http://schemas.google.com/g/2005#work' primary='false'>"
+					"<gd:orgName>OrgCorp</gd:orgName>"
+					"<gd:orgTitle>President</gd:orgTitle>"
+				"</gd:organization>"
+				"<gContact:jot rel='other'>This is a jot.</gContact:jot>"
+				"<gContact:relation rel='friend'>Brian Haddock</gContact:relation>"
+				"<gContact:website href='http://example.com/' rel='profile' primary='true'/>"
+				"<gContact:event rel='anniversary'><gd:when startTime='1900-01-01'/></gContact:event>"
+				"<gContact:calendarLink href='http://calendar.example.com/' rel='home' primary='true'/>"
 				"<gd:extendedProperty name='CALURI'>http://example.com/</gd:extendedProperty>"
+				"<gContact:nickname>Big J</gContact:nickname>"
+				"<gContact:birthday when='--01-01'/>"
 			 "</entry>");
 	g_free (xml);
 
@@ -272,6 +328,13 @@ test_insert_simple (gconstpointer service)
 	gdata_contacts_contact_get_edited (new_contact, &creation_time);
 	g_assert_cmpint (creation_time.tv_sec, >=, edited->tv_sec);
 
+	/* Nickname and birthday */
+	g_assert_cmpstr (gdata_contacts_contact_get_nickname (new_contact), ==, "Big J");
+	g_assert (gdata_contacts_contact_get_birthday (new_contact, &date) == FALSE);
+	g_assert (g_date_valid (&date) == TRUE);
+	g_assert_cmpuint (g_date_get_month (&date), ==, 1);
+	g_assert_cmpuint (g_date_get_day (&date), ==, 1);
+
 	/* E-mail addresses */
 	list = gdata_contacts_contact_get_email_addresses (new_contact);
 	g_assert_cmpuint (g_list_length (list), ==, 2);
@@ -302,10 +365,40 @@ test_insert_simple (gconstpointer service)
 
 	/* Organizations */
 	list = gdata_contacts_contact_get_organizations (new_contact);
-	g_assert (list == NULL);
+	g_assert_cmpuint (g_list_length (list), ==, 1);
+	g_assert (GDATA_IS_GD_ORGANIZATION (list->data));
 
 	g_assert (gdata_contacts_contact_get_primary_organization (new_contact) == NULL);
 
+	/* Jots */
+	list = gdata_contacts_contact_get_jots (new_contact);
+	g_assert_cmpuint (g_list_length (list), ==, 1);
+	g_assert (GDATA_IS_GCONTACT_JOT (list->data));
+
+	/* Relations */
+	list = gdata_contacts_contact_get_relations (new_contact);
+	g_assert_cmpuint (g_list_length (list), ==, 1);
+	g_assert (GDATA_IS_GCONTACT_RELATION (list->data));
+
+	/* Websites */
+	list = gdata_contacts_contact_get_websites (new_contact);
+	g_assert_cmpuint (g_list_length (list), ==, 1);
+	g_assert (GDATA_IS_GCONTACT_WEBSITE (list->data));
+
+	g_assert (GDATA_IS_GCONTACT_WEBSITE (gdata_contacts_contact_get_primary_website (new_contact)));
+
+	/* Events */
+	list = gdata_contacts_contact_get_events (new_contact);
+	g_assert_cmpuint (g_list_length (list), ==, 1);
+	g_assert (GDATA_IS_GCONTACT_EVENT (list->data));
+
+	/* Calendars */
+	list = gdata_contacts_contact_get_calendars (new_contact);
+	g_assert_cmpuint (g_list_length (list), ==, 1);
+	g_assert (GDATA_IS_GCONTACT_CALENDAR (list->data));
+
+	g_assert (GDATA_IS_GCONTACT_CALENDAR (gdata_contacts_contact_get_primary_calendar (new_contact)));
+
 	/* Extended properties */
 	g_assert_cmpstr (gdata_contacts_contact_get_extended_property (new_contact, "CALURI"), ==, "http://example.com/";);
 	g_assert (gdata_contacts_contact_get_extended_property (new_contact, "non-existent") == NULL);
@@ -323,6 +416,44 @@ test_insert_simple (gconstpointer service)
 
 	/* TODO: check entries and feed properties */
 
+	/* Try removing some things from the new contact and ensure it works */
+	gdata_contacts_contact_remove_all_email_addresses (new_contact);
+	g_assert (gdata_contacts_contact_get_email_addresses (new_contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_email_address (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_im_addresses (new_contact);
+	g_assert (gdata_contacts_contact_get_im_addresses (new_contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_im_address (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_phone_numbers (new_contact);
+	g_assert (gdata_contacts_contact_get_phone_numbers (new_contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_phone_number (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_postal_addresses (new_contact);
+	g_assert (gdata_contacts_contact_get_postal_addresses (new_contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_postal_address (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_organizations (new_contact);
+	g_assert (gdata_contacts_contact_get_organizations (new_contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_organization (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_jots (new_contact);
+	g_assert (gdata_contacts_contact_get_jots (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_relations (new_contact);
+	g_assert (gdata_contacts_contact_get_relations (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_websites (new_contact);
+	g_assert (gdata_contacts_contact_get_websites (new_contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_website (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_events (new_contact);
+	g_assert (gdata_contacts_contact_get_events (new_contact) == NULL);
+
+	gdata_contacts_contact_remove_all_calendars (new_contact);
+	g_assert (gdata_contacts_contact_get_calendars (new_contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_calendar (new_contact) == NULL);
+
 	g_free (edited);
 	g_object_unref (contact);
 	g_object_unref (new_contact);
@@ -442,6 +573,7 @@ static void
 test_parser_minimal (gconstpointer service)
 {
 	GDataContactsContact *contact;
+	GDate birthday;
 	GError *error = NULL;
 
 	g_test_bug ("580330");
@@ -471,6 +603,17 @@ test_parser_minimal (gconstpointer service)
 
 	/* TODO: Check the other properties */
 
+	g_assert (gdata_contacts_contact_get_nickname (contact) == NULL);
+	g_assert (gdata_contacts_contact_get_birthday (contact, &birthday) == FALSE);
+	g_assert (g_date_valid (&birthday) == FALSE);
+	g_assert (gdata_contacts_contact_get_jots (contact) == NULL);
+	g_assert (gdata_contacts_contact_get_relations (contact) == NULL);
+	g_assert (gdata_contacts_contact_get_websites (contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_website (contact) == NULL);
+	g_assert (gdata_contacts_contact_get_events (contact) == NULL);
+	g_assert (gdata_contacts_contact_get_calendars (contact) == NULL);
+	g_assert (gdata_contacts_contact_get_primary_calendar (contact) == NULL);
+
 	g_object_unref (contact);
 }
 
@@ -478,6 +621,8 @@ static void
 test_parser_normal (gconstpointer service)
 {
 	GDataContactsContact *contact;
+	GDate date;
+	GList *list;
 	GError *error = NULL;
 
 	contact = GDATA_CONTACTS_CONTACT (gdata_parsable_new_from_xml (GDATA_TYPE_CONTACTS_CONTACT,
@@ -500,6 +645,21 @@ test_parser_normal (gconstpointer service)
 				"deleted='true'/>"
 			"<gContact:groupMembershipInfo href='http://www.google.com/feeds/contacts/groups/jo%40gmail.com/base/1234b'/>"
 			"<gd:deleted/>"
+			"<gContact:nickname>Agent Smith</gContact:nickname>"
+			"<gContact:birthday when='2010-12-03'/>"
+			"<gContact:jot rel='home'>Moved house on 2010-02-14 to the North Pole.</gContact:jot>"
+			"<gContact:jot rel='user'>Owes me ten pounds.</gContact:jot>"
+			"<gContact:jot rel='other'></gContact:jot>" /* Empty on purpose */
+			"<gContact:relation rel='father'>Darth Vader</gContact:relation>"
+			"<gContact:relation label='Favourite singer'>Rob Halford</gContact:relation>"
+			"<gContact:website href='http://example.com' rel='home-page' label='Home tab #1' primary='true'/>"
+			"<gContact:website href='http://example.com' rel='work'/>"
+			"<gContact:website href='http://bar.com' rel='profile' primary='false'/>"
+			"<gContact:event rel='anniversary'><gd:when startTime='2010-03-04'/></gContact:event>"
+			"<gContact:event label='Foobar'><gd:when startTime='1900-01-01'/></gContact:event>"
+			"<gContact:calendarLink href='http://example.com/' rel='free-busy' primary='true'/>"
+			"<gContact:calendarLink href='http://example.com/' label='Gig list' primary='false'/>"
+			"<gContact:calendarLink href='http://foo.com/calendar' rel='home'/>"
 		"</entry>", -1, &error));
 	g_assert_no_error (error);
 	g_assert (GDATA_IS_CONTACTS_CONTACT (contact));
@@ -508,6 +668,123 @@ test_parser_normal (gconstpointer service)
 
 	/* TODO: Check the other properties */
 
+	g_assert_cmpstr (gdata_contacts_contact_get_nickname (contact), ==, "Agent Smith");
+
+	/* Birthday */
+	g_assert (gdata_contacts_contact_get_birthday (contact, &date) == TRUE);
+	g_assert (g_date_valid (&date) == TRUE);
+	g_assert_cmpuint (g_date_get_year (&date), ==, 2010);
+	g_assert_cmpuint (g_date_get_month (&date), ==, 12);
+	g_assert_cmpuint (g_date_get_day (&date), ==, 3);
+
+	/* Jots */
+	list = gdata_contacts_contact_get_jots (contact);
+	g_assert_cmpuint (g_list_length (list), ==, 3);
+
+	g_assert (GDATA_IS_GCONTACT_JOT (list->data));
+	g_assert_cmpstr (gdata_gcontact_jot_get_content (GDATA_GCONTACT_JOT (list->data)), ==, "Moved house on 2010-02-14 to the North Pole.");
+	g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (GDATA_GCONTACT_JOT (list->data)), ==, GDATA_GCONTACT_JOT_HOME);
+
+	list = list->next;
+	g_assert (GDATA_IS_GCONTACT_JOT (list->data));
+	g_assert_cmpstr (gdata_gcontact_jot_get_content (GDATA_GCONTACT_JOT (list->data)), ==, "Owes me ten pounds.");
+	g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (GDATA_GCONTACT_JOT (list->data)), ==, GDATA_GCONTACT_JOT_USER);
+
+	list = list->next;
+	g_assert (GDATA_IS_GCONTACT_JOT (list->data));
+	g_assert (gdata_gcontact_jot_get_content (GDATA_GCONTACT_JOT (list->data)) == NULL);
+	g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (GDATA_GCONTACT_JOT (list->data)), ==, GDATA_GCONTACT_JOT_OTHER);
+
+	/* Relations */
+	list = gdata_contacts_contact_get_relations (contact);
+	g_assert_cmpuint (g_list_length (list), ==, 2);
+
+	g_assert (GDATA_IS_GCONTACT_RELATION (list->data));
+	g_assert_cmpstr (gdata_gcontact_relation_get_name (GDATA_GCONTACT_RELATION (list->data)), ==, "Darth Vader");
+	g_assert_cmpstr (gdata_gcontact_relation_get_relation_type (GDATA_GCONTACT_RELATION (list->data)), ==, GDATA_GCONTACT_RELATION_FATHER);
+	g_assert (gdata_gcontact_relation_get_label (GDATA_GCONTACT_RELATION (list->data)) == NULL);
+
+	list = list->next;
+	g_assert (GDATA_IS_GCONTACT_RELATION (list->data));
+	g_assert_cmpstr (gdata_gcontact_relation_get_name (GDATA_GCONTACT_RELATION (list->data)), ==, "Rob Halford");
+	g_assert (gdata_gcontact_relation_get_relation_type (GDATA_GCONTACT_RELATION (list->data)) == NULL);
+	g_assert_cmpstr (gdata_gcontact_relation_get_label (GDATA_GCONTACT_RELATION (list->data)), ==, "Favourite singer");
+
+	/* Websites */
+	list = gdata_contacts_contact_get_websites (contact);
+	g_assert_cmpuint (g_list_length (list), ==, 3);
+
+	g_assert (GDATA_IS_GCONTACT_WEBSITE (list->data));
+	g_assert_cmpstr (gdata_gcontact_website_get_uri (GDATA_GCONTACT_WEBSITE (list->data)), ==, "http://example.com";);
+	g_assert_cmpstr (gdata_gcontact_website_get_relation_type (GDATA_GCONTACT_WEBSITE (list->data)), ==, GDATA_GCONTACT_WEBSITE_HOME_PAGE);
+	g_assert_cmpstr (gdata_gcontact_website_get_label (GDATA_GCONTACT_WEBSITE (list->data)), ==, "Home tab #1");
+	g_assert (gdata_gcontact_website_is_primary (GDATA_GCONTACT_WEBSITE (list->data)) == TRUE);
+
+	g_assert (gdata_contacts_contact_get_primary_website (contact) == list->data);
+
+	list = list->next;
+	g_assert (GDATA_IS_GCONTACT_WEBSITE (list->data));
+	g_assert_cmpstr (gdata_gcontact_website_get_uri (GDATA_GCONTACT_WEBSITE (list->data)), ==, "http://example.com";);
+	g_assert_cmpstr (gdata_gcontact_website_get_relation_type (GDATA_GCONTACT_WEBSITE (list->data)), ==, GDATA_GCONTACT_WEBSITE_WORK);
+	g_assert (gdata_gcontact_website_get_label (GDATA_GCONTACT_WEBSITE (list->data)) == NULL);
+	g_assert (gdata_gcontact_website_is_primary (GDATA_GCONTACT_WEBSITE (list->data)) == FALSE);
+
+	list = list->next;
+	g_assert (GDATA_IS_GCONTACT_WEBSITE (list->data));
+	g_assert_cmpstr (gdata_gcontact_website_get_uri (GDATA_GCONTACT_WEBSITE (list->data)), ==, "http://bar.com";);
+	g_assert_cmpstr (gdata_gcontact_website_get_relation_type (GDATA_GCONTACT_WEBSITE (list->data)), ==, GDATA_GCONTACT_WEBSITE_PROFILE);
+	g_assert (gdata_gcontact_website_get_label (GDATA_GCONTACT_WEBSITE (list->data)) == NULL);
+	g_assert (gdata_gcontact_website_is_primary (GDATA_GCONTACT_WEBSITE (list->data)) == FALSE);
+
+	/* Events */
+	list = gdata_contacts_contact_get_events (contact);
+	g_assert_cmpuint (g_list_length (list), ==, 2);
+
+	g_assert (GDATA_IS_GCONTACT_EVENT (list->data));
+	gdata_gcontact_event_get_date (GDATA_GCONTACT_EVENT (list->data), &date);
+	g_assert (g_date_valid (&date) == TRUE);
+	g_assert_cmpuint (g_date_get_year (&date), ==, 2010);
+	g_assert_cmpuint (g_date_get_month (&date), ==, 3);
+	g_assert_cmpuint (g_date_get_day (&date), ==, 4);
+	g_assert_cmpstr (gdata_gcontact_event_get_relation_type (GDATA_GCONTACT_EVENT (list->data)), ==, GDATA_GCONTACT_EVENT_ANNIVERSARY);
+	g_assert (gdata_gcontact_event_get_label (GDATA_GCONTACT_EVENT (list->data)) == NULL);
+
+	list = list->next;
+	g_assert (GDATA_IS_GCONTACT_EVENT (list->data));
+	gdata_gcontact_event_get_date (GDATA_GCONTACT_EVENT (list->data), &date);
+	g_assert (g_date_valid (&date) == TRUE);
+	g_assert_cmpuint (g_date_get_year (&date), ==, 1900);
+	g_assert_cmpuint (g_date_get_month (&date), ==, 1);
+	g_assert_cmpuint (g_date_get_day (&date), ==, 1);
+	g_assert (gdata_gcontact_event_get_relation_type (GDATA_GCONTACT_EVENT (list->data)) == NULL);
+	g_assert_cmpstr (gdata_gcontact_event_get_label (GDATA_GCONTACT_EVENT (list->data)), ==, "Foobar");
+
+	/* Calendars */
+	list = gdata_contacts_contact_get_calendars (contact);
+	g_assert_cmpuint (g_list_length (list), ==, 3);
+
+	g_assert (GDATA_IS_GCONTACT_CALENDAR (list->data));
+	g_assert_cmpstr (gdata_gcontact_calendar_get_uri (GDATA_GCONTACT_CALENDAR (list->data)), ==, "http://example.com/";);
+	g_assert_cmpstr (gdata_gcontact_calendar_get_relation_type (GDATA_GCONTACT_CALENDAR (list->data)), ==, GDATA_GCONTACT_CALENDAR_FREE_BUSY);
+	g_assert (gdata_gcontact_calendar_get_label (GDATA_GCONTACT_CALENDAR (list->data)) == NULL);
+	g_assert (gdata_gcontact_calendar_is_primary (GDATA_GCONTACT_CALENDAR (list->data)) == TRUE);
+
+	g_assert (gdata_contacts_contact_get_primary_calendar (contact) == list->data);
+
+	list = list->next;
+	g_assert (GDATA_IS_GCONTACT_CALENDAR (list->data));
+	g_assert_cmpstr (gdata_gcontact_calendar_get_uri (GDATA_GCONTACT_CALENDAR (list->data)), ==, "http://example.com/";);
+	g_assert (gdata_gcontact_calendar_get_relation_type (GDATA_GCONTACT_CALENDAR (list->data)) == NULL);
+	g_assert_cmpstr (gdata_gcontact_calendar_get_label (GDATA_GCONTACT_CALENDAR (list->data)), ==, "Gig list");
+	g_assert (gdata_gcontact_calendar_is_primary (GDATA_GCONTACT_CALENDAR (list->data)) == FALSE);
+
+	list = list->next;
+	g_assert (GDATA_IS_GCONTACT_CALENDAR (list->data));
+	g_assert_cmpstr (gdata_gcontact_calendar_get_uri (GDATA_GCONTACT_CALENDAR (list->data)), ==, "http://foo.com/calendar";);
+	g_assert_cmpstr (gdata_gcontact_calendar_get_relation_type (GDATA_GCONTACT_CALENDAR (list->data)), ==, GDATA_GCONTACT_CALENDAR_HOME);
+	g_assert (gdata_gcontact_calendar_get_label (GDATA_GCONTACT_CALENDAR (list->data)) == NULL);
+	g_assert (gdata_gcontact_calendar_is_primary (GDATA_GCONTACT_CALENDAR (list->data)) == FALSE);
+
 	g_object_unref (contact);
 }
 
@@ -556,6 +833,30 @@ test_parser_error_handling (gconstpointer service)
 	TEST_XML_ERROR_HANDLING ("<gContact:groupMembershipInfo/>");
 	TEST_XML_ERROR_HANDLING ("<gContact:groupMembershipInfo href='http://foobar.com/base/1234b' deleted='maybe'/>");
 
+	/* gContact:nickname */
+	TEST_XML_ERROR_HANDLING ("<gContact:nickname/>"); /* missing content */
+	TEST_XML_ERROR_HANDLING ("<gContact:nickname>Nickname 1</gContact:nickname><gContact:nickname>Duplicate!</gContact:nickname>"); /* duplicate */
+
+	/* gContact:birthday */
+	TEST_XML_ERROR_HANDLING ("<gContact:birthday/>"); /* missing "when" attribute */
+	TEST_XML_ERROR_HANDLING ("<gContact:birthday when='foobar'/>"); /* invalid date */
+	TEST_XML_ERROR_HANDLING ("<gContact:birthday when='2000-01-01'/><gContact:birthday when='--01-01'/>"); /* duplicate */
+
+	/* gContact:jot */
+	TEST_XML_ERROR_HANDLING ("<gContact:jot/>");
+
+	/* gContact:relation */
+	TEST_XML_ERROR_HANDLING ("<gContact:relation/>");
+
+	/* gContact:website */
+	TEST_XML_ERROR_HANDLING ("<gContact:website/>");
+
+	/* gContact:event */
+	TEST_XML_ERROR_HANDLING ("<gContact:event/>");
+
+	/* gContact:calendar */
+	TEST_XML_ERROR_HANDLING ("<gContact:calendarLink/>");
+
 #undef TEST_XML_ERROR_HANDLING
 }
 
diff --git a/gdata/tests/general.c b/gdata/tests/general.c
index c8b1621..5ed1e86 100644
--- a/gdata/tests/general.c
+++ b/gdata/tests/general.c
@@ -2334,6 +2334,427 @@ test_media_thumbnail (void)
 	g_object_unref (thumbnail);
 }
 
+static void
+test_gcontact_calendar (void)
+{
+	GDataGContactCalendar *calendar, *calendar2;
+	gchar *xml;
+	GError *error = NULL;
+
+	calendar = GDATA_GCONTACT_CALENDAR (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_CALENDAR,
+		"<gContact:calendarLink xmlns:gContact='http://schemas.google.com/contact/2008' rel='work' primary='true' "
+			"href='http://calendar.com/'/>", -1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_CALENDAR (calendar));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	g_assert_cmpstr (gdata_gcontact_calendar_get_uri (calendar), ==, "http://calendar.com/";);
+	g_assert_cmpstr (gdata_gcontact_calendar_get_relation_type (calendar), ==, GDATA_GCONTACT_CALENDAR_WORK);
+	g_assert (gdata_gcontact_calendar_get_label (calendar) == NULL);
+	g_assert (gdata_gcontact_calendar_is_primary (calendar) == TRUE);
+
+	/* Compare it against another identical calendar */
+	calendar2 = gdata_gcontact_calendar_new ("http://calendar.com/";, GDATA_GCONTACT_CALENDAR_WORK, NULL, TRUE);
+	g_assert_cmpint (gdata_gcontact_calendar_compare (calendar, calendar2), ==, 0);
+
+	/* â?¦and a different one */
+	gdata_gcontact_calendar_set_uri (calendar2, "http://calendar.somewhereelse.com/";);
+	g_assert_cmpint (gdata_gcontact_calendar_compare (calendar, calendar2), !=, 0);
+	g_object_unref (calendar2);
+
+	/* More comparisons */
+	g_assert_cmpint (gdata_gcontact_calendar_compare (calendar, NULL), ==, 1);
+	g_assert_cmpint (gdata_gcontact_calendar_compare (NULL, calendar), ==, -1);
+	g_assert_cmpint (gdata_gcontact_calendar_compare (NULL, NULL), ==, 0);
+	g_assert_cmpint (gdata_gcontact_calendar_compare (calendar, calendar), ==, 0);
+
+	/* Check the outputted XML is the same */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (calendar));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:calendarLink xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+				"href='http://calendar.com/' rel='work' primary='true'/>");
+	g_free (xml);
+	g_object_unref (calendar);
+
+	/* Now parse a calendar with less information available */
+	calendar = GDATA_GCONTACT_CALENDAR (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_CALENDAR,
+		"<gContact:calendarLink xmlns:gContact='http://schemas.google.com/contact/2008' href='http://example.com/' label='&lt;a&gt;'/>",
+		-1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_CALENDAR (calendar));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	g_assert_cmpstr (gdata_gcontact_calendar_get_uri (calendar), ==, "http://example.com/";);
+	g_assert (gdata_gcontact_calendar_get_relation_type (calendar) == NULL);
+	g_assert_cmpstr (gdata_gcontact_calendar_get_label (calendar), ==, "<a>");
+	g_assert (gdata_gcontact_calendar_is_primary (calendar) == FALSE);
+
+	/* Check the outputted XML is still OK */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (calendar));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:calendarLink xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+				"href='http://example.com/' label='&lt;a&gt;' primary='false'/>");
+	g_free (xml);
+	g_object_unref (calendar);
+}
+
+static void
+test_gcontact_calendar_error_handling (void)
+{
+	GDataGContactCalendar *calendar;
+	GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) calendar = GDATA_GCONTACT_CALENDAR (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_CALENDAR,\
+		"<gContact:calendarLink xmlns:gContact='http://schemas.google.com/contact/2008' "\
+			x\
+		"/>", -1, &error));\
+	g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+	g_assert (calendar == NULL);\
+	g_clear_error (&error)
+
+	TEST_XML_ERROR_HANDLING ("rel='work'"); /* no href */
+	TEST_XML_ERROR_HANDLING ("rel='work' href=''"); /* empty href */
+	TEST_XML_ERROR_HANDLING ("href='http://example.com/'"); /* no rel or label */
+	TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel=''"); /* empty rel */
+	TEST_XML_ERROR_HANDLING ("href='http://example.com/' label=''"); /* empty label */
+	TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel='other' label='Other'"); /* rel and label */
+	TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel='home' primary='not a boolean'"); /* invalid primary */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
+static void
+test_gcontact_event (void)
+{
+	GDataGContactEvent *event, *event2;
+	GDate date;
+	gchar *xml;
+	GError *error = NULL;
+
+	event = GDATA_GCONTACT_EVENT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_EVENT,
+		"<gContact:event xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' rel='other'>"
+			"<gd:when startTime='2004-03-12'/>"
+		"</gContact:event>", -1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_EVENT (event));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	gdata_gcontact_event_get_date (event, &date);
+	g_assert (g_date_valid (&date) == TRUE);
+	g_assert_cmpuint (g_date_get_year (&date), ==, 2004);
+	g_assert_cmpuint (g_date_get_month (&date), ==, 3);
+	g_assert_cmpuint (g_date_get_day (&date), ==, 12);
+	g_assert_cmpstr (gdata_gcontact_event_get_relation_type (event), ==, GDATA_GCONTACT_EVENT_OTHER);
+	g_assert (gdata_gcontact_event_get_label (event) == NULL);
+
+	/* Try creating another event from scratch */
+	event2 = gdata_gcontact_event_new (&date, GDATA_GCONTACT_EVENT_OTHER, NULL);
+	g_assert (GDATA_IS_GCONTACT_EVENT (event2));
+	g_object_unref (event2);
+
+	/* Check the outputted XML is the same */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (event));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:event xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005' "
+			   "xmlns:gContact='http://schemas.google.com/contact/2008' rel='other'>"
+				"<gd:when startTime='2004-03-12'/>"
+			 "</gContact:event>");
+	g_free (xml);
+	g_object_unref (event);
+
+	/* Now parse an event with different information available */
+	event = GDATA_GCONTACT_EVENT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_EVENT,
+		"<gContact:event xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' "
+		  "label='&lt;a&gt;'>"
+			"<gd:when startTime='2000-01-01'/>"
+		"</gContact:event>", -1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_EVENT (event));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	gdata_gcontact_event_get_date (event, &date);
+	g_assert (g_date_valid (&date) == TRUE);
+	g_assert_cmpuint (g_date_get_year (&date), ==, 2000);
+	g_assert_cmpuint (g_date_get_month (&date), ==, 1);
+	g_assert_cmpuint (g_date_get_day (&date), ==, 1);
+	g_assert (gdata_gcontact_event_get_relation_type (event) == NULL);
+	g_assert_cmpstr (gdata_gcontact_event_get_label (event), ==, "<a>");
+
+	/* Check the outputted XML is still OK */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (event));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:event xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005' "
+			   "xmlns:gContact='http://schemas.google.com/contact/2008' label='&lt;a&gt;'>"
+				"<gd:when startTime='2000-01-01'/>"
+			 "</gContact:event>");
+	g_free (xml);
+	g_object_unref (event);
+}
+
+static void
+test_gcontact_event_error_handling (void)
+{
+	GDataGContactEvent *event;
+	GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) event = GDATA_GCONTACT_EVENT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_EVENT,\
+		"<gContact:event xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' "\
+			x\
+		"</gContact:event>", -1, &error));\
+	g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+	g_assert (event == NULL);\
+	g_clear_error (&error)
+
+	TEST_XML_ERROR_HANDLING ("rel='anniversary'>"); /* no gd:when */
+	TEST_XML_ERROR_HANDLING ("rel='anniversary'><gd:when/>"); /* no startTime */
+	TEST_XML_ERROR_HANDLING ("rel='anniversary'><gd:when startTime='foobar'/>"); /* invalid startTime */
+	TEST_XML_ERROR_HANDLING ("rel='anniversary'><gd:when startTime='2001-12-41'/>");
+	TEST_XML_ERROR_HANDLING ("rel='anniversary'><gd:when startTime='2010-03-25T22:01Z'/>");
+	TEST_XML_ERROR_HANDLING ("><gd:when startTime='2000-01-01'/>"); /* no rel or label */
+	TEST_XML_ERROR_HANDLING ("rel=''><gd:when startTime='2000-01-01'/>"); /* empty rel */
+	TEST_XML_ERROR_HANDLING ("label=''><gd:when startTime='2000-01-01'/>"); /* empty label */
+	TEST_XML_ERROR_HANDLING ("rel='other' label='Other'><gd:when startTime='2000-01-01'/>"); /* rel and label */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
+static void
+test_gcontact_jot (void)
+{
+	GDataGContactJot *jot, *jot2;
+	gchar *xml;
+	GError *error = NULL;
+
+	jot = GDATA_GCONTACT_JOT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_JOT,
+		"<gContact:jot xmlns:gContact='http://schemas.google.com/contact/2008' rel='user'>They like &lt;angles&gt;.</gContact:jot>",
+		-1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_JOT (jot));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (jot), ==, GDATA_GCONTACT_JOT_USER);
+	g_assert_cmpstr (gdata_gcontact_jot_get_content (jot), ==, "They like <angles>.");
+
+	/* Try creating another jot from scratch */
+	jot2 = gdata_gcontact_jot_new ("friend,local,oss", GDATA_GCONTACT_JOT_KEYWORDS);
+	g_assert (GDATA_IS_GCONTACT_JOT (jot2));
+	g_object_unref (jot2);
+
+	/* Check the outputted XML is the same */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (jot));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:jot xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' rel='user'>"
+				"They like &lt;angles&gt;.</gContact:jot>");
+	g_free (xml);
+	g_object_unref (jot);
+
+	/* Now parse a jot with different information available */
+	jot = GDATA_GCONTACT_JOT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_JOT,
+		"<gContact:jot xmlns:gContact='http://schemas.google.com/contact/2008' rel='other'/>", -1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_JOT (jot));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	g_assert_cmpstr (gdata_gcontact_jot_get_relation_type (jot), ==, GDATA_GCONTACT_JOT_OTHER);
+	g_assert (gdata_gcontact_jot_get_content (jot) == NULL);
+
+	/* Check the outputted XML is still OK */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (jot));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:jot xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' rel='other'/>");
+	g_free (xml);
+	g_object_unref (jot);
+}
+
+static void
+test_gcontact_jot_error_handling (void)
+{
+	GDataGContactJot *jot;
+	GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) jot = GDATA_GCONTACT_JOT (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_JOT,\
+		"<gContact:jot xmlns:gContact='http://schemas.google.com/contact/2008' "\
+			x\
+		"</gContact:jot>", -1, &error));\
+	g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+	g_assert (jot == NULL);\
+	g_clear_error (&error)
+
+	TEST_XML_ERROR_HANDLING (">Content"); /* no rel */
+	TEST_XML_ERROR_HANDLING ("rel=''>Content"); /* empty rel */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
+static void
+test_gcontact_relation (void)
+{
+	GDataGContactRelation *relation, *relation2;
+	gchar *xml;
+	GError *error = NULL;
+
+	relation = GDATA_GCONTACT_RELATION (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_RELATION,
+		"<gContact:relation xmlns:gContact='http://schemas.google.com/contact/2008' rel='child'>Fred</gContact:relation>", -1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_RELATION (relation));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	g_assert_cmpstr (gdata_gcontact_relation_get_name (relation), ==, "Fred");
+	g_assert_cmpstr (gdata_gcontact_relation_get_relation_type (relation), ==, GDATA_GCONTACT_RELATION_CHILD);
+	g_assert (gdata_gcontact_relation_get_label (relation) == NULL);
+
+	/* Try creating another relation from scratch */
+	relation2 = gdata_gcontact_relation_new ("Brian", GDATA_GCONTACT_RELATION_RELATIVE, NULL);
+	g_assert (GDATA_IS_GCONTACT_RELATION (relation2));
+	g_object_unref (relation2);
+
+	/* Check the outputted XML is the same */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (relation));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:relation xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' rel='child'>"
+				"Fred</gContact:relation>");
+	g_free (xml);
+	g_object_unref (relation);
+
+	/* Now parse a relation with different information available */
+	relation = GDATA_GCONTACT_RELATION (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_RELATION,
+		"<gContact:relation xmlns:gContact='http://schemas.google.com/contact/2008' label='&lt;a&gt;'>Sid</gContact:relation>", -1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_RELATION (relation));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	g_assert_cmpstr (gdata_gcontact_relation_get_name (relation), ==, "Sid");
+	g_assert (gdata_gcontact_relation_get_relation_type (relation) == NULL);
+	g_assert_cmpstr (gdata_gcontact_relation_get_label (relation), ==, "<a>");
+
+	/* Check the outputted XML is still OK */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (relation));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:relation xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+			   "label='&lt;a&gt;'>Sid</gContact:relation>");
+	g_free (xml);
+	g_object_unref (relation);
+}
+
+static void
+test_gcontact_relation_error_handling (void)
+{
+	GDataGContactRelation *relation;
+	GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) relation = GDATA_GCONTACT_RELATION (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_RELATION,\
+		"<gContact:relation xmlns:gContact='http://schemas.google.com/contact/2008' "\
+			x\
+		"</gContact:relation>", -1, &error));\
+	g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+	g_assert (relation == NULL);\
+	g_clear_error (&error)
+
+	TEST_XML_ERROR_HANDLING ("rel='spouse'>"); /* no name */
+	TEST_XML_ERROR_HANDLING (">Brian"); /* no rel or label */
+	TEST_XML_ERROR_HANDLING ("rel=''>Brian"); /* empty rel */
+	TEST_XML_ERROR_HANDLING ("label=''>Brian"); /* empty label */
+	TEST_XML_ERROR_HANDLING ("rel='sister' label='Older sister'>Brian"); /* rel and label */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
+static void
+test_gcontact_website (void)
+{
+	GDataGContactWebsite *website, *website2;
+	gchar *xml;
+	GError *error = NULL;
+
+	website = GDATA_GCONTACT_WEBSITE (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_WEBSITE,
+		"<gContact:website xmlns:gContact='http://schemas.google.com/contact/2008' href='http://example.com/' rel='work' primary='true' "
+			"label='&lt;Markup&gt; blog'/>", -1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_WEBSITE (website));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	g_assert_cmpstr (gdata_gcontact_website_get_uri (website), ==, "http://example.com/";);
+	g_assert_cmpstr (gdata_gcontact_website_get_relation_type (website), ==, GDATA_GCONTACT_WEBSITE_WORK);
+	g_assert_cmpstr (gdata_gcontact_website_get_label (website), ==, "<Markup> blog");
+	g_assert (gdata_gcontact_website_is_primary (website) == TRUE);
+
+	/* Compare it against another identical website */
+	website2 = gdata_gcontact_website_new ("http://example.com/";, GDATA_GCONTACT_WEBSITE_WORK, "<Markup> blog", TRUE);
+	g_assert_cmpint (gdata_gcontact_website_compare (website, website2), ==, 0);
+
+	/* â?¦and a different one */
+	gdata_gcontact_website_set_uri (website2, "http://somewhereelse.com/";);
+	g_assert_cmpint (gdata_gcontact_website_compare (website, website2), !=, 0);
+	g_object_unref (website2);
+
+	/* More comparisons */
+	g_assert_cmpint (gdata_gcontact_website_compare (website, NULL), ==, 1);
+	g_assert_cmpint (gdata_gcontact_website_compare (NULL, website), ==, -1);
+	g_assert_cmpint (gdata_gcontact_website_compare (NULL, NULL), ==, 0);
+	g_assert_cmpint (gdata_gcontact_website_compare (website, website), ==, 0);
+
+	/* Check the outputted XML is the same */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (website));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:website xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+				"href='http://example.com/' rel='work' label='&lt;Markup&gt; blog' primary='true'/>");
+	g_free (xml);
+	g_object_unref (website);
+
+	/* Now parse a website with less information available */
+	website = GDATA_GCONTACT_WEBSITE (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_WEBSITE,
+		"<gContact:website xmlns:gContact='http://schemas.google.com/contact/2008' href='http://test.com/' rel='ftp'/>", -1, &error));
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_GCONTACT_WEBSITE (website));
+	g_clear_error (&error);
+
+	/* Check the properties */
+	g_assert_cmpstr (gdata_gcontact_website_get_uri (website), ==, "http://test.com/";);
+	g_assert_cmpstr (gdata_gcontact_website_get_relation_type (website), ==, GDATA_GCONTACT_WEBSITE_FTP);
+	g_assert (gdata_gcontact_website_get_label (website) == NULL);
+	g_assert (gdata_gcontact_website_is_primary (website) == FALSE);
+
+	/* Check the outputted XML is still OK */
+	xml = gdata_parsable_get_xml (GDATA_PARSABLE (website));
+	g_assert_cmpstr (xml, ==,
+			 "<gContact:website xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' "
+				"href='http://test.com/' rel='ftp' primary='false'/>");
+	g_free (xml);
+	g_object_unref (website);
+}
+
+static void
+test_gcontact_website_error_handling (void)
+{
+	GDataGContactWebsite *website;
+	GError *error = NULL;
+
+#define TEST_XML_ERROR_HANDLING(x) website = GDATA_GCONTACT_WEBSITE (gdata_parsable_new_from_xml (GDATA_TYPE_GCONTACT_WEBSITE,\
+		"<gContact:website xmlns:gContact='http://schemas.google.com/contact/2008' "\
+			x\
+		"/>", -1, &error));\
+	g_assert_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR);\
+	g_assert (website == NULL);\
+	g_clear_error (&error)
+
+	TEST_XML_ERROR_HANDLING ("rel='work'"); /* no href */
+	TEST_XML_ERROR_HANDLING ("rel='work' href=''"); /* empty href */
+	TEST_XML_ERROR_HANDLING ("href='http://example.com/'"); /* no rel */
+	TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel=''"); /* empty rel */
+	TEST_XML_ERROR_HANDLING ("href='http://example.com/' rel='profile' primary='not a boolean'"); /* invalid primary */
+
+#undef TEST_XML_ERROR_HANDLING
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -2380,5 +2801,16 @@ main (int argc, char *argv[])
 	/*g_test_add_data_func ("/media/thumbnail/parse_time", "", test_media_thumbnail_parse_time);
 	g_test_add_data_func ("/media/thumbnail/parse_time", "de_DE", test_media_thumbnail_parse_time);*/
 
+	g_test_add_func ("/gcontact/calendar", test_gcontact_calendar);
+	g_test_add_func ("/gcontact/calendar/error_handling", test_gcontact_calendar_error_handling);
+	g_test_add_func ("/gcontact/event", test_gcontact_event);
+	g_test_add_func ("/gcontact/event/error_handling", test_gcontact_event_error_handling);
+	g_test_add_func ("/gcontact/jot", test_gcontact_jot);
+	g_test_add_func ("/gcontact/jot/error_handling", test_gcontact_jot_error_handling);
+	g_test_add_func ("/gcontact/relation", test_gcontact_relation);
+	g_test_add_func ("/gcontact/relation/error_handling", test_gcontact_relation_error_handling);
+	g_test_add_func ("/gcontact/website", test_gcontact_website);
+	g_test_add_func ("/gcontact/website/error_handling", test_gcontact_website_error_handling);
+
 	return g_test_run ();
 }



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