[Rhythmbox-devel] music sharing patch #3



-
Yet another music sharing patch for people to play with.  This should be
mostly final (in the sense that maybe this could find its way into cvs
and be worked on from there, soon?).

Most of the daap code lives in daapsharing/ now, except for changes to
existing files, and the daap & daapplaylist sources in sources/.

The DNS-SD/Bonjour/Rendezvous isn't done via gnome-vfs anymore
(previously the daap patch used gnome-vfs [which used howl] to
browse/discovery daap hosts).  It is done directly via howl now.  I've
abstracted that part of the code out so that a drop in replacement
(avahi) should be relatively easy.

You'll need libsoup & howl to browse music and publish your own.  I'm
planning on writing in support for avahi
( http://www.freedesktop.org/Software/Avahi ) as soon as I get it
working on my machine (damn dbus).  Hopefully between the two of them
people will be able to set up mdns themselves (or, with avahi, have it
come with their distribution).

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

Issues/Known Bugs:
* Signed errors when compiling.  According to walters (i think), gcc 4.0
added the signed checking thing.  I havn't found time to find packages
for 4.0 for my system yet, or I'd try to fix this myself.  Until then, I
suggest writing me a patch or turning off the extra warning checking.
* The patch from http://bugzilla.gnome.org/show_bug.cgi?id=167364
"broke" seeking for daap streams.  You can still seek in them just fine,
but because the GStreamer playbin isn't aware that you have, it can't
report the time properly.  While I agree that the "new" way
(gst_element_query) is better, I can't see any way to get the time
reported for daap streams except to do it the "old" way.  Any thoughts?
* There isn't really any error reporting yet.
* When sharing your music, if the share name already exists on the
network, Rhythmbox won't notify you of this.
* When loading a daap server the user interface is not updated until it
the network communications are finished.  Ideally, the network stuff
should be in a separate thread, or at the very least the daap buffer
parsing stuff should be done incrementally in a timeout.
* No authentication.
* Anywhere else there's a 'FIXME'

So.  Get out there and try it, please!!

-
charlie

diff -x CVS -x cvs -urN rhythmbox/configure.ac myrhythmbox/configure.ac
--- rhythmbox/configure.ac	2005-08-10 09:35:53.000000000 -0400
+++ myrhythmbox/configure.ac	2005-08-22 23:58:27.755653266 -0400
@@ -156,6 +156,48 @@
    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)
