[Rhythmbox-devel] music sharing patch #2



-
I've got another music sharing patch for everyone to play with.

Lots of reorganization & changes from the first one, including the much
desired ability to share your music via RB.

The patch is /everything/ against CVS (not split into 'core' & 'daap'
parts like last time), mostly because I don't have the time to split it
and test the patches - I'm going out of town tonight and won't be back
til next Sunday.  I felt it'd be good to get this out there and get some
feedback as it is.

As before, you'll still need the patch at
http://bugzilla.gnome.org/show_bug.cgi?id=312114 against gst-plugins.

You'll need libsoup to browse shares, and libsoup & howl to publish your
own share (and the development headers/libraries probably, since you'll
be compiling this yourself).

Like I said, I'll be out of town til next Sunday, I hope to have a few
bug reports/suggestions when I return.

-
charlie
diff -x CVS -x cvs -urN rhythmbox/configure.ac myrhythmbox/configure.ac
--- rhythmbox/configure.ac	2005-07-20 10:20:32.000000000 -0400
+++ myrhythmbox/configure.ac	2005-08-08 20:06:51.849925112 -0400
@@ -156,6 +156,29 @@
    AC_DEFINE(ENABLE_TAG_WRITING, 1, [Define if tag writing should be enabled])
 fi
 
