[Rhythmbox-devel] Patches for iTunes music share browsing



Hi all,

The attached patches are those I've developed to get browsing of iTunes
music shares working in Rhythmbox.

core.diff is a number of changes to RB 'core' that are needed for
daap.diff.  Walters had suggested submitting these as a separate patch
so they can be considered separate to all the daap code.

daap.diff is the daap code.  

You will also need the patch at
http://bugzilla.gnome.org/show_bug.cgi?id=312114

Changes in core.diff:
***********************************
shell/rb-shell.c/h:
* In rb_shell_construct, there is now a check for the validity of the
sources returned from the known_sources function array.  This is the
simplest place to insert a function like rb_daap_sources_init() which
sets up monitoring for daap sources to add to the source list later.
Since this function won't actually create an RBSource, it returns NULL
and rb_shell_construct catches that.
* There is a new function array known_source_shutdowns[] to compliment
the known_sources function array.  Its purpose is to call functions to
shutdown sources, like rb_daap_sources_shutdown() which stops monitoring
for daap sources.  The array is iterated in rb_shell_finalize().
* rb_shell_append_source is no longer static, so that other parts of the
RB code can add sources to the list (for example, a timeout function
monitoring daap servers).
* rb_shell_append_source now takes a RBSource *parent argument, which is
NULL in the case of adding a toplevel source.  This is so sources can
have children (playlists within a daap server/ipod).
* rb_shell_select_internal now fires off rb_source_deactivate and
rb_source_activate to the proper sources.  This is so a source can know
when it is activated (daap source told to connect to the server & fetch
data)
* Sources can now be disconnected.  There changes to provide a UI hook
for disconnecting sources.

sources/rb-source.c/h:
* Overrideable functions _activate, _deactivate and _disconnect for
sources to be notified when they are selected in the source list as
explained above.

sources/rb-sourcelist.c/h:
* Moved from a GtkListStore to a GtkTreeStore so that sources can have
children.  This is mostly just a s/List/Tree/ type operation, except for
the two rb_sourcelist_source_to_iter &
rb_sourcelist_visible_source_to_iter functions, which have been
rewritten to use gtk_tree_model_foreach to navigate the tree.
* rb_sourcelist_append now takes a RBSource *parent argument which is
NULL in the case of adding a toplevel source. (same as
rb_shell_append_source).

sources rb-sourcelist-model.c:
* Moved from a GtkListStore to a GtkTreeStore as explained above.


Changes in daap.diff
***********************************
configure.ac:
* New --enable-daap switch to enable daap.  There is a check for
libsoup, which is needed for its http client routines (and, in the
future, its http server routines as well).

data/ui/rhythmbox-ui.xml:
* Popup menu for RBDAAPSource.

player/rb-player-gst.c:
* Callbacks to append additional headers to an http:// request in
gnome-vfs (assuming gstreamer's playbin element uses gnomevfssrc, which
it should).  There are a pair of utility functions in rb-daap-source
that are declared extern here (to avoid including all of
rb-daap-source.h).
* Daap uris are stored daap:// within Rhythmbox so they can be
identified as such and given special treatment (extra http headers).
However, gstreamer expects an http:// uri, so there is code in
rb_player_open to fudge the uri (daap->http).

rhythmdb/rhythmdb.c:
* Check for daap files to fake a GnomeVFSFileInfo structure since
stat'ing every file would be time consuming and pointless.

shell/rb-shell.c:
* Add rb_daap_sources_init() to the known_sources array, and
rb_daap_sources_shutdown() to the known_source_shutdowns array.  These
functions start & stop monitoring for daap shares on the network.

sources/daap.c/h:
* Daap client routines.  Some of the code is borrowed from libopendaap
(the authentication/hashing stuff), which is licensed under the LGPL, so
I *think* we're OK just including it (with proper attribution, of
course).

sources/rb-daap-source.c/h:
* Daap source.

sources/rb-daap-playlist-source.c/h:
* Daap playlist source.


Hopefully at least core.diff can be reviewed and committed and hopefully
the gstreamer people will approve the patch in bug 312114 and get a new
release out sooner or later.

I don't expect daap.diff to be committed anytime soon as there will
probably be quite a few changes to daap.c as sharing gets implemented,
but I figured I'd let people see what I've got so far.  Everything works
for me, but much testing & comments as I can get would be great though.

As far as instructions for getting it all to work:

1. Install howl http://www.porchdogsoft.com/products/howl/ and
mDNSResponder.
2. Make sure your copy of gnome-vfs is compiled with support for
zeroconf/rendezvous/bonjour/dns-sd/whatever they're calling it this week
through howl.
3. Patch your copy of gst-plugins with the patch at
http://bugzilla.gnome.org/show_bug.cgi?id=312114
4. Patch CVS Rhythmbox with core.diff
5. Patch CVS Rhythmbox with daap.diff
6. Put iTunes on a computer on your local network, and enable sharing in
it.
7. Pray.
8. Submit bug reports to me (email or on irc, I'm usually around as
ish).

Good luck.

Things that I /know/ don't work right now:
* iTunes shares with authentication
* Versions of iTunes older than 4.5 (for reference, its at 4.9 now).

Thinks that may or may not work for you:
* Disconnecting from shares.  Could be a bug introduced by the fix in
149799 or something I'm not doing right.

-charlie

diff -x CVS -Nurb rhythmbox/shell/rb-shell.c rhythmbox-core-changes/shell/rb-shell.c
--- rhythmbox/shell/rb-shell.c	2005-07-29 10:53:10.000000000 -0400
+++ rhythmbox-core-changes/shell/rb-shell.c	2005-07-31 05:12:33.983089827 -0400
@@ -110,7 +110,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,
@@ -165,6 +164,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,
@@ -261,7 +262,11 @@
 	NULL,
 };
 
+typedef void (*SourceShutdownFunc)(RBShell *);
 
+static SourceShutdownFunc known_source_shutdowns[] = {
+	NULL,
+};
 
 static gboolean save_yourself_cb (GnomeClient *client, 
                                   gint phase,
@@ -400,6 +405,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);
 
@@ -755,6 +763,13 @@
 rb_shell_finalize (GObject *object)
 {
         RBShell *shell = RB_SHELL (object);
+	gint i = 0;
+
+	/* shutdown sources */
+	while (known_source_shutdowns[i] != NULL) {
+		known_source_shutdowns[i] (shell);
+		i++;
+	}
 
 	gtk_widget_hide (shell->priv->window);
 	gtk_widget_hide (GTK_WIDGET (shell->priv->tray_icon));
@@ -1000,7 +1015,9 @@
 		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++;
 	}
 
@@ -1297,9 +1314,10 @@
 
 
 
-static void
+void
 rb_shell_append_source (RBShell *shell,
-			RBSource *source)
+			RBSource *source,
+			RBSource *parent)
 {
 	char *search_text;
 	
@@ -1315,7 +1333,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));
@@ -1327,7 +1345,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
@@ -1443,7 +1461,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);
 
@@ -2007,6 +2030,28 @@
 }
 
 static void
+rb_shell_cmd_source_disconnect (GtkAction *action,
+				RBShell *shell)
+{
+	rb_debug ("disconnect from source");
+
+	if (shell->priv->selected_source) {
+		RBSource *source;
+		RBSource *library_source;
+
+		source = shell->priv->selected_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 (source);
+	}
+	
+	return;
+}
+
+static void
 rb_shell_cmd_view_all (GtkAction *action,
 		       RBShell *shell)
 {
diff -x CVS -Nurb rhythmbox/shell/rb-shell.h rhythmbox-core-changes/shell/rb-shell.h
--- rhythmbox/shell/rb-shell.h	2004-07-13 23:13:03.000000000 -0400
+++ rhythmbox-core-changes/shell/rb-shell.h	2005-07-31 04:14:16.000000000 -0400
@@ -62,6 +62,9 @@
 void            rb_shell_register_entry_type_for_source (RBShell *shell,
 							 RBSource *source,
 							 RhythmDBEntryType type);
+
+void rb_shell_append_source (RBShell *shell, RBSource *source, RBSource *parent);
+
 G_END_DECLS
 
 #endif /* __RB_SHELL_H */
diff -x CVS -Nurb rhythmbox/sources/rb-source.c rhythmbox-core-changes/sources/rb-source.c
--- rhythmbox/sources/rb-source.c	2005-07-07 10:30:20.000000000 -0400
+++ rhythmbox-core-changes/sources/rb-source.c	2005-07-31 04:14:04.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,
@@ -692,3 +698,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 -Nurb rhythmbox/sources/rb-source.h rhythmbox-core-changes/sources/rb-source.h
--- rhythmbox/sources/rb-source.h	2005-06-12 12:02:15.000000000 -0400
+++ rhythmbox-core-changes/sources/rb-source.h	2005-07-31 04:14:04.000000000 -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 -Nurb rhythmbox/sources/rb-sourcelist.c rhythmbox-core-changes/sources/rb-sourcelist.c
--- rhythmbox/sources/rb-sourcelist.c	2005-07-28 23:53:43.000000000 -0400
+++ rhythmbox-core-changes/sources/rb-sourcelist.c	2005-07-31 04:26:42.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,7 +361,8 @@
 
 void
 rb_sourcelist_append (RBSourceList *sourcelist,
-		      RBSource *source)
+		      RBSource *source,
+		      RBSource *parent)
 {
 	GtkTreeIter iter;
 	PangoAttrList *attrs = pango_attr_list_new ();
@@ -366,9 +373,15 @@
 
 	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 -Nurb rhythmbox/sources/rb-sourcelist.h rhythmbox-core-changes/sources/rb-sourcelist.h
--- rhythmbox/sources/rb-sourcelist.h	2005-07-26 10:07:29.000000000 -0400
+++ rhythmbox-core-changes/sources/rb-sourcelist.h	2005-07-31 04:14:04.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 -Nurb rhythmbox/sources/rb-sourcelist-model.c rhythmbox-core-changes/sources/rb-sourcelist-model.c
--- rhythmbox/sources/rb-sourcelist-model.c	2005-06-04 13:57:08.000000000 -0400
+++ rhythmbox-core-changes/sources/rb-sourcelist-model.c	2005-07-31 04:14:04.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);
 }
 
diff -x CVS -Nurb rhythmbox/configure.ac myrhythmbox/configure.ac
--- rhythmbox/configure.ac	2005-07-20 10:20:32.000000000 -0400
+++ myrhythmbox/configure.ac	2005-07-31 04:42:38.733513304 -0400
@@ -156,6 +156,23 @@
    AC_DEFINE(ENABLE_TAG_WRITING, 1, [Define if tag writing should be enabled])
 fi
 