+   PKG_CHECK_MODULES(MDNS,
+		howl,
+		have_howl=yes,
+		have_howl=no)
+   if test x"$have_howl" = xyes; then
+      AC_DEFINE(WITH_HOWL, 1, [Define if Howl should be used for mDNS])
+      have_mdns="yes"
+   else
+	PKG_CHECK_MODULES(MDNS,
+		avahi-client avahi-glib,
+		have_avahi=yes,
+		have_avahi=no)
+	if test x"$have_avahi" = xyes; then
+	   AC_DEFINE(WITH_AVAHI, 1, [Define if Avahi should be used for mDNS])
+	   have_mdns="yes"
+        fi
+   fi
+
+   enable_daap="no"
+
+   if test x"$have_libsoup" = xyes; then
+     if test x"$have_mdns" = xyes; then
+	AC_DEFINE(WITH_DAAP_SUPPORT, 1, [Define if daap should be enabled])
+	enable_daap="yes"
+     else
+	AC_MSG_ERROR([DAAP support excplicity requested, but no mDNS implentation found.  Download Howl or Avahi])
+     fi	
+   fi
+
+   AM_CONDITIONAL(USE_DAAP, test "x$enable_daap" = "xyes")
+fi
 
 dnl AC_CHECK_LIB(lirc_client, lirc_init,
 dnl 		[ AC_CHECK_HEADER(lirc/lirc_client.h,
@@ -424,6 +466,7 @@
 remote/Makefile
 remote/bonobo/Makefile
 remote/dbus/Makefile
+daapsharing/Makefile
 shell/Makefile
 data/Makefile
 data/ui/Makefile
@@ -481,6 +524,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-20 23:04:10.000000000 -0400
@@ -0,0 +1,154 @@
+/*
+ *  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-20 21:44:32.000000000 -0400
@@ -0,0 +1,38 @@
+/*
+ *  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/libdaapstructure.la myrhythmbox/daapsharing/libdaapstructure.la
--- rhythmbox/daapsharing/libdaapstructure.la	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/libdaapstructure.la	2005-08-22 17:07:03.000000000 -0400
@@ -0,0 +1,35 @@
+# libdaapstructure.la - a libtool library file
+# Generated by ltmain.sh - GNU libtool 1.5.6 (1.1220.2.95 2004/04/11 05:50:42) Debian: 224 $
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname=''
+
+# Names of this library.
+library_names=''
+
+# The name of the static archive.
+old_library='libdaapstructure.a'
+
+# Libraries that this one depends upon.
+dependency_libs=' -L/usr/X11R6/lib'
+
+# Version information for libdaapstructure.
+current=
+age=
+revision=
+
+# Is this an already installed library?
+installed=no
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=no
+
+# Files to dlopen/dlpreopen
+dlopen=''
+dlpreopen=''
+
+# Directory that this library needs to be installed in:
+libdir=''
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-22 23:07:02.762767574 -0400
@@ -0,0 +1,42 @@
+noinst_LTLIBRARIES = libdaapsharing.la
+
+libdaapsharing_la_SOURCES = 
+
+if USE_DAAP
+libdaapsharing_la_SOURCES += \
+		daap-sharing.c 	\
+		daap-sharing.h 	\
+		rb-daap-share.c	\
+		rb-daap-share.h \
+		rb-daap-structure.c \
+		rb-daap-structure.h \
+		rb-daap-mdns.c \
+		rb-daap-mdns.h \
+		rb-daap-connection.c \
+		rb-daap-connection.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)					\
+	$(MDNS_CFLAGS)
+
+libdaapsharing_la_LDFLAGS = -export-dynamic
+
diff -x CVS -x cvs -urN rhythmbox/daapsharing/rb-daap-connection.c myrhythmbox/daapsharing/rb-daap-connection.c
--- rhythmbox/daapsharing/rb-daap-connection.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/rb-daap-connection.c	2005-08-22 23:30:27.470358212 -0400
@@ -0,0 +1,1236 @@
+/*
+ *  Implementation of DAAP (iTunes Music Sharing) hashing, parsing, 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], 
+	      gint 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, 
+		  gint 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 */
+
+    return;
+}
+
+#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], 
+	      gint 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) {
+		rb_debug ("Error building message for %s", path);
+		return FALSE;
+	}
+	
+	soup_session_send_message (connection->session, message);
+	
+	if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code) == FALSE) {
+		g_object_unref (message);
+		rb_debug ("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) {
+		rb_debug ("No daap structure returned %s", path);
+		return FALSE;
+	}
+
+	item = rb_daap_structure_find_item (*structure, RB_DAAP_CC_MSTT);
+	if (item == NULL) {
+		rb_debug ("Could not find dmap.status item in %s", path);
+		return FALSE;
+	}
+
+	if (g_value_get_int (&(item->content)) != 200) {
+		rb_debug ("Error, dmap.status is not 200 in %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);
+}
+
+static gboolean
+connection_get_info (RBDAAPConnection *connection)
+{
+	GNode *structure = NULL;
+	gboolean ret;
+	RBDAAPItem *item = NULL;
+
+	/* get the daap version number */
+	ret = http_get (connection, "/server-info", FALSE, 0.0, 0, FALSE, &structure);
+	if (structure == NULL || ret == FALSE) {
+		rb_debug ("Got invalid response from /server-info");
+		ret = FALSE;
+		goto out;
+	}
+	
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_APRO);
+	if (item == NULL ){
+		rb_debug ("Could not find daap.protocolversion item in /server-info");
+		ret = FALSE;
+		goto out;
+	}
+
+	connection->daap_version = g_value_get_double (&(item->content));
+	ret = TRUE;
+
+out:
+	rb_daap_structure_destroy (structure);
+
+	return ret;
+}
+
+static gboolean
+connection_login (RBDAAPConnection *connection)
+{
+	GNode *structure = NULL;
+	gboolean ret;
+	RBDAAPItem *item = NULL;
+	gchar *path;
+
+	/* get a session id */
+	ret = http_get (connection,"/login", FALSE, 0.0, 0, FALSE, &structure);
+
+	if (structure == NULL || ret == FALSE) {
+		rb_debug ("Got invalid response from /login");
+		ret = FALSE;
+		goto out;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MLID);
+	if (item == NULL) {
+		rb_debug ("Could not find daap.sessionid item in /login");
+		ret = FALSE;
+		goto out;
+	}
+
+	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);
+
+	if (structure == NULL || ret == FALSE) {
+		rb_debug ("Got invalid response from %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUSR);
+	if (item == NULL) {
+		rb_debug ("Could not find daap.serverrevision item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	connection->revision_number = g_value_get_int (&(item->content));
+
+	ret = TRUE;
+out:
+	rb_daap_structure_destroy (structure);
+	g_free (path);
+
+	return ret;
+}
+
+static gboolean
+connection_get_database_info (RBDAAPConnection *connection)
+{
+	GNode *structure = NULL;
+	gboolean ret;
+	RBDAAPItem *item = NULL;
+	GNode *listing_node;
+	gchar *path;
+	gint n_databases = 0;
+
+	/* 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);
+	
+	if (structure == NULL || ret == FALSE) {
+		rb_debug ("Got invalid response from %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
+	if (item == NULL) {
+		rb_debug ("Could not find dmap.returnedcount item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	n_databases = g_value_get_int (&(item->content));
+	if (n_databases != 1) {
+		rb_debug ("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) {
+		rb_debug ("Could not find dmap.listing item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	item = rb_daap_structure_find_item (listing_node->children, RB_DAAP_CC_MIID);
+	if (item == NULL) {
+		rb_debug ("Could not find dmap.itemid item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+	
+	connection->database_id = g_value_get_int (&(item->content));
+
+	ret = TRUE;
+
+out:
+	rb_daap_structure_destroy (structure);
+	g_free (path);
+
+	return ret;
+}
+
+static gboolean
+connection_get_song_listing (RBDAAPConnection *connection,
+			     const gchar *host,
+			     gint port,
+			     RhythmDB *db,
+			     RhythmDBEntryType type)
+{
+	GNode *structure = NULL;
+	gboolean ret;
+	RBDAAPItem *item = NULL;
+	GNode *listing_node;
+	gchar *path;
+	gint returned_count;
+	gint i;
+	GNode *n;
+	gint specified_total_count;
+	gboolean update_type;
+
+	/* 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);
+	
+	if (structure == NULL || ret == FALSE) {
+		rb_debug ("Got invalid response from %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
+	if (item == NULL) {
+		rb_debug ("Could not find dmap.returnedcount item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+	returned_count = g_value_get_int (&(item->content));
+	
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MTCO);
+	if (item == NULL) {
+		rb_debug ("Could not find dmap.specifiedtotalcount item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+	specified_total_count = g_value_get_int (&(item->content));
+	
+	item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUTY);
+	if (item == NULL) {
+		rb_debug ("Could not find dmap.updatetype item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+	update_type = g_value_get_char (&(item->content));
+
+	listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
+	if (listing_node == NULL) {
+		rb_debug ("Could not find dmap.listing item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	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 {
+//		??FIXME??
+//		OLD ITUNES
+		// 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);
+
+	ret = TRUE;
+out:
+	rb_daap_structure_destroy (structure);
+	g_free (path);
+
+	return ret;
+}
+
+static gboolean
+connection_get_playlists (RBDAAPConnection *connection)
+{
+	GNode *structure = NULL;
+	gboolean ret;
+	GNode *listing_node;
+	gchar *path;
+	gint i;
+	GNode *n;
+
+	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);
+	
+	if (structure == NULL || ret == FALSE) {
+		rb_debug ("Got invalid response from %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
+	if (listing_node == NULL) {
+		rb_debug ("Could not find dmap.listing item in %s", path);
+		ret = FALSE;
+		goto out;
+	}
+
+	for (i = 0, n = listing_node->children; n; n = n->next, i++) {
+		RBDAAPItem *item;
+		gint id;
+		gchar *name;
+		RBDAAPPlaylist *playlist;
+		GNode *playlist_structure;
+		GNode *playlist_listing_node;
+		GNode *n2;
+		gint j;
+		GList *playlist_uris = NULL;
+		gchar *path2;
+		
+		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) {
+			rb_debug ("Could not find dmap.itemid item in %s", path);
+			ret = FALSE;
+			goto out;
+		}
+		id = g_value_get_int (&(item->content));
+
+		item = rb_daap_structure_find_item (n, RB_DAAP_CC_MINM);
+		if (item == NULL) {
+			rb_debug ("Could not find dmap.itemname item in %s", path);
+			ret = FALSE;
+			goto out;
+		}
+		name = g_value_dup_string (&(item->content));
+
+		path2 = 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, path2, TRUE, connection->daap_version,0, FALSE,&playlist_structure);
+		
+		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) {
+			rb_debug ("Could not find dmap.listing item in %s", path2);
+			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) {
+				rb_debug ("Could not find dmap.itemid item in %s", path2);
+				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);
+	}
+
+	ret = TRUE;
+out:
+	rb_daap_structure_destroy (structure);
+	structure = NULL;
+
+	return ret;
+}
+	
+RBDAAPConnection * 
+rb_daap_connection_new (const gchar *host, 
+			gint port, 
+			RhythmDB *db, 
+			RhythmDBEntryType type)
+{
+	RBDAAPConnection *connection = NULL;
+	gchar *path = NULL;
+	
+	connection = g_new0 (RBDAAPConnection, 1);
+
+	rb_debug ("Creating new DAAP connection to %s:%d", host, port);
+
+	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) {
+		rb_debug ("Error parsing %s", path);
+		goto error_out;
+	}
+	
+	rb_debug ("Getting DAAP connection info");
+	if (connection_get_info (connection) == FALSE) {
+		rb_debug ("Could not get DAAP connection info");
+		goto error_out;
+	}
+
+	rb_debug ("Logging into DAAP server");
+	if (connection_login (connection) == FALSE) {
+		rb_debug ("Could not login to DAAP server");
+		goto error_out;
+	}
+	
+	rb_debug ("Getting DAAP database info");
+	if (connection_get_database_info (connection) == FALSE) {
+		rb_debug ("Could not get DAAP database info");
+		goto error_out;
+	}
+	
+	rb_debug ("Getting DAAP song listing");
+	if (connection_get_song_listing (connection, host, port, db, type) == FALSE) {
+		rb_debug ("Could not get DAAP song listing");
+		goto error_out;
+	}
+
+	rb_debug ("Getting DAAP playlists");
+	if (connection_get_playlists (connection) == FALSE) {
+		rb_debug ("Could not get DAAP playlsits");
+		goto error_out;
+	}
+	
+	rb_debug ("Successfully created DAAP connection");
+	
+	goto out;
+
+	
+error_out:
+	rb_daap_connection_destroy (connection);
+	connection = NULL;
+	
+out:
+	if (path) {
+		g_free (path);
+	}
+	
+	return connection;
+}
+
+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/daapsharing/rb-daap-connection.h myrhythmbox/daapsharing/rb-daap-connection.h
--- rhythmbox/daapsharing/rb-daap-connection.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/rb-daap-connection.h	2005-08-22 21:34:16.000000000 -0400
@@ -0,0 +1,60 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) hashing, parsing, 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/daapsharing/rb-daap-mdns.c myrhythmbox/daapsharing/rb-daap-mdns.c
--- rhythmbox/daapsharing/rb-daap-mdns.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/rb-daap-mdns.c	2005-08-22 22:55:35.388010544 -0400
@@ -0,0 +1,381 @@
+/*
+ *  Implentation of Multicast DNS for DAAP 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 "rb-daap-mdns.h"
+
+#ifdef WITH_HOWL
+#include <howl.h>
+
+typedef struct _CallbackAndData {
+	gpointer callback;
+	gpointer data;
+} CallbackAndData;
+
+static gboolean
+howl_in_cb (GIOChannel *io_channel,
+	    GIOCondition condition,
+	    gpointer data)
+{
+	sw_discovery discovery = (sw_discovery) data;
+	sw_salt salt;
+
+	if (sw_discovery_salt (discovery, &salt) == SW_OKAY) {
+		sw_salt_lock (salt);
+		sw_discovery_read_socket (discovery);
+		sw_salt_unlock (salt);
+	}
+	
+	return TRUE;
+}
+
+static void
+setup_sw_discovery (sw_discovery discovery)
+{
+	int fd;
+	GIOChannel *channel;
+
+	fd = sw_discovery_socket (discovery);
+
+	channel = g_io_channel_unix_new (fd);
+	g_io_add_watch (channel, G_IO_IN, howl_in_cb, discovery);
+	g_io_channel_unref (channel);
+
+	return;
+}
+
+static sw_discovery
+get_sw_discovery ()
+{
+	sw_result result;
+	static sw_discovery discovery = NULL;
+	static gboolean initialized = FALSE;
+	
+	if (initialized == FALSE) {
+		result = sw_discovery_init (&discovery);
+	
+		if (result != SW_OKAY) {
+			g_warning ("Error starting mDNS discovery");
+			return NULL;
+		}
+
+		setup_sw_discovery (discovery);
+
+		initialized = TRUE;
+	}
+
+	return discovery;
+}
+		
+static sw_result
+browse_cb (sw_discovery discovery,
+	   sw_discovery_oid oid,
+	   sw_discovery_browse_status status,
+	   sw_uint32 interface_index,
+	   sw_const_string name,
+	   sw_const_string type,
+	   sw_const_string domain,
+	   sw_opaque extra)
+{
+	CallbackAndData *cd = (CallbackAndData *) extra;
+	RBDAAPmDNSBrowserStatus bstatus;
+
+	if (status == SW_DISCOVERY_BROWSE_ADD_SERVICE) {
+		bstatus = RB_DAAP_MDNS_BROWSER_ADD_SERVICE;
+	} else if (status == SW_DISCOVERY_BROWSE_REMOVE_SERVICE) {
+		bstatus = RB_DAAP_MDNS_BROWSER_REMOVE_SERVICE;
+	} else {
+		return SW_OKAY;
+	}
+	
+	((RBDAAPmDNSBrowserCallback)cd->callback) ((RBDAAPmDNSBrowser) oid,
+						   bstatus,
+						   (const gchar *) name,
+						   cd->data);
+	
+	return SW_OKAY;
+}
+
+gboolean
+rb_daap_mdns_browse (RBDAAPmDNSBrowser *browser,
+		     RBDAAPmDNSBrowserCallback callback,
+		     gpointer user_data)
+{
+	sw_result result;
+	sw_discovery discovery;
+	
+	discovery = get_sw_discovery ();
+
+	if (discovery) {
+		static CallbackAndData cd;
+		sw_result result;
+		
+		cd.callback = callback;
+		cd.data = user_data;
+		
+	       	result = sw_discovery_browse (discovery,
+					      0,
+					      "_daap._tcp", 
+					      "local", 
+					      (sw_discovery_browse_reply) browse_cb, 
+					      (sw_opaque) &cd, 
+					      (sw_discovery_oid *)browser);
+
+		if (result == SW_OKAY) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+void
+rb_daap_mdns_browse_cancel (RBDAAPmDNSBrowser browser)
+{
+	sw_discovery discovery;
+	
+	discovery = get_sw_discovery ();
+
+	if (discovery) {
+		sw_discovery_cancel (discovery, (sw_discovery_oid) browser);
+	}
+
+	return;
+}
+
+static sw_result
+resolve_cb (sw_discovery disc,
+	    sw_discovery_oid oid, 
+	    sw_uint32 interface_index, 
+	    sw_const_string name, 
+	    sw_const_string type, 
+	    sw_const_string domain, 
+	    sw_ipv4_address address, 
+	    sw_port port, 
+	    sw_octets text_record, 
+	    sw_ulong text_record_length, 
+	    sw_opaque extra)
+{
+	gchar *host = g_malloc (16);
+	CallbackAndData *cd = (CallbackAndData *) extra;
+
+	sw_ipv4_address_name (address, host, 16);
+
+	sw_discovery_cancel (disc, oid);
+
+	((RBDAAPmDNSResolverCallback)cd->callback) ((RBDAAPmDNSResolver) oid,
+						    RB_DAAP_MDNS_RESOLVER_FOUND,
+						    name,
+						    host,
+						    (guint) port,
+						    cd->data);
+
+	return SW_OKAY;
+}
+	
+
+gboolean
+rb_daap_mdns_resolve (RBDAAPmDNSResolver *resolver,
+		      const gchar *name,
+		      RBDAAPmDNSResolverCallback callback,
+		      gpointer user_data)
+{
+	sw_result result;
+	sw_discovery discovery;
+	
+	discovery = get_sw_discovery ();
+
+	if (discovery) {
+		static CallbackAndData cd;
+		sw_result result;
+		
+		cd.callback = callback;
+		cd.data = user_data;
+		
+	       	result = sw_discovery_resolve (discovery,
+					       0,
+					       name, 
+					       "_daap._tcp", 
+					       "local",
+					       (sw_discovery_resolve_reply) resolve_cb,
+		       			       (sw_opaque) &cd,
+					       (sw_discovery_oid *)resolver);
+		
+		if (result == SW_OKAY) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+void
+rb_daap_mdns_resolve_cancel (RBDAAPmDNSResolver resolver)
+{
+	sw_discovery discovery;
+	
+	discovery = get_sw_discovery ();
+
+	if (discovery) {
+		sw_discovery_cancel (discovery, (sw_discovery_oid) resolver);
+	}
+
+	return;
+}
+
+static sw_result
+publish_cb (sw_discovery discovery, 
+	    sw_discovery_oid oid, 
+	    sw_discovery_publish_status status, 
+	    sw_opaque extra)
+{
+	CallbackAndData *cd = (CallbackAndData *) extra;
+	RBDAAPmDNSPublisherStatus pstatus;
+
+	if (status == SW_DISCOVERY_PUBLISH_STARTED) {
+		pstatus = RB_DAAP_MDNS_PUBLISHER_STARTED;
+	} else if (status == SW_DISCOVERY_PUBLISH_NAME_COLLISION) {
+		pstatus = RB_DAAP_MDNS_PUBLISHER_COLLISION;
+	} else {
+		return SW_OKAY;
+	}
+
+	((RBDAAPmDNSPublisherCallback)cd->callback) ((RBDAAPmDNSPublisher) oid,
+						     pstatus,
+						     cd->data);
+
+	return SW_OKAY;
+}
+
+gboolean
+rb_daap_mdns_publish (RBDAAPmDNSPublisher *publisher,
+		      const gchar *name,
+		      guint port,
+		      RBDAAPmDNSPublisherCallback callback,
+		      gpointer user_data)
+{
+	sw_discovery discovery;
+	
+	discovery = get_sw_discovery ();
+
+	if (discovery) {
+		static CallbackAndData cd;
+		sw_result result;
+		
+		cd.callback = callback;
+		cd.data = user_data;
+		
+	       	result = sw_discovery_publish (discovery,
+					       0,
+					       name, 
+					       "_daap._tcp", 
+					       "local",
+					       NULL,
+					       port,
+					       NULL,
+					       0,
+					       (sw_discovery_publish_reply) publish_cb,
+		       			       (sw_opaque) &cd,
+					       (sw_discovery_oid *)publisher);
+		
+		if (result == SW_OKAY) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+void
+rb_daap_mdns_publish_cancel (RBDAAPmDNSPublisher publisher)
+{
+	sw_discovery discovery;
+	
+	discovery = get_sw_discovery ();
+
+	if (discovery) {
+		sw_discovery_cancel (discovery, (sw_discovery_oid) publisher);
+	}
+
+	return;
+}
+#endif
+
+#ifdef HAVE_AVAHI
+
+gboolean
+rb_daap_mdns_browse (RBDAAPmDNSBrowser *browser,
+		     RBDAAPmDNSBrowserCallback callback,
+		     gpointer user_data)
+{
+	g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+	return FALSE;
+}
+
+void
+rb_daap_mdns_browse_cancel (RBDAAPmDNSBrowser browser)
+{
+	g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+	return;
+}
+
+gboolean
+rb_daap_mdns_resolve (RBDAAPmDNSResolver *resolver,
+		      const gchar *name,
+		      RBDAAPmDNSResolverCallback callback,
+		      gpointer user_data)
+{
+	g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+	return FALSE;
+}
+
+void
+rb_daap_mdns_resolve_cancel (RBDAAPmDNSResolver resolver)
+{
+	g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+	return;
+}
+
+gboolean
+rb_daap_mdns_publish (RBDAAPmDNSPublisher *publisher,
+		      const gchar *name,
+		      guint port,
+		      RBDAAPmDNSPublisherCallback callback,
+		      gpointer user_data)
+{
+	g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+	return FALSE;
+}
+
+void
+rb_daap_mdns_publish_cancel (RBDAAPmDNSPublisher publisher)
+{
+	g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+	return;
+}
+#endif
diff -x CVS -x cvs -urN rhythmbox/daapsharing/rb-daap-mdns.h myrhythmbox/daapsharing/rb-daap-mdns.h
--- rhythmbox/daapsharing/rb-daap-mdns.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/daapsharing/rb-daap-mdns.h	2005-08-22 22:37:37.817256116 -0400
@@ -0,0 +1,97 @@
+/*
+ *  Header for abstraction of Multicast DNS for DAAP 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_MDNS_H
+#define __RB_DAAP_MDNS_H
+
+#include <glib.h>
+
+/* discovering hosts */
+typedef gpointer RBDAAPmDNSBrowser;
+
+typedef enum {
+	RB_DAAP_MDNS_BROWSER_ADD_SERVICE = 1,
+	RB_DAAP_MDNS_BROWSER_REMOVE_SERVICE
+} RBDAAPmDNSBrowserStatus;
+
+typedef void (* RBDAAPmDNSBrowserCallback) (RBDAAPmDNSBrowser browser,
+					    RBDAAPmDNSBrowserStatus status,
+					    const gchar *name,
+					    gpointer user_data);
+gboolean
+rb_daap_mdns_browse (RBDAAPmDNSBrowser *browser,
+		     RBDAAPmDNSBrowserCallback callback,
+		     gpointer data);
+
+void
+rb_daap_mdns_browse_cancel (RBDAAPmDNSBrowser browser);
+
+/* resolving hosts */
+typedef gpointer RBDAAPmDNSResolver;
+
+typedef enum {
+	RB_DAAP_MDNS_RESOLVER_FOUND = 1,
+	RB_DAAP_MDNS_RESOLVER_TIMEOUT
+} RBDAAPmDNSResolverStatus;
+
+typedef void (* RBDAAPmDNSResolverCallback) (RBDAAPmDNSResolver resolver,
+					     RBDAAPmDNSResolverStatus status,
+					     const gchar *name,
+					     gchar *host,
+					     guint port,
+					     gpointer user_data);
+
+gboolean
+rb_daap_mdns_resolve (RBDAAPmDNSResolver *resolver,
+		      const gchar *name,
+		      RBDAAPmDNSResolverCallback callback,
+		      gpointer data);
+
+
+void
+rb_daap_mdns_resolve_cancel (RBDAAPmDNSResolver resolver);
+
+/* publishing */
+typedef gpointer RBDAAPmDNSPublisher;
+
+typedef enum {
+	RB_DAAP_MDNS_PUBLISHER_STARTED = 1,
+	RB_DAAP_MDNS_PUBLISHER_COLLISION
+} RBDAAPmDNSPublisherStatus;
+
+typedef void (* RBDAAPmDNSPublisherCallback) (RBDAAPmDNSPublisher publisher,
+					      RBDAAPmDNSPublisherStatus status,
+					      gpointer user_data);
+
+gboolean
+rb_daap_mdns_publish (RBDAAPmDNSPublisher *publisher,
+		      const gchar *name,
+		      guint port,
+		      RBDAAPmDNSPublisherCallback callback,
+		      gpointer user_data);
+
+void
+rb_daap_mdns_publish_cancel (RBDAAPmDNSPublisher publisher);
+
+
+#endif /* __RB_DAAP_MDNS_H */
+
+
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-22 22:34:27.103768625 -0400
@@ -0,0 +1,1229 @@
+/*
+ *  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 "rb-daap-share.h"
+#include "rb-daap-structure.h"
+#include "rb-daap-mdns.h"
+
+#include "rb-playlist-source.h"
+#include "rb-debug.h"
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-uri.h>
+#include <libsoup/soup-server.h>
+#include <libsoup/soup-server-message.h>
+#include <libgnomevfs/gnome-vfs.h>
+
+#include <time.h>
+#include <string.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;
+	RBDAAPmDNSPublisher publisher;
+
+	/* 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->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 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;
+}
+
+#define DMAP_STATUS_OK 200
+
+#define DMAP_VERSION 2.0
+#define DAAP_VERSION 3.0
+#define DMAP_TIMEOUT 1800
+
+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) DMAP_STATUS_OK);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_MPRO, (gdouble) DMAP_VERSION);
+	rb_daap_structure_add (msrv, RB_DAAP_CC_APRO, (gdouble) DAAP_VERSION);
+	/* 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) DMAP_TIMEOUT);
+	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) DMAP_STATUS_OK);
+
+	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;
+}
+
+/* This is arbitrary.  iTunes communicates with a session id for
+ * reasons relating to updates to the database and such, I think
+ * Since we don't do that, and since we don't keep track of connections
+ * like iTunes do, everyone can just share the same, special, arbitrary
+ * session id.
+ */
+#define DAAP_SESSION_ID 42
+	
+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) DMAP_STATUS_OK);
+	rb_daap_structure_add (mlog, RB_DAAP_CC_MLID, (gint32) DAAP_SESSION_ID);
+
+	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) DMAP_STATUS_OK);
+		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));
+}
+
+#define DMAP_ITEM_KIND_AUDIO 2
+#define DAAP_SONG_DATA_KIND_NONE 0
+
+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) DMAP_ITEM_KIND_AUDIO);
+	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) DAAP_SONG_DATA_KIND_NONE);
+	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) DMAP_ITEM_KIND_AUDIO);
+	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) DMAP_STATUS_OK);
+		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) DMAP_STATUS_OK);
+		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) DMAP_STATUS_OK);
+		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) DMAP_STATUS_OK);
+		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;
+		}
+		
+		/* FIXME FIXME FIXME
+		 * Ideally, it seems that an mmap type solution should be used
+		 * here.  However, this works for now.
+		 */
+		buf = g_try_malloc (file_size);
+		if (buf == NULL) {
+			g_warning ("Unable to malloc %"G_GUINT64_FORMAT" bytes to transfer file", file_size);
+			soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+			goto out;
+		}
+
+		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 
+publish_cb (RBDAAPmDNSPublisher publisher,
+	    RBDAAPmDNSPublisherStatus status,
+	    RBDAAPShare *share)
+{
+	switch (status) {
+		case RB_DAAP_MDNS_PUBLISHER_STARTED:
+			rb_debug ("mdns publish successful\n");
+			share->priv->published = TRUE;
+			break;
+		case RB_DAAP_MDNS_PUBLISHER_COLLISION:
+			/* FIXME need a way to handle this */
+			g_warning ("error publishing because of name collision");
+			break;
+	}
+
+	return;
+}
+
+static void
+rb_daap_share_start_publish (RBDAAPShare *share)
+{
+	gint port = SOUP_ADDRESS_ANY_PORT;
+	gboolean ret;
+	
+	share->priv->server = soup_server_new (SOUP_SERVER_PORT, port, NULL);
+	if (share->priv->server == NULL) {
+		g_warning ("unable to start daap server");
+		return;
+	}
+
+	port = soup_server_get_port (share->priv->server);
+	rb_debug ("Started DAAP server on port %d", port);
+
+	soup_server_add_handler (share->priv->server, 
+				 NULL, 
+				 NULL, 
+				 (SoupServerCallbackFn)server_cb,
+				 NULL,
+				 share);
+	soup_server_run_async (share->priv->server);
+	
+	ret = rb_daap_mdns_publish (&(share->priv->publisher),
+				    share->priv->name,
+				    port,
+				    (RBDAAPmDNSPublisherCallback) publish_cb,
+				    share);
+	
+	if (ret == FALSE) {
+		g_warning ("mDNS publish of DAAP server failed");
+		return;
+	}
+
+	rb_debug ("Published DAAP server information to mdns");
+
+	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) {
+		/* FIXME */
+		/* This will spew:
+		 * GLib-CRITICAL **: g_main_loop_quit: assertion `loop != NULL' failed
+		 * But it doesn't seem to matter.
+		 */
+		soup_server_quit (share->priv->server);
+		g_object_unref (G_OBJECT (share->priv->server));
+		share->priv->server = NULL;
+	}
+	
+	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;
+	}
+	
+	if (share->priv->publisher) {
+		rb_daap_mdns_publish_cancel (share->priv->publisher);
+		share->priv->publisher = 0;
+	}
+
+	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-20 21:47:51.000000000 -0400
@@ -0,0 +1,60 @@
+/*
+ *  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-20 23:12:24.000000000 -0400
@@ -0,0 +1,696 @@
+/*
+ *  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 <glib-object.h>
+#include <gobject/gvaluecollector.h>
+#include "rb-daap-structure.h"
+#include "rb-debug.h"
+
+#include <gst/gstutils.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_INT64},
+	{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'), "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'), "daap.serverdatabases", "avdb", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABRO, MAKE_CONTENT_CODE('a','b','r','o'), "daap.databasebrowse", "abro", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABAL, MAKE_CONTENT_CODE('a','b','a','l'), "daap.browsealbumlisting", "abal", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABAR, MAKE_CONTENT_CODE('a','b','a','r'), "daap.browseartistlisting", "abar", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABCP, MAKE_CONTENT_CODE('a','b','c','p'), "daap.browsecomposerlisting", "abcp", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABGN, MAKE_CONTENT_CODE('a','b','g','n'), "daap.browsegenrelisting", "abgn", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ADBS, MAKE_CONTENT_CODE('a','d','b','s'), "daap.returndatabasesongs", "adbs", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ASAL, MAKE_CONTENT_CODE('a','s','a','l'), "daap.songalbum", "asal", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASAR, MAKE_CONTENT_CODE('a','s','a','r'), "daap.songartist", "asar", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASBT, MAKE_CONTENT_CODE('a','s','b','t'), "daap.songsbeatsperminute", "asbt", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASBR, MAKE_CONTENT_CODE('a','s','b','r'), "daap.songbitrate", "asbr", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASCM, MAKE_CONTENT_CODE('a','s','c','m'), "daap.songcomment", "ascm", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASCO, MAKE_CONTENT_CODE('a','s','c','o'), "daap.songcompliation", "asco", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_ASDA, MAKE_CONTENT_CODE('a','s','d','a'), "daap.songdateadded", "asda", RB_DAAP_TYPE_DATE},
+	{RB_DAAP_CC_ASDM, MAKE_CONTENT_CODE('a','s','d','m'), "daap.songdatemodified", "asdm", RB_DAAP_TYPE_DATE},
+	{RB_DAAP_CC_ASDC, MAKE_CONTENT_CODE('a','s','d','c'), "daap.songdisccount", "asdc", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASDN, MAKE_CONTENT_CODE('a','s','d','n'), "daap.songdiscnumber", "asdn", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASDB, MAKE_CONTENT_CODE('a','s','d','b'), "daap.songdisabled", "asdb", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_ASEQ, MAKE_CONTENT_CODE('a','s','e','q'), "daap.songeqpreset", "aseq", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASFM, MAKE_CONTENT_CODE('a','s','f','m'), "daap.songformat", "asfm", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASGN, MAKE_CONTENT_CODE('a','s','g','n'), "daap.songgenre", "asgn", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASDT, MAKE_CONTENT_CODE('a','s','d','t'), "daap.songdescription", "asdt", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASRV, MAKE_CONTENT_CODE('a','s','r','v'), "daap.songrelativevolume", "asrv", RB_DAAP_TYPE_SIGNED_INT},
+	{RB_DAAP_CC_ASSR, MAKE_CONTENT_CODE('a','s','s','r'), "daap.songsamplerate", "assr", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASSZ, MAKE_CONTENT_CODE('a','s','s','z'), "daap.songsize", "assz", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASST, MAKE_CONTENT_CODE('a','s','s','t'), "daap.songstarttime", "asst", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASSP, MAKE_CONTENT_CODE('a','s','s','p'), "daap.songstoptime", "assp", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASTM, MAKE_CONTENT_CODE('a','s','t','m'), "daap.songtime", "astm", RB_DAAP_TYPE_INT},
+	{RB_DAAP_CC_ASTC, MAKE_CONTENT_CODE('a','s','t','c'), "daap.songtrackcount", "astc", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASTN, MAKE_CONTENT_CODE('a','s','t','n'), "daap.songtracknumber", "astn", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASUR, MAKE_CONTENT_CODE('a','s','u','r'), "daap.songuserrating", "asur", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_ASYR, MAKE_CONTENT_CODE('a','s','y','r'), "daap.songyear", "asyr", RB_DAAP_TYPE_SHORT},
+	{RB_DAAP_CC_ASDK, MAKE_CONTENT_CODE('a','s','d','k'), "daap.songdatakind", "asdk", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_ASUL, MAKE_CONTENT_CODE('a','s','u','l'), "daap.songdataurl", "asul", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_APLY, MAKE_CONTENT_CODE('a','p','l','y'), "daap.databaseplaylists", "aply", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ABPL, MAKE_CONTENT_CODE('a','b','p','l'), "daap.baseplaylist", "abpl", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_APSO, MAKE_CONTENT_CODE('a','p','s','o'), "daap.playlistsongs", "apso", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_PRSV, MAKE_CONTENT_CODE('p','r','s','v'), "daap.resolve", "prsv", RB_DAAP_TYPE_CONTAINER},
+	{RB_DAAP_CC_ARIF, MAKE_CONTENT_CODE('a','r','i','f'), "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'), "daap.authentication.schemes", "msas", RB_DAAP_TYPE_BYTE},
+	{RB_DAAP_CC_AGRP, MAKE_CONTENT_CODE('a','g','r','p'), "daap.songgrouping", "agrp", RB_DAAP_TYPE_STRING},
+	{RB_DAAP_CC_ASCP, MAKE_CONTENT_CODE('a','s','c','p'), "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_INT64:
+			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;
+	gchar *error = NULL;
+	
+	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);
+	}
+	
+	if (rb_daap_type != RB_DAAP_TYPE_STRING && rb_daap_type != RB_DAAP_TYPE_CONTAINER) {
+		G_VALUE_COLLECT (&(item->content), list, G_VALUE_NOCOPY_CONTENTS, &error);
+		if (error) {
+			g_warning (error);
+			g_free (error);
+		}
+	}
+
+	switch (rb_daap_type) {
+		case RB_DAAP_TYPE_BYTE: 
+		case RB_DAAP_TYPE_SIGNED_INT:
+			item->size = 1;
+			break;
+		case RB_DAAP_TYPE_SHORT: 
+			item->size = 2;
+			break;
+		case RB_DAAP_TYPE_DATE:
+		case RB_DAAP_TYPE_INT:
+		case RB_DAAP_TYPE_VERSION:
+			item->size = 4;
+			break;
+		case RB_DAAP_TYPE_INT64: 
+			item->size = 8;
+			break;
+		case RB_DAAP_TYPE_STRING: {
+			gchar *s = va_arg (list, gchar *);
+
+			g_value_set_string (&(item->content), s);
+
+			/* we dont use G_VALUE_COLLECT for this because we also
+			 * need the length */
+			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_INT64: {
+			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;
+}
+
+#define rb_daap_buffer_read_int8(b)  	GST_READ_UINT8 (b)
+#define rb_daap_buffer_read_int16(b) 	(gint16) GST_READ_UINT16_BE (b)
+#define rb_daap_buffer_read_int32(b) 	(gint32) GST_READ_UINT32_BE (b)
+#define rb_daap_buffer_read_int64(b) 	(gint64) GST_READ_UINT64_BE (b)
+#define rb_daap_buffer_read_string(b,s)	g_strndup(b,s)
+
+//#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 guchar *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;
+		
+#ifdef PARSE_DEBUG
+		g_print ("l is %d and buf_length is %d\n", l, buf_length);
+#endif		
+		g_node_append (parent, node);
+
+		if (buf_length < 8) {
+			g_warning ("Malformed response");
+			return;
+		}
+		
+		item->content_code = rb_daap_buffer_read_content_code (&(buf[l]));
+		l += 4;
+
+		codesize = rb_daap_buffer_read_int32(&(buf[l]));
+		l += 4;
+
+#ifdef PARSE_DEBUG
+		g_print ("content_code = %d, codesize is %d, l is %d\n", item->content_code, codesize, l);
+#endif
+		
+		if (buf_length < codesize + 8) {
+			g_warning ("Malformed response");
+			return;
+		}
+		
+		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 = (gchar) 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_INT64: {
+				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, (guchar *)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);
+	}
+
+	return;
+}
+	
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-20 21:46:31.000000000 -0400
@@ -0,0 +1,192 @@
+/*
+ *  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_INT64 = 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-04 20:33:01.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-07-29 12:08:38.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-07-31 14:53:27.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-20 15:47:33.000000000 -0400
@@ -17,6 +17,10 @@
 	-I$(top_srcdir) 				\
 	-I$(top_srcdir)/lib 				\
 	-I$(top_srcdir)/metadata			\
+	-I$(top_srcdir)/sources				\
+	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/widgets				\
 	$(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-08-21 05:58:26.000000000 -0400
+++ myrhythmbox/player/rb-player-gst.c	2005-08-22 23:51:29.860585065 -0400
@@ -29,14 +29,18 @@
 #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"
 
+#include "rb-daap-source.h"
+
 G_DEFINE_TYPE(RBPlayer, rb_player, G_TYPE_OBJECT)
 
 static void rb_player_finalize (GObject *object);
@@ -480,6 +484,31 @@
 	return TRUE;
 }
 
+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;
+}
+
 void
 rb_player_open (RBPlayer *mp,
 		const char *uri,
@@ -498,12 +527,30 @@
 	g_free (mp->priv->uri);
 	mp->priv->uri = NULL;
 
-	if (uri == NULL) {
-		mp->priv->playing = FALSE;
-		return;
+	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 {
+		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);
 	}
-	g_object_set (G_OBJECT (mp->priv->playbin), "uri", uri, NULL);	
-	mp->priv->uri = g_strdup (uri);
 
 	if (!rb_player_sync_pipeline (mp, error)) {
 		rb_player_close (mp, NULL);
@@ -520,6 +567,8 @@
 	g_free (mp->priv->uri);
 	mp->priv->uri = NULL;
 
+	gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); // get rid of it if we've already pushed it 
+	
 	if (mp->priv->playbin == NULL)
 		return;
 
@@ -664,11 +713,28 @@
 
 	gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED);
 
-	gst_element_seek (mp->priv->playbin, 
+	/* FIXME FIXME
+	 * the patch from 167364 "broke" seeking in daap streams
+	 * you can still seek, but the time slider gets reset to 0 every time
+	 * blah.
+	 */
+	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 {
+		gst_element_seek (mp->priv->playbin, 
 			  GST_FORMAT_TIME 
 			  | GST_SEEK_METHOD_SET 
 			  | GST_SEEK_FLAG_FLUSH, 
 			  time * GST_SECOND);
+	}
 
 	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-17 05:37:42.000000000 -0400
