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