+dnl DAAP (iTunes Music Shares)
+AC_ARG_ENABLE(daap,
+	      AC_HELP_STRING([--enable-daap],
+			     [Enable Digital Audio Access Protocol in rhythmbox **EXPERIMENTAL**]))
+AM_CONDITIONAL(USE_DAAP, test x"$enable_daap" = xyes)
+if test x"$enable_daap" = xyes; then
+   AC_MSG_WARN([DAAP support is experimental, and may cause Rhythmbox to crash uncontrollably, use at your own risk])
+   AC_DEFINE(WITH_DAAP_SUPPORT, 1, [Define if daap should be enabled])
+   PKG_CHECK_MODULES(SOUP,                            \
+		libsoup-2.2,
+		have_libsoup=yes)
+   if test x"$have_libsoup" = xyes; then
+      AC_DEFINE(WITH_DAAP_BROWSE_SUPPORT, 1, [Define if daap browsing should be enabled])
+      AM_CONDITIONAL(USE_DAAP_BROWSE, test "x$have_libsoup" = "xyes")
+      PKG_CHECK_MODULES(HOWL,
+		howl,
+		have_howl=yes)
+      if test x"$have_howl" = xyes; then
+	 AC_DEFINE(WITH_DAAP_SHARE_SUPPORT, 1, [Define if daap sharing should be enabled])
+	 AM_CONDITIONAL(USE_DAAP_SHARE, test "x$have_howl" = "xyes")
+      fi
+   fi
+fi
 
 dnl AC_CHECK_LIB(lirc_client, lirc_init,
 dnl 		[ AC_CHECK_HEADER(lirc/lirc_client.h,
@@ -424,6 +447,7 @@
 remote/Makefile
 remote/bonobo/Makefile
 remote/dbus/Makefile
+daapsharing/Makefile
 shell/Makefile
 data/Makefile
 data/ui/Makefile
@@ -481,6 +505,11 @@
 else
 	AC_MSG_NOTICE([   iPod integration disabled])
 fi
+if test x"$enable_daap" = xyes; then
+	AC_MSG_NOTICE([** DAAP shares enabled])
+else
+	AC_MSG_NOTICE([   DAAP shares disabled])
+fi
 if test x"$enable_cd_burner" = xyes; then
 	AC_MSG_NOTICE([** CD burner support is enabled])
 else
diff -x CVS -x cvs -urN rhythmbox/daapsharing/daap-sharing.c myrhythmbox/daapsharing/daap-sharing.c
--- rhythmbox/daapsharing/daap-sharing.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/daap-sharing.c	2005-08-08 05:17:08.000000000 -0400
@@ -0,0 +1,149 @@
+/*
+ *  Implmentation of DAAP (iTunes Music Sharing) sharing
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include "daap-sharing.h"
+#include "rb-daap-share.h"
+#include "rb-debug.h"
+#include "rb-dialog.h"
+#include "rb-playlist-manager.h"
+#include "eel-gconf-extensions.h"
+#include <libgnome/gnome-i18n.h>
+#include <string.h>
+
+#define CONF_ENABLE_SHARING "/apps/rhythmbox/sharing/enable_sharing"
+#define CONF_SHARE_NAME "/apps/rhythmbox/sharing/share_name"
+
+static RBDAAPShare *share = NULL;
+
+static void create_share (RBShell *shell)
+{
+	RhythmDB *db;
+	RBPlaylistManager *playlist_manager;
+	
+	gchar *name;
+	
+	rb_debug ("initialize daap sharing\n");
+		
+	name = eel_gconf_get_string (CONF_SHARE_NAME);
+
+	if (name == NULL || *name == '\0') {
+		const gchar *real_name;
+
+		g_free(name);
+
+		real_name = g_get_real_name ();
+		if (strcmp (real_name, "Unknown") == 0) {
+			real_name = g_get_user_name ();
+		}
+		
+		name = g_strconcat (real_name, "'s Music", NULL);
+		eel_gconf_set_string (CONF_SHARE_NAME, name);
+	}
+	
+	g_object_get (G_OBJECT (shell), "db", &db, "playlist-manager", &playlist_manager, NULL);
+	
+	share = rb_daap_share_new (name, db, playlist_manager);
+
+	g_object_unref (db);
+	g_object_unref (playlist_manager);
+	g_free (name);
+
+	return;
+}
+
+static void enable_sharing_changed_cb (GConfClient *client,
+				       guint cnxn_id,
+				       GConfEntry *entry,
+				       RBShell *shell)
+{
+	gboolean enabled;
+
+	enabled = eel_gconf_get_boolean (CONF_ENABLE_SHARING);
+
+	if (enabled) {
+		create_share (shell);
+	} else {
+		rb_debug ("shutdown daap sharing\n");
+
+		g_object_unref (share);
+		share = NULL;
+	}
+
+	return;
+}
+
+static void share_name_changed_cb (GConfClient *client,
+				   guint cnxn_id,
+				   GConfEntry *entry,
+				   RBShell *shell)
+{
+	gchar *name;
+
+	name = eel_gconf_get_string (CONF_SHARE_NAME);
+
+	if (share) {
+		g_object_set (G_OBJECT (share), "name", name, NULL);
+	}
+
+	g_free (name);
+	
+	return;
+}
+
+
+void daap_sharing_init (RBShell *shell)
+{
+	gboolean enabled;
+
+	g_object_ref (shell);
+	
+	enabled = eel_gconf_get_boolean (CONF_ENABLE_SHARING);
+	
+	if (enabled) {
+		create_share (shell);
+	}
+	
+	eel_gconf_notification_add (CONF_ENABLE_SHARING,
+				    (GConfClientNotifyFunc) enable_sharing_changed_cb,
+				    shell);
+	eel_gconf_notification_add (CONF_SHARE_NAME,
+				    (GConfClientNotifyFunc) share_name_changed_cb,
+				    shell);
+
+	return;
+}
+
+void daap_sharing_shutdown (RBShell *shell)
+{
+	g_object_unref (shell);
+	
+	if (share) {
+		rb_debug ("shutdown daap sharing\n");
+
+		g_object_unref (share);
+		share = NULL;
+	}
+	
+	return;
+}
+
diff -x CVS -x cvs -urN rhythmbox/daapsharing/daap-sharing.h myrhythmbox/daapsharing/daap-sharing.h
--- rhythmbox/daapsharing/daap-sharing.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/daap-sharing.h	2005-08-08 05:17:08.000000000 -0400
@@ -0,0 +1,35 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) sharing
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __DAAP_SHARING_H
+#define __DAAP_SHARING_H
+
+#include "rb-shell.h"
+
+G_BEGIN_DECLS
+
+void daap_sharing_init (RBShell *shell);
+void daap_sharing_shutdown (RBShell *shell);
+
+G_END_DECLS
+
+#endif /* __DAAP_SHARING_H */
+
diff -x CVS -x cvs -urN rhythmbox/daapsharing/Makefile.am myrhythmbox/daapsharing/Makefile.am
--- rhythmbox/daapsharing/Makefile.am	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/Makefile.am	2005-08-08 05:17:08.000000000 -0400
@@ -0,0 +1,43 @@
+noinst_LTLIBRARIES = libdaapsharing.la libdaapstructure.la
+
+libdaapsharing_la_SOURCES = 
+if USE_DAAP_SHARE
+libdaapsharing_la_SOURCES += \
+		daap-sharing.c 	\
+		daap-sharing.h 	\
+		rb-daap-share.c	\
+		rb-daap-share.h 
+endif
+
+libdaapstructure_la_SOURCES =
+if USE_DAAP_BROWSE
+libdaapstructure_la_SOURCES += \
+		rb-daap-structure.c \
+		rb-daap-structure.h
+endif
+
+INCLUDES =						\
+        -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"		 	\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib 				\
+	-I$(top_builddir)/lib 				\
+	-I$(top_srcdir)/corba 				\
+	-I$(top_builddir)/corba				\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/library 			\
+	-I$(top_srcdir)/iradio				\
+	-I$(top_srcdir)/widgets				\
+	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/sources				\
+	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
+	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
+	-DDATADIR=\""$(datadir)"\"			\
+	$(WARN_CFLAGS)					\
+	$(RHYTHMBOX_CFLAGS)				\
+	$(SOUP_CFLAGS)					\
+	$(HOWL_CFLAGS)
+
+libdaapsharing_la_LDFLAGS = -export-dynamic
+libdaapstructure_la_LDFLAGS = -export-dynamic
+
diff -x CVS -x cvs -urN rhythmbox/daapsharing/rb-daap-share.c myrhythmbox/daapsharing/rb-daap-share.c
--- rhythmbox/daapsharing/rb-daap-share.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/rb-daap-share.c	2005-08-08 05:17:08.000000000 -0400
@@ -0,0 +1,1189 @@
+/*
+ *  Implmentation of DAAP (iTunes Music Sharing) sharing
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <howl.h>
+#undef PACKAGE
+#undef VERSION
+#include "rb-daap-share.h"
+#include "rb-daap-structure.h"
+
+#include "rb-playlist-source.h"
+#include "rb-debug.h"
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-uri.h>
+#include <libsoup/soup-server.h>
+#include <libsoup/soup-server-message.h>
+#include <libgnomevfs/gnome-vfs.h>
+
+#include <time.h>
+
+static void rb_daap_share_init (RBDAAPShare *share);
+static void rb_daap_share_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void rb_daap_share_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void rb_daap_share_finalize (GObject *object);
+static void rb_daap_share_class_init (RBDAAPShareClass *klass);
+
+static void rb_daap_share_start_publish (RBDAAPShare *share);
+static void rb_daap_share_stop_publish (RBDAAPShare *share);
+
+struct RBDAAPSharePrivate {
+	gchar *name;
+
+	/* mdns/zeroconf/dns-sd/rendezvous publishing things */
+	gboolean published;
+	sw_discovery discovery;
+	sw_discovery_oid publish_oid;
+	sw_salt salt;
+	guint salt_timeout;
+
+	/* http server things */
+	SoupServer *server;
+	guint revision_number;
+
+	/* db things */
+	RhythmDB *db;
+	gint32 num_songs;
+	GHashTable *id_to_entry;
+	GHashTable *entry_to_id;
+	gulong entry_added_id;
+	gulong entry_deleted_id;
+	
+	/* playlist things */
+	RBPlaylistManager *playlist_manager;
+};
+
+enum {
+	PROP_0,
+	PROP_NAME,
+	PROP_DB
+};
+
+GType 
+rb_daap_share_get_type (void)
+{
+	static GType rb_daap_share_type = 0;
+
+	if (rb_daap_share_type == 0) {
+		static const GTypeInfo our_info = {
+			sizeof (RBDAAPShareClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) rb_daap_share_class_init,
+			NULL,
+			NULL,
+			sizeof (RBDAAPShare),
+			0,
+			(GInstanceInitFunc) rb_daap_share_init
+		};
+
+		rb_daap_share_type = g_type_register_static (G_TYPE_OBJECT,
+		 	 				     "RBDAAPShare",
+							     &our_info, 0);
+
+	}
+
+	return rb_daap_share_type;
+}
+
+static void
+rb_daap_share_class_init (RBDAAPShareClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	
+	object_class->get_property = rb_daap_share_get_property;
+	object_class->set_property = rb_daap_share_set_property;
+	object_class->finalize = rb_daap_share_finalize;
+
+	g_object_class_install_property (object_class,
+					 PROP_NAME,
+					 g_param_spec_string ("name",
+						 	      "Name",
+							      "Share Name",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_DB,
+					 g_param_spec_object ("db", 
+							      "RhythmDB", 
+							      "RhythmDB object", 
+							      RHYTHMDB_TYPE,
+							       G_PARAM_READABLE));
+
+	return;
+}
+
+static void
+rb_daap_share_init (RBDAAPShare *share)
+{
+	share->priv = g_new0 (RBDAAPSharePrivate, 1);
+	share->priv->salt_timeout = 0;
+	share->priv->revision_number = 5;
+
+	return;
+}
+
+static void
+rb_daap_share_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	RBDAAPShare *share = RB_DAAP_SHARE (object);
+
+	switch (prop_id) {
+		case PROP_NAME: {
+			gboolean restart_publish = FALSE;
+			
+			if (share->priv->name && share->priv->published) {
+				rb_daap_share_stop_publish (share);
+				g_free (share->priv->name);
+				restart_publish = TRUE;
+			}
+
+			share->priv->name = g_value_dup_string (value);
+
+			if (restart_publish) {
+				rb_daap_share_start_publish (share);
+			}
+
+			break;
+		}
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+rb_daap_share_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	RBDAAPShare *share = RB_DAAP_SHARE (object);
+
+	switch (prop_id) {
+		case PROP_NAME:
+			g_value_set_string (value, share->priv->name);
+			break;
+		case PROP_DB:
+			g_value_set_object (value, share->priv->db);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+
+static void 
+rb_daap_share_finalize (GObject *object)
+{
+	RBDAAPShare *share = RB_DAAP_SHARE (object);
+
+	if (share->priv->published) {
+		rb_daap_share_stop_publish (share);
+	}
+	
+	if (share->priv) {
+		g_free (share->priv->name);
+		g_object_unref (share->priv->db);
+		g_object_unref (share->priv->playlist_manager);
+		g_free (share->priv);
+		share->priv = NULL;
+	}
+
+	return;
+}
+
+
+RBDAAPShare * 
+rb_daap_share_new (const gchar *name, RhythmDB *db, RBPlaylistManager *playlist_manager)
+{
+	RBDAAPShare *share;
+
+	share = RB_DAAP_SHARE (g_object_new (RB_TYPE_DAAP_SHARE, "name", name, NULL));
+	share->priv->db = g_object_ref (db);
+	share->priv->playlist_manager = g_object_ref (playlist_manager);
+
+	rb_daap_share_start_publish (share);
+	
+	return share;
+}
+
+static sw_result
+publish_reply_cb (sw_discovery discovery, 
+		  sw_discovery_oid oid,
+		  sw_discovery_publish_status status,
+		  sw_opaque extra)
+{
+	RBDAAPShare *share = RB_DAAP_SHARE (extra);
+
+	switch (status) {
+		case SW_DISCOVERY_PUBLISH_STARTED:
+			rb_debug ("mdns publish successful\n");
+			share->priv->published = TRUE;
+			break;
+		case SW_DISCOVERY_PUBLISH_NAME_COLLISION:
+		case SW_DISCOVERY_PUBLISH_INVALID:
+			g_warning("error publishing %d",status);
+			break;
+		default:
+			break;
+	}
+
+	return SW_OKAY;
+}
+
+static gboolean
+howl_salt_timeout (RBDAAPShare *share)
+{
+	sw_ulong msecs = 500;
+	
+	sw_salt_step (share->priv->salt, &msecs);
+
+	return TRUE;
+}
+
+static void message_add_standard_headers (SoupMessage *message)
+{
+	gchar *s;
+	time_t t;
+	struct tm *tm;
+
+	soup_message_add_header (message->response_headers, "DAAP-Server", "Rhythmbox " VERSION);
+	
+	soup_message_add_header (message->response_headers, "Content-Type", "application/x-dmap-tagged");
+	
+	t = time (NULL);
+	tm = gmtime (&t);
+	s = g_new (gchar, 100);
+	strftime (s, 100, "%a, %d %b %Y %T GMT", tm);
+	soup_message_add_header (message->response_headers, "Date", s);
+	g_free (s);
+
+	return;
+}
+
+static void message_set_from_rb_daap_structure (SoupMessage *message, GNode *structure)
+{
+	gchar *resp;
+	guint length;
+
+	resp = rb_daap_structure_serialize (structure, &length);
+
+	if (resp == NULL) {
+		g_print ("serialize gave us null?\n");
+		return;
+	}
+
+	message->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
+	message->response.length = length;
+	message->response.body = resp;
+		
+	message_add_standard_headers (message);
+		
+	soup_message_set_status (message, SOUP_STATUS_OK);
+	soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CONTENT_LENGTH);
+
+	return;
+}
+
+static void server_info_cb (RBDAAPShare *share, SoupMessage *message)
+{
+/* MSRV	server info response
+ * 	MSTT status
+ * 	MPRO dmap version
+ * 	APRO daap version
+ * 	MINM name
+ * 	MSAU authentication method
+ * 	MSLR login required
+ * 	MSTM timeout interval
+ * 	MSAL supports auto logout
+ * 	MSUP supports update
+ * 	MSPI supports persistent ids
+ * 	MSEX supports extensions
+ * 	MSBR supports browse
+ * 	MSQY supports query
+ * 	MSIX supports index
+ * 	MSRS supports resolve
+ * 	MSDC databases count
+ */
+	GNode *msrv;
+
+	msrv = rb_daap_structure_add (NULL, RB_DAAP_CC_MSRV);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSTT, (gint32)200);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MPRO, (gdouble)2.0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_APRO, (gdouble)3.0);
+	/* 2/3 is for itunes 4.8 (at least).  its determined by the
+	 * Client-DAAP-Version header sent, but if we decide not to support
+	 * older versions..? anyway
+	 *
+	 * 1.0 is 1/1
+	 * 2.0 is 1/2
+	 * 3.0 is 2/3
+	 */
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MINM, share->priv->name);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSAU, 0);
+	/* authentication method
+	 * 0 is nothing
+	 * 1 is name & password
+	 * 2 is password only
+	 */
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSLR, 0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSTM, (gint32)1800);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSAL, (gchar)0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSUP, (gchar)0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSPI, (gchar)0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSEX, (gchar)0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSBR, (gchar)0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSQY, (gchar)0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSIX, (gchar)0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSRS, (gchar)0);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MSDC, (gint32)1);
+
+	message_set_from_rb_daap_structure (message, msrv);
+	rb_daap_structure_destroy (msrv);
+	
+	return;
+}
+
+static void content_codes_cb (RBDAAPShare *share, SoupMessage *message)
+{
+/* MCCR content codes response
+ * 	MSTT status
+ * 	MDCL dictionary
+ * 		MCNM content codes number
+ * 		MCNA content codes name
+ * 		MCTY content codes type
+ * 	MDCL dictionary
+ * 	...
+ */
+	const RBDAAPContentCodeDefinition *defs;
+	guint num_defs = 0;
+	guint i;
+	GNode *mccr;
+	
+	defs = rb_daap_content_codes (&num_defs);
+
+	mccr = rb_daap_structure_add (NULL, RB_DAAP_CC_MCCR);
+	rb_daap_structure_add (mccr, RB_DAAP_CC_MSTT, (gint32)200);
+
+	for (i = 0; i < num_defs; i++) {
+		GNode *mdcl;
+		
+		mdcl = rb_daap_structure_add (mccr, RB_DAAP_CC_MDCL);
+		rb_daap_structure_add (mdcl, RB_DAAP_CC_MCNM, rb_daap_content_code_string_as_int32(defs[i].string));
+		rb_daap_structure_add (mdcl, RB_DAAP_CC_MCNA, defs[i].name);
+		rb_daap_structure_add (mdcl, RB_DAAP_CC_MCTY, (gint32)defs[i].type);
+	}
+	
+	message_set_from_rb_daap_structure (message, mccr);
+	rb_daap_structure_destroy (mccr);
+
+	return;
+}
+	
+static void login_cb (RBDAAPShare *share, SoupMessage *message)
+{
+/* MLOG login response
+ * 	MSTT status
+ * 	MLID session id
+ */
+	GNode *mlog;
+
+	mlog = rb_daap_structure_add (NULL, RB_DAAP_CC_MLOG);
+	rb_daap_structure_add (mlog, RB_DAAP_CC_MSTT, (gint32)200);
+	rb_daap_structure_add (mlog, RB_DAAP_CC_MLID, (gint32)42);
+
+	message_set_from_rb_daap_structure (message, mlog);
+	rb_daap_structure_destroy (mlog);
+	
+	return;
+}
+
+static void update_cb (RBDAAPShare *share, SoupMessage *message)
+{
+	gchar *path;
+	gchar *revision_number_position;
+	guint revision_number;
+	
+	path = soup_uri_to_string (soup_message_get_uri (message), TRUE);
+
+	revision_number_position = strstr (path, "revision-number=");
+	
+	if (revision_number_position == NULL) {
+		g_print ("client asked for an update without a revision number?!?\n");
+		g_free (path);
+		return;
+	}
+
+	revision_number_position += 16;
+	revision_number = atoi (revision_number_position);
+
+	g_free (path);
+	
+	if (revision_number != share->priv->revision_number) {
+		/* MUPD update response
+		 * 	MSTT status
+		 * 	MUSR server revision
+		 */
+		GNode *mupd;
+		
+		mupd = rb_daap_structure_add (NULL, RB_DAAP_CC_MUPD);
+		rb_daap_structure_add (mupd, RB_DAAP_CC_MSTT, (gint32)200);
+		rb_daap_structure_add (mupd, RB_DAAP_CC_MUSR, (gint32)share->priv->revision_number);
+
+		message_set_from_rb_daap_structure (message, mupd);
+		rb_daap_structure_destroy (mupd);
+	} else {
+		g_object_ref (message);
+		soup_message_io_pause (message);
+	}
+	
+	return;
+}
+
+typedef enum {
+	ITEM_ID = 0,
+	ITEM_NAME,
+	ITEM_KIND,
+	PERSISTENT_ID,
+	CONTAINER_ITEM_ID,
+	SONG_ALBUM,
+	SONG_GROUPING,
+	SONG_ARTIST,
+	SONG_BITRATE,
+	SONG_BPM,
+	SONG_COMMENT,
+	SONG_COMPILATION,
+	SONG_COMPOSER,
+	SONG_DATA_KIND,
+	SONG_DATA_URL,
+	SONG_DATE_ADDED,
+	SONG_DATE_MODIFIED,
+	SONG_DISC_COUNT,
+	SONG_DISC_NUMBER,
+	SONG_DISABLED,
+	SONG_EQ_PRESET,
+	SONG_FORMAT,
+	SONG_GENRE,
+	SONG_DESCRIPTION,
+	SONG_RELATIVE_VOLUME,
+	SONG_SAMPLE_RATE,
+	SONG_SIZE,
+	SONG_START_TIME,
+	SONG_STOP_TIME,
+	SONG_TIME,
+	SONG_TRACK_COUNT,
+	SONG_TRACK_NUMBER,
+	SONG_USER_RATING,
+	SONG_YEAR
+} DAAPMetaData;
+
+struct DAAPMetaDataMap {
+	gchar *tag;
+	DAAPMetaData md;
+};
+
+struct DAAPMetaDataMap meta_data_map[] = {
+	{"dmap.itemid",			ITEM_ID},			
+    	{"dmap.itemname",		ITEM_NAME},		
+    	{"dmap.itemkind",		ITEM_KIND},			
+    	{"dmap.persistentid",		PERSISTENT_ID},	
+	{"dmap.containeritemid",	CONTAINER_ITEM_ID},	
+    	{"daap.songalbum",		SONG_ALBUM},
+    	{"daap.songartist",		SONG_ARTIST},
+    	{"daap.songbitrate",		SONG_BITRATE},
+    	{"daap.songbeatsperminute",	SONG_BPM},
+    	{"daap.songcomment",		SONG_COMMENT},
+    	{"daap.songcompilation",	SONG_COMPILATION},
+    	{"daap.songcomposer",		SONG_COMPOSER},
+    	{"daap.songdatakind",		SONG_DATA_KIND},
+    	{"daap.songdataurl",		SONG_DATA_URL},
+    	{"daap.songdateadded",		SONG_DATE_ADDED},
+    	{"daap.songdatemodified",	SONG_DATE_MODIFIED},
+    	{"daap.songdescription",	SONG_DESCRIPTION},
+    	{"daap.songdisabled",		SONG_DISABLED},
+    	{"daap.songdisccount",		SONG_DISC_COUNT},
+    	{"daap.songdiscnumber",		SONG_DISC_NUMBER},
+    	{"daap.songeqpreset",		SONG_EQ_PRESET},
+    	{"daap.songformat",		SONG_FORMAT},
+    	{"daap.songgenre",		SONG_GENRE},
+    	{"daap.songgrouping",		SONG_GROUPING},
+    	{"daap.songrelativevolume",	SONG_RELATIVE_VOLUME},
+    	{"daap.songsamplerate",		SONG_SAMPLE_RATE},
+    	{"daap.songsize",		SONG_SIZE},
+    	{"daap.songstarttime",		SONG_START_TIME},
+    	{"daap.songstoptime",		SONG_STOP_TIME},
+   	{"daap.songtime",		SONG_TIME},
+    	{"daap.songtrackcount",		SONG_TRACK_COUNT},
+    	{"daap.songtracknumber",	SONG_TRACK_NUMBER},
+    	{"daap.songuserrating",		SONG_USER_RATING},
+    	{"daap.songyear",		SONG_YEAR}};
+
+typedef unsigned long long bitwise;
+
+struct MLCL_Bits {
+	GNode *mlcl;
+	bitwise bits;
+	gpointer pointer;
+};
+
+static gboolean client_requested (bitwise bits,gint field)
+{
+	return 0 != (bits & (((bitwise) 1) << field));
+}
+
+static void add_entry_to_mlcl (RhythmDBEntry *entry, gint id, struct MLCL_Bits *mb)
+{
+	GNode *mlit;
+	
+	mlit = rb_daap_structure_add (mb->mlcl, RB_DAAP_CC_MLIT);
+	
+	if (client_requested (mb->bits, ITEM_KIND))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MIKD, (gchar)2);
+	if (client_requested (mb->bits, ITEM_ID)) 
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32)id);
+	if (client_requested (mb->bits, ITEM_NAME))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MINM, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+	if (client_requested (mb->bits, PERSISTENT_ID))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MPER, (gint64)id);
+	if (client_requested (mb->bits, CONTAINER_ITEM_ID))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MCTI, (gint32)id);
+	if (client_requested (mb->bits, SONG_DATA_KIND))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASDK, (gchar)0);
+	if (client_requested (mb->bits, SONG_DATA_URL))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASUL, "");
+	if (client_requested (mb->bits, SONG_ALBUM))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASAL, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+	if (client_requested (mb->bits, SONG_GROUPING))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_AGRP, "");
+	if (client_requested (mb->bits, SONG_ARTIST))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASAR, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+	if (client_requested (mb->bits, SONG_BITRATE)) {
+		gulong bitrate = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE);
+		
+		if (bitrate == 0) { /* because gstreamer is stupid */
+		/* bitrate needs to be sent in kbps, kb/s
+		 * a kilobit is 128 bytes
+		 * if the length is L seconds, 
+		 * and the file is S bytes
+		 * then 
+		 * (S / 128) / L is kbps */
+			gulong length = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
+			guint64 file_size = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
+			
+			bitrate = (file_size / 128) / length;
+		}
+		
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASBR, (gint32)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE));
+	}
+	if (client_requested (mb->bits, SONG_BPM))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASBT, (gint32)0);
+	if (client_requested (mb->bits, SONG_COMMENT))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASCM, "");
+	if (client_requested (mb->bits, SONG_COMPILATION))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASCO, (gchar)FALSE);
+	if (client_requested (mb->bits, SONG_COMPOSER))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASCP, "");
+	if (client_requested (mb->bits, SONG_DATE_ADDED))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASDA, (gint32)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_FIRST_SEEN));
+	if (client_requested (mb->bits, SONG_DATE_MODIFIED))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASDM, (gint32)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_MTIME));
+	if (client_requested (mb->bits, SONG_DISC_COUNT))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASDC, (gint32)0);
+	if (client_requested (mb->bits, SONG_DISC_NUMBER))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASDN, (gint32)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER));
+	if (client_requested (mb->bits, SONG_DISABLED))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASDB, (gchar)FALSE);
+	if (client_requested (mb->bits, SONG_EQ_PRESET))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASEQ, "");
+	if (client_requested (mb->bits, SONG_FORMAT)) {
+		const gchar *filename;
+		gchar *ext;
+		
+		filename = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
+		ext = strrchr (filename, '.');
+		ext++;
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASFM, ext);
+	}
+	if (client_requested (mb->bits, SONG_GENRE))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASGN, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE));
+	if (client_requested (mb->bits, SONG_DESCRIPTION))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASDT, "");
+	if (client_requested (mb->bits, SONG_RELATIVE_VOLUME))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASRV, 0);
+	if (client_requested (mb->bits, SONG_SAMPLE_RATE))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASSR, 0);
+	if (client_requested (mb->bits, SONG_SIZE))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASSZ, (gint32)rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE));
+	if (client_requested (mb->bits, SONG_START_TIME))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASST, 0);
+	if (client_requested (mb->bits, SONG_STOP_TIME))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASSP, 0);
+	if (client_requested (mb->bits, SONG_TIME))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASTM, (gint32)(1000 * rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION)));
+	if (client_requested (mb->bits, SONG_TRACK_COUNT))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASTC, 0);
+	if (client_requested (mb->bits, SONG_TRACK_NUMBER))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASTN, (gint32)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
+	if (client_requested (mb->bits, SONG_USER_RATING))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASUR, 0); // fixme
+	if (client_requested (mb->bits, SONG_YEAR))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ASYR, 0);
+	
+	return;
+}
+
+static void add_playlist_to_mlcl (RBSource *source, GNode *mlcl)
+{
+ /* 	MLIT listing item
+ * 		MIID item id
+ * 		MPER persistent item id
+ * 		MINM item name
+ * 		MIMC item count
+ */
+	GNode *mlit;
+	gchar *name;
+	RBEntryView *ev;
+	guint num_songs;
+	
+	g_object_get (G_OBJECT (source), "name", &name, NULL);
+	
+	ev = rb_source_get_entry_view (source);
+	num_songs = rb_entry_view_get_num_entries (ev);
+	
+	mlit = rb_daap_structure_add (mlcl, RB_DAAP_CC_MLIT);
+	rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32)source);
+	rb_daap_structure_add (mlit, RB_DAAP_CC_MPER, (gint64)(gint32)source);
+	rb_daap_structure_add (mlit, RB_DAAP_CC_MINM, name);
+	rb_daap_structure_add (mlit, RB_DAAP_CC_MIMC, (gint32)num_songs);
+	
+	g_free (name);
+	
+	return;
+}
+
+static gboolean add_playlist_entry_to_mlcl (GtkTreeModel *model,
+					    GtkTreePath *path,
+					    GtkTreeIter *iter,
+					    struct MLCL_Bits *mb)
+{
+	GNode *mlit;
+	RhythmDBEntry *entry;
+	gint id;
+	
+	mlit = rb_daap_structure_add (mb->mlcl, RB_DAAP_CC_MLIT);
+
+	gtk_tree_model_get (model, iter, 0, &entry, -1);
+	
+	id = GPOINTER_TO_INT (g_hash_table_lookup ((GHashTable *)mb->pointer, entry));
+
+	if (client_requested (mb->bits, ITEM_KIND))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MIKD, (gchar)2);
+	if (client_requested (mb->bits, ITEM_ID)) 
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32)id);
+	if (client_requested (mb->bits, CONTAINER_ITEM_ID))
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MCTI, (gint32)id);
+		
+	return FALSE;
+}	
+
+static bitwise parse_meta (const gchar *s)
+{
+	gchar *start_of_attrs;
+	gchar *end_of_attrs;
+	gchar *attrs;
+	gchar **attrsv;
+	guint i;
+	bitwise bits = 0;
+
+	start_of_attrs = strstr (s, "meta=");
+	if (start_of_attrs == NULL) {
+		return 0;
+	}
+	start_of_attrs += 5;
+
+	end_of_attrs = strchr (start_of_attrs, '&');
+	if (end_of_attrs) {
+		attrs = g_strndup (start_of_attrs, end_of_attrs - start_of_attrs);
+	} else {
+		attrs = g_strdup (start_of_attrs);
+	}
+
+	attrsv = g_strsplit (attrs,",",-1);
+	
+	for (i = 0; attrsv[i]; i++) {
+		guint j;
+
+		for (j = 0; j < G_N_ELEMENTS (meta_data_map); j++) {
+			if (strcmp (meta_data_map[j].tag, attrsv[i]) == 0) {
+				bits |= (((bitwise) 1) << meta_data_map[j].md);
+			}
+		}
+	}
+
+	g_free (attrs);
+	g_strfreev (attrsv);
+	
+	return bits;
+}
+
+static void databases_cb (RBDAAPShare *share, SoupMessage *message)
+{
+	gchar *path;
+	gchar *rest_of_path;
+	guint revision_number;
+	
+	path = soup_uri_to_string (soup_message_get_uri (message), TRUE);
+
+	rest_of_path = strchr (path + 1, '/');
+	
+	if (rest_of_path == NULL) {
+	/* AVDB server databases
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT listing item
+	 * 			MIID item id
+	 * 			MPER persistent id
+	 * 			MINM item name
+	 * 			MIMC item count
+	 * 			MCTC container count
+	 */
+		GNode *avdb;
+		GNode *mlcl;
+		GNode *mlit;
+
+		avdb = rb_daap_structure_add (NULL, RB_DAAP_CC_AVDB);
+		rb_daap_structure_add (avdb, RB_DAAP_CC_MSTT, (gint32)200);
+		rb_daap_structure_add (avdb, RB_DAAP_CC_MUTY, 0);
+		rb_daap_structure_add (avdb, RB_DAAP_CC_MTCO, (gint32)1);
+		rb_daap_structure_add (avdb, RB_DAAP_CC_MRCO, (gint32)1);
+		mlcl = rb_daap_structure_add (avdb, RB_DAAP_CC_MLCL);
+		mlit = rb_daap_structure_add (mlcl, RB_DAAP_CC_MLIT);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32)1);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MPER, (gint64)1);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MINM, share->priv->name);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MIMC, (gint32)share->priv->num_songs);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MCTC, (gint32)1);
+	
+		message_set_from_rb_daap_structure (message, avdb);
+		rb_daap_structure_destroy (avdb);
+	} else if (g_ascii_strncasecmp ("/1/items?", rest_of_path, 9) == 0) {
+	/* ADBS database songs
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT
+	 * 			attrs
+	 * 		MLIT
+	 * 		...
+	 */
+		GNode *adbs;
+		struct MLCL_Bits mb = {NULL,0};
+
+		mb.bits = parse_meta (rest_of_path);
+		
+		adbs = rb_daap_structure_add (NULL, RB_DAAP_CC_ADBS);
+		rb_daap_structure_add (adbs, RB_DAAP_CC_MSTT, (gint32)200);
+		rb_daap_structure_add (adbs, RB_DAAP_CC_MUTY, 0);
+		rb_daap_structure_add (adbs, RB_DAAP_CC_MTCO, (gint32)share->priv->num_songs);
+		rb_daap_structure_add (adbs, RB_DAAP_CC_MRCO, (gint32)share->priv->num_songs);
+		mb.mlcl = rb_daap_structure_add (adbs, RB_DAAP_CC_MLCL);
+		
+		g_hash_table_foreach (share->priv->entry_to_id, (GHFunc) add_entry_to_mlcl, &mb);
+
+		message_set_from_rb_daap_structure (message, adbs);
+		rb_daap_structure_destroy (adbs);
+		adbs = NULL;
+	} else if (g_ascii_strncasecmp ("/1/containers?", rest_of_path, 14) == 0) {
+	/* APLY database playlists
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT listing item
+	 * 			MIID item id
+	 * 			MPER persistent item id
+	 * 			MINM item name
+	 * 			MIMC item count
+	 * 			ABPL baseplaylist (only for base)
+	 * 		MLIT
+	 * 		...
+	 */
+		GNode *aply;
+		GNode *mlcl;
+		GNode *mlit;
+		GList *playlists;
+
+		aply = rb_daap_structure_add (NULL, RB_DAAP_CC_APLY);
+		rb_daap_structure_add (aply, RB_DAAP_CC_MSTT, (gint32)200);
+		rb_daap_structure_add (aply, RB_DAAP_CC_MUTY, 0);
+		rb_daap_structure_add (aply, RB_DAAP_CC_MTCO, (gint32)1);
+		rb_daap_structure_add (aply, RB_DAAP_CC_MRCO, (gint32)1);
+		mlcl = rb_daap_structure_add (aply, RB_DAAP_CC_MLCL);
+		mlit = rb_daap_structure_add (mlcl, RB_DAAP_CC_MLIT);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32)1);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MPER, (gint64)1);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MINM, share->priv->name);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_MIMC, (gint32)share->priv->num_songs);
+		rb_daap_structure_add (mlit, RB_DAAP_CC_ABPL, (gchar)1); /* base playlist */
+
+		playlists = rb_playlist_manager_get_playlists (share->priv->playlist_manager);
+		g_list_foreach (playlists, (GFunc)add_playlist_to_mlcl, mlcl);
+		
+		message_set_from_rb_daap_structure (message, aply);
+		rb_daap_structure_destroy (aply);
+	} else if (g_ascii_strncasecmp ("/1/containers/", rest_of_path, 14) == 0) {
+	/* APSO playlist songs
+	 * 	MSTT status
+	 * 	MUTY update type
+	 * 	MTCO specified total count
+	 * 	MRCO returned count
+	 * 	MLCL listing
+	 * 		MLIT listing item
+	 * 			MIKD item kind
+	 * 			MIID item id
+	 * 			MCTI container item id
+	 * 		MLIT
+	 * 		...
+	 */
+		GNode *apso;
+		struct MLCL_Bits mb = {NULL,0};
+		gint pl_id = atoi (rest_of_path + 14);
+
+		mb.bits = parse_meta (rest_of_path);
+		
+		apso = rb_daap_structure_add (NULL, RB_DAAP_CC_APSO);
+		rb_daap_structure_add (apso, RB_DAAP_CC_MSTT, (gint32)200);
+		rb_daap_structure_add (apso, RB_DAAP_CC_MUTY, 0);
+
+		if (pl_id == 1) {
+			rb_daap_structure_add (apso, RB_DAAP_CC_MTCO, (gint32)share->priv->num_songs);
+			rb_daap_structure_add (apso, RB_DAAP_CC_MRCO, (gint32)share->priv->num_songs);
+			mb.mlcl = rb_daap_structure_add (apso, RB_DAAP_CC_MLCL);
+		
+			g_hash_table_foreach (share->priv->entry_to_id, (GHFunc) add_entry_to_mlcl, &mb);
+		} else {
+			RBSource *source = GINT_TO_POINTER (pl_id);
+			RBEntryView *ev;
+			guint num_songs;
+			RhythmDBQueryModel *model;
+
+			mb.mlcl = rb_daap_structure_add (apso, RB_DAAP_CC_MLCL);
+
+			mb.pointer = share->priv->entry_to_id;
+			
+			ev = rb_source_get_entry_view (source);
+			num_songs = rb_entry_view_get_num_entries (ev);
+				
+			rb_daap_structure_add (apso, RB_DAAP_CC_MTCO, (gint32)num_songs);
+			rb_daap_structure_add (apso, RB_DAAP_CC_MRCO, (gint32)num_songs);
+
+			model = rb_playlist_source_get_model (RB_PLAYLIST_SOURCE (source));
+			gtk_tree_model_foreach (GTK_TREE_MODEL (model), (GtkTreeModelForeachFunc) add_playlist_entry_to_mlcl, &mb);
+			
+		}
+		
+		message_set_from_rb_daap_structure (message, apso);
+		rb_daap_structure_destroy (apso);
+	} else if (g_ascii_strncasecmp ("/1/items/", rest_of_path, 9) == 0) {
+	/* just the file :) */
+		gchar *id_str;
+		gint id;
+		RhythmDBEntry *entry;
+		const gchar *location;
+		guint64 file_size;
+		GnomeVFSResult result;
+		GnomeVFSHandle *handle;
+		const gchar *range_header;
+		gchar *buf;
+		guint status_code = SOUP_STATUS_OK;
+		
+		id_str = rest_of_path + 9;
+		id = atoi (id_str);
+
+		entry = g_hash_table_lookup (share->priv->id_to_entry, GINT_TO_POINTER (id));
+		location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
+		file_size = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
+
+		result = gnome_vfs_open (&handle, location, GNOME_VFS_OPEN_READ);
+		if (result != GNOME_VFS_OK) {
+			soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+			goto out;
+		}
+
+		range_header = soup_message_get_header (message->request_headers, "Range");
+		if (range_header) {
+			const gchar *s;
+			GnomeVFSFileOffset range;
+			gchar *content_range;
+			
+			s = range_header + 6; // bytes=
+			range = atoll (s);
+			
+			result = gnome_vfs_seek (handle, GNOME_VFS_SEEK_START, range);
+			
+			if (result != GNOME_VFS_OK) {
+				g_message ("Error seeking: %s", gnome_vfs_result_to_string (result));
+				soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+				goto out;
+			}
+
+			status_code = SOUP_STATUS_PARTIAL_CONTENT;
+
+			content_range = g_strdup_printf ("bytes %"GNOME_VFS_OFFSET_FORMAT_STR"-%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT, range, file_size, file_size);
+			soup_message_add_header (message->response_headers, "Content-Range", content_range);
+			g_free (content_range);
+
+			file_size -= range;
+		}
+		
+		buf = g_malloc (file_size);
+
+		result = gnome_vfs_read (handle, buf, file_size, NULL);
+		if (result != GNOME_VFS_OK) {
+			soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+			g_free (buf);
+			goto out;
+		}
+		
+		message_add_standard_headers (message);
+		soup_message_add_header (message->response_headers, "Accept-Ranges", "bytes");
+		message->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
+		message->response.length = file_size;
+		message->response.body = buf;
+
+		gnome_vfs_close (handle);
+		
+		soup_message_set_status (message, status_code);
+		soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CONTENT_LENGTH);
+
+	} else {
+		g_print("unhandled: %s\n",path);
+	}
+		
+out:
+	g_free (path);
+		
+	return;
+}
+
+typedef void (* DAAPPathFunction) (RBDAAPShare *share, SoupMessage *message);
+
+struct DAAPPath {
+	const gchar *path;
+	guint path_length;
+	DAAPPathFunction function;
+};
+
+static const struct DAAPPath paths_to_functions[] = {
+	{"/server-info", 12, server_info_cb},
+	{"/content-codes", 14, content_codes_cb},
+	{"/login", 6, login_cb},
+	{"/update", 7, update_cb},
+	{"/databases", 10, databases_cb}
+};
+
+static void server_cb (SoupServerContext *context, SoupMessage *message, RBDAAPShare *share)
+{
+	gchar *path;
+	guint i;
+	
+	path = soup_uri_to_string (soup_message_get_uri (message), TRUE);
+	
+	for (i = 0; i < G_N_ELEMENTS (paths_to_functions); i++) {
+		if (g_ascii_strncasecmp (paths_to_functions[i].path, path, paths_to_functions[i].path_length) == 0) {
+			paths_to_functions[i].function (share, message);
+			
+			return;
+		}
+	}
+	
+	g_warning("unhandled path %s\n",soup_uri_to_string (soup_message_get_uri (message), TRUE));
+
+	g_free (path);
+	
+	return;
+}
+
+static void add_db_entry (RhythmDBEntry *entry, RBDAAPShare *share)
+{
+	RhythmDBEntryType type;
+	
+	type = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE);
+
+	if (type == rhythmdb_entry_song_get_type ()) {
+		share->priv->num_songs++;
+	
+		g_hash_table_insert (share->priv->id_to_entry, GINT_TO_POINTER (share->priv->num_songs), entry);
+		g_hash_table_insert (share->priv->entry_to_id, entry, GINT_TO_POINTER (share->priv->num_songs));
+	}
+
+	return;
+}
+
+static void db_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry, RBDAAPShare *share)
+{
+	RhythmDBEntryType type;
+	
+	type = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE);
+
+	if (type == rhythmdb_entry_song_get_type ()) {
+		share->priv->num_songs++;
+
+		g_hash_table_insert (share->priv->id_to_entry, GINT_TO_POINTER (share->priv->num_songs), entry);
+		g_hash_table_insert (share->priv->entry_to_id, entry, GINT_TO_POINTER (share->priv->num_songs));
+	}
+	
+	return;
+}
+
+static void db_entry_deleted_cb (RhythmDB *db, RhythmDBEntry *entry, RBDAAPShare *share)
+{
+	RhythmDBEntryType type;
+	
+	type = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE);
+
+	if (type == rhythmdb_entry_song_get_type ()) {
+		gpointer id;
+
+		id = g_hash_table_lookup (share->priv->entry_to_id, entry);
+		g_hash_table_remove (share->priv->entry_to_id, entry);
+		g_hash_table_remove (share->priv->id_to_entry, id);
+
+		share->priv->num_songs--;
+	}
+
+	return;
+}
+
+
+static void
+rb_daap_share_start_publish (RBDAAPShare *share)
+{
+	sw_result result;
+	
+	share->priv->server = soup_server_new (SOUP_SERVER_PORT, 3689, NULL);
+	if (share->priv->server == NULL) {
+		g_warning ("unable to start daap server on port 3689");
+		return;
+	}
+
+	soup_server_add_handler (share->priv->server, 
+				 NULL, 
+				 NULL, 
+				 (SoupServerCallbackFn)server_cb,
+				 NULL,
+				 share);
+	soup_server_run_async (share->priv->server);
+	
+	result = sw_discovery_init (&(share->priv->discovery));
+	if (result != SW_OKAY) {
+		g_warning ("error starting mdns discovery");
+		return;
+	}
+	result = sw_discovery_salt (share->priv->discovery,
+				    &(share->priv->salt));
+	if (result != SW_OKAY) {
+		g_warning ("could not get howl salt");
+		return;
+	}
+	
+	result = sw_discovery_publish (share->priv->discovery,
+				      0,    /* interface index, 0 means all */
+				      share->priv->name,
+				      "_daap._tcp",
+				      NULL, /* domain: .local */
+				      NULL, /* host: self */
+				      3689,
+				      NULL, /* no text record */
+				      0,    /* no text record length */
+				      publish_reply_cb,
+				      (sw_opaque) share,
+				      &(share->priv->publish_oid));
+	if (result != SW_OKAY) {
+		g_warning ("howl publish failed");
+		return;
+	}
+
+	share->priv->salt_timeout = g_timeout_add (1000, (GSourceFunc)howl_salt_timeout, share);
+
+	share->priv->id_to_entry = g_hash_table_new (NULL, NULL);
+	share->priv->entry_to_id = g_hash_table_new (NULL, NULL);
+	share->priv->num_songs = 0;
+
+	rhythmdb_entry_foreach (share->priv->db, (GFunc)add_db_entry, share);
+	
+	share->priv->entry_added_id = g_signal_connect (G_OBJECT (share->priv->db), "entry-added", G_CALLBACK (db_entry_added_cb), share);
+	share->priv->entry_deleted_id = g_signal_connect (G_OBJECT (share->priv->db), "entry-deleted", G_CALLBACK (db_entry_deleted_cb), share);
+	
+	return;
+}
+
+static void
+rb_daap_share_stop_publish (RBDAAPShare *share)
+{
+	if (share->priv->server) {
+		/* this errors, but otherwise i dont believe it actually stops
+		 * serving */
+		soup_server_quit (share->priv->server);
+		g_object_unref (G_OBJECT (share->priv->server));
+		share->priv->server = NULL;
+	}
+	
+	if (share->priv->salt_timeout != 0) {
+		g_source_remove (share->priv->salt_timeout);
+		share->priv->salt_timeout = 0;
+	}
+
+	if (share->priv->id_to_entry) {
+		g_hash_table_destroy (share->priv->id_to_entry);
+		share->priv->id_to_entry = NULL;
+	}
+
+	if (share->priv->entry_to_id) {
+		g_hash_table_destroy (share->priv->entry_to_id);
+		share->priv->entry_to_id = NULL;
+	}
+	
+	if (share->priv->entry_added_id != 0) {
+		g_signal_handler_disconnect (share->priv->db, share->priv->entry_added_id);
+		share->priv->entry_added_id = 0;
+	}
+
+	if (share->priv->entry_deleted_id != 0) {
+		g_signal_handler_disconnect (share->priv->db, share->priv->entry_deleted_id);
+		share->priv->entry_deleted_id = 0;
+	}
+	
+	sw_discovery_cancel (share->priv->discovery, share->priv->publish_oid);
+	
+	share->priv->published = FALSE;
+	
+	return;
+}
+
+
diff -x CVS -x cvs -urN rhythmbox/daapsharing/rb-daap-share.h myrhythmbox/daapsharing/rb-daap-share.h
--- rhythmbox/daapsharing/rb-daap-share.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/rb-daap-share.h	2005-08-08 05:17:08.000000000 -0400
@@ -0,0 +1,58 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) sharing
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_SHARE_H
+#define __RB_DAAP_SHARE_H
+
+#include <glib-object.h>
+#include "rhythmdb.h"
+#include "rb-playlist-manager.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_DAAP_SHARE         (rb_daap_share_get_type ())
+#define RB_DAAP_SHARE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DAAP_SHARE, RBDAAPShare))
+#define RB_DAAP_SHARE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DAAP_SHARE, RBDAAPShareClass))
+#define RB_IS_DAAP_SHARE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DAAP_SHARE))
+#define RB_IS_DAAP_SHARE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DAAP_SHARE))
+#define RB_DAAP_SHARE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DAAP_SHARE, RBDAAPShareClass))
+
+typedef struct RBDAAPSharePrivate RBDAAPSharePrivate;
+
+typedef struct {
+	GObject parent;
+
+	RBDAAPSharePrivate *priv;
+} RBDAAPShare;
+
+typedef struct {
+	GObjectClass parent;
+} RBDAAPShareClass;
+
+GType		rb_daap_share_get_type	(void);
+
+RBDAAPShare * 	rb_daap_share_new 	(const gchar *name, 
+					 RhythmDB *db,
+					 RBPlaylistManager *playlist_manager);
+
+#endif /* __RB_DAAP_SHARE_H */
+
+G_END_DECLS
diff -x CVS -x cvs -urN rhythmbox/daapsharing/rb-daap-structure.c myrhythmbox/daapsharing/rb-daap-structure.c
--- rhythmbox/daapsharing/rb-daap-structure.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/rb-daap-structure.c	2005-08-08 05:17:07.000000000 -0400
@@ -0,0 +1,676 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) structures
+ *
+ *  Copyright (C) 2004,2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <glib.h>
+#include "rb-daap-structure.h"
+#include "rb-debug.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+#define MAKE_CONTENT_CODE(ch0, ch1, ch2, ch3) \
+    (( (gint32)(gchar)(ch0) | ( (gint32)(gchar)(ch1) << 8 ) | \
+    ( (gint32)(gchar)(ch2) << 16 ) | \
+    ( (gint32)(gchar)(ch3) << 24 ) ))
+
+static const RBDAAPContentCodeDefinition cc_defs[] = {
+	{RB_DAAP_CC_MDCL, MAKE_CONTENT_CODE('m','d','c','l'), "dmap.dictionary", "mdcl", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MSTT, MAKE_CONTENT_CODE('m','s','t','t'), "dmap.status", "mstt", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MIID, MAKE_CONTENT_CODE('m','i','i','d'), "dmap.itemid", "miid", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MINM, MAKE_CONTENT_CODE('m','i','n','m'), "dmap.itemname", "minm", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_MIKD, MAKE_CONTENT_CODE('m','i','k','d'), "dmap.itemkind", "mikd", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MPER, MAKE_CONTENT_CODE('m','p','e','r'), "dmap.persistentid", "mper", RB_DAAP_TYPE_LONG},
+	{RB_DAAP_CC_MCON, MAKE_CONTENT_CODE('m','c','o','n'), "dmap.container", "mcon", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MCTI, MAKE_CONTENT_CODE('m','c','t','i'), "dmap.containeritemid", "mcti", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MPCO, MAKE_CONTENT_CODE('m','p','c','o'), "dmap.parentcontainerid", "mpco", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MSTS, MAKE_CONTENT_CODE('m','s','t','s'), "dmap.statusstring", "msts", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_MIMC, MAKE_CONTENT_CODE('m','i','m','c'), "dmap.itemcount", "mimc", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MCTC, MAKE_CONTENT_CODE('m','c','t','c'), "dmap.containercount", "mctc", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MRCO, MAKE_CONTENT_CODE('m','r','c','o'), "dmap.returnedcount", "mrco", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MTCO, MAKE_CONTENT_CODE('m','t','c','o'), "dmap.specifiedtotalcount", "mtco", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MLCL, MAKE_CONTENT_CODE('m','l','c','l'), "dmap.listing", "mlcl", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MLIT, MAKE_CONTENT_CODE('m','l','i','t'), "dmap.listingitem", "mlit", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MBCL, MAKE_CONTENT_CODE('m','b','c','l'), "dmap.bag", "mbcl", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MSRV, MAKE_CONTENT_CODE('m','s','r','v'), "dmap.serverinforesponse", "msrv", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MSAU, MAKE_CONTENT_CODE('m','s','a','u'), "dmap.authenticationmethod", "msau", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSLR, MAKE_CONTENT_CODE('m','s','l','r'), "dmap.loginrequired", "mslr", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MPRO, MAKE_CONTENT_CODE('m','p','r','o'), "dmap.protocolversion", "mpro", RB_DAAP_TYPE_VERSION},
+	{RB_DAAP_CC_APRO, MAKE_CONTENT_CODE('a','p','r','o'), "rb_daap.protocolversion", "apro", RB_DAAP_TYPE_VERSION},
+	{RB_DAAP_CC_MSAL, MAKE_CONTENT_CODE('m','s','a','l'), "dmap.supportsautologout", "msal", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSUP, MAKE_CONTENT_CODE('m','s','u','p'), "dmap.supportsupdate", "msup", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSPI, MAKE_CONTENT_CODE('m','s','p','i'), "dmap.supportspersistenids", "mspi", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSEX, MAKE_CONTENT_CODE('m','s','e','x'), "dmap.supportsextensions", "msex", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSBR, MAKE_CONTENT_CODE('m','s','b','r'), "dmap.supportsbrowse", "msbr", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSQY, MAKE_CONTENT_CODE('m','s','q','y'), "dmap.supportsquery", "msqy", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSIX, MAKE_CONTENT_CODE('m','s','i','x'), "dmap.supportsindex", "msix", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSRS, MAKE_CONTENT_CODE('m','s','r','s'), "dmap.supportsresolve", "msrs", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MSTM, MAKE_CONTENT_CODE('m','s','t','m'), "dmap.timeoutinterval", "mstm", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MSDC, MAKE_CONTENT_CODE('m','s','d','c'), "dmap.databasescount", "msdc", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MCCR, MAKE_CONTENT_CODE('m','c','c','r'), "dmap.contentcodesresponse", "mccr", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MCNM, MAKE_CONTENT_CODE('m','c','n','m'), "dmap.contentcodesnumber", "mcnm", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MCNA, MAKE_CONTENT_CODE('m','c','n','a'), "dmap.contentcodesname", "mcna", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_MCTY, MAKE_CONTENT_CODE('m','c','t','y'), "dmap.contentcodestype", "mcty", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_MLOG, MAKE_CONTENT_CODE('m','l','o','g'), "dmap.loginresponse", "mlog", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MLID, MAKE_CONTENT_CODE('m','l','i','d'), "dmap.sessionid", "mlid", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MUPD, MAKE_CONTENT_CODE('m','u','p','d'), "dmap.updateresponse", "mupd", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_MUSR, MAKE_CONTENT_CODE('m','u','s','r'), "dmap.serverrevision", "musr", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MUTY, MAKE_CONTENT_CODE('m','u','t','y'), "dmap.updatetype", "muty", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_MUDL, MAKE_CONTENT_CODE('m','u','d','l'), "dmap.deletedidlisting", "mudl", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_AVDB, MAKE_CONTENT_CODE('a','v','d','b'), "rb_daap.serverdatabases", "avdb", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABRO, MAKE_CONTENT_CODE('a','b','r','o'), "rb_daap.databasebrowse", "abro", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABAL, MAKE_CONTENT_CODE('a','b','a','l'), "rb_daap.browsealbumlisting", "abal", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABAR, MAKE_CONTENT_CODE('a','b','a','r'), "rb_daap.browseartistlisting", "abar", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABCP, MAKE_CONTENT_CODE('a','b','c','p'), "rb_daap.browsecomposerlisting", "abcp", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABGN, MAKE_CONTENT_CODE('a','b','g','n'), "rb_daap.browsegenrelisting", "abgn", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ADBS, MAKE_CONTENT_CODE('a','d','b','s'), "rb_daap.returndatabasesongs", "adbs", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ASAL, MAKE_CONTENT_CODE('a','s','a','l'), "rb_daap.songalbum", "asal", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASAR, MAKE_CONTENT_CODE('a','s','a','r'), "rb_daap.songartist", "asar", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASBT, MAKE_CONTENT_CODE('a','s','b','t'), "rb_daap.songsbeatsperminute", "asbt", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASBR, MAKE_CONTENT_CODE('a','s','b','r'), "rb_daap.songbitrate", "asbr", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASCM, MAKE_CONTENT_CODE('a','s','c','m'), "rb_daap.songcomment", "ascm", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASCO, MAKE_CONTENT_CODE('a','s','c','o'), "rb_daap.songcompliation", "asco", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_ASDA, MAKE_CONTENT_CODE('a','s','d','a'), "rb_daap.songdateadded", "asda", RB_DAAP_TYPE_DATE},
+	{RB_DAAP_CC_ASDM, MAKE_CONTENT_CODE('a','s','d','m'), "rb_daap.songdatemodified", "asdm", RB_DAAP_TYPE_DATE},
+	{RB_DAAP_CC_ASDC, MAKE_CONTENT_CODE('a','s','d','c'), "rb_daap.songdisccount", "asdc", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASDN, MAKE_CONTENT_CODE('a','s','d','n'), "rb_daap.songdiscnumber", "asdn", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASDB, MAKE_CONTENT_CODE('a','s','d','b'), "rb_daap.songdisabled", "asdb", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_ASEQ, MAKE_CONTENT_CODE('a','s','e','q'), "rb_daap.songeqpreset", "aseq", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASFM, MAKE_CONTENT_CODE('a','s','f','m'), "rb_daap.songformat", "asfm", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASGN, MAKE_CONTENT_CODE('a','s','g','n'), "rb_daap.songgenre", "asgn", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASDT, MAKE_CONTENT_CODE('a','s','d','t'), "rb_daap.songdescription", "asdt", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASRV, MAKE_CONTENT_CODE('a','s','r','v'), "rb_daap.songrelativevolume", "asrv", RB_DAAP_TYPE_SIGNED_INT},
+	{RB_DAAP_CC_ASSR, MAKE_CONTENT_CODE('a','s','s','r'), "rb_daap.songsamplerate", "assr", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASSZ, MAKE_CONTENT_CODE('a','s','s','z'), "rb_daap.songsize", "assz", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASST, MAKE_CONTENT_CODE('a','s','s','t'), "rb_daap.songstarttime", "asst", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASSP, MAKE_CONTENT_CODE('a','s','s','p'), "rb_daap.songstoptime", "assp", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASTM, MAKE_CONTENT_CODE('a','s','t','m'), "rb_daap.songtime", "astm", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASTC, MAKE_CONTENT_CODE('a','s','t','c'), "rb_daap.songtrackcount", "astc", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASTN, MAKE_CONTENT_CODE('a','s','t','n'), "rb_daap.songtracknumber", "astn", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASUR, MAKE_CONTENT_CODE('a','s','u','r'), "rb_daap.songuserrating", "asur", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_ASYR, MAKE_CONTENT_CODE('a','s','y','r'), "rb_daap.songyear", "asyr", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASDK, MAKE_CONTENT_CODE('a','s','d','k'), "rb_daap.songdatakind", "asdk", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_ASUL, MAKE_CONTENT_CODE('a','s','u','l'), "rb_daap.songdataurl", "asul", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_APLY, MAKE_CONTENT_CODE('a','p','l','y'), "rb_daap.databaseplaylists", "aply", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABPL, MAKE_CONTENT_CODE('a','b','p','l'), "rb_daap.baseplaylist", "abpl", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_APSO, MAKE_CONTENT_CODE('a','p','s','o'), "rb_daap.playlistsongs", "apso", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_PRSV, MAKE_CONTENT_CODE('p','r','s','v'), "rb_daap.resolve", "prsv", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ARIF, MAKE_CONTENT_CODE('a','r','i','f'), "rb_daap.resolveinfo", "arif", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_AESV, MAKE_CONTENT_CODE('a','e','S','V'), "com.applie.itunes.music-sharing-version", "aesv", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_MSAS, MAKE_CONTENT_CODE('m','s','a','s'), "rb_daap.authentication.schemes", "msas", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_AGRP, MAKE_CONTENT_CODE('a','g','r','p'), "rb_daap.songgrouping", "agrp", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASCP, MAKE_CONTENT_CODE('a','s','c','p'), "rb_daap.songcomposer", "ascp", RB_DAAP_TYPE_STRING}
+	};
+
+
+const gchar * rb_daap_content_code_name (RBDAAPContentCode code)
+{
+	return cc_defs[code-1].name;
+}
+
+RBDAAPType rb_daap_content_code_rb_daap_type (RBDAAPContentCode code)
+{
+	return cc_defs[code-1].type;
+}
+
+const gchar * rb_daap_content_code_string (RBDAAPContentCode code)
+{
+	return cc_defs[code-1].string;
+}
+			
+static GType rb_daap_content_code_gtype (RBDAAPContentCode code)
+{
+	switch (rb_daap_content_code_rb_daap_type (code)) {
+		case RB_DAAP_TYPE_BYTE:
+		case RB_DAAP_TYPE_SIGNED_INT:
+			return G_TYPE_CHAR;
+		case RB_DAAP_TYPE_SHORT:
+		case RB_DAAP_TYPE_INT:
+		case RB_DAAP_TYPE_DATE:
+			return G_TYPE_INT;
+		case RB_DAAP_TYPE_LONG:
+			return G_TYPE_INT64;
+		case RB_DAAP_TYPE_VERSION:
+			return G_TYPE_DOUBLE;
+		case RB_DAAP_TYPE_STRING:
+			return G_TYPE_STRING;
+		case RB_DAAP_TYPE_CONTAINER:
+		default:
+			return G_TYPE_NONE;
+	}
+}
+
+GNode * rb_daap_structure_add (GNode *parent, RBDAAPContentCode cc, ...)
+{
+	RBDAAPType rb_daap_type;
+	GType gtype;
+	RBDAAPItem *item;
+	va_list list;
+	GNode *node;
+
+	va_start (list, cc);
+
+	rb_daap_type = rb_daap_content_code_rb_daap_type (cc);
+	gtype = rb_daap_content_code_gtype (cc);
+
+	item = g_new0(RBDAAPItem, 1);
+	item->content_code = cc;
+	
+	if (gtype != G_TYPE_NONE) {
+		g_value_init (&(item->content), gtype);
+	}
+	
+	switch (rb_daap_type) {
+		case RB_DAAP_TYPE_BYTE: 
+		case RB_DAAP_TYPE_SIGNED_INT: {
+			gchar c = (gchar)va_arg (list, gint32);
+	
+			g_value_set_char (&(item->content), c);
+
+			item->size = 1;
+			break;
+		}
+		case RB_DAAP_TYPE_SHORT: {
+			gint32 i = va_arg (list, gint32);
+			
+			g_value_set_int (&(item->content), i);
+
+			item->size = 2;
+			break;
+	        }
+		case RB_DAAP_TYPE_DATE:
+		case RB_DAAP_TYPE_INT: {
+			gint32 i = va_arg (list, gint32);
+			
+			g_value_set_int (&(item->content), i);
+
+			item->size = 4;
+			break;
+		}
+		case RB_DAAP_TYPE_VERSION: {
+			gdouble d = va_arg (list, gdouble);
+
+			g_value_set_double (&(item->content), d);
+
+			item->size = 4;
+			break;
+		}
+		case RB_DAAP_TYPE_LONG: {
+			gint64 i = va_arg (list, gint64);
+
+			g_value_set_int64 (&(item->content), i);
+
+			item->size = 8;
+			break;
+		}
+		case RB_DAAP_TYPE_STRING: {
+			gchar *s = va_arg (list, gchar *);
+
+			g_value_set_string (&(item->content), s);
+
+			item->size = strlen (s);
+			break;
+		}
+		case RB_DAAP_TYPE_CONTAINER:
+		default:
+			break;
+	}
+	
+	node = g_node_new (item);
+	
+	if (parent) {
+		g_node_append (parent, node);
+
+		while (parent) {
+			RBDAAPItem *parent_item = parent->data;
+
+			parent_item->size += (4 + 4 + item->size);
+
+			parent = parent->parent;
+		}
+	}
+
+	return node;
+}
+
+static gboolean rb_daap_structure_node_serialize (GNode *node, GByteArray *array)
+{
+	RBDAAPItem *item = node->data;
+	RBDAAPType rb_daap_type;
+	guint32 size = GINT32_TO_BE (item->size);
+
+	g_byte_array_append (array, rb_daap_content_code_string (item->content_code), 4);
+	g_byte_array_append (array, (const guint8 *)&size, 4);
+	
+	rb_daap_type = rb_daap_content_code_rb_daap_type (item->content_code);
+
+	switch (rb_daap_type) {
+		case RB_DAAP_TYPE_BYTE: 
+		case RB_DAAP_TYPE_SIGNED_INT: {
+			gchar c = g_value_get_char (&(item->content));
+			
+			g_byte_array_append (array, &c, 1);
+			
+			break;
+		}
+		case RB_DAAP_TYPE_SHORT: {
+			gint32 i = g_value_get_int (&(item->content));
+			gint16 s = GINT16_TO_BE ((gint16) i);
+			
+			g_byte_array_append (array, (const guint8 *)&s, 2);
+
+			break;
+	        }
+		case RB_DAAP_TYPE_DATE: 
+		case RB_DAAP_TYPE_INT: {
+			gint32 i = g_value_get_int (&(item->content));
+			gint32 s = GINT32_TO_BE (i);
+
+			g_byte_array_append (array, (const guint8 *)&s, 4);
+			
+			break;
+		}
+		case RB_DAAP_TYPE_VERSION: {
+			gdouble v = g_value_get_double (&(item->content));
+			gint16 major;
+			gint8 minor;
+			gint8 patch = 0;
+
+			major = (gint16)v;
+			minor = (gint8)(v - ((gdouble)major));
+
+			major = GINT16_TO_BE(major);
+
+			g_byte_array_append (array, (const guint8 *)&major, 2);
+			g_byte_array_append (array, (const guint8 *)&minor, 1);
+			g_byte_array_append (array, (const guint8 *)&patch, 1);
+			
+			break;
+		}		
+		case RB_DAAP_TYPE_LONG: {
+			gint64 i = g_value_get_int64 (&(item->content));
+			gint64 s = GINT64_TO_BE (i);
+
+			g_byte_array_append (array, (const guint8 *)&s, 8);
+			
+			break;
+		}
+		case RB_DAAP_TYPE_STRING: {
+			const gchar *s = g_value_get_string (&(item->content));
+
+			g_byte_array_append (array, (const guint8 *)s, strlen (s));
+			
+			break;
+		}
+		case RB_DAAP_TYPE_CONTAINER:
+		default:
+			break;
+	}
+
+	return FALSE;
+}
+	
+gchar * rb_daap_structure_serialize (GNode *structure, guint *length)
+{
+	GByteArray *array;
+	gchar *data;
+
+	array = g_byte_array_new ();
+	
+	if (structure) {
+		g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)rb_daap_structure_node_serialize, array);
+	}
+	
+	data = (gchar *) array->data;
+	*length = array->len;
+	g_byte_array_free (array, FALSE);
+	
+	return data;
+}
+
+static RBDAAPContentCode rb_daap_buffer_read_content_code (const gchar *buf)
+{
+	gint32 c = MAKE_CONTENT_CODE (buf[0], buf[1], buf[2], buf[3]);
+	guint i;
+
+	for (i = 0; i < G_N_ELEMENTS (cc_defs); i++) {
+		if (cc_defs[i].int_code == c) {
+			return cc_defs[i].code;
+		}
+	}
+
+	g_warning ("ContentCode buffer: \"%4s\" is invalid",buf);
+	g_assert_not_reached ();
+	return 0;
+}
+
+static gint8 rb_daap_buffer_read_int8(const gchar *buf)
+{
+	return *(gint8*)buf;
+}
+
+static gint16 rb_daap_buffer_read_int16(const gchar *buf)
+{
+	return GINT16_FROM_BE(*(gint16*)buf);
+}
+
+static gint32 rb_daap_buffer_read_int32(const gchar *buf)
+{
+	return GINT32_FROM_BE(*(gint32*)buf);
+}
+
+static gint64 rb_daap_buffer_read_int64(const gchar *buf)
+{
+	return GINT64_FROM_BE(*(gint64*)buf);
+}
+
+static gchar * rb_daap_buffer_read_string (const gchar *buf, gint size)
+{
+	return g_strndup (buf, size);
+}
+
+//#define PARSE_DEBUG
+#define PARSE_DEBUG_FILE "daapbuffer"
+
+#ifdef PARSE_DEBUG
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+static void rb_daap_structure_parse_container_buffer (GNode *parent, const gchar *buf, gint buf_length)
+{
+	gint l = 0;
+
+	while (l < buf_length) {
+		gint codesize;
+		RBDAAPItem *item = g_new0 (RBDAAPItem, 1);
+		GNode *node = g_node_new (item);
+		GType gtype;
+		
+		g_node_append (parent, node);
+
+		item->content_code = rb_daap_buffer_read_content_code (&(buf[l]));
+		l += 4;
+
+		codesize = rb_daap_buffer_read_int32(&(buf[l]));
+		l += 4;
+
+		gtype = rb_daap_content_code_gtype (item->content_code);
+
+		if (gtype != G_TYPE_NONE) {
+			g_value_init (&(item->content), gtype);
+		}
+		
+#ifdef PARSE_DEBUG 
+		{
+			guint i;
+
+			for (i = 2; i < g_node_depth (node); i++) {
+				g_print ("\t");
+			}
+		}
+#endif
+		
+// FIXME USE THE G_TYPE CONVERTOR FUNCTION rb_daap_type_to_gtype
+		switch (rb_daap_content_code_rb_daap_type (item->content_code)) {
+			case RB_DAAP_TYPE_SIGNED_INT:
+			case RB_DAAP_TYPE_BYTE: {
+				gchar c = rb_daap_buffer_read_int8(&(buf[l]));
+				
+				g_value_set_char (&(item->content), c);
+#ifdef PARSE_DEBUG
+				g_print ("Code: %s, content (%d): \"%c\"\n", rb_daap_content_code_string (item->content_code), codesize, (gchar)c);
+#endif
+
+				break;
+			}
+			case RB_DAAP_TYPE_SHORT: {
+				gint16 s = rb_daap_buffer_read_int16(&(buf[l]));
+
+				g_value_set_int (&(item->content),(gint32)s);
+#ifdef PARSE_DEBUG
+				g_print ("Code: %s, content (%d): %hi\n", rb_daap_content_code_string (item->content_code), codesize, s);
+#endif
+
+				break;
+			}
+			case RB_DAAP_TYPE_DATE:
+			case RB_DAAP_TYPE_INT: {
+				gint32 i = rb_daap_buffer_read_int32(&(buf[l]));
+				
+				g_value_set_int (&(item->content), i);
+#ifdef PARSE_DEBUG
+				g_print ("Code: %s, content (%d): %d\n", rb_daap_content_code_string (item->content_code), codesize, i);
+#endif
+				break;
+			}
+			case RB_DAAP_TYPE_LONG: {
+				gint64 l = rb_daap_buffer_read_int64(&(buf[l]));
+	
+				g_value_set_int64 (&(item->content), l);
+#ifdef PARSE_DEBUG
+				g_print ("Code: %s, content (%d): %lld\n", rb_daap_content_code_string (item->content_code), codesize, l);
+#endif
+
+				break;
+			}
+			case RB_DAAP_TYPE_STRING: {
+				gchar *s = rb_daap_buffer_read_string (&(buf[l]), codesize);
+
+				g_value_take_string (&(item->content), s);
+#ifdef PARSE_DEBUG
+				g_print ("Code: %s, content (%d): \"%s\"\n", rb_daap_content_code_string (item->content_code), codesize, s);
+#endif
+
+				break;
+			}
+			case RB_DAAP_TYPE_VERSION: {
+				gint16 major = rb_daap_buffer_read_int16(&buf[l]);
+				gint16 minor = rb_daap_buffer_read_int16(&buf[l] + 2);
+				gdouble v = 0;
+
+
+				v = (gdouble)major;
+				v += (gdouble)(minor * 0.1);
+				
+				g_value_set_double (&(item->content), v);
+#ifdef PARSE_DEBUG
+				g_print ("Code: %s, content: %f\n", rb_daap_content_code_string (item->content_code), v);
+#endif
+
+				break;
+			}
+			case RB_DAAP_TYPE_CONTAINER: {
+#ifdef PARSE_DEBUG
+				g_print ("Code: %s, container\n", rb_daap_content_code_string (item->content_code));
+#endif
+				rb_daap_structure_parse_container_buffer (node,&buf[l], codesize);
+				break;
+			}
+		}
+
+		l += codesize;
+	}
+
+	return;
+}
+
+GNode * rb_daap_structure_parse (const gchar *buf, gint buf_length)
+{
+	GNode *root = NULL;
+	GNode *child = NULL;
+
+#ifdef PARSE_DEBUG
+	{
+		int fd;
+
+		fd = open (PARSE_DEBUG_FILE, O_WRONLY | O_CREAT);
+		write (fd, (const void *)buf, (size_t)buf_length);
+		close (fd);
+	}
+#endif
+	
+	root = g_node_new (NULL);
+
+	rb_daap_structure_parse_container_buffer (root, buf, buf_length);
+
+	child = root->children;
+	if (child) {
+		g_node_unlink (child);
+	}
+	g_node_destroy (root);
+	
+	return child;
+}
+
+struct NodeFinder {
+	RBDAAPContentCode code;
+	GNode *node;
+};
+
+static gboolean gnode_find_node (GNode *node, gpointer data)
+{
+	struct NodeFinder *finder = (struct NodeFinder *)data;
+	RBDAAPItem *item = node->data;
+
+	if (item->content_code == finder->code) {
+		finder->node = node;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+RBDAAPItem * rb_daap_structure_find_item (GNode *structure, RBDAAPContentCode code)
+{
+	GNode *node = NULL;
+	
+	node = rb_daap_structure_find_node (structure, code);
+
+	if (node) {
+		return node->data;
+	}
+
+	return NULL;
+}
+
+GNode * rb_daap_structure_find_node (GNode *structure, RBDAAPContentCode code)
+{
+	struct NodeFinder *finder;
+	GNode *node = NULL;
+
+	finder = g_new0(struct NodeFinder,1);
+	finder->code = code;
+
+	g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1, gnode_find_node, finder);
+
+	node = finder->node;
+	g_free (finder);
+	finder = NULL;
+
+	return node;
+}
+
+
+
+static void rb_daap_item_free (RBDAAPItem *item)
+{
+	if (rb_daap_content_code_rb_daap_type (item->content_code) != RB_DAAP_TYPE_CONTAINER) {
+		g_value_unset (&(item->content));
+	}
+	g_free (item);
+
+	return;
+}
+
+static gboolean gnode_free_rb_daap_item (GNode *node, gpointer data)
+{
+	rb_daap_item_free ((RBDAAPItem *)node->data);
+
+	return FALSE;
+}
+
+void rb_daap_structure_destroy (GNode *structure)
+{
+	if (structure) {
+		g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1, gnode_free_rb_daap_item, NULL);
+
+		g_node_destroy (structure);
+
+		structure = NULL;
+	}
+
+	return;
+}
+
+const RBDAAPContentCodeDefinition * rb_daap_content_codes (guint *number)
+{
+	*number = G_N_ELEMENTS(cc_defs);
+
+	return cc_defs;
+}
+
+gint32 rb_daap_content_code_string_as_int32 (const gchar *str)
+{
+	union {
+		gint32 i;
+		gchar str[4];
+	} u;
+
+	strncpy (u.str, str, 4);
+
+	return u.i;
+}
+
+static gboolean print_rb_daap_item (GNode *node, gpointer data)
+{
+	RBDAAPItem *item;
+	const gchar *name;
+	gchar *value;
+	gint i;
+
+	for (i = 1; i < g_node_depth (node); i++) {
+		g_print ("\t");
+	}
+
+	item = node->data;
+
+	name = rb_daap_content_code_name (item->content_code);
+
+	if (G_IS_VALUE (&(item->content))) {
+		value = g_strdup_value_contents (&(item->content));
+	} else {
+		value = g_strdup ("");
+	}
+	
+	g_print ("%d, %s = %s (%d)\n", g_node_depth (node), name, value, item->size);
+	g_free(value);
+
+	return FALSE;
+}
+
+void rb_daap_structure_print (GNode *structure)
+{
+	if (structure) {
+		g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)print_rb_daap_item, NULL);
+	}
+}
+	
diff -x CVS -x cvs -urN rhythmbox/daapsharing/rb-daap-structure.h myrhythmbox/daapsharing/rb-daap-structure.h
--- rhythmbox/daapsharing/rb-daap-structure.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/rb-daap-structure.h	2005-08-08 05:17:07.000000000 -0400
@@ -0,0 +1,168 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) structures
+ *
+ *  Copyright (C) 2004,2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_STRUCTURE_H
+#define __RB_DAAP_STRUCTURE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	RB_DAAP_CC_MDCL = 1,
+	RB_DAAP_CC_MSTT,
+	RB_DAAP_CC_MIID,
+	RB_DAAP_CC_MINM,
+	RB_DAAP_CC_MIKD,
+	RB_DAAP_CC_MPER,
+	RB_DAAP_CC_MCON,
+	RB_DAAP_CC_MCTI,
+	RB_DAAP_CC_MPCO,
+	RB_DAAP_CC_MSTS, // 10
+	RB_DAAP_CC_MIMC,
+	RB_DAAP_CC_MCTC,
+	RB_DAAP_CC_MRCO,
+	RB_DAAP_CC_MTCO,
+	RB_DAAP_CC_MLCL,
+	RB_DAAP_CC_MLIT,
+	RB_DAAP_CC_MBCL,
+	RB_DAAP_CC_MSRV, 
+	RB_DAAP_CC_MSAU,
+	RB_DAAP_CC_MSLR, // 20
+	RB_DAAP_CC_MPRO, 
+	RB_DAAP_CC_APRO,
+	RB_DAAP_CC_MSAL,
+	RB_DAAP_CC_MSUP,
+	RB_DAAP_CC_MSPI,
+	RB_DAAP_CC_MSEX,
+	RB_DAAP_CC_MSBR,
+	RB_DAAP_CC_MSQY,
+	RB_DAAP_CC_MSIX,
+	RB_DAAP_CC_MSRS, // 30
+	RB_DAAP_CC_MSTM, 
+	RB_DAAP_CC_MSDC,
+	RB_DAAP_CC_MCCR,
+	RB_DAAP_CC_MCNM,
+	RB_DAAP_CC_MCNA,
+	RB_DAAP_CC_MCTY,
+	RB_DAAP_CC_MLOG,
+	RB_DAAP_CC_MLID,
+	RB_DAAP_CC_MUPD,
+	RB_DAAP_CC_MUSR, // 40
+	RB_DAAP_CC_MUTY, 
+	RB_DAAP_CC_MUDL,
+	RB_DAAP_CC_AVDB,
+	RB_DAAP_CC_ABRO,
+	RB_DAAP_CC_ABAL,
+	RB_DAAP_CC_ABAR,
+	RB_DAAP_CC_ABCP,
+	RB_DAAP_CC_ABGN,
+	RB_DAAP_CC_ADBS,
+	RB_DAAP_CC_ASAL, // 50
+	RB_DAAP_CC_ASAR, 
+	RB_DAAP_CC_ASBT,
+	RB_DAAP_CC_ASBR,
+	RB_DAAP_CC_ASCM,
+	RB_DAAP_CC_ASCO,
+	RB_DAAP_CC_ASDA,
+	RB_DAAP_CC_ASDM,
+	RB_DAAP_CC_ASDC,
+	RB_DAAP_CC_ASDN,
+	RB_DAAP_CC_ASDB, // 60
+	RB_DAAP_CC_ASEQ,
+	RB_DAAP_CC_ASFM,
+	RB_DAAP_CC_ASGN,
+	RB_DAAP_CC_ASDT,
+	RB_DAAP_CC_ASRV,
+	RB_DAAP_CC_ASSR,
+	RB_DAAP_CC_ASSZ,
+	RB_DAAP_CC_ASST,
+	RB_DAAP_CC_ASSP,
+	RB_DAAP_CC_ASTM, // 70
+	RB_DAAP_CC_ASTC, 
+	RB_DAAP_CC_ASTN,
+	RB_DAAP_CC_ASUR,
+	RB_DAAP_CC_ASYR,
+	RB_DAAP_CC_ASDK,
+	RB_DAAP_CC_ASUL,
+	RB_DAAP_CC_APLY,
+	RB_DAAP_CC_ABPL,
+	RB_DAAP_CC_APSO,
+	RB_DAAP_CC_PRSV, // 80
+	RB_DAAP_CC_ARIF, 
+	RB_DAAP_CC_AESV,
+	RB_DAAP_CC_MSAS,
+	RB_DAAP_CC_AGRP,
+	RB_DAAP_CC_ASCP
+} RBDAAPContentCode;
+
+typedef struct _RBDAAPItem RBDAAPItem;
+
+struct _RBDAAPItem {
+	RBDAAPContentCode content_code;
+	GValue content;
+	guint size;
+};
+
+GNode * rb_daap_structure_add (GNode *parent, RBDAAPContentCode cc, ... );
+gchar * rb_daap_structure_serialize (GNode *structure, guint *length);
+
+GNode * rb_daap_structure_parse (const gchar *buf, gint buf_length);
+
+RBDAAPItem * rb_daap_structure_find_item (GNode *structure, RBDAAPContentCode code);
+GNode * rb_daap_structure_find_node (GNode *structure, RBDAAPContentCode code);
+
+void rb_daap_structure_print (GNode *structure);
+
+void rb_daap_structure_destroy (GNode *structure);
+
+typedef enum {
+	RB_DAAP_TYPE_BYTE = 0x0001,
+	RB_DAAP_TYPE_SIGNED_INT = 0x0002,
+	RB_DAAP_TYPE_SHORT = 0x0003,
+	RB_DAAP_TYPE_INT = 0x0005,
+	RB_DAAP_TYPE_LONG = 0x0007,
+	RB_DAAP_TYPE_STRING = 0x0009,
+	RB_DAAP_TYPE_DATE = 0x000A,
+	RB_DAAP_TYPE_VERSION = 0x000B,
+	RB_DAAP_TYPE_CONTAINER = 0x000C
+} RBDAAPType;
+
+typedef struct _RBDAAPContentCodeDefinition RBDAAPContentCodeDefinition;
+
+struct _RBDAAPContentCodeDefinition {
+	RBDAAPContentCode code;
+	gint32 int_code;
+	const gchar *name;
+	const gchar *string;
+	RBDAAPType type;
+};
+
+const RBDAAPContentCodeDefinition * rb_daap_content_codes (guint *number);
+gint32 rb_daap_content_code_string_as_int32 (const gchar *str);
+const gchar * rb_daap_content_code_name (RBDAAPContentCode code);
+RBDAAPType rb_daap_content_code_rb_daap_type (RBDAAPContentCode code);
+const gchar * rb_daap_content_code_string (RBDAAPContentCode code);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_STRUCTURE_H */
diff -x CVS -x cvs -urN rhythmbox/data/rhythmbox.schemas myrhythmbox/data/rhythmbox.schemas
--- rhythmbox/data/rhythmbox.schemas	2004-09-29 17:38:09.000000000 -0400
+++ myrhythmbox/data/rhythmbox.schemas	2005-08-08 05:16:58.000000000 -0400
@@ -334,5 +334,38 @@
 	<long>Audio disc burner device.</long>
         </locale>
       </schema>