+++ myrhythmbox/rhythmdb/rhythmdb.c	2005-08-22 21:40:26.000000000 -0400
@@ -1611,6 +1611,18 @@
 						  GNOME_VFS_FILE_INFO_FOLLOW_LINKS))
 	    == GNOME_VFS_OK)
 		return;
+	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;
+	}
 error:
 	unescaped = gnome_vfs_unescape_string_for_display (uri);
 	event->error = g_error_new (RHYTHMDB_ERROR,
diff -x CVS -x cvs -urN rhythmbox/shell/Makefile.am myrhythmbox/shell/Makefile.am
--- rhythmbox/shell/Makefile.am	2005-08-10 09:35:54.000000000 -0400
+++ myrhythmbox/shell/Makefile.am	2005-08-22 23:07:13.372794695 -0400
@@ -117,6 +117,14 @@
 rhythmbox_LDADD += $(LIBNAUTILUS_BURN_LIBS)
 endif
 
+if USE_DAAP
+INCLUDES += -I$(top_srcdir)/daapsharing
+
+rhythmbox_LDADD += $(SOUP_LIBS) \
+		   $(MDNS_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-08-15 09:18:02.000000000 -0400
+++ myrhythmbox/shell/rb-playlist-manager.c	2005-08-22 12:23:25.000000000 -0400
@@ -723,6 +723,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	2005-08-15 09:18:02.000000000 -0400
+++ myrhythmbox/shell/rb-playlist-manager.h	2005-08-22 12:23:25.000000000 -0400
@@ -79,6 +79,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-15 09:18:02.000000000 -0400
+++ myrhythmbox/shell/rb-shell.c	2005-08-23 00:03:24.137410861 -0400
@@ -67,6 +67,10 @@
 #ifdef WITH_IPOD_SUPPORT
 #include "rb-ipod-source.h"
 #endif /* WITH_IPOD_SUPPORT */
+#include "rb-daap-source.h"
+#ifdef WITH_DAAP_SUPPORT
+#include "daap-sharing.h"
+#endif /* WITH_DAAP_SUPPORT */
 #include "rb-load-failure-dialog.h"
 #include "rb-new-station-dialog.h"
 #include "rb-iradio-source.h"
@@ -108,7 +112,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,
@@ -164,6 +167,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,
@@ -257,10 +262,16 @@
 #ifdef WITH_IPOD_SUPPORT
 	rb_ipod_source_new,	
 #endif /* WITH_IPOD_SUPPORT */
+	rb_daap_sources_init,
 	NULL,
 };
 
+typedef void (*SourceShutdownFunc)(RBShell *);
 
+static SourceShutdownFunc known_source_shutdowns[] = {
+	rb_daap_sources_shutdown,
+	NULL,
+};
 
 static gboolean save_yourself_cb (GnomeClient *client, 
                                   gint phase,
@@ -291,7 +302,8 @@
 	PROP_RHYTHMDB_FILE,
 	PROP_SELECTED_SOURCE,
 	PROP_DB,
-	PROP_UI_MANAGER
+	PROP_UI_MANAGER,
+	PROP_PLAYLIST_MANAGER
 };
 
 /* prefs */
@@ -397,6 +409,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);
 