+dnl DAAP (iTunes Music Shares)
+AC_ARG_ENABLE(daap,
+	      AC_HELP_STRING([--enable-daap],
+			     [Enable Digital Audio Access Protocol in rhythmbox **EXPERIMENTAL**]))
+AM_CONDITIONAL(USE_DAAP, test x"$enable_daap" = xyes)
+if test x"$enable_daap" = xyes; then
+   AC_MSG_WARN([DAAP support is experimental, and may cause Rhythmbox to crash uncontrollably, use at your own risk])
+   AC_DEFINE(WITH_DAAP_SUPPORT, 1, [Define if daap should be enabled])
+   PKG_CHECK_MODULES(SOUP,                            \
+		libsoup-2.2,
+		have_libsoup=yes)
+   if test x"$have_libsoup" = xyes; then
+      AC_DEFINE(WITH_DAAP_BROWSE_SUPPORT, 1, [Define if daap browsing should be enabled])
+      AM_CONDITIONAL(USE_DAAP_BROWSE, test "x$have_libsoup" = "xyes")
+   fi
+fi
+
 
 dnl AC_CHECK_LIB(lirc_client, lirc_init,
 dnl 		[ AC_CHECK_HEADER(lirc/lirc_client.h,
@@ -481,6 +498,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 -Nurb 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 -Nurb rhythmbox/player/rb-player-gst.c myrhythmbox/player/rb-player-gst.c
--- rhythmbox/player/rb-player-gst.c	2005-07-31 04:16:53.000000000 -0400
+++ myrhythmbox/player/rb-player-gst.c	2005-07-31 04:12:58.549217701 -0400
@@ -29,14 +29,25 @@
 #include <string.h>
 #include <stdlib.h>
 #include <libgnome/gnome-i18n.h>
+#include <libgnomevfs/gnome-vfs.h>
 #include <libgnomevfs/gnome-vfs-ops.h>
 #include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnomevfs/gnome-vfs-standard-callbacks.h>
 
 #include "rb-debug.h"
 #include "rb-player.h"
 #include "rb-debug.h"
 #include "rb-marshal.h"
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+typedef struct _RBDAAPSource RBDAAPSource;
+extern RBDAAPSource *	rb_daap_source_find_for_uri	(const gchar *uri);
+
+extern gchar *		rb_daap_source_get_headers	(RBDAAPSource *source,
+							 const gchar *uri, 
+							 glong time);
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
+
 G_DEFINE_TYPE(RBPlayer, rb_player, G_TYPE_OBJECT)
 
 static void rb_player_finalize (GObject *object);
@@ -455,6 +466,32 @@
 	return TRUE;
 }
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+static void gnomevfssrc_send_additional_headers_cb (gconstpointer in,
+						    gsize in_size,
+						    gpointer out,
+						    gsize out_size,
+						    gpointer callback_data)
+{
+	gchar *headers = (gchar *)callback_data;
+	gchar **split_headers;
+	gint i = 0;
+	
+  	GnomeVFSModuleCallbackAdditionalHeadersOut *out_args = (GnomeVFSModuleCallbackAdditionalHeadersOut *) out;
+	
+	split_headers = g_strsplit (headers, "\r\n", -1);
+	while (split_headers[i] && split_headers[i][0] != '\0') {
+		gchar *h = g_strdup_printf ("%s\r\n", split_headers[i]);
+		out_args->headers = g_list_append (out_args->headers, h);
+		i++;
+	}
+
+	g_strfreev (split_headers);
+
+	return;
+}
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
+
 void
 rb_player_open (RBPlayer *mp,
 		const char *uri,
@@ -473,12 +510,36 @@
 	g_free (mp->priv->uri);
 	mp->priv->uri = NULL;
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	if (g_strncasecmp (uri, "daap://", 7) == 0) {
+		RBDAAPSource *source;
+		gchar *headers;
+		gchar *fudged_uri;
+		
+		source = rb_daap_source_find_for_uri (uri);
+		headers = rb_daap_source_get_headers (source, uri, 0);
+		gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); // get rid of it if we've already pushed it 
+		gnome_vfs_module_callback_push (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS, gnomevfssrc_send_additional_headers_cb, headers, g_free);
+
+		fudged_uri = g_strconcat ("http", uri + 4, NULL);
+		
+		g_object_set (G_OBJECT (mp->priv->playbin), "uri", fudged_uri, NULL);	
+		mp->priv->uri = g_strdup(uri);
+		g_free (fudged_uri);
+
+	} else {
+#else /* WITH_DAAP_BROWSE_SUPPORT */
 	if (uri == NULL) {
 		mp->priv->playing = FALSE;
 		return;
 	}
 	g_object_set (G_OBJECT (mp->priv->playbin), "uri", uri, NULL);	
 	mp->priv->uri = g_strdup (uri);
+#endif
+
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	}
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 
 	if (!rb_player_sync_pipeline (mp, error)) {
 		rb_player_close (mp, NULL);
@@ -495,6 +556,10 @@
 	g_free (mp->priv->uri);
 	mp->priv->uri = NULL;
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); // get rid of it if we've already pushed it 
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
+	
 	if (mp->priv->playbin == NULL)
 		return;
 
@@ -639,12 +704,27 @@
 
 	gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED);
 
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	if (g_strncasecmp (mp->priv->uri, "daap://", 7) == 0) {
+		RBDAAPSource *source;
+		gchar *headers;
+
+		gst_element_set_state (mp->priv->playbin, GST_STATE_READY);
+		
+		source = rb_daap_source_find_for_uri (mp->priv->uri);
+		headers = rb_daap_source_get_headers (source, mp->priv->uri, time);
+		gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); // get rid of it if we've already pushed it 
+		gnome_vfs_module_callback_push (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS, gnomevfssrc_send_additional_headers_cb, headers, g_free);
+	} else {
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 	gst_element_seek (mp->priv->playbin, 
 			  GST_FORMAT_TIME 
 			  | GST_SEEK_METHOD_SET 
 			  | GST_SEEK_FLAG_FLUSH, 
 			  time * GST_SECOND);
-
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	}
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 	if (mp->priv->playing)
 		gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
 }
diff -x CVS -Nurb rhythmbox/rhythmdb/rhythmdb.c myrhythmbox/rhythmdb/rhythmdb.c
--- rhythmbox/rhythmdb/rhythmdb.c	2005-07-30 01:42:57.000000000 -0400
+++ myrhythmbox/rhythmdb/rhythmdb.c	2005-07-31 03:03:08.000000000 -0400
@@ -1605,6 +1605,20 @@
 						  GNOME_VFS_FILE_INFO_FOLLOW_LINKS))
 	    == GNOME_VFS_OK)
 		return;
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	if (g_str_has_prefix (uri, "daap://")) {
+		GnomeVFSFileInfo *info = event->vfsinfo;
+
+		info->name = g_strdup(strrchr(uri,'/') + 1);
+		info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_FLAGS;
+
+		info->type = GNOME_VFS_FILE_TYPE_REGULAR;
+		info->flags = GNOME_VFS_FILE_FLAGS_NONE;
+		info->permissions = GNOME_VFS_PERM_USER_READ;
+		
+		return;
+	}
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 error:
 	unescaped = gnome_vfs_unescape_string_for_display (uri);
 	event->error = g_error_new (RHYTHMDB_ERROR,
diff -x CVS -Nurb rhythmbox/shell/Makefile.am myrhythmbox/shell/Makefile.am
--- rhythmbox/shell/Makefile.am	2005-06-19 14:30:57.000000000 -0400
+++ myrhythmbox/shell/Makefile.am	2005-07-31 04:10:49.053787860 -0400
@@ -116,6 +116,10 @@
 rhythmbox_LDADD += $(LIBNAUTILUS_BURN_LIBS)
 endif
 
+if USE_DAAP_BROWSE
+rhythmbox_LDADD += $(SOUP_LIBS)
+endif
+
 BUILT_SOURCES = $(tab_files)
 
 CLEANFILES = $(BUILT_SOURCES)
diff -x CVS -Nurb rhythmbox/shell/rb-shell.c myrhythmbox/shell/rb-shell.c
--- rhythmbox/shell/rb-shell.c	2005-07-31 04:25:40.000000000 -0400
+++ myrhythmbox/shell/rb-shell.c	2005-07-31 04:10:37.000000000 -0400
@@ -67,6 +67,9 @@
 #ifdef WITH_IPOD_SUPPORT
 #include "rb-ipod-source.h"
 #endif /* WITH_IPOD_SUPPORT */
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+#include "rb-daap-source.h"
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 #include "rb-load-failure-dialog.h"
 #include "rb-new-station-dialog.h"
 #include "rb-iradio-source.h"
@@ -259,12 +262,18 @@
 #ifdef WITH_IPOD_SUPPORT
 	rb_ipod_source_new,	
 #endif /* WITH_IPOD_SUPPORT */
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	rb_daap_sources_init,
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 	NULL,
 };
 
 typedef void (*SourceShutdownFunc)(RBShell *);
 
 static SourceShutdownFunc known_source_shutdowns[] = {
+#ifdef WITH_DAAP_BROWSE_SUPPORT
+	rb_daap_sources_shutdown,
+#endif /* WITH_DAAP_BROWSE_SUPPORT */
 	NULL,
 };
 