+      <schema>
+	<key>/schemas/apps/rhythmbox/sharing/enable_browsing</key>
+	<applyto>/apps/rhythmbox/sharing/enable_browsing</applyto>
+	<owner>rhythmbox</owner>
+	<type>bool</type>
+	<default>0</default>
+	<locale name="C">
+	<short>Enables browsing your network for computers with shared music</short>
+	<long>When enabled, Rhythmbox will search for computers running Rhythmbox or Apple iTunes with shared music on your local network</long>
+	</locale>
+      </schema>
+      <schema>
+	<key>/schemas/apps/rhythmbox/sharing/enable_sharing</key>
+	<applyto>/apps/rhythmbox/sharing/enable_sharing</applyto>
+	<owner>rhythmbox</owner>
+	<type>bool</type>
+	<default>0</default>
+	<locale name="C">
+	<short>Enables sharing your music with computers on your network</short>
+	<long>When enabled, Rhythmbox will share your music with other computers running Rhythmbox or Apple iTunes</long>
+	</locale>
+      </schema>
+      <schema>
+	<key>/schemas/apps/rhythmbox/sharing/share_name</key>
+	<applyto>/apps/rhythmbox/sharing/share_name</applyto>
+	<owner>rhythmbox</owner>
+	<type>string</type>
+	<default></default>
+	<locale name="C">
+	<short>Name for your shared music</short>
+	<long>Name other computers will see your music shared as</long>
+	</locale>
+      </schema>
     </schemalist>
 </gconfschemafile>
