diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..90c2def
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,52 @@
+*.[oa]
+*.l[oa]
+.deps
+.libs
+*.pyc
+*.*~
+
+.anjuta*
+
+autom4te*.cache
+stamp-h
+stamp-h.in
+stamp-h1
+libtool
+ltmain.sh
+intltool*
+compile
+confdefs.h
+configure
+config.*
+aclocal.m4
+Makefile
+Makefile.in
+install-sh
+missing
+mkinstalldirs
+py-compile
+INSTALL
+depcomp
+gtk-doc.make
+gnome-doc-utils.make
+libdmapsharing.pc
+libdmapsharing-1.9.pc
+
+#
+*.desktop
+
+#
+docs/*.txt
+
+#
+m4/*.m4
+
+#
+tests/test-dmap-server
+tests/test-dmap-client
+
+#
+libdmapsharing/dmap-marshal.h
+libdmapsharing/dmap-marshal.c
+libdmapsharing/dmap-config.h
+
diff --git a/config.h.in b/config.h.in
index 4e5b5f2..d06e6a1 100644
--- a/config.h.in
+++ b/config.h.in
@@ -82,6 +82,9 @@
 /* Define to the one symbol short name of this package. */
 #undef PACKAGE_TARNAME
 
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
diff --git a/configure.ac b/configure.ac
index 141c479..9604dca 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,6 +4,8 @@ dnl when going to/from release please set the nano (fourth number) right !
 dnl releases only do Wall, cvs and prerelease does Werror too
 AS_VERSION(libdmapsharing, LIBDMAPSHARING, 2, 0, 0, LIBDMAPSHARING_CVS="no", LIBDMAPSHARING_CVS="yes")
 
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
+
 AM_INIT_AUTOMAKE
 
 LIBDMAPSHARING_MAJORMINOR=$LIBDMAPSHARING_MAJOR_VERSION.$LIBDMAPSHARING_MINOR_VERSION
diff --git a/docs/Makefile.am b/docs/Makefile.am
index bd6f455..38e8707 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -48,8 +48,8 @@ FIXXREF_OPTIONS=
 # Used for dependencies. The docs will be rebuilt if any of these change.
 # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
 # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