diff -x CVS -Nurb rhythmbox/sources/daap.c myrhythmbox/sources/daap.c
--- rhythmbox/sources/daap.c	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/sources/daap.c	2005-07-31 03:01:15.298505247 -0400
@@ -0,0 +1,2017 @@
+/*
+ *  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 "daap.h"
+#include <libgnome/gnome-i18n.h>
+#include "rb-debug.h"
+
+/* hashing - based on/copied from libopendaap
+ * Copyright (c) 2004 David Hammerton
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+typedef struct {
+    guint32 buf[4];
+    guint32 bits[2];
+    unsigned char in[64];
+    int apple_ver;
+} MD5_CTX;
+
+/*
+* This code implements the MD5 message-digest algorithm.
+* The algorithm is due to Ron Rivest.  This code was
+* written by Colin Plumb in 1993, no copyright is claimed.
+* This code is in the public domain; do with it what you wish.
+*
+* Equivalent code is available from RSA Data Security, Inc.
+* This code has been tested against that, and is equivalent,
+* except that you don't need to include two pages of legalese
+* with every copy.
+*
+* To compute the message digest of a chunk of bytes, declare an MD5Context
+* structure, pass it to OpenDaap_MD5Init, call OpenDaap_MD5Update as needed
+* on buffers full of bytes, and then call OpenDaap_MD5Final, which will fill
+* a supplied 16-byte array with the digest.
+*/
+static void MD5Transform(guint32 buf[4], guint32 const in[16], int apple_ver);
+/* for some reason we still have to reverse bytes on bigendian machines
+ * I don't really know why... but otherwise it fails..
+ * Any MD5 gurus out there know why???
+ */
+#if 0 //ndef WORDS_BIGENDIAN /* was: HIGHFIRST */
+#define byteReverse(buf, len)     /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+* Note: this code is harmless on little-endian machines.
+*/
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+     guint32 t;
+     do {
+          t = (guint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+               ((unsigned) buf[1] << 8 | buf[0]);
+          *(guint32 *) buf = t;
+          buf += 4;
+     } while (--longs);
+}
+#endif
+#endif
+
+
+static void OpenDaap_MD5Init(MD5_CTX *ctx, int apple_ver)
+{
+    memset(ctx, 0, sizeof(MD5_CTX));
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+
+    ctx->apple_ver = apple_ver;
+}
+
+static void OpenDaap_MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned int len)
+{
+    guint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
+        ctx->bits[1]++;          /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;     /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+        unsigned char *p = (unsigned char *) ctx->in + t;
+
+        t = 64 - t;
+        if (len < t) {
+            memcpy(p, buf, len);
+            return;
+        }
+        memcpy(p, buf, t);
+        byteReverse(ctx->in, 16);
+        MD5Transform(ctx->buf, (guint32 *) ctx->in, ctx->apple_ver);
+        buf += t;
+        len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+        memcpy(ctx->in, buf, 64);
+        byteReverse(ctx->in, 16);
+        MD5Transform(ctx->buf, (guint32 *) ctx->in, ctx->apple_ver);
+        buf += 64;
+        len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+
+}
+
+static void OpenDaap_MD5Final(MD5_CTX *ctx, unsigned char digest[16])
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+    always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+        /* Two lots of padding:  Pad the first block to 64 bytes */
+        memset(p, 0, count);
+        byteReverse(ctx->in, 16);
+        MD5Transform(ctx->buf, (guint32 *) ctx->in, ctx->apple_ver);
+
+        /* Now fill the next block with 56 bytes */
+        memset(ctx->in, 0, 56);
+    } else {
+        /* Pad block to 56 bytes */
+        memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((guint32 *) ctx->in)[14] = ctx->bits[0];
+    ((guint32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (guint32 *) ctx->in, ctx->apple_ver);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));     /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+* The core of the MD5 algorithm, this alters an existing MD5 hash to reflect
+* the addition of 16 longwords of new data.  OpenDaap_MD5Update blocks the
+* data and converts bytes into longwords for this routine.
+*/
+static void MD5Transform(guint32 buf[4], guint32 const in[16], int apple_ver)
+{
+    guint32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+
+    if (apple_ver == 1)
+    {
+        MD5STEP(F2, b, c, d, a, in[8] + 0x445a14ed, 20);
+    }
+    else
+    {
+        MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    }
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
+
+
+
+
+
+static int staticHashDone = 0;
+static char staticHash_42[256*65] = {0};
+static char staticHash_45[256*65] = {0};
+
+static const char hexchars[] = "0123456789ABCDEF";
+static const char appleCopyright[] = "Copyright 2003 Apple Computer, Inc.";
+
+static void DigestToString(const unsigned char *digest, char *string)
+{
+    int i;
+    for (i = 0; i < 16; i++)
+    {
+        unsigned char tmp = digest[i];
+        string[i*2+1] = hexchars[tmp & 0x0f];
+        string[i*2] = hexchars[(tmp >> 4) & 0x0f];
+    }
+}
+
+static void GenerateStatic_42()
+{
+    MD5_CTX ctx;
+    unsigned char *p = staticHash_42;
+    int i;
+    char buf[16];
+
+    for (i = 0; i < 256; i++)
+    {
+        OpenDaap_MD5Init(&ctx, 0);
+
+#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, str, strlen(str))
+
+        if ((i & 0x80) != 0)
+            MD5_STRUPDATE("Accept-Language");
+        else
+            MD5_STRUPDATE("user-agent");
+
+        if ((i & 0x40) != 0)
+            MD5_STRUPDATE("max-age");
+        else
+            MD5_STRUPDATE("Authorization");
+
+        if ((i & 0x20) != 0)
+            MD5_STRUPDATE("Client-DAAP-Version");
+        else
+            MD5_STRUPDATE("Accept-Encoding");
+
+        if ((i & 0x10) != 0)
+            MD5_STRUPDATE("daap.protocolversion");
+        else
+            MD5_STRUPDATE("daap.songartist");
+
+        if ((i & 0x08) != 0)
+            MD5_STRUPDATE("daap.songcomposer");
+        else
+            MD5_STRUPDATE("daap.songdatemodified");
+
+        if ((i & 0x04) != 0)
+            MD5_STRUPDATE("daap.songdiscnumber");
+        else
+            MD5_STRUPDATE("daap.songdisabled");
+
+        if ((i & 0x02) != 0)
+            MD5_STRUPDATE("playlist-item-spec");
+        else
+            MD5_STRUPDATE("revision-number");
+
+        if ((i & 0x01) != 0)
+            MD5_STRUPDATE("session-id");
+        else
+            MD5_STRUPDATE("content-codes");
+#undef MD5_STRUPDATE
+
+        OpenDaap_MD5Final(&ctx, buf);
+        DigestToString(buf, p);
+        p += 65;
+    }
+}
+
+static void GenerateStatic_45()
+{
+    MD5_CTX ctx;
+    unsigned char *p = staticHash_45;
+    int i;
+    char buf[16];
+
+    for (i = 0; i < 256; i++)
+    {
+        OpenDaap_MD5Init(&ctx, 1);
+
+#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, str, strlen(str))
+
+        if ((i & 0x40) != 0)
+            MD5_STRUPDATE("eqwsdxcqwesdc");
+        else
+            MD5_STRUPDATE("op[;lm,piojkmn");
+
+        if ((i & 0x20) != 0)
+            MD5_STRUPDATE("876trfvb 34rtgbvc");
+        else
+            MD5_STRUPDATE("=-0ol.,m3ewrdfv");
+
+        if ((i & 0x10) != 0)
+            MD5_STRUPDATE("87654323e4rgbv ");
+        else
+            MD5_STRUPDATE("1535753690868867974342659792");
+
+        if ((i & 0x08) != 0)
+            MD5_STRUPDATE("Song Name");
+        else
+            MD5_STRUPDATE("DAAP-CLIENT-ID:");
+
+        if ((i & 0x04) != 0)
+            MD5_STRUPDATE("111222333444555");
+        else
+            MD5_STRUPDATE("4089961010");
+
+        if ((i & 0x02) != 0)
+            MD5_STRUPDATE("playlist-item-spec");
+        else
+            MD5_STRUPDATE("revision-number");
+
+        if ((i & 0x01) != 0)
+            MD5_STRUPDATE("session-id");
+        else
+            MD5_STRUPDATE("content-codes");
+
+        if ((i & 0x80) != 0)
+            MD5_STRUPDATE("IUYHGFDCXWEDFGHN");
+        else
+            MD5_STRUPDATE("iuytgfdxwerfghjm");
+
+#undef MD5_STRUPDATE
+
+        OpenDaap_MD5Final(&ctx, buf);
+        DigestToString(buf, p);
+        p += 65;
+    }
+}
+
+static void 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 */
+
+
+/* parsing */
+/* parsing */
+typedef enum {
+	DAAP_CC_MDCL = 1,
+	DAAP_CC_MSTT,
+	DAAP_CC_MIID,
+	DAAP_CC_MINM,
+	DAAP_CC_MIKD,
+	DAAP_CC_MPER,
+	DAAP_CC_MCON,
+	DAAP_CC_MCTI,
+	DAAP_CC_MPCO,
+	DAAP_CC_MSTS, // 10
+	DAAP_CC_MIMC,
+	DAAP_CC_MCTC,
+	DAAP_CC_MRCO,
+	DAAP_CC_MTCO,
+	DAAP_CC_MLCL,
+	DAAP_CC_MLIT,
+	DAAP_CC_MBCL,
+	DAAP_CC_MSRV, 
+	DAAP_CC_MSAU,
+	DAAP_CC_MSLR, // 20
+	DAAP_CC_MPRO, 
+	DAAP_CC_APRO,
+	DAAP_CC_MSAL,
+	DAAP_CC_MSUP,
+	DAAP_CC_MSPI,
+	DAAP_CC_MSEX,
+	DAAP_CC_MSBR,
+	DAAP_CC_MSQY,
+	DAAP_CC_MSIX,
+	DAAP_CC_MSRS, // 30
+	DAAP_CC_MSTM, 
+	DAAP_CC_MSDC,
+	DAAP_CC_MCCR,
+	DAAP_CC_MCNM,
+	DAAP_CC_MCNA,
+	DAAP_CC_MCTY,
+	DAAP_CC_MLOG,
+	DAAP_CC_MLID,
+	DAAP_CC_MUPD,
+	DAAP_CC_MUSR, // 40
+	DAAP_CC_MUTY, 
+	DAAP_CC_MUDL,
+	DAAP_CC_AVDB,
+	DAAP_CC_ABRO,
+	DAAP_CC_ABAL,
+	DAAP_CC_ABAR,
+	DAAP_CC_ABCP,
+	DAAP_CC_ABGN,
+	DAAP_CC_ADBS,
+	DAAP_CC_ASAL, // 50
+	DAAP_CC_ASAR, 
+	DAAP_CC_ASBT,
+	DAAP_CC_ASBR,
+	DAAP_CC_ASCM,
+	DAAP_CC_ASCO,
+	DAAP_CC_ASDA,
+	DAAP_CC_ASDM,
+	DAAP_CC_ASDC,
+	DAAP_CC_ASDN,
+	DAAP_CC_ASDB, // 60
+	DAAP_CC_ASEQ,
+	DAAP_CC_ASFM,
+	DAAP_CC_ASGN,
+	DAAP_CC_ASDT,
+	DAAP_CC_ASRV,
+	DAAP_CC_ASSR,
+	DAAP_CC_ASSZ,
+	DAAP_CC_ASST,
+	DAAP_CC_ASSP,
+	DAAP_CC_ASTM, // 70
+	DAAP_CC_ASTC, 
+	DAAP_CC_ASTN,
+	DAAP_CC_ASUR,
+	DAAP_CC_ASYR,
+	DAAP_CC_ASDK,
+	DAAP_CC_ASUL,
+	DAAP_CC_APLY,
+	DAAP_CC_ABPL,
+	DAAP_CC_APSO,
+	DAAP_CC_PRSV, // 80
+	DAAP_CC_ARIF, 
+	DAAP_CC_AESV,
+	DAAP_CC_MSAS
+} DAAPContentCode;
+
+typedef enum {
+	DAAP_TYPE_BYTE = 1,
+	DAAP_TYPE_UBYTE,
+	DAAP_TYPE_SHORT,
+	DAAP_TYPE_USHORT,
+	DAAP_TYPE_INT,
+	DAAP_TYPE_UINT,
+	DAAP_TYPE_LONG,
+	DAAP_TYPE_ULONG,
+	DAAP_TYPE_STRING,
+	DAAP_TYPE_DATE,
+	DAAP_TYPE_VERSION,
+	DAAP_TYPE_CONTAINER
+} DAAPType;
+
+static const gchar * daap_content_code_string    (DAAPContentCode code);
+static DAAPType      daap_content_code_daap_type (DAAPContentCode code);
+
+typedef struct _DAAPItem DAAPItem;
+
+struct _DAAPItem {
+	DAAPContentCode content_code;
+	GValue content;
+};
+
+static GNode * daap_structure_parse (const gchar *buf, gint buf_length);
+static DAAPItem * daap_structure_find_item (GNode *structure, DAAPContentCode code);
+static GNode * daap_structure_find_node (GNode *structure, DAAPContentCode code);
+static void daap_structure_destroy (GNode *structure);
+//#define daap_structure_item_name(i) (daap_content_code_string (i->content_code))
+/* this leaks because i dont care enough to do it right, its only a debugging function */
+//#define daap_structure_item_value(i) (g_strdup_value_contents (&(i->content)))
+
+
+#define MAKE_CONTENT_CODE(ch0, ch1, ch2, ch3) \
+    (( (gint32)(gchar)(ch0) | ( (gint32)(gchar)(ch1) << 8 ) | \
+    ( (gint32)(gchar)(ch2) << 16 ) | \
+    ( (gint32)(gchar)(ch3) << 24 ) ))
+
+#define SPLIT_CONTENT_CODE(code) \
+    (gchar)code, (gchar)((gint32)code >> 8), \
+    (gchar)((gint32)code >> 16), \
+    (gchar)((gint32)code >> 24)
+
+
+#ifdef WORDS_BIGENDIAN
+
+#define __Swap16(v) (v)
+#define __Swap32(v) (v)
+#define __Swap64(v) (v)
+
+#else /* WORDS_BIGENDIAN */
+
+#define __Swap16(v) ((((v) & 0x00FF) << 0x08) | \
+                     (((v) & 0xFF00) >> 0x08))
+#define __Swap32(v) ((((v) & 0x000000FF) << 0x18) | \
+                     (((v) & 0x0000FF00) << 0x08) | \
+                     (((v) & 0x00FF0000) >> 0x08) | \
+                     (((v) & 0xFF000000) >> 0x18))
+
+#define __Swap64(v) ((((v) & 0x00000000000000FFLL) >> 0x38) | \
+                     (((v) & 0x000000000000FF00LL) >> 0x28) | \
+                     (((v) & 0x0000000000FF0000LL) >> 0x18) | \
+                     (((v) & 0x00000000FF000000LL) >> 0x08) | \
+                     (((v) & 0x000000FF00000000LL) << 0x08) | \
+                     (((v) & 0x0000FF0000000000LL) << 0x18) | \
+                     (((v) & 0x00FF000000000000LL) << 0x28) | \
+                     (((v) & 0xFF00000000000000LL) << 0x38))
+
+#endif /* WORDS_BIGENDIAN */
+
+
+
+static GHashTable *content_codes = NULL;
+
+void daap_content_codes_init (void)
+{
+	gint32 k;
+	gint32 v;
+
+	content_codes = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+	k = MAKE_CONTENT_CODE ('m', 'd', 'c', 'l');
+	v = DAAP_CC_MDCL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 't', 't');
+	v = DAAP_CC_MSTT;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'i', 'i', 'd');
+	v = DAAP_CC_MIID;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'i', 'n', 'm');
+	v = DAAP_CC_MINM;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'i', 'k', 'd');
+	v = DAAP_CC_MIKD;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'p', 'e', 'r');
+	v = DAAP_CC_MPER;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'c', 'o', 'n');
+	v = DAAP_CC_MCON;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'c', 't', 'i');
+	v = DAAP_CC_MCTI;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'p', 'c', 'o');
+	v = DAAP_CC_MPCO;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 't', 's');
+	v = DAAP_CC_MSTS;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'i', 'm', 'c');
+	v = DAAP_CC_MIMC;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'c', 't', 'c');
+	v = DAAP_CC_MCTC;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'r', 'c', 'o');
+	v = DAAP_CC_MRCO;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 't', 'c', 'o');
+	v = DAAP_CC_MTCO;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'l', 'c', 'l');
+	v = DAAP_CC_MLCL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'l', 'i', 't');
+	v = DAAP_CC_MLIT;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'b', 'c', 'l');
+	v = DAAP_CC_MBCL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'r', 'v');
+	v = DAAP_CC_MSRV;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'a', 'u');
+	v = DAAP_CC_MSAU;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'l', 'r');
+	v = DAAP_CC_MSLR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'p', 'r', 'o');
+	v = DAAP_CC_MPRO;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'p', 'r', 'o');
+	v = DAAP_CC_APRO;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'a', 'l');
+	v = DAAP_CC_MSAL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'u', 'p');
+	v = DAAP_CC_MSUP;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'p', 'i');
+	v = DAAP_CC_MSPI;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'e', 'x');
+	v = DAAP_CC_MSEX;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'b', 'r');
+	v = DAAP_CC_MSBR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'q', 'y');
+	v = DAAP_CC_MSQY;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'i', 'x');
+	v = DAAP_CC_MSIX;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'r', 's');
+	v = DAAP_CC_MSRS;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 't', 'm');
+	v = DAAP_CC_MSTM;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 's', 'd', 'c');
+	v = DAAP_CC_MSDC;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'c', 'c', 'r');
+	v = DAAP_CC_MCCR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'c', 'n', 'm');
+	v = DAAP_CC_MCNM;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'c', 'n', 'a');
+	v = DAAP_CC_MCNA;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'c', 't', 'y');
+	v = DAAP_CC_MCTY;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'l', 'o', 'g');
+	v = DAAP_CC_MLOG;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'l', 'i', 'd');
+	v = DAAP_CC_MLID;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'u', 'p', 'd');
+	v = DAAP_CC_MUPD;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'u', 's', 'r');
+	v = DAAP_CC_MUSR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'u', 't', 'y');
+	v = DAAP_CC_MUTY;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('m', 'u', 'd', 'l');
+	v = DAAP_CC_MUDL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'v', 'd', 'b');
+	v = DAAP_CC_AVDB;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'b', 'r', 'o');
+	v = DAAP_CC_ABRO;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'b', 'a', 'l');
+	v = DAAP_CC_ABAL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'b', 'a', 'r');
+	v = DAAP_CC_ABAR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'b', 'c', 'p');
+	v = DAAP_CC_ABCP;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'b', 'g', 'n');
+	v = DAAP_CC_ABGN;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'd', 'b', 's');
+	v = DAAP_CC_ADBS;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'a', 'l');
+	v = DAAP_CC_ASAL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'a', 'r');
+	v = DAAP_CC_ASAR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'b', 't');
+	v = DAAP_CC_ASBT;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'b', 'r');
+	v = DAAP_CC_ASBR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'c', 'm');
+	v = DAAP_CC_ASCM;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'c', 'o');
+	v = DAAP_CC_ASCO;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'd', 'a');
+	v = DAAP_CC_ASDA;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'd', 'm');
+	v = DAAP_CC_ASDM;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'd', 'c');
+	v = DAAP_CC_ASDC;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'd', 'n');
+	v = DAAP_CC_ASDN;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'd', 'b');
+	v = DAAP_CC_ASDB;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'e', 'q');
+	v = DAAP_CC_ASEQ;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'f', 'm');
+	v = DAAP_CC_ASFM;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'g', 'n');
+	v = DAAP_CC_ASGN;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'd', 't');
+	v = DAAP_CC_ASDT;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'r', 'v');
+	v = DAAP_CC_ASRV;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 's', 'r');
+	v = DAAP_CC_ASSR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 's', 'z');
+	v = DAAP_CC_ASSZ;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 's', 't');
+	v = DAAP_CC_ASST;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 's', 'p');
+	v = DAAP_CC_ASSP;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 't', 'm');
+	v = DAAP_CC_ASTM;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 't', 'c');
+	v = DAAP_CC_ASTC;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 't', 'n');
+	v = DAAP_CC_ASTN;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'u', 'r');
+	v = DAAP_CC_ASUR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'y', 'r');
+	v = DAAP_CC_ASYR;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'd', 'k');
+	v = DAAP_CC_ASDK;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 's', 'u', 'l');
+	v = DAAP_CC_ASUL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'p', 'l', 'y');
+	v = DAAP_CC_APLY;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'b', 'p', 'l');
+	v = DAAP_CC_ABPL;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'p', 's', 'o');
+	v = DAAP_CC_APSO;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('p', 'r', 's', 'v');
+	v = DAAP_CC_PRSV;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'r', 'i', 'f');
+	v = DAAP_CC_ARIF;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+	k = MAKE_CONTENT_CODE ('a', 'e', 'S', 'V');
+	v = DAAP_CC_AESV;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+
+	k = MAKE_CONTENT_CODE ('m', 's', 'a', 's');
+	v = DAAP_CC_MSAS;
+	g_hash_table_insert (content_codes, GINT_TO_POINTER (k), GINT_TO_POINTER (v));
+
+	return;
+}
+	
+
+void daap_content_codes_shutdown (void)
+{
+	g_hash_table_destroy (content_codes);
+	content_codes = NULL;
+}
+
+static const gchar * daap_content_code_string (DAAPContentCode code)
+{
+	switch (code) {
+		case DAAP_CC_MDCL:
+			return "dmap.dictionary";
+		case DAAP_CC_MSTT:
+			return "dmap.status";
+		case DAAP_CC_MIID:
+			return "dmap.itemid";
+		case DAAP_CC_MINM:
+			return "dmap.itemname";
+		case DAAP_CC_MIKD:
+			return "dmap.itemkind";
+		case DAAP_CC_MPER:
+			return "dmap.persistentid";
+		case DAAP_CC_MCON:
+			return "dmap.container";
+		case DAAP_CC_MCTI:
+			return "dmap.containeritemid";
+		case DAAP_CC_MPCO:
+			return "dmap.parentcontainerid";
+		case DAAP_CC_MSTS:
+			return "dmap.statusstring";
+		case DAAP_CC_MIMC:
+			return "dmap.itemcount";
+		case DAAP_CC_MCTC:
+			return "dmap.containercount";
+		case DAAP_CC_MRCO:
+			return "dmap.returnedcount";
+		case DAAP_CC_MTCO:
+			return "dmap.specifiedtotalcount";
+		case DAAP_CC_MLCL:
+			return "dmap.listing";
+		case DAAP_CC_MLIT:
+			return "dmap.listingitem";
+		case DAAP_CC_MBCL:
+			return "dmap.bag";
+		case DAAP_CC_MSRV:
+			return "dmap.serverinforesponse";
+		case DAAP_CC_MSAU:
+			return "dmap.authenticationmethod";
+		case DAAP_CC_MSLR:
+			return "dmap.loginrequired";
+		case DAAP_CC_MPRO:
+			return "dmap.protocolversion";
+		case DAAP_CC_APRO:
+			return "daap.protocolversion";
+		case DAAP_CC_MSAL:
+			return "dmap.supportsautologout";
+		case DAAP_CC_MSUP:
+			return "dmap.supportsupdate";
+		case DAAP_CC_MSPI:
+			return "dmap.supportspersistenids";
+		case DAAP_CC_MSEX:
+			return "dmap.supportsextensions";
+		case DAAP_CC_MSBR:
+			return "dmap.supportsbrowse";
+		case DAAP_CC_MSQY:
+			return "dmap.supportsquery";
+		case DAAP_CC_MSIX:
+			return "dmap.supportsindex";
+		case DAAP_CC_MSRS:
+			return "dmap.supportsresolve";
+		case DAAP_CC_MSTM:
+			return "dmap.timeoutinterval";
+		case DAAP_CC_MSDC:
+			return "dmap.databasescount";
+		case DAAP_CC_MCCR:
+			return "dmap.contentcodesresponse";
+		case DAAP_CC_MCNM:
+			return "dmap.contentcodesnumber";
+		case DAAP_CC_MCNA:
+			return "dmap.contentcodesname";
+		case DAAP_CC_MCTY:
+			return "dmap.contentcodestype";
+		case DAAP_CC_MLOG:
+			return "dmap.loginresponse";
+		case DAAP_CC_MLID:
+			return "dmap.sessionid";
+		case DAAP_CC_MUPD:
+			return "dmap.updateresponse";
+		case DAAP_CC_MUSR:
+			return "dmap.serverrevision";
+		case DAAP_CC_MUTY:
+			return "dmap.updatetype";
+		case DAAP_CC_MUDL:
+			return "dmap.deletedidlisting";
+		case DAAP_CC_AVDB:
+			return "daap.serverdatabases";
+		case DAAP_CC_ABRO:
+			return "daap.databasebrowse";
+		case DAAP_CC_ABAL:
+			return "daap.browsealbumlisting";
+		case DAAP_CC_ABAR:
+			return "daap.browseartistlisting";
+		case DAAP_CC_ABCP:
+			return "daap.browsecomposerlisting";
+		case DAAP_CC_ABGN:
+			return "daap.browsegenrelisting";
+		case DAAP_CC_ADBS:
+			return "daap.databasesongs";
+		case DAAP_CC_ASAL:
+			return "daap.songalbum";
+		case DAAP_CC_ASAR:
+			return "daap.songartist";
+		case DAAP_CC_ASBT:
+			return "daap.songsbeatsperminute";
+		case DAAP_CC_ASBR:
+			return "daap.songbitrate";
+		case DAAP_CC_ASCM:
+			return "daap.songcomment";
+		case DAAP_CC_ASCO:
+			return "daap.songcompliation";
+		case DAAP_CC_ASDA:
+			return "daap.songdateadded";
+		case DAAP_CC_ASDM:
+			return "daap.songdatemodified";
+		case DAAP_CC_ASDC:
+			return "daap.songdisccount";
+		case DAAP_CC_ASDN:
+			return "daap.songdiscnumber";
+		case DAAP_CC_ASDB:
+			return "daap.songdisabled";
+		case DAAP_CC_ASEQ:
+			return "daap.songeqpreset";
+		case DAAP_CC_ASFM:
+			return "daap.songformat";
+		case DAAP_CC_ASGN:
+			return "daap.songgenre";
+		case DAAP_CC_ASDT:
+			return "daap.songdescription";
+		case DAAP_CC_ASRV:
+			return "daap.songrelativevolume";
+		case DAAP_CC_ASSR:
+			return "daap.songsamplerate";
+		case DAAP_CC_ASSZ:
+			return "daap.songsize";
+		case DAAP_CC_ASST:
+			return "daap.songstarttime";
+		case DAAP_CC_ASSP:
+			return "daap.songstoptime";
+		case DAAP_CC_ASTM:
+			return "daap.songtime";
+		case DAAP_CC_ASTC:
+			return "daap.songtrackcount";
+		case DAAP_CC_ASTN:
+			return "daap.songtracknumber";
+		case DAAP_CC_ASUR:
+			return "daap.songuserrating";
+		case DAAP_CC_ASYR:
+			return "daap.songyear";
+		case DAAP_CC_ASDK:
+			return "daap.songdatakind";
+		case DAAP_CC_ASUL:
+			return "daap.songdataurl";
+		case DAAP_CC_APLY:
+			return "daap.databaseplaylists";
+		case DAAP_CC_ABPL:
+			return "daap.baseplaylist";
+		case DAAP_CC_APSO:
+			return "daap.playlistsongs";
+		case DAAP_CC_PRSV:
+			return "daap.resolve";
+		case DAAP_CC_ARIF:
+			return "daap.resolveinfo";
+		case DAAP_CC_AESV:
+			return "com.applie.itunes.music-sharing-version";
+		case DAAP_CC_MSAS:
+			return "daap.authentication.schemes";
+		default:
+			rb_debug ("Invalid DaapContentCode: %d\n", code);
+			return NULL;
+	}
+}
+
+
+static DAAPType daap_content_code_daap_type (DAAPContentCode code)
+{
+	switch (code) {
+		case DAAP_CC_MSTT:
+		case DAAP_CC_MIID:
+		case DAAP_CC_MCTI:
+		case DAAP_CC_MPCO:
+		case DAAP_CC_MIMC:
+		case DAAP_CC_MCTC:
+		case DAAP_CC_MRCO:
+		case DAAP_CC_MTCO:
+		case DAAP_CC_MSTM:
+		case DAAP_CC_MSDC:
+		case DAAP_CC_MCNM:
+		case DAAP_CC_MLID:
+		case DAAP_CC_MUSR:
+		case DAAP_CC_ASSR:
+		case DAAP_CC_ASSZ:
+		case DAAP_CC_ASST:
+		case DAAP_CC_ASSP:
+		case DAAP_CC_ASTM:
+		case DAAP_CC_AESV:
+			return DAAP_TYPE_INT;
+		case DAAP_CC_MIKD:
+		case DAAP_CC_MSAU:
+		case DAAP_CC_MSLR:
+		case DAAP_CC_MSAL:
+		case DAAP_CC_MSUP:
+		case DAAP_CC_MSPI:
+		case DAAP_CC_MSEX:
+		case DAAP_CC_MSBR:
+		case DAAP_CC_MSQY:
+		case DAAP_CC_MSIX:
+		case DAAP_CC_MSRS:
+		case DAAP_CC_MUTY:
+		case DAAP_CC_ASCO:
+		case DAAP_CC_ASDB:
+		case DAAP_CC_ASRV:
+		case DAAP_CC_ASUR:
+		case DAAP_CC_ASDK:
+		case DAAP_CC_ABPL:
+		case DAAP_CC_MSAS:
+			return DAAP_TYPE_BYTE;
+		case DAAP_CC_MCTY:
+		case DAAP_CC_ASBT:
+		case DAAP_CC_ASBR:
+		case DAAP_CC_ASDC:
+		case DAAP_CC_ASDN:
+		case DAAP_CC_ASTC:
+		case DAAP_CC_ASTN:
+		case DAAP_CC_ASYR:
+			return DAAP_TYPE_SHORT;
+		case DAAP_CC_MPER: 
+			return DAAP_TYPE_LONG;
+		case DAAP_CC_MPRO:
+		case DAAP_CC_APRO:
+			return DAAP_TYPE_VERSION;
+		case DAAP_CC_MINM:
+		case DAAP_CC_MSTS:
+		case DAAP_CC_MCNA:
+		case DAAP_CC_ASAL:
+		case DAAP_CC_ASAR:
+		case DAAP_CC_ASCM:
+		case DAAP_CC_ASEQ:
+		case DAAP_CC_ASFM:
+		case DAAP_CC_ASGN:
+		case DAAP_CC_ASDT:
+		case DAAP_CC_ASUL:
+			return DAAP_TYPE_STRING;
+		case DAAP_CC_ASDA:
+		case DAAP_CC_ASDM:
+			return DAAP_TYPE_DATE;
+		case DAAP_CC_MDCL:
+		case DAAP_CC_MCON:
+		case DAAP_CC_MLCL:
+		case DAAP_CC_MLIT:
+		case DAAP_CC_MBCL:
+		case DAAP_CC_MSRV:
+		case DAAP_CC_MCCR:
+		case DAAP_CC_MLOG:
+		case DAAP_CC_MUPD:
+		case DAAP_CC_MUDL:
+		case DAAP_CC_AVDB:
+		case DAAP_CC_ABRO:
+		case DAAP_CC_ABAL:
+		case DAAP_CC_ABAR:
+		case DAAP_CC_ABCP:
+		case DAAP_CC_ABGN:
+		case DAAP_CC_ADBS:
+		case DAAP_CC_APLY:
+		case DAAP_CC_APSO:
+		case DAAP_CC_PRSV:
+		case DAAP_CC_ARIF:
+			return DAAP_TYPE_CONTAINER;
+		default:
+			rb_debug ("Invalid DaapContentCode: %d\n", code);
+			return -1;
+	}
+}
+
+static DAAPContentCode daap_buffer_read_content_code (const gchar *buf)
+{
+	gint32 c = MAKE_CONTENT_CODE (buf[0], buf[1], buf[2], buf[3]);
+
+	return GPOINTER_TO_INT (g_hash_table_lookup (content_codes, GINT_TO_POINTER (c)));
+}
+
+static gint8 daap_buffer_read_int8(const gchar *buf)
+{
+	return *(gint8*)buf;
+}
+
+static gint16 daap_buffer_read_int16(const gchar *buf)
+{
+	return __Swap16(*(gint16*)buf);
+}
+
+static gint32 daap_buffer_read_int32(const gchar *buf)
+{
+	return __Swap32(*(gint32*)buf);
+}
+
+static gint64 daap_buffer_read_int64(const gchar *buf)
+{
+	return __Swap64(*(gint64*)buf);
+}
+
+static gchar * daap_buffer_read_string (const gchar *buf, gint size)
+{
+	return g_strndup (buf, size);
+}
+
+
+static void daap_structure_parse_container (GNode *parent, const gchar *buf, gint buf_length)
+{
+	gint l = 0;
+
+	while (l < buf_length) {
+		gint codesize;
+		DAAPItem *item = g_new0 (DAAPItem, 1);
+		GNode *node = g_node_new (item);
+
+		g_node_append (parent, node);
+
+		item->content_code = daap_buffer_read_content_code (&(buf[l]));
+		l += 4;
+
+		codesize = daap_buffer_read_int32(&(buf[l]));
+		l += 4;
+// FIXME USE THE G_TYPE CONVERTOR FUNCTION daap_type_to_gtype
+		switch (daap_content_code_daap_type (item->content_code)) {
+			case DAAP_TYPE_BYTE:
+			case DAAP_TYPE_UBYTE: {
+				gboolean b = daap_buffer_read_int8(&(buf[l]));
+				
+				g_value_init (&(item->content), G_TYPE_BOOLEAN);
+				g_value_set_boolean (&(item->content), b);
+//				g_print ("Code: %s, content: %s\n", daap_content_code_string (item->content_code),(b ? "TRUE" : "FALSE"));
+
+				break;
+			}
+			case DAAP_TYPE_SHORT:
+			case DAAP_TYPE_USHORT: {
+				gshort s = daap_buffer_read_int16(&(buf[l]));
+
+				g_value_init (&(item->content), G_TYPE_INT);
+				g_value_set_int (&(item->content),(gint)s);
+//				g_print ("Code: %s, content: %d\n", daap_content_code_string (item->content_code),(gint)s);
+
+				break;
+			}
+			case DAAP_TYPE_INT:
+			case DAAP_TYPE_UINT: {
+				gint32 i = daap_buffer_read_int32(&(buf[l]));
+				
+				g_value_init (&(item->content), G_TYPE_INT);
+				g_value_set_int (&(item->content), i);
+//				g_print ("Code: %s, content: %d\n", daap_content_code_string (item->content_code), i);
+
+				break;
+			}
+			case DAAP_TYPE_LONG:
+			case DAAP_TYPE_ULONG: {
+				gint64 l = daap_buffer_read_int64(&(buf[l]));
+	
+				g_value_init (&(item->content), G_TYPE_INT64);
+				g_value_set_int64 (&(item->content), l);
+//				g_print ("Code: %s, content: %lld\n", daap_content_code_string (item->content_code), l);
+
+				break;
+			}
+			case DAAP_TYPE_STRING: {
+				gchar *s = daap_buffer_read_string (&(buf[l]), codesize);
+
+				g_value_init (&(item->content), G_TYPE_STRING);
+				g_value_take_string (&(item->content), s);
+//				g_print ("Code: %s, content: %s\n", daap_content_code_string (item->content_code), s);
+
+				break;
+			}
+			case DAAP_TYPE_DATE:
+				g_print ("Code: %s, date variable type\n", daap_content_code_string (item->content_code));
+
+				break;
+			case DAAP_TYPE_VERSION: {
+				gint16 major = daap_buffer_read_int16(&buf[l]);
+				gint16 minor = daap_buffer_read_int16(&buf[l] + 2);
+				gfloat v = 0;
+
+
+				v = (gfloat)major;
+				v += (gfloat)(minor * 0.1);
+				
+				g_value_init (&(item->content), G_TYPE_FLOAT);
+				g_value_set_float (&(item->content), v);
+//				g_print ("Code: %s, content: %f\n", daap_content_code_string (item->content_code), v);
+
+				break;
+			}
+			case DAAP_TYPE_CONTAINER: {
+//				g_print ("Code: %s, container\n", daap_content_code_string (item->content_code));
+	
+				daap_structure_parse_container (node,&buf[l], codesize);
+				break;
+			}
+		}
+
+		l += codesize;
+	}
+
+	return;
+}
+
+static GNode * daap_structure_parse (const gchar *buf, gint buf_length)
+{
+	GNode *root = NULL;
+	GNode *child = NULL;
+
+	root = g_node_new (NULL);
+
+	daap_structure_parse_container (root, buf, buf_length);
+
+	child = root->children;
+	if (child) {
+		g_node_unlink (child);
+	}
+	g_node_destroy (root);
+	
+	return child;
+}
+
+struct NodeFinder {
+	DAAPContentCode code;
+	GNode *node;
+};
+
+static gboolean gnode_find_node (GNode *node, gpointer data)
+{
+	struct NodeFinder *finder = (struct NodeFinder *)data;
+	DAAPItem *item = node->data;
+
+	if (item->content_code == finder->code) {
+		finder->node = node;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static DAAPItem * daap_structure_find_item (GNode *structure, DAAPContentCode code)
+{
+	GNode *node = NULL;
+	
+	node = daap_structure_find_node (structure, code);
+
+	if (node) {
+		return node->data;
+	}
+
+	return NULL;
+}
+
+static GNode * daap_structure_find_node (GNode *structure, DAAPContentCode 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 daap_item_free (DAAPItem *item)
+{
+	if (daap_content_code_daap_type (item->content_code) != DAAP_TYPE_CONTAINER) {
+		g_value_unset (&(item->content));
+	}
+	g_free (item);
+
+	return;
+}
+/*
+static gboolean gnode_print_daap_item (GNode *node, gpointer data)
+{
+	gint i;
+
+	for (i = 0; i < g_node_depth (node); i++) {
+		g_print ("\t");
+	}
+
+	g_print ("%s = %s\n", daap_structure_item_name (((DAAPItem *)node->data)), daap_structure_item_value (((DAAPItem *)node->data)));
+
+	return FALSE;
+}
+
+static void daap_structure_print (GNode *structure)
+{
+	if (structure) {
+		g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL,-1, gnode_print_daap_item, NULL);
+	}
+
+	return;
+}
+*/
+static gboolean gnode_free_daap_item (GNode *node, gpointer data)
+{
+	daap_item_free ((DAAPItem *)node->data);
+
+	return FALSE;
+}
+
+static void daap_structure_destroy (GNode *structure)
+{
+	if (structure) {
+		g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1, gnode_free_daap_item, NULL);
+
+		g_node_destroy (structure);
+
+		structure = NULL;
+	}
+
+	return;
+}
+/* end parsing */
+
+
+/* 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 DAAP_USER_AGENT "iTunes/4.6 (Windows; N)"
+
+struct _DAAPConnection {
+	SoupSession *session;
+	SoupUri *base_uri;
+	
+	gfloat 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 (DAAPConnection *connection, const gchar *path, gboolean need_hash, gfloat 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 *nodaap_path = (gchar *)path;
+		
+		if (g_strncasecmp (path, "daap://", 7) == 0) {
+			nodaap_path = strstr (path, "/data");
+		}
+
+		daap_hash_generate ((short)floorf (version), nodaap_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 (DAAPConnection *connection, const gchar *path, gboolean need_hash, gfloat version, gint req_id, gboolean send_close, GNode **structure)
+{
+	SoupMessage *message;
+	gint res;
+	DAAPItem *item = NULL;
+	
+	message = build_message (connection, path, need_hash, version, req_id, send_close);
+	if (message == NULL) {
+		g_print("Error building message\n");
+		return FALSE;
+	}
+	
+	soup_session_send_message (connection->session, message);
+	
+	if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code) == FALSE) {
+		g_object_unref (message);
+		g_print("error getting %s: %d, %s\n",soup_uri_to_string(soup_message_get_uri(message),FALSE),message->status_code,message->reason_phrase);
+		return FALSE;
+	} 
+
+	*structure = daap_structure_parse (message->response.body, message->response.length);
+	g_object_unref (message);
+	
+	if (*structure == NULL) {
+		g_warning ("No daap structure returned: '%s'", path);
+		return FALSE;
+	}
+
+	item = daap_structure_find_item (*structure, DAAP_CC_MSTT);
+	if (item == NULL) {
+		g_warning ("Error, could not find dmap.status in server-info response: '%s'", path);
+		return FALSE;
+	}
+
+	if (g_value_get_int (&(item->content)) != 200) {
+		g_warning ("Error, dmap.status is not 200: '%s'", path);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+	
+static void 
+entry_set_string_prop (RhythmDB *db, RhythmDBEntry *entry,
+		       RhythmDBPropType propid, const char *str)
+{
+	GValue value = {0,};
+	gchar *tmp;
+
+	if (str == NULL) {
+		tmp = g_strdup (_("Unknown"));
+	} else {
+		tmp = g_strdup (str);
+	}
+
+	g_value_init (&value, G_TYPE_STRING);
+	g_value_set_string_take_ownership (&value, tmp);
+	rhythmdb_entry_set (RHYTHMDB (db), entry, propid, &value);
+	g_value_unset (&value);
+}
+
+DAAPConnection * daap_connection_new (const gchar *host, gint port, RhythmDB *db, RhythmDBEntryType type)
+{
+	DAAPConnection *connection;
+ 	GNode *structure = NULL;
+	gboolean ret;
+	DAAPItem *item = NULL;
+	gchar *path = NULL;
+	gint n_databases;
+	GNode *listing_node = NULL;
+	gint i;
+	GNode *n;
+	gint returned_count;
+	gint specified_total_count;
+	gboolean update_type;
+
+	connection = g_new0 (DAAPConnection, 1);
+
+	connection->session = soup_session_sync_new ();
+	path = g_strdup_printf ("http://%s:%d/";, host, port);
+	connection->base_uri = soup_uri_new (path);
+	if (connection->base_uri == NULL) {
+		g_print ("Error parsing uri\n");
+		g_free (path);
+		goto connection_new_cleanup;
+	}
+	
+	
+	/* get the version number */
+	ret = http_get (connection,"/server-info", FALSE, 0.0, 0, FALSE, &structure);
+
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = daap_structure_find_item (structure, DAAP_CC_APRO);
+	if (item == NULL) {
+		g_print ("Error, could not find daap.protocolversion in server-info response\n");
+		goto connection_new_cleanup;
+	}
+
+	connection->daap_version = g_value_get_float (&(item->content));
+
+	daap_structure_destroy (structure);
+	structure = NULL;
+
+
+	/* get a session id */
+	ret = http_get (connection,"/login", FALSE, 0.0, 0, FALSE, &structure);
+
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = daap_structure_find_item (structure, DAAP_CC_MLID);
+	if (item == NULL) {
+		g_print ("Error, could not find daap.sessionid in login response\n");
+		goto connection_new_cleanup;
+	}
+
+	connection->session_id = g_value_get_int (&(item->content));
+
+	daap_structure_destroy (structure);
+	structure = NULL;
+
+
+	/* get a revision number */
+	path = g_strdup_printf ("/update?session-id=%d&revision-number=1", connection->session_id);
+	ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+	g_free (path);
+
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = daap_structure_find_item (structure, DAAP_CC_MUSR);
+	if (item == NULL) {
+		g_print ("Error, could not find daap.serverrevision in update response\n");
+		goto connection_new_cleanup;
+	}
+
+	connection->revision_number = g_value_get_int (&(item->content));
+
+	daap_structure_destroy (structure);
+	structure = NULL;
+
+	/* get database id */
+	/* get a list of databases, there should be only 1 */
+	path = g_strdup_printf ("/databases?session-id=%d&revision-number=%d", connection->session_id, connection->revision_number);
+	ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+	g_free (path);
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = daap_structure_find_item (structure, DAAP_CC_MRCO);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.returnedcount in databases response\n");
+		goto connection_new_cleanup;
+	}
+
+	n_databases = g_value_get_int (&(item->content));
+	if (n_databases != 1) {
+		g_print ("Host seems to have more than 1 database, how strange\n");
+	}
+
+	listing_node = daap_structure_find_node (structure, DAAP_CC_MLCL);
+	if (listing_node == NULL) {
+		g_print ("Error, could not find dmap.listing in databases response\n");
+		goto connection_new_cleanup;
+	}
+
+	item = daap_structure_find_item (listing_node->children, DAAP_CC_MIID);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.itemid in databases response\n");
+		goto connection_new_cleanup;
+	}
+	
+	connection->database_id = g_value_get_int (&(item->content));
+	daap_structure_destroy (structure);
+	structure = NULL;
+	
+	/* get the songs */
+	path = g_strdup_printf ("/databases/%i/items?session-id=%i&revision-number=%i&meta=dmap.itemid,dmap.itemname,daap.songalbum,daap.songartist,daap.daap.songgenre,daap.songsize,daap.songtime,daap.songtrackcount,daap.songtracknumber,daap.songyear,daap.songformat,daap.songgenre,daap.songbitrate", connection->database_id, connection->session_id, connection->revision_number);
+	ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+	g_free (path);
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	item = daap_structure_find_item (structure, DAAP_CC_MRCO);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.returnedcount in items response\n");
+		goto connection_new_cleanup;
+	}
+	returned_count = g_value_get_int (&(item->content));
+	
+	item = daap_structure_find_item (structure, DAAP_CC_MTCO);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.specifiedtotalcount in items response\n");
+		goto connection_new_cleanup;
+	}
+	specified_total_count = g_value_get_int (&(item->content));
+	
+	item = daap_structure_find_item (structure, DAAP_CC_MUTY);
+	if (item == NULL) {
+		g_print ("Error, could not find dmap.updatetype in items response\n");
+		goto connection_new_cleanup;
+	}
+	update_type = g_value_get_boolean (&(item->content));
+
+	listing_node = daap_structure_find_node (structure, DAAP_CC_MLCL);
+	if (listing_node == NULL) {
+		g_print ("Error, could not find dmap.listing in items response\n");
+		goto connection_new_cleanup;
+	}
+
+	connection->item_id_to_uri = g_hash_table_new_full ((GHashFunc)g_direct_hash,(GEqualFunc)g_direct_equal, NULL, g_free);
+	
+	for (i = 0, n = listing_node->children; n; i++, n = n->next) {
+		GNode *n2;
+		RhythmDBEntry *entry = NULL;
+		GValue value = {0,};
+		gchar *uri = NULL;
+		gint item_id = 0;
+		const gchar *title = NULL;
+		const gchar *album = NULL;
+		const gchar *artist = NULL;
+		const gchar *format = NULL;
+		const gchar *genre = NULL;
+		gint length = 0;
+		gint track_count = 0;
+		gint track_number = 0;
+		gint year = 0;
+		gint size = 0;
+		gint bitrate = 0;
+		
+		for (n2 = n->children; n2; n2 = n2->next) {
+			DAAPItem *meta_item;
+			
+			meta_item = n2->data;
+
+			switch (meta_item->content_code) {
+				case DAAP_CC_MIID:
+					item_id = g_value_get_int (&(meta_item->content));
+					break;
+				case DAAP_CC_MINM:
+					title = g_value_get_string (&(meta_item->content));
+					break;
+				case DAAP_CC_ASAL:
+					album = g_value_get_string (&(meta_item->content));
+					break;
+				case DAAP_CC_ASAR:
+					artist = g_value_get_string (&(meta_item->content));
+					break;
+				case DAAP_CC_ASFM:
+					format = g_value_get_string (&(meta_item->content));
+					break;
+				case DAAP_CC_ASGN:
+					genre = g_value_get_string (&(meta_item->content));
+					break;
+				case DAAP_CC_ASTM:
+					length = g_value_get_int (&(meta_item->content));
+					break;
+				case DAAP_CC_ASTC:
+					track_count = g_value_get_int (&(meta_item->content));
+					break;	
+				case DAAP_CC_ASTN:
+					track_number = g_value_get_int (&(meta_item->content));
+					break;
+				case DAAP_CC_ASYR:
+					year = g_value_get_int (&(meta_item->content));
+					break;
+				case DAAP_CC_ASSZ:
+					size = g_value_get_int (&(meta_item->content));
+					break;
+				case DAAP_CC_ASBR:
+					bitrate = g_value_get_int (&(meta_item->content));
+					break;
+				default:
+					break;
+			}
+		}
+
+		if (connection->daap_version == 3.0) {
+			uri = g_strdup_printf ("daap://%s:%d/databases/%d/items/%d.%s?session-id=%d", host, port, connection->database_id, item_id, format, connection->session_id);
+		} else {
+			g_warning ("OLD ITUNES!\n");
+			// uri should be 
+			// "/databases/%d/items/%d.%s?session-id=%d&revision-id=%d";
+			// but its not going to work cause the other parts of the code 
+			// depend on the uri to have the ip address so that the
+			// RBDAAPSource can be found to ++request_id
+			// maybe just /dont/ support older itunes.  doesn't seem 
+			// unreasonable to me, honestly
+		}
+		entry = rhythmdb_entry_new (db, type, uri);
+		g_hash_table_insert (connection->item_id_to_uri, GINT_TO_POINTER (item_id), uri);
+
+		 /* track number */
+		g_value_init (&value, G_TYPE_ULONG);
+		g_value_set_ulong (&value,(gulong)track_number);
+		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_TRACK_NUMBER,&value);
+		g_value_unset (&value);
+
+		/* disc number */
+	
+		/* 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);
+	daap_structure_destroy (structure);
+	structure = NULL;
+
+	/* get a list of playlists */
+	path = g_strdup_printf ("/databases/%d/containers?session-id=%d&revision-number=%d", connection->database_id, connection->session_id, connection->revision_number);
+	ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+	g_free (path);
+	if (structure == NULL || ret == FALSE) {
+		goto connection_new_cleanup;
+	}
+
+	listing_node = daap_structure_find_node (structure, DAAP_CC_MLCL);
+	if (listing_node == NULL) {
+		g_print ("Error, could not find dmap.listing in playlists response\n");
+		goto connection_new_cleanup;
+	}
+
+	for (i = 0, n = listing_node->children; n; n = n->next, i++) {
+		gint id;
+		gchar *name;
+		DAAPPlaylist *playlist;
+		GNode *playlist_structure;
+		GNode *playlist_listing_node;
+		GNode *n2;
+		gint j;
+		GList *playlist_uris = NULL;
+	
+		item = daap_structure_find_item (n, DAAP_CC_ABPL);
+		if (item != NULL) {
+			continue;
+		}
+
+		item = daap_structure_find_item (n, DAAP_CC_MIID);
+		if (item == NULL) {
+			g_print ("Error, could not find dmap.itemid in playlists response");
+			goto connection_new_cleanup;
+		}
+		id = g_value_get_int (&(item->content));
+
+		item = daap_structure_find_item (n, DAAP_CC_MINM);
+		if (item == NULL) {
+			g_print ("Error, could not find dmap.itemname in playlists response");
+			goto connection_new_cleanup;
+		}
+		name = g_value_dup_string (&(item->content));
+
+		path = g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%d&revision-number=%d", connection->database_id, id, connection->session_id, connection->revision_number);
+		ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&playlist_structure);
+		g_free (path);
+		if (playlist_structure == NULL || ret == FALSE) {
+			g_free (name);
+			continue;
+			}
+
+		playlist_listing_node = daap_structure_find_node (playlist_structure, DAAP_CC_MLCL);
+		if (playlist_listing_node == NULL) {
+			g_print ("Error, could not find dmap.listing in playlists response\n");
+			g_free (name);
+			continue;
+		}
+
+		for (j = 0, n2 = playlist_listing_node->children; n2; n2 = n2->next, j++) {
+			gchar *item_uri;
+			gint playlist_item_id;
+
+			item = daap_structure_find_item (n2, DAAP_CC_MIID);
+			if (item == NULL) {
+				g_print ("Error, could not find dmap.itemid in playlists response");
+				continue;
+			}
+			playlist_item_id = g_value_get_int (&(item->content));
+		
+			item_uri = g_hash_table_lookup (connection->item_id_to_uri, GINT_TO_POINTER (playlist_item_id));
+			if (item_uri == NULL) {
+				rb_debug ("%d in %s doesnt exist in the database\n", playlist_item_id, name);
+				continue;
+			}
+			
+			playlist_uris = g_list_prepend (playlist_uris, item_uri);
+		}
+		
+		playlist = g_new0(DAAPPlaylist,1);
+
+		playlist->id = id;
+		playlist->name = name;
+		playlist->uris = playlist_uris;
+
+		connection->playlists = g_slist_prepend (connection->playlists, playlist);
+
+		
+	}
+	daap_structure_destroy (structure);
+	structure = NULL;
+
+//	g_hash_table_destroy (item_id_to_uri);
+	return connection;
+
+connection_new_cleanup:
+	if (connection && connection->item_id_to_uri) {
+		g_hash_table_destroy (connection->item_id_to_uri);
+	}
+	daap_structure_destroy (structure);
+	structure = NULL;
+//FIXME destroy connection ?
+	return NULL;
+}
+
+gchar * daap_connection_get_headers (DAAPConnection *connection, const gchar *uri, guint64 bytes)
+{
+	GString *headers;
+	gchar hash[33] = {0};
+	gchar *nodaap_uri = (gchar *)uri;
+	gchar *s;
+	
+	connection->request_id++;
+	
+	if (g_strncasecmp (uri,"daap://",7) == 0) {
+		nodaap_uri = strstr (uri,"/data");
+	}
+
+	daap_hash_generate ((short)floorf (connection->daap_version), nodaap_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 * daap_connection_get_playlists (DAAPConnection *connection)
+{
+	if (connection) {
+		return connection->playlists;
+	}
+
+	return NULL;
+}
+
+
+void daap_connection_destroy (DAAPConnection *connection)
+{
+	GSList *l;
+
+	for (l = connection->playlists; l; l = l->next) {
+		DAAPPlaylist *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 -Nurb rhythmbox/sources/daap.h myrhythmbox/sources/daap.h
--- rhythmbox/sources/daap.h	1969-12-31 19:00:00.000000000 -0500
+++ myrhythmbox/sources/daap.h	2005-07-29 04:07:20.000000000 -0400
@@ -0,0 +1,58 @@
+/*
+ *  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 __DAAP_H
+#define __DAAP_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "rhythmdb.h"
+
+G_BEGIN_DECLS
+
+
+
+
+void daap_content_codes_init(void);
+void daap_content_codes_shutdown(void);
+
+
+
+/* connection */
+typedef struct _DAAPConnection DAAPConnection;
+
+typedef struct {
+	gchar *name;
+	gint id;
+	GList *uris;
+} DAAPPlaylist;
+
+DAAPConnection * daap_connection_new(const gchar *host,gint port,RhythmDB *db,RhythmDBEntryType type);
+gchar * daap_connection_get_headers(DAAPConnection *connection,const gchar *uri,guint64 bytes);
+GSList * daap_connection_get_playlists(DAAPConnection *connection);
+void daap_connection_destroy(DAAPConnection *connection);
+/* end connection */
+
+
+G_END_DECLS
+
+#endif /* __DAAP_H */
+
diff -x CVS -Nurb 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-07-31 04:10:58.005817455 -0400
@@ -55,3 +55,16 @@
 INCLUDES += $(LIBNAUTILUS_BURN_CFLAGS)
 endif
 
+if USE_DAAP_BROWSE
+libsourcesimpl_la_SOURCES +=		\
+	rb-daap-source.c 		\
+	rb-daap-source.h		\
+	rb-daap-playlist-source.c	\
+	rb-daap-playlist-source.h	\
+	daap.c				\
+	daap.h	
+
+INCLUDES += $(SOUP_CFLAGS)
+
+endif
+
diff -x CVS -Nurb 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-07-27 23:19:57.000000000 -0400
@@ -0,0 +1,186 @@
+/*
+ *  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 "daap.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);
+static void rb_daap_playlist_source_activate (RBSource *source);
+static void rb_daap_playlist_source_deactivate (RBSource *source);
+
+struct RBDAAPPlaylistSourcePrivate
+{
+	DAAPPlaylist *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_activate = rb_daap_playlist_source_activate;
+	source_class->impl_deactivate = rb_daap_playlist_source_deactivate;
+	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",
+						 	       "DAAPPlaylist",
+							       "DAAPPlaylist 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;
+}
+
+
+static void rb_daap_playlist_source_activate (RBSource *source)
+{
+	g_print ("activate\n");
+	
+	return;
+}
+
+static void rb_daap_playlist_source_deactivate (RBSource *source)
+{
+	g_print ("deactivate\n");
+}
+
+
diff -x CVS -Nurb 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-07-27 21:02:46.000000000 -0400
@@ -0,0 +1,54 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) playlist source object
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_PLAYLIST_SOURCE_H
+#define __RB_DAAP_PLAYLIST_SOURCE_H
+
+#include "rb-playlist-source.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_DAAP_PLAYLIST_SOURCE         (rb_daap_playlist_source_get_type ())
+#define RB_DAAP_PLAYLIST_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSource))
+#define RB_DAAP_PLAYLIST_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSourceClass))
+#define RB_IS_DAAP_PLAYLIST_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE))
+#define RB_IS_DAAP_PLAYLIST_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DAAP_PLAYLIST_SOURCE))
+#define RB_DAAP_PLAYLIST_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSourceClass))
+
+typedef struct RBDAAPPlaylistSourcePrivate RBDAAPPlaylistSourcePrivate;
+
+typedef struct
+{
+	RBPlaylistSource parent;
+
+	RBDAAPPlaylistSourcePrivate *priv;
+} RBDAAPPlaylistSource;
+
+typedef struct
+{
+	RBPlaylistSourceClass parent;
+} RBDAAPPlaylistSourceClass;
+
+GType		rb_daap_playlist_source_get_type	        (void);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_PLAYLIST_SOURCE_H */
diff -x CVS -Nurb 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-07-29 15:59:37.000000000 -0400
@@ -0,0 +1,456 @@
+/*
+ *  Implementation of DAAP (iTunes Music Sharing) source object
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkicontheme.h>
+#include <string.h>
+#include "rhythmdb.h"
+#include "rb-shell.h"
+#include <libgnome/gnome-i18n.h>
+#include "eel-gconf-extensions.h"
+#include "rb-daap-source.h"
+#include "rb-stock-icons.h"
+#include "rb-debug.h"
+#include "rb-util.h"
+#include "rb-dialog.h"
+#include <libgnomevfs/gnome-vfs-dns-sd.h>
+
+#include "daap.h"
+#include "rb-daap-playlist-source.h"
+
+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;
+
+	DAAPConnection *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_delete = NULL;
+	source_class->impl_receive_drag = NULL;
+	source_class->impl_show_popup = rb_daap_source_show_popup;
+
+	return;
+}
+
+static void
+rb_daap_source_init (RBDAAPSource *source)
+{
+	source->priv = g_new0 (RBDAAPSourcePrivate, 1);
+
+	return;
+}
+
+
+static void 
+rb_daap_source_finalize (GObject *object)
+{
+	RBDAAPSource *source = RB_DAAP_SOURCE (object);
+
+	rb_daap_source_disconnect (RB_SOURCE (source));
+	
+	if (source->priv) {
+		g_free (source->priv->host);
+	
+		g_free (source->priv);
+		source->priv = NULL;
+	}
+
+	return;
+}
+
+static GdkPixbuf *
+rb_daap_get_icon (void)
+{
+	GdkPixbuf *icon;
+	GtkIconTheme *theme;
+
+	theme = gtk_icon_theme_get_default ();
+	icon = gtk_icon_theme_load_icon (theme, "gnome-fs-network", 24, 0, NULL);
+
+	return icon;
+}
+
+static RBSource *
+rb_daap_source_new (RBShell *shell,const gchar *name)
+{
+	RBSource *source;
+	RhythmDBEntryType type;
+	gchar *internal_name;
+	
+	type = rhythmdb_entry_daap_type_new ();
+	internal_name = g_strconcat ("<daap-", name, ">", NULL);
+
+	source = RB_SOURCE (g_object_new (RB_TYPE_DAAP_SOURCE,
+					  "name", name,
+					  "entry-type", type,
+					  "internal-name", internal_name,
+					  "icon", rb_daap_get_icon (),
+					  "shell", shell,
+					  "visibility", TRUE,
+					  NULL));
+
+	rb_shell_register_entry_type_for_source (shell, source, 
+						 type);
+
+	g_free (internal_name);
+	internal_name = NULL;
+
+	return source;
+}
+
+static GnomeVFSDNSSDBrowseHandle *browse_handle = NULL;
+static GSList *sources = NULL;
+
+static RBSource * find_source_by_name(const gchar *name)
+{
+	GSList *l;
+	RBSource *source = NULL;
+
+	for (l = sources; l; l = l->next) {
+		gchar *s;
+		
+		source = l->data;
+		g_object_get (G_OBJECT (source), "name", &s, NULL);
+		
+		if (strcmp (name, s) == 0) {
+			g_free (s);
+			return source;
+		}
+
+		g_free (s);
+	}
+
+	return NULL;
+}
+
+
+static void dns_sd_browse_cb (GnomeVFSDNSSDBrowseHandle *handle,
+			      GnomeVFSDNSSDServiceStatus status,
+			      const GnomeVFSDNSSDService *service,
+			      RBShell *shell)
+{
+	if (status == GNOME_VFS_DNS_SD_SERVICE_ADDED) { /* new daap server */
+		RBSource *source = find_source_by_name(service->name);
+
+		if (source) {
+			g_warning("Got duplicate!?\n");
+			return;
+		}
+
+		source = rb_daap_source_new (shell, service->name);
+
+		sources = g_slist_prepend (sources, source);
+
+		rb_shell_append_source (shell, source, NULL);
+
+	} else if (status == GNOME_VFS_DNS_SD_SERVICE_REMOVED) { /* daap server went away */
+		RBSource *source = find_source_by_name(service->name);
+
+		if (source == NULL) {
+			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;
+}
+
+RBSource * rb_daap_sources_init (RBShell *shell)
+{
+	GnomeVFSResult result;
+
+	result = gnome_vfs_dns_sd_browse (&browse_handle, 
+					  "local", 
+					  "_daap._tcp", 
+					  (GnomeVFSDNSSDBrowseCallback)dns_sd_browse_cb,shell,
+					  NULL);
+
+	if (result != GNOME_VFS_OK) {
+		g_warning ("Could not start listening for DAAP hosts");
+		return NULL;
+	}
+
+	daap_content_codes_init (); // FIXME _shutdown is never called
+
+	return NULL;
+}
+
+void rb_daap_sources_shutdown (RBShell *shell)
+{
+	GnomeVFSResult result;
+
+	daap_content_codes_shutdown ();
+	
+	result = gnome_vfs_dns_sd_stop_browse(browse_handle);
+
+	if (result != GNOME_VFS_OK) {
+		g_warning ("Unable to shutdown listening for DAAPHosts");
+	}
+	
+	return;
+}
+
+static void rb_daap_source_activate (RBSource *source)
+{
+	RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
+	RBShell *shell;
+	RhythmDB *db;
+	RhythmDBEntryType type;
+	gchar *name;
+	
+	g_object_get (G_OBJECT (source), "name", &name, NULL);
+	
+	if (daap_source->priv->resolved == FALSE) {
+		GnomeVFSResult result;
+	
+		result = gnome_vfs_dns_sd_resolve_sync (name,
+						       "_daap._tcp",
+						       "local",
+						       20,
+						       &(daap_source->priv->host),
+						       &(daap_source->priv->port),
+						       NULL,
+						       NULL,
+						       NULL);
+
+		if (result == GNOME_VFS_OK) {
+			daap_source->priv->resolved = TRUE;
+		} else {
+			rb_error_dialog (NULL,_("Error finding host"),_("Could not find %s on the network"),name);
+			g_free (name);
+			
+			return;
+		}
+
+	}
+
+	if (daap_source->priv->connection == NULL) {
+		GSList *playlists;
+		GSList *l;
+		
+		g_object_get (G_OBJECT (daap_source), "shell", &shell, "entry-type", &type, NULL);
+		g_object_get (G_OBJECT (shell), "db", &db, NULL);
+
+		daap_source->priv->connection = daap_connection_new (daap_source->priv->host, daap_source->priv->port, db, type);
+		playlists = daap_connection_get_playlists(daap_source->priv->connection);
+		for (l = playlists; l; l = l->next) {
+			DAAPPlaylist *playlist = l->data;
+			RBSource *playlist_source;
+			gchar *internal_name;
+	
+			internal_name = g_strconcat ("<daap-", name, "-",playlist->name,">", NULL);
+
+			playlist_source = RB_SOURCE (g_object_new (RB_TYPE_DAAP_PLAYLIST_SOURCE,
+					  "name", playlist->name,
+					  "internal-name", internal_name,
+					  "shell", shell,
+					  "visibility", TRUE,
+					  NULL));
+/* this is set here instead of in construction so that 
+ * rb_playlist_source_constructor has a chance to be run to set things up */
+			g_object_set (G_OBJECT (playlist_source), "playlist", playlist, NULL); 
+			g_free (internal_name);
+			internal_name = NULL;
+
+			rb_shell_append_source (shell, playlist_source, source);
+			daap_source->priv->playlist_sources = g_slist_prepend (daap_source->priv->playlist_sources, playlist_source);
+		}
+
+		g_object_unref (G_OBJECT (db));
+		g_object_unref (G_OBJECT (shell));
+
+		daap_source->priv->connected = TRUE;
+	}
+	
+	g_free(name);
+	
+	return;
+}
+
+static gboolean rb_daap_source_disconnect (RBSource *source)
+{
+	RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
+
+	if (daap_source->priv->connected) {
+		GSList *l;
+		RBShell *shell;
+		RhythmDB *db;
+		RhythmDBEntryType type;
+
+		g_object_get (G_OBJECT (source), "shell", &shell, "entry-type", &type, NULL);
+		g_object_get (G_OBJECT (shell), "db", &db, NULL);
+		rhythmdb_entry_delete_by_type (db, type);
+		rhythmdb_commit (db);
+		g_object_unref (G_OBJECT (db));
+		g_object_unref (G_OBJECT (shell));
+
+		for (l = daap_source->priv->playlist_sources; l; l = l->next) {
+			RBSource *playlist_source = RB_SOURCE (l->data);
+	
+			rb_source_delete_thyself (playlist_source);	
+		}
+		
+		g_slist_free(daap_source->priv->playlist_sources);
+		daap_source->priv->playlist_sources = NULL;
+	
+		if (daap_source->priv->connection) {
+			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 daap_connection_get_headers (source->priv->connection, uri, bytes);
+}
+
+
+
diff -x CVS -Nurb 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-07-29 13:06:33.000000000 -0400
@@ -0,0 +1,63 @@
+/*
+ *  Header for DAAP (iTunes Music Sharing) source object
+ *
+ *  Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_SOURCE_H
+#define __RB_DAAP_SOURCE_H
+
+#include "rb-shell.h"
+#include "rb-library-source.h"
+#include "rhythmdb.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_DAAP_SOURCE         (rb_daap_source_get_type ())
+#define RB_DAAP_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DAAP_SOURCE, RBDAAPSource))
+#define RB_DAAP_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DAAP_SOURCE, RBDAAPSourceClass))
+#define RB_IS_DAAP_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DAAP_SOURCE))
+#define RB_IS_DAAP_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DAAP_SOURCE))
+#define RB_DAAP_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DAAP_SOURCE, RBDAAPSourceClass))
+
+typedef struct RBDAAPSourcePrivate RBDAAPSourcePrivate;
+
+typedef struct {
+	RBLibrarySource parent;
+
+	RBDAAPSourcePrivate *priv;
+} RBDAAPSource;
+
+typedef struct {
+	RBLibrarySourceClass parent;
+} RBDAAPSourceClass;
+
+RBSource *	rb_daap_sources_init		(RBShell *shell);
+void 		rb_daap_sources_shutdown 	(RBShell *shell);
+
+GType		rb_daap_source_get_type	        (void);
+
+RBDAAPSource *	rb_daap_source_find_for_uri	(const gchar *uri);
+
+gchar *		rb_daap_source_get_headers	(RBDAAPSource *source, 
+						 const gchar *uri, 
+						 glong time);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_SOURCE_H */


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