diff -x CVS -x cvs -urN rhythmbox/data/ui/rhythmbox-ui.xml myrhythmbox/data/ui/rhythmbox-ui.xml
--- rhythmbox/data/ui/rhythmbox-ui.xml	2005-07-26 10:07:28.000000000 -0400
+++ myrhythmbox/data/ui/rhythmbox-ui.xml	2005-08-08 05:16:55.000000000 -0400
@@ -106,6 +106,10 @@
     <menuitem name="LibrarySrcPopupAddCD" action="MusicImportCD"/>
   </popup>
 
+  <popup name="DAAPSourcePopup">
+    <menuitem name="DAAPSrcPopupDisconnect" action="SourceDisconnect"/>
+  </popup>
+
   <popup name="IRadioSourcePopup">
     <menuitem name="IRadioSrcPopupNewStation" action="MusicNewInternetRadioStation"/>
   </popup>
diff -x CVS -x cvs -urN rhythmbox/Makefile.am myrhythmbox/Makefile.am
--- rhythmbox/Makefile.am	2004-09-02 23:14:32.000000000 -0400
+++ myrhythmbox/Makefile.am	2005-08-08 05:18:14.000000000 -0400
@@ -4,7 +4,7 @@
 
 DISTCHECK_CONFIGURE_FLAGS = --disable-schemas-install
 
-SUBDIRS = lib metadata player rhythmdb widgets sources iradio remote shell data po help tests
+SUBDIRS = lib metadata player rhythmdb widgets sources iradio remote daapsharing shell data po help tests
 
 EXTRA_DIST = 			\
 	autogen.sh		\
diff -x CVS -x cvs -urN rhythmbox/player/Makefile.am myrhythmbox/player/Makefile.am
--- rhythmbox/player/Makefile.am	2005-06-13 06:33:53.000000000 -0400
+++ myrhythmbox/player/Makefile.am	2005-08-08 05:18:20.000000000 -0400
@@ -17,6 +17,7 @@
 	-I$(top_srcdir) 				\
 	-I$(top_srcdir)/lib 				\
 	-I$(top_srcdir)/metadata			\
+	-I$(top_srcdir)/sources				\
 	$(RHYTHMBOX_CFLAGS)
 
 
diff -x CVS -x cvs -urN rhythmbox/player/rb-player-gst.c myrhythmbox/player/rb-player-gst.c
--- rhythmbox/player/rb-player-gst.c	2005-07-31 10:49:33.000000000 -0400
+++ myrhythmbox/player/rb-player-gst.c	2005-08-08 05:18:21.000000000 -0400
@@ -29,14 +29,25 @@
 #include <string.h>
 #include <stdlib.h>
 #include <libgnome/gnome-i18n.h>
+#include <libgnomevfs/gnome-vfs.h>
 #include <libgnomevfs/gnome-vfs-ops.h>
 #include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnomevfs/gnome-vfs-standard-callbacks.h>
 
 #include "rb-debug.h"
 #include "rb-player.h"
 #include "rb-debug.h"
 #include "rb-marshal.h"
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+typedef struct _RBDAAPSource RBDAAPSource;
+extern RBDAAPSource *	rb_daap_source_find_for_uri	(const gchar *uri);
+
+extern gchar *		rb_daap_source_get_headers	(RBDAAPSource *source,
+							 const gchar *uri, 
+							 glong time);
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
+
 G_DEFINE_TYPE(RBPlayer, rb_player, G_TYPE_OBJECT)
 
 static void rb_player_finalize (GObject *object);