-HFILE_GLOB=
-CFILE_GLOB=
+HFILE_GLOB=../libdmapsharing/*.h
+CFILE_GLOB=../libdmapsharing/*.c
 
 # Extra header to include when scanning, which are not under DOC_SOURCE_DIR
 # e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
diff --git a/docs/libdmapsharing-docs.sgml b/docs/libdmapsharing-docs.sgml
index 9fd9c39..1cd2ee4 100644
--- a/docs/libdmapsharing-docs.sgml
+++ b/docs/libdmapsharing-docs.sgml
@@ -51,6 +51,7 @@
     <xi:include href="xml/dpap-connection.xml"/>
     <xi:include href="xml/dpap-record.xml"/>
     <xi:include href="xml/dpap-share.xml"/>
+    <xi:include href="xml/dacp-share.xml"/>
   </chapter>
 
   <chapter id="object-tree">
diff --git a/libdmapsharing/Makefile.am b/libdmapsharing/Makefile.am
index 920648e..a8f06a4 100644
--- a/libdmapsharing/Makefile.am
+++ b/libdmapsharing/Makefile.am
@@ -6,6 +6,7 @@ libdmapsharing_la_SOURCES = \
 	$(BUILT_SOURCES) \
 	daap-record.c \
 	daap-share.c \
+	dacp-share.c \
 	dmap-connection.c \
 	dmap-container-db.c \
 	dmap-container-record.c \
@@ -64,6 +65,7 @@ libdmapsharingincludedir = \
 libdmapsharinginclude_HEADERS = \
 	daap-record.h \
 	daap-share.h \
+	dacp-share.h \
 	dmap.h \
 	dmap-connection.h \
 	dmap-container-db.h \
diff --git a/libdmapsharing/daap-record.c b/libdmapsharing/daap-record.c
index 54e764b..6c3cd3a 100644
--- a/libdmapsharing/daap-record.c
+++ b/libdmapsharing/daap-record.c
@@ -44,10 +44,20 @@ daap_record_init (DAAPRecordInterface *iface)
 					     "Unknown",
 					     G_PARAM_READWRITE));
 
+		//FIXME: This is actually an enum
+		g_object_interface_install_property (iface,
+			g_param_spec_int ("mediakind",
+			                  "Media kind",
+			                  "Media kind",
+			                  0,
+			                  G_MAXINT,
+			                  1,
+			                  G_PARAM_READWRITE));
+
 		/* NOTE: experimenting with matching property name with
 		 * DAAP protocol keyword to make some code simpler. */
 		g_object_interface_install_property (iface,
-			g_param_spec_string ("daap.songalbum",
+			g_param_spec_string ("songalbum",
 					     "Album name",
 					     "Album name",
 					     "Unknown",
@@ -61,7 +71,7 @@ daap_record_init (DAAPRecordInterface *iface)
 					     G_PARAM_READWRITE));
 
 		g_object_interface_install_property (iface,
-			g_param_spec_string ("daap.songartist",
+			g_param_spec_string ("songartist",
 					     "Song artist",
 					     "Song artist",
 					     "Unknown",
@@ -75,7 +85,7 @@ daap_record_init (DAAPRecordInterface *iface)
 					     G_PARAM_READWRITE));
 
 		g_object_interface_install_property (iface,
-			g_param_spec_string ("daap.songgenre",
+			g_param_spec_string ("songgenre",
 					     "Song genre",
 					     "Song genre",
 					     "Unknown",
diff --git a/libdmapsharing/daap-share.c b/libdmapsharing/daap-share.c
index cd423b9..2504a60 100644
--- a/libdmapsharing/daap-share.c
+++ b/libdmapsharing/daap-share.c
@@ -560,7 +560,7 @@ add_entry_to_mlcl (gpointer id,
 	*/
 	if (_dmap_share_client_requested (mb->bits, SONG_ALBUM)) {
 		gchar *album;
-		g_object_get (record, "daap.songalbum", &album, NULL);
+		g_object_get (record, "songalbum", &album, NULL);
 		dmap_structure_add (mlit, DMAP_CC_ASAL, album);
 		g_free (album);
 	}
@@ -568,7 +568,7 @@ add_entry_to_mlcl (gpointer id,
 		dmap_structure_add (mlit, DMAP_CC_AGRP, "");
 	if (_dmap_share_client_requested (mb->bits, SONG_ARTIST)) {
 		gchar *artist;
-		g_object_get (record, "daap.songartist", &artist, NULL);
+		g_object_get (record, "songartist", &artist, NULL);
 		dmap_structure_add (mlit, DMAP_CC_ASAR, artist);
 		g_free (artist);
 	}
@@ -618,7 +618,7 @@ add_entry_to_mlcl (gpointer id,
 	}
 	if (_dmap_share_client_requested (mb->bits, SONG_GENRE)) {
 		gchar *genre;
-		g_object_get (record, "daap.songgenre", &genre, NULL);
+		g_object_get (record, "songgenre", &genre, NULL);
 		dmap_structure_add (mlit, DMAP_CC_ASGN, genre);
 		g_free (genre);
 	}
@@ -688,7 +688,7 @@ static void
 genre_tabulator (gpointer id, DMAPRecord *record, GHashTable *ht)
 {
 	const gchar *genre;
-	g_object_get (record, "daap.songgenre", &genre, NULL);
+	g_object_get (record, "songgenre", &genre, NULL);
 	if (! g_hash_table_lookup (ht, genre))
 		g_hash_table_insert (ht, (gchar *) genre, NULL);
 }
@@ -697,7 +697,7 @@ static void
 artist_tabulator (gpointer id, DMAPRecord *record, GHashTable *ht)
 {
 	const gchar *artist;
-	g_object_get (record, "daap.songartist", &artist, NULL);
+	g_object_get (record, "songartist", &artist, NULL);
 	if (! g_hash_table_lookup (ht, artist))
 		g_hash_table_insert (ht, (gchar *) artist, NULL);
 }
@@ -706,7 +706,7 @@ static void
 album_tabulator (gpointer id, DMAPRecord *record, GHashTable *ht)
 {
 	const gchar *album;
-	g_object_get (record, "daap.songalbum", &album, NULL);
+	g_object_get (record, "songalbum", &album, NULL);
 	if (! g_hash_table_lookup (ht, album))
 		g_hash_table_insert (ht, (gchar *) album, NULL);
 }
diff --git a/libdmapsharing/dacp-share.c b/libdmapsharing/dacp-share.c
new file mode 100644
index 0000000..47ee100
--- /dev/null
+++ b/libdmapsharing/dacp-share.c
@@ -0,0 +1,952 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Implmentation of DACP (e.g., iTunes Remote) sharing
+ *
+ * Copyright (C) 2010 Alexandre Rosenfeld <airmind@gmail.com>
+ *
+ * This library 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "config.h"
+
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-uri.h>
+#include <libsoup/soup-server.h>
+
+#include <libdmapsharing/dmap.h>
+#include <libdmapsharing/dmap-marshal.h>
+#include <libdmapsharing/dmap-structure.h>
+
+#include <libdmapsharing/dmap-share.h>
+#include <libdmapsharing/dacp-share.h>
+
+static void dacp_share_set_property  (GObject *object,
+					 guint prop_id,
+					 const GValue *value,
+					 GParamSpec *pspec);
+static void dacp_share_get_property  (GObject *object,
+					 guint prop_id,
+					 GValue *value,
+				 	 GParamSpec *pspec);
+static void dacp_share_dispose	(GObject *object);
+const char *dacp_share_get_type_of_service (DMAPShare *share);
+void dacp_share_ctrl_int (DMAPShare *share,
+		      SoupServer        *server,
+		      SoupMessage       *message,
+		      const char        *path,
+		      GHashTable        *query,
+		      SoupClientContext *context);
+void dacp_share_login (DMAPShare *share,
+		  SoupServer        *server,
+		  SoupMessage       *message,
+		  const char        *path,
+		  GHashTable        *query,
+		  SoupClientContext *context);
+
+static gchar *dacp_share_pairing_code(DACPShare *share, gchar* pair_txt, gchar passcode[4]);
+
+#define DACP_TYPE_OF_SERVICE "_touch-able._tcp"
+#define DACP_PORT 3689
+
+struct DACPSharePrivate {
+	DMAPMdnsBrowser *mdns_browser;
+	
+	GHashTable *remotes;
+};
+
+/*
+ * Internal representation of a DACP remote.
+ */
+typedef struct {
+	gchar *host;
+	guint port;
+	gchar *pair_txt;
+	DMAPConnection *connection;
+} DACPRemoteInfo;
+
+#define DACP_SHARE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_DACP_SHARE, DACPSharePrivate))
+
+enum {
+	PROP_0,
+};
+
+enum {
+	REMOTE_FOUND,
+	REMOTE_LOST,
+	REMOTE_PAIRED,
+
+	LOOKUP_GUID,
+	ADD_GUID,
+	
+	PLAY_STATUS_UPDATE,
+	PLAY_PAUSE,
+	NEXT_ITEM,
+	PREV_ITEM,
+	NOW_PLAYING_ARTWORK,
+	GET_SHUFFLE_STATE,
+	SET_SHUFFLE_STATE,
+	GET_REPEAT_STATE,
+	SET_REPEAT_STATE,
+	GET_VOLUME,
+	SET_VOLUME,
+	LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (DACPShare, dacp_share, TYPE_DAAP_SHARE)
+
+#define G_TYPE_PLAY_STATUS_UPDATE_DATA (play_status_update_data_get_type ())
+
+static gpointer
+play_status_update_data_copy (gpointer boxed)
+{
+	const DACPPlayStatusUpdateData *data = (const DACPPlayStatusUpdateData*)boxed;
+	DACPPlayStatusUpdateData *copy = g_memdup (data, sizeof (DACPPlayStatusUpdateData));
+	copy->artist = g_strdup (data->artist);
+	copy->track = g_strdup (data->track);
+	copy->album = g_strdup (data->album);
+	copy->album_id = g_strdup (data->album_id);
+	return copy;
+}
+
+static void
+play_status_update_data_free (gpointer boxed)
+{
+	DACPPlayStatusUpdateData *data = (DACPPlayStatusUpdateData*)boxed;
+	g_free (data->track);
+	g_free (data->track);
+	g_free (data->album_id);
+	g_free (data->album_id);
+	g_free (data);
+}
+
+GType
+play_status_update_data_get_type (void)
+{
+	static GType type_id = 0;
+	if (!type_id)
+		type_id = g_boxed_type_register_static(g_intern_static_string("DACPPlayStatusUpdateData"),
+			(GBoxedCopyFunc) play_status_update_data_copy,
+			(GBoxedFreeFunc) play_status_update_data_free);
+	return type_id;
+}
+
+static void
+dacp_share_class_init (DACPShareClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	DMAPShareClass *dmap_class = DMAP_SHARE_CLASS (object_class);
+
+	object_class->get_property = dacp_share_get_property;
+	object_class->set_property = dacp_share_set_property;
+	object_class->dispose = dacp_share_dispose;
+
+	dmap_class->get_type_of_service  = dacp_share_get_type_of_service;
+	dmap_class->ctrl_int = dacp_share_ctrl_int;
+	dmap_class->login = dacp_share_login;
+	
+	/**
+	 * DACPShare::remote-found
+	 * @share: the #DACPShare that received the signal.
+	 * @service_name: the remote identifier.
+	 * @remote_name: the remote friendly name.
+	 *
+	 * Signal emited when a remote is found in the local network. You can 
+	 * associate a data pointer with this remote with 
+	 * dacp_share_set_remote_data().
+	 */
+	signals [REMOTE_FOUND] =
+		g_signal_new ("remote-found",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, remote_found),
+			      NULL,
+			      NULL,
+			      dmap_marshal_VOID__STRING_STRING,
+			      G_TYPE_NONE,
+			      2, G_TYPE_STRING, G_TYPE_STRING);
+			      
+	/**
+	 * DACPShare::remote-lost
+	 * @share: the #DACPShare that received the signal
+	 * @service_name: the remote identifier.
+	 *
+	 * Signal emited when a remote is lost in the local network. If you 
+	 * associated any data with dacp_share_set_remote_data() and it needs
+	 * to be freed, it must be done this time.
+	 */
+	signals [REMOTE_LOST] =
+		g_signal_new ("remote-lost",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, remote_lost),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE,
+			      1, G_TYPE_STRING);
+
+	/**
+	 * DACPShare::remote-paired
+	 * @share: the #DACPShare that received the signal
+	 * @service_name: the remote identifier.
+	 * @connected: indicates if the connection was succesfull or not.
+	 *
+	 * Signal emited when a remote is paired.
+	 */
+	signals [REMOTE_PAIRED] =
+		g_signal_new ("remote-paired",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, remote_paired),
+			      NULL,
+			      NULL,
+			      dmap_marshal_VOID__STRING_BOOLEAN,
+			      G_TYPE_NONE,
+			      2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+
+	signals [LOOKUP_GUID] =
+		g_signal_new ("lookup-guid",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, lookup_guid),
+			      NULL,
+			      NULL,
+			      dmap_marshal_BOOLEAN__STRING,
+			      G_TYPE_BOOLEAN,
+			      1, G_TYPE_STRING);
+	
+	signals [ADD_GUID] =
+		g_signal_new ("add-guid",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, add_guid),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE,
+			      1, G_TYPE_STRING);
+
+	/**
+	 * DACPShare::play-status-update:
+	 * @share: the #DACPShare that received the signal
+	 * @play_status_update_data: #DACPPlayStatusUpdateData data filled by
+	 * the receiver.
+	 *
+	 * The ::play-status-update signal is emitted when the Remote wants
+	 * information from the DACP server.
+	 */
+	signals [PLAY_STATUS_UPDATE] =
+		g_signal_new ("play-status-update",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, play_status_update),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__POINTER,
+			      G_TYPE_NONE,
+			      1, G_TYPE_POINTER);
+
+	/**
+	 * DACPShare::play-pause
+	 * @share: the #DACPShare that received the signal
+	 *
+	 * The ::play-pause signal is emitted when the Remote wants to play /
+	 * pause the player.
+	 */
+	signals [PLAY_PAUSE] =
+		g_signal_new ("play-pause",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, play_pause),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+
+	/**
+	 * DACPShare::now-playing-artwork
+	 * @share: the #DACPShare that received the signal.
+	 * @width: Artwork width in pixels.
+	 * @height: Artwork height in pixels.
+	 *
+	 * The ::now-playing-artwork signal is emitted when the Remote wants to 
+	 * retrieve a PNG containing the album artwork.
+	 *
+	 * Returns: a filename to the PNG file containing the album artwork.
+	 */
+	signals [NOW_PLAYING_ARTWORK] =
+		g_signal_new ("now-playing-artwork",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, now_playing_artwork),
+			      NULL,
+			      NULL,
+			      dmap_marshal_STRING__ULONG_ULONG,
+			      G_TYPE_STRING,
+			      2, G_TYPE_ULONG, G_TYPE_ULONG);
+
+	signals [NEXT_ITEM] =
+		g_signal_new ("next-item",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, next_item),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 
+			      0);
+
+	signals [PREV_ITEM] =
+		g_signal_new ("prev-item",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, prev_item),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 
+			      0);
+
+	signals [GET_SHUFFLE_STATE] =
+		g_signal_new ("get-shuffle-state",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, get_shuffle_state),
+			      NULL,
+			      NULL,
+			      dmap_marshal_ULONG__VOID,
+			      G_TYPE_ULONG,
+			      0);
+
+	signals [SET_SHUFFLE_STATE] =
+		g_signal_new ("set-shuffle-state",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, set_shuffle_state),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__ULONG,
+			      G_TYPE_NONE,
+			      1, G_TYPE_ULONG);
+
+	signals [GET_REPEAT_STATE] =
+		g_signal_new ("get-repeat-state",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, get_repeat_state),
+			      NULL,
+			      NULL,
+			      dmap_marshal_ULONG__VOID,
+			      G_TYPE_ULONG,
+			      0);
+
+	signals [SET_REPEAT_STATE] =
+		g_signal_new ("set-repeat-state",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, set_repeat_state),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__ULONG,
+			      G_TYPE_NONE,
+			      1, G_TYPE_ULONG);
+
+	signals [GET_VOLUME] =
+		g_signal_new ("get-volume",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, get_volume),
+			      NULL,
+			      NULL,
+			      dmap_marshal_ULONG__VOID,
+			      G_TYPE_ULONG,
+			      0);
+
+	signals [SET_VOLUME] =
+		g_signal_new ("set-volume",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (DACPShareClass, set_volume),
+			      NULL,
+			      NULL,
+			      g_cclosure_marshal_VOID__ULONG,
+			      G_TYPE_NONE,
+			      1, G_TYPE_ULONG);
+
+	g_type_class_add_private (klass, sizeof (DACPSharePrivate));
+}
+
+static void
+dacp_share_init (DACPShare *share)
+{
+	share->priv = DACP_SHARE_GET_PRIVATE (share);
+}
+
+static void
+dacp_share_set_property (GObject *object,
+			    guint prop_id,
+			    const GValue *value,
+			    GParamSpec *pspec)
+{
+	//DACPShare *share = DACP_SHARE (object);
+
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+dacp_share_get_property (GObject *object,
+			    guint prop_id,
+			    GValue *value,
+			    GParamSpec *pspec)
+{
+	//DACPShare *share = DACP_SHARE (object);
+
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+dacp_share_dispose (GObject *object)
+{
+	DACPShare *share = DACP_SHARE (object);
+	
+	g_object_unref (share->priv->mdns_browser);
+	
+	g_hash_table_destroy (share->priv->remotes);
+}
+
+void 
+mdns_remote_added (DMAPMdnsBrowser *browser, 
+                   DMAPMdnsBrowserService *service,
+                   DACPShare *share) 
+{
+	DACPRemoteInfo *remote_info;
+	
+	remote_info = g_new (DACPRemoteInfo, 1);
+	remote_info->host = g_strdup (service->host);
+	remote_info->port = service->port;
+	remote_info->connection = NULL;
+	remote_info->pair_txt = g_strdup (service->pair);
+	
+	g_debug ("New Remote found: %s name=%s host=%s port=%u pair=%s",
+	         service->service_name,
+	         service->name,
+	         remote_info->host,
+	         remote_info->port,
+	         remote_info->pair_txt);
+	
+	g_hash_table_insert (share->priv->remotes,
+	                     service->service_name,
+	                     remote_info);
+	
+	g_debug ("Emitting remote-found");
+	g_signal_emit (share, 
+	               signals [REMOTE_FOUND], 
+	               0, 
+	               service->service_name,
+	               service->name);
+}
+
+void
+mdns_remote_removed (DMAPMdnsBrowser *browser,
+                     const char *service_name,
+                     DACPShare *share)
+{
+	g_signal_emit (share,
+	               signals [REMOTE_LOST],
+	               0,
+	               service_name);
+	               
+	g_hash_table_remove (share->priv->remotes,
+	                     service_name);
+}
+
+DACPShare *
+dacp_share_new (DMAPDb *db,
+		DMAPContainerDb *container_db)
+{
+	DACPShare *share;
+	GString *name;
+	gchar *dbid_record;
+	gchar *txt_records[] = {"Ver=131073", 
+	                        "DvSv=2049",
+	                        "", // Will be replaced by dbid_record
+	                        "DvTy=iTunes",
+	                        "OSsi=0x1F6",
+	                        "txtvers=1",
+	                        "CtlN=DACP on Rhythmbox",
+	                        NULL};
+	
+	// Creates a service name 14 characters long concatenating the hostname
+	// hash hex value with itself.
+	// Idea taken from stereo.
+	name = g_string_new (NULL);
+	g_string_printf (name, "%.8x", g_str_hash(g_get_host_name ()));
+	g_string_ascii_up (name);
+	g_string_append_len (name, name->str, 4);
+
+	g_debug("DACP Sharing name: %s", name->str);
+
+	dbid_record = g_strdup_printf("DbId=%s", name->str);
+	txt_records[2] = dbid_record;
+	
+	g_object_ref (db);
+	g_object_ref (container_db);
+	
+	share = DACP_SHARE (g_object_new (TYPE_DACP_SHARE,
+					  "name", name->str,
+					  "password", NULL,
+					  "db", db,
+					  "container-db", container_db,
+					  "transcode-mimetype", NULL,
+					  "txt-records", txt_records,
+					  NULL));
+
+	g_free (dbid_record);
+	g_string_free (name, TRUE);
+	
+	g_debug("Starting DACP server");
+	_dmap_share_server_start (DMAP_SHARE (share));
+	_dmap_share_publish_start (DMAP_SHARE (share));
+
+	share->priv->remotes = g_hash_table_new_full ((GHashFunc)g_str_hash,
+	                                              (GEqualFunc)g_str_equal,
+	                                              (GDestroyNotify)g_free,
+	                                              (GDestroyNotify)g_free);
+
+	return share;
+}
+
+void
+dacp_share_start_lookup (DACPShare *share) 
+{
+	GError *error;
+	
+	if (share->priv->mdns_browser) {
+		g_warning ("DACP browsing already started");
+		return;
+	}
+	
+	share->priv->mdns_browser = dmap_mdns_browser_new (DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP);
+	
+	g_signal_connect_object (share->priv->mdns_browser,
+				 "service-added",
+				 G_CALLBACK (mdns_remote_added),
+				 share,
+				 0);
+	g_signal_connect_object (share->priv->mdns_browser,
+				 "service-removed",
+				 G_CALLBACK (mdns_remote_removed),
+				 share,
+				 0);
+	
+	error = NULL;
+	dmap_mdns_browser_start (share->priv->mdns_browser, &error);
+	if (error != NULL) {
+		g_warning ("Unable to start Remote lookup: %s", error->message);
+		g_error_free (error);
+	}
+}
+
+static gboolean
+remove_remotes_cb (gpointer service_name, gpointer remote_info, gpointer share)
+{
+	g_signal_emit ((DACPShare*) share,
+	               signals [REMOTE_LOST],
+	               0,
+	               (gchar *) service_name);
+	return TRUE;
+}
+
+void
+dacp_share_stop_lookup (DACPShare *share) 
+{
+	GError *error;
+	
+	if (!share->priv->mdns_browser) {
+		g_warning ("DACP browsing not started");
+		return;
+	}
+	
+	g_hash_table_foreach_remove (share->priv->remotes, remove_remotes_cb, share);
+	
+	error = NULL;
+	dmap_mdns_browser_stop (share->priv->mdns_browser, &error);
+	if (error != NULL) {
+		g_warning ("Unable to stop Remote lookup: %s", error->message);
+		g_error_free (error);
+	}
+	
+	share->priv->mdns_browser = NULL;
+}
+
+const char *
+dacp_share_get_type_of_service (DMAPShare *share)
+{
+	return DACP_TYPE_OF_SERVICE;
+}
+
+static void
+debug_param (gpointer key, gpointer val, gpointer user_data)
+{
+        g_debug ("%s %s", (char *) key, (char *) val);
+}
+
+void
+dacp_share_login (DMAPShare *share,
+	  SoupServer        *server,
+	  SoupMessage       *message,
+	  const char        *path,
+	  GHashTable        *query,
+	  SoupClientContext *context)
+{
+	gchar *pairing_guid;
+	
+	
+	g_debug ("(DACP) Path is %s.", path);
+	if (query) {
+		g_hash_table_foreach (query, debug_param, NULL);
+	}
+
+	pairing_guid = g_hash_table_lookup (query, "pairing-guid");
+
+	if (pairing_guid != NULL) {
+		gboolean allow_login;
+
+		g_signal_emit (share, signals [LOOKUP_GUID], 0, pairing_guid, &allow_login);
+
+		if (!allow_login) {
+			g_debug ("Unknown remote trying to connect");
+			soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
+			return;
+		}
+	}
+	
+	_dmap_share_login (share, server, message, path, query, context);
+}
+
+void
+dacp_share_ctrl_int (DMAPShare *share,
+		      SoupServer        *server,
+		      SoupMessage       *message,
+		      const char        *path,
+		      GHashTable        *query,
+		      SoupClientContext *context)
+{
+	const char *rest_of_path;
+	
+	//g_debug ("(DACP) Path is %s.", path);
+	//if (query) {
+	//	g_hash_table_foreach (query, debug_param, NULL);
+	//}
+		
+	rest_of_path = strchr (path + 1, '/');
+
+	if (rest_of_path && g_ascii_strcasecmp ("/1/playstatusupdate", rest_of_path))
+		g_debug ("(DACP) Path is %s.", path);
+	
+	/* If calling /ctrl-int without args, the client doesnt need a 
+	   session-id, otherwise it does and it should be validated. */
+	//if ((rest_of_path != NULL) && (! _dmap_share_session_id_validate (share, context, message, query, NULL))) {
+	//	soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
+	//	return;
+	//}
+
+	if (rest_of_path == NULL) {
+	/* CACI control-int
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT listing item
+	 * 			MIID item id
+	 * 			CMIK Unknown (TRUE)
+	 * 			CMSP Unknown (TRUE)
+	 * 			CMSV Unknown (TRUE)
+	 * 			CASS Unknown (TRUE)
+	 * 			CASU Unknown (TRUE)
+	 * 			CASG Unknown (TRUE)
+	 */
+	
+		GNode *caci;
+		GNode *mlcl;
+		GNode *mlit;
+	
+		// dacp.controlint
+		caci = dmap_structure_add (NULL, DMAP_CC_CACI);
+		// dmap.status
+		dmap_structure_add (caci, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+		// dmap.updatetype
+		dmap_structure_add (caci, DMAP_CC_MUTY, 0);
+		// dmap.specifiedtotalcount
+		dmap_structure_add (caci, DMAP_CC_MTCO, (gint32) 1);
+		// dmap.returnedcount
+		dmap_structure_add (caci, DMAP_CC_MRCO, (gint32) 1);
+		// dmap.listing
+		mlcl = dmap_structure_add (caci, DMAP_CC_MLCL);
+		// dmap.listingitem
+		mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
+		// dmap.itemid
+		dmap_structure_add (mlit, DMAP_CC_MIID, (gint32) 1);
+		// Unknown (TRUE)
+		dmap_structure_add (mlit, DMAP_CC_CMIK, (gint32) 1);
+		// Unknown (TRUE)
+		dmap_structure_add (mlit, DMAP_CC_CMSP, (gint32) 1);
+		// Unknown (TRUE)
+		dmap_structure_add (mlit, DMAP_CC_CMSV, (gint32) 1);
+		// Unknown (TRUE)
+		dmap_structure_add (mlit, DMAP_CC_CASS, (gint32) 1);
+		// Unknown (TRUE)
+		dmap_structure_add (mlit, DMAP_CC_CASU, (gint32) 1);
+		// Unknown (TRUE)
+		dmap_structure_add (mlit, DMAP_CC_CASG, (gint32) 1);
+
+		_dmap_share_message_set_from_dmap_structure (share, message, caci);
+		dmap_structure_destroy (caci);
+	} else if (g_ascii_strcasecmp ("/1/getproperty", rest_of_path) == 0) {
+		gchar *properties_query, **properties, **property;
+		GNode *cmgt;
+
+		properties_query = g_hash_table_lookup (query, "properties");
+		
+		if (!properties_query) {
+			g_warning ("No property specified");
+			return;
+		}
+		
+		properties = g_strsplit (properties_query, ",", -1);
+
+		cmgt = dmap_structure_add (NULL, DMAP_CC_CMGT);
+		dmap_structure_add (cmgt, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+		
+		for (property = properties; *property; property++) {
+			//FIXME: Correctly parse properties
+			if (g_ascii_strcasecmp (*property, "dmcp.volume") == 0) {
+				gulong volume;
+				
+				g_signal_emit (share, signals [GET_VOLUME], 0, &volume);
+				dmap_structure_add (cmgt, DMAP_CC_CMVO, volume);
+			} else {
+				g_warning ("Unhandled property %s", *property);
+			}
+		}
+
+		g_strfreev (properties);
+
+		_dmap_share_message_set_from_dmap_structure (share, message, cmgt);
+		dmap_structure_destroy (cmgt);
+	} else if (g_ascii_strcasecmp ("/1/setproperty", rest_of_path) == 0) {
+		if (g_hash_table_lookup (query, "dmcp.volume")) {
+			gdouble volume = strtod (g_hash_table_lookup (query, "dmcp.volume"), NULL);
+			g_signal_emit (share, signals [SET_VOLUME], 0, (gulong) volume);
+		}
+	} else if (g_ascii_strcasecmp ("/1/playstatusupdate", rest_of_path) == 0) {
+		GNode *cmst;
+		DACPPlayStatusUpdateData data;
+		memset (&data, 0, sizeof (data));
+		
+		g_signal_emit (share, signals [PLAY_STATUS_UPDATE], 0, &data);
+
+		cmst = dmap_structure_add (NULL, DMAP_CC_CMST);
+		dmap_structure_add (cmst, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+		//
+		dmap_structure_add (cmst, DMAP_CC_CMSR, 6);
+		dmap_structure_add (cmst, DMAP_CC_CAPS, (gint32) data.play_state);
+		dmap_structure_add (cmst, DMAP_CC_CASH, data.shuffle_state ? 1 : 0);
+		dmap_structure_add (cmst, DMAP_CC_CARP, (gint32) data.repeat_state);
+		if (data.play_state == PLAY_PAUSED || data.play_state == PLAY_PLAYING) {
+			//dmap_structure_add (cmst, DMAP_CC_CAVC, 1);
+			dmap_structure_add (cmst, DMAP_CC_CAAS, 2);
+			dmap_structure_add (cmst, DMAP_CC_CAAR, 6);
+			dmap_structure_add (cmst, DMAP_CC_CANP, (gint64) 0);
+			if (data.track)
+				dmap_structure_add (cmst, DMAP_CC_CANN, data.track);
+			if (data.artist)
+				dmap_structure_add (cmst, DMAP_CC_CANA, data.artist);
+			if (data.album)
+				dmap_structure_add (cmst, DMAP_CC_CANL, data.album);
+			dmap_structure_add (cmst, DMAP_CC_CANG, "");
+			dmap_structure_add (cmst, DMAP_CC_ASAI, 0);
+			//dmap_structure_add (cmst, DMAP_CC_AEMK, 1);
+			dmap_structure_add (cmst, DMAP_CC_CANT, data.remaining_track_time);
+			dmap_structure_add (cmst, DMAP_CC_CAST, data.total_track_time);
+		}
+		_dmap_share_message_set_from_dmap_structure (share, message, cmst);
+		dmap_structure_destroy (cmst);
+
+		if (data.track) g_free (data.track);
+		if (data.artist) g_free (data.artist);
+		if (data.album) g_free (data.album);
+	} else {
+		g_warning ("Unhandled ctrl-int command: %s", rest_of_path);
+	}
+}
+
+#define PAIR_TXT_LENGTH 16
+#define PASSCODE_LENGTH 4
+
+static gchar *
+dacp_share_pairing_code(DACPShare *share, gchar* pair_txt, gchar passcode[4]) {
+	int i;
+	GString *pairing_code;
+	gchar *pairing_string;
+	gchar *ret;
+	
+	/* FIXME: Remove this debug message */
+	g_debug("Creating pairing code (pair: %s, passcode: %4s)", pair_txt, passcode);
+	
+	/* The pairing code is the MD5 sum of the concatenation of pair_txt
+	   with the passcode, but the passcode takes 16-bits unicodes characters */
+	pairing_string = g_strnfill(PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2, '\0');
+	g_strlcpy(pairing_string, pair_txt, PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2);
+	for (i = 0; i < 4; i++) {
+		pairing_string[PAIR_TXT_LENGTH + i * 2] = passcode[i];
+	}
+	
+	pairing_code = g_string_new (
+		g_compute_checksum_for_data(G_CHECKSUM_MD5, 
+		                            (guchar*)pairing_string, 
+		                            PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2));
+	g_string_ascii_up (pairing_code);
+	ret = pairing_code->str;
+	g_string_free (pairing_code, FALSE);
+
+	/* FIXME: Remove this debug message */
+	g_debug("Pairing code: %s", ret);
+	
+	return ret;
+}
+
+void
+connection_handler_cb (DMAPConnection *connection, guint status, GNode *structure, gpointer user_data) 
+{
+	gboolean connected;
+	GHashTableIter iter;
+	gpointer key, value;
+	DACPShare *share = user_data;
+	DACPRemoteInfo *remote_info = NULL;
+	gchar *service_name = NULL;
+	DMAPStructureItem *item = NULL;
+	gchar *pairing_guid;
+
+	/* FIXME: Remove debug message */
+	g_debug ("Pairing status code: %u", status);
+	if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+		connected = TRUE;
+	} else {
+		connected = FALSE;
+	}
+	
+	/* Get the pairing-guid to identify this remote in the future. */
+	if (structure)
+		item = dmap_structure_find_item (structure, DMAP_CC_CMPG);
+	if (item) {
+		guint64 guid = g_value_get_int64 (&(item->content));
+		pairing_guid = g_strdup_printf ("0x%0" G_GINT64_MODIFIER "X", guid);
+		g_signal_emit (share, signals [ADD_GUID], 0, pairing_guid);
+	}
+
+	/* Find the remote that initiated this connection */
+	g_hash_table_iter_init (&iter, share->priv->remotes);
+	while (g_hash_table_iter_next (&iter, &key, &value)) 
+	{
+		if (((DACPRemoteInfo*) value)->connection == connection) {
+			service_name = (gchar *) key;
+			remote_info = (DACPRemoteInfo*) value;
+			break;
+		}
+	}
+
+	if (remote_info == NULL) {
+		g_warning ("Remote for connection not found");
+		return;
+	}
+
+	/* Frees the connection */
+	remote_info->connection = NULL;	
+	g_object_unref (connection);
+
+	/* FIXME: Send more detailed error info, such as wrong pair code, etc */
+	g_signal_emit (share, signals [REMOTE_PAIRED], 0, service_name, connected);
+}
+
+void
+dacp_share_pair (DACPShare *share, gchar *service_name, gchar passcode[4]) 
+{
+	//CallbackData *callback_data;
+	//DMAPConnection *connection;
+	gchar *pairing_code;
+	gchar *name;
+	gchar *path;
+	DACPRemoteInfo *remote_info;
+	
+	remote_info = g_hash_table_lookup (share->priv->remotes,
+	                                   service_name);
+	                                   
+	if (remote_info == NULL) {
+		g_warning ("Remote %s not found.", service_name);
+		return;
+	}
+
+	if (remote_info->connection != NULL) {
+		g_warning ("Already pairing remote %s.", service_name);
+		return;
+	}
+	
+	g_object_get (share, "name", &name, NULL);
+	
+	remote_info->connection = dmap_connection_new (name, 
+	                                               remote_info->host, 
+	                                               remote_info->port, 
+	                                               FALSE, 
+	                                               NULL, 
+	                                               NULL);
+	/* This is required since we don't call DMAPConnection default handler */
+	dmap_connection_setup (remote_info->connection);
+	
+	/* Get the remote path for pairing */
+	pairing_code = dacp_share_pairing_code (share, remote_info->pair_txt, passcode);
+	path = g_strdup_printf ("/pair?pairingcode=%s&servicename=%s", 
+	                        pairing_code,
+	                        name);
+	g_free (pairing_code);
+	
+	g_debug ("Sending message to: %s:%d/%s", remote_info->host, remote_info->port, path);
+
+	/* Let DMAPConnection do the heavy work */
+	dmap_connection_get (remote_info->connection, path, FALSE, connection_handler_cb, share);
+
+	g_free (path);
+}
diff --git a/libdmapsharing/dacp-share.h b/libdmapsharing/dacp-share.h
new file mode 100644
index 0000000..1c9fa83
--- /dev/null
+++ b/libdmapsharing/dacp-share.h
@@ -0,0 +1,219 @@
+/*
+ * Header for DACP (e.g., iTunes Remote) sharing
+ *
+ * Copyright (C) 2010 Alexandre Rosenfeld <airmind@gmail.com>
+ *
+ * This library 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.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __DACP_SHARE_H
+#define __DACP_SHARE_H
+
+#include <glib-object.h>
+
+#include <libdmapsharing/dmap-share.h>
+#include <libdmapsharing/dmap-db.h>
+#include <libdmapsharing/dmap-container-db.h>
+#include <libdmapsharing/daap-share.h>
+
+G_BEGIN_DECLS
+
+/**
+ * TYPE_DACP_SHARE:
+ *
+ * The type for #DACPShare.
+ */
+#define TYPE_DACP_SHARE         (dacp_share_get_type ())
+/**
+ * DACP_SHARE:
+ * @o: Object which is subject to casting.
+ * 
+ * Casts a #DACPShare or derived pointer into a (DACPShare*) pointer.
+ * Depending on the current debugging level, this function may invoke
+ * certain runtime checks to identify invalid casts.
+ */
+#define DACP_SHARE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), \
+				 TYPE_DACP_SHARE, DACPShare))
+/**
+ * DACP_SHARE_CLASS:
+ * @k: a valid #DACPShareClass
+ *
+ * Casts a derived #DACPShareClass structure into a #DACPShareClass structure.
+ */
+#define DACP_SHARE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), \
+				 TYPE_DACP_SHARE, DACPShareClass))
+/**
+ * IS_DACP_SHARE:
+ * @o: Instance to check for being a %TYPE_DACP_SHARE.
+ * 
+ * Checks whether a valid #GTypeInstance pointer is of type %TYPE_DACP_SHARE.
+ */
+#define IS_DACP_SHARE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), \
+				 TYPE_DACP_SHARE))
+/**
+ * IS_DACP_SHARE_CLASS:
+ * @k: a #DACPShareClass
+ * 
+ * Checks whether @k "is a" valid #DACPShareClass structure of type
+ * %DACP_SHARE or derived.
+ */
+#define IS_DACP_SHARE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), \
+				 TYPE_DACP_SHARE))
+/**
+ * DACP_SHARE_GET_CLASS:
+ * @o: a #DACPShare instance.
+ * 
+ * Get the class structure associated to a #DACPShare instance.
+ *
+ * Returns: pointer to object class structure.
+ */
+#define DACP_SHARE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), \
+				 TYPE_DACP_SHARE, DACPShareClass))
+
+typedef struct DACPSharePrivate DACPSharePrivate;
+
+typedef struct {
+	DAAPShare daap_share_instance;
+	DACPSharePrivate *priv;
+} DACPShare;
+
+/**
+ * DACPPlayState:
+ */
+typedef enum {
+	PLAY_STOPPED = 2,
+	PLAY_PAUSED = 3,
+	PLAY_PLAYING = 4
+} DACPPlayState;
+
+/**
+ * DACPRepeatState:
+ */
+typedef enum {
+	REPEAT_NONE = 0,
+	REPEAT_SINGLE = 1,
+	REPEAT_ALL = 2
+} DACPRepeatState;
+
+/**
+ * DACPPlayStatusUpdateData:
+ * @play_state: TRUE if playing, FALSE if paused.
+ * @shuffle_state: TRUE if shuffle is on, FALSE otherwise.
+ * @repeat_state: indicates if repeat is on, single or off.
+ * @track: playing track title. Can be NULL if stopped.
+ * @artist: playing track artist. Can be NULL if stopped.
+ * @album: playing track album. Can be NULL if stopped.
+ * @album_id: playing track algum id. Can be NULL if stopped.
+ * @remaining_track_time: remaining track time in ms.
+ * @total_track_time: total track length in ms.
+ *
+ * The media player should fill this struct with playing information. Used with
+ * the #DACPShare::play-status-update signal.
+ * All strings will be freed after this structure is processed.
+ */
+typedef struct {
+	DACPPlayState play_state;
+	gboolean shuffle_state;
+	DACPRepeatState repeat_state;
+	gchar *track;
+	gchar *artist;
+	gchar *album;
+	gchar *album_id;
+	guint remaining_track_time;
+	guint total_track_time;
+} DACPPlayStatusUpdateData;
+
+typedef struct {
+	DAAPShareClass daap_share_class;
+	
+	gboolean (*lookup_guid)            (DACPShare *share, gchar *guid);
+	void     (*add_guid)               (DACPShare *share, gchar *guid);
+	
+	void (*remote_found)               (DACPShare *share,
+	                                    gchar *service_name,
+	                                    gchar *remote_name);
+	void (*remote_lost)                (DACPShare *share,
+	                                    gchar *service_name);
+	void (*remote_paired)              (DACPShare *share,
+	                                    gchar *service_name,
+	                                    gboolean connected);
+	
+	void (*play_status_update)         (DACPShare *share, 
+	                                    DACPPlayStatusUpdateData *data);
+	void (*play_pause)                 (DACPShare *share);
+	gchar *(*now_playing_artwork)      (DACPShare *share, guint width, guint height);
+	void (*next_item)                  (DACPShare *share);
+	void (*prev_item)                  (DACPShare *share);
+	gboolean (*get_shuffle_state)      (DACPShare *share);
+	void (*set_shuffle_state)          (DACPShare *share, gboolean state);
+	DACPRepeatState (*get_repeat_state) (DACPShare *share);
+	void (*set_repeat_state)           (DACPShare *share, DACPRepeatState state);
+	guint (*get_volume)                (DACPShare *share);
+	void (*set_volume)                 (DACPShare *share, guint value);
+} DACPShareClass;
+
+GType      dacp_share_get_type (void);
+
+/**
+ * dacp_share_new:
+ * @db: a media database represented by a #DMAPDb instance.
+ * @container_db: a container (album) database represented by a #DMAPContainerDb
+ *                instance.
+ * @guid_handler: Functions to save known guids. See #DACPGuidHandler for more
+ *                information.
+ * 
+ * Creates a new DACP share and publishes it using mDNS.
+ *
+ * Returns: a pointer to a #DACPShare.
+ */
+DACPShare *dacp_share_new (DMAPDb *db, DMAPContainerDb *container_db);
+
+/**
+ * dacp_share_pair:
+ * @share: a #DACPShare 
+ * @service_name: DACP client (remote) service identifier.
+ * @passcode: 4-Digit PIN code entered by the user.
+ * @callback: callback called when pairing is done, either successfully or not.
+ * @user_data: user data parameter on callback.
+ *     
+ * Pairs a DACP client (Remote) with this server. If the passcode is 
+ * correct (the same as shown on the remote), the remote will start connecting
+ * to this server.
+ */
+void dacp_share_pair (DACPShare *share, gchar *service_name, gchar passcode[4]);
+
+/**
+ * dacp_share_start_lookup:
+ * @share: A #DACPShare.
+ *     
+ * Start looking up for DACP remotes. Connect to #DACPShare::remote-found signal
+ * to detect new remotes. Be aware that when a #DACPShare is created, only 
+ * after calling this function is that it starts looking up for Remotes on the
+ * network.
+ */
+void dacp_share_start_lookup (DACPShare *share);
+
+/**
+ * dacp_share_stop_lookup:
+ * @share: A #DACPShare.
+ *     
+ * Stop looking up for DACP remotes.
+ */
+void dacp_share_stop_lookup (DACPShare *share);
+
+#endif /* __DACP_SHARE_H */
+
+G_END_DECLS
diff --git a/libdmapsharing/dmap-connection.c b/libdmapsharing/dmap-connection.c
index 34e43be..d9a3cb9 100644
--- a/libdmapsharing/dmap-connection.c
+++ b/libdmapsharing/dmap-connection.c
@@ -91,6 +91,7 @@ struct DMAPConnectionPrivate {
 
 	DMAPConnectionState state;
 	DMAPResponseHandler response_handler;
+	gpointer response_handler_data;
 	gboolean use_response_handler_thread;
 	float progress;
 
@@ -140,8 +141,6 @@ dmap_connection_finalize (GObject *object)
 
 	g_return_if_fail (connection->priv != NULL);
 
-	g_debug ("Finalize");
-
 	G_OBJECT_CLASS (dmap_connection_parent_class)->finalize (object);
 }
 