@@ -538,7 +553,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));
 
 }
 
@@ -678,6 +699,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;
@@ -993,12 +1017,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);
@@ -1105,6 +1132,10 @@
 	rb_statusbar_sync_state (shell->priv->statusbar);
 	rb_shell_sync_smalldisplay (shell);
 	gtk_widget_show (GTK_WIDGET (shell->priv->window));
+
+#ifdef WITH_DAAP_SUPPORT
+	daap_sharing_init (shell);
+#endif /* WITH_DAAP_SUPPORT */
 }
 
 static gboolean
@@ -1276,9 +1307,10 @@
 
 
 
-static void
+void
 rb_shell_append_source (RBShell *shell,
-			RBSource *source)
+			RBSource *source,
+			RBSource *parent)
 {
 	char *search_text;
 	
@@ -1294,7 +1326,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));
@@ -1306,7 +1338,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
@@ -1435,7 +1467,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);
 
@@ -1767,8 +1804,19 @@
 static void
 rb_shell_quit (RBShell *shell)
 {
+	gint i = 0;
+
 	rb_debug ("Quitting");
 
+	/* shutdown sources */
+	while (known_source_shutdowns[i] != NULL) {
+		known_source_shutdowns[i] (shell);
+		i++;
+	}
+	
+#ifdef WITH_DAAP_SUPPORT
+	daap_sharing_shutdown (shell);
+#endif /* WITH_DAAP_SUPPORT */
 	rb_shell_shutdown (shell);
 	rb_shell_sync_state (shell);
 	g_object_unref (G_OBJECT (shell));
@@ -1999,6 +2047,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	2005-08-13 22:49:07.000000000 -0400
+++ myrhythmbox/shell/rb-shell.h	2005-08-22 12:23:26.000000000 -0400
@@ -68,6 +68,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-22 21:38:33.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,80 @@
 		rb_debug ("No config widget for source %s", name);
 }
 