@@ -476,6 +487,32 @@
 	return TRUE;
 }
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+static void gnomevfssrc_send_additional_headers_cb (gconstpointer in,
+						    gsize in_size,
+						    gpointer out,
+						    gsize out_size,
+						    gpointer callback_data)
+{
+	gchar *headers = (gchar *)callback_data;
+	gchar **split_headers;
+	gint i = 0;
+	
+  	GnomeVFSModuleCallbackAdditionalHeadersOut *out_args = (GnomeVFSModuleCallbackAdditionalHeadersOut *) out;
+	
+	split_headers = g_strsplit (headers, "\r\n", -1);
+	while (split_headers[i] && split_headers[i][0] != '\0') {
+		gchar *h = g_strdup_printf ("%s\r\n", split_headers[i]);
+		out_args->headers = g_list_append (out_args->headers, h);
+		i++;
+	}
+
+	g_strfreev (split_headers);
+
+	return;
+}
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
+
 void
 rb_player_open (RBPlayer *mp,
 		const char *uri,
@@ -494,12 +531,36 @@
 	g_free (mp->priv->uri);
 	mp->priv->uri = NULL;
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	if (g_strncasecmp (uri, "daap://", 7) == 0) {
+		RBDAAPSource *source;
+		gchar *headers;
+		gchar *fudged_uri;
+		
+		source = rb_daap_source_find_for_uri (uri);
+		headers = rb_daap_source_get_headers (source, uri, 0);
+		gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); // get rid of it if we've already pushed it 
+		gnome_vfs_module_callback_push (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS, gnomevfssrc_send_additional_headers_cb, headers, g_free);
+
+		fudged_uri = g_strconcat ("http", uri + 4, NULL);
+		
+		g_object_set (G_OBJECT (mp->priv->playbin), "uri", fudged_uri, NULL);	
+		mp->priv->uri = g_strdup(uri);
+		g_free (fudged_uri);
+
+	} else {
+#else /* WITH_DAAP_BROWSE_SUPPORT */
 	if (uri == NULL) {
 		mp->priv->playing = FALSE;
 		return;
 	}
 	g_object_set (G_OBJECT (mp->priv->playbin), "uri", uri, NULL);	
 	mp->priv->uri = g_strdup (uri);
+#endif
+
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	}
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 
 	if (!rb_player_sync_pipeline (mp, error)) {
 		rb_player_close (mp, NULL);
@@ -516,6 +577,10 @@
 	g_free (mp->priv->uri);
 	mp->priv->uri = NULL;
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); // get rid of it if we've already pushed it 
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
+	
 	if (mp->priv->playbin == NULL)
 		return;
 
@@ -660,12 +725,27 @@
 
 	gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED);
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	if (g_strncasecmp (mp->priv->uri, "daap://", 7) == 0) {
+		RBDAAPSource *source;
+		gchar *headers;
+
+		gst_element_set_state (mp->priv->playbin, GST_STATE_READY);
+		
+		source = rb_daap_source_find_for_uri (mp->priv->uri);
+		headers = rb_daap_source_get_headers (source, mp->priv->uri, time);
+		gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); // get rid of it if we've already pushed it 
+		gnome_vfs_module_callback_push (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS, gnomevfssrc_send_additional_headers_cb, headers, g_free);
+	} else {
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 	gst_element_seek (mp->priv->playbin, 
 			  GST_FORMAT_TIME 
 			  | GST_SEEK_METHOD_SET 
 			  | GST_SEEK_FLAG_FLUSH, 
 			  time * GST_SECOND);
-
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	}
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 	if (mp->priv->playing)
 		gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
 }
diff -x CVS -x cvs -urN rhythmbox/rhythmdb/rhythmdb.c myrhythmbox/rhythmdb/rhythmdb.c
--- rhythmbox/rhythmdb/rhythmdb.c	2005-08-01 16:15:40.000000000 -0400
+++ myrhythmbox/rhythmdb/rhythmdb.c	2005-08-08 20:05:47.530703120 -0400
@@ -1604,6 +1604,20 @@
 						  GNOME_VFS_FILE_INFO_FOLLOW_LINKS))
 	    == GNOME_VFS_OK)
 		return;
+#ifdef WITH_DAAP_SUPPORT
+	if (g_str_has_prefix (uri, "daap://")) {
+		GnomeVFSFileInfo *info = event->vfsinfo;
+
+		info->name = g_strdup(strrchr(uri,'/') + 1);
+		info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_FLAGS;
+
+		info->type = GNOME_VFS_FILE_TYPE_REGULAR;
+		info->flags = GNOME_VFS_FILE_FLAGS_NONE;
+		info->permissions = GNOME_VFS_PERM_USER_READ;
+		
+		return;
+	}
+#endif
 error:
 	unescaped = gnome_vfs_unescape_string_for_display (uri);
 	event->error = g_error_new (RHYTHMDB_ERROR,
diff -x CVS -x cvs -urN rhythmbox/shell/main.c myrhythmbox/shell/main.c
--- rhythmbox/shell/main.c	2005-07-11 00:07:00.000000000 -0400
+++ myrhythmbox/shell/main.c	2005-08-08 05:17:40.000000000 -0400
@@ -53,6 +53,10 @@
 #include "rb-remote-dbus.h"
 #endif
 
+#ifdef WITH_DAAP_SHARE_SUPPORT
+#include "daap-sharing.h"
+#endif /* WITH_DAAP_SHARE_SUPPORT */
+
 #include "rb-refstring.h"
 #include "rb-shell.h"
 #include "rb-debug.h"
@@ -267,6 +271,11 @@
 					 no_update, dry_run, rhythmdb_file);
 		rb_shell_construct (rb_shell);
 		g_object_weak_ref (G_OBJECT (rb_shell), main_shell_weak_ref_cb, NULL);
+#ifdef WITH_DAAP_SHARE_SUPPORT
+		{
+			daap_sharing_init (rb_shell);
+		}
+#endif /* WITH_DAAP_SHARE_SUPPORT */
 #ifdef WITH_BONOBO
 		rb_remote_bonobo_acquire (bonobo, RB_REMOTE_PROXY (rb_shell), error);
 		if (error) {
@@ -307,6 +316,9 @@
 
 		rb_debug ("out of toplevel loop");
 
+#ifdef WITH_DAAP_SHARE_SUPPORT
+		daap_sharing_shutdown (rb_shell);
+#endif
 		rb_file_helpers_shutdown ();
 		rb_string_helpers_shutdown ();
 		rb_stock_icons_shutdown ();
diff -x CVS -x cvs -urN rhythmbox/shell/Makefile.am myrhythmbox/shell/Makefile.am
--- rhythmbox/shell/Makefile.am	2005-06-19 14:30:57.000000000 -0400
+++ myrhythmbox/shell/Makefile.am	2005-08-08 05:17:37.000000000 -0400
@@ -116,6 +116,19 @@
 rhythmbox_LDADD += $(LIBNAUTILUS_BURN_LIBS)
 endif
 
+if USE_DAAP_BROWSE
+rhythmbox_LDADD += $(SOUP_LIBS) \
+		   $(top_builddir)/daapsharing/libdaapstructure.la
+endif
+
+if USE_DAAP_SHARE
+INCLUDES += -I$(top_srcdir)/daapsharing
+
+rhythmbox_LDADD += $(SOUP_LIBS) \
+		   $(HOWL_LIBS) \
+		   $(top_builddir)/daapsharing/libdaapsharing.la
+endif
+
 BUILT_SOURCES = $(tab_files)
 
 CLEANFILES = $(BUILT_SOURCES)
diff -x CVS -x cvs -urN rhythmbox/shell/rb-playlist-manager.c myrhythmbox/shell/rb-playlist-manager.c
--- rhythmbox/shell/rb-playlist-manager.c	2005-06-19 12:38:12.000000000 -0400
+++ myrhythmbox/shell/rb-playlist-manager.c	2005-08-08 05:17:18.000000000 -0400
@@ -709,6 +709,12 @@
 	return playlist;
 }
 
+GList *
+rb_playlist_manager_get_playlists (RBPlaylistManager *mgr)
+{
+	return mgr->priv->playlists;
+}
+
 static void
 rb_playlist_manager_cmd_new_playlist (GtkAction *action,
 				      RBPlaylistManager *mgr)
diff -x CVS -x cvs -urN rhythmbox/shell/rb-playlist-manager.h myrhythmbox/shell/rb-playlist-manager.h
--- rhythmbox/shell/rb-playlist-manager.h	2004-09-10 01:12:59.000000000 -0400
+++ myrhythmbox/shell/rb-playlist-manager.h	2005-08-08 05:17:18.000000000 -0400
@@ -78,6 +78,7 @@
 RBSource *		rb_playlist_manager_new_playlist (RBPlaylistManager *mgr,
 							  const char *suggested_name,
 							  gboolean automatic);
+GList *			rb_playlist_manager_get_playlists (RBPlaylistManager *manager);
 
 G_END_DECLS
 
diff -x CVS -x cvs -urN rhythmbox/shell/rb-shell.c myrhythmbox/shell/rb-shell.c
--- rhythmbox/shell/rb-shell.c	2005-08-04 10:18:04.000000000 -0400
+++ myrhythmbox/shell/rb-shell.c	2005-08-08 20:05:55.542485144 -0400
@@ -67,6 +67,9 @@
 #ifdef WITH_IPOD_SUPPORT
 #include "rb-ipod-source.h"
 #endif /* WITH_IPOD_SUPPORT */
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+#include "rb-daap-source.h"
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 #include "rb-load-failure-dialog.h"
 #include "rb-new-station-dialog.h"
 #include "rb-iradio-source.h"
@@ -108,7 +111,6 @@
 				    RBShell *shell);
 static void rb_shell_select_source (RBShell *shell, RBSource *source);
 static void rb_shell_select_source_internal (RBShell *shell, RBSource *source);
-static void rb_shell_append_source (RBShell *shell, RBSource *source);
 static RBSource *rb_shell_get_source_by_entry_type (RBShell *shell, 
 						    RhythmDBEntryType type);
 static void source_selected_cb (RBSourceList *sourcelist,
@@ -163,6 +165,8 @@
 				     RBShell *shell);
 static void rb_shell_cmd_current_song (GtkAction *action,
 				       RBShell *shell);
+static void rb_shell_cmd_source_disconnect (GtkAction *action,
+					    RBShell *shell);
 static void rb_shell_jump_to_current (RBShell *shell);
 static void rb_shell_jump_to_entry (RBShell *shell, RhythmDBEntry *entry);
 static void rb_shell_jump_to_entry_with_source (RBShell *shell, RBSource *source,
@@ -256,10 +260,20 @@
 #ifdef WITH_IPOD_SUPPORT
 	rb_ipod_source_new,	
 #endif /* WITH_IPOD_SUPPORT */
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	rb_daap_sources_init,
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 	NULL,
 };
 
+typedef void (*SourceShutdownFunc)(RBShell *);
 
+static SourceShutdownFunc known_source_shutdowns[] = {
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	rb_daap_sources_shutdown,
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
+	NULL,
+};
 
 static gboolean save_yourself_cb (GnomeClient *client, 
                                   gint phase,
@@ -290,7 +304,8 @@
 	PROP_RHYTHMDB_FILE,
 	PROP_SELECTED_SOURCE,
 	PROP_DB,
-	PROP_UI_MANAGER
+	PROP_UI_MANAGER,
+	PROP_PLAYLIST_MANAGER
 };
 
 /* prefs */
@@ -396,6 +411,9 @@
 	{ "ViewJumpToPlaying", GTK_STOCK_JUMP_TO, N_("_Jump to Playing Song"), "<control>J",
 	  N_("Scroll the view to the currently playing song"),
 	  G_CALLBACK (rb_shell_cmd_current_song) },
+	{ "SourceDisconnect", GTK_STOCK_DISCONNECT, N_("_Disconnect"), NULL, 
+	  N_("Disconnect from selected source"), 
+	  G_CALLBACK (rb_shell_cmd_source_disconnect) },
 };
 static guint rb_shell_n_actions = G_N_ELEMENTS (rb_shell_actions);
 
@@ -537,7 +555,13 @@
 							      "GtkUIManager object", 
 							      GTK_TYPE_UI_MANAGER,
 							       G_PARAM_READABLE));
-
+	g_object_class_install_property (object_class,
+					 PROP_PLAYLIST_MANAGER,
+					 g_param_spec_object ("playlist-manager",
+						 	      "RBPlaylistManager",
+							      "RBPlaylistManager object",
+							      RB_TYPE_PLAYLIST_MANAGER,
+							      G_PARAM_READABLE));
 
 }
 
@@ -677,6 +701,9 @@
 	case PROP_UI_MANAGER:
 		g_value_set_object (value, shell->priv->ui_manager);
 		break;
+	case PROP_PLAYLIST_MANAGER:
+		g_value_set_object (value, shell->priv->playlist_manager);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -751,7 +778,14 @@
 rb_shell_finalize (GObject *object)
 {
         RBShell *shell = RB_SHELL (object);
+	gint i = 0;
 
+	/* shutdown sources */
+	while (known_source_shutdowns[i] != NULL) {
+		known_source_shutdowns[i] (shell);
+		i++;
+	}
+	
 	gtk_widget_hide (shell->priv->window);
 	gtk_widget_hide (GTK_WIDGET (shell->priv->tray_icon));
 	rb_shell_player_stop (shell->priv->player_shell);
@@ -992,12 +1026,15 @@
 		RBSource *source;
 
 		source = known_sources[i] (shell);
-		rb_shell_append_source (shell, RB_SOURCE (source));
+		if (source) {
+			rb_shell_append_source (shell, RB_SOURCE (source), NULL);
+		}
 		i++;
 	}
 
 	library_source = rb_shell_get_source_by_entry_type (shell, RHYTHMDB_ENTRY_TYPE_SONG);
 	g_assert (library_source != NULL);
+	rb_library_source_setup_prefs (RB_LIBRARY_SOURCE (library_source));
 
 	iradio_source  = rb_shell_get_source_by_entry_type (shell, RHYTHMDB_ENTRY_TYPE_IRADIO_STATION);
 	g_assert (iradio_source != NULL);
@@ -1273,9 +1310,10 @@
 
 
 
-static void
+void
 rb_shell_append_source (RBShell *shell,
-			RBSource *source)
+			RBSource *source,
+			RBSource *parent)
 {
 	char *search_text;
 	
@@ -1291,7 +1329,7 @@
 	gtk_widget_show (GTK_WIDGET (source));
 
 	rb_sourcelist_append (RB_SOURCELIST (shell->priv->sourcelist),
-			      source);
+			      source, parent);
 
 	if (rb_source_can_search (source)) {
 		search_text = eel_gconf_get_string (rb_source_get_search_key (source));
@@ -1303,7 +1341,7 @@
 static void
 rb_shell_playlist_added_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell)
 {
-	rb_shell_append_source (shell, source);
+	rb_shell_append_source (shell, source, NULL);
 }
 
 static void
@@ -1419,7 +1457,12 @@
 
 	rb_debug ("selecting source %p", source);
 	
+	if (shell->priv->selected_source) {
+		rb_source_deactivate (shell->priv->selected_source);
+	}
+	
 	shell->priv->selected_source = source;
+	rb_source_activate (shell->priv->selected_source);
 	
 	view = rb_source_get_entry_view (shell->priv->selected_source);
 
@@ -1983,6 +2026,27 @@
 }
 
 static void
+rb_shell_cmd_source_disconnect (GtkAction *action,
+				RBShell *shell)
+{
+	rb_debug ("disconnect from source");
+
+	if (shell->priv->selected_source) {
+		RBSource *library_source;
+
+		library_source = rb_shell_get_source_by_entry_type (shell, 
+								    RHYTHMDB_ENTRY_TYPE_SONG);
+		rb_shell_select_source (shell, library_source);
+		
+		rb_source_disconnect (shell->priv->selected_source);
+	}
+	
+
+	
+	return;
+}
+
+static void
 rb_shell_cmd_view_all (GtkAction *action,
 		       RBShell *shell)
 {
diff -x CVS -x cvs -urN rhythmbox/shell/rb-shell.h myrhythmbox/shell/rb-shell.h
--- rhythmbox/shell/rb-shell.h	2004-07-13 23:13:03.000000000 -0400
+++ myrhythmbox/shell/rb-shell.h	2005-08-08 05:17:38.000000000 -0400
@@ -62,6 +62,9 @@
 void            rb_shell_register_entry_type_for_source (RBShell *shell,
 							 RBSource *source,
 							 RhythmDBEntryType type);
+
+void rb_shell_append_source (RBShell *shell, RBSource *source, RBSource *parent);
+
 G_END_DECLS
 
 #endif /* __RB_SHELL_H */
diff -x CVS -x cvs -urN rhythmbox/shell/rb-shell-preferences.c myrhythmbox/shell/rb-shell-preferences.c
--- rhythmbox/shell/rb-shell-preferences.c	2005-07-14 19:15:52.000000000 -0400
+++ myrhythmbox/shell/rb-shell-preferences.c	2005-08-08 05:17:36.000000000 -0400
@@ -31,6 +31,9 @@
 #include <gtk/gtkoptionmenu.h>
 #include <gtk/gtkradiobutton.h>
 #include <gtk/gtknotebook.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkcheckbutton.h>
 #include <glade/glade.h>
 #include <libgnome/gnome-help.h>
 #include <string.h>
@@ -271,6 +274,105 @@
 		rb_debug ("No config widget for source %s", name);
 }
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+
+#define CONF_ENABLE_SHARING "/apps/rhythmbox/sharing/enable_sharing"
+#define CONF_SHARE_NAME "/apps/rhythmbox/sharing/share_name"
+#define CONF_ENABLE_BROWSING "/apps/rhythmbox/sharing/enable_browsing"
+
+static void
+browse_check_button_toggled_cb (GtkToggleButton *button, gpointer data)
+{
+	gboolean b;
+
+	b = gtk_toggle_button_get_active (button);
+
+	eel_gconf_set_boolean (CONF_ENABLE_BROWSING, b);
+
+	return;
+}
+
+static void
+share_check_button_toggled_cb (GtkToggleButton *button, GtkWidget *hbox)
+{
+	gboolean b;
+
+	b = gtk_toggle_button_get_active (button);
+
+	eel_gconf_set_boolean (CONF_ENABLE_SHARING, b);
+
+	gtk_widget_set_sensitive (hbox, b);
+	
+	return;
+}
+
+static gboolean
+share_name_entry_focus_out_event_cb (GtkEntry *entry, GdkEventFocus *event, gpointer data)
+{
+	const gchar *name;
+
+	name = gtk_entry_get_text (entry);
+
+	eel_gconf_set_string (CONF_SHARE_NAME, name);
+
+	return FALSE;
+}
+
+static void
+add_daap_preferences (RBShellPreferences *shell_preferences)
+{
+	GtkWidget *vbox;
+	GtkWidget *check;
+	gboolean b;
+	
+	vbox = gtk_vbox_new (FALSE, 5);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+
+	check = gtk_check_button_new_with_mnemonic (_("_Look for shared music"));
+	gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
+	
+	b = eel_gconf_get_boolean (CONF_ENABLE_BROWSING);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), b);
+	g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (browse_check_button_toggled_cb), NULL);
+
+#ifdef WITH_DAAP_SHARE_SUPPORT
+	{
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *entry;
+	gchar *name;
+		
+	hbox = gtk_hbox_new (FALSE, 5);
+
+	check = gtk_check_button_new_with_mnemonic (_("_Share my music"));
+	gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
+	
+	b = eel_gconf_get_boolean (CONF_ENABLE_SHARING);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), b);
+	g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (share_check_button_toggled_cb), hbox);
+	
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new_with_mnemonic (_("Shared music _name"));
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+	entry = gtk_entry_new ();
+	gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+
+	name = eel_gconf_get_string (CONF_SHARE_NAME);
+	gtk_entry_set_text (GTK_ENTRY (entry), name);
+	g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (share_name_entry_focus_out_event_cb), NULL);	
+
+	gtk_widget_set_sensitive (hbox, b);
+	}
+#endif		
+	
+	gtk_notebook_append_page (GTK_NOTEBOOK (shell_preferences->priv->notebook), vbox, gtk_label_new (_("Sharing")));
+
+	return;
+}
+#endif
+
 GtkWidget *
 rb_shell_preferences_new (GList *views)
 {
@@ -290,6 +392,11 @@
 						       name,
 						       RB_SOURCE (views->data));
 	}
+
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	add_daap_preferences (shell_preferences);
+#endif
+
 	return GTK_WIDGET (shell_preferences);
 }
 
diff -x CVS -x cvs -urN rhythmbox/sources/Makefile.am myrhythmbox/sources/Makefile.am
--- rhythmbox/sources/Makefile.am	2005-06-12 09:57:38.000000000 -0400
+++ myrhythmbox/sources/Makefile.am	2005-08-08 05:18:10.000000000 -0400
@@ -30,6 +30,7 @@
 	-I$(top_srcdir)/player	 			\
 	-I$(top_srcdir)/iradio				\
 	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/daapsharing			\
 	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
 	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
 	-DDATADIR=\""$(datadir)"\"			\
@@ -55,3 +56,16 @@
 INCLUDES += $(LIBNAUTILUS_BURN_CFLAGS)
 endif
 