@@ -588,14 +587,15 @@ actual_http_response_handler (DAAPResponseData *data)
 		} else {
 			int dmap_status = 0;
 			item = dmap_structure_find_item (structure, DMAP_CC_MSTT);
-			if (item)
+			if (item) {
 				dmap_status = g_value_get_int (&(item->content));
 
-			if (dmap_status != 200) {
-				g_debug ("Error, dmap.status is not 200 in response from %s",
-					  message_path);
+				if (dmap_status != 200) {
+					g_debug ("Error, dmap.status is not 200 in response from %s",
+						  message_path);
 
-				data->status = SOUP_STATUS_MALFORMED;
+					data->status = SOUP_STATUS_MALFORMED;
+				}
 			}
 		}
 		if (/* FIXME: ! rb_is_main_thread () */ TRUE) {
@@ -615,8 +615,10 @@ actual_http_response_handler (DAAPResponseData *data)
 
 	if (priv->response_handler) {
 		DMAPResponseHandler h = priv->response_handler;
+		gpointer user_data = priv->response_handler_data;
 		priv->response_handler = NULL;
-		(*h) (data->connection, data->status, structure);
+		priv->response_handler_data = NULL;
+		(*h) (data->connection, data->status, structure, user_data);
 	}
 
 	if (structure) {
@@ -683,7 +685,8 @@ http_get (DMAPConnection     *connection,
 	  gdouble               version,
 	  gint                  req_id,
 	  gboolean              send_close,
-	  DMAPResponseHandler handler,
+	  DMAPResponseHandler   handler,
+	  gpointer              user_data,
 	  gboolean              use_thread)
 {
 	DMAPConnectionPrivate *priv = connection->priv;
@@ -700,6 +703,7 @@ http_get (DMAPConnection     *connection,
 
 	priv->use_response_handler_thread = use_thread;
 	priv->response_handler = handler;
+	priv->response_handler_data = user_data;
 	soup_session_queue_message (priv->session, message,
 				    (SoupSessionCallback) http_response_handler,
 				    connection);
@@ -720,7 +724,7 @@ dmap_connection_get (DMAPConnection *self,
     /* FIXME: used to pass user_data as 8th argument, but this is not right. */
     return http_get (self, path, need_hash,
             self->priv->dmap_version, 0, FALSE,
-            (DMAPResponseHandler) handler, FALSE);
+            (DMAPResponseHandler) handler, user_data, FALSE);
 }
 
 static gboolean
@@ -740,7 +744,8 @@ emit_progress_idle (DMAPConnection *connection)
 static void
 handle_server_info (DMAPConnection *connection,
 		    guint             status,
-		    GNode            *structure)
+		    GNode            *structure,
+		    gpointer          user_data)
 {
 	DMAPConnectionPrivate *priv = connection->priv;
 	DMAPStructureItem *item = NULL;
@@ -764,7 +769,8 @@ handle_server_info (DMAPConnection *connection,
 static void
 handle_login (DMAPConnection *connection,
 	      guint             status,
-	      GNode            *structure)
+	      GNode            *structure,
+	      gpointer          user_data)
 {
 	DMAPConnectionPrivate *priv = connection->priv;
 	DMAPStructureItem *item = NULL;
@@ -801,7 +807,8 @@ handle_login (DMAPConnection *connection,
 static void
 handle_update (DMAPConnection *connection,
 	       guint             status,
-	       GNode            *structure)
+	       GNode            *structure,
+	       gpointer          user_data)
 {
 	DMAPConnectionPrivate *priv = connection->priv;
 	DMAPStructureItem *item;
@@ -826,7 +833,8 @@ handle_update (DMAPConnection *connection,
 static void
 handle_database_info (DMAPConnection *connection,
 		      guint             status,
-		      GNode            *structure)
+		      GNode            *structure,
+		      gpointer          user_data)
 {
 	DMAPConnectionPrivate *priv = connection->priv;
 	DMAPStructureItem *item = NULL;
@@ -873,7 +881,8 @@ handle_database_info (DMAPConnection *connection,
 static void
 handle_song_listing (DMAPConnection *connection,
 		     guint             status,
-		     GNode            *structure)
+		     GNode            *structure,
+		     gpointer          user_data)
 {
 	DMAPConnectionPrivate *priv = connection->priv;
 	DMAPStructureItem *item = NULL;
@@ -1093,7 +1102,8 @@ compare_playlists_by_name(gconstpointer a, gconstpointer b)
 static void
 handle_playlists (DMAPConnection *connection,
 		  guint             status,
-		  GNode            *structure)
+		  GNode            *structure,
+		  gpointer          user_data)
 {
 	DMAPConnectionPrivate *priv = connection->priv;
 	GNode *listing_node;
@@ -1158,7 +1168,8 @@ handle_playlists (DMAPConnection *connection,
 static void
 handle_playlist_entries (DMAPConnection *connection,
 			 guint             status,
-			 GNode            *structure)
+			 GNode            *structure,
+			 gpointer          user_data)
 {
 	DMAPConnectionPrivate *priv = connection->priv;
 	DMAPPlaylist *playlist;
@@ -1215,7 +1226,8 @@ handle_playlist_entries (DMAPConnection *connection,
 static void
 handle_logout (DMAPConnection *connection,
 	       guint             status,
-	       GNode            *structure)
+	       GNode            *structure,
+	       gpointer          user_data)
 {
 	connection_disconnected (connection);
 
@@ -1298,6 +1310,17 @@ connected_cb (DMAPConnection       *connection,
 	}
 }
 
+void 
+dmap_connection_setup (DMAPConnection *connection) 
+{
+	connection->priv->session = soup_session_async_new ();
+
+	connection->priv->base_uri = soup_uri_new (NULL);
+	soup_uri_set_scheme (connection->priv->base_uri, SOUP_URI_SCHEME_HTTP);
+	soup_uri_set_host (connection->priv->base_uri, connection->priv->host);
+	soup_uri_set_port (connection->priv->base_uri, connection->priv->port);
+}
+
 void
 dmap_connection_connect (DMAPConnection        *connection,
 			    DMAPConnectionCallback callback,
@@ -1310,12 +1333,7 @@ dmap_connection_connect (DMAPConnection        *connection,
 
 	g_debug ("Creating new DAAP connection to %s:%d", connection->priv->host, connection->priv->port);
 
-	connection->priv->session = soup_session_async_new ();
-
-	connection->priv->base_uri = soup_uri_new (NULL);
-	soup_uri_set_scheme (connection->priv->base_uri, SOUP_URI_SCHEME_HTTP);
-	soup_uri_set_host (connection->priv->base_uri, connection->priv->host);
-	soup_uri_set_port (connection->priv->base_uri, connection->priv->port);
+	dmap_connection_setup (connection);
 
 	if (connection->priv->base_uri == NULL) {
 		g_debug ("Error parsing http://%s:%d", connection->priv->host, connection->priv->port);
@@ -1496,7 +1514,7 @@ dmap_connection_do_something (DMAPConnection *connection)
 	case DMAP_GET_INFO:
 		g_debug ("Getting DAAP server info");
 		if (! http_get (connection, "/server-info", FALSE, 0.0, 0, FALSE,
-				(DMAPResponseHandler) handle_server_info, FALSE)) {
+				(DMAPResponseHandler) handle_server_info, NULL, FALSE)) {
 			g_debug ("Could not get DAAP connection info");
 			dmap_connection_state_done (connection, FALSE);
 		}
@@ -1531,7 +1549,7 @@ dmap_connection_do_something (DMAPConnection *connection)
 	case DMAP_LOGIN:
 		g_debug ("Logging into DAAP server");
 		if (! http_get (connection, "/login", FALSE, 0.0, 0, FALSE,
-			       (DMAPResponseHandler) handle_login, FALSE)) {
+			       (DMAPResponseHandler) handle_login, NULL, FALSE)) {
 			g_debug ("Could not login to DAAP server");
 			/* FIXME: set state back to GET_PASSWORD to try again */
 			dmap_connection_state_done (connection, FALSE);
@@ -1543,7 +1561,7 @@ dmap_connection_do_something (DMAPConnection *connection)
 		g_debug ("Getting DAAP server database revision number");
 		path = g_strdup_printf ("/update?session-id=%u&revision-number=1", priv->session_id);
 		if (! http_get (connection, path, TRUE, priv->dmap_version, 0, FALSE,
-			       (DMAPResponseHandler) handle_update, FALSE)) {
+			       (DMAPResponseHandler) handle_update, NULL, FALSE)) {
 			g_debug ("Could not get server database revision number");
 			dmap_connection_state_done (connection, FALSE);
 		}
@@ -1555,7 +1573,7 @@ dmap_connection_do_something (DMAPConnection *connection)
 		path = g_strdup_printf ("/databases?session-id=%u&revision-number=%d",
 					priv->session_id, priv->revision_number);
 		if (! http_get (connection, path, TRUE, priv->dmap_version, 0, FALSE,
-			       (DMAPResponseHandler) handle_database_info, FALSE)) {
+			       (DMAPResponseHandler) handle_database_info, NULL, FALSE)) {
 			g_debug ("Could not get DAAP database info");
 			dmap_connection_state_done (connection, FALSE);
 		}
@@ -1575,7 +1593,7 @@ dmap_connection_do_something (DMAPConnection *connection)
 					priv->session_id,
 					priv->revision_number);
 		if (! http_get (connection, path, TRUE, priv->dmap_version, 0, FALSE,
-			       (DMAPResponseHandler) handle_song_listing, TRUE)) {
+			       (DMAPResponseHandler) handle_song_listing, NULL, TRUE)) {
 			g_debug ("Could not get DAAP song listing");
 			dmap_connection_state_done (connection, FALSE);
 		}
@@ -1589,7 +1607,7 @@ dmap_connection_do_something (DMAPConnection *connection)
 					priv->session_id,
 					priv->revision_number);
 		if (! http_get (connection, path, TRUE, priv->dmap_version, 0, FALSE,
-			       (DMAPResponseHandler) handle_playlists, TRUE)) {
+			       (DMAPResponseHandler) handle_playlists, NULL, TRUE)) {
 			g_debug ("Could not get DAAP playlists");
 			dmap_connection_state_done (connection, FALSE);
 		}
@@ -1608,7 +1626,7 @@ dmap_connection_do_something (DMAPConnection *connection)
 						playlist->id,
 						priv->session_id, priv->revision_number);
 			if (! http_get (connection, path, TRUE, priv->dmap_version, 0, FALSE,
-				       (DMAPResponseHandler) handle_playlist_entries, TRUE)) {
+				       (DMAPResponseHandler) handle_playlist_entries, NULL, TRUE)) {
 				g_debug ("Could not get entries for DAAP playlist %d",
 					  priv->reading_playlist);
 				dmap_connection_state_done (connection, FALSE);
@@ -1621,7 +1639,7 @@ dmap_connection_do_something (DMAPConnection *connection)
 		g_debug ("Logging out of DAAP server");
 		path = g_strdup_printf ("/logout?session-id=%u", priv->session_id);
 		if (! http_get (connection, path, TRUE, priv->dmap_version, 0, FALSE,
-			       (DMAPResponseHandler) handle_logout, FALSE)) {
+			       (DMAPResponseHandler) handle_logout, NULL, FALSE)) {
 			g_debug ("Could not log out of DAAP server");
 			dmap_connection_state_done (connection, FALSE);
 		}
@@ -1711,8 +1729,6 @@ dmap_connection_dispose (GObject *object)
 	DMAPConnectionPrivate *priv = DMAP_CONNECTION (object)->priv;
 	GSList *l;
 
-	g_debug ("DAAP connection dispose");
-
 	if (priv->emit_progress_id != 0) {
 		g_source_remove (priv->emit_progress_id);
 		priv->emit_progress_id = 0;
diff --git a/libdmapsharing/dmap-connection.h b/libdmapsharing/dmap-connection.h
index de29001..0e824aa 100644
--- a/libdmapsharing/dmap-connection.h
+++ b/libdmapsharing/dmap-connection.h
@@ -135,7 +135,8 @@ typedef gboolean (* DMAPConnectionCallback)  (DMAPConnection *connection,
 
 typedef void (* DMAPResponseHandler) (DMAPConnection *connection,
                                       guint status,
-				      GNode *structure);
+                                      GNode *structure,
+                                      gpointer user_data);
 
 GType              dmap_connection_get_type        (void);
 
@@ -147,6 +148,7 @@ DMAPConnection * dmap_connection_new             (const char              *name,
 						       DMAPRecordFactory *factory);
 
 gboolean           dmap_connection_is_connected    (DMAPConnection        *connection);
+void               dmap_connection_setup           (DMAPConnection        *connection);
 void               dmap_connection_connect         (DMAPConnection        *connection,
 						       DMAPConnectionCallback callback,
 						       gpointer                 user_data);
diff --git a/libdmapsharing/dmap-db.c b/libdmapsharing/dmap-db.c
index 9c74a7f..9b71073 100644
--- a/libdmapsharing/dmap-db.c
+++ b/libdmapsharing/dmap-db.c
@@ -105,24 +105,6 @@ dmap_db_count (const DMAPDb *db)
 	return DMAP_DB_GET_INTERFACE (db)->count (db);
 }
 
-/* Change "\'" to "'". */
-static gchar *
-unescape (const gchar *str)
-{
-	gchar *fnval;
-	gsize j, i, size;
-
-	size = strlen (str) + 1;
-	fnval = g_new0 (gchar, size);
-
-	j = 0;
-	for (i = 0; i < size; i++)
-		if (str[i] != '\\' || str[i+1] != '\'')
-			fnval[j++] = str[i];
-
-	return fnval;
-}
-
 gchar **
 _dmap_db_strsplit_using_quotes (const gchar *str)
 {
@@ -168,44 +150,143 @@ _dmap_db_strsplit_using_quotes (const gchar *str)
         return fnval;
 }
 
+static gboolean
+compare_record_property (DMAPRecord *record, const gchar *property_name, const gchar *property_value)
+{
+	GParamSpec *pspec;
+	GValue value = {0,};
+	/* Note that this string belongs to value and will not be freed explicitely.*/
+	const gchar *str_value;
+	gboolean accept;
+	
+	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (record), property_name);
+
+	if (pspec == NULL)
+		// Can't find the property in this record, so don't accept it.
+		return FALSE;
+
+	// Get the property value as a GValue set to the type of this
+	// property.
+	g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+	g_object_get_property (G_OBJECT (record), property_name, &value);
+
+	if (G_VALUE_HOLDS_STRING (&value)) {
+		str_value = g_value_get_string (&value);
+	} else if (G_VALUE_HOLDS_BOOLEAN (&value)) {
+		accept = (g_value_get_boolean (&value) && \
+		          g_strcmp0 (property_value, "1") == 0);
+		g_value_unset (&value);
+		return accept;
+	} else if (g_value_type_transformable (G_VALUE_TYPE (&value), G_TYPE_LONG)) {
+		// Prefer integer conversion.
+		GValue dest = {0,};
+		g_value_init (&dest, G_TYPE_LONG);
+		if (!g_value_transform (&value, &dest)) {
+			g_warning ("Failed to convert value into long for property %s", property_name);
+			g_value_unset (&value);
+			return FALSE;
+		}
+		accept = (g_value_get_long (&dest) == atoi (property_value));
+		g_value_unset (&value);
+		return accept;
+	} else if (g_value_type_transformable (G_VALUE_TYPE (&value), G_TYPE_STRING)) {
+		// Use standard transform functions from GLib (note that these
+		// functions are unreliable and known cases should be handled
+		// above).
+		GValue dest;
+		g_value_init (&dest, G_TYPE_STRING);
+		if (!g_value_transform (&value, &dest)) {
+			g_warning ("Failed to convert value into string for property %s", property_name);
+			g_value_unset (&value);
+			return FALSE;
+		}
+		str_value = g_value_dup_string (&dest);
+		g_value_reset (&value);
+		//Sets the string to value so that it will be freed later.
+		g_value_take_string (&value, (gchar *)str_value);
+		g_value_unset (&dest);
+	} else {
+		g_value_unset (&value);
+		return FALSE;
+	}
+
+	// Only arrive here if we are handling strings.
+	if (str_value != NULL && property_value != NULL && \
+	    g_ascii_strcasecmp (str_value, property_value) == 0) {
+		accept = TRUE;
+	} else if (str_value == NULL && property_value == NULL) {
+		accept = TRUE;
+	} else {
+		accept = FALSE;
+	}
+
+	// This will destroy str_value since it belongs to value.
+	g_value_unset (&value);
+
+	return accept;
+}
+
 static void
 apply_filter (gpointer id, DMAPRecord *record, gpointer data)
 {
 	FilterData *fd;
+	gboolean accept = FALSE;
+
+	const gchar *query_key;
+	const gchar *query_value;
+
+	g_return_if_fail (record != NULL);
+	g_return_if_fail (G_IS_OBJECT (record));
 	
 	fd = data;
 	if (fd->filter_def == NULL) {
 		g_hash_table_insert (fd->ht, GUINT_TO_POINTER (id), record);
-	} else {
-		GSList *ptr1, *ptr2;
-		gboolean accepted = TRUE;
-		for (ptr1 = fd->filter_def; ptr1 != NULL; ptr1 = ptr1->next) {
-			for (ptr2 = ptr1->data; ptr2 != NULL; ptr2 = ptr2->next) {
-				gchar *value1 = unescape (((FilterDefinition *) ptr2->data)->value);
-
-				if (((FilterDefinition *) ptr2->data)->is_string == FALSE) {
-					if (GPOINTER_TO_UINT (id) == atoi (value1))
-						g_hash_table_insert (fd->ht, id, dmap_db_lookup_by_id (fd->db, GPOINTER_TO_UINT (id)));
-					accepted = FALSE; /* Not really, but we've already added if required. */
-				} else {
-					gchar *value2;
-					GParamSpec *pspec = g_object_class_find_property (G_OBJECT_CLASS (record), ((FilterDefinition *) ptr2->data)->key);
-					if (G_IS_PARAM_SPEC_STRING (pspec)) {
-						g_object_get (record, ((FilterDefinition *) ptr2->data)->key, &value2, NULL); 
-						if (value2 == NULL || g_strcasecmp (value1, value2) != 0) {
-							accepted = FALSE;
-						}
-					} else {
-						/* FIXME: unhandled filter (non-string): */
-						accepted = FALSE;
-					}
-					g_free (value2);
+		return;
+	} 
+	
+	GSList *list, *filter;
+	for (list = fd->filter_def; list != NULL; list = list->next) {
+		for (filter = list->data; filter != NULL; filter = filter->next) {
+			FilterDefinition *def = filter->data;
+			const gchar *property_name;
+
+			query_key   = def->key;
+			query_value = def->value;
+
+			if (g_strcmp0 (query_key, "dmap.itemid") == 0) {
+				if (GPOINTER_TO_UINT (id) == atoi (query_value)) {
+					accept = TRUE;
+					continue;
 				}
-				g_free (value1);
-			}
+			};
+
+			// Use only the part after the last dot.
+			// For instance, dmap.songgenre becomes songgenre.
+			property_name = strrchr (query_key, '.');
+			if (property_name == NULL)
+				property_name = query_key;
+			else
+				//Don't include the dot in the property name.
+				property_name++;
+
+			accept = compare_record_property (record, property_name, query_value);
+			
+			if (def->negate)
+				accept = !accept;
+			
+			// If we accept this value, then quit looking at this 
+			// group (groups are always OR)
+			if (accept)
+				break;
 		}
-		if (accepted == TRUE)
-			g_hash_table_insert (fd->ht, GUINT_TO_POINTER (id), record);
+		// Don't look any further, because groups are AND between 
+		// each other, the first FALSE means FALSE at the end.
+		if (!accept)
+			break;
+	}
+	if (accept) {
+		g_object_ref (record);
+		g_hash_table_insert (fd->ht, GUINT_TO_POINTER (id), G_OBJECT (record));
 	}
 }
 
@@ -215,7 +296,7 @@ dmap_db_apply_filter (DMAPDb *db, GSList *filter_def)
 	GHashTable *ht;
 	FilterData data;
 
-	ht = g_hash_table_new (g_direct_hash, g_direct_equal);
+	ht = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
 	data.db = db;
 	data.filter_def = filter_def;
 	data.ht = ht;
diff --git a/libdmapsharing/dmap-db.h b/libdmapsharing/dmap-db.h
index 686a4d2..06f7238 100644
--- a/libdmapsharing/dmap-db.h
+++ b/libdmapsharing/dmap-db.h
@@ -84,12 +84,10 @@ struct _DMAPDbInterface {
 
 typedef const char *(*RecordGetValueFunc) (DMAPRecord *record);
 
-/* FIXME: This is in transition: how to keep this safe when value might 
- * be compared to a string, int, etc.? */
 typedef struct FilterDefinition {
 	gchar *key;
 	gchar *value;
-	gboolean is_string;
+	gboolean negate;
 } FilterDefinition;
 
 GType dmap_db_get_type		    (void);
@@ -170,7 +168,7 @@ guint dmap_db_lookup_id_by_location (const DMAPDb *db, const gchar *location);
 /**
  * dmap_db_foreach:
  * @db: A media database.
- * @fn: The function to apply to each record in the database.
+ * @func: The function to apply to each record in the database.
  * @data: User data to pass to the function.
  *
  * Apply a function to each record in a media database.
diff --git a/libdmapsharing/dmap-marshal.list b/libdmapsharing/dmap-marshal.list
index 99c4268..a25b0e7 100644
--- a/libdmapsharing/dmap-marshal.list
+++ b/libdmapsharing/dmap-marshal.list
@@ -1,2 +1,7 @@
 STRING:STRING
 VOID:ULONG,FLOAT
+STRING:ULONG,ULONG
+ULONG:VOID
+VOID:STRING,STRING
+VOID:STRING,BOOLEAN
+BOOLEAN:STRING
diff --git a/libdmapsharing/dmap-mdns-browser-avahi.c b/libdmapsharing/dmap-mdns-browser-avahi.c
index bf67bd8..51b40fb 100644
--- a/libdmapsharing/dmap-mdns-browser-avahi.c
+++ b/libdmapsharing/dmap-mdns-browser-avahi.c
@@ -232,11 +232,27 @@ dmap_mdns_browser_start (DMAPMdnsBrowser *browser,
         g_debug ("Browser already active");
         return TRUE;
     }
+    
+    gchar *service_type = NULL;
+
+    switch (browser->priv->service_type) {
+    case DMAP_MDNS_BROWSER_SERVICE_TYPE_DAAP:
+         service_type = "_daap._tcp";
+         break;
+    case DMAP_MDNS_BROWSER_SERVICE_TYPE_DPAP:
+         service_type = "_dpap._tcp";
+         break;
+    case DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP:
+         service_type = "_touch-remote._tcp";
+         break;
+    default:
+         break;
+    }
 
     browser->priv->service_browser = avahi_service_browser_new (browser->priv->client,
             AVAHI_IF_UNSPEC,
             AVAHI_PROTO_UNSPEC,
-            (browser->priv->service_type == DMAP_MDNS_BROWSER_SERVICE_TYPE_DAAP ? "_daap._tcp" :  "_dpap._tcp"),
+            service_type,
             NULL,
 #ifdef HAVE_AVAHI_0_6
             0,
@@ -366,6 +382,7 @@ resolve_cb (AvahiServiceResolver *service_resolver,
 {
     if (event == AVAHI_RESOLVER_FOUND) {
         gchar *name = NULL;
+        gchar *pair = NULL;
         gchar host[AVAHI_ADDRESS_STR_MAX];
         gboolean pp = FALSE;
         DMAPMdnsBrowserService *service;
@@ -392,6 +409,12 @@ resolve_cb (AvahiServiceResolver *service_resolver,
 		    }
                 } else if (strcmp (key, "Machine Name") == 0) {
                     name = g_strdup (value);
+                } else if (strcmp (key, "DvNm") == 0) {
+                    /* Remote's name is presented as DvNm in DACP */
+                    name = g_strdup(value);
+                } else if (strcmp (key, "Pair") == 0) {
+                    /* Pair is used when first connecting to a DACP remote */
+                    pair = g_strdup(value);
                 }
 
                 g_free (key);
@@ -410,6 +433,7 @@ resolve_cb (AvahiServiceResolver *service_resolver,
         service->name = name;
         service->host = g_strdup (host);
         service->port = port;
+        service->pair = pair;
         service->password_protected = pp;
         browser->priv->services = g_slist_append (browser->priv->services, service);
         g_signal_emit (browser,
@@ -427,11 +451,29 @@ dmap_mdns_browser_resolve (DMAPMdnsBrowser *browser,
 {
     AvahiServiceResolver *service_resolver;
 
+    gchar *service_type = NULL;
+
+    switch (browser->priv->service_type) {
+    case DMAP_MDNS_BROWSER_SERVICE_TYPE_DAAP:
+         service_type = "_daap._tcp";
+         break;
+    case DMAP_MDNS_BROWSER_SERVICE_TYPE_DPAP:
+         service_type = "_dpap._tcp";
+         break;
+    case DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP:
+         service_type = "_touch-remote._tcp";
+         break;
+    default:
+         break;
+    }
+    
+    g_debug ("Browsing for %s", service_type);
+
     service_resolver = avahi_service_resolver_new (browser->priv->client,
             AVAHI_IF_UNSPEC,
             AVAHI_PROTO_INET,
             name,
-            (browser->priv->service_type == DMAP_MDNS_BROWSER_SERVICE_TYPE_DAAP ? "_daap._tcp" :  "_dpap._tcp"),
+            service_type,
             domain,
             AVAHI_PROTO_UNSPEC,
 #ifdef HAVE_AVAHI_0_6
@@ -506,5 +548,6 @@ free_service (DMAPMdnsBrowserService *service)
     g_free (service->service_name);
     g_free (service->name);
     g_free (service->host);
+	g_free (service->pair);
     g_free (service);
 }
diff --git a/libdmapsharing/dmap-mdns-browser.h b/libdmapsharing/dmap-mdns-browser.h
index 1a5cf8a..a24387a 100644
--- a/libdmapsharing/dmap-mdns-browser.h
+++ b/libdmapsharing/dmap-mdns-browser.h
@@ -84,7 +84,8 @@ typedef enum
     DMAP_MDNS_BROWSER_SERVICE_TYPE_INVALID = 0,
     DMAP_MDNS_BROWSER_SERVICE_TYPE_DAAP,
     DMAP_MDNS_BROWSER_SERVICE_TYPE_DPAP,
-    DMAP_MDNS_BROWSER_SERVICE_TYPE_LAST = DMAP_MDNS_BROWSER_SERVICE_TYPE_DPAP
+    DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP,
+    DMAP_MDNS_BROWSER_SERVICE_TYPE_LAST = DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP
 } DMAPMdnsBrowserServiceType;
 
 typedef enum
@@ -100,6 +101,7 @@ struct _DMAPMdnsBrowserService
     gchar *host;
     guint port;
     gboolean password_protected;
+    gchar *pair;
 };
 
 struct _DMAPMdnsBrowserClass
diff --git a/libdmapsharing/dmap-mdns-publisher-avahi.c b/libdmapsharing/dmap-mdns-publisher-avahi.c
index 487f293..314adf6 100644
--- a/libdmapsharing/dmap-mdns-publisher-avahi.c
+++ b/libdmapsharing/dmap-mdns-publisher-avahi.c
@@ -55,6 +55,7 @@ struct DmapMdnsPublisherPrivate
 	guint            port;
 	char		*type_of_service;
 	gboolean         password_required;
+	gchar          **txt_records;
 };
 
 enum {
@@ -104,7 +105,8 @@ create_service (DmapMdnsPublisher *publisher,
 		GError             **error)
 {
 	int         ret;
-	const char *txt_record;
+	const char *password_record;
+	AvahiStringList *txt_records;
 
 	if (publisher->priv->entry_group == NULL) {
 		publisher->priv->entry_group = avahi_entry_group_new (publisher->priv->client,
@@ -133,12 +135,24 @@ create_service (DmapMdnsPublisher *publisher,
 #endif
 
 	if (publisher->priv->password_required) {
-		txt_record = "Password=true";
+		password_record = "Password=true";
 	} else {
-		txt_record = "Password=false";
+		password_record = "Password=false";
+	}
+	
+	txt_records = avahi_string_list_new(password_record, NULL);
+	
+	if (publisher->priv->txt_records) {
+		//g_debug("Number of txt records: %d", publisher->priv->txt_records);
+		gchar **txt_record = publisher->priv->txt_records;
+		while (*txt_record) {
+			g_debug ("TXT Record: %s", *txt_record);
+			txt_records = avahi_string_list_add(txt_records, *txt_record);
+			txt_record++;
+		}
 	}
 
-	ret = avahi_entry_group_add_service (publisher->priv->entry_group,
+	ret = avahi_entry_group_add_service_strlst (publisher->priv->entry_group,
 					     AVAHI_IF_UNSPEC,
 					     AVAHI_PROTO_UNSPEC,
 					     0,
@@ -147,8 +161,9 @@ create_service (DmapMdnsPublisher *publisher,
 					     NULL,
 					     NULL,
 					     publisher->priv->port,
-					     txt_record,
-					     NULL);
+					     txt_records);
+					     
+	avahi_string_list_free(txt_records);
 
 	if (ret < 0) {
 		g_set_error (error,
@@ -287,12 +302,40 @@ dmap_mdns_publisher_set_password_required (DmapMdnsPublisher *publisher,
 	return TRUE;
 }
 
+
+static gboolean
+publisher_set_txt_records_internal (DmapMdnsPublisher *publisher,
+					  gchar              **txt_records,
+					  GError             **error)
+{
+	g_strfreev (publisher->priv->txt_records);
+	publisher->priv->txt_records = g_strdupv (txt_records);
+	return TRUE;
+}
+
+gboolean
+dmap_mdns_publisher_set_txt_records (DmapMdnsPublisher *publisher,
+					      gchar              **txt_records,
+					      GError             **error)
+{
+        g_return_val_if_fail (publisher != NULL, FALSE);
+
+	publisher_set_txt_records_internal (publisher, txt_records, error);
+
+	if (publisher->priv->entry_group) {
+		refresh_service (publisher, error);
+	}
+
+	return TRUE;
+}
+
 gboolean
 dmap_mdns_publisher_publish (DmapMdnsPublisher *publisher,
 				const char          *name,
 				guint                port,
 				const char          *type_of_service,
 				gboolean             password_required,
+				gchar              **txt_records,
 				GError             **error)
 {
 	if (publisher->priv->client == NULL) {
@@ -308,6 +351,7 @@ dmap_mdns_publisher_publish (DmapMdnsPublisher *publisher,
 	publisher_set_port_internal (publisher, port, NULL);
 	publisher_set_type_of_service_internal (publisher, type_of_service, NULL);
 	publisher_set_password_required_internal (publisher, password_required, NULL);
+	publisher_set_txt_records_internal (publisher, txt_records, NULL);
 
 	return create_service (publisher, error);
 }
@@ -429,6 +473,8 @@ dmap_mdns_publisher_finalize (GObject *object)
 
 	g_free (publisher->priv->name);
 	g_free (publisher->priv->type_of_service);
+	
+	g_strfreev (publisher->priv->txt_records);
 
 	G_OBJECT_CLASS (dmap_mdns_publisher_parent_class)->finalize (object);
 }
diff --git a/libdmapsharing/dmap-mdns-publisher.h b/libdmapsharing/dmap-mdns-publisher.h
index 9ad83e6..ba5976a 100644
--- a/libdmapsharing/dmap-mdns-publisher.h
+++ b/libdmapsharing/dmap-mdns-publisher.h
@@ -75,6 +75,7 @@ gboolean             dmap_mdns_publisher_publish                (DmapMdnsPublish
 								    guint                port,
 								    const char          *type_of_service,
 								    gboolean             password_required,
+								    gchar              **txt_records,
 								    GError             **error);
 gboolean             dmap_mdns_publisher_set_name               (DmapMdnsPublisher *publisher,
 								    const char          *name,
@@ -85,6 +86,9 @@ gboolean             dmap_mdns_publisher_set_port               (DmapMdnsPublish
 gboolean             dmap_mdns_publisher_set_password_required  (DmapMdnsPublisher *publisher,
 								    gboolean             required,
 								    GError             **error);
+gboolean             dmap_mdns_publisher_set_txt_records        (DmapMdnsPublisher *publisher,
+								    gchar          **txt_records,
+								    GError         **error);
 gboolean             dmap_mdns_publisher_withdraw               (DmapMdnsPublisher *publisher,
 								    GError             **error);
 
diff --git a/libdmapsharing/dmap-share.c b/libdmapsharing/dmap-share.c
index dd2490f..3e2e039 100644
--- a/libdmapsharing/dmap-share.c
+++ b/libdmapsharing/dmap-share.c
@@ -55,7 +55,8 @@ enum {
 	PROP_AUTH_METHOD,
 	PROP_DB,
 	PROP_CONTAINER_DB,
-	PROP_TRANSCODE_MIMETYPE
+	PROP_TRANSCODE_MIMETYPE,
+	PROP_TXT_RECORDS
 };
 
 struct DMAPSharePrivate {
@@ -82,6 +83,9 @@ struct DMAPSharePrivate {
 	/* The media database */
 	DMAPDb *db;
 	DMAPContainerDb *container_db;
+	
+	/* TXT-RECORDS published by mDNS */
+	gchar **txt_records;
 
 	GHashTable *session_ids;
 };
@@ -203,6 +207,21 @@ static void databases_adapter (SoupServer *server,
 						 context);
 }
 
+static void ctrl_int_adapter (SoupServer *server,
+                              SoupMessage *message,
+                              const char *path,
+                              GHashTable *query,
+                              SoupClientContext *context,
+                              DMAPShare *share)
+{
+	DMAP_SHARE_GET_CLASS (share)->ctrl_int (share,
+	                                        server,
+	                                        message,
+	                                        path,
+	                                        query,
+	                                        context);
+}
+
 gboolean
 _dmap_share_server_start (DMAPShare *share)
 {
@@ -259,6 +278,9 @@ _dmap_share_server_start (DMAPShare *share)
 	soup_server_add_handler (share->priv->server, "/databases",
 				 (SoupServerCallback) databases_adapter,
 				 share, NULL);
+	soup_server_add_handler (share->priv->server, "/ctrl-int",
+				 (SoupServerCallback) ctrl_int_adapter,
+				 share, NULL);
 	soup_server_run_async (share->priv->server);
 
 	/* using direct since there is no g_uint_hash or g_uint_equal */
@@ -309,6 +331,7 @@ _dmap_share_publish_start (DMAPShare *share)
 					      share->priv->port,
 					      DMAP_SHARE_GET_CLASS (share)->get_type_of_service (share),
 					      password_required,
+					      share->priv->txt_records,
 					      &error);
 
 	if (res == FALSE) {
@@ -438,6 +461,9 @@ _dmap_share_set_property (GObject *object,
 		/* FIXME: get or dup? */
                 share->priv->transcode_mimetype = g_value_dup_string (value);
                 break;
+        case PROP_TXT_RECORDS:
+                share->priv->txt_records = g_value_dup_boxed (value);
+                break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -478,6 +504,9 @@ _dmap_share_get_property (GObject *object,
         case PROP_TRANSCODE_MIMETYPE:
                 g_value_set_string (value, share->priv->transcode_mimetype);
                 break;
+        case PROP_TXT_RECORDS:
+                g_value_set_boxed (value, share->priv->txt_records);
+                break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -504,6 +533,8 @@ _dmap_share_finalize (GObject *object)
 
 	g_object_unref (share->priv->db);
 	g_object_unref (share->priv->container_db);
+	
+	g_strfreev (share->priv->txt_records);
 
 	if (share->priv->publisher) {
 		g_object_unref (share->priv->publisher);
@@ -538,6 +569,7 @@ dmap_share_class_init (DMAPShareClass *klass)
 	klass->published      = _dmap_share_published;
 	klass->name_collision = _dmap_share_name_collision;
 	klass->databases      = _dmap_share_databases;
+	klass->ctrl_int       = _dmap_share_ctrl_int;
 
 	g_object_class_install_property (object_class,
 					 PROP_NAME,
@@ -594,6 +626,15 @@ dmap_share_class_init (DMAPShareClass *klass)
                                                              "Set mimetype of stream after transcoding",
                                                              NULL,
                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                                             
+
+	g_object_class_install_property (object_class,
+                                         PROP_TXT_RECORDS,
+                                         g_param_spec_boxed ("txt-records",
+                                                             "TXT-Records",
+                                                             "Set TXT-Records used for MDNS publishing",
+                                                             G_TYPE_STRV,
+                                                             G_PARAM_READWRITE));
 
 	g_type_class_add_private (klass, sizeof (DMAPSharePrivate));
 }
@@ -1102,57 +1143,176 @@ _dmap_share_build_filter (gchar *filterstr)
 	 * 'daap.songgenre:Foo'+'daap.songartist:Bar'+'daap.songalbum:Baz'
 	 * or (iPhoto '09)
 	 * ('daap.idemid:1000','dmap:itemid:1001')
+	 * or (DACP):
+	 * ('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32') 'daap.songartist!:'
          */
 
-	size_t len;
+	gchar *next_char;
+	GString *value;
+	//gboolean save_value;
+	gboolean is_key;
+	gboolean is_value;
+	gboolean new_group;
+	gboolean accept;
+	gboolean negate;
+	gint parentheses_count;
+	gint quotes_count;
+	FilterDefinition *def;
+	
 	GSList *list = NULL;
+	GSList *filter = NULL;
 
 	g_debug ("Filter string is %s.", filterstr);
-
-	len = strlen (filterstr);
-	filterstr = *filterstr == '(' ? filterstr + 1 : filterstr;
-	/* FIXME: I thought this should be -1, but there is a trailing ' ' in iPhoto '09: */
-	filterstr[len - 2] = filterstr[len - 2] == ')' ? 0x00 : filterstr[len - 2];
-
-	if (filterstr != NULL) {
-		int i;
-		gchar **t1 = g_strsplit (filterstr, ",", 0);
-
-		for (i = 0; t1[i]; i++) {
-			int j;
-			GSList *filter = NULL;
-			gchar **t2;
-
-			t2 = _dmap_db_strsplit_using_quotes (t1[i]);
-
-			for (j = 0; t2[j]; j++) {
-				FilterDefinition *def;
-				gchar **t3;
-
-				t3 = g_strsplit (t2[j], ":", 0);
-
+	
+	next_char = filterstr;
+	
+	parentheses_count = 0;
+	quotes_count = 0;
+	is_key = TRUE;
+	is_value = FALSE;
+	new_group = FALSE;
+	negate = FALSE;
+	value = NULL;
+	def = NULL;
+	
+	// The query string is divided in groups of AND separated by a space,
+	// each group containing queries of OR separated by comma enclosed in
+	// parentheses. Queries are key, value pairs separated by ':' enclosed 
+	// in quotes or not.
+	// The result from this is a list of lists. Here is an example:
+	// String is ('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32') 'daap.songartist!:'
+	// list:
+	// |-> filter1: -> com.apple.itunes.mediakind = 1
+	// |   |        -> com.apple.itunes.mediakind = 32
+	// |-> filter1: -> daap.songartist! = (null)
+	// This basically means that the query is (filter1 AND filter2), and
+	// filter1 is (com.apple.itunes.mediakind = 1) OR (com.apple.itunes.mediakind = 32)
+	while (TRUE) {
+		// We check each character to see if it should be included in
+		// the current value (the current value will become the query key
+		// or the query value). Anything that is not a special character
+		// handled below will be accepted (this means weird characters
+		// might appear in names).
+		// Handling of unicode characters is unknown, but as long it 
+		// doesn't appear as a character handled below, it will be
+		// included.
+		// This parser will result in unknown behaviour on bad-formatted
+		// queries (it does not check for syntax errors), but should not 
+		// crash. In this way it may accept much more syntaxes then
+		// other parsers.
+		accept = FALSE;
+		// A slash can escape characters such as ', so add the character
+		// after the slash.
+		if (*next_char == '\\') {
+			accept = TRUE;
+			next_char++;
+		} else {
+			switch (*next_char) {
+				case '(':
+					parentheses_count++;
+					break;
+				case ')':
+					parentheses_count--;
+					break;
+				case '\'':
+					if (quotes_count > 0) {
+						quotes_count = 0;
+					} else {
+						quotes_count = 1;
+					}
+					break;
+				case ' ':
+					if (quotes_count > 0) {
+						accept = TRUE;
+					} else {
+						new_group = TRUE;
+					}
+					break;
+				case ':':
+					// Inside values, they will be included in the 
+					// query string, otherwise it indicates there
+					// will be a value next.
+					if (is_value) {
+						accept = TRUE;
+					} else if (is_key) {
+						is_value = TRUE;
+					}
+					break;
+				case ',':
+				case '+':
+				case '\0':
+					// Don't accept these caracters
+					break;
+				case '!':
+					if (value) {
+						negate = TRUE;
+						break;
+					}
+				default:
+					accept = TRUE;
+			}
+		}
+		//g_debug ("Char: %c, Accept: %s", *next_char, accept?"TRUE":"FALSE");
+		//Is the next character to be accepted?
+		if (accept) {
+			if (!value) {
+				value = g_string_new ("");
+			}
+			g_string_append_c (value, *next_char);
+		} else if (value != NULL && *next_char != '!') {
+			// If we won't accept this character, we are ending a 
+			// query key or value, so we should save them in a new
+			// FilterDefinition. If is_value is TRUE, we will still
+			// parse the query value, so we must keep our def around.
+			// Otherwise, save it in the list of filters.
+			if (!def) {
 				def = g_new0 (FilterDefinition, 1);
-				def->key   = g_strdup (t3[0]);
-				def->value = g_strdup (t3[1]);
-
-				if (g_strcasecmp ("dmap.itemid", t3[0]) == 0) {
-					def->is_string = FALSE;
-				} else {
-					def->is_string = TRUE;
-				}
-
-				if (def != NULL)
-					filter = g_slist_append (filter, def);
-
-				g_strfreev (t3);
 			}
-
+			if (is_key) {
+				def->key = value->str;
+				g_string_free (value, FALSE);
+				def->negate = negate;
+				negate = FALSE;
+				is_key = FALSE;
+			} else {
+				def->value = value->str;
+				g_string_free (value, FALSE);
+				is_value = FALSE;
+				is_key = TRUE;
+			}
+			value = NULL;
+			if (!is_value) {
+				filter = g_slist_append (filter, def);
+				def = NULL;
+			}
+		}
+		if (new_group && filter) {
 			list = g_slist_append (list, filter);
-
-			g_strfreev (t2);
+			filter = NULL;
+			new_group = FALSE;
 		}
-		g_strfreev (t1);
+		// Only handle \0 here so we can handle remaining values above.
+		if (*next_char == '\0')
+			break;
+		next_char++;
+	};
+
+	// Any remaining def or filter must still be handled here.
+	if (def) {
+		filter = g_slist_append (filter, def);
 	}
+	
+	if (filter) {
+		list = g_slist_append (list, filter);
+	}	
+
+	GSList *ptr1, *ptr2;
+
+        for (ptr1 = list; ptr1 != NULL; ptr1 = ptr1->next) {
+                for (ptr2 = ptr1->data; ptr2 != NULL; ptr2 = ptr2->next) {
+                	g_debug ("%s = %s", ((FilterDefinition *) ptr2->data)->key, ((FilterDefinition *) ptr2->data)->value);
+                }
+        }
 
         return list;
 }
@@ -1170,6 +1330,30 @@ dmap_share_free_filter (GSList *filter)
         }
 }
 
+typedef struct {
+	gchar *artist;
+	int tracks;
+} GroupInfo;
+
+static void
+group_items (gpointer key, DMAPRecord *record, GHashTable *groups)
+{
+	gchar *album, *artist;
+	GroupInfo *group_info;
+	
+	g_object_get (record, "songartist", &artist, "songalbum", &album, NULL);
+	group_info = g_hash_table_lookup (groups, album);
+	if (!group_info) {
+		group_info = g_new0 (GroupInfo, 1);
+		g_hash_table_insert (groups, album, group_info);
+		group_info->artist = artist;
+	} else {
+		g_free (album);
+		g_free (artist);
+	}
+	(group_info->tracks)++;
+}
+
 static void
 debug_param (gpointer key, gpointer val, gpointer user_data)
 {
@@ -1177,6 +1361,22 @@ debug_param (gpointer key, gpointer val, gpointer user_data)
 }
 
 void
+_dmap_share_ctrl_int (DMAPShare *share,
+		      SoupServer        *server,
+		      SoupMessage       *message,
+		      const char        *path,
+		      GHashTable        *query,
+		      SoupClientContext *context)
+{
+	g_debug ("Path is %s.", path);
+	if (query) {
+		g_hash_table_foreach (query, debug_param, NULL);
+	}
+	
+	g_debug ("ctrl-int not implemented");
+}
+
+void
 _dmap_share_databases (DMAPShare *share,
 		       SoupServer        *server,
 		       SoupMessage       *message,
@@ -1189,6 +1389,9 @@ _dmap_share_databases (DMAPShare *share,
 
 	g_debug ("Path is %s.", path);
 	g_hash_table_foreach (query, debug_param, NULL);
+	
+	
+	g_debug ("URI: %s", soup_uri_to_string (soup_message_get_uri (message), FALSE));
 
 	if (! _dmap_share_session_id_validate (share, context, message, query, NULL)) {
 		soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
@@ -1235,6 +1438,71 @@ _dmap_share_databases (DMAPShare *share,
 		dmap_structure_destroy (avdb);
 
 		g_free (nameprop);
+	} else if (g_ascii_strcasecmp ("/1/groups", rest_of_path) == 0) {
+	/* ADBS database songs
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT
+	 * 			attrs
+	 * 		MLIT
+	 * 		...
+	 */		 
+
+		GSList *filter_def;
+		gchar *record_query;
+		GHashTable *records = NULL;
+		GHashTable *groups;
+		GHashTableIter iter;
+		gchar *group_name;
+		GroupInfo *group_info;
+		GNode *agal;
+		GNode *mlcl;
+		GNode *mlit;
+		gint num;
+
+		if (g_strcmp0 (g_hash_table_lookup (query, "group-type"), "albums") != 0) {
+			g_warning ("Unsupported grouping");
+			soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+			return;
+		}
+
+		record_query = g_hash_table_lookup (query, "query");
+		filter_def = _dmap_share_build_filter (record_query);
+		records = dmap_db_apply_filter (DMAP_DB (share->priv->db), filter_def);
+
+		groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+		g_hash_table_foreach (records, (GHFunc) group_items, groups);
+		
+		agal = dmap_structure_add (NULL, DMAP_CC_AGAL);
+		dmap_structure_add (agal, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+		dmap_structure_add (agal, DMAP_CC_MUTY, 0);
+		
+		num = g_hash_table_size (groups);
+		dmap_structure_add (agal, DMAP_CC_MTCO, (gint32) num);
+		dmap_structure_add (agal, DMAP_CC_MRCO, (gint32) num);
+		
+		mlcl = dmap_structure_add (agal, DMAP_CC_MLCL);
+
+		g_hash_table_iter_init (&iter, groups);
+		while (g_hash_table_iter_next (&iter, ((gpointer*)&group_name), ((gpointer*)&group_info))) {
+			mlit = dmap_structure_add (mlcl, DMAP_CC_MLIT);
+			dmap_structure_add (mlit, DMAP_CC_MIID, (gint) g_str_hash (group_name));
+			dmap_structure_add (mlit, DMAP_CC_MPER, (gint64) g_str_hash (group_name));
+			dmap_structure_add (mlit, DMAP_CC_MINM, group_name);
+			dmap_structure_add (mlit, DMAP_CC_ASAA, group_info->artist);
+			dmap_structure_add (mlit, DMAP_CC_MIMC, (gint32) group_info->tracks);
+
+			g_free (group_info->artist);
+			g_hash_table_iter_remove (&iter);
+		}
+
+		_dmap_share_message_set_from_dmap_structure (share, message, agal);
+
+		g_hash_table_destroy (groups);
+		dmap_structure_destroy (agal);
 	} else if (g_ascii_strcasecmp ("/1/items", rest_of_path) == 0) {
 	/* ADBS database songs
 	 * 	MSTT status
diff --git a/libdmapsharing/dmap-share.h b/libdmapsharing/dmap-share.h
index 3da79d5..88dc694 100644
--- a/libdmapsharing/dmap-share.h
+++ b/libdmapsharing/dmap-share.h
@@ -112,6 +112,8 @@ typedef struct {
 	void              (*message_add_standard_headers) (DMAPShare *share,
 							   SoupMessage *msg);
 	struct DMAPMetaDataMap * (*get_meta_data_map)     (DMAPShare *share);
+	void              (*add_server_handlers)          (DMAPShare *share, 
+	                                                   SoupServer *server);
 	void              (*add_entry_to_mlcl)            (gpointer id,
 							   DMAPRecord *record,
 							   gpointer mb);
@@ -148,6 +150,10 @@ typedef struct {
 	void	  (*update)        (DMAPShare *share, SoupServer *server,
 				    SoupMessage *message, const char *path,
 				    GHashTable *query, SoupClientContext *ctx);
+				    
+	void	  (*ctrl_int)        (DMAPShare *share, SoupServer *server,
+				    SoupMessage *message, const char *path,
+				    GHashTable *query, SoupClientContext *ctx);
 
 	/* Virtual methods: MDNS callbacks */
 	void	  (*published)	   (DMAPShare         *share,
@@ -252,6 +258,28 @@ void _dmap_share_update (DMAPShare *share,
 		  GHashTable        *query,
 		  SoupClientContext *context);
 
+void
+_dmap_share_databases (DMAPShare *share,
+                       SoupServer        *server,
+		       SoupMessage       *message,
+		       const char        *path,
+		       GHashTable        *query,
+		       SoupClientContext *context);
+
+void _dmap_share_ctrl_int (DMAPShare         *share,
+		           SoupServer        *server,
+		           SoupMessage       *message,
+		           const char        *path,
+		           GHashTable        *query,
+		           SoupClientContext *context);
+
+void
+_dmap_share_ctrl_int_adapter (DMAPShare         *share,
+		              SoupServer        *server,
+		              SoupMessage       *message,
+		              const char        *path,
+		              GHashTable        *query,
+		              SoupClientContext *context);
 
 /* Virtual methods: MDNS callbacks */
 void _dmap_share_published     (DMAPShare         *share,
@@ -262,13 +290,6 @@ void _dmap_share_name_collision(DMAPShare         *share,
 			       DmapMdnsPublisher *publisher,
 			       const char        *name);
 
-void
-_dmap_share_databases (DMAPShare *share,
-                       SoupServer        *server,
-		       SoupMessage       *message,
-		       const char        *path,
-		       GHashTable        *query,
-		       SoupClientContext *context);
 
 #endif /* __DMAP_SHARE_H */
 
diff --git a/libdmapsharing/dmap-structure.c b/libdmapsharing/dmap-structure.c
index 5e9470d..c138f04 100644
--- a/libdmapsharing/dmap-structure.c
+++ b/libdmapsharing/dmap-structure.c
@@ -85,6 +85,8 @@ static const DMAPContentCodeDefinition cc_defs[] = {
     {DMAP_CC_ABGN, MAKE_CONTENT_CODE('a','b','g','n'), "daap.browsegenrelisting", "abgn", DMAP_TYPE_CONTAINER},
     {DMAP_CC_ADBS, MAKE_CONTENT_CODE('a','d','b','s'), "daap.returndatabasesongs", "adbs", DMAP_TYPE_CONTAINER},
     {DMAP_CC_ASAL, MAKE_CONTENT_CODE('a','s','a','l'), "daap.songalbum", "asal", DMAP_TYPE_STRING},
+    {DMAP_CC_ASAI, MAKE_CONTENT_CODE('a','s','a','i'), "daap.songalbumid", "asai", DMAP_TYPE_INT},
+    {DMAP_CC_ASAA, MAKE_CONTENT_CODE('a','s','a','a'), "daap.songalbumartist", "asaa", DMAP_TYPE_STRING},
     {DMAP_CC_ASAR, MAKE_CONTENT_CODE('a','s','a','r'), "daap.songartist", "asar", DMAP_TYPE_STRING},
     {DMAP_CC_ASBT, MAKE_CONTENT_CODE('a','s','b','t'), "daap.songsbeatsperminute", "asbt", DMAP_TYPE_SHORT},
     {DMAP_CC_ASBR, MAKE_CONTENT_CODE('a','s','b','r'), "daap.songbitrate", "asbr", DMAP_TYPE_SHORT},
@@ -121,6 +123,7 @@ static const DMAPContentCodeDefinition cc_defs[] = {
     {DMAP_CC_AESV, MAKE_CONTENT_CODE('a','e','S','V'), "com.applie.itunes.music-sharing-version", "aesv", DMAP_TYPE_INT},
     {DMAP_CC_MSAS, MAKE_CONTENT_CODE('m','s','a','s'), "daap.authentication.schemes", "msas", DMAP_TYPE_BYTE},
     {DMAP_CC_AGRP, MAKE_CONTENT_CODE('a','g','r','p'), "daap.songgrouping", "agrp", DMAP_TYPE_STRING},
+    {DMAP_CC_AGAL, MAKE_CONTENT_CODE('a','g','a','l'), "daap.albumgrouping", "agal", DMAP_TYPE_CONTAINER},
     {DMAP_CC_ASCP, MAKE_CONTENT_CODE('a','s','c','p'), "daap.songcomposer", "ascp", DMAP_TYPE_STRING},
     {DMAP_CC_PPRO, MAKE_CONTENT_CODE('p','p','r','o'), "dpap.protocolversion", "ppro", DMAP_TYPE_VERSION},
     {DMAP_CC_PASP, MAKE_CONTENT_CODE('p','a','s','p'), "dpap.aspectratio", "pasp", DMAP_TYPE_STRING},
@@ -135,7 +138,43 @@ static const DMAPContentCodeDefinition cc_defs[] = {
     {DMAP_CC_PRAT, MAKE_CONTENT_CODE('p','r','a','t'), "dpap.imagerating", "prat", DMAP_TYPE_INT},
     {DMAP_CC_PCMT, MAKE_CONTENT_CODE('p','c','m','t'), "dpap.imagecomments", "pcmt", DMAP_TYPE_STRING},
     {DMAP_CC_PRET, MAKE_CONTENT_CODE('p','r','e','t'), "dpap.pret", "pret", DMAP_TYPE_STRING},
-    {DMAP_CC_AEHV, MAKE_CONTENT_CODE('a','e','H','V'), "com.apple.itunes.has-video", "aeHV", DMAP_TYPE_BYTE}
+    {DMAP_CC_AEHV, MAKE_CONTENT_CODE('a','e','H','V'), "com.apple.itunes.has-video", "aeHV", DMAP_TYPE_BYTE},
+    
+    /* DACP */
+    {DMAP_CC_CMPA, MAKE_CONTENT_CODE('c','m','p','a'), "dacp.contentcontainer", "cmpa", DMAP_TYPE_CONTAINER},
+    {DMAP_CC_CMNM, MAKE_CONTENT_CODE('c','m','n','m'), "dacp.contentname", "cmnm", DMAP_TYPE_STRING},
+    {DMAP_CC_CMTY, MAKE_CONTENT_CODE('c','m','t','y'), "dacp.contentvalue", "cmty", DMAP_TYPE_STRING},
+    {DMAP_CC_CMPG, MAKE_CONTENT_CODE('c','m','p','g'), "dacp.passguid", "cmpy", DMAP_TYPE_INT64},
+    
+    {DMAP_CC_CACI, MAKE_CONTENT_CODE('c','a','c','i'), "dacp.controlint", "caci", DMAP_TYPE_CONTAINER},
+    {DMAP_CC_CAPS, MAKE_CONTENT_CODE('c','a','p','s'), "dacp.playstatus", "caci", DMAP_TYPE_BYTE},
+    {DMAP_CC_CASH, MAKE_CONTENT_CODE('c','a','s','h'), "dacp.shufflestate", "caci", DMAP_TYPE_BYTE},
+    {DMAP_CC_CARP, MAKE_CONTENT_CODE('c','a','r','p'), "dacp.repeatstate", "caci", DMAP_TYPE_BYTE},
+    {DMAP_CC_CAAS, MAKE_CONTENT_CODE('c','a','a','s'), "dacp.albumshuffle", "caas", DMAP_TYPE_INT},
+    {DMAP_CC_CAAR, MAKE_CONTENT_CODE('c','a','a','r'), "dacp.albumrepeat", "caci", DMAP_TYPE_INT},    
+    {DMAP_CC_CAIA, MAKE_CONTENT_CODE('c','a','i','a'), "dacp.isavailiable", "caia", DMAP_TYPE_BYTE},
+    {DMAP_CC_CANP, MAKE_CONTENT_CODE('c','a','n','p'), "dacp.nowplaying", "canp", DMAP_TYPE_INT64},
+    {DMAP_CC_CANN, MAKE_CONTENT_CODE('c','a','n','n'), "dacp.nowplayingtrack", "cann", DMAP_TYPE_STRING},
+    {DMAP_CC_CANA, MAKE_CONTENT_CODE('c','a','n','a'), "dacp.nowplayingartist", "cana", DMAP_TYPE_STRING},
+    {DMAP_CC_CANL, MAKE_CONTENT_CODE('c','a','n','l'), "dacp.nowplayingalbum", "canl", DMAP_TYPE_STRING},
+    {DMAP_CC_CANG, MAKE_CONTENT_CODE('c','a','n','g'), "dacp.nowplayinggenre", "cang", DMAP_TYPE_STRING},
+    {DMAP_CC_CANT, MAKE_CONTENT_CODE('c','a','n','t'), "dacp.remainingtime", "cant", DMAP_TYPE_INT},
+    {DMAP_CC_CASP, MAKE_CONTENT_CODE('c','a','s','p'), "dacp.speakers", "casp", DMAP_TYPE_CONTAINER},
+    {DMAP_CC_CASS, MAKE_CONTENT_CODE('c','a','s','s'), "dacp.ss", "cass", DMAP_TYPE_BYTE},
+    {DMAP_CC_CAST, MAKE_CONTENT_CODE('c','a','s','t'), "dacp.tracklength", "cast", DMAP_TYPE_INT},
+    {DMAP_CC_CASU, MAKE_CONTENT_CODE('c','a','s','u'), "dacp.su", "casu", DMAP_TYPE_BYTE},
+    {DMAP_CC_CASG, MAKE_CONTENT_CODE('c','a','s','t'), "dacp.sg", "caSG", DMAP_TYPE_BYTE},
+    
+    {DMAP_CC_CMCP, MAKE_CONTENT_CODE('c','m','c','p'), "dmcp.controlprompt", "cmcp", DMAP_TYPE_CONTAINER},
+    {DMAP_CC_CMGT, MAKE_CONTENT_CODE('c','m','g','t'), "dmcp.getpropertyresponse", "cmgt", DMAP_TYPE_CONTAINER},
+    {DMAP_CC_CMIK, MAKE_CONTENT_CODE('c','m','i','k'), "dmcp.ik", "cmik", DMAP_TYPE_BYTE},
+    {DMAP_CC_CMSP, MAKE_CONTENT_CODE('c','m','s','p'), "dmcp.ik", "cmsp", DMAP_TYPE_BYTE},
+    {DMAP_CC_CMST, MAKE_CONTENT_CODE('c','m','s','t'), "dmcp.status", "cmst", DMAP_TYPE_CONTAINER},
+    {DMAP_CC_CMSV, MAKE_CONTENT_CODE('c','m','s','v'), "dmcp.sv", "cmsv", DMAP_TYPE_BYTE},
+    {DMAP_CC_CMSR, MAKE_CONTENT_CODE('c','m','s','r'), "dmcp.mediarevision", "cmsr", DMAP_TYPE_INT},
+    {DMAP_CC_CMMK, MAKE_CONTENT_CODE('c','m','m','k'), "dmcp.mediakind", "cmmk", DMAP_TYPE_INT},
+    {DMAP_CC_CMVO, MAKE_CONTENT_CODE('c','m','v','o'), "dmcp.volume", "cmvo", DMAP_TYPE_INT},
+    
 };
 
 const gchar * 
@@ -300,6 +339,8 @@ dmap_content_code_read_from_buffer (const gchar *buf)
             return cc_defs[i].code;
         }
     }
+    
+    g_warning ("Content code %4s is invalid.", buf);
 
     return DMAP_CC_INVALID;
 }
@@ -338,7 +379,7 @@ dmap_structure_parse_container_buffer (GNode *parent,
         
         cc = dmap_content_code_read_from_buffer ((const gchar*)&(buf[l]));
         if (cc == DMAP_CC_INVALID) {
-            g_debug ("Invalid/Unknown content_code recieved\n");
+            //g_debug ("Invalid/Unknown content_code recieved\n");
             return;
         }
         l += 4;
@@ -415,7 +456,7 @@ dmap_structure_parse_container_buffer (GNode *parent,
                 gint64 i = 0;
         
                 if (codesize == 8) {
-                    i = DMAP_READ_UINT16_BE(&(buf[l]));
+                    i = DMAP_READ_UINT64_BE(&(buf[l]));
                 }
                 
                 item->size = 8;
diff --git a/libdmapsharing/dmap-structure.h b/libdmapsharing/dmap-structure.h
index dbd6000..5a42976 100644
--- a/libdmapsharing/dmap-structure.h
+++ b/libdmapsharing/dmap-structure.h
@@ -84,6 +84,8 @@ typedef enum {
 	DMAP_CC_ABGN,
 	DMAP_CC_ADBS,
 	DMAP_CC_ASAL,
+	DMAP_CC_ASAI,
+	DMAP_CC_ASAA,
 	DMAP_CC_ASAR, 
 	DMAP_CC_ASBT,
 	DMAP_CC_ASBR,
@@ -120,6 +122,7 @@ typedef enum {
 	DMAP_CC_AESV,
 	DMAP_CC_MSAS,
 	DMAP_CC_AGRP,
+	DMAP_CC_AGAL,
 	DMAP_CC_ASCP,
 	DMAP_CC_PPRO, 
 	DMAP_CC_PASP,
@@ -136,7 +139,42 @@ typedef enum {
 	DMAP_CC_PRET,
         
 	/* iTunes 6.02+ */
-	DMAP_CC_AEHV
+	DMAP_CC_AEHV,
+	
+	/* DACP */
+	DMAP_CC_CMPA,
+	DMAP_CC_CMNM,
+	DMAP_CC_CMTY,
+	DMAP_CC_CMPG,
+	
+	DMAP_CC_CACI,
+	DMAP_CC_CAPS,
+	DMAP_CC_CASH,
+	DMAP_CC_CARP,
+	DMAP_CC_CAAS,
+	DMAP_CC_CAAR,
+	DMAP_CC_CAIA,
+	DMAP_CC_CANP,
+	DMAP_CC_CANN,
+	DMAP_CC_CANA,
+	DMAP_CC_CANL,
+	DMAP_CC_CANG,
+	DMAP_CC_CANT,
+	DMAP_CC_CASP,
+	DMAP_CC_CASS,
+	DMAP_CC_CAST,
+	DMAP_CC_CASU,
+	DMAP_CC_CASG,
+
+	DMAP_CC_CMCP,
+	DMAP_CC_CMGT,
+	DMAP_CC_CMIK,
+	DMAP_CC_CMSP,
+	DMAP_CC_CMST,
+	DMAP_CC_CMSV,
+	DMAP_CC_CMSR,
+	DMAP_CC_CMMK,
+	DMAP_CC_CMVO
 } DMAPContentCode;
 
 typedef struct _DMAPStructureItem DMAPStructureItem;
diff --git a/libdmapsharing/dmap-utils.h b/libdmapsharing/dmap-utils.h
index ec76004..6cc8df4 100644
--- a/libdmapsharing/dmap-utils.h
+++ b/libdmapsharing/dmap-utils.h
@@ -31,6 +31,7 @@ G_BEGIN_DECLS
 #define _DMAP_GET(__data, __size, __end) \
     (GUINT##__size##_FROM_##__end (* ((guint##__size *) (__data))))
 
+#define DMAP_READ_UINT64_BE(data) _DMAP_GET (data, 64, BE)
 #define DMAP_READ_UINT32_BE(data) _DMAP_GET (data, 32, BE)
 #define DMAP_READ_UINT16_BE(data) _DMAP_GET (data, 16, BE)
 #define DMAP_READ_UINT8(data)     (* ((guint8 *) (data)))
@@ -40,6 +41,15 @@ G_BEGIN_DECLS
 #define _DMAP_GET(__data, __idx, __size, __shift) \
     (((guint##__size) (((guint8 *) (__data))[__idx])) << __shift)
 
+#define DMAP_READ_UINT64_BE(data)  \
+    (_DMAP_GET (data, 0, 64, 56) | \
+     _DMAP_GET (data, 1, 64, 48) | \
+     _DMAP_GET (data, 2, 64, 40) | \
+     _DMAP_GET (data, 3, 64, 32) | \
+     _DMAP_GET (data, 4, 64, 24) | \
+     _DMAP_GET (data, 5, 64, 16) | \
+     _DMAP_GET (data, 6, 64,  8) | \
+     _DMAP_GET (data, 7, 64,  0))
 #define DMAP_READ_UINT32_BE(data) \
     (_DMAP_GET (data, 0, 32, 24) | \
      _DMAP_GET (data, 1, 32, 16) | \