+#ifdef WITH_DAAP_SUPPORT
+
+#define CONF_ENABLE_SHARING "/apps/rhythmbox/sharing/enable_sharing"
+#define CONF_NAME "/apps/rhythmbox/sharing/share_name"
+
+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_NAME, name);
+
+	return FALSE;
+}
+
+static void
+add_daap_preferences (RBShellPreferences *shell_preferences)
+{
+	GtkWidget *vbox;
+	GtkWidget *check;
+	gboolean b;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *entry;
+	gchar *name;
+
+	vbox = gtk_vbox_new (FALSE, 5);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+
+	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_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);
+	
+	gtk_notebook_append_page (GTK_NOTEBOOK (shell_preferences->priv->notebook), vbox, gtk_label_new (_("Sharing")));
+
+	return;
+}
+#endif /* WITH_DAAP_SUPPORT */
+
 GtkWidget *
 rb_shell_preferences_new (GList *views)
 {
@@ -290,6 +367,11 @@
 						       name,
 						       RB_SOURCE (views->data));
 	}
+
+#ifdef WITH_DAAP_SUPPORT
+	add_daap_preferences (shell_preferences);
+#endif /* WITH_DAAP_SUPPORT */
+
 	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-22 21:46:28.570410674 -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,14 @@
 INCLUDES += $(LIBNAUTILUS_BURN_CFLAGS)
 endif
 