+if USE_DAAP_BROWSE
+libsourcesimpl_la_SOURCES +=		\
+	rb-daap-source.c 		\
+	rb-daap-source.h		\
+	rb-daap-playlist-source.c	\
+	rb-daap-playlist-source.h	\
+	rb-daap-connection.c		\
+	rb-daap-connection.h	
+
+INCLUDES += $(SOUP_CFLAGS)
+
+endif
+
diff -x CVS -x cvs -urN rhythmbox/sources/rb-daap-connection.c myrhythmbox/sources/rb-daap-connection.c
--- rhythmbox/sources/rb-daap-connection.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/sources/rb-daap-connection.c	2005-08-08 05:18:12.000000000 -0400
@@ -0,0 +1,1060 @@
+/*
+ *  Implementation of DAAP (iTunes Music Sharing) hashing & connection
+ *
+ *  Copyright (C) 2004,2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "rb-daap-connection.h"
+#include "rb-daap-structure.h"
+#include <libgnome/gnome-i18n.h>
+#include "rb-debug.h"
+
+/* hashing - based on/copied from libopendaap
+ * Copyright (c) 2004 David Hammerton
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+typedef struct {
+    guint32 buf[4];
+    guint32 bits[2];
+    unsigned char in[64];
+    int apple_ver;
+} MD5_CTX;
+
+/*
+* This code implements the MD5 message-digest algorithm.
+* The algorithm is due to Ron Rivest.  This code was
+* written by Colin Plumb in 1993, no copyright is claimed.
+* This code is in the public domain; do with it what you wish.
+*
+* Equivalent code is available from RSA Data Security, Inc.
+* This code has been tested against that, and is equivalent,
+* except that you don't need to include two pages of legalese
+* with every copy.
+*
+* To compute the message digest of a chunk of bytes, declare an MD5Context
+* structure, pass it to OpenDaap_MD5Init, call OpenDaap_MD5Update as needed
+* on buffers full of bytes, and then call OpenDaap_MD5Final, which will fill
+* a supplied 16-byte array with the digest.
+*/
+static void MD5Transform(guint32 buf[4], guint32 const in[16], int apple_ver);
+/* for some reason we still have to reverse bytes on bigendian machines
+ * I don't really know why... but otherwise it fails..
+ * Any MD5 gurus out there know why???
+ */
+#if 0 //ndef WORDS_BIGENDIAN /* was: HIGHFIRST */
+#define byteReverse(buf, len)     /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+* Note: this code is harmless on little-endian machines.
+*/
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+     guint32 t;
+     do {
+          t = (guint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+               ((unsigned) buf[1] << 8 | buf[0]);
+          *(guint32 *) buf = t;
+          buf += 4;
+     } while (--longs);
+}
+#endif
+#endif
+
+
+static void OpenDaap_MD5Init(MD5_CTX *ctx, int apple_ver)
+{
+    memset(ctx, 0, sizeof(MD5_CTX));
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+
+    ctx->apple_ver = apple_ver;
+}
+
+static void OpenDaap_MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned int len)
+{
+    guint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
+        ctx->bits[1]++;          /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;     /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+        unsigned char *p = (unsigned char *) ctx->in + t;
+
+        t = 64 - t;
+        if (len < t) {
+            memcpy(p, buf, len);
+            return;
+        }
+        memcpy(p, buf, t);
+        byteReverse(ctx->in, 16);
+        MD5Transform(ctx->buf, (guint32 *) ctx->in, ctx->apple_ver);
+        buf += t;
+        len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+        memcpy(ctx->in, buf, 64);
+        byteReverse(ctx->in, 16);
+        MD5Transform(ctx->buf, (guint32 *) ctx->in, ctx->apple_ver);
+        buf += 64;
+        len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+
+}
+
+static void OpenDaap_MD5Final(MD5_CTX *ctx, unsigned char digest[16])
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+    always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+        /* Two lots of padding:  Pad the first block to 64 bytes */
+        memset(p, 0, count);
+        byteReverse(ctx->in, 16);
+        MD5Transform(ctx->buf, (guint32 *) ctx->in, ctx->apple_ver);
+
+        /* Now fill the next block with 56 bytes */
+        memset(ctx->in, 0, 56);
+    } else {
+        /* Pad block to 56 bytes */
+        memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((guint32 *) ctx->in)[14] = ctx->bits[0];
+    ((guint32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (guint32 *) ctx->in, ctx->apple_ver);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));     /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+* The core of the MD5 algorithm, this alters an existing MD5 hash to reflect
+* the addition of 16 longwords of new data.  OpenDaap_MD5Update blocks the
+* data and converts bytes into longwords for this routine.
+*/
+static void MD5Transform(guint32 buf[4], guint32 const in[16], int apple_ver)
+{
+    guint32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+
+    if (apple_ver == 1)
+    {
+        MD5STEP(F2, b, c, d, a, in[8] + 0x445a14ed, 20);
+    }
+    else
+    {
+        MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    }
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
+
+
+
+
+
+static int staticHashDone = 0;
+static char staticHash_42[256*65] = {0};
+static char staticHash_45[256*65] = {0};
+
+static const char hexchars[] = "0123456789ABCDEF";
+static const char appleCopyright[] = "Copyright 2003 Apple Computer, Inc.";
+
+static void DigestToString(const unsigned char *digest, char *string)
+{
+    int i;
+    for (i = 0; i < 16; i++)
+    {
+        unsigned char tmp = digest[i];
+        string[i*2+1] = hexchars[tmp & 0x0f];
+        string[i*2] = hexchars[(tmp >> 4) & 0x0f];
+    }
+}
+
+static void GenerateStatic_42()
+{
+    MD5_CTX ctx;
+    unsigned char *p = staticHash_42;
+    int i;
+    char buf[16];
+
+    for (i = 0; i < 256; i++)
+    {
+        OpenDaap_MD5Init(&ctx, 0);
+
+#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, str, strlen(str))
+
+        if ((i & 0x80) != 0)
+            MD5_STRUPDATE("Accept-Language");
+        else
+            MD5_STRUPDATE("user-agent");
+
+        if ((i & 0x40) != 0)
+            MD5_STRUPDATE("max-age");
+        else
+            MD5_STRUPDATE("Authorization");
+
+        if ((i & 0x20) != 0)
+            MD5_STRUPDATE("Client-DAAP-Version");
+        else
+            MD5_STRUPDATE("Accept-Encoding");
+
+        if ((i & 0x10) != 0)
+            MD5_STRUPDATE("daap.protocolversion");
+        else
+            MD5_STRUPDATE("daap.songartist");
+
+        if ((i & 0x08) != 0)
+            MD5_STRUPDATE("daap.songcomposer");
+        else
+            MD5_STRUPDATE("daap.songdatemodified");
+
+        if ((i & 0x04) != 0)
+            MD5_STRUPDATE("daap.songdiscnumber");
+        else
+            MD5_STRUPDATE("daap.songdisabled");
+
+        if ((i & 0x02) != 0)
+            MD5_STRUPDATE("playlist-item-spec");
+        else
+            MD5_STRUPDATE("revision-number");
+
+        if ((i & 0x01) != 0)
+            MD5_STRUPDATE("session-id");
+        else
+            MD5_STRUPDATE("content-codes");
+#undef MD5_STRUPDATE
+
+        OpenDaap_MD5Final(&ctx, buf);
+        DigestToString(buf, p);
+        p += 65;
+    }
+}
+
+static void GenerateStatic_45()
+{
+    MD5_CTX ctx;
+    unsigned char *p = staticHash_45;
+    int i;
+    char buf[16];
+
+    for (i = 0; i < 256; i++)
+    {
+        OpenDaap_MD5Init(&ctx, 1);
+
+#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, str, strlen(str))
+
+        if ((i & 0x40) != 0)
+            MD5_STRUPDATE("eqwsdxcqwesdc");
+        else
+            MD5_STRUPDATE("op[;lm,piojkmn");
+
+        if ((i & 0x20) != 0)
+            MD5_STRUPDATE("876trfvb 34rtgbvc");
+        else
+            MD5_STRUPDATE("=-0ol.,m3ewrdfv");
+
+        if ((i & 0x10) != 0)
+            MD5_STRUPDATE("87654323e4rgbv ");
+        else
+            MD5_STRUPDATE("1535753690868867974342659792");
+
+        if ((i & 0x08) != 0)
+            MD5_STRUPDATE("Song Name");
+        else
+            MD5_STRUPDATE("DAAP-CLIENT-ID:");
+
+        if ((i & 0x04) != 0)
+            MD5_STRUPDATE("111222333444555");
+        else
+            MD5_STRUPDATE("4089961010");
+
+        if ((i & 0x02) != 0)
+            MD5_STRUPDATE("playlist-item-spec");
+        else
+            MD5_STRUPDATE("revision-number");
+
+        if ((i & 0x01) != 0)
+            MD5_STRUPDATE("session-id");
+        else
+            MD5_STRUPDATE("content-codes");
+
+        if ((i & 0x80) != 0)
+            MD5_STRUPDATE("IUYHGFDCXWEDFGHN");
+        else
+            MD5_STRUPDATE("iuytgfdxwerfghjm");
+
+#undef MD5_STRUPDATE
+
+        OpenDaap_MD5Final(&ctx, buf);
+        DigestToString(buf, p);
+        p += 65;
+    }
+}
+
+static void rb_daap_hash_generate (short version_major, 
+				const guchar *url, 
+				guchar hash_select, 
+				guchar *out, 
+				gint request_id)
+{
+    char buf[16];
+    MD5_CTX ctx;
+
+    char *hashTable = (version_major == 3) ?
+                      staticHash_45 : staticHash_42;
+
+    if (!staticHashDone)
+    {
+        GenerateStatic_42 ();
+        GenerateStatic_45 ();
+        staticHashDone = 1;
+    }
+
+    OpenDaap_MD5Init (&ctx, (version_major == 3) ? 1 : 0);
+
+    OpenDaap_MD5Update (&ctx, url, strlen(url));
+    OpenDaap_MD5Update (&ctx, appleCopyright, strlen (appleCopyright));
+
+    OpenDaap_MD5Update (&ctx, &hashTable[hash_select * 65], 32);
+
+    if (request_id && version_major == 3)
+    {
+        char scribble[20];
+        sprintf (scribble, "%u", request_id);
+        OpenDaap_MD5Update (&ctx, scribble, strlen (scribble));
+    }
+
+    OpenDaap_MD5Final (&ctx, buf);
+    DigestToString (buf, out);
+
+    return;
+}
+
+/* end hashing */
+
+
+/* connection */
+#include <math.h>
+#include <libsoup/soup.h>
+#include <libsoup/soup-connection.h>
+#include <libsoup/soup-session-async.h>
+
+#include <libsoup/soup-uri.h>
+
+#define RB_DAAP_USER_AGENT "iTunes/4.6 (Windows; N)"
+
+struct _RBDAAPConnection {
+	SoupSession *session;
+	SoupUri *base_uri;
+	
+	gdouble daap_version;
+	gint session_id;
+	gint revision_number;
+
+	gint request_id;
+	gint database_id;
+
+	GSList *playlists;
+	GHashTable *item_id_to_uri;
+};
+
+
+static SoupMessage * build_message (RBDAAPConnection *connection, const gchar *path, gboolean need_hash, gdouble version, gint req_id, gboolean send_close)
+{
+	SoupMessage *message = NULL;
+	SoupUri *uri = NULL;
+	
+	uri = soup_uri_new_with_base(connection->base_uri, path);
+	if (uri == NULL) {
+		return NULL;
+	}		
+	
+	message = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+	soup_message_set_http_version (message, SOUP_HTTP_1_1);
+	
+	soup_message_add_header (message->request_headers, "Client-DAAP-Version", 	"3.0");
+	soup_message_add_header (message->request_headers, "Accept-Laungage", 		"en-us, en;q=5.0");
+	soup_message_add_header (message->request_headers, "Client-DAAP-Access-Index", 	"2");
+	
+	if (need_hash) {
+		gchar hash[33] = {0};
+		gchar *norb_daap_path = (gchar *)path;
+		
+		if (g_strncasecmp (path, "daap://", 7) == 0) {
+			norb_daap_path = strstr (path, "/data");
+		}
+
+		rb_daap_hash_generate ((short)floor (version), norb_daap_path, 2, hash, req_id);
+
+		soup_message_add_header (message->request_headers, "Client-DAAP-Validation", hash);
+	}
+	if (send_close) {
+		soup_message_add_header (message->request_headers, "Connection", "close");
+	}
+	
+	return message;
+}
+
+static gboolean http_get (RBDAAPConnection *connection, const gchar *path, gboolean need_hash, gdouble version, gint req_id, gboolean send_close, GNode **structure)
+{
+	SoupMessage *message;
+	gint res;
+	RBDAAPItem *item = NULL;
+	
+	message = build_message (connection, path, need_hash, version, req_id, send_close);
+	if (message == NULL) {
+		g_print("Error building message\n");
+		return FALSE;
+	}
+	
+	soup_session_send_message (connection->session, message);
+	
+	if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code) == FALSE) {
+		g_object_unref (message);
+		g_print("error getting %s: %d, %s\n",path,message->status_code,message->reason_phrase);
+		return FALSE;
+	} 
+
+	*structure = rb_daap_structure_parse (message->response.body, message->response.length);
+	g_object_unref (message);
+	
+	if (*structure == NULL) {
+		g_warning ("No daap structure returned: '%s'", path);
+		return FALSE;
+	}
+
+	item = rb_daap_structure_find_item (*structure, RB_DAAP_CC_MSTT);
+	if (item == NULL) {
+		g_warning ("Error, could not find dmap.status in server-info response: '%s'", path);
+		return FALSE;
+	}
+
+	if (g_value_get_int (&(item->content)) != 200) {
+		g_warning ("Error, dmap.status is not 200: '%s'", path);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+	
+static void 
+entry_set_string_prop (RhythmDB *db, RhythmDBEntry *entry,
+		       RhythmDBPropType propid, const char *str)
+{
+	GValue value = {0,};
+	gchar *tmp;
+
+	if (str == NULL) {
+		tmp = g_strdup (_("Unknown"));
+	} else {
+		tmp = g_strdup (str);
+	}
+
+	g_value_init (&value, G_TYPE_STRING);
+	g_value_set_string_take_ownership (&value, tmp);
+	rhythmdb_entry_set (RHYTHMDB (db), entry, propid, &value);
+	g_value_unset (&value);
+}
+
+RBDAAPConnection * rb_daap_connection_new (const gchar *host, gint port, RhythmDB *db, RhythmDBEntryType type)
+{
+	RBDAAPConnection *connection;
+ 	GNode *structure = NULL;
+	gboolean ret;
+	RBDAAPItem *item = NULL;
+	gchar *path = NULL;
+	gint n_databases;
+	GNode *listing_node = NULL;
+	gint i;
+	GNode *n;
+	gint returned_count;
+	gint specified_total_count;
+	gboolean update_type;
+
+	connection = g_new0 (RBDAAPConnection, 1);
+
+	connection->session = soup_session_sync_new ();
+	path = g_strdup_printf ("http://%s:%d/";, host, port);
+	connection->base_uri = soup_uri_new (path);
+	if (connection->base_uri == NULL) {
+		g_print ("Error parsing uri\n");
+		g_free (path);
+		goto connection_new_cleanup;
+	}
+	
+	
+	/* get the version number */
+	ret = http_get (connection,"/server-info", FALSE, 0.0, 0, FALSE, &structure);
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_APRO);
+	if (item == NULL) {
+		g_print ("Error, could not find daap.protocolversion in server-info response\n");
+		goto connection_new_cleanup;
+	}
+
+	connection->daap_version = g_value_get_double (&(item->content));
+
+	rb_daap_structure_destroy (structure);
+	structure = NULL;
+
+
+	/* get a session id */
+	ret = http_get (connection,"/login", FALSE, 0.0, 0, FALSE, &structure);
+
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MLID);
+	if (item == NULL) {
+		g_print ("Error, could not find daap.sessionid in login response\n");
+		goto connection_new_cleanup;
+	}
+
+	connection->session_id = g_value_get_int (&(item->content));
+
+	rb_daap_structure_destroy (structure);
+	structure = NULL;
+
+	/* get a revision number */
+	path = g_strdup_printf ("/update?session-id=%d&revision-number=1", connection->session_id);
+	ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+	g_free (path);
+
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUSR);
+	if (item == NULL) {
+		g_print ("Error, could not find daap.serverrevision in update response\n");
+		goto connection_new_cleanup;
+	}
+
+	connection->revision_number = g_value_get_int (&(item->content));
+
+	rb_daap_structure_destroy (structure);
+	structure = NULL;
+
+	/* get database id */
+	/* get a list of databases, there should be only 1 */
+	path = g_strdup_printf ("/databases?session-id=%d&revision-number=%d", connection->session_id, connection->revision_number);
+	ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+	g_free (path);
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.returnedcount in databases response\n");
+		goto connection_new_cleanup;
+	}
+
+	n_databases = g_value_get_int (&(item->content));
+	if (n_databases != 1) {
+		g_print ("Host seems to have more than 1 database, how strange\n");
+	}
+
+	listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
+	if (listing_node == NULL) {
+		g_print ("Error, could not find dmap.listing in databases response\n");
+		goto connection_new_cleanup;
+	}
+
+	item = rb_daap_structure_find_item (listing_node->children, RB_DAAP_CC_MIID);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.itemid in databases response\n");
+		goto connection_new_cleanup;
+	}
+	
+	connection->database_id = g_value_get_int (&(item->content));
+	rb_daap_structure_destroy (structure);
+	structure = NULL;
+	
+	/* get the songs */
+	path = g_strdup_printf ("/databases/%i/items?session-id=%i&revision-number=%i&meta=dmap.itemid,dmap.itemname,daap.songalbum,daap.songartist,daap.daap.songgenre,daap.songsize,daap.songtime,daap.songtrackcount,daap.songtracknumber,daap.songyear,daap.songformat,daap.songgenre,daap.songbitrate", connection->database_id, connection->session_id, connection->revision_number);
+	ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+	g_free (path);
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.returnedcount in items response\n");
+		goto connection_new_cleanup;
+	}
+	returned_count = g_value_get_int (&(item->content));
+	
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MTCO);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.specifiedtotalcount in items response\n");
+		goto connection_new_cleanup;
+	}
+	specified_total_count = g_value_get_int (&(item->content));
+	
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUTY);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.updatetype in items response\n");
+		goto connection_new_cleanup;
+	}
+	update_type = g_value_get_char (&(item->content));
+
+	listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
+	if (listing_node == NULL) {
+		g_print ("Error, could not find dmap.listing in items response\n");
+		goto connection_new_cleanup;
+	}
+
+	connection->item_id_to_uri = g_hash_table_new_full ((GHashFunc)g_direct_hash,(GEqualFunc)g_direct_equal, NULL, g_free);
+	
+	for (i = 0, n = listing_node->children; n; i++, n = n->next) {
+		GNode *n2;
+		RhythmDBEntry *entry = NULL;
+		GValue value = {0,};
+		gchar *uri = NULL;
+		gint item_id = 0;
+		const gchar *title = NULL;
+		const gchar *album = NULL;
+		const gchar *artist = NULL;
+		const gchar *format = NULL;
+		const gchar *genre = NULL;
+		gint length = 0;
+		gint track_number = 0;
+		gint disc_number = 0;
+		gint year = 0;
+		gint size = 0;
+		gint bitrate = 0;
+		
+		for (n2 = n->children; n2; n2 = n2->next) {
+			RBDAAPItem *meta_item;
+			
+			meta_item = n2->data;
+
+			switch (meta_item->content_code) {
+				case RB_DAAP_CC_MIID:
+					item_id = g_value_get_int (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_MINM:
+					title = g_value_get_string (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASAL:
+					album = g_value_get_string (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASAR:
+					artist = g_value_get_string (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASFM:
+					format = g_value_get_string (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASGN:
+					genre = g_value_get_string (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASTM:
+					length = g_value_get_int (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASTN:
+					track_number = g_value_get_int (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASDN:
+					disc_number = g_value_get_int (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASYR:
+					year = g_value_get_int (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASSZ:
+					size = g_value_get_int (&(meta_item->content));
+					break;
+				case RB_DAAP_CC_ASBR:
+					bitrate = g_value_get_int (&(meta_item->content));
+					break;
+				default:
+					break;
+			}
+		}
+
+//		if (connection->daap_version == 3.0) {
+			uri = g_strdup_printf ("daap://%s:%d/databases/%d/items/%d.%s?session-id=%d", host, port, connection->database_id, item_id, format, connection->session_id);
+//		} else {
+//			g_warning ("OLD ITUNES!\n");
+			// uri should be 
+			// "/databases/%d/items/%d.%s?session-id=%d&revision-id=%d";
+			// but its not going to work cause the other parts of the code 
+			// depend on the uri to have the ip address so that the
+			// RBDAAPSource can be found to ++request_id
+			// maybe just /dont/ support older itunes.  doesn't seem 
+			// unreasonable to me, honestly
+//		}
+		entry = rhythmdb_entry_new (db, type, uri);
+		g_hash_table_insert (connection->item_id_to_uri, GINT_TO_POINTER (item_id), uri);
+
+		 /* track number */
+		g_value_init (&value, G_TYPE_ULONG);
+		g_value_set_ulong (&value,(gulong)track_number);
+		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_TRACK_NUMBER,&value);
+		g_value_unset (&value);
+
+		/* disc number */
+		g_value_init (&value, G_TYPE_ULONG);
+		g_value_set_ulong (&value,(gulong)disc_number);
+		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DISC_NUMBER,&value);
+		g_value_unset (&value);
+
+		/* bitrate */
+		g_value_init (&value, G_TYPE_ULONG);
+		g_value_set_ulong (&value,(gulong)bitrate);
+		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_BITRATE,&value);
+		g_value_unset (&value);
+		
+		/* length */
+		g_value_init (&value, G_TYPE_ULONG);
+		g_value_set_ulong (&value,(gulong)length / 1000);
+		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DURATION,&value);
+		g_value_unset (&value);
+
+		/* file size */
+		g_value_init (&value, G_TYPE_UINT64);
+		g_value_set_uint64(&value,(gint64)size);
+		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_FILE_SIZE,&value);
+		g_value_unset (&value);
+
+		/* title */
+		entry_set_string_prop (db, entry, RHYTHMDB_PROP_TITLE, title);
+
+		/* album */
+		entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM, album);
+
+		/* artist */
+		entry_set_string_prop (db, entry, RHYTHMDB_PROP_ARTIST, artist);
+
+		/* genre */
+		entry_set_string_prop (db, entry, RHYTHMDB_PROP_GENRE, genre);
+	}
+
+	rhythmdb_commit (db);
+	rb_daap_structure_destroy (structure);
+	structure = NULL;
+
+	/* get a list of playlists */
+	path = g_strdup_printf ("/databases/%d/containers?session-id=%d&revision-number=%d", connection->database_id, connection->session_id, connection->revision_number);
+	ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+	g_free (path);
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
+	if (listing_node == NULL) {
+		g_print ("Error, could not find dmap.listing in playlists response\n");
+		goto connection_new_cleanup;
+	}
+
+	for (i = 0, n = listing_node->children; n; n = n->next, i++) {
+		gint id;
+		gchar *name;
+		RBDAAPPlaylist *playlist;
+		GNode *playlist_structure;
+		GNode *playlist_listing_node;
+		GNode *n2;
+		gint j;
+		GList *playlist_uris = NULL;
+	
+		item = rb_daap_structure_find_item (n, RB_DAAP_CC_ABPL);
+		if (item != NULL) {
+			continue;
+		}
+
+		item = rb_daap_structure_find_item (n, RB_DAAP_CC_MIID);
+		if (item == NULL) {
+			g_print ("Error, could not find dmap.itemid in playlists response");
+			goto connection_new_cleanup;
+		}
+		id = g_value_get_int (&(item->content));
+
+		item = rb_daap_structure_find_item (n, RB_DAAP_CC_MINM);
+		if (item == NULL) {
+			g_print ("Error, could not find dmap.itemname in playlists response");
+			goto connection_new_cleanup;
+		}
+		name = g_value_dup_string (&(item->content));
+
+		path = g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%d&revision-number=%d&meta=dmap.itemid", connection->database_id, id, connection->session_id, connection->revision_number);
+		ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&playlist_structure);
+		g_free (path);
+		if (playlist_structure == NULL || ret == FALSE) {
+			g_free (name);
+			continue;
+			}
+
+		playlist_listing_node = rb_daap_structure_find_node (playlist_structure, RB_DAAP_CC_MLCL);
+		if (playlist_listing_node == NULL) {
+			g_print ("Error, could not find dmap.listing in playlists response\n");
+			g_free (name);
+			continue;
+		}
+
+		for (j = 0, n2 = playlist_listing_node->children; n2; n2 = n2->next, j++) {
+			gchar *item_uri;
+			gint playlist_item_id;
+
+			item = rb_daap_structure_find_item (n2, RB_DAAP_CC_MIID);
+			if (item == NULL) {
+				g_print ("Error, could not find dmap.itemid in playlists response");
+				continue;
+			}
+			playlist_item_id = g_value_get_int (&(item->content));
+		
+			item_uri = g_hash_table_lookup (connection->item_id_to_uri, GINT_TO_POINTER (playlist_item_id));
+			if (item_uri == NULL) {
+				rb_debug ("%d in %s doesnt exist in the database\n", playlist_item_id, name);
+				continue;
+			}
+			
+			playlist_uris = g_list_prepend (playlist_uris, item_uri);
+		}
+		
+		playlist = g_new0(RBDAAPPlaylist,1);
+
+		playlist->id = id;
+		playlist->name = name;
+		playlist->uris = playlist_uris;
+
+		connection->playlists = g_slist_prepend (connection->playlists, playlist);
+
+		
+	}
+	rb_daap_structure_destroy (structure);
+	structure = NULL;
+
+//	g_hash_table_destroy (item_id_to_uri);
+	return connection;
+
+connection_new_cleanup:
+	if (connection && connection->item_id_to_uri) {
+		g_hash_table_destroy (connection->item_id_to_uri);
+	}
+	rb_daap_structure_destroy (structure);
+	structure = NULL;
+//FIXME destroy connection ?
+	return NULL;
+}
+
+gchar * rb_daap_connection_get_headers (RBDAAPConnection *connection, const gchar *uri, guint64 bytes)
+{
+	GString *headers;
+	gchar hash[33] = {0};
+	gchar *norb_daap_uri = (gchar *)uri;
+	gchar *s;
+	
+	connection->request_id++;
+	
+	if (g_strncasecmp (uri,"daap://",7) == 0) {
+		norb_daap_uri = strstr (uri,"/data");
+	}
+
+	rb_daap_hash_generate ((short)floorf (connection->daap_version), norb_daap_uri,2, hash, connection->request_id);
+
+	headers = g_string_new ("Accept: */*\r\nCache-Control: no-cache\r\nUser-Agent: iTunes/4.6 (Windows; N)\r\nClient-DAAP-Access-Index: 2\r\nClient-DAAP-Version: 3.0\r\n");
+	g_string_append_printf (headers,"Client-DAAP-Validation: %s\r\nClient-DAAP-Request-ID: %d\r\nConnection: close\r\n", hash, connection->request_id);
+
+	if (bytes != 0) {
+		g_string_append_printf (headers,"Range: bytes=%"G_GUINT64_FORMAT"-\r\n", bytes);
+	}
+	
+	s = headers->str;
+	g_string_free (headers, FALSE);
+
+	return s;
+}
+
+GSList * rb_daap_connection_get_playlists (RBDAAPConnection *connection)
+{
+	if (connection) {
+		return connection->playlists;
+	}
+
+	return NULL;
+}
+
+
+void rb_daap_connection_destroy (RBDAAPConnection *connection)
+{
+	GSList *l;
+
+	for (l = connection->playlists; l; l = l->next) {
+		RBDAAPPlaylist *playlist = l->data;
+
+		g_list_free (playlist->uris);
+		g_free (playlist->name);
+		g_free (playlist);
+		l->data = NULL;
+	}
+	g_slist_free (connection->playlists);
+	connection->playlists = NULL;
+
+	if (connection->item_id_to_uri) {
+		g_hash_table_destroy (connection->item_id_to_uri);
+		connection->item_id_to_uri = NULL;
+	}
+	
+	if (connection->session) {
+		g_object_unref (connection->session);
+		connection->session = NULL;
+	}
+	
+	g_free (connection);
+	connection = NULL;
+
+	return;
+}
diff -x CVS -x cvs -urN rhythmbox/sources/rb-daap-connection.h myrhythmbox/sources/rb-daap-connection.h
--- rhythmbox/sources/rb-daap-connection.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/sources/rb-daap-connection.h	2005-08-08 05:18:12.000000000 -0400
@@ -0,0 +1,48 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) hashing & connection
+ *
+ *  Copyright (C) 2004,2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_CONNECTION_H
+#define __RB_DAAP_CONNECTIONH
+
+#include <glib.h>
+#include <glib-object.h>
+#include "rhythmdb.h"
+
+G_BEGIN_DECLS
+
+typedef struct _RBDAAPConnection RBDAAPConnection;
+
+typedef struct {
+	gchar *name;
+	gint id;
+	GList *uris;
+} RBDAAPPlaylist;
+
+RBDAAPConnection * rb_daap_connection_new(const gchar *host,gint port,RhythmDB *db,RhythmDBEntryType type);
+gchar * rb_daap_connection_get_headers(RBDAAPConnection *connection,const gchar *uri,guint64 bytes);
+GSList * rb_daap_connection_get_playlists(RBDAAPConnection *connection);
+void rb_daap_connection_destroy(RBDAAPConnection *connection);
+
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_CONNECTION_H */
+
diff -x CVS -x cvs -urN rhythmbox/sources/rb-daap-playlist-source.c myrhythmbox/sources/rb-daap-playlist-source.c
--- rhythmbox/sources/rb-daap-playlist-source.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/sources/rb-daap-playlist-source.c	2005-08-08 05:18:10.000000000 -0400
@@ -0,0 +1,167 @@
+/*
+ *  Implementation of DAAP (iTunes Music Sharing) playlist source object
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkicontheme.h>
+#include <string.h>
+#include "rhythmdb.h"
+#include "rb-shell.h"
+#include <libgnome/gnome-i18n.h>
+#include "eel-gconf-extensions.h"
+#include "rb-daap-playlist-source.h"
+#include "rb-stock-icons.h"
+#include "rb-debug.h"
+#include "rb-util.h"
+
+#include "rb-daap-connection.h"
+
+enum {
+	PROP_0,
+	PROP_PLAYLIST
+};
+
+static void rb_daap_playlist_source_init (RBDAAPPlaylistSource *source);
+static void rb_daap_playlist_source_finalize (GObject *object);
+static void rb_daap_playlist_source_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); 
+static void rb_daap_playlist_source_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void rb_daap_playlist_source_class_init (RBDAAPPlaylistSourceClass *klass);
+
+struct RBDAAPPlaylistSourcePrivate
+{
+	RBDAAPPlaylist *playlist;
+};
+
+GType rb_daap_playlist_source_get_type (void)
+{
+	static GType rb_daap_playlist_source_type = 0;
+
+	if (rb_daap_playlist_source_type == 0)
+	{
+		static const GTypeInfo our_info =
+		{
+			sizeof (RBDAAPPlaylistSourceClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) rb_daap_playlist_source_class_init,
+			NULL,
+			NULL,
+			sizeof (RBDAAPPlaylistSource),
+			0,
+			(GInstanceInitFunc) rb_daap_playlist_source_init
+		};
+
+		rb_daap_playlist_source_type = g_type_register_static (RB_TYPE_PLAYLIST_SOURCE,
+							      "RBDAAPPlaylistSource",
+							      &our_info, 0);
+
+	}
+
+	return rb_daap_playlist_source_type;
+}
+
+static void rb_daap_playlist_source_class_init (RBDAAPPlaylistSourceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+
+	object_class->set_property = rb_daap_playlist_source_set_property;
+	object_class->get_property = rb_daap_playlist_source_get_property;	
+	object_class->finalize = rb_daap_playlist_source_finalize;
+
+	source_class->impl_can_rename = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
+
+	g_object_class_install_property (object_class,
+					 PROP_PLAYLIST,
+					 g_param_spec_pointer ("playlist",
+						 	       "RBDAAPPlaylist",
+							       "RBDAAPPlaylist structure",
+							       G_PARAM_READWRITE));
+
+	return;
+}
+
+static void rb_daap_playlist_source_init (RBDAAPPlaylistSource *source)
+{
+	source->priv = g_new0 (RBDAAPPlaylistSourcePrivate, 1);
+
+	return;
+}
+
+static void rb_daap_playlist_source_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	RBDAAPPlaylistSource *source = RB_DAAP_PLAYLIST_SOURCE (object);
+
+	switch (prop_id) {
+		case PROP_PLAYLIST:
+		{
+			GList *l;
+			
+			source->priv->playlist = g_value_get_pointer (value);
+			
+			for (l = source->priv->playlist->uris; l; l = l->next) {
+				const gchar *uri = (const gchar *)l->data;
+
+				rb_playlist_source_add_location(RB_PLAYLIST_SOURCE(source),uri);
+			}
+			
+			break;
+		}
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+
+	return;
+}
+			
+static void rb_daap_playlist_source_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	RBDAAPPlaylistSource *source = RB_DAAP_PLAYLIST_SOURCE (object);
+
+	switch (prop_id) {
+		case PROP_PLAYLIST:
+			g_value_set_pointer (value, source->priv->playlist);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+
+	return;
+}
+	
+static void rb_daap_playlist_source_finalize (GObject *object)
+{
+	RBDAAPPlaylistSource *source = RB_DAAP_PLAYLIST_SOURCE (object);
+
+	if (source->priv) {
+	
+		g_free (source->priv);
+		source->priv = NULL;
+	}
+
+	return;
+}
diff -x CVS -x cvs -urN rhythmbox/sources/rb-daap-playlist-source.h myrhythmbox/sources/rb-daap-playlist-source.h
--- rhythmbox/sources/rb-daap-playlist-source.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/sources/rb-daap-playlist-source.h	2005-08-08 05:18:10.000000000 -0400
@@ -0,0 +1,54 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) playlist source object
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_PLAYLIST_SOURCE_H
+#define __RB_DAAP_PLAYLIST_SOURCE_H
+
+#include "rb-playlist-source.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_DAAP_PLAYLIST_SOURCE         (rb_daap_playlist_source_get_type ())
+#define RB_DAAP_PLAYLIST_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSource))
+#define RB_DAAP_PLAYLIST_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSourceClass))
+#define RB_IS_DAAP_PLAYLIST_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE))
+#define RB_IS_DAAP_PLAYLIST_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DAAP_PLAYLIST_SOURCE))
+#define RB_DAAP_PLAYLIST_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSourceClass))
+
+typedef struct RBDAAPPlaylistSourcePrivate RBDAAPPlaylistSourcePrivate;
+
+typedef struct
+{
+	RBPlaylistSource parent;
+
+	RBDAAPPlaylistSourcePrivate *priv;
+} RBDAAPPlaylistSource;
+
+typedef struct
+{
+	RBPlaylistSourceClass parent;
+} RBDAAPPlaylistSourceClass;
+
+GType		rb_daap_playlist_source_get_type	        (void);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_PLAYLIST_SOURCE_H */
diff -x CVS -x cvs -urN rhythmbox/sources/rb-daap-source.c myrhythmbox/sources/rb-daap-source.c
--- rhythmbox/sources/rb-daap-source.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/sources/rb-daap-source.c	2005-08-08 05:18:12.000000000 -0400
@@ -0,0 +1,535 @@
+/*
+ *  Implementation of DAAP (iTunes Music Sharing) source object
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkicontheme.h>
+#include <string.h>
+#include "rhythmdb.h"
+#include "rb-shell.h"
+#include <libgnome/gnome-i18n.h>
+#include "eel-gconf-extensions.h"
+#include "rb-daap-source.h"
+#include "rb-stock-icons.h"
+#include "rb-debug.h"
+#include "rb-util.h"
+#include "rb-dialog.h"
+#include <libgnomevfs/gnome-vfs-dns-sd.h>
+
+#include "rb-daap-connection.h"
+#include "rb-daap-playlist-source.h"
+
+#define CONF_ENABLE_BROWSING "/apps/rhythmbox/sharing/enable_browsing"
+#define CONF_ENABLE_SHARING "/apps/rhythmbox/sharing/enable_sharing"
+#define CONF_SHARE_NAME "/apps/rhythmbox/sharing/share_name"
+
+static void rb_daap_source_init (RBDAAPSource *source);
+static void rb_daap_source_finalize (GObject *object);
+static void rb_daap_source_class_init (RBDAAPSourceClass *klass);
+static void rb_daap_source_activate (RBSource *source);
+static gboolean rb_daap_source_disconnect (RBSource *source);
+static gboolean rb_daap_source_show_popup (RBSource *source);
+
+struct RBDAAPSourcePrivate
+{
+	gboolean resolved;
+	gchar *host;
+	gint port;
+
+	RBDAAPConnection *connection;
+	GSList *playlist_sources;
+
+	gboolean connected;
+};
+
+
+static RhythmDBEntryType rhythmdb_entry_daap_type_new (void) 
+{
+	return rhythmdb_entry_register_type ();
+}
+
+GType
+rb_daap_source_get_type (void)
+{
+	static GType rb_daap_source_type = 0;
+
+	if (rb_daap_source_type == 0) {
+		static const GTypeInfo our_info = {
+			sizeof (RBDAAPSourceClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) rb_daap_source_class_init,
+			NULL,
+			NULL,
+			sizeof (RBDAAPSource),
+			0,
+			(GInstanceInitFunc) rb_daap_source_init
+		};
+
+		rb_daap_source_type = g_type_register_static (RB_TYPE_LIBRARY_SOURCE,
+							      "RBDAAPSource",
+							      &our_info, 0);
+
+	}
+
+	return rb_daap_source_type;
+}
+
+static void
+rb_daap_source_class_init (RBDAAPSourceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+	
+	object_class->finalize = rb_daap_source_finalize;
+
+	source_class->impl_activate = rb_daap_source_activate;
+	source_class->impl_disconnect = rb_daap_source_disconnect;
+	source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_paste = NULL;
+	source_class->impl_receive_drag = NULL;
+	source_class->impl_delete = NULL;
+	source_class->impl_receive_drag = NULL;
+	source_class->impl_show_popup = rb_daap_source_show_popup;
+	source_class->impl_get_config_widget = NULL;
+	
+	return;
+}
+
+static void
+rb_daap_source_init (RBDAAPSource *source)
+{
+	source->priv = g_new0 (RBDAAPSourcePrivate, 1);
+
+	return;
+}
+
+
+static void 
+rb_daap_source_finalize (GObject *object)
+{
+	RBDAAPSource *source = RB_DAAP_SOURCE (object);
+
+	rb_daap_source_disconnect (RB_SOURCE (source));
+	
+	if (source->priv) {
+		g_free (source->priv->host);
+	
+		g_free (source->priv);
+		source->priv = NULL;
+	}
+
+	return;
+}
+
+static GdkPixbuf *
+rb_daap_get_icon (void)
+{
+	GdkPixbuf *icon;
+	GtkIconTheme *theme;
+
+	theme = gtk_icon_theme_get_default ();
+	icon = gtk_icon_theme_load_icon (theme, "gnome-fs-network", 24, 0, NULL);
+
+	return icon;
+}
+
+static RBSource *
+rb_daap_source_new (RBShell *shell,const gchar *name)
+{
+	RBSource *source;
+	RhythmDBEntryType type;
+	gchar *internal_name;
+	
+	type = rhythmdb_entry_daap_type_new ();
+	internal_name = g_strconcat ("<daap-", name, ">", NULL);
+
+	source = RB_SOURCE (g_object_new (RB_TYPE_DAAP_SOURCE,
+					  "name", name,
+					  "entry-type", type,
+					  "internal-name", internal_name,
+					  "icon", rb_daap_get_icon (),
+					  "shell", shell,
+					  "visibility", TRUE,
+					  NULL));
+
+	rb_shell_register_entry_type_for_source (shell, source, 
+						 type);
+
+	g_free (internal_name);
+	internal_name = NULL;
+
+	return source;
+}
+
+static GnomeVFSDNSSDBrowseHandle *browse_handle = NULL;
+static GSList *sources = NULL;
+
+static RBSource * find_source_by_name(const gchar *name)
+{
+	GSList *l;
+	RBSource *source = NULL;
+
+	for (l = sources; l; l = l->next) {
+		gchar *s;
+		
+		source = l->data;
+		g_object_get (G_OBJECT (source), "name", &s, NULL);
+		
+		if (strcmp (name, s) == 0) {
+			g_free (s);
+			return source;
+		}
+
+		g_free (s);
+	}
+
+	return NULL;
+}
+
+
+static void dns_sd_browse_cb (GnomeVFSDNSSDBrowseHandle *handle,
+			      GnomeVFSDNSSDServiceStatus status,
+			      const GnomeVFSDNSSDService *service,
+			      RBShell *shell)
+{
+	if (status == GNOME_VFS_DNS_SD_SERVICE_ADDED) { /* new daap server */
+		RBSource *source = find_source_by_name(service->name);
+
+		if (source) {
+			g_warning("Got duplicate!?\n");
+			return;
+		}
+
+		if (eel_gconf_get_boolean (CONF_ENABLE_SHARING)) {
+			gchar *name;
+
+			name = eel_gconf_get_string (CONF_SHARE_NAME);
+
+			if (strcmp (name, service->name) == 0) {
+				return;
+			}
+		}
+		
+		source = rb_daap_source_new (shell, service->name);
+
+		sources = g_slist_prepend (sources, source);
+
+		rb_shell_append_source (shell, source, NULL);
+
+	} else if (status == GNOME_VFS_DNS_SD_SERVICE_REMOVED) { /* daap server went away */
+		RBSource *source = find_source_by_name(service->name);
+
+		if (source == NULL) {
+			/* if this happens, its almost always because the user
+			 * turned sharing off in the preferences, and we've 
+			 * been notified that our own daap server has gone away
+			 * fancy that.
+			 *
+			 * probably no need for any error messages
+			 */
+//			g_warning ("notification of removed daap source that does not exist in rhythmbox");
+			return;
+		}
+		
+		sources = g_slist_remove (sources, source);
+		
+		rb_daap_source_disconnect (source);
+		rb_source_delete_thyself (source);
+		/* unref is done via gtk in rb_shell_source_delete_cb at
+		 * gtk_notebook_remove_page */
+	}
+	
+	return;
+}
+
+static void start_browsing (RBShell *shell)
+{
+	GnomeVFSResult result;
+		
+	result = gnome_vfs_dns_sd_browse (&browse_handle, 
+					  "local", 
+					  "_daap._tcp", 
+					  (GnomeVFSDNSSDBrowseCallback)dns_sd_browse_cb,shell,
+					  NULL);
+
+	if (result != GNOME_VFS_OK) {
+		g_warning ("Could not start listening for DAAP hosts");
+		return;
+	}
+
+	return;
+}
+
+static void stop_browsing (RBShell *shell)
+{
+	GnomeVFSResult result;
+	GSList *l;
+	
+	for (l = sources; l; l = l->next) {
+		RBSource *source = l->data;
+
+		rb_daap_source_disconnect (source);
+		rb_source_delete_thyself (source);
+	}
+	
+	sources = NULL;
+	
+	result = gnome_vfs_dns_sd_stop_browse(browse_handle);
+
+	if (result != GNOME_VFS_OK) {
+		g_warning ("Unable to shutdown listening for DAAPHosts");
+	}
+
+	browse_handle = NULL;
+
+	return;
+}
+
+static void enable_browsing_changed_cb (GConfClient *client,
+				       guint cnxn_id,
+				       GConfEntry *entry,
+				       RBShell *shell)
+{
+	gboolean enabled;
+
+	enabled = eel_gconf_get_boolean (CONF_ENABLE_BROWSING);
+
+	if (enabled) {
+		start_browsing (shell);
+	} else {
+		stop_browsing (shell);
+	}
+
+	return;
+}
+
+RBSource * rb_daap_sources_init (RBShell *shell)
+{
+	GnomeVFSResult result;
+	gboolean enabled;
+
+	g_object_ref (shell);
+	
+	enabled = eel_gconf_get_boolean (CONF_ENABLE_BROWSING);
+	
+	if (enabled) {
+		start_browsing (shell);
+	}
+
+	eel_gconf_notification_add (CONF_ENABLE_BROWSING,
+				    (GConfClientNotifyFunc) enable_browsing_changed_cb,
+				    shell);
+
+	return NULL;
+}	
+
+void rb_daap_sources_shutdown (RBShell *shell)
+{
+	g_object_unref (shell);
+	
+	if (browse_handle) {
+		stop_browsing (shell);
+	}
+	
+	return;
+}
+
+static void rb_daap_source_activate (RBSource *source)
+{
+	RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
+	RBShell *shell;
+	RhythmDB *db;
+	RhythmDBEntryType type;
+	gchar *name;
+	
+	g_object_get (G_OBJECT (source), "name", &name, NULL);
+	
+	if (daap_source->priv->resolved == FALSE) {
+		GnomeVFSResult result;
+	
+		result = gnome_vfs_dns_sd_resolve_sync (name,
+						       "_daap._tcp",
+						       "local",
+						       20,
+						       &(daap_source->priv->host),
+						       &(daap_source->priv->port),
+						       NULL,
+						       NULL,
+						       NULL);
+
+		if (result == GNOME_VFS_OK) {
+			daap_source->priv->resolved = TRUE;
+		} else {
+			rb_error_dialog (NULL,_("Error finding host"),_("Could not find %s on the network"),name);
+			g_free (name);
+			
+			return;
+		}
+
+	}
+
+	if (daap_source->priv->connection == NULL) {
+		GSList *playlists;
+		GSList *l;
+		
+		g_object_get (G_OBJECT (daap_source), "shell", &shell, "entry-type", &type, NULL);
+		g_object_get (G_OBJECT (shell), "db", &db, NULL);
+
+		daap_source->priv->connection = rb_daap_connection_new (daap_source->priv->host, daap_source->priv->port, db, type);
+		playlists = rb_daap_connection_get_playlists(daap_source->priv->connection);
+		for (l = playlists; l; l = l->next) {
+			RBDAAPPlaylist *playlist = l->data;
+			RBSource *playlist_source;
+			gchar *internal_name;
+	
+			internal_name = g_strconcat ("<daap-", name, "-",playlist->name,">", NULL);
+
+			playlist_source = RB_SOURCE (g_object_new (RB_TYPE_DAAP_PLAYLIST_SOURCE,
+					  "name", playlist->name,
+					  "internal-name", internal_name,
+					  "shell", shell,
+					  "visibility", TRUE,
+					  NULL));
+/* this is set here instead of in construction so that 
+ * rb_playlist_source_constructor has a chance to be run to set things up */
+			g_object_set (G_OBJECT (playlist_source), "playlist", playlist, NULL); 
+			g_free (internal_name);
+			internal_name = NULL;
+
+			rb_shell_append_source (shell, playlist_source, source);
+			daap_source->priv->playlist_sources = g_slist_prepend (daap_source->priv->playlist_sources, playlist_source);
+		}
+
+		g_object_unref (G_OBJECT (db));
+		g_object_unref (G_OBJECT (shell));
+
+		daap_source->priv->connected = TRUE;
+	}
+	
+	g_free(name);
+	
+	return;
+}
+
+static gboolean rb_daap_source_disconnect (RBSource *source)
+{
+	RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
+
+	if (daap_source->priv->connected) {
+		GSList *l;
+		RBShell *shell;
+		RhythmDB *db;
+		RhythmDBEntryType type;
+
+		g_object_get (G_OBJECT (source), "shell", &shell, "entry-type", &type, NULL);
+		g_object_get (G_OBJECT (shell), "db", &db, NULL);
+		rhythmdb_entry_delete_by_type (db, type);
+		rhythmdb_commit (db);
+		g_object_unref (G_OBJECT (db));
+		g_object_unref (G_OBJECT (shell));
+
+		for (l = daap_source->priv->playlist_sources; l; l = l->next) {
+			RBSource *playlist_source = RB_SOURCE (l->data);
+	
+			rb_source_delete_thyself (playlist_source);	
+		}
+		
+		g_slist_free(daap_source->priv->playlist_sources);
+		daap_source->priv->playlist_sources = NULL;
+	
+		if (daap_source->priv->connection) {
+			rb_daap_connection_destroy (daap_source->priv->connection);
+			daap_source->priv->connection = NULL;
+		}
+
+		daap_source->priv->connected = FALSE;
+	}
+	
+	return TRUE;
+}
+
+static gboolean rb_daap_source_show_popup (RBSource *source)
+{
+	_rb_source_show_popup (RB_SOURCE (source), "/DAAPSourcePopup");
+	return TRUE;
+}	
+
+RBDAAPSource * rb_daap_source_find_for_uri (const gchar *uri)
+{
+	GSList *l;
+	gchar *ip;
+	gchar *s;
+	RBDAAPSource *found_source = NULL;
+	
+	ip = strdup (uri + 7); // daap://
+	s = strchr (ip, ':');
+	*s = '\0';
+
+	for (l = sources; l; l = l->next) {
+		RBDAAPSource *source = l->data;
+
+		if (source->priv->resolved == TRUE && strcmp (ip, source->priv->host) == 0) {
+			found_source = source;
+
+			break;
+		}
+
+	}
+
+	g_free (ip);
+	
+	return found_source;
+}
+
+gchar *	rb_daap_source_get_headers (RBDAAPSource *source, const gchar *uri, glong time)
+{
+	guint64 bytes = 0;
+
+	if (time != 0) {
+		RhythmDB *db;
+		RBShell *shell;
+		RhythmDBEntry *entry;
+		gulong bitrate;
+		
+		g_object_get (G_OBJECT (source), "shell", &shell, NULL);
+		g_object_get (G_OBJECT (shell), "db", &db, NULL);
+
+		entry = rhythmdb_entry_lookup_by_location (db, uri);
+
+		g_object_unref (G_OBJECT (shell));
+		g_object_unref (G_OBJECT (db));
+		
+		bitrate = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE); 
+		// bitrate is kilobites per second
+		// a kilobit is 128 bytes
+		bytes = (time * bitrate) * 128; 
+	}
+	
+	return rb_daap_connection_get_headers (source->priv->connection, uri, bytes);
+}
+
+
+
diff -x CVS -x cvs -urN rhythmbox/sources/rb-daap-source.h myrhythmbox/sources/rb-daap-source.h
--- rhythmbox/sources/rb-daap-source.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/sources/rb-daap-source.h	2005-08-08 05:18:12.000000000 -0400
@@ -0,0 +1,63 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) source object
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_SOURCE_H
+#define __RB_DAAP_SOURCE_H
+
+#include "rb-shell.h"
+#include "rb-library-source.h"
+#include "rhythmdb.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_DAAP_SOURCE         (rb_daap_source_get_type ())
+#define RB_DAAP_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DAAP_SOURCE, RBDAAPSource))
+#define RB_DAAP_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DAAP_SOURCE, RBDAAPSourceClass))
+#define RB_IS_DAAP_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DAAP_SOURCE))
+#define RB_IS_DAAP_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DAAP_SOURCE))
+#define RB_DAAP_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DAAP_SOURCE, RBDAAPSourceClass))
+
+typedef struct RBDAAPSourcePrivate RBDAAPSourcePrivate;
+
+typedef struct {
+	RBLibrarySource parent;
+
+	RBDAAPSourcePrivate *priv;
+} RBDAAPSource;
+
+typedef struct {
+	RBLibrarySourceClass parent;
+} RBDAAPSourceClass;
+
+RBSource *	rb_daap_sources_init		(RBShell *shell);
+void 		rb_daap_sources_shutdown 	(RBShell *shell);
+
+GType		rb_daap_source_get_type	        (void);
+
+RBDAAPSource *	rb_daap_source_find_for_uri	(const gchar *uri);
+
+gchar *		rb_daap_source_get_headers	(RBDAAPSource *source, 
+						 const gchar *uri, 
+						 glong time);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_SOURCE_H */
diff -x CVS -x cvs -urN rhythmbox/sources/rb-library-source.c myrhythmbox/sources/rb-library-source.c
--- rhythmbox/sources/rb-library-source.c	2005-07-23 08:41:07.000000000 -0400
+++ myrhythmbox/sources/rb-library-source.c	2005-08-08 05:18:06.000000000 -0400
@@ -617,21 +617,10 @@
 	rb_library_source_state_prefs_sync (source);
 	rb_library_source_ui_prefs_sync (source);
 	update_browser_views_visibility (source);
-	source->priv->ui_dir_notify_id =
-		eel_gconf_notification_add (CONF_STATE_LIBRARY_DIR,
-				    (GConfClientNotifyFunc) rb_library_source_state_pref_changed,
-				    source);
-	source->priv->state_dir_notify_id =
-		eel_gconf_notification_add (CONF_UI_LIBRARY_DIR,
-				    (GConfClientNotifyFunc) rb_library_source_ui_pref_changed, source);
-	source->priv->browser_view_notify_id =
-		eel_gconf_notification_add (CONF_UI_LIBRARY_BROWSER_VIEWS,
-				    (GConfClientNotifyFunc) rb_library_source_browser_views_changed, source);
 	rb_library_source_do_query (source, RB_LIBRARY_QUERY_TYPE_ALL);
 	return G_OBJECT (source);
 }
 
-
 static void
 rb_library_source_set_property (GObject *object,
 			      guint prop_id,
@@ -704,6 +693,22 @@
 	return source;
 }
 
+void
+rb_library_source_setup_prefs (RBLibrarySource *source)
+{
+	source->priv->ui_dir_notify_id =
+		eel_gconf_notification_add (CONF_STATE_LIBRARY_DIR,
+				    (GConfClientNotifyFunc) rb_library_source_state_pref_changed,
+				    source);
+	source->priv->state_dir_notify_id =
+		eel_gconf_notification_add (CONF_UI_LIBRARY_DIR,
+				    (GConfClientNotifyFunc) rb_library_source_ui_pref_changed, source);
+	source->priv->browser_view_notify_id =
+		eel_gconf_notification_add (CONF_UI_LIBRARY_BROWSER_VIEWS,
+				    (GConfClientNotifyFunc) rb_library_source_browser_views_changed, source);
+	return;
+}
+
 
 static gboolean
 string_list_equal (GList *a, GList *b)