+if USE_DAAP
+libsourcesimpl_la_SOURCES +=		\
+	rb-daap-source.c 		\
+	rb-daap-source.h		\
+	rb-daap-playlist-source.c	\
+	rb-daap-playlist-source.h	
+
+INCLUDES += $(SOUP_CFLAGS) 
+
+endif
+
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-20 23:06:25.000000000 -0400
@@ -0,0 +1,190 @@
+/*
+ *  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-20 21:51:23.000000000 -0400
@@ -0,0 +1,55 @@
+/*
+ *  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-22 23:24:10.172393783 -0400
@@ -0,0 +1,563 @@
+/*
+ *  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 "rb-daap-connection.h"
+#include "rb-daap-mdns.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 RBDAAPmDNSBrowser browser = 0;
+static RBDAAPmDNSResolver resolver = 0;
+
+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
+browse_cb (RBDAAPmDNSBrowser b,
+	   RBDAAPmDNSBrowserStatus status,
+	   const gchar *name,
+	   RBShell *shell)
+{
+	if (status == RB_DAAP_MDNS_BROWSER_ADD_SERVICE) {
+		RBSource *source = find_source_by_name (name);
+
+		rb_debug ("New daap source '%s' discovered", name);
+		
+		if (source) {
+			rb_debug ("Ignoring duplicate daap source");
+			return;
+		}
+
+		if (eel_gconf_get_boolean (CONF_ENABLE_SHARING)) {
+			gchar *my_name;
+
+			my_name = eel_gconf_get_string (CONF_SHARE_NAME);
+
+			if (strcmp (my_name, name) == 0) {
+				return;
+			}
+		}
+		
+		source = rb_daap_source_new (shell, name);
+
+		sources = g_slist_prepend (sources, source);
+
+		rb_shell_append_source (shell, source, NULL);
+
+	} else if (status == RB_DAAP_MDNS_BROWSER_REMOVE_SERVICE) {
+		RBSource *source = find_source_by_name (name);
+
+		rb_debug ("DAAP source '%s' went away", 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)
+{
+	gboolean ret;
+
+	ret = rb_daap_mdns_browse (&browser, 
+				   (RBDAAPmDNSBrowserCallback) browse_cb,
+				   shell);
+
+	if (ret == FALSE) {
+		g_warning("Could not start browsing for DAAP hosts");
+		// FIXME display a gui error box
+	}
+	
+	return;
+}
+
+static void 
+stop_browsing (RBShell *shell)
+{
+	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;
+	
+	if (browser) {
+		rb_daap_mdns_browse_cancel (browser);
+		browser = 0;
+	}
+
+	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 (browser) {
+		stop_browsing (shell);
+	}
+	
+	return;
+}
+
+static void
+resolve_cb (RBDAAPmDNSResolver resolver,
+	    RBDAAPmDNSResolverStatus status,
+	    const gchar *name,
+	    gchar *host,
+	    guint port,
+	    RBDAAPSource *daap_source)
+{
+	RBShell *shell = NULL;
+	RhythmDB *db = NULL;
+	RhythmDBEntryType type;
+
+	if (status == RB_DAAP_MDNS_RESOLVER_FOUND) {
+		daap_source->priv->host = host;
+		daap_source->priv->port = port;
+	
+		daap_source->priv->resolved = TRUE;
+
+		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);
+	
+			/* FIXME FIXME FIXME
+			 * this is the call that takes a long time 
+			 */
+			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, RB_SOURCE (daap_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;
+		}
+	} else if (status == RB_DAAP_MDNS_RESOLVER_TIMEOUT) {
+		g_warning ("Unable to resolve %s", name);
+	}
+
+	return;
+}
+
+static void 
+rb_daap_source_activate (RBSource *source)
+{
+	RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
+	gchar *name;
+	
+	g_object_get (G_OBJECT (source), "name", &name, NULL);
+	
+	if (daap_source->priv->resolved == FALSE) {
+		gboolean ret;
+
+		ret = rb_daap_mdns_resolve (&resolver,
+					    name,
+					    (RBDAAPmDNSResolverCallback) resolve_cb,
+					    source);
+
+		if (ret == FALSE) {
+			g_warning ("could not start resolving host");
+		}
+	}
+
+	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-20 21:51:11.000000000 -0400
@@ -0,0 +1,69 @@
+/*
+ *  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-08-17 12:32:54.000000000 -0400
+++ myrhythmbox/sources/rb-library-source.c	2005-08-22 12:23:26.000000000 -0400
@@ -616,21 +616,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,
@@ -703,6 +692,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-04 22:42:00.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-04 22:51:30.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-23 00:04:25.722568281 -0400
@@ -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-07-31 04:45:48.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-07-31 04:45:48.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-07-31 04:45:48.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]