diff -x CVS -x cvs -urN rhythmbox/sources/rb-library-source.h myrhythmbox/sources/rb-library-source.h
--- rhythmbox/sources/rb-library-source.h	2004-09-10 01:12:59.000000000 -0400
+++ myrhythmbox/sources/rb-library-source.h	2005-08-08 05:18:06.000000000 -0400
@@ -56,6 +56,8 @@
 
 RBSource *      rb_library_source_new           (RBShell *shell);
 
+void		rb_library_source_setup_prefs	(RBLibrarySource *source);
+
 void		rb_library_source_show_browser	(RBLibrarySource *source,
 						 gboolean show);
 
diff -x CVS -x cvs -urN rhythmbox/sources/rb-source.c myrhythmbox/sources/rb-source.c
--- rhythmbox/sources/rb-source.c	2005-07-07 10:30:20.000000000 -0400
+++ myrhythmbox/sources/rb-source.c	2005-08-08 05:18:13.000000000 -0400
@@ -59,6 +59,9 @@
 static gboolean default_receive_drag  (RBSource *source, GtkSelectionData *data);
 static gboolean default_show_popup  (RBSource *source);
 static void default_delete_thyself (RBSource *source);
+static void default_activate (RBSource *source);
+static void default_deactivate (RBSource *source);
+static gboolean default_disconnect (RBSource *source);
 
 struct RBSourcePrivate
 {
@@ -147,6 +150,9 @@
 	klass->impl_receive_drag = default_receive_drag;
 	klass->impl_show_popup = default_show_popup;
 	klass->impl_delete_thyself = default_delete_thyself;
+	klass->impl_activate = default_activate;
+	klass->impl_deactivate = default_deactivate;
+	klass->impl_disconnect = default_disconnect;
 
 	g_object_class_install_property (object_class,
 					 PROP_NAME,
@@ -482,7 +488,11 @@
 {
 	RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
 
-	return klass->impl_get_config_widget (source);
+	if (klass->impl_get_config_widget) {
+		return klass->impl_get_config_widget (source);
+	} else {
+		return NULL;
+	}
 }
 
 static gboolean
@@ -692,3 +702,44 @@
 	klass->impl_delete_thyself (source);
 	g_signal_emit (G_OBJECT (source), rb_source_signals[DELETED], 0);
 }
+
+static void default_activate (RBSource *source)
+{
+	return;
+}
+
+static void default_deactivate (RBSource *source)
+{
+	return;
+}
+
+static gboolean default_disconnect (RBSource *source)
+{
+	return TRUE;
+}
+
+void rb_source_activate (RBSource *source)
+{
+	RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
+
+	klass->impl_activate (source);
+
+	return;
+}
+
+void rb_source_deactivate (RBSource *source)
+{
+	RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
+
+	klass->impl_deactivate (source);
+
+	return;
+}
+
+gboolean rb_source_disconnect (RBSource *source)
+{
+	RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
+
+	return klass->impl_disconnect (source);
+}
+
diff -x CVS -x cvs -urN rhythmbox/sources/rb-source.h myrhythmbox/sources/rb-source.h
--- rhythmbox/sources/rb-source.h	2005-06-12 12:02:15.000000000 -0400
+++ myrhythmbox/sources/rb-source.h	2005-08-08 05:18:13.000000000 -0400
@@ -60,7 +60,7 @@
 	void (*status_changed)	(RBSource *source);
 	void (*filter_changed)	(RBSource *source);
 	void (*deleted)		(RBSource *source);
-
+	
 	/* methods */
 	char *	        (*impl_get_status)	(RBSource *source);
 
@@ -102,6 +102,9 @@
 	gboolean	(*impl_show_popup)	(RBSource *source);
 				   
 	void		(*impl_delete_thyself)	(RBSource *source);
+	void		(*impl_activate)	(RBSource *source);
+	void		(*impl_deactivate)	(RBSource *source);
+	gboolean	(*impl_disconnect)	(RBSource *source);
 } RBSourceClass;
 
 typedef gboolean (*RBSourceFeatureFunc) (RBSource *source);
@@ -163,6 +166,10 @@
 
 void		rb_source_delete_thyself	(RBSource *source);
 
+void		rb_source_activate		(RBSource *source);
+void		rb_source_deactivate		(RBSource *source);
+gboolean	rb_source_disconnect		(RBSource *source);
+
 /* Protected method, should only be used by objects inheriting from RBSource */
 void            _rb_source_show_popup           (RBSource *source, 
 						 const char *ui_path);
diff -x CVS -x cvs -urN rhythmbox/sources/rb-sourcelist.c myrhythmbox/sources/rb-sourcelist.c
--- rhythmbox/sources/rb-sourcelist.c	2005-07-28 23:53:43.000000000 -0400
+++ myrhythmbox/sources/rb-sourcelist.c	2005-08-08 05:18:07.000000000 -0400
@@ -79,6 +79,12 @@
 					guint prop_id,
 					GValue *value,
 					GParamSpec *pspec);
+static gboolean rb_sourcelist_source_to_iter (RBSourceList *sourcelist, 
+					      RBSource *source,
+					      GtkTreeIter *iter);
+static gboolean rb_sourcelist_visible_source_to_iter (RBSourceList *sourcelist, 
+						      RBSource *source,
+						      GtkTreeIter *iter);
 static void rb_sourcelist_selection_changed_cb (GtkTreeSelection *selection,
 						RBSourceList *sourcelist);
 static void drop_received_cb (RBSourceListModel *model, RBSource *target, GtkTreeViewDropPosition pos,
@@ -355,20 +361,27 @@
 
 void
 rb_sourcelist_append (RBSourceList *sourcelist,
-		      RBSource *source)
+		      RBSource *source,
+		      RBSource *parent)
 {
 	GtkTreeIter iter;
 	PangoAttrList *attrs = pango_attr_list_new ();
 	const char *name;
-
+	
 	g_return_if_fail (RB_IS_SOURCELIST (sourcelist));
 	g_return_if_fail (RB_IS_SOURCE (source));
 
 	g_object_get (G_OBJECT (source), "name", &name, NULL);
 
-	gtk_list_store_append (GTK_LIST_STORE (sourcelist->priv->real_model), &iter);
+	if (parent) {
+		GtkTreeIter parent_iter;
+		g_assert (rb_sourcelist_source_to_iter (sourcelist, parent, &parent_iter));
+		gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), &iter, &parent_iter);
+	} else {
+		gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), &iter, NULL);
+	}
 
-	gtk_list_store_set (GTK_LIST_STORE (sourcelist->priv->real_model), &iter,
+	gtk_tree_store_set (GTK_TREE_STORE (sourcelist->priv->real_model), &iter,
 			    RB_SOURCELIST_MODEL_COLUMN_PIXBUF, rb_source_get_pixbuf (source),
 			    RB_SOURCELIST_MODEL_COLUMN_NAME, name,
 			    RB_SOURCELIST_MODEL_COLUMN_SOURCE, source,
@@ -380,39 +393,70 @@
 
 }
 
+typedef struct _SourcePath {
+	RBSource *source;
+	GtkTreePath *path;
+} SourcePath;
+
+static gboolean
+match_source_to_iter (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+		      SourcePath *sp)
+{
+	RBSource *target = NULL;
+	
+	gtk_tree_model_get (model, iter, RB_SOURCELIST_MODEL_COLUMN_SOURCE, &target, -1);
+	if (target == sp->source) {
+		sp->path = gtk_tree_path_copy(path);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
 static gboolean
 rb_sourcelist_source_to_iter (RBSourceList *sourcelist, RBSource *source,
 			      GtkTreeIter *iter)
 {
-	if (gtk_tree_model_get_iter_first (sourcelist->priv->real_model, iter) == FALSE) {
-		return FALSE;
+	SourcePath *sp = g_new0(SourcePath,1);
+	gboolean ret = FALSE;
+	
+	sp->source = source;
+	
+	gtk_tree_model_foreach (sourcelist->priv->real_model, (GtkTreeModelForeachFunc) match_source_to_iter, sp);
+	
+	if (sp->path) {
+		gtk_tree_model_get_iter (sourcelist->priv->real_model, iter, sp->path);
+		ret = TRUE;
 	}
 
-	do {
-		RBSource *target = NULL;
-		gtk_tree_model_get (sourcelist->priv->real_model, iter,
-				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &target, -1);
-		if (source == target)
-			return TRUE;
-	} while (gtk_tree_model_iter_next (sourcelist->priv->real_model, iter));
-	return FALSE;
+	gtk_tree_path_free(sp->path);
+	g_free(sp);
+	sp = NULL;
+
+	return ret;
 }
 
 static gboolean
 rb_sourcelist_visible_source_to_iter (RBSourceList *sourcelist, RBSource *source,
 				      GtkTreeIter *iter)
 {
-	if (gtk_tree_model_get_iter_first (sourcelist->priv->filter_model, iter) == FALSE) {
-		return FALSE;
+	SourcePath *sp = g_new0(SourcePath,1);
+	gboolean ret = FALSE;
+	
+	sp->source = source;
+	
+	gtk_tree_model_foreach (sourcelist->priv->filter_model, (GtkTreeModelForeachFunc) match_source_to_iter, sp);
+
+	if (sp->path) {
+		gtk_tree_model_get_iter (sourcelist->priv->filter_model, iter, sp->path);
+		ret = TRUE;
 	}
-	do {
-		RBSource *target = NULL;
-		gtk_tree_model_get (sourcelist->priv->filter_model, iter,
-				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &target, -1);
-		if (source == target)
-			return TRUE;
-	} while (gtk_tree_model_iter_next (sourcelist->priv->filter_model, iter));
-	return FALSE;
+
+	gtk_tree_path_free(sp->path);
+	g_free(sp);
+	sp = NULL;
+
+	return ret;
 }
 
 void
@@ -446,13 +490,13 @@
 		g_assert (rb_sourcelist_source_to_iter (sourcelist,
 							sourcelist->priv->playing_source,
 							&iter));
-		gtk_list_store_set (GTK_LIST_STORE (sourcelist->priv->real_model), &iter,
+		gtk_tree_store_set (GTK_TREE_STORE (sourcelist->priv->real_model), &iter,
 				    RB_SOURCELIST_MODEL_COLUMN_PLAYING, FALSE, -1);
 	}
 	sourcelist->priv->playing_source = source;
 	if (source) {
 		g_assert (rb_sourcelist_source_to_iter (sourcelist, source, &iter));
-		gtk_list_store_set (GTK_LIST_STORE (sourcelist->priv->real_model), &iter,
+		gtk_tree_store_set (GTK_TREE_STORE (sourcelist->priv->real_model), &iter,
 				    RB_SOURCELIST_MODEL_COLUMN_PLAYING, TRUE, -1);
 	}
 }
@@ -463,7 +507,7 @@
 	GtkTreeIter iter;
 
 	g_assert (rb_sourcelist_source_to_iter (sourcelist, source, &iter));
-	gtk_list_store_remove (GTK_LIST_STORE (sourcelist->priv->real_model), &iter);
+	gtk_tree_store_remove (GTK_TREE_STORE (sourcelist->priv->real_model), &iter);
 	g_signal_handlers_disconnect_by_func (G_OBJECT (source),
 					      G_CALLBACK (name_notify_cb), sourcelist);
         g_signal_handlers_disconnect_by_func (G_OBJECT (source),
@@ -631,7 +675,7 @@
 	
 	if (rb_sourcelist_source_to_iter (sourcelist, source, &iter)) {
 		g_object_get (obj, "name", &name, NULL);
-		gtk_list_store_set (GTK_LIST_STORE (sourcelist->priv->real_model),
+		gtk_tree_store_set (GTK_TREE_STORE (sourcelist->priv->real_model),
 				    &iter,
 				    RB_SOURCELIST_MODEL_COLUMN_NAME, name, -1);
 	}
diff -x CVS -x cvs -urN rhythmbox/sources/rb-sourcelist.h myrhythmbox/sources/rb-sourcelist.h
--- rhythmbox/sources/rb-sourcelist.h	2005-07-26 10:07:29.000000000 -0400
+++ myrhythmbox/sources/rb-sourcelist.h	2005-08-08 05:18:07.000000000 -0400
@@ -65,7 +65,8 @@
 GtkWidget *	rb_sourcelist_new		(RBShell *shell);
 
 void		rb_sourcelist_append		(RBSourceList *sourcelist,
-						 RBSource *source);
+						 RBSource *source,
+						 RBSource *parent);
 
 void		rb_sourcelist_set_playing_source(RBSourceList *sourcelist,
 						 RBSource *source);
diff -x CVS -x cvs -urN rhythmbox/sources/rb-sourcelist-model.c myrhythmbox/sources/rb-sourcelist-model.c
--- rhythmbox/sources/rb-sourcelist-model.c	2005-06-04 13:57:08.000000000 -0400
+++ myrhythmbox/sources/rb-sourcelist-model.c	2005-08-08 05:18:08.000000000 -0400
@@ -210,7 +210,7 @@
 rb_sourcelist_model_new (void)
 {
 	RBSourceListModel *model;
-	GtkListStore *store;
+	GtkTreeStore *store;
  	GType *column_types = g_new (GType, RB_SOURCELIST_MODEL_N_COLUMNS);
 
 	column_types[RB_SOURCELIST_MODEL_COLUMN_PLAYING] = G_TYPE_BOOLEAN;
@@ -218,7 +218,7 @@
 	column_types[RB_SOURCELIST_MODEL_COLUMN_NAME] = G_TYPE_STRING;
 	column_types[RB_SOURCELIST_MODEL_COLUMN_SOURCE] = G_TYPE_POINTER;
 	column_types[RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES] = PANGO_TYPE_ATTR_LIST;
-	store = gtk_list_store_newv (RB_SOURCELIST_MODEL_N_COLUMNS, 
+	store = gtk_tree_store_newv (RB_SOURCELIST_MODEL_N_COLUMNS, 
 				     column_types);
 
  	model = RB_SOURCELIST_MODEL (g_object_new (RB_TYPE_SOURCELIST_MODEL, 
@@ -289,13 +289,13 @@
 		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model),
 					     &dest_iter, dest)) {
 			if (pos == GTK_TREE_VIEW_DROP_AFTER)
-				gtk_list_store_move_after (GTK_LIST_STORE (model),
+				gtk_tree_store_move_after (GTK_TREE_STORE (model),
 							   &iter, &dest_iter);
 			else
-				gtk_list_store_move_before (GTK_LIST_STORE (model),
+				gtk_tree_store_move_before (GTK_TREE_STORE (model),
 							    &iter, &dest_iter);
 		} else
-			gtk_list_store_move_before (GTK_LIST_STORE (model),
+			gtk_tree_store_move_before (GTK_TREE_STORE (model),
 						    &iter, NULL);
 		g_free (path_str);
 	}
@@ -323,7 +323,7 @@
 		return TRUE;
 			
 	/* Call the superclass method */
-	return gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (GTK_LIST_STORE (model)),
+	return gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (GTK_TREE_STORE (model)),
 						     dest, selection_data);
 }
 


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