[Rhythmbox-devel] patch #5 (Was: Re: patch #4)
- From: Charles Schmidt <cschmidt2 emich edu>
- To: rhythmbox-devel gnome org
- Subject: [Rhythmbox-devel] patch #5 (Was: Re: patch #4)
- Date: Fri, 26 Aug 2005 18:52:57 -0400
-
Yet another music sharing patch. This one addresses the most recent
things that have been changed in CVS, so it should compile cleanly. It
also implements its own GStreamer source. So you don't need the
GStreamer gnomevfssrc patch I've linked to before - so it should be
easier for people to try out, not needing to recompile GStreamer stuff.
Its against CVS, and almost certainly won't work against any of the 0.9
tarball releases.
Give it a try and let me know. Unless anything crops up before I get a
CVS account, this is what I'll commit when I do.
Thanks,
-charlie
diff -x cvs -x CVS -x ignore -ur rhythmbox/configure.ac myrhythmbox/configure.ac
--- rhythmbox/configure.ac 2005-08-25 20:48:22.000000000 -0400
+++ myrhythmbox/configure.ac 2005-08-26 15:09:53.000000000 -0400
@@ -155,6 +155,47 @@
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])
+ PKG_CHECK_MODULES(SOUP, \
+ libsoup-2.2,
+ have_libsoup=yes)
+ PKG_CHECK_MODULES(MDNS,
+ howl,
+ have_howl=yes,
+ have_howl=no)
+ if test x"$have_howl" = xyes; then
+ AC_DEFINE(WITH_HOWL, 1, [Define if Howl should be used for mDNS])
+ have_mdns="yes"
+ else
+ PKG_CHECK_MODULES(MDNS,
+ avahi-client avahi-glib,
+ have_avahi=yes,
+ have_avahi=no)
+ if test x"$have_avahi" = xyes; then
+ AC_DEFINE(WITH_AVAHI, 1, [Define if Avahi should be used for mDNS])
+ have_mdns="yes"
+ fi
+ fi
+
+ enable_daap="no"
+
+ if test x"$have_libsoup" = xyes; then
+ if test x"$have_mdns" = xyes; then
+ AC_DEFINE(WITH_DAAP_SUPPORT, 1, [Define if daap should be enabled])
+ enable_daap="yes"
+ else
+ AC_MSG_ERROR([DAAP support explicitly requested, but no mDNS implentation found. Download Howl or Avahi])
+ fi
+ fi
+
+ AM_CONDITIONAL(USE_DAAP, test "x$enable_daap" = "xyes")
+fi
dnl AC_CHECK_LIB(lirc_client, lirc_init,
dnl [ AC_CHECK_HEADER(lirc/lirc_client.h,
@@ -423,6 +464,7 @@
remote/Makefile
remote/bonobo/Makefile
remote/dbus/Makefile
+daapsharing/Makefile
shell/Makefile
data/Makefile
data/ui/Makefile
@@ -480,6 +522,11 @@
else
AC_MSG_NOTICE([ iPod integration disabled])
fi
+if test x"$enable_daap" = xyes; then
+ AC_MSG_NOTICE([** DAAP sharing enabled])
+else
+ AC_MSG_NOTICE([ DAAP sharing disabled])
+fi
if test x"$enable_cd_burner" = xyes; then
AC_MSG_NOTICE([** CD burner support is enabled])
else
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/daap-sharing.c myrhythmbox/daapsharing/daap-sharing.c
--- rhythmbox/daapsharing/daap-sharing.c 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/daap-sharing.c 2005-08-20 23:04:10.000000000 -0400
@@ -0,0 +1,154 @@
+/*
+ * Implmentation of DAAP (iTunes Music Sharing) sharing
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include "daap-sharing.h"
+#include "rb-daap-share.h"
+#include "rb-debug.h"
+#include "rb-dialog.h"
+#include "rb-playlist-manager.h"
+#include "eel-gconf-extensions.h"
+#include <libgnome/gnome-i18n.h>
+#include <string.h>
+
+#define CONF_ENABLE_SHARING "/apps/rhythmbox/sharing/enable_sharing"
+#define CONF_SHARE_NAME "/apps/rhythmbox/sharing/share_name"
+
+static RBDAAPShare *share = NULL;
+
+static void
+create_share (RBShell *shell)
+{
+ RhythmDB *db;
+ RBPlaylistManager *playlist_manager;
+
+ gchar *name;
+
+ rb_debug ("initialize daap sharing\n");
+
+ name = eel_gconf_get_string (CONF_SHARE_NAME);
+
+ if (name == NULL || *name == '\0') {
+ const gchar *real_name;
+
+ g_free (name);
+
+ real_name = g_get_real_name ();
+ if (strcmp (real_name, "Unknown") == 0) {
+ real_name = g_get_user_name ();
+ }
+
+ name = g_strconcat (real_name, "'s Music", NULL);
+ eel_gconf_set_string (CONF_SHARE_NAME, name);
+ }
+
+ g_object_get (G_OBJECT (shell), "db", &db, "playlist-manager", &playlist_manager, NULL);
+
+ share = rb_daap_share_new (name, db, playlist_manager);
+
+ g_object_unref (db);
+ g_object_unref (playlist_manager);
+ g_free (name);
+
+ return;
+}
+
+static void
+enable_sharing_changed_cb (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ RBShell *shell)
+{
+ gboolean enabled;
+
+ enabled = eel_gconf_get_boolean (CONF_ENABLE_SHARING);
+
+ if (enabled) {
+ create_share (shell);
+ } else {
+ rb_debug ("shutdown daap sharing\n");
+
+ g_object_unref (share);
+ share = NULL;
+ }
+
+ return;
+}
+
+static void
+share_name_changed_cb (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ RBShell *shell)
+{
+ gchar *name;
+
+ name = eel_gconf_get_string (CONF_SHARE_NAME);
+
+ if (share) {
+ g_object_set (G_OBJECT (share), "name", name, NULL);
+ }
+
+ g_free (name);
+
+ return;
+}
+
+
+void
+daap_sharing_init (RBShell *shell)
+{
+ gboolean enabled;
+
+ g_object_ref (shell);
+
+ enabled = eel_gconf_get_boolean (CONF_ENABLE_SHARING);
+
+ if (enabled) {
+ create_share (shell);
+ }
+
+ eel_gconf_notification_add (CONF_ENABLE_SHARING,
+ (GConfClientNotifyFunc) enable_sharing_changed_cb,
+ shell);
+ eel_gconf_notification_add (CONF_SHARE_NAME,
+ (GConfClientNotifyFunc) share_name_changed_cb,
+ shell);
+
+ return;
+}
+
+void
+daap_sharing_shutdown (RBShell *shell)
+{
+ g_object_unref (shell);
+
+ if (share) {
+ rb_debug ("shutdown daap sharing\n");
+
+ g_object_unref (share);
+ share = NULL;
+ }
+
+ return;
+}
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/daap-sharing.h myrhythmbox/daapsharing/daap-sharing.h
--- rhythmbox/daapsharing/daap-sharing.h 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/daap-sharing.h 2005-08-20 21:44:32.000000000 -0400
@@ -0,0 +1,38 @@
+/*
+ * Header for DAAP (iTunes Music Sharing) sharing
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __DAAP_SHARING_H
+#define __DAAP_SHARING_H
+
+#include "rb-shell.h"
+
+G_BEGIN_DECLS
+
+void
+daap_sharing_init (RBShell *shell);
+
+void
+daap_sharing_shutdown (RBShell *shell);
+
+G_END_DECLS
+
+#endif /* __DAAP_SHARING_H */
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/Makefile.am myrhythmbox/daapsharing/Makefile.am
--- rhythmbox/daapsharing/Makefile.am 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/Makefile.am 2005-08-24 17:12:24.000000000 -0400
@@ -0,0 +1,44 @@
+noinst_LTLIBRARIES = libdaapsharing.la
+
+libdaapsharing_la_SOURCES =
+
+if USE_DAAP
+libdaapsharing_la_SOURCES += \
+ daap-sharing.c \
+ daap-sharing.h \
+ rb-daap-share.c \
+ rb-daap-share.h \
+ rb-daap-structure.c \
+ rb-daap-structure.h \
+ rb-daap-mdns.c \
+ rb-daap-mdns.h \
+ rb-daap-connection.c \
+ rb-daap-connection.h \
+ rb-daap-src.c \
+ rb-daap-src.h
+endif
+
+INCLUDES = \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -DG_LOG_DOMAIN=\"Rhythmbox\" \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/lib \
+ -I$(top_builddir)/lib \
+ -I$(top_srcdir)/corba \
+ -I$(top_builddir)/corba \
+ -I$(top_srcdir)/rhythmdb \
+ -I$(top_srcdir)/library \
+ -I$(top_srcdir)/iradio \
+ -I$(top_srcdir)/widgets \
+ -I$(top_srcdir)/shell \
+ -I$(top_srcdir)/sources \
+ -DPIXMAP_DIR=\""$(datadir)/pixmaps"\" \
+ -DSHARE_DIR=\"$(pkgdatadir)\" \
+ -DDATADIR=\""$(datadir)"\" \
+ $(WARN_CFLAGS) \
+ $(RHYTHMBOX_CFLAGS) \
+ $(SOUP_CFLAGS) \
+ $(MDNS_CFLAGS)
+
+libdaapsharing_la_LDFLAGS = -export-dynamic
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/rb-daap-connection.c myrhythmbox/daapsharing/rb-daap-connection.c
--- rhythmbox/daapsharing/rb-daap-connection.c 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-connection.c 2005-08-25 18:01:00.000000000 -0400
@@ -0,0 +1,1253 @@
+/*
+ * Implementation of DAAP (iTunes Music Sharing) hashing, parsing, connection
+ *
+ * Copyright (C) 2004,2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "rb-daap-connection.h"
+#include "rb-daap-structure.h"
+
+#include <libgnome/gnome-i18n.h>
+#include "rb-debug.h"
+
+/* hashing - based on/copied from libopendaap
+ * Copyright (c) 2004 David Hammerton
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+typedef struct {
+ guint32 buf[4];
+ guint32 bits[2];
+ unsigned char in[64];
+ int version;
+} MD5_CTX;
+
+/*
+* This code implements the MD5 message-digest algorithm.
+* The algorithm is due to Ron Rivest. This code was
+* written by Colin Plumb in 1993, no copyright is claimed.
+* This code is in the public domain; do with it what you wish.
+*
+* Equivalent code is available from RSA Data Security, Inc.
+* This code has been tested against that, and is equivalent,
+* except that you don't need to include two pages of legalese
+* with every copy.
+*
+* To compute the message digest of a chunk of bytes, declare an MD5Context
+* structure, pass it to OpenDaap_MD5Init, call OpenDaap_MD5Update as needed
+* on buffers full of bytes, and then call OpenDaap_MD5Final, which will fill
+* a supplied 16-byte array with the digest.
+*/
+static void
+MD5Transform (guint32 buf[4],
+ guint32 const in[16],
+ gint version);
+/* for some reason we still have to reverse bytes on bigendian machines
+ * I don't really know why... but otherwise it fails..
+ * Any MD5 gurus out there know why???
+ */
+#if 0 //ndef WORDS_BIGENDIAN /* was: HIGHFIRST */
+#define byteReverse(buf, len) /* Nothing */
+#else
+static void
+byteReverse (unsigned char *buf,
+ unsigned longs);
+
+#ifndef ASM_MD5
+/*
+* Note: this code is harmless on little-endian machines.
+*/
+static void
+byteReverse (unsigned char *buf,
+ unsigned longs)
+{
+ guint32 t;
+ do {
+ t = (guint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(guint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+
+static void
+OpenDaap_MD5Init (MD5_CTX *ctx,
+ gint version)
+{
+ 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->version = version;
+}
+
+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->version);
+ 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->version);
+ 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->version);
+
+ /* 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->version);
+ byteReverse ((unsigned char *) ctx->buf, 4);
+ memcpy (digest, ctx->buf, 16);
+ memset (ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+
+ return;
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+* The core of the MD5 algorithm, this alters an existing MD5 hash to reflect
+* the addition of 16 longwords of new data. OpenDaap_MD5Update blocks the
+* data and converts bytes into longwords for this routine.
+*/
+static void
+MD5Transform (guint32 buf[4],
+ guint32 const in[16],
+ gint version)
+{
+ 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 (version == 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 unsigned char staticHash_42[256*65] = {0};
+static unsigned char staticHash_45[256*65] = {0};
+
+static const char hexchars[] = "0123456789ABCDEF";
+static char ac[] = "Dpqzsjhiu!3114!Bqqmf!Dpnqvufs-!Jod/"; /* +1 */
+static gboolean ac_unfudged = FALSE;
+
+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;
+ unsigned char buf[16];
+
+ for (i = 0; i < 256; i++)
+ {
+ OpenDaap_MD5Init (&ctx, 0);
+
+#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, (unsigned char const *)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, (char *)p);
+ p += 65;
+ }
+}
+
+static void GenerateStatic_45()
+{
+ MD5_CTX ctx;
+ unsigned char *p = staticHash_45;
+ int i;
+ unsigned char buf[16];
+
+ for (i = 0; i < 256; i++)
+ {
+ OpenDaap_MD5Init (&ctx, 1);
+
+#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, (unsigned char const *)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, (char *)p);
+ p += 65;
+ }
+}
+
+static void
+rb_daap_hash_generate (short version_major,
+ const guchar *url,
+ guchar hash_select,
+ guchar *out,
+ gint request_id)
+{
+ unsigned char buf[16];
+ MD5_CTX ctx;
+ int i;
+
+ unsigned 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 ((const gchar*)url));
+ if (ac_unfudged == FALSE) {
+ for (i = 0; i < strlen (ac); i++) {
+ ac[i] = ac[i]-1;
+ }
+ ac_unfudged = TRUE;
+ }
+ OpenDaap_MD5Update (&ctx, (const guchar*)ac, strlen (ac));
+
+ 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, (const guchar*)scribble, strlen (scribble));
+ }
+
+ OpenDaap_MD5Final (&ctx, buf);
+ DigestToString (buf, (char *)out);
+
+ return;
+}
+
+/* end hashing */
+
+
+/* connection */
+#include <math.h>
+#include <libsoup/soup.h>
+#include <libsoup/soup-connection.h>
+#include <libsoup/soup-session-sync.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), (const guchar*)norb_daap_path, 2, (guchar*)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");
+ }
+
+ soup_uri_free (uri);
+
+ 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;
+ RBDAAPItem *item = NULL;
+
+ message = build_message (connection, path, need_hash, version, req_id, send_close);
+ if (message == NULL) {
+ rb_debug ("Error building message for %s", path);
+ return FALSE;
+ }
+
+ soup_session_send_message (connection->session, message);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code) == FALSE) {
+ g_object_unref (message);
+ rb_debug ("Error getting %s: %d, %s\n", path, message->status_code, message->reason_phrase);
+ return FALSE;
+ }
+
+ *structure = rb_daap_structure_parse (message->response.body, message->response.length);
+ g_object_unref (message);
+
+ if (*structure == NULL) {
+ rb_debug ("No daap structure returned %s", path);
+ return FALSE;
+ }
+
+ item = rb_daap_structure_find_item (*structure, RB_DAAP_CC_MSTT);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.status item in %s", path);
+ return FALSE;
+ }
+
+ if (g_value_get_int (&(item->content)) != 200) {
+ rb_debug ("Error, dmap.status is not 200 in %s", path);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+entry_set_string_prop (RhythmDB *db,
+ RhythmDBEntry *entry,
+ RhythmDBPropType propid,
+ const char *str)
+{
+ GValue value = {0,};
+ gchar *tmp;
+
+ if (str == NULL) {
+ tmp = g_strdup (_("Unknown"));
+ } else {
+ tmp = g_strdup (str);
+ }
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string_take_ownership (&value, tmp);
+ rhythmdb_entry_set (RHYTHMDB (db), entry, propid, &value);
+ g_value_unset (&value);
+}
+
+static gboolean
+connection_get_info (RBDAAPConnection *connection)
+{
+ GNode *structure = NULL;
+ gboolean ret;
+ RBDAAPItem *item = NULL;
+
+ /* get the daap version number */
+ ret = http_get (connection, "/server-info", FALSE, 0.0, 0, FALSE, &structure);
+ if (structure == NULL || ret == FALSE) {
+ rb_debug ("Got invalid response from /server-info");
+ ret = FALSE;
+ goto out;
+ }
+
+ item = rb_daap_structure_find_item (structure, RB_DAAP_CC_APRO);
+ if (item == NULL ){
+ rb_debug ("Could not find daap.protocolversion item in /server-info");
+ ret = FALSE;
+ goto out;
+ }
+
+ connection->daap_version = g_value_get_double (&(item->content));
+ ret = TRUE;
+
+out:
+ rb_daap_structure_destroy (structure);
+
+ return ret;
+}
+
+static gboolean
+connection_login (RBDAAPConnection *connection)
+{
+ GNode *structure = NULL;
+ gboolean ret;
+ RBDAAPItem *item = NULL;
+ gchar *path = NULL;
+
+ /* get a session id */
+ ret = http_get (connection,"/login", FALSE, 0.0, 0, FALSE, &structure);
+
+ if (structure == NULL || ret == FALSE) {
+ rb_debug ("Got invalid response from /login");
+ ret = FALSE;
+ goto out;
+ }
+
+ item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MLID);
+ if (item == NULL) {
+ rb_debug ("Could not find daap.sessionid item in /login");
+ ret = FALSE;
+ goto out;
+ }
+
+ connection->session_id = g_value_get_int (&(item->content));
+
+ rb_daap_structure_destroy (structure);
+ structure = NULL;
+
+ /* get a revision number */
+ path = g_strdup_printf ("/update?session-id=%d&revision-number=1", connection->session_id);
+ ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+
+ if (structure == NULL || ret == FALSE) {
+ rb_debug ("Got invalid response from %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUSR);
+ if (item == NULL) {
+ rb_debug ("Could not find daap.serverrevision item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ connection->revision_number = g_value_get_int (&(item->content));
+
+ ret = TRUE;
+out:
+ rb_daap_structure_destroy (structure);
+ g_free (path);
+
+ return ret;
+}
+
+static gboolean
+connection_get_database_info (RBDAAPConnection *connection)
+{
+ GNode *structure = NULL;
+ gboolean ret;
+ RBDAAPItem *item = NULL;
+ GNode *listing_node;
+ gchar *path;
+ gint n_databases = 0;
+
+ /* get database id */
+ /* get a list of databases, there should be only 1 */
+
+ path = g_strdup_printf ("/databases?session-id=%d&revision-number=%d", connection->session_id, connection->revision_number);
+ ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+
+ if (structure == NULL || ret == FALSE) {
+ rb_debug ("Got invalid response from %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.returnedcount item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ n_databases = g_value_get_int (&(item->content));
+ if (n_databases != 1) {
+ rb_debug ("Host seems to have more than 1 database, how strange\n");
+ }
+
+ listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
+ if (listing_node == NULL) {
+ rb_debug ("Could not find dmap.listing item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ item = rb_daap_structure_find_item (listing_node->children, RB_DAAP_CC_MIID);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.itemid item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ connection->database_id = g_value_get_int (&(item->content));
+
+ ret = TRUE;
+
+out:
+ rb_daap_structure_destroy (structure);
+ g_free (path);
+
+ return ret;
+}
+
+static gboolean
+connection_get_song_listing (RBDAAPConnection *connection,
+ const gchar *host,
+ gint port,
+ RhythmDB *db,
+ RhythmDBEntryType type)
+{
+ GNode *structure = NULL;
+ gboolean ret;
+ RBDAAPItem *item = NULL;
+ GNode *listing_node;
+ gchar *path;
+ gint returned_count;
+ gint i;
+ GNode *n;
+ gint specified_total_count;
+ gboolean update_type;
+
+ /* get the songs */
+ path = g_strdup_printf ("/databases/%i/items?session-id=%i&revision-number=%i&meta=dmap.itemid,dmap.itemname,daap.songalbum,daap.songartist,daap.daap.songgenre,daap.songsize,daap.songtime,daap.songtrackcount,daap.songtracknumber,daap.songyear,daap.songformat,daap.songgenre,daap.songbitrate", connection->database_id, connection->session_id, connection->revision_number);
+ ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+
+ if (structure == NULL || ret == FALSE) {
+ rb_debug ("Got invalid response from %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.returnedcount item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+ returned_count = g_value_get_int (&(item->content));
+
+ item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MTCO);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.specifiedtotalcount item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+ specified_total_count = g_value_get_int (&(item->content));
+
+ item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUTY);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.updatetype item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+ update_type = g_value_get_char (&(item->content));
+
+ listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
+ if (listing_node == NULL) {
+ rb_debug ("Could not find dmap.listing item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ connection->item_id_to_uri = g_hash_table_new_full ((GHashFunc)g_direct_hash,(GEqualFunc)g_direct_equal, NULL, g_free);
+
+ for (i = 0, n = listing_node->children; n; i++, n = n->next) {
+ GNode *n2;
+ RhythmDBEntry *entry = NULL;
+ GValue value = {0,};
+ gchar *uri = NULL;
+ gint item_id = 0;
+ const gchar *title = NULL;
+ const gchar *album = NULL;
+ const gchar *artist = NULL;
+ const gchar *format = NULL;
+ const gchar *genre = NULL;
+ gint length = 0;
+ gint track_number = 0;
+ gint disc_number = 0;
+ gint year = 0;
+ gint size = 0;
+ gint bitrate = 0;
+
+ for (n2 = n->children; n2; n2 = n2->next) {
+ RBDAAPItem *meta_item;
+
+ meta_item = n2->data;
+
+ switch (meta_item->content_code) {
+ case RB_DAAP_CC_MIID:
+ item_id = g_value_get_int (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_MINM:
+ title = g_value_get_string (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASAL:
+ album = g_value_get_string (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASAR:
+ artist = g_value_get_string (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASFM:
+ format = g_value_get_string (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASGN:
+ genre = g_value_get_string (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASTM:
+ length = g_value_get_int (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASTN:
+ track_number = g_value_get_int (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASDN:
+ disc_number = g_value_get_int (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASYR:
+ year = g_value_get_int (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASSZ:
+ size = g_value_get_int (&(meta_item->content));
+ break;
+ case RB_DAAP_CC_ASBR:
+ bitrate = g_value_get_int (&(meta_item->content));
+ break;
+ default:
+ break;
+ }
+ }
+
+// if (connection->daap_version == 3.0) {
+ uri = g_strdup_printf ("daap://%s:%d/databases/%d/items/%d.%s?session-id=%d", host, port, connection->database_id, item_id, format, connection->session_id);
+// } else {
+// ??FIXME??
+// OLD ITUNES
+ // uri should be
+ // "/databases/%d/items/%d.%s?session-id=%d&revision-id=%d";
+ // but its not going to work cause the other parts of the code
+ // depend on the uri to have the ip address so that the
+ // RBDAAPSource can be found to ++request_id
+ // maybe just /dont/ support older itunes. doesn't seem
+ // unreasonable to me, honestly
+// }
+ entry = rhythmdb_entry_new (db, type, uri);
+ g_hash_table_insert (connection->item_id_to_uri, GINT_TO_POINTER (item_id), uri);
+
+ /* track number */
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value,(gulong)track_number);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_TRACK_NUMBER, &value);
+ g_value_unset (&value);
+
+ /* disc number */
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value,(gulong)disc_number);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DISC_NUMBER, &value);
+ g_value_unset (&value);
+
+ /* bitrate */
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value,(gulong)bitrate);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_BITRATE, &value);
+ g_value_unset (&value);
+
+ /* length */
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value,(gulong)length / 1000);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DURATION, &value);
+ g_value_unset (&value);
+
+ /* file size */
+ g_value_init (&value, G_TYPE_UINT64);
+ g_value_set_uint64(&value,(gint64)size);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_FILE_SIZE, &value);
+ g_value_unset (&value);
+
+ /* title */
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_TITLE, title);
+
+ /* album */
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM, album);
+
+ /* artist */
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ARTIST, artist);
+
+ /* genre */
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_GENRE, genre);
+ }
+
+ rhythmdb_commit (db);
+
+ ret = TRUE;
+out:
+ rb_daap_structure_destroy (structure);
+ g_free (path);
+
+ return ret;
+}
+
+/* FIXME
+ * what we really should do is only get a list of playlists and their ids
+ * then when they are clicked on ('activate'd) by the user, get a list of
+ * the files that are actually in them. This will speed up initial daap
+ * connection times and reduce memory consumption.
+ */
+
+static gboolean
+connection_get_playlists (RBDAAPConnection *connection)
+{
+ GNode *structure = NULL;
+ gboolean ret;
+ GNode *listing_node;
+ gchar *path;
+ gint i;
+ GNode *n;
+
+ path = g_strdup_printf ("/databases/%d/containers?session-id=%d&revision-number=%d", connection->database_id, connection->session_id, connection->revision_number);
+ ret = http_get (connection, path, TRUE, connection->daap_version,0, FALSE,&structure);
+
+ if (structure == NULL || ret == FALSE) {
+ rb_debug ("Got invalid response from %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
+ if (listing_node == NULL) {
+ rb_debug ("Could not find dmap.listing item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+
+ for (i = 0, n = listing_node->children; n; n = n->next, i++) {
+ RBDAAPItem *item;
+ gint id;
+ gchar *name;
+ RBDAAPPlaylist *playlist;
+ GNode *playlist_structure;
+ GNode *playlist_listing_node;
+ GNode *n2;
+ gint j;
+ GList *playlist_uris = NULL;
+ gchar *path2;
+
+ item = rb_daap_structure_find_item (n, RB_DAAP_CC_ABPL);
+ if (item != NULL) {
+ continue;
+ }
+
+ item = rb_daap_structure_find_item (n, RB_DAAP_CC_MIID);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.itemid item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+ id = g_value_get_int (&(item->content));
+
+ item = rb_daap_structure_find_item (n, RB_DAAP_CC_MINM);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.itemname item in %s", path);
+ ret = FALSE;
+ goto out;
+ }
+ name = g_value_dup_string (&(item->content));
+
+ path2 = g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%d&revision-number=%d&meta=dmap.itemid", connection->database_id, id, connection->session_id, connection->revision_number);
+ ret = http_get (connection, path2, TRUE, connection->daap_version,0, FALSE,&playlist_structure);
+
+ if (playlist_structure == NULL || ret == FALSE) {
+ g_free (name);
+ continue;
+ }
+
+ playlist_listing_node = rb_daap_structure_find_node (playlist_structure, RB_DAAP_CC_MLCL);
+ if (playlist_listing_node == NULL) {
+ rb_debug ("Could not find dmap.listing item in %s", path2);
+ g_free (name);
+ continue;
+ }
+
+ for (j = 0, n2 = playlist_listing_node->children; n2; n2 = n2->next, j++) {
+ gchar *item_uri;
+ gint playlist_item_id;
+
+ item = rb_daap_structure_find_item (n2, RB_DAAP_CC_MIID);
+ if (item == NULL) {
+ rb_debug ("Could not find dmap.itemid item in %s", path2);
+ continue;
+ }
+ playlist_item_id = g_value_get_int (&(item->content));
+
+ item_uri = g_hash_table_lookup (connection->item_id_to_uri, GINT_TO_POINTER (playlist_item_id));
+ if (item_uri == NULL) {
+ rb_debug ("%d in %s doesnt exist in the database\n", playlist_item_id, name);
+ continue;
+ }
+
+ playlist_uris = g_list_prepend (playlist_uris, item_uri);
+ }
+
+ playlist = g_new0(RBDAAPPlaylist,1);
+
+ playlist->id = id;
+ playlist->name = name;
+ playlist->uris = playlist_uris;
+
+ connection->playlists = g_slist_prepend (connection->playlists, playlist);
+ }
+
+ ret = TRUE;
+out:
+ rb_daap_structure_destroy (structure);
+ structure = NULL;
+
+ return ret;
+}
+
+RBDAAPConnection *
+rb_daap_connection_new (const gchar *host,
+ gint port,
+ RhythmDB *db,
+ RhythmDBEntryType type)
+{
+ RBDAAPConnection *connection = NULL;
+ gchar *path = NULL;
+
+ connection = g_new0 (RBDAAPConnection, 1);
+
+ rb_debug ("Creating new DAAP connection to %s:%d", host, port);
+
+ connection->session = soup_session_sync_new ();
+ path = g_strdup_printf ("http://%s:%d/", host, port);
+ connection->base_uri = soup_uri_new (path);
+ if (connection->base_uri == NULL) {
+ rb_debug ("Error parsing %s", path);
+ goto error_out;
+ }
+
+ rb_debug ("Getting DAAP connection info");
+ if (connection_get_info (connection) == FALSE) {
+ rb_debug ("Could not get DAAP connection info");
+ goto error_out;
+ }
+
+ rb_debug ("Logging into DAAP server");
+ if (connection_login (connection) == FALSE) {
+ rb_debug ("Could not login to DAAP server");
+ goto error_out;
+ }
+
+ rb_debug ("Getting DAAP database info");
+ if (connection_get_database_info (connection) == FALSE) {
+ rb_debug ("Could not get DAAP database info");
+ goto error_out;
+ }
+
+ rb_debug ("Getting DAAP song listing");
+ if (connection_get_song_listing (connection, host, port, db, type) == FALSE) {
+ rb_debug ("Could not get DAAP song listing");
+ goto error_out;
+ }
+
+ rb_debug ("Getting DAAP playlists");
+ if (connection_get_playlists (connection) == FALSE) {
+ rb_debug ("Could not get DAAP playlsits");
+ goto error_out;
+ }
+
+ rb_debug ("Successfully created DAAP connection");
+
+ goto out;
+
+
+error_out:
+ rb_daap_connection_destroy (connection);
+ connection = NULL;
+
+out:
+ if (path) {
+ g_free (path);
+ }
+
+ return connection;
+}
+
+gchar *
+rb_daap_connection_get_headers (RBDAAPConnection *connection,
+ const gchar *uri,
+ gint64 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), (const guchar*)norb_daap_uri,2, (guchar*)hash, connection->request_id);
+
+ headers = g_string_new ("Accept: */*\r\nCache-Control: no-cache\r\nUser-Agent: "RB_DAAP_USER_AGENT"\r\nAccept-Language: en-us, en;q=5.0\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_GINT64_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 -x ignore -ur rhythmbox/daapsharing/rb-daap-connection.h myrhythmbox/daapsharing/rb-daap-connection.h
--- rhythmbox/daapsharing/rb-daap-connection.h 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-connection.h 2005-08-25 14:03:05.000000000 -0400
@@ -0,0 +1,60 @@
+/*
+ * Header for DAAP (iTunes Music Sharing) hashing, parsing, connection
+ *
+ * Copyright (C) 2004,2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_CONNECTION_H
+#define __RB_DAAP_CONNECTIONH
+
+#include <glib.h>
+#include <glib-object.h>
+#include "rhythmdb.h"
+
+G_BEGIN_DECLS
+
+typedef struct _RBDAAPConnection RBDAAPConnection;
+
+typedef struct {
+ gchar *name;
+ gint id;
+ GList *uris;
+} RBDAAPPlaylist;
+
+RBDAAPConnection *
+rb_daap_connection_new (const gchar *host,
+ gint port,
+ RhythmDB *db,
+ RhythmDBEntryType type);
+
+gchar *
+rb_daap_connection_get_headers (RBDAAPConnection *connection,
+ const gchar *uri,
+ gint64 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 -x ignore -ur rhythmbox/daapsharing/rb-daap-mdns.c myrhythmbox/daapsharing/rb-daap-mdns.c
--- rhythmbox/daapsharing/rb-daap-mdns.c 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-mdns.c 2005-08-26 15:52:37.000000000 -0400
@@ -0,0 +1,390 @@
+/*
+ * Implentation of Multicast DNS for DAAP sharing
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include "rb-daap-mdns.h"
+
+#include <libgnome/gnome-i18n.h>
+#include "rb-dialog.h"
+
+#ifdef WITH_HOWL
+/* stupid howl includes howl_config.h */
+#undef PACKAGE
+#undef VERSION
+#include <howl.h>
+
+typedef struct _CallbackAndData {
+ gpointer callback;
+ gpointer data;
+} CallbackAndData;
+
+static gboolean
+howl_in_cb (GIOChannel *io_channel,
+ GIOCondition condition,
+ gpointer data)
+{
+ sw_discovery discovery = (sw_discovery) data;
+ sw_salt salt;
+
+ if (sw_discovery_salt (discovery, &salt) == SW_OKAY) {
+ sw_salt_lock (salt);
+ sw_discovery_read_socket (discovery);
+ sw_salt_unlock (salt);
+ }
+
+ return TRUE;
+}
+
+static void
+setup_sw_discovery (sw_discovery discovery)
+{
+ int fd;
+ GIOChannel *channel;
+
+ fd = sw_discovery_socket (discovery);
+
+ channel = g_io_channel_unix_new (fd);
+ g_io_add_watch (channel, G_IO_IN, howl_in_cb, discovery);
+ g_io_channel_unref (channel);
+
+ return;
+}
+
+static sw_discovery
+get_sw_discovery ()
+{
+ sw_result result;
+ static sw_discovery discovery = NULL;
+ static gboolean initialized = FALSE;
+
+ if (initialized == FALSE) {
+ result = sw_discovery_init (&discovery);
+
+ if (result != SW_OKAY) {
+ g_warning ("Error starting mDNS discovery");
+ rb_error_dialog (NULL, _("Unable to browse for remote shares"), _("Could not start browsing for music servers on your network. Rhythmbox will continue to function, without this feature. Check your %s installation"), "Howl");
+ return NULL;
+ }
+
+ setup_sw_discovery (discovery);
+
+ initialized = TRUE;
+ }
+
+ return discovery;
+}
+
+static sw_result
+browse_cb (sw_discovery discovery,
+ sw_discovery_oid oid,
+ sw_discovery_browse_status status,
+ sw_uint32 interface_index,
+ sw_const_string name,
+ sw_const_string type,
+ sw_const_string domain,
+ sw_opaque extra)
+{
+ CallbackAndData *cd = (CallbackAndData *) extra;
+ RBDAAPmDNSBrowserStatus bstatus;
+
+ if (status == SW_DISCOVERY_BROWSE_ADD_SERVICE) {
+ bstatus = RB_DAAP_MDNS_BROWSER_ADD_SERVICE;
+ } else if (status == SW_DISCOVERY_BROWSE_REMOVE_SERVICE) {
+ bstatus = RB_DAAP_MDNS_BROWSER_REMOVE_SERVICE;
+ } else {
+ return SW_OKAY;
+ }
+
+ ((RBDAAPmDNSBrowserCallback)cd->callback) ((RBDAAPmDNSBrowser) oid,
+ bstatus,
+ (const gchar *) name,
+ cd->data);
+
+ return SW_OKAY;
+}
+
+gboolean
+rb_daap_mdns_browse (RBDAAPmDNSBrowser *browser,
+ RBDAAPmDNSBrowserCallback callback,
+ gpointer user_data)
+{
+ sw_discovery discovery;
+
+ discovery = get_sw_discovery ();
+
+ if (discovery) {
+ static CallbackAndData cd;
+ sw_result result;
+
+ cd.callback = callback;
+ cd.data = user_data;
+
+ result = sw_discovery_browse (discovery,
+ 0,
+ "_daap._tcp",
+ "local",
+ (sw_discovery_browse_reply) browse_cb,
+ (sw_opaque) &cd,
+ (sw_discovery_oid *)browser);
+
+ if (result == SW_OKAY) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void
+rb_daap_mdns_browse_cancel (RBDAAPmDNSBrowser browser)
+{
+ sw_discovery discovery;
+
+ discovery = get_sw_discovery ();
+
+ if (discovery) {
+ sw_discovery_cancel (discovery, (sw_discovery_oid) browser);
+ }
+
+ return;
+}
+
+static sw_result
+resolve_cb (sw_discovery disc,
+ sw_discovery_oid oid,
+ sw_uint32 interface_index,
+ sw_const_string name,
+ sw_const_string type,
+ sw_const_string domain,
+ sw_ipv4_address address,
+ sw_port port,
+ sw_octets text_record,
+ sw_ulong text_record_length,
+ sw_opaque extra)
+{
+ gchar *host = g_malloc (16);
+ CallbackAndData *cd = (CallbackAndData *) extra;
+
+ sw_ipv4_address_name (address, host, 16);
+
+ sw_discovery_cancel (disc, oid);
+
+ ((RBDAAPmDNSResolverCallback)cd->callback) ((RBDAAPmDNSResolver) oid,
+ RB_DAAP_MDNS_RESOLVER_FOUND,
+ name,
+ host,
+ (guint) port,
+ cd->data);
+
+ return SW_OKAY;
+}
+
+
+gboolean
+rb_daap_mdns_resolve (RBDAAPmDNSResolver *resolver,
+ const gchar *name,
+ RBDAAPmDNSResolverCallback callback,
+ gpointer user_data)
+{
+ sw_discovery discovery;
+
+ discovery = get_sw_discovery ();
+
+ if (discovery) {
+ static CallbackAndData cd;
+ sw_result result;
+
+ cd.callback = callback;
+ cd.data = user_data;
+
+ result = sw_discovery_resolve (discovery,
+ 0,
+ name,
+ "_daap._tcp",
+ "local",
+ (sw_discovery_resolve_reply) resolve_cb,
+ (sw_opaque) &cd,
+ (sw_discovery_oid *)resolver);
+
+ if (result == SW_OKAY) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void
+rb_daap_mdns_resolve_cancel (RBDAAPmDNSResolver resolver)
+{
+ sw_discovery discovery;
+
+ discovery = get_sw_discovery ();
+
+ if (discovery) {
+ sw_discovery_cancel (discovery, (sw_discovery_oid) resolver);
+ }
+
+ return;
+}
+
+static sw_result
+publish_cb (sw_discovery discovery,
+ sw_discovery_oid oid,
+ sw_discovery_publish_status status,
+ sw_opaque extra)
+{
+ CallbackAndData *cd = (CallbackAndData *) extra;
+ RBDAAPmDNSPublisherStatus pstatus;
+
+ if (status == SW_DISCOVERY_PUBLISH_STARTED) {
+ pstatus = RB_DAAP_MDNS_PUBLISHER_STARTED;
+ } else if (status == SW_DISCOVERY_PUBLISH_NAME_COLLISION) {
+ /* This is all well and good, but howl won't report a name collision.
+ * http://lists.porchdogsoft.com/pipermail/howl-users/Week-of-Mon-20041206/000487.html
+ * So. Fuck.
+ */
+ pstatus = RB_DAAP_MDNS_PUBLISHER_COLLISION;
+ } else {
+ return SW_OKAY;
+ }
+
+ ((RBDAAPmDNSPublisherCallback)cd->callback) ((RBDAAPmDNSPublisher) oid,
+ pstatus,
+ cd->data);
+
+ return SW_OKAY;
+}
+
+gboolean
+rb_daap_mdns_publish (RBDAAPmDNSPublisher *publisher,
+ const gchar *name,
+ guint port,
+ RBDAAPmDNSPublisherCallback callback,
+ gpointer user_data)
+{
+ sw_discovery discovery;
+
+ discovery = get_sw_discovery ();
+
+ if (discovery) {
+ static CallbackAndData cd;
+ sw_result result;
+
+ cd.callback = callback;
+ cd.data = user_data;
+
+ result = sw_discovery_publish (discovery,
+ 0,
+ name,
+ "_daap._tcp",
+ "local",
+ NULL,
+ port,
+ NULL,
+ 0,
+ (sw_discovery_publish_reply) publish_cb,
+ (sw_opaque) &cd,
+ (sw_discovery_oid *)publisher);
+
+ if (result == SW_OKAY) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void
+rb_daap_mdns_publish_cancel (RBDAAPmDNSPublisher publisher)
+{
+ sw_discovery discovery;
+
+ discovery = get_sw_discovery ();
+
+ if (discovery) {
+ sw_discovery_cancel (discovery, (sw_discovery_oid) publisher);
+ }
+
+ return;
+}
+#endif
+
+#ifdef HAVE_AVAHI
+
+gboolean
+rb_daap_mdns_browse (RBDAAPmDNSBrowser *browser,
+ RBDAAPmDNSBrowserCallback callback,
+ gpointer user_data)
+{
+ g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+ return FALSE;
+}
+
+void
+rb_daap_mdns_browse_cancel (RBDAAPmDNSBrowser browser)
+{
+ g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+ return;
+}
+
+gboolean
+rb_daap_mdns_resolve (RBDAAPmDNSResolver *resolver,
+ const gchar *name,
+ RBDAAPmDNSResolverCallback callback,
+ gpointer user_data)
+{
+ g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+ return FALSE;
+}
+
+void
+rb_daap_mdns_resolve_cancel (RBDAAPmDNSResolver resolver)
+{
+ g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+ return;
+}
+
+gboolean
+rb_daap_mdns_publish (RBDAAPmDNSPublisher *publisher,
+ const gchar *name,
+ guint port,
+ RBDAAPmDNSPublisherCallback callback,
+ gpointer user_data)
+{
+ g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+ return FALSE;
+}
+
+void
+rb_daap_mdns_publish_cancel (RBDAAPmDNSPublisher publisher)
+{
+ g_warning ("IMPLEMENT ME %s:%d %s",__FILE__,__LINE__,__FUNCTION__);
+
+ return;
+}
+#endif
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/rb-daap-mdns.h myrhythmbox/daapsharing/rb-daap-mdns.h
--- rhythmbox/daapsharing/rb-daap-mdns.h 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-mdns.h 2005-08-22 22:37:37.000000000 -0400
@@ -0,0 +1,97 @@
+/*
+ * Header for abstraction of Multicast DNS for DAAP sharing
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_MDNS_H
+#define __RB_DAAP_MDNS_H
+
+#include <glib.h>
+
+/* discovering hosts */
+typedef gpointer RBDAAPmDNSBrowser;
+
+typedef enum {
+ RB_DAAP_MDNS_BROWSER_ADD_SERVICE = 1,
+ RB_DAAP_MDNS_BROWSER_REMOVE_SERVICE
+} RBDAAPmDNSBrowserStatus;
+
+typedef void (* RBDAAPmDNSBrowserCallback) (RBDAAPmDNSBrowser browser,
+ RBDAAPmDNSBrowserStatus status,
+ const gchar *name,
+ gpointer user_data);
+gboolean
+rb_daap_mdns_browse (RBDAAPmDNSBrowser *browser,
+ RBDAAPmDNSBrowserCallback callback,
+ gpointer data);
+
+void
+rb_daap_mdns_browse_cancel (RBDAAPmDNSBrowser browser);
+
+/* resolving hosts */
+typedef gpointer RBDAAPmDNSResolver;
+
+typedef enum {
+ RB_DAAP_MDNS_RESOLVER_FOUND = 1,
+ RB_DAAP_MDNS_RESOLVER_TIMEOUT
+} RBDAAPmDNSResolverStatus;
+
+typedef void (* RBDAAPmDNSResolverCallback) (RBDAAPmDNSResolver resolver,
+ RBDAAPmDNSResolverStatus status,
+ const gchar *name,
+ gchar *host,
+ guint port,
+ gpointer user_data);
+
+gboolean
+rb_daap_mdns_resolve (RBDAAPmDNSResolver *resolver,
+ const gchar *name,
+ RBDAAPmDNSResolverCallback callback,
+ gpointer data);
+
+
+void
+rb_daap_mdns_resolve_cancel (RBDAAPmDNSResolver resolver);
+
+/* publishing */
+typedef gpointer RBDAAPmDNSPublisher;
+
+typedef enum {
+ RB_DAAP_MDNS_PUBLISHER_STARTED = 1,
+ RB_DAAP_MDNS_PUBLISHER_COLLISION
+} RBDAAPmDNSPublisherStatus;
+
+typedef void (* RBDAAPmDNSPublisherCallback) (RBDAAPmDNSPublisher publisher,
+ RBDAAPmDNSPublisherStatus status,
+ gpointer user_data);
+
+gboolean
+rb_daap_mdns_publish (RBDAAPmDNSPublisher *publisher,
+ const gchar *name,
+ guint port,
+ RBDAAPmDNSPublisherCallback callback,
+ gpointer user_data);
+
+void
+rb_daap_mdns_publish_cancel (RBDAAPmDNSPublisher publisher);
+
+
+#endif /* __RB_DAAP_MDNS_H */
+
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/rb-daap-share.c myrhythmbox/daapsharing/rb-daap-share.c
--- rhythmbox/daapsharing/rb-daap-share.c 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-share.c 2005-08-25 17:58:50.000000000 -0400
@@ -0,0 +1,1288 @@
+/*
+ * Implmentation of DAAP (iTunes Music Sharing) sharing
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include "rb-daap-share.h"
+#include "rb-daap-structure.h"
+#include "rb-daap-mdns.h"
+
+#include "rb-playlist-source.h"
+#include "rb-debug.h"
+#include "eel-gconf-extensions.h"
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-uri.h>
+#include <libsoup/soup-server.h>
+#include <libsoup/soup-server-message.h>
+#include <libgnomevfs/gnome-vfs.h>
+
+#include <time.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <libgnome/gnome-i18n.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;
+ guint port;
+
+ /* mdns/zeroconf/dns-sd/rendezvous publishing things */
+ gboolean published;
+ RBDAAPmDNSPublisher publisher;
+
+ /* http server things */
+ SoupServer *server;
+ guint revision_number;
+
+ /* db things */
+ RhythmDB *db;
+ gint32 num_songs;
+ GHashTable *id_to_entry;
+ GHashTable *entry_to_id;
+ gulong entry_added_id;
+ gulong entry_deleted_id;
+
+ /* playlist things */
+ RBPlaylistManager *playlist_manager;
+};
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_DB
+};
+
+GType
+rb_daap_share_get_type (void)
+{
+ static GType rb_daap_share_type = 0;
+
+ if (rb_daap_share_type == 0) {
+ static const GTypeInfo our_info = {
+ sizeof (RBDAAPShareClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) rb_daap_share_class_init,
+ NULL,
+ NULL,
+ sizeof (RBDAAPShare),
+ 0,
+ (GInstanceInitFunc) rb_daap_share_init
+ };
+
+ rb_daap_share_type = g_type_register_static (G_TYPE_OBJECT,
+ "RBDAAPShare",
+ &our_info, 0);
+
+ }
+
+ return rb_daap_share_type;
+}
+
+static void
+rb_daap_share_class_init (RBDAAPShareClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = rb_daap_share_get_property;
+ object_class->set_property = rb_daap_share_set_property;
+ object_class->finalize = rb_daap_share_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "Name",
+ "Share Name",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_DB,
+ g_param_spec_object ("db",
+ "RhythmDB",
+ "RhythmDB object",
+ RHYTHMDB_TYPE,
+ G_PARAM_READABLE));
+
+ return;
+}
+
+static void
+rb_daap_share_init (RBDAAPShare *share)
+{
+ share->priv = g_new0 (RBDAAPSharePrivate, 1);
+ share->priv->revision_number = 5;
+
+ return;
+}
+
+static void
+rb_daap_share_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RBDAAPShare *share = RB_DAAP_SHARE (object);
+
+ switch (prop_id) {
+ case PROP_NAME: {
+ gboolean restart_publish = FALSE;
+
+ if (share->priv->name && share->priv->published) {
+ rb_daap_share_stop_publish (share);
+ g_free (share->priv->name);
+ restart_publish = TRUE;
+ }
+
+ share->priv->name = g_value_dup_string (value);
+
+ if (restart_publish) {
+ rb_daap_share_start_publish (share);
+ }
+
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+rb_daap_share_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ RBDAAPShare *share = RB_DAAP_SHARE (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, share->priv->name);
+ break;
+ case PROP_DB:
+ g_value_set_object (value, share->priv->db);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+rb_daap_share_finalize (GObject *object)
+{
+ RBDAAPShare *share = RB_DAAP_SHARE (object);
+
+ if (share->priv->published) {
+ rb_daap_share_stop_publish (share);
+ }
+
+ if (share->priv) {
+ g_free (share->priv->name);
+ g_object_unref (share->priv->db);
+ g_object_unref (share->priv->playlist_manager);
+ g_free (share->priv);
+ share->priv = NULL;
+ }
+
+ return;
+}
+
+
+RBDAAPShare *
+rb_daap_share_new (const gchar *name,
+ RhythmDB *db,
+ RBPlaylistManager *playlist_manager)
+{
+ RBDAAPShare *share;
+
+ share = RB_DAAP_SHARE (g_object_new (RB_TYPE_DAAP_SHARE, "name", name, NULL));
+ share->priv->db = g_object_ref (db);
+ share->priv->playlist_manager = g_object_ref (playlist_manager);
+
+ rb_daap_share_start_publish (share);
+
+ return share;
+}
+
+static void
+message_add_standard_headers (SoupMessage *message)
+{
+ gchar *s;
+ time_t t;
+ struct tm *tm;
+
+ soup_message_add_header (message->response_headers, "DAAP-Server", "Rhythmbox " VERSION);
+
+ soup_message_add_header (message->response_headers, "Content-Type", "application/x-dmap-tagged");
+
+ t = time (NULL);
+ tm = gmtime (&t);
+ s = g_new (gchar, 100);
+ strftime (s, 100, "%a, %d %b %Y %T GMT", tm);
+ soup_message_add_header (message->response_headers, "Date", s);
+ g_free (s);
+
+ return;
+}
+
+static void
+message_set_from_rb_daap_structure (SoupMessage *message,
+ GNode *structure)
+{
+ gchar *resp;
+ guint length;
+
+ resp = rb_daap_structure_serialize (structure, &length);
+
+ if (resp == NULL) {
+ g_print ("serialize gave us null?\n");
+ return;
+ }
+
+ message->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
+ message->response.length = length;
+ message->response.body = resp;
+
+ message_add_standard_headers (message);
+
+ soup_message_set_status (message, SOUP_STATUS_OK);
+ soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CONTENT_LENGTH);
+
+ return;
+}
+
+#define DMAP_STATUS_OK 200
+
+#define DMAP_VERSION 2.0
+#define DAAP_VERSION 3.0
+#define DMAP_TIMEOUT 1800
+
+static void
+server_info_cb (RBDAAPShare *share,
+ SoupMessage *message)
+{
+/* MSRV server info response
+ * MSTT status
+ * MPRO dmap version
+ * APRO daap version
+ * MINM name
+ * MSAU authentication method
+ * MSLR login required
+ * MSTM timeout interval
+ * MSAL supports auto logout
+ * MSUP supports update
+ * MSPI supports persistent ids
+ * MSEX supports extensions
+ * MSBR supports browse
+ * MSQY supports query
+ * MSIX supports index
+ * MSRS supports resolve
+ * MSDC databases count
+ */
+ GNode *msrv;
+
+ msrv = rb_daap_structure_add (NULL, RB_DAAP_CC_MSRV);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MPRO, (gdouble) DMAP_VERSION);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_APRO, (gdouble) DAAP_VERSION);
+ /* 2/3 is for itunes 4.8 (at least). its determined by the
+ * Client-DAAP-Version header sent, but if we decide not to support
+ * older versions..? anyway
+ *
+ * 1.0 is 1/1
+ * 2.0 is 1/2
+ * 3.0 is 2/3
+ */
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MINM, share->priv->name);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSAU, 0);
+ /* authentication method
+ * 0 is nothing
+ * 1 is name & password
+ * 2 is password only
+ */
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSLR, 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSTM, (gint32) DMAP_TIMEOUT);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSAL, (gchar) 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSUP, (gchar) 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSPI, (gchar) 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSEX, (gchar) 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSBR, (gchar) 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSQY, (gchar) 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSIX, (gchar) 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSRS, (gchar) 0);
+ rb_daap_structure_add (msrv, RB_DAAP_CC_MSDC, (gint32) 1);
+
+ message_set_from_rb_daap_structure (message, msrv);
+ rb_daap_structure_destroy (msrv);
+
+ return;
+}
+
+static void
+content_codes_cb (RBDAAPShare *share,
+ SoupMessage *message)
+{
+/* MCCR content codes response
+ * MSTT status
+ * MDCL dictionary
+ * MCNM content codes number
+ * MCNA content codes name
+ * MCTY content codes type
+ * MDCL dictionary
+ * ...
+ */
+ const RBDAAPContentCodeDefinition *defs;
+ guint num_defs = 0;
+ guint i;
+ GNode *mccr;
+
+ defs = rb_daap_content_codes (&num_defs);
+
+ mccr = rb_daap_structure_add (NULL, RB_DAAP_CC_MCCR);
+ rb_daap_structure_add (mccr, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+
+ for (i = 0; i < num_defs; i++) {
+ GNode *mdcl;
+
+ mdcl = rb_daap_structure_add (mccr, RB_DAAP_CC_MDCL);
+ rb_daap_structure_add (mdcl, RB_DAAP_CC_MCNM, rb_daap_content_code_string_as_int32(defs[i].string));
+ rb_daap_structure_add (mdcl, RB_DAAP_CC_MCNA, defs[i].name);
+ rb_daap_structure_add (mdcl, RB_DAAP_CC_MCTY, (gint32) defs[i].type);
+ }
+
+ message_set_from_rb_daap_structure (message, mccr);
+ rb_daap_structure_destroy (mccr);
+
+ return;
+}
+
+/* This is arbitrary. iTunes communicates with a session id for
+ * reasons relating to updates to the database and such, I think
+ * Since we don't do that, and since we don't keep track of connections
+ * like iTunes do, everyone can just share the same, special, arbitrary
+ * session id.
+ */
+#define DAAP_SESSION_ID 42
+
+static void
+login_cb (RBDAAPShare *share,
+ SoupMessage *message)
+{
+/* MLOG login response
+ * MSTT status
+ * MLID session id
+ */
+ GNode *mlog;
+
+ mlog = rb_daap_structure_add (NULL, RB_DAAP_CC_MLOG);
+ rb_daap_structure_add (mlog, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+ rb_daap_structure_add (mlog, RB_DAAP_CC_MLID, (gint32) DAAP_SESSION_ID);
+
+ message_set_from_rb_daap_structure (message, mlog);
+ rb_daap_structure_destroy (mlog);
+
+ return;
+}
+
+static void
+update_cb (RBDAAPShare *share,
+ SoupMessage *message)
+{
+ gchar *path;
+ gchar *revision_number_position;
+ guint revision_number;
+
+ path = soup_uri_to_string (soup_message_get_uri (message), TRUE);
+
+ revision_number_position = strstr (path, "revision-number=");
+
+ if (revision_number_position == NULL) {
+ g_print ("client asked for an update without a revision number?!?\n");
+ g_free (path);
+ return;
+ }
+
+ revision_number_position += 16;
+ revision_number = atoi (revision_number_position);
+
+ g_free (path);
+
+ if (revision_number != share->priv->revision_number) {
+ /* MUPD update response
+ * MSTT status
+ * MUSR server revision
+ */
+ GNode *mupd;
+
+ mupd = rb_daap_structure_add (NULL, RB_DAAP_CC_MUPD);
+ rb_daap_structure_add (mupd, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+ rb_daap_structure_add (mupd, RB_DAAP_CC_MUSR, (gint32) share->priv->revision_number);
+
+ message_set_from_rb_daap_structure (message, mupd);
+ rb_daap_structure_destroy (mupd);
+ } else {
+ g_object_ref (message);
+ soup_message_io_pause (message);
+ }
+
+ return;
+}
+
+typedef enum {
+ ITEM_ID = 0,
+ ITEM_NAME,
+ ITEM_KIND,
+ PERSISTENT_ID,
+ CONTAINER_ITEM_ID,
+ SONG_ALBUM,
+ SONG_GROUPING,
+ SONG_ARTIST,
+ SONG_BITRATE,
+ SONG_BPM,
+ SONG_COMMENT,
+ SONG_COMPILATION,
+ SONG_COMPOSER,
+ SONG_DATA_KIND,
+ SONG_DATA_URL,
+ SONG_DATE_ADDED,
+ SONG_DATE_MODIFIED,
+ SONG_DISC_COUNT,
+ SONG_DISC_NUMBER,
+ SONG_DISABLED,
+ SONG_EQ_PRESET,
+ SONG_FORMAT,
+ SONG_GENRE,
+ SONG_DESCRIPTION,
+ SONG_RELATIVE_VOLUME,
+ SONG_SAMPLE_RATE,
+ SONG_SIZE,
+ SONG_START_TIME,
+ SONG_STOP_TIME,
+ SONG_TIME,
+ SONG_TRACK_COUNT,
+ SONG_TRACK_NUMBER,
+ SONG_USER_RATING,
+ SONG_YEAR
+} DAAPMetaData;
+
+struct DAAPMetaDataMap {
+ gchar *tag;
+ DAAPMetaData md;
+};
+
+struct DAAPMetaDataMap meta_data_map[] = {
+ {"dmap.itemid", ITEM_ID},
+ {"dmap.itemname", ITEM_NAME},
+ {"dmap.itemkind", ITEM_KIND},
+ {"dmap.persistentid", PERSISTENT_ID},
+ {"dmap.containeritemid", CONTAINER_ITEM_ID},
+ {"daap.songalbum", SONG_ALBUM},
+ {"daap.songartist", SONG_ARTIST},
+ {"daap.songbitrate", SONG_BITRATE},
+ {"daap.songbeatsperminute", SONG_BPM},
+ {"daap.songcomment", SONG_COMMENT},
+ {"daap.songcompilation", SONG_COMPILATION},
+ {"daap.songcomposer", SONG_COMPOSER},
+ {"daap.songdatakind", SONG_DATA_KIND},
+ {"daap.songdataurl", SONG_DATA_URL},
+ {"daap.songdateadded", SONG_DATE_ADDED},
+ {"daap.songdatemodified", SONG_DATE_MODIFIED},
+ {"daap.songdescription", SONG_DESCRIPTION},
+ {"daap.songdisabled", SONG_DISABLED},
+ {"daap.songdisccount", SONG_DISC_COUNT},
+ {"daap.songdiscnumber", SONG_DISC_NUMBER},
+ {"daap.songeqpreset", SONG_EQ_PRESET},
+ {"daap.songformat", SONG_FORMAT},
+ {"daap.songgenre", SONG_GENRE},
+ {"daap.songgrouping", SONG_GROUPING},
+ {"daap.songrelativevolume", SONG_RELATIVE_VOLUME},
+ {"daap.songsamplerate", SONG_SAMPLE_RATE},
+ {"daap.songsize", SONG_SIZE},
+ {"daap.songstarttime", SONG_START_TIME},
+ {"daap.songstoptime", SONG_STOP_TIME},
+ {"daap.songtime", SONG_TIME},
+ {"daap.songtrackcount", SONG_TRACK_COUNT},
+ {"daap.songtracknumber", SONG_TRACK_NUMBER},
+ {"daap.songuserrating", SONG_USER_RATING},
+ {"daap.songyear", SONG_YEAR}};
+
+typedef unsigned long long bitwise;
+
+struct MLCL_Bits {
+ GNode *mlcl;
+ bitwise bits;
+ gpointer pointer;
+};
+
+static gboolean
+client_requested (bitwise bits,
+ gint field)
+{
+ return 0 != (bits & (((bitwise) 1) << field));
+}
+
+#define DMAP_ITEM_KIND_AUDIO 2
+#define DAAP_SONG_DATA_KIND_NONE 0
+
+static void
+add_entry_to_mlcl (RhythmDBEntry *entry,
+ gint id,
+ struct MLCL_Bits *mb)
+{
+ GNode *mlit;
+
+ mlit = rb_daap_structure_add (mb->mlcl, RB_DAAP_CC_MLIT);
+
+ if (client_requested (mb->bits, ITEM_KIND))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIKD, (gchar) DMAP_ITEM_KIND_AUDIO);
+ if (client_requested (mb->bits, ITEM_ID))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32) id);
+ if (client_requested (mb->bits, ITEM_NAME))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MINM, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+ if (client_requested (mb->bits, PERSISTENT_ID))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MPER, (gint64) id);
+ if (client_requested (mb->bits, CONTAINER_ITEM_ID))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MCTI, (gint32) id);
+ if (client_requested (mb->bits, SONG_DATA_KIND))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASDK, (gchar) DAAP_SONG_DATA_KIND_NONE);
+ if (client_requested (mb->bits, SONG_DATA_URL))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASUL, "");
+ if (client_requested (mb->bits, SONG_ALBUM))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASAL, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+ if (client_requested (mb->bits, SONG_GROUPING))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_AGRP, "");
+ if (client_requested (mb->bits, SONG_ARTIST))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASAR, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+ if (client_requested (mb->bits, SONG_BITRATE)) {
+ gulong bitrate = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE);
+
+ if (bitrate == 0) { /* because gstreamer is stupid */
+ /* bitrate needs to be sent in kbps, kb/s
+ * a kilobit is 128 bytes
+ * if the length is L seconds,
+ * and the file is S bytes
+ * then
+ * (S / 128) / L is kbps */
+ gulong length = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
+ guint64 file_size = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
+
+ bitrate = (file_size / 128) / length;
+ }
+
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASBR, (gint32) rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE));
+ }
+ if (client_requested (mb->bits, SONG_BPM))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASBT, (gint32) 0);
+ if (client_requested (mb->bits, SONG_COMMENT))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASCM, "");
+ if (client_requested (mb->bits, SONG_COMPILATION))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASCO, (gchar) FALSE);
+ if (client_requested (mb->bits, SONG_COMPOSER))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASCP, "");
+ if (client_requested (mb->bits, SONG_DATE_ADDED))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASDA, (gint32) rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_FIRST_SEEN));
+ if (client_requested (mb->bits, SONG_DATE_MODIFIED))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASDM, (gint32) rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_MTIME));
+ if (client_requested (mb->bits, SONG_DISC_COUNT))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASDC, (gint32) 0);
+ if (client_requested (mb->bits, SONG_DISC_NUMBER))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASDN, (gint32) rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER));
+ if (client_requested (mb->bits, SONG_DISABLED))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASDB, (gchar) FALSE);
+ if (client_requested (mb->bits, SONG_EQ_PRESET))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASEQ, "");
+ if (client_requested (mb->bits, SONG_FORMAT)) {
+ const gchar *filename;
+ gchar *ext;
+
+ filename = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
+ ext = strrchr (filename, '.');
+ ext++;
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASFM, ext);
+ }
+ if (client_requested (mb->bits, SONG_GENRE))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASGN, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE));
+ if (client_requested (mb->bits, SONG_DESCRIPTION))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASDT, "");
+ if (client_requested (mb->bits, SONG_RELATIVE_VOLUME))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASRV, 0);
+ if (client_requested (mb->bits, SONG_SAMPLE_RATE))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASSR, 0);
+ if (client_requested (mb->bits, SONG_SIZE))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASSZ, (gint32) rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE));
+ if (client_requested (mb->bits, SONG_START_TIME))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASST, 0);
+ if (client_requested (mb->bits, SONG_STOP_TIME))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASSP, 0);
+ if (client_requested (mb->bits, SONG_TIME))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASTM, (gint32) (1000 * rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION)));
+ if (client_requested (mb->bits, SONG_TRACK_COUNT))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASTC, 0);
+ if (client_requested (mb->bits, SONG_TRACK_NUMBER))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASTN, (gint32) rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
+ if (client_requested (mb->bits, SONG_USER_RATING))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASUR, 0); // fixme
+ if (client_requested (mb->bits, SONG_YEAR))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ASYR, 0);
+
+ return;
+}
+
+static void
+add_playlist_to_mlcl (RBSource *source,
+ GNode *mlcl)
+{
+ /* MLIT listing item
+ * MIID item id
+ * MPER persistent item id
+ * MINM item name
+ * MIMC item count
+ */
+ GNode *mlit;
+ gchar *name;
+ RBEntryView *ev;
+ guint num_songs;
+
+ g_object_get (G_OBJECT (source), "name", &name, NULL);
+
+ ev = rb_source_get_entry_view (source);
+ num_songs = rb_entry_view_get_num_entries (ev);
+
+ mlit = rb_daap_structure_add (mlcl, RB_DAAP_CC_MLIT);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32) source);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MPER, (gint64)(gint32) source);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MINM, name);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIMC, (gint32) num_songs);
+
+ g_free (name);
+
+ return;
+}
+
+static gboolean
+add_playlist_entry_to_mlcl (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ struct MLCL_Bits *mb)
+{
+ GNode *mlit;
+ RhythmDBEntry *entry;
+ gint id;
+
+ mlit = rb_daap_structure_add (mb->mlcl, RB_DAAP_CC_MLIT);
+
+ gtk_tree_model_get (model, iter, 0, &entry, -1);
+
+ id = GPOINTER_TO_INT (g_hash_table_lookup ((GHashTable *)mb->pointer, entry));
+
+ if (client_requested (mb->bits, ITEM_KIND))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIKD, (gchar) DMAP_ITEM_KIND_AUDIO);
+ if (client_requested (mb->bits, ITEM_ID))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32) id);
+ if (client_requested (mb->bits, CONTAINER_ITEM_ID))
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MCTI, (gint32) id);
+
+ return FALSE;
+}
+
+static bitwise
+parse_meta (const gchar *s)
+{
+ gchar *start_of_attrs;
+ gchar *end_of_attrs;
+ gchar *attrs;
+ gchar **attrsv;
+ guint i;
+ bitwise bits = 0;
+
+ start_of_attrs = strstr (s, "meta=");
+ if (start_of_attrs == NULL) {
+ return 0;
+ }
+ start_of_attrs += 5;
+
+ end_of_attrs = strchr (start_of_attrs, '&');
+ if (end_of_attrs) {
+ attrs = g_strndup (start_of_attrs, end_of_attrs - start_of_attrs);
+ } else {
+ attrs = g_strdup (start_of_attrs);
+ }
+
+ attrsv = g_strsplit (attrs,",",-1);
+
+ for (i = 0; attrsv[i]; i++) {
+ guint j;
+
+ for (j = 0; j < G_N_ELEMENTS (meta_data_map); j++) {
+ if (strcmp (meta_data_map[j].tag, attrsv[i]) == 0) {
+ bits |= (((bitwise) 1) << meta_data_map[j].md);
+ }
+ }
+ }
+
+ g_free (attrs);
+ g_strfreev (attrsv);
+
+ return bits;
+}
+
+static void
+databases_cb (RBDAAPShare *share,
+ SoupMessage *message)
+{
+ gchar *path;
+ gchar *rest_of_path;
+// guint revision_number;
+
+ path = soup_uri_to_string (soup_message_get_uri (message), TRUE);
+
+ rest_of_path = strchr (path + 1, '/');
+
+ if (rest_of_path == NULL) {
+ /* AVDB server databases
+ * MSTT status
+ * MUTY update type
+ * MTCO specified total count
+ * MRCO returned count
+ * MLCL listing
+ * MLIT listing item
+ * MIID item id
+ * MPER persistent id
+ * MINM item name
+ * MIMC item count
+ * MCTC container count
+ */
+ GNode *avdb;
+ GNode *mlcl;
+ GNode *mlit;
+
+ avdb = rb_daap_structure_add (NULL, RB_DAAP_CC_AVDB);
+ rb_daap_structure_add (avdb, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+ rb_daap_structure_add (avdb, RB_DAAP_CC_MUTY, 0);
+ rb_daap_structure_add (avdb, RB_DAAP_CC_MTCO, (gint32) 1);
+ rb_daap_structure_add (avdb, RB_DAAP_CC_MRCO, (gint32) 1);
+ mlcl = rb_daap_structure_add (avdb, RB_DAAP_CC_MLCL);
+ mlit = rb_daap_structure_add (mlcl, RB_DAAP_CC_MLIT);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32) 1);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MPER, (gint64) 1);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MINM, share->priv->name);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIMC, (gint32)share->priv->num_songs);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MCTC, (gint32) 1);
+
+ message_set_from_rb_daap_structure (message, avdb);
+ rb_daap_structure_destroy (avdb);
+ } else if (g_ascii_strncasecmp ("/1/items?", rest_of_path, 9) == 0) {
+ /* ADBS database songs
+ * MSTT status
+ * MUTY update type
+ * MTCO specified total count
+ * MRCO returned count
+ * MLCL listing
+ * MLIT
+ * attrs
+ * MLIT
+ * ...
+ */
+ GNode *adbs;
+ struct MLCL_Bits mb = {NULL,0};
+
+ mb.bits = parse_meta (rest_of_path);
+
+ adbs = rb_daap_structure_add (NULL, RB_DAAP_CC_ADBS);
+ rb_daap_structure_add (adbs, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+ rb_daap_structure_add (adbs, RB_DAAP_CC_MUTY, 0);
+ rb_daap_structure_add (adbs, RB_DAAP_CC_MTCO, (gint32) share->priv->num_songs);
+ rb_daap_structure_add (adbs, RB_DAAP_CC_MRCO, (gint32) share->priv->num_songs);
+ mb.mlcl = rb_daap_structure_add (adbs, RB_DAAP_CC_MLCL);
+
+ g_hash_table_foreach (share->priv->entry_to_id, (GHFunc) add_entry_to_mlcl, &mb);
+
+ message_set_from_rb_daap_structure (message, adbs);
+ rb_daap_structure_destroy (adbs);
+ adbs = NULL;
+ } else if (g_ascii_strncasecmp ("/1/containers?", rest_of_path, 14) == 0) {
+ /* APLY database playlists
+ * MSTT status
+ * MUTY update type
+ * MTCO specified total count
+ * MRCO returned count
+ * MLCL listing
+ * MLIT listing item
+ * MIID item id
+ * MPER persistent item id
+ * MINM item name
+ * MIMC item count
+ * ABPL baseplaylist (only for base)
+ * MLIT
+ * ...
+ */
+ GNode *aply;
+ GNode *mlcl;
+ GNode *mlit;
+ GList *playlists;
+
+ aply = rb_daap_structure_add (NULL, RB_DAAP_CC_APLY);
+ rb_daap_structure_add (aply, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+ rb_daap_structure_add (aply, RB_DAAP_CC_MUTY, 0);
+ rb_daap_structure_add (aply, RB_DAAP_CC_MTCO, (gint32) 1);
+ rb_daap_structure_add (aply, RB_DAAP_CC_MRCO, (gint32) 1);
+ mlcl = rb_daap_structure_add (aply, RB_DAAP_CC_MLCL);
+ mlit = rb_daap_structure_add (mlcl, RB_DAAP_CC_MLIT);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIID, (gint32) 1);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MPER, (gint64) 1);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MINM, share->priv->name);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_MIMC, (gint32) share->priv->num_songs);
+ rb_daap_structure_add (mlit, RB_DAAP_CC_ABPL, (gchar) 1); /* base playlist */
+
+ playlists = rb_playlist_manager_get_playlists (share->priv->playlist_manager);
+ g_list_foreach (playlists, (GFunc)add_playlist_to_mlcl, mlcl);
+
+ message_set_from_rb_daap_structure (message, aply);
+ rb_daap_structure_destroy (aply);
+ } else if (g_ascii_strncasecmp ("/1/containers/", rest_of_path, 14) == 0) {
+ /* APSO playlist songs
+ * MSTT status
+ * MUTY update type
+ * MTCO specified total count
+ * MRCO returned count
+ * MLCL listing
+ * MLIT listing item
+ * MIKD item kind
+ * MIID item id
+ * MCTI container item id
+ * MLIT
+ * ...
+ */
+ GNode *apso;
+ struct MLCL_Bits mb = {NULL,0};
+ gint pl_id = atoi (rest_of_path + 14);
+
+ mb.bits = parse_meta (rest_of_path);
+
+ apso = rb_daap_structure_add (NULL, RB_DAAP_CC_APSO);
+ rb_daap_structure_add (apso, RB_DAAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
+ rb_daap_structure_add (apso, RB_DAAP_CC_MUTY, 0);
+
+ if (pl_id == 1) {
+ rb_daap_structure_add (apso, RB_DAAP_CC_MTCO, (gint32) share->priv->num_songs);
+ rb_daap_structure_add (apso, RB_DAAP_CC_MRCO, (gint32) share->priv->num_songs);
+ mb.mlcl = rb_daap_structure_add (apso, RB_DAAP_CC_MLCL);
+
+ g_hash_table_foreach (share->priv->entry_to_id, (GHFunc) add_entry_to_mlcl, &mb);
+ } else {
+ RBSource *source = GINT_TO_POINTER (pl_id);
+ RBEntryView *ev;
+ guint num_songs;
+ RhythmDBQueryModel *model;
+
+ mb.mlcl = rb_daap_structure_add (apso, RB_DAAP_CC_MLCL);
+
+ mb.pointer = share->priv->entry_to_id;
+
+ ev = rb_source_get_entry_view (source);
+ num_songs = rb_entry_view_get_num_entries (ev);
+
+ rb_daap_structure_add (apso, RB_DAAP_CC_MTCO, (gint32) num_songs);
+ rb_daap_structure_add (apso, RB_DAAP_CC_MRCO, (gint32) num_songs);
+
+ g_object_get (G_OBJECT (source), "query-model", &model, NULL);
+ gtk_tree_model_foreach (GTK_TREE_MODEL (model), (GtkTreeModelForeachFunc) add_playlist_entry_to_mlcl, &mb);
+ g_object_unref (model);
+
+ }
+
+ message_set_from_rb_daap_structure (message, apso);
+ rb_daap_structure_destroy (apso);
+ } else if (g_ascii_strncasecmp ("/1/items/", rest_of_path, 9) == 0) {
+ /* just the file :) */
+ gchar *id_str;
+ gint id;
+ RhythmDBEntry *entry;
+ const gchar *location;
+ guint64 file_size;
+ GnomeVFSResult result;
+ GnomeVFSHandle *handle;
+ const gchar *range_header;
+ gchar *buf;
+ guint status_code = SOUP_STATUS_OK;
+
+ id_str = rest_of_path + 9;
+ id = atoi (id_str);
+
+ entry = g_hash_table_lookup (share->priv->id_to_entry, GINT_TO_POINTER (id));
+ location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
+ file_size = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
+
+ result = gnome_vfs_open (&handle, location, GNOME_VFS_OPEN_READ);
+ if (result != GNOME_VFS_OK) {
+ soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ goto out;
+ }
+
+ range_header = soup_message_get_header (message->request_headers, "Range");
+ if (range_header) {
+ const gchar *s;
+ GnomeVFSFileOffset range;
+ gchar *content_range;
+
+ s = range_header + 6; // bytes=
+ range = atoll (s);
+
+ result = gnome_vfs_seek (handle, GNOME_VFS_SEEK_START, range);
+
+ if (result != GNOME_VFS_OK) {
+ g_message ("Error seeking: %s", gnome_vfs_result_to_string (result));
+ soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ goto out;
+ }
+
+ status_code = SOUP_STATUS_PARTIAL_CONTENT;
+
+ content_range = g_strdup_printf ("bytes %"GNOME_VFS_OFFSET_FORMAT_STR"-%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT, range, file_size, file_size);
+ soup_message_add_header (message->response_headers, "Content-Range", content_range);
+ g_free (content_range);
+
+ file_size -= range;
+ }
+
+ /* FIXME FIXME FIXME
+ * Ideally, it seems that an mmap type solution should be used
+ * here. However, this works for now.
+ */
+ buf = g_try_malloc (file_size);
+ if (buf == NULL) {
+ g_warning ("Unable to malloc %"G_GUINT64_FORMAT" bytes to transfer file", file_size);
+ soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ goto out;
+ }
+
+ result = gnome_vfs_read (handle, buf, file_size, NULL);
+ if (result != GNOME_VFS_OK) {
+ soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ g_free (buf);
+ goto out;
+ }
+
+ message_add_standard_headers (message);
+ soup_message_add_header (message->response_headers, "Accept-Ranges", "bytes");
+ message->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
+ message->response.length = file_size;
+ message->response.body = buf;
+
+ gnome_vfs_close (handle);
+
+ soup_message_set_status (message, status_code);
+ soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (message), SOUP_TRANSFER_CONTENT_LENGTH);
+
+ } else {
+ g_print ("unhandled: %s\n", path);
+ }
+
+out:
+ g_free (path);
+
+ return;
+}
+
+typedef void (* DAAPPathFunction) (RBDAAPShare *share, SoupMessage *message);
+
+struct DAAPPath {
+ const gchar *path;
+ guint path_length;
+ DAAPPathFunction function;
+};
+
+static const struct DAAPPath paths_to_functions[] = {
+ {"/server-info", 12, server_info_cb},
+ {"/content-codes", 14, content_codes_cb},
+ {"/login", 6, login_cb},
+ {"/update", 7, update_cb},
+ {"/databases", 10, databases_cb}
+};
+
+static void
+server_cb (SoupServerContext *context,
+ SoupMessage *message,
+ RBDAAPShare *share)
+{
+ gchar *path;
+ guint i;
+
+ path = soup_uri_to_string (soup_message_get_uri (message), TRUE);
+
+ for (i = 0; i < G_N_ELEMENTS (paths_to_functions); i++) {
+ if (g_ascii_strncasecmp (paths_to_functions[i].path, path, paths_to_functions[i].path_length) == 0) {
+ paths_to_functions[i].function (share, message);
+
+ return;
+ }
+ }
+
+ g_warning ("unhandled path %s\n", soup_uri_to_string (soup_message_get_uri (message), TRUE));
+
+ g_free (path);
+
+ return;
+}
+
+static void
+add_db_entry (RhythmDBEntry *entry,
+ RBDAAPShare *share)
+{
+ RhythmDBEntryType type;
+
+ type = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE);
+
+ if (type == rhythmdb_entry_song_get_type ()) {
+ share->priv->num_songs++;
+
+ g_hash_table_insert (share->priv->id_to_entry, GINT_TO_POINTER (share->priv->num_songs), entry);
+ g_hash_table_insert (share->priv->entry_to_id, entry, GINT_TO_POINTER (share->priv->num_songs));
+ }
+
+ return;
+}
+
+static void
+db_entry_added_cb (RhythmDB *db,
+ RhythmDBEntry *entry,
+ RBDAAPShare *share)
+{
+ RhythmDBEntryType type;
+
+ type = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE);
+
+ if (type == rhythmdb_entry_song_get_type ()) {
+ share->priv->num_songs++;
+
+ g_hash_table_insert (share->priv->id_to_entry, GINT_TO_POINTER (share->priv->num_songs), entry);
+ g_hash_table_insert (share->priv->entry_to_id, entry, GINT_TO_POINTER (share->priv->num_songs));
+ }
+
+ return;
+}
+
+static void
+db_entry_deleted_cb (RhythmDB *db,
+ RhythmDBEntry *entry,
+ RBDAAPShare *share)
+{
+ RhythmDBEntryType type;
+
+ type = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE);
+
+ if (type == rhythmdb_entry_song_get_type ()) {
+ gpointer id;
+
+ id = g_hash_table_lookup (share->priv->entry_to_id, entry);
+ g_hash_table_remove (share->priv->entry_to_id, entry);
+ g_hash_table_remove (share->priv->id_to_entry, id);
+
+ share->priv->num_songs--;
+ }
+
+ return;
+}
+
+#define CONF_NAME "/apps/rhythmbox/sharing/share_name"
+
+static void
+publish_cb (RBDAAPmDNSPublisher publisher,
+ RBDAAPmDNSPublisherStatus status,
+ RBDAAPShare *share)
+{
+ switch (status) {
+ case RB_DAAP_MDNS_PUBLISHER_STARTED:
+ rb_debug ("mDNS publish successful");
+ share->priv->published = TRUE;
+ break;
+ case RB_DAAP_MDNS_PUBLISHER_COLLISION: {
+ GtkWidget *dialog;
+ gchar *s;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *hbox;
+ GtkWidget *entry;
+ gint result;
+
+ g_message ("Duplicate share name on mDNS");
+
+ dialog = gtk_dialog_new_with_buttons (_("Invalid share name"),
+ NULL,
+ 0,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ NULL);
+
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
+
+ s = g_strconcat (_("The share name '"),
+ share->priv->name,
+ _("' is already taken. Please choose another."),
+ NULL);
+ label = gtk_label_new (s);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ g_free (s);
+
+ hbox = gtk_hbox_new (FALSE,5);
+ 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);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+
+ gtk_entry_set_text (GTK_ENTRY (entry), share->priv->name);
+
+ gtk_widget_show_all (dialog);
+
+run:
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (result == GTK_RESPONSE_OK) {
+ const gchar *new_share_name = gtk_entry_get_text (GTK_ENTRY (entry));
+ eel_gconf_set_string (CONF_NAME, new_share_name);
+ gtk_widget_destroy (dialog);
+ } else {
+ goto run;
+ }
+
+ break;
+ }
+ }
+
+ return;
+}
+
+static void
+rb_daap_share_start_publish (RBDAAPShare *share)
+{
+ gint port = SOUP_ADDRESS_ANY_PORT;
+ gboolean ret;
+
+ share->priv->server = soup_server_new (SOUP_SERVER_PORT, port, NULL);
+ if (share->priv->server == NULL) {
+ g_warning ("Unable to start music sharing server");
+ return;
+ }
+
+ share->priv->port = soup_server_get_port (share->priv->server);
+ rb_debug ("Started DAAP server on port %d", port);
+
+ soup_server_add_handler (share->priv->server,
+ NULL,
+ NULL,
+ (SoupServerCallbackFn)server_cb,
+ NULL,
+ share);
+ soup_server_run_async (share->priv->server);
+
+ ret = rb_daap_mdns_publish (&(share->priv->publisher),
+ share->priv->name,
+ share->priv->port,
+ (RBDAAPmDNSPublisherCallback) publish_cb,
+ share);
+
+ if (ret == FALSE) {
+ g_warning ("Unable to notify network of music sharing");
+ return;
+ }
+
+ rb_debug ("Published DAAP server information to mdns");
+
+ share->priv->id_to_entry = g_hash_table_new (NULL, NULL);
+ share->priv->entry_to_id = g_hash_table_new (NULL, NULL);
+ share->priv->num_songs = 0;
+
+ rhythmdb_entry_foreach (share->priv->db, (GFunc)add_db_entry, share);
+
+ share->priv->entry_added_id = g_signal_connect (G_OBJECT (share->priv->db), "entry-added", G_CALLBACK (db_entry_added_cb), share);
+ share->priv->entry_deleted_id = g_signal_connect (G_OBJECT (share->priv->db), "entry-deleted", G_CALLBACK (db_entry_deleted_cb), share);
+
+ return;
+}
+
+static void
+rb_daap_share_stop_publish (RBDAAPShare *share)
+{
+ if (share->priv->server) {
+ /* FIXME */
+ /* This will spew:
+ * GLib-CRITICAL **: g_main_loop_quit: assertion `loop != NULL' failed
+ * But it doesn't seem to matter.
+ */
+// soup_server_quit (share->priv->server);
+ g_object_unref (G_OBJECT (share->priv->server));
+ share->priv->server = NULL;
+ }
+
+ if (share->priv->id_to_entry) {
+ g_hash_table_destroy (share->priv->id_to_entry);
+ share->priv->id_to_entry = NULL;
+ }
+
+ if (share->priv->entry_to_id) {
+ g_hash_table_destroy (share->priv->entry_to_id);
+ share->priv->entry_to_id = NULL;
+ }
+
+ if (share->priv->entry_added_id != 0) {
+ g_signal_handler_disconnect (share->priv->db, share->priv->entry_added_id);
+ share->priv->entry_added_id = 0;
+ }
+
+ if (share->priv->entry_deleted_id != 0) {
+ g_signal_handler_disconnect (share->priv->db, share->priv->entry_deleted_id);
+ share->priv->entry_deleted_id = 0;
+ }
+
+ if (share->priv->publisher) {
+ rb_daap_mdns_publish_cancel (share->priv->publisher);
+ share->priv->publisher = 0;
+ }
+
+ share->priv->published = FALSE;
+
+ return;
+}
+
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/rb-daap-share.h myrhythmbox/daapsharing/rb-daap-share.h
--- rhythmbox/daapsharing/rb-daap-share.h 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-share.h 2005-08-20 21:47:51.000000000 -0400
@@ -0,0 +1,60 @@
+/*
+ * Header for DAAP (iTunes Music Sharing) sharing
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_SHARE_H
+#define __RB_DAAP_SHARE_H
+
+#include <glib-object.h>
+#include "rhythmdb.h"
+#include "rb-playlist-manager.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_DAAP_SHARE (rb_daap_share_get_type ())
+#define RB_DAAP_SHARE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DAAP_SHARE, RBDAAPShare))
+#define RB_DAAP_SHARE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DAAP_SHARE, RBDAAPShareClass))
+#define RB_IS_DAAP_SHARE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DAAP_SHARE))
+#define RB_IS_DAAP_SHARE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DAAP_SHARE))
+#define RB_DAAP_SHARE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DAAP_SHARE, RBDAAPShareClass))
+
+typedef struct RBDAAPSharePrivate RBDAAPSharePrivate;
+
+typedef struct {
+ GObject parent;
+
+ RBDAAPSharePrivate *priv;
+} RBDAAPShare;
+
+typedef struct {
+ GObjectClass parent;
+} RBDAAPShareClass;
+
+GType
+rb_daap_share_get_type (void);
+
+RBDAAPShare *
+rb_daap_share_new (const gchar *name,
+ RhythmDB *db,
+ RBPlaylistManager *playlist_manager);
+
+#endif /* __RB_DAAP_SHARE_H */
+
+G_END_DECLS
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/rb-daap-src.c myrhythmbox/daapsharing/rb-daap-src.c
--- rhythmbox/daapsharing/rb-daap-src.c 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-src.c 2005-08-26 15:06:37.000000000 -0400
@@ -0,0 +1,1017 @@
+
+#include "config.h"
+
+#include "rb-daap-src.h"
+#include "rb-daap-source.h"
+
+#include <string.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include <libgnome/gnome-i18n.h>
+#include <gst/gst.h>
+
+#define RB_TYPE_DAAP_SRC (rb_daap_src_get_type())
+#define RB_DAAP_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RB_TYPE_DAAP_SRC,RBDAAPSrc))
+#define RB_DAAP_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RB_TYPE_DAAP_SRC,RBDAAPSrcClass))
+#define RB_IS_DAAP_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RB_TYPE_DAAP_SRC))
+#define RB_IS_DAAP_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),RB_TYPE_DAAP_SRC))
+
+typedef enum {
+ RB_DAAP_SRC_OPEN = GST_ELEMENT_FLAG_LAST,
+
+ RB_DAAP_SRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2
+} RBDAAPSrcFlags;
+
+typedef struct _RBDAAPSrc RBDAAPSrc;
+typedef struct _RBDAAPSrcClass RBDAAPSrcClass;
+
+struct _RBDAAPSrc
+{
+ GstElement element;
+
+ /* pads */
+ GstPad *srcpad;
+
+ /* uri */
+ gchar *http_uri;
+ gchar *daap_uri;
+
+ /* connection */
+ int port;
+ gchar *host;
+ gchar *path;
+ struct sockaddr_in server_sockaddr;
+ int sock_fd;
+
+ /* Seek stuff */
+ gboolean need_flush;
+ gboolean send_discont;
+ gint64 size;
+ gint64 curoffset;
+ gint64 seek_bytes;
+ guint32 bytes_per_read;
+};
+
+struct _RBDAAPSrcClass
+{
+ GstElementClass parent_class;
+};
+
+static GstElementClass *parent_class = NULL;
+
+static void
+rb_daap_src_base_init (gpointer g_class);
+static void
+rb_daap_src_class_init (RBDAAPSrcClass *klass);
+static void
+rb_daap_src_instance_init (RBDAAPSrc *daap_src);
+static void
+rb_daap_src_finalize (GObject *object);
+
+static void
+rb_daap_src_uri_handler_init (gpointer g_iface,
+ gpointer iface_data);
+
+static void
+rb_daap_src_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void
+rb_daap_src_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static GstCaps *
+rb_daap_src_getcaps (GstPad *pad);
+
+static GstData *
+rb_daap_src_get (GstPad *pad);
+
+static GstElementStateReturn
+rb_daap_src_change_state (GstElement *element);
+
+static void
+rb_daap_src_close_file (RBDAAPSrc *src);
+static gboolean
+rb_daap_src_open_file (RBDAAPSrc *src);
+static gboolean
+rb_daap_src_srcpad_event (GstPad *pad,
+ GstEvent *event);
+static gboolean
+rb_daap_src_srcpad_query (GstPad *pad,
+ GstQueryType type,
+ GstFormat *format,
+ gint64 *value);
+
+static GstElementDetails rb_daap_src_details =
+GST_ELEMENT_DETAILS ("RBDAAP Source",
+ "Source/File",
+ "Read a DAAP (music share) file",
+ "Charles Schmidt <cschmidt2 emich edu");
+
+static const GstFormat *
+rb_daap_src_get_formats (GstPad *pad)
+{
+ static const GstFormat formats[] = {
+ GST_FORMAT_BYTES,
+ 0,
+ };
+
+ return formats;
+}
+
+static const GstQueryType *
+rb_daap_src_get_query_types (GstPad *pad)
+{
+ static const GstQueryType types[] = {
+ GST_QUERY_TOTAL,
+ GST_QUERY_POSITION,
+ 0,
+ };
+
+ return types;
+}
+
+static const GstEventMask *
+rb_daap_src_get_event_mask (GstPad *pad)
+{
+ static const GstEventMask masks[] = {
+// {GST_EVENT_SEEK, GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_SET | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
+ {GST_EVENT_FLUSH, 0},
+ {GST_EVENT_SIZE, 0},
+ {0, 0},
+ };
+
+ return masks;
+}
+
+enum
+{
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0,
+ ARG_LOCATION,
+ ARG_SEEKABLE,
+ ARG_BYTESPERREAD
+};
+
+static GType
+rb_daap_src_get_type ()
+{
+ static GType daap_src_type = 0;
+
+ if (!daap_src_type) {
+ static const GTypeInfo daap_src_info = {
+ sizeof (RBDAAPSrcClass),
+ rb_daap_src_base_init,
+ NULL,
+ (GClassInitFunc) rb_daap_src_class_init,
+ NULL,
+ NULL,
+ sizeof (RBDAAPSrc),
+ 0,
+ (GInstanceInitFunc) rb_daap_src_instance_init,
+ };
+
+ static const GInterfaceInfo urihandler_info = {
+ rb_daap_src_uri_handler_init,
+ NULL,
+ NULL
+ };
+
+ daap_src_type = g_type_register_static (GST_TYPE_ELEMENT,
+ "RBDAAPSrc",
+ &daap_src_info,
+ 0);
+ g_type_add_interface_static (daap_src_type,
+ GST_TYPE_URI_HANDLER,
+ &urihandler_info);
+ }
+
+ return daap_src_type;
+}
+
+static void
+rb_daap_src_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details (element_class, &rb_daap_src_details);
+
+ return;
+}
+
+static void
+rb_daap_src_class_init (RBDAAPSrcClass *klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+ gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass),
+ "bytesperread", ARG_BYTESPERREAD, G_PARAM_READWRITE,
+ "location", ARG_LOCATION, G_PARAM_READWRITE, NULL);
+
+ gobject_class->finalize = rb_daap_src_finalize;
+
+ g_object_class_install_property (gobject_class,
+ ARG_SEEKABLE,
+ g_param_spec_boolean ("seekable",
+ "seekable",
+ "TRUE is stream is seekable",
+ TRUE,
+ G_PARAM_READABLE));
+
+ gstelement_class->set_property = rb_daap_src_set_property;
+ gstelement_class->get_property = rb_daap_src_get_property;
+
+ gstelement_class->change_state = rb_daap_src_change_state;
+
+ return;
+}
+
+static void
+rb_daap_src_instance_init (RBDAAPSrc *src)
+{
+ src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+ gst_pad_set_getcaps_function (src->srcpad,
+ rb_daap_src_getcaps);
+ gst_pad_set_get_function (src->srcpad,
+ rb_daap_src_get);
+ gst_pad_set_event_mask_function (src->srcpad,
+ rb_daap_src_get_event_mask);
+ gst_pad_set_event_function (src->srcpad,
+ rb_daap_src_srcpad_event);
+ gst_pad_set_query_type_function (src->srcpad,
+ rb_daap_src_get_query_types);
+ gst_pad_set_query_function (src->srcpad,
+ rb_daap_src_srcpad_query);
+ gst_pad_set_formats_function (src->srcpad,
+ rb_daap_src_get_formats);
+ gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
+
+ src->http_uri = NULL;
+ src->daap_uri = NULL;
+ src->host = NULL;
+ src->path = NULL;
+ src->curoffset = 0;
+ src->sock_fd = -1;
+ src->bytes_per_read = 4096 * 2;
+ src->seek_bytes = 0;
+ src->send_discont = FALSE;
+ src->need_flush = FALSE;
+
+ return;
+}
+
+static void
+rb_daap_src_finalize (GObject *object)
+{
+ RBDAAPSrc *src = RB_DAAP_SRC (object);
+
+ if (GST_FLAG_IS_SET (src, RB_DAAP_SRC_OPEN)) {
+ rb_daap_src_close_file (src);
+ }
+
+ if (src->daap_uri) {
+ g_free (src->daap_uri);
+ src->daap_uri = NULL;
+ }
+
+ if (src->http_uri) {
+ g_free (src->http_uri);
+ src->http_uri = NULL;
+ }
+
+ if (src->host) {
+ g_free (src->host);
+ src->host = NULL;
+ }
+
+ if (src->path) {
+ g_free (src->path);
+ src->path = NULL;
+ }
+
+ if (src->sock_fd != -1) {
+ close (src->sock_fd);
+ src->sock_fd = -1;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+
+ return;
+}
+
+static guint
+rb_daap_src_uri_get_type (void)
+{
+ return GST_URI_SRC;
+}
+
+static gchar **
+rb_daap_src_uri_get_protocols (void)
+{
+ static gchar *protocols[] = {"daap", NULL};
+
+ return protocols;
+}
+
+static const gchar *
+rb_daap_src_uri_get_uri (GstURIHandler *handler)
+{
+ RBDAAPSrc *src = RB_DAAP_SRC (handler);
+
+ return src->daap_uri;
+}
+
+static gboolean
+rb_daap_src_uri_set_uri (GstURIHandler *handler,
+ const gchar *uri)
+{
+ RBDAAPSrc *src = RB_DAAP_SRC (handler);
+
+ if (GST_STATE (src) == GST_STATE_PLAYING || GST_STATE (src) == GST_STATE_PAUSED) {
+ return FALSE;
+ }
+
+ g_object_set (G_OBJECT (src), "location", uri, NULL);
+
+ return TRUE;
+}
+
+
+static void
+rb_daap_src_uri_handler_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+ iface->get_type = rb_daap_src_uri_get_type;
+ iface->get_protocols = rb_daap_src_uri_get_protocols;
+ iface->get_uri = rb_daap_src_uri_get_uri;
+ iface->set_uri = rb_daap_src_uri_set_uri;
+
+ return;
+}
+
+static void
+rb_daap_src_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RBDAAPSrc *src;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail (RB_IS_DAAP_SRC (object));
+
+ src = RB_DAAP_SRC (object);
+
+ switch (prop_id) {
+ case ARG_LOCATION:
+ /* the element must be stopped or paused in order to do src */
+ if (GST_STATE (src) == GST_STATE_PLAYING || GST_STATE (src) == GST_STATE_PAUSED) {
+ break;
+ }
+
+ if (src->daap_uri) {
+ g_free (src->daap_uri);
+ src->daap_uri = NULL;
+ }
+ if (src->http_uri) {
+ g_free (src->http_uri);
+ src->http_uri = NULL;
+ }
+ if (src->host) {
+ g_free (src->host);
+ src->host = NULL;
+ }
+ if (src->path) {
+ g_free (src->path);
+ src->path = NULL;
+ }
+
+ if (g_value_get_string (value)) {
+ const gchar *location = g_value_get_string (value);
+ const gchar *pathstart = NULL;
+ const gchar *hostport = NULL;
+ const gchar *portstart = NULL;
+ gint locationlen;
+
+ src->daap_uri = g_strdup (location);
+ src->http_uri = g_strconcat ("http", location + 4, NULL);
+
+ locationlen = strlen (location);
+ hostport = location + 7;
+ pathstart = strchr (hostport, '/');
+
+ if (pathstart) {
+ src->path = g_strdup (pathstart);
+ } else {
+ src->path = g_strdup ("/");
+ pathstart = location + locationlen;
+ }
+
+ portstart = strrchr (hostport, ':');
+ if (portstart) {
+ src->host = g_strndup (hostport, portstart - hostport);
+ src->port = strtoul (portstart + 1, NULL, 0);
+ } else {
+ src->host = g_strndup (hostport, pathstart - hostport);
+ src->port = 3869;
+ }
+ }
+ break;
+ case ARG_BYTESPERREAD:
+ src->bytes_per_read = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ return;
+}
+
+static void
+rb_daap_src_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ RBDAAPSrc *src;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail (RB_IS_DAAP_SRC (object));
+
+ src = RB_DAAP_SRC (object);
+
+ switch (prop_id) {
+ case ARG_LOCATION:
+ g_value_set_string (value, src->daap_uri);
+ break;
+ case ARG_SEEKABLE:
+ g_value_set_boolean (value, FALSE);
+ break;
+ case ARG_BYTESPERREAD:
+ g_value_set_int (value, src->bytes_per_read);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ return;
+}
+
+/* I tell you, it'd be nice if Gstreamer's typefind element actually listened
+ * to this stuff, then it wouldnt have to do all that seeking and testing
+ * the streams against the typefind functions. Here I am, /telling/ it what
+ * sort of stream it is, and it can't even listen. Bah.
+ */
+static GstCaps *
+rb_daap_src_getcaps (GstPad *pad)
+{
+ RBDAAPSrc *src = NULL;
+ gchar *extension = NULL;
+ static GstStaticCaps mp3_caps = GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) 1, layer = (int) [ 1, 3 ]");
+ static GstStaticCaps ogg_caps = GST_STATIC_CAPS ("application/ogg");
+ static GstStaticCaps wav_caps = GST_STATIC_CAPS ("audio/x-wav");
+ static GstStaticCaps m4a_caps = GST_STATIC_CAPS ("audio/x-m4a");
+ static GstStaticCaps aac_caps = GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) { 2, 4 }, framed = (bool) false");
+
+ src = RB_DAAP_SRC (GST_OBJECT_PARENT (pad));
+
+ if (src->daap_uri == NULL) {
+ return gst_caps_new_any ();
+ }
+
+ extension = strrchr (src->daap_uri, '.');
+ extension++;
+
+ if ((g_strncasecmp (extension, "mp3", 3) == 0) ||
+ (g_strncasecmp (extension, "mp2", 3) == 0) ||
+ (g_strncasecmp (extension, "mp1", 3) == 0) ||
+ (g_strncasecmp (extension, "mpga", 4) == 0)) {
+ return gst_caps_copy (gst_static_caps_get(&mp3_caps));
+ }
+
+ if ((g_strncasecmp (extension, "ogg", 3) == 0) ||
+ (g_strncasecmp (extension, "oggm", 4) == 0)) {
+ return gst_caps_copy (gst_static_caps_get(&ogg_caps));
+ }
+
+ if ((g_strncasecmp (extension, "wav", 3) == 0)) {
+ return gst_caps_copy (gst_static_caps_get(&wav_caps));
+ }
+
+ if ((g_strncasecmp (extension, "m4a", 3) == 0)) {
+ return gst_caps_copy (gst_static_caps_get(&m4a_caps));
+ }
+
+ if ((g_strncasecmp (extension, "aac", 3) == 0)) {
+ return gst_caps_copy (gst_static_caps_get(&aac_caps));
+ }
+
+ return gst_caps_new_any ();
+}
+
+static glong seek_time = 0;
+static glong seek_time_to_return = 0;
+
+static char *
+get_headers (RBDAAPSrc *src)
+{
+ RBDAAPSource *source = NULL;
+ gchar *headers;
+
+ source = rb_daap_source_find_for_uri (src->daap_uri);
+ headers = rb_daap_source_get_headers (source, src->daap_uri, seek_time, &(src->seek_bytes));
+ if (src->seek_bytes) {
+ src->send_discont = TRUE;
+ src->need_flush = TRUE;
+ }
+ seek_time_to_return = seek_time;
+ seek_time = 0;
+
+ return headers;
+}
+
+/* read number of bytes from a socket into a given buffer incrementally.
+ * Returns number of bytes read with same semantics as read(2):
+ * < 0: error, see errno
+ * = 0: EOF
+ * > 0: bytes read
+ */
+static gint
+gst_tcp_socket_read (int socket, guchar *buf, size_t count)
+{
+ size_t bytes_read = 0;
+
+ while (bytes_read < count) {
+ ssize_t ret = read (socket, buf + bytes_read, count - bytes_read);
+
+ if (ret < 0) {
+ GST_WARNING ("error while reading: %s", g_strerror (errno));
+ }
+ if (ret <= 0) {
+ return bytes_read;
+ }
+ bytes_read += ret;
+ }
+
+ if (bytes_read < 0) {
+ GST_WARNING ("error while reading: %s", g_strerror (errno));
+ } else {
+ GST_LOG ("read %d bytes succesfully", bytes_read);
+ }
+
+ return bytes_read;
+}
+
+static gint
+gst_tcp_socket_write (int socket, const guchar *buf, size_t count)
+{
+ size_t bytes_written = 0;
+
+ while (bytes_written < count) {
+ ssize_t wrote = send (socket, buf + bytes_written, count - bytes_written, MSG_NOSIGNAL);
+
+ if (wrote <= 0) {
+ return bytes_written;
+ }
+ bytes_written += wrote;
+ }
+
+ if (bytes_written < 0) {
+ GST_WARNING ("error while writing");
+ } else {
+ GST_LOG ("wrote %d bytes succesfully", bytes_written);
+ }
+
+ return bytes_written;
+}
+
+static GstData *
+rb_daap_src_get (GstPad *pad)
+{
+ RBDAAPSrc *src;
+ size_t readsize;
+ GstBuffer *buf = NULL;
+
+ g_return_val_if_fail (pad != NULL, NULL);
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ src = RB_DAAP_SRC (GST_OBJECT_PARENT (pad));
+ g_return_val_if_fail (GST_FLAG_IS_SET (src, RB_DAAP_SRC_OPEN), NULL);
+
+
+ /* try to negotiate here */
+ if (!gst_pad_is_negotiated (pad)) {
+ if (GST_PAD_LINK_FAILED (gst_pad_renegotiate (pad))) {
+ GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL), GST_ERROR_SYSTEM);
+ gst_buffer_unref (buf);
+ return GST_DATA (gst_event_new (GST_EVENT_EOS));
+ }
+ }
+
+ if (src->need_flush) {
+ GstEvent *event = gst_event_new_flush ();
+
+ src->need_flush = FALSE;
+ return GST_DATA (event);
+ }
+
+ if (src->send_discont) {
+ GstEvent *event;
+
+ src->send_discont = FALSE;
+ event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, src->curoffset + src->seek_bytes, NULL);
+ return GST_DATA (event);
+ }
+
+ buf = gst_buffer_new ();
+ g_return_val_if_fail (buf, NULL);
+
+ GST_BUFFER_DATA (buf) = g_malloc0 (src->bytes_per_read);
+ g_return_val_if_fail (GST_BUFFER_DATA (buf) != NULL, NULL);
+
+ GST_LOG_OBJECT (src, "Reading %d bytes", src->bytes_per_read);
+ readsize = gst_tcp_socket_read (src->sock_fd, GST_BUFFER_DATA (buf), src->bytes_per_read);
+ if (readsize < 0) {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
+ gst_buffer_unref (buf);
+ return GST_DATA (gst_event_new (GST_EVENT_EOS));
+ }
+
+ /* if we read 0 bytes, and we're blocking, we hit eos */
+ if (readsize == 0) {
+ GST_DEBUG ("blocking read returns 0, EOS");
+ gst_buffer_unref (buf);
+ gst_element_set_eos (GST_ELEMENT (src));
+ return GST_DATA (gst_event_new (GST_EVENT_EOS));
+ }
+
+ GST_BUFFER_OFFSET (buf) = src->curoffset;// + src->seek_bytes;
+ GST_BUFFER_SIZE (buf) = readsize;
+ GST_BUFFER_TIMESTAMP (buf) = -1;
+
+ src->curoffset += readsize;
+ GST_LOG_OBJECT (src,
+ "Returning buffer from _get of size %d, ts %"
+ GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT
+ ", offset %" G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
+ GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
+ GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf));
+ return GST_DATA (buf);
+}
+
+static GstElementStateReturn
+rb_daap_src_change_state (GstElement *element)
+{
+ g_return_val_if_fail (RB_IS_DAAP_SRC (element), GST_STATE_FAILURE);
+
+ switch (GST_STATE_TRANSITION (element)) {
+ case GST_STATE_READY_TO_PAUSED:
+ if (!GST_FLAG_IS_SET (element, RB_DAAP_SRC_OPEN)) {
+ if (!rb_daap_src_open_file (RB_DAAP_SRC (element))) {
+ return GST_STATE_FAILURE;
+ }
+ }
+ break;
+ case GST_STATE_PAUSED_TO_READY:
+ if (GST_FLAG_IS_SET (element, RB_DAAP_SRC_OPEN)) {
+ rb_daap_src_close_file (RB_DAAP_SRC (element));
+ }
+ break;
+ case GST_STATE_NULL_TO_READY:
+ case GST_STATE_READY_TO_NULL:
+ default:
+ break;
+ }
+
+ if (GST_ELEMENT_CLASS (parent_class)->change_state) {
+ return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+ }
+
+ return GST_STATE_SUCCESS;
+}
+
+
+static gboolean
+rb_daap_src_open_file (RBDAAPSrc *src)
+{
+ int ret;
+ gchar *request = NULL;
+ gchar *headers = NULL;
+
+ seek_time_to_return = 0;
+
+ if (src->sock_fd != -1) {
+ close (src->sock_fd);
+ }
+
+ if ((src->sock_fd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+
+ src->server_sockaddr.sin_family = AF_INET;
+ src->server_sockaddr.sin_port = htons (src->port);
+ src->server_sockaddr.sin_addr.s_addr = inet_addr (src->host);
+ memset (&(src->server_sockaddr.sin_zero), '\0', 8);
+
+ /* connect to server */
+ GST_DEBUG_OBJECT (src, "connecting to server ip=%s port=%d ",
+ src->host, src->port);
+ ret = connect (src->sock_fd, (struct sockaddr *) &(src->server_sockaddr), sizeof (struct sockaddr));
+
+ if (ret) {
+ switch (errno) {
+ case ECONNREFUSED:
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Connection to %s:%d refused."), src->host, src->port),
+ (NULL));
+ return FALSE;
+ default:
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
+ ("connect to %s:%d failed: %s", src->host, src->port,
+ g_strerror (errno)));
+ return FALSE;
+ }
+ }
+
+ /* send request and headers */
+ headers = get_headers (src);
+ request = g_strdup_printf("GET %s HTTP/1.1\r\nHost: %s\r\n%s\r\n",
+ src->path, src->host, headers);
+ g_free (headers);
+ GST_DEBUG_OBJECT(src, "sending request %s", request);
+ if (gst_tcp_socket_write(src->sock_fd, (guchar *)request, strlen(request)) < 0) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
+ ("sending HTTP request to %s failed: %s", src->daap_uri,
+ g_strerror (errno)));
+ return FALSE;
+ }
+ g_free(request);
+
+
+ /* receive and discard headers */
+ /* FIXME this part is slow
+ * FIXME we should, i think, find the size from these so we can use it
+ * later
+ */
+ {
+ guchar responseline[12];
+ gint rc;
+ /* receive response line (HTTP/1.x NNN) */
+ if ((rc = gst_tcp_socket_read(src->sock_fd, (guchar *)responseline, sizeof(responseline))) < 0) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
+ ("reading HTTP response from %s failed: %s", src->daap_uri,
+ g_strerror (errno)));
+ return FALSE;
+ }
+ GST_DEBUG_OBJECT(src, "got %d byte response %s", rc, responseline);
+
+ enum response_state {
+ RESPONSE_CHAR,
+ RESPONSE_CR,
+ RESPONSE_CRLF,
+ RESPONSE_CRLFCR,
+ RESPONSE_END_OF_HEADERS /* saw crlfcrlf */
+ } response_state = RESPONSE_CHAR;
+ while (response_state != RESPONSE_END_OF_HEADERS) {
+ guchar ch;
+ if (gst_tcp_socket_read(src->sock_fd, (guchar *)&ch, sizeof(ch)) < 0) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
+ ("reading HTTP response from %s failed: %s", src->daap_uri,
+ g_strerror (errno)));
+ return FALSE;
+ }
+ switch (ch) {
+ case '\n':
+ switch (response_state) {
+ case RESPONSE_CR:
+ response_state = RESPONSE_CRLF;
+ break;
+ case RESPONSE_CRLFCR:
+ response_state = RESPONSE_END_OF_HEADERS;
+ break;
+ default:
+ response_state = RESPONSE_CHAR;
+ break;
+ }
+ break;
+ case '\r':
+ switch (response_state) {
+ case RESPONSE_CRLF:
+ response_state = RESPONSE_CRLFCR;
+ break;
+ default:
+ response_state = RESPONSE_CR;
+ break;
+ }
+ break;
+ default:
+ response_state = RESPONSE_CHAR;
+ }
+ }
+ }
+
+ GST_FLAG_SET (src, RB_DAAP_SRC_OPEN);
+
+ return TRUE;
+
+}
+
+
+static void
+rb_daap_src_close_file (RBDAAPSrc *src)
+{
+ if (src->sock_fd != -1) {
+ close (src->sock_fd);
+ src->sock_fd = -1;
+ }
+ src->seek_bytes = 0;
+ src->curoffset = 0;
+ src->size = 0;
+ src->send_discont = FALSE;
+
+ GST_FLAG_UNSET (src, RB_DAAP_SRC_OPEN);
+
+ return;
+}
+
+static gboolean
+rb_daap_src_srcpad_event (GstPad *pad,
+ GstEvent *event)
+{
+ RBDAAPSrc *src = RB_DAAP_SRC (GST_PAD_PARENT (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK: {
+ gint64 desired_offset = 0;
+
+ if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_BYTES) {
+ gst_event_unref (event);
+ return FALSE;
+ }
+
+ switch (GST_EVENT_SEEK_METHOD (event)) {
+ case GST_SEEK_METHOD_SET:
+ desired_offset = (gint64) GST_EVENT_SEEK_OFFSET (event);
+ break;
+ case GST_SEEK_METHOD_CUR:
+ desired_offset = src->curoffset + GST_EVENT_SEEK_OFFSET (event);
+ break;
+ case GST_SEEK_METHOD_END:
+ if (src->size == 0) {
+ return FALSE;
+ }
+ desired_offset = src->size - ABS (GST_EVENT_SEEK_OFFSET (event));
+ break;
+ default:
+ gst_event_unref (event);
+ return FALSE;
+ }
+
+ g_print ("desired_offset = %"G_GINT64_FORMAT" curoffset=%"G_GINT64_FORMAT"\n",desired_offset,src->curoffset);
+ return FALSE;
+ break;
+ }
+ case GST_EVENT_SIZE:
+ if (GST_EVENT_SIZE_FORMAT (event) != GST_FORMAT_BYTES) {
+ gst_event_unref (event);
+ return FALSE;
+ }
+ src->bytes_per_read = GST_EVENT_SIZE_VALUE (event);
+ g_object_notify (G_OBJECT (src), "bytesperread");
+ break;
+ case GST_EVENT_FLUSH:
+ src->need_flush = TRUE;
+ break;
+ default:
+ gst_event_unref (event);
+ return FALSE;
+ break;
+ }
+
+ gst_event_unref (event);
+
+ return TRUE;
+}
+
+static gboolean
+rb_daap_src_srcpad_query (GstPad *pad,
+ GstQueryType type,
+ GstFormat *format,
+ gint64 *value)
+{
+ RBDAAPSrc *src = RB_DAAP_SRC (gst_pad_get_parent (pad));
+
+ switch (type) {
+ case GST_QUERY_TOTAL:
+ if (*format != GST_FORMAT_BYTES || src->size == 0) {
+ return FALSE;
+ }
+ *value = src->size;
+ break;
+ case GST_QUERY_POSITION:
+ switch (*format) {
+ case GST_FORMAT_BYTES:
+ *value = src->curoffset;
+ break;
+ case GST_FORMAT_PERCENT:
+ return FALSE; /* FIXME */
+ if (src->size == 0) {
+ return FALSE;
+ }
+ *value = src->curoffset * GST_FORMAT_PERCENT_MAX / src->size;
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+plugin_init (GstPlugin *plugin)
+{
+ gboolean ret = gst_element_register (plugin, "rbdaapsrc", GST_RANK_PRIMARY, RB_TYPE_DAAP_SRC);
+
+ return ret;
+}
+
+GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "rbdaap",
+ "element to access DAAP music share files",
+ plugin_init,
+ VERSION,
+ "GPL",
+ PACKAGE,
+ "");
+
+static gboolean src_initialized = FALSE;
+
+void
+rb_daap_src_init ()
+{
+ if (src_initialized == FALSE) {
+ seek_time = 0;
+ src_initialized = TRUE;
+ }
+
+ return;
+}
+
+void
+rb_daap_src_shutdown ()
+{
+ src_initialized = FALSE;
+
+ return;
+}
+
+void
+rb_daap_src_set_time (glong time)
+{
+ seek_time = time;
+
+ return;
+}
+
+glong
+rb_daap_src_get_time ()
+{
+ return seek_time_to_return;
+}
+
+
+
+
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/rb-daap-src.h myrhythmbox/daapsharing/rb-daap-src.h
--- rhythmbox/daapsharing/rb-daap-src.h 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-src.h 2005-08-25 16:13:12.000000000 -0400
@@ -0,0 +1,18 @@
+
+
+#ifndef __RB_DAAP_SRC_H
+#define __RB_DAAP_SRC_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void rb_daap_src_init (void);
+void rb_daap_src_shutdown (void);
+
+void rb_daap_src_set_time (glong time);
+glong rb_daap_src_get_time (void);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_SRC_H */
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/rb-daap-structure.c myrhythmbox/daapsharing/rb-daap-structure.c
--- rhythmbox/daapsharing/rb-daap-structure.c 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-structure.c 2005-08-26 17:16:49.000000000 -0400
@@ -0,0 +1,744 @@
+/*
+ * Header for DAAP (iTunes Music Sharing) structures
+ *
+ * Copyright (C) 2004,2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gobject/gvaluecollector.h>
+#include "rb-daap-structure.h"
+#include "rb-debug.h"
+
+#include <gst/gstutils.h>
+
+#include <string.h>
+#include <stdarg.h>
+
+#define MAKE_CONTENT_CODE(ch0, ch1, ch2, ch3) \
+ (( (gint32)(gchar)(ch0) | ( (gint32)(gchar)(ch1) << 8 ) | \
+ ( (gint32)(gchar)(ch2) << 16 ) | \
+ ( (gint32)(gchar)(ch3) << 24 ) ))
+
+static const RBDAAPContentCodeDefinition cc_defs[] = {
+ {RB_DAAP_CC_MDCL, MAKE_CONTENT_CODE('m','d','c','l'), "dmap.dictionary", "mdcl", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MSTT, MAKE_CONTENT_CODE('m','s','t','t'), "dmap.status", "mstt", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MIID, MAKE_CONTENT_CODE('m','i','i','d'), "dmap.itemid", "miid", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MINM, MAKE_CONTENT_CODE('m','i','n','m'), "dmap.itemname", "minm", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_MIKD, MAKE_CONTENT_CODE('m','i','k','d'), "dmap.itemkind", "mikd", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MPER, MAKE_CONTENT_CODE('m','p','e','r'), "dmap.persistentid", "mper", RB_DAAP_TYPE_INT64},
+ {RB_DAAP_CC_MCON, MAKE_CONTENT_CODE('m','c','o','n'), "dmap.container", "mcon", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MCTI, MAKE_CONTENT_CODE('m','c','t','i'), "dmap.containeritemid", "mcti", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MPCO, MAKE_CONTENT_CODE('m','p','c','o'), "dmap.parentcontainerid", "mpco", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MSTS, MAKE_CONTENT_CODE('m','s','t','s'), "dmap.statusstring", "msts", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_MIMC, MAKE_CONTENT_CODE('m','i','m','c'), "dmap.itemcount", "mimc", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MCTC, MAKE_CONTENT_CODE('m','c','t','c'), "dmap.containercount", "mctc", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MRCO, MAKE_CONTENT_CODE('m','r','c','o'), "dmap.returnedcount", "mrco", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MTCO, MAKE_CONTENT_CODE('m','t','c','o'), "dmap.specifiedtotalcount", "mtco", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MLCL, MAKE_CONTENT_CODE('m','l','c','l'), "dmap.listing", "mlcl", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MLIT, MAKE_CONTENT_CODE('m','l','i','t'), "dmap.listingitem", "mlit", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MBCL, MAKE_CONTENT_CODE('m','b','c','l'), "dmap.bag", "mbcl", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MSRV, MAKE_CONTENT_CODE('m','s','r','v'), "dmap.serverinforesponse", "msrv", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MSAU, MAKE_CONTENT_CODE('m','s','a','u'), "dmap.authenticationmethod", "msau", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSLR, MAKE_CONTENT_CODE('m','s','l','r'), "dmap.loginrequired", "mslr", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MPRO, MAKE_CONTENT_CODE('m','p','r','o'), "dmap.protocolversion", "mpro", RB_DAAP_TYPE_VERSION},
+ {RB_DAAP_CC_APRO, MAKE_CONTENT_CODE('a','p','r','o'), "daap.protocolversion", "apro", RB_DAAP_TYPE_VERSION},
+ {RB_DAAP_CC_MSAL, MAKE_CONTENT_CODE('m','s','a','l'), "dmap.supportsautologout", "msal", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSUP, MAKE_CONTENT_CODE('m','s','u','p'), "dmap.supportsupdate", "msup", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSPI, MAKE_CONTENT_CODE('m','s','p','i'), "dmap.supportspersistenids", "mspi", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSEX, MAKE_CONTENT_CODE('m','s','e','x'), "dmap.supportsextensions", "msex", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSBR, MAKE_CONTENT_CODE('m','s','b','r'), "dmap.supportsbrowse", "msbr", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSQY, MAKE_CONTENT_CODE('m','s','q','y'), "dmap.supportsquery", "msqy", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSIX, MAKE_CONTENT_CODE('m','s','i','x'), "dmap.supportsindex", "msix", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSRS, MAKE_CONTENT_CODE('m','s','r','s'), "dmap.supportsresolve", "msrs", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MSTM, MAKE_CONTENT_CODE('m','s','t','m'), "dmap.timeoutinterval", "mstm", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MSDC, MAKE_CONTENT_CODE('m','s','d','c'), "dmap.databasescount", "msdc", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MCCR, MAKE_CONTENT_CODE('m','c','c','r'), "dmap.contentcodesresponse", "mccr", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MCNM, MAKE_CONTENT_CODE('m','c','n','m'), "dmap.contentcodesnumber", "mcnm", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MCNA, MAKE_CONTENT_CODE('m','c','n','a'), "dmap.contentcodesname", "mcna", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_MCTY, MAKE_CONTENT_CODE('m','c','t','y'), "dmap.contentcodestype", "mcty", RB_DAAP_TYPE_SHORT},
+ {RB_DAAP_CC_MLOG, MAKE_CONTENT_CODE('m','l','o','g'), "dmap.loginresponse", "mlog", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MLID, MAKE_CONTENT_CODE('m','l','i','d'), "dmap.sessionid", "mlid", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MUPD, MAKE_CONTENT_CODE('m','u','p','d'), "dmap.updateresponse", "mupd", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_MUSR, MAKE_CONTENT_CODE('m','u','s','r'), "dmap.serverrevision", "musr", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MUTY, MAKE_CONTENT_CODE('m','u','t','y'), "dmap.updatetype", "muty", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_MUDL, MAKE_CONTENT_CODE('m','u','d','l'), "dmap.deletedidlisting", "mudl", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_AVDB, MAKE_CONTENT_CODE('a','v','d','b'), "daap.serverdatabases", "avdb", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ABRO, MAKE_CONTENT_CODE('a','b','r','o'), "daap.databasebrowse", "abro", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ABAL, MAKE_CONTENT_CODE('a','b','a','l'), "daap.browsealbumlisting", "abal", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ABAR, MAKE_CONTENT_CODE('a','b','a','r'), "daap.browseartistlisting", "abar", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ABCP, MAKE_CONTENT_CODE('a','b','c','p'), "daap.browsecomposerlisting", "abcp", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ABGN, MAKE_CONTENT_CODE('a','b','g','n'), "daap.browsegenrelisting", "abgn", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ADBS, MAKE_CONTENT_CODE('a','d','b','s'), "daap.returndatabasesongs", "adbs", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ASAL, MAKE_CONTENT_CODE('a','s','a','l'), "daap.songalbum", "asal", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_ASAR, MAKE_CONTENT_CODE('a','s','a','r'), "daap.songartist", "asar", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_ASBT, MAKE_CONTENT_CODE('a','s','b','t'), "daap.songsbeatsperminute", "asbt", RB_DAAP_TYPE_SHORT},
+ {RB_DAAP_CC_ASBR, MAKE_CONTENT_CODE('a','s','b','r'), "daap.songbitrate", "asbr", RB_DAAP_TYPE_SHORT},
+ {RB_DAAP_CC_ASCM, MAKE_CONTENT_CODE('a','s','c','m'), "daap.songcomment", "ascm", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_ASCO, MAKE_CONTENT_CODE('a','s','c','o'), "daap.songcompliation", "asco", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_ASDA, MAKE_CONTENT_CODE('a','s','d','a'), "daap.songdateadded", "asda", RB_DAAP_TYPE_DATE},
+ {RB_DAAP_CC_ASDM, MAKE_CONTENT_CODE('a','s','d','m'), "daap.songdatemodified", "asdm", RB_DAAP_TYPE_DATE},
+ {RB_DAAP_CC_ASDC, MAKE_CONTENT_CODE('a','s','d','c'), "daap.songdisccount", "asdc", RB_DAAP_TYPE_SHORT},
+ {RB_DAAP_CC_ASDN, MAKE_CONTENT_CODE('a','s','d','n'), "daap.songdiscnumber", "asdn", RB_DAAP_TYPE_SHORT},
+ {RB_DAAP_CC_ASDB, MAKE_CONTENT_CODE('a','s','d','b'), "daap.songdisabled", "asdb", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_ASEQ, MAKE_CONTENT_CODE('a','s','e','q'), "daap.songeqpreset", "aseq", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_ASFM, MAKE_CONTENT_CODE('a','s','f','m'), "daap.songformat", "asfm", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_ASGN, MAKE_CONTENT_CODE('a','s','g','n'), "daap.songgenre", "asgn", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_ASDT, MAKE_CONTENT_CODE('a','s','d','t'), "daap.songdescription", "asdt", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_ASRV, MAKE_CONTENT_CODE('a','s','r','v'), "daap.songrelativevolume", "asrv", RB_DAAP_TYPE_SIGNED_INT},
+ {RB_DAAP_CC_ASSR, MAKE_CONTENT_CODE('a','s','s','r'), "daap.songsamplerate", "assr", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_ASSZ, MAKE_CONTENT_CODE('a','s','s','z'), "daap.songsize", "assz", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_ASST, MAKE_CONTENT_CODE('a','s','s','t'), "daap.songstarttime", "asst", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_ASSP, MAKE_CONTENT_CODE('a','s','s','p'), "daap.songstoptime", "assp", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_ASTM, MAKE_CONTENT_CODE('a','s','t','m'), "daap.songtime", "astm", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_ASTC, MAKE_CONTENT_CODE('a','s','t','c'), "daap.songtrackcount", "astc", RB_DAAP_TYPE_SHORT},
+ {RB_DAAP_CC_ASTN, MAKE_CONTENT_CODE('a','s','t','n'), "daap.songtracknumber", "astn", RB_DAAP_TYPE_SHORT},
+ {RB_DAAP_CC_ASUR, MAKE_CONTENT_CODE('a','s','u','r'), "daap.songuserrating", "asur", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_ASYR, MAKE_CONTENT_CODE('a','s','y','r'), "daap.songyear", "asyr", RB_DAAP_TYPE_SHORT},
+ {RB_DAAP_CC_ASDK, MAKE_CONTENT_CODE('a','s','d','k'), "daap.songdatakind", "asdk", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_ASUL, MAKE_CONTENT_CODE('a','s','u','l'), "daap.songdataurl", "asul", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_APLY, MAKE_CONTENT_CODE('a','p','l','y'), "daap.databaseplaylists", "aply", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ABPL, MAKE_CONTENT_CODE('a','b','p','l'), "daap.baseplaylist", "abpl", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_APSO, MAKE_CONTENT_CODE('a','p','s','o'), "daap.playlistsongs", "apso", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_PRSV, MAKE_CONTENT_CODE('p','r','s','v'), "daap.resolve", "prsv", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_ARIF, MAKE_CONTENT_CODE('a','r','i','f'), "daap.resolveinfo", "arif", RB_DAAP_TYPE_CONTAINER},
+ {RB_DAAP_CC_AESV, MAKE_CONTENT_CODE('a','e','S','V'), "com.applie.itunes.music-sharing-version", "aesv", RB_DAAP_TYPE_INT},
+ {RB_DAAP_CC_MSAS, MAKE_CONTENT_CODE('m','s','a','s'), "daap.authentication.schemes", "msas", RB_DAAP_TYPE_BYTE},
+ {RB_DAAP_CC_AGRP, MAKE_CONTENT_CODE('a','g','r','p'), "daap.songgrouping", "agrp", RB_DAAP_TYPE_STRING},
+ {RB_DAAP_CC_ASCP, MAKE_CONTENT_CODE('a','s','c','p'), "daap.songcomposer", "ascp", RB_DAAP_TYPE_STRING}
+ };
+
+
+const gchar *
+rb_daap_content_code_name (RBDAAPContentCode code)
+{
+ return cc_defs[code-1].name;
+}
+
+RBDAAPType
+rb_daap_content_code_rb_daap_type (RBDAAPContentCode code)
+{
+ return cc_defs[code-1].type;
+}
+
+const gchar *
+rb_daap_content_code_string (RBDAAPContentCode code)
+{
+ return cc_defs[code-1].string;
+}
+
+static GType
+rb_daap_content_code_gtype (RBDAAPContentCode code)
+{
+ switch (rb_daap_content_code_rb_daap_type (code)) {
+ case RB_DAAP_TYPE_BYTE:
+ case RB_DAAP_TYPE_SIGNED_INT:
+ return G_TYPE_CHAR;
+ case RB_DAAP_TYPE_SHORT:
+ case RB_DAAP_TYPE_INT:
+ case RB_DAAP_TYPE_DATE:
+ return G_TYPE_INT;
+ case RB_DAAP_TYPE_INT64:
+ return G_TYPE_INT64;
+ case RB_DAAP_TYPE_VERSION:
+ return G_TYPE_DOUBLE;
+ case RB_DAAP_TYPE_STRING:
+ return G_TYPE_STRING;
+ case RB_DAAP_TYPE_CONTAINER:
+ default:
+ return G_TYPE_NONE;
+ }
+}
+
+GNode *
+rb_daap_structure_add (GNode *parent,
+ RBDAAPContentCode cc,
+ ...)
+{
+ RBDAAPType rb_daap_type;
+ GType gtype;
+ RBDAAPItem *item;
+ va_list list;
+ GNode *node;
+ gchar *error = NULL;
+
+ va_start (list, cc);
+
+ rb_daap_type = rb_daap_content_code_rb_daap_type (cc);
+ gtype = rb_daap_content_code_gtype (cc);
+
+ item = g_new0(RBDAAPItem, 1);
+ item->content_code = cc;
+
+ if (gtype != G_TYPE_NONE) {
+ g_value_init (&(item->content), gtype);
+ }
+
+ if (rb_daap_type != RB_DAAP_TYPE_STRING && rb_daap_type != RB_DAAP_TYPE_CONTAINER) {
+ G_VALUE_COLLECT (&(item->content), list, G_VALUE_NOCOPY_CONTENTS, &error);
+ if (error) {
+ g_warning (error);
+ g_free (error);
+ }
+ }
+
+ switch (rb_daap_type) {
+ case RB_DAAP_TYPE_BYTE:
+ case RB_DAAP_TYPE_SIGNED_INT:
+ item->size = 1;
+ break;
+ case RB_DAAP_TYPE_SHORT:
+ item->size = 2;
+ break;
+ case RB_DAAP_TYPE_DATE:
+ case RB_DAAP_TYPE_INT:
+ case RB_DAAP_TYPE_VERSION:
+ item->size = 4;
+ break;
+ case RB_DAAP_TYPE_INT64:
+ item->size = 8;
+ break;
+ case RB_DAAP_TYPE_STRING: {
+ gchar *s = va_arg (list, gchar *);
+
+ g_value_set_string (&(item->content), s);
+
+ /* we dont use G_VALUE_COLLECT for this because we also
+ * need the length */
+ item->size = strlen (s);
+ break;
+ }
+ case RB_DAAP_TYPE_CONTAINER:
+ default:
+ break;
+ }
+
+ node = g_node_new (item);
+
+ if (parent) {
+ g_node_append (parent, node);
+
+ while (parent) {
+ RBDAAPItem *parent_item = parent->data;
+
+ parent_item->size += (4 + 4 + item->size);
+
+ parent = parent->parent;
+ }
+ }
+
+ return node;
+}
+
+static gboolean
+rb_daap_structure_node_serialize (GNode *node,
+ GByteArray *array)
+{
+ RBDAAPItem *item = node->data;
+ RBDAAPType rb_daap_type;
+ guint32 size = GINT32_TO_BE (item->size);
+
+ g_byte_array_append (array, (const guint8 *)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, (const guint8 *)&c, 1);
+
+ break;
+ }
+ case RB_DAAP_TYPE_SHORT: {
+ gint32 i = g_value_get_int (&(item->content));
+ gint16 s = GINT16_TO_BE ((gint16) i);
+
+ g_byte_array_append (array, (const guint8 *)&s, 2);
+
+ break;
+ }
+ case RB_DAAP_TYPE_DATE:
+ case RB_DAAP_TYPE_INT: {
+ gint32 i = g_value_get_int (&(item->content));
+ gint32 s = GINT32_TO_BE (i);
+
+ g_byte_array_append (array, (const guint8 *)&s, 4);
+
+ break;
+ }
+ case RB_DAAP_TYPE_VERSION: {
+ gdouble v = g_value_get_double (&(item->content));
+ gint16 major;
+ gint8 minor;
+ gint8 patch = 0;
+
+ major = (gint16)v;
+ minor = (gint8)(v - ((gdouble)major));
+
+ major = GINT16_TO_BE (major);
+
+ g_byte_array_append (array, (const guint8 *)&major, 2);
+ g_byte_array_append (array, (const guint8 *)&minor, 1);
+ g_byte_array_append (array, (const guint8 *)&patch, 1);
+
+ break;
+ }
+ case RB_DAAP_TYPE_INT64: {
+ gint64 i = g_value_get_int64 (&(item->content));
+ gint64 s = GINT64_TO_BE (i);
+
+ g_byte_array_append (array, (const guint8 *)&s, 8);
+
+ break;
+ }
+ case RB_DAAP_TYPE_STRING: {
+ const gchar *s = g_value_get_string (&(item->content));
+
+ g_byte_array_append (array, (const guint8 *)s, strlen (s));
+
+ break;
+ }
+ case RB_DAAP_TYPE_CONTAINER:
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+gchar *
+rb_daap_structure_serialize (GNode *structure,
+ guint *length)
+{
+ GByteArray *array;
+ gchar *data;
+
+ array = g_byte_array_new ();
+
+ if (structure) {
+ g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)rb_daap_structure_node_serialize, array);
+ }
+
+ data = (gchar *) array->data;
+ *length = array->len;
+ g_byte_array_free (array, FALSE);
+
+ return data;
+}
+
+static RBDAAPContentCode
+rb_daap_buffer_read_content_code (const gchar *buf)
+{
+ gint32 c = MAKE_CONTENT_CODE (buf[0], buf[1], buf[2], buf[3]);
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cc_defs); i++) {
+ if (cc_defs[i].int_code == c) {
+ return cc_defs[i].code;
+ }
+ }
+
+ return RB_DAAP_CC_INVALID;
+}
+
+#define rb_daap_buffer_read_int8(b) GST_READ_UINT8 (b)
+#define rb_daap_buffer_read_int16(b) (gint16) GST_READ_UINT16_BE (b)
+#define rb_daap_buffer_read_int32(b) (gint32) GST_READ_UINT32_BE (b)
+#define rb_daap_buffer_read_int64(b) (gint64) GST_READ_UINT64_BE (b)
+
+static gchar *
+rb_daap_buffer_read_string (const gchar *buf, gssize size)
+{
+ if (g_utf8_validate (buf, size, NULL) == TRUE) {
+ return g_strndup (buf, size);
+ }
+
+ return g_strdup ("");
+}
+
+//#define PARSE_DEBUG
+#define PARSE_DEBUG_FILE "daapbuffer"
+
+#ifdef PARSE_DEBUG
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+static void
+rb_daap_structure_parse_container_buffer (GNode *parent,
+ const guchar *buf,
+ gint buf_length)
+{
+ gint l = 0;
+
+ while (l < buf_length) {
+ RBDAAPContentCode cc;
+ gint codesize = 0;
+ RBDAAPItem *item = NULL;
+ GNode *node = NULL;
+ GType gtype;
+
+#ifdef PARSE_DEBUG
+ g_print ("l is %d and buf_length is %d\n", l, buf_length);
+#endif
+
+ /* we need at least 8 bytes, 4 of content_code and 4 of size */
+ if (buf_length - l < 8) {
+#ifdef PARSE_DEBUG
+ g_print ("Malformed response recieved\n");
+#endif
+ return;
+ }
+
+ cc = rb_daap_buffer_read_content_code ((const gchar*)&(buf[l]));
+ if (cc == RB_DAAP_CC_INVALID) {
+#ifdef PARSE_DEBUG
+ g_print ("Invalid content_code recieved\n");
+#endif
+ return;
+ }
+ l += 4;
+
+ codesize = rb_daap_buffer_read_int32(&(buf[l]));
+ /* CCCCSIZECONTENT
+ * if the buffer length (minus 8 for the content code & size)
+ * is smaller than the read codesize (ie, someone sent us
+ * a codesize that is larger than the remaining data)
+ * then get out before we start processing it
+ */
+ if (codesize > buf_length - l - 4 || codesize < 0) {
+#ifdef PARSE_DEBUG
+ g_print ("Invalid codesize %d recieved in buf_length %d\n", codesize, buf_length);
+#endif
+ return;
+ }
+ l += 4;
+
+#ifdef PARSE_DEBUG
+ g_print ("content_code = %d, codesize is %d, l is %d\n", cc, codesize, l);
+#endif
+
+ item = g_new0 (RBDAAPItem, 1);
+ item->content_code = cc;
+ node = g_node_new (item);
+ g_node_append (parent, node);
+
+ 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 = 0;
+
+ if (codesize == 1) {
+ c = (gchar) rb_daap_buffer_read_int8(&(buf[l]));
+ }
+
+ g_value_set_char (&(item->content), c);
+#ifdef PARSE_DEBUG
+ g_print ("Code: %s, content (%d): \"%c\"\n", rb_daap_content_code_string (item->content_code), codesize, (gchar)c);
+#endif
+
+ break;
+ }
+ case RB_DAAP_TYPE_SHORT: {
+ gint16 s = 0;
+
+ if (codesize == 2) {
+ 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 = 0;
+
+ if (codesize == 4) {
+ i = rb_daap_buffer_read_int32(&(buf[l]));
+ }
+
+ g_value_set_int (&(item->content), i);
+#ifdef PARSE_DEBUG
+ g_print ("Code: %s, content (%d): %d\n", rb_daap_content_code_string (item->content_code), codesize, i);
+#endif
+ break;
+ }
+ case RB_DAAP_TYPE_INT64: {
+ gint64 i = 0;
+
+ if (codesize == 8) {
+ i = rb_daap_buffer_read_int16(&(buf[l]));
+ }
+
+ g_value_set_int64 (&(item->content), i);
+#ifdef PARSE_DEBUG
+ g_print ("Code: %s, content (%d): %"G_GINT64_FORMAT"\n", rb_daap_content_code_string (item->content_code), codesize, i);
+#endif
+
+ break;
+ }
+ case RB_DAAP_TYPE_STRING: {
+ gchar *s = rb_daap_buffer_read_string ((const gchar*)&(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 = 0;
+ gint16 minor = 0;
+ gdouble v = 0;
+
+ if (codesize == 4) {
+ major = rb_daap_buffer_read_int16(&(buf[l]));
+ minor = rb_daap_buffer_read_int16(&(buf[l]) + 2);
+ }
+
+ v = (gdouble)major;
+ v += (gdouble)(minor * 0.1);
+
+ g_value_set_double (&(item->content), v);
+#ifdef PARSE_DEBUG
+ g_print ("Code: %s, content: %f\n", rb_daap_content_code_string (item->content_code), v);
+#endif
+
+ break;
+ }
+ case RB_DAAP_TYPE_CONTAINER: {
+#ifdef PARSE_DEBUG
+ g_print ("Code: %s, container\n", rb_daap_content_code_string (item->content_code));
+#endif
+ rb_daap_structure_parse_container_buffer (node,&(buf[l]), codesize);
+ break;
+ }
+ }
+
+ l += codesize;
+ }
+
+ return;
+}
+
+GNode *
+rb_daap_structure_parse (const gchar *buf,
+ gint buf_length)
+{
+ GNode *root = NULL;
+ GNode *child = NULL;
+
+#ifdef PARSE_DEBUG
+ {
+ int fd;
+
+ fd = open (PARSE_DEBUG_FILE, O_WRONLY | O_CREAT);
+ write (fd, (const void *)buf, (size_t)buf_length);
+ close (fd);
+ }
+#endif
+
+ root = g_node_new (NULL);
+
+ rb_daap_structure_parse_container_buffer (root, (guchar *)buf, buf_length);
+
+ child = root->children;
+ if (child) {
+ g_node_unlink (child);
+ }
+ g_node_destroy (root);
+
+ return child;
+}
+
+struct NodeFinder {
+ RBDAAPContentCode code;
+ GNode *node;
+};
+
+static gboolean
+gnode_find_node (GNode *node,
+ gpointer data)
+{
+ struct NodeFinder *finder = (struct NodeFinder *)data;
+ RBDAAPItem *item = node->data;
+
+ if (item->content_code == finder->code) {
+ finder->node = node;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+RBDAAPItem *
+rb_daap_structure_find_item (GNode *structure,
+ RBDAAPContentCode code)
+{
+ GNode *node = NULL;
+
+ node = rb_daap_structure_find_node (structure, code);
+
+ if (node) {
+ return node->data;
+ }
+
+ return NULL;
+}
+
+GNode *
+rb_daap_structure_find_node (GNode *structure,
+ RBDAAPContentCode code)
+{
+ struct NodeFinder *finder;
+ GNode *node = NULL;
+
+ finder = g_new0(struct NodeFinder,1);
+ finder->code = code;
+
+ g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1, gnode_find_node, finder);
+
+ node = finder->node;
+ g_free (finder);
+ finder = NULL;
+
+ return node;
+}
+
+
+
+static void
+rb_daap_item_free (RBDAAPItem *item)
+{
+ if (rb_daap_content_code_rb_daap_type (item->content_code) != RB_DAAP_TYPE_CONTAINER) {
+ g_value_unset (&(item->content));
+ }
+ g_free (item);
+
+ return;
+}
+
+static gboolean
+gnode_free_rb_daap_item (GNode *node,
+ gpointer data)
+{
+ rb_daap_item_free ((RBDAAPItem *)node->data);
+
+ return FALSE;
+}
+
+void
+rb_daap_structure_destroy (GNode *structure)
+{
+ if (structure) {
+ g_node_traverse (structure, G_IN_ORDER, G_TRAVERSE_ALL, -1, gnode_free_rb_daap_item, NULL);
+
+ g_node_destroy (structure);
+
+ structure = NULL;
+ }
+
+ return;
+}
+
+const RBDAAPContentCodeDefinition *
+rb_daap_content_codes (guint *number)
+{
+ *number = G_N_ELEMENTS (cc_defs);
+
+ return cc_defs;
+}
+
+gint32
+rb_daap_content_code_string_as_int32 (const gchar *str)
+{
+ union {
+ gint32 i;
+ gchar str[4];
+ } u;
+
+ strncpy (u.str, str, 4);
+
+ return u.i;
+}
+
+static gboolean
+print_rb_daap_item (GNode *node,
+ gpointer data)
+{
+ RBDAAPItem *item;
+ const gchar *name;
+ gchar *value;
+ gint i;
+
+ for (i = 1; i < g_node_depth (node); i++) {
+ g_print ("\t");
+ }
+
+ item = node->data;
+
+ name = rb_daap_content_code_name (item->content_code);
+
+ if (G_IS_VALUE (&(item->content))) {
+ value = g_strdup_value_contents (&(item->content));
+ } else {
+ value = g_strdup ("");
+ }
+
+ g_print ("%d, %s = %s (%d)\n", g_node_depth (node), name, value, item->size);
+ g_free (value);
+
+ return FALSE;
+}
+
+void
+rb_daap_structure_print (GNode *structure)
+{
+ if (structure) {
+ g_node_traverse (structure, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)print_rb_daap_item, NULL);
+ }
+
+ return;
+}
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/daapsharing/rb-daap-structure.h myrhythmbox/daapsharing/rb-daap-structure.h
--- rhythmbox/daapsharing/rb-daap-structure.h 2005-08-26 18:45:01.000000000 -0400
+++ myrhythmbox/daapsharing/rb-daap-structure.h 2005-08-23 13:50:34.000000000 -0400
@@ -0,0 +1,193 @@
+/*
+ * 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_INVALID = 0,
+ RB_DAAP_CC_MDCL = 1,
+ RB_DAAP_CC_MSTT,
+ RB_DAAP_CC_MIID,
+ RB_DAAP_CC_MINM,
+ RB_DAAP_CC_MIKD,
+ RB_DAAP_CC_MPER,
+ RB_DAAP_CC_MCON,
+ RB_DAAP_CC_MCTI,
+ RB_DAAP_CC_MPCO,
+ RB_DAAP_CC_MSTS, // 10
+ RB_DAAP_CC_MIMC,
+ RB_DAAP_CC_MCTC,
+ RB_DAAP_CC_MRCO,
+ RB_DAAP_CC_MTCO,
+ RB_DAAP_CC_MLCL,
+ RB_DAAP_CC_MLIT,
+ RB_DAAP_CC_MBCL,
+ RB_DAAP_CC_MSRV,
+ RB_DAAP_CC_MSAU,
+ RB_DAAP_CC_MSLR, // 20
+ RB_DAAP_CC_MPRO,
+ RB_DAAP_CC_APRO,
+ RB_DAAP_CC_MSAL,
+ RB_DAAP_CC_MSUP,
+ RB_DAAP_CC_MSPI,
+ RB_DAAP_CC_MSEX,
+ RB_DAAP_CC_MSBR,
+ RB_DAAP_CC_MSQY,
+ RB_DAAP_CC_MSIX,
+ RB_DAAP_CC_MSRS, // 30
+ RB_DAAP_CC_MSTM,
+ RB_DAAP_CC_MSDC,
+ RB_DAAP_CC_MCCR,
+ RB_DAAP_CC_MCNM,
+ RB_DAAP_CC_MCNA,
+ RB_DAAP_CC_MCTY,
+ RB_DAAP_CC_MLOG,
+ RB_DAAP_CC_MLID,
+ RB_DAAP_CC_MUPD,
+ RB_DAAP_CC_MUSR, // 40
+ RB_DAAP_CC_MUTY,
+ RB_DAAP_CC_MUDL,
+ RB_DAAP_CC_AVDB,
+ RB_DAAP_CC_ABRO,
+ RB_DAAP_CC_ABAL,
+ RB_DAAP_CC_ABAR,
+ RB_DAAP_CC_ABCP,
+ RB_DAAP_CC_ABGN,
+ RB_DAAP_CC_ADBS,
+ RB_DAAP_CC_ASAL, // 50
+ RB_DAAP_CC_ASAR,
+ RB_DAAP_CC_ASBT,
+ RB_DAAP_CC_ASBR,
+ RB_DAAP_CC_ASCM,
+ RB_DAAP_CC_ASCO,
+ RB_DAAP_CC_ASDA,
+ RB_DAAP_CC_ASDM,
+ RB_DAAP_CC_ASDC,
+ RB_DAAP_CC_ASDN,
+ RB_DAAP_CC_ASDB, // 60
+ RB_DAAP_CC_ASEQ,
+ RB_DAAP_CC_ASFM,
+ RB_DAAP_CC_ASGN,
+ RB_DAAP_CC_ASDT,
+ RB_DAAP_CC_ASRV,
+ RB_DAAP_CC_ASSR,
+ RB_DAAP_CC_ASSZ,
+ RB_DAAP_CC_ASST,
+ RB_DAAP_CC_ASSP,
+ RB_DAAP_CC_ASTM, // 70
+ RB_DAAP_CC_ASTC,
+ RB_DAAP_CC_ASTN,
+ RB_DAAP_CC_ASUR,
+ RB_DAAP_CC_ASYR,
+ RB_DAAP_CC_ASDK,
+ RB_DAAP_CC_ASUL,
+ RB_DAAP_CC_APLY,
+ RB_DAAP_CC_ABPL,
+ RB_DAAP_CC_APSO,
+ RB_DAAP_CC_PRSV, // 80
+ RB_DAAP_CC_ARIF,
+ RB_DAAP_CC_AESV,
+ RB_DAAP_CC_MSAS,
+ RB_DAAP_CC_AGRP,
+ RB_DAAP_CC_ASCP,
+} RBDAAPContentCode;
+
+typedef struct _RBDAAPItem RBDAAPItem;
+
+struct _RBDAAPItem {
+ RBDAAPContentCode content_code;
+ GValue content;
+ guint size;
+};
+
+GNode *
+rb_daap_structure_add (GNode *parent,
+ RBDAAPContentCode cc,
+ ...);
+
+gchar *
+rb_daap_structure_serialize (GNode *structure,
+ guint *length);
+
+GNode *
+rb_daap_structure_parse (const gchar *buf,
+ gint buf_length);
+
+RBDAAPItem *
+rb_daap_structure_find_item (GNode *structure,
+ RBDAAPContentCode code);
+
+GNode *
+rb_daap_structure_find_node (GNode *structure,
+ RBDAAPContentCode code);
+
+void
+rb_daap_structure_print (GNode *structure);
+
+void
+rb_daap_structure_destroy (GNode *structure);
+
+typedef enum {
+ RB_DAAP_TYPE_BYTE = 0x0001,
+ RB_DAAP_TYPE_SIGNED_INT = 0x0002,
+ RB_DAAP_TYPE_SHORT = 0x0003,
+ RB_DAAP_TYPE_INT = 0x0005,
+ RB_DAAP_TYPE_INT64 = 0x0007,
+ RB_DAAP_TYPE_STRING = 0x0009,
+ RB_DAAP_TYPE_DATE = 0x000A,
+ RB_DAAP_TYPE_VERSION = 0x000B,
+ RB_DAAP_TYPE_CONTAINER = 0x000C
+} RBDAAPType;
+
+typedef struct _RBDAAPContentCodeDefinition RBDAAPContentCodeDefinition;
+
+struct _RBDAAPContentCodeDefinition {
+ RBDAAPContentCode code;
+ gint32 int_code;
+ const gchar *name;
+ const gchar *string;
+ RBDAAPType type;
+};
+
+const RBDAAPContentCodeDefinition *
+rb_daap_content_codes (guint *number);
+
+gint32
+rb_daap_content_code_string_as_int32 (const gchar *str);
+
+const gchar *
+rb_daap_content_code_name (RBDAAPContentCode code);
+
+RBDAAPType
+rb_daap_content_code_rb_daap_type (RBDAAPContentCode code);
+
+const gchar *
+rb_daap_content_code_string (RBDAAPContentCode code);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_STRUCTURE_H */
diff -x cvs -x CVS -x ignore -ur rhythmbox/data/rhythmbox.schemas myrhythmbox/data/rhythmbox.schemas
--- rhythmbox/data/rhythmbox.schemas 2004-09-29 17:38:09.000000000 -0400
+++ myrhythmbox/data/rhythmbox.schemas 2005-08-04 20:33:01.000000000 -0400
@@ -334,5 +334,38 @@
<long>Audio disc burner device.</long>
</locale>
</schema>
+ <schema>
+ <key>/schemas/apps/rhythmbox/sharing/enable_browsing</key>
+ <applyto>/apps/rhythmbox/sharing/enable_browsing</applyto>
+ <owner>rhythmbox</owner>
+ <type>bool</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Enables browsing your network for computers with shared music</short>
+ <long>When enabled, Rhythmbox will search for computers running Rhythmbox or Apple iTunes with shared music on your local network</long>
+ </locale>
+ </schema>
+ <schema>
+ <key>/schemas/apps/rhythmbox/sharing/enable_sharing</key>
+ <applyto>/apps/rhythmbox/sharing/enable_sharing</applyto>
+ <owner>rhythmbox</owner>
+ <type>bool</type>
+ <default>0</default>
+ <locale name="C">
+ <short>Enables sharing your music with computers on your network</short>
+ <long>When enabled, Rhythmbox will share your music with other computers running Rhythmbox or Apple iTunes</long>
+ </locale>
+ </schema>
+ <schema>
+ <key>/schemas/apps/rhythmbox/sharing/share_name</key>
+ <applyto>/apps/rhythmbox/sharing/share_name</applyto>
+ <owner>rhythmbox</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>Name for your shared music</short>
+ <long>Name other computers will see your music shared as</long>
+ </locale>
+ </schema>
</schemalist>
</gconfschemafile>
diff -x cvs -x CVS -x ignore -ur rhythmbox/data/ui/rhythmbox-ui.xml myrhythmbox/data/ui/rhythmbox-ui.xml
--- rhythmbox/data/ui/rhythmbox-ui.xml 2005-07-26 10:07:28.000000000 -0400
+++ myrhythmbox/data/ui/rhythmbox-ui.xml 2005-07-29 12:08:38.000000000 -0400
@@ -106,6 +106,10 @@
<menuitem name="LibrarySrcPopupAddCD" action="MusicImportCD"/>
</popup>
+ <popup name="DAAPSourcePopup">
+ <menuitem name="DAAPSrcPopupDisconnect" action="SourceDisconnect"/>
+ </popup>
+
<popup name="IRadioSourcePopup">
<menuitem name="IRadioSrcPopupNewStation" action="MusicNewInternetRadioStation"/>
</popup>
diff -x cvs -x CVS -x ignore -ur rhythmbox/Makefile.am myrhythmbox/Makefile.am
--- rhythmbox/Makefile.am 2004-09-02 23:14:32.000000000 -0400
+++ myrhythmbox/Makefile.am 2005-07-31 14:53:27.000000000 -0400
@@ -4,7 +4,7 @@
DISTCHECK_CONFIGURE_FLAGS = --disable-schemas-install
-SUBDIRS = lib metadata player rhythmdb widgets sources iradio remote shell data po help tests
+SUBDIRS = lib metadata player rhythmdb widgets sources iradio remote daapsharing shell data po help tests
EXTRA_DIST = \
autogen.sh \
diff -x cvs -x CVS -x ignore -ur 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-25 13:27:19.000000000 -0400
@@ -17,9 +17,18 @@
-I$(top_srcdir) \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/metadata \
+ -I$(top_srcdir)/sources \
+ -I$(top_srcdir)/shell \
+ -I$(top_srcdir)/rhythmdb \
+ -I$(top_srcdir)/widgets \
$(RHYTHMBOX_CFLAGS)
+if USE_DAAP
+INCLUDES += -I$(top_srcdir)/daapsharing
+endif
+
+
BUILT_SOURCES = rb-recorder-marshal.c rb-recorder-marshal.h
if USE_CD_BURNER
diff -x cvs -x CVS -x ignore -ur rhythmbox/player/rb-player-gst.c myrhythmbox/player/rb-player-gst.c
--- rhythmbox/player/rb-player-gst.c 2005-08-25 20:48:21.000000000 -0400
+++ myrhythmbox/player/rb-player-gst.c 2005-08-26 15:09:53.000000000 -0400
@@ -29,14 +29,20 @@
#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_SUPPORT
+#include "rb-daap-src.h"
+#endif /* WITH_DAAP_SUPPORT */
+
G_DEFINE_TYPE(RBPlayer, rb_player, G_TYPE_OBJECT)
static void rb_player_finalize (GObject *object);
@@ -497,7 +503,7 @@
g_free (mp->priv->uri);
mp->priv->uri = NULL;
-
+
if (uri == NULL) {
mp->priv->playing = FALSE;
return TRUE;
@@ -668,12 +674,29 @@
gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED);
- gst_element_seek (mp->priv->playbin,
+#ifdef WITH_DAAP_SUPPORT
+ if (g_strncasecmp (mp->priv->uri, "daap://", 7) == 0) {
+ gst_element_set_state (mp->priv->playbin, GST_STATE_READY);
+ /* FIXME?
+ * This is sorta hack/sorta best way to do it.
+ * If we set up the daapsrc to do regular GStreamer seeking,
+ * GStreamer goes ape-shit and tries to seek all over the
+ * place, which can cause iTunes to return errors
+ * (probably cause we're requesting the same file too often)
+ *
+ * So, we do it this way.
+ */
+ rb_daap_src_set_time (time);
+ } else {
+#endif /* WITH_DAAP_SUPPORT */
+ gst_element_seek (mp->priv->playbin,
GST_FORMAT_TIME
| GST_SEEK_METHOD_SET
| GST_SEEK_FLAG_FLUSH,
time * GST_SECOND);
-
+#ifdef WITH_DAAP_SUPPORT
+ }
+#endif /* WITH_DAAP_SUPPORT */
if (mp->priv->playing)
gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
}
@@ -685,10 +708,18 @@
if (mp->priv->playbin != NULL) {
gint64 gst_position;
+ glong ret;
GstFormat fmt = GST_FORMAT_TIME;
gst_element_query (mp->priv->playbin, GST_QUERY_POSITION, &fmt, &gst_position);
-
- return (long)(gst_position / (1000*1000*1000));
+
+ ret = (glong) (gst_position / (1000*1000*1000));
+#ifdef WITH_DAAP_SUPPORT
+ if (g_strncasecmp (mp->priv->uri, "daap://", 7) == 0) {
+ ret += rb_daap_src_get_time ();
+ }
+#endif
+
+ return ret;
} else
return -1;
}
diff -x cvs -x CVS -x ignore -ur rhythmbox/shell/Makefile.am myrhythmbox/shell/Makefile.am
--- rhythmbox/shell/Makefile.am 2005-08-25 20:48:20.000000000 -0400
+++ myrhythmbox/shell/Makefile.am 2005-08-26 15:55:07.000000000 -0400
@@ -130,6 +130,14 @@
rhythmbox_LDADD += $(LIBNAUTILUS_BURN_LIBS)
endif
+if USE_DAAP
+INCLUDES += -I$(top_srcdir)/daapsharing
+
+rhythmbox_LDADD += $(SOUP_LIBS) \
+ $(MDNS_LIBS) \
+ $(top_builddir)/daapsharing/libdaapsharing.la
+endif
+
BUILT_SOURCES += $(tab_files)
CLEANFILES += $(BUILT_SOURCES)
diff -x cvs -x CVS -x ignore -ur rhythmbox/shell/rb-playlist-manager.c myrhythmbox/shell/rb-playlist-manager.c
--- rhythmbox/shell/rb-playlist-manager.c 2005-08-24 22:47:32.000000000 -0400
+++ myrhythmbox/shell/rb-playlist-manager.c 2005-08-26 18:24:24.000000000 -0400
@@ -32,6 +32,9 @@
#include "rb-playlist-manager.h"
#include "rb-playlist-source.h"
+#ifdef WITH_DAAP_SUPPORT
+#include "rb-daap-playlist-source.h"
+#endif
#if defined(WITH_CD_BURNER_SUPPORT)
#include "rb-recorder.h"
#endif
@@ -639,12 +642,14 @@
xmlNodePtr root;
struct RBPlaylistManagerSaveThreadData *data;
GtkTreeIter iter;
+ GtkTreeModel *fmodel;
GtkTreeModel *model;
rb_debug ("saving the playlists");
- g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &model, NULL);
- model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+ g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &fmodel, NULL);
+ model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
+ g_object_unref (fmodel);
if (!force) {
gboolean dirty = FALSE;
@@ -660,7 +665,10 @@
source = g_value_get_pointer (&v);
if (RB_IS_PLAYLIST_SOURCE (source) == FALSE)
continue;
-
+#ifdef WITH_DAAP_SUPPORT
+ if (RB_IS_DAAP_PLAYLIST_SOURCE (source))
+ continue;
+#endif /* WITH DAAP_SUPPORT */
g_object_get (G_OBJECT (source), "dirty", &dirty, NULL);
} while (gtk_tree_model_iter_next (model, &iter));
@@ -706,6 +714,10 @@
source = g_value_get_pointer (&v);
if (RB_IS_PLAYLIST_SOURCE (source) == FALSE)
continue;
+#ifdef WITH_DAAP_SUPPORT
+ if (RB_IS_DAAP_PLAYLIST_SOURCE (source))
+ continue;
+#endif /* WITH DAAP_SUPPORT */
rb_playlist_source_save_to_xml (RB_PLAYLIST_SOURCE (source), root);
} while (gtk_tree_model_iter_next (model, &iter));
@@ -715,6 +727,7 @@
mgr->priv->outstanding_threads++;
g_thread_create ((GThreadFunc) rb_playlist_manager_save_thread_main, data, FALSE, NULL);
+ g_object_unref (G_OBJECT (model));
}
static void
@@ -754,6 +767,44 @@
return playlist;
}
+GList *
+rb_playlist_manager_get_playlists (RBPlaylistManager *mgr)
+{
+ GList *playlists = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *fmodel;
+ GtkTreeModel *model;
+
+ g_object_get (G_OBJECT (mgr->priv->sourcelist), "model", &fmodel, NULL);
+ model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (fmodel));
+ g_object_unref (fmodel);
+
+ if (gtk_tree_model_get_iter_first (model, &iter)) {
+ do {
+ RBSource *source;
+ GValue v = {0,};
+ gtk_tree_model_get_value (model,
+ &iter,
+ RB_SOURCELIST_MODEL_COLUMN_SOURCE,
+ &v);
+ source = g_value_get_pointer (&v);
+ if (RB_IS_PLAYLIST_SOURCE (source) == FALSE)
+ continue;
+#ifdef WITH_DAAP_SUPPORT
+ if (RB_IS_DAAP_PLAYLIST_SOURCE (source))
+ continue;
+#endif
+
+ playlists = g_list_prepend (playlists, source);
+
+ } while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ g_object_unref (model);
+
+ return playlists;
+}
+
static void
rb_playlist_manager_cmd_new_playlist (GtkAction *action,
RBPlaylistManager *mgr)
diff -x cvs -x CVS -x ignore -ur rhythmbox/shell/rb-playlist-manager.h myrhythmbox/shell/rb-playlist-manager.h
--- rhythmbox/shell/rb-playlist-manager.h 2005-08-15 09:18:02.000000000 -0400
+++ myrhythmbox/shell/rb-playlist-manager.h 2005-08-22 12:23:25.000000000 -0400
@@ -79,6 +79,7 @@
RBSource * rb_playlist_manager_new_playlist (RBPlaylistManager *mgr,
const char *suggested_name,
gboolean automatic);
+GList * rb_playlist_manager_get_playlists (RBPlaylistManager *manager);
G_END_DECLS
diff -x cvs -x CVS -x ignore -ur rhythmbox/shell/rb-shell.c myrhythmbox/shell/rb-shell.c
--- rhythmbox/shell/rb-shell.c 2005-08-26 10:50:41.000000000 -0400
+++ myrhythmbox/shell/rb-shell.c 2005-08-26 15:56:27.000000000 -0400
@@ -67,6 +67,10 @@
#ifdef WITH_IPOD_SUPPORT
#include "rb-ipod-source.h"
#endif /* WITH_IPOD_SUPPORT */
+#ifdef WITH_DAAP_SUPPORT
+#include "rb-daap-source.h"
+#include "daap-sharing.h"
+#endif /* WITH_DAAP_SUPPORT */
#include "rb-load-failure-dialog.h"
#include "rb-new-station-dialog.h"
#include "rb-iradio-source.h"
@@ -110,7 +114,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,
@@ -166,6 +169,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,
@@ -259,10 +264,20 @@
#ifdef WITH_IPOD_SUPPORT
rb_ipod_source_new,
#endif /* WITH_IPOD_SUPPORT */
+#ifdef WITH_DAAP_SUPPORT
+ rb_daap_sources_init,
+#endif /* WITH_DAAP_SUPPORT */
NULL,
};
+typedef void (*SourceShutdownFunc)(RBShell *);
+static SourceShutdownFunc known_source_shutdowns[] = {
+#ifdef WITH_DAAP_SUPPORT
+ rb_daap_sources_shutdown,
+#endif /* WITH_DAAP_SUPPORT */
+ NULL,
+};
static gboolean save_yourself_cb (GnomeClient *client,
gint phase,
@@ -286,7 +301,8 @@
PROP_RHYTHMDB_FILE,
PROP_SELECTED_SOURCE,
PROP_DB,
- PROP_UI_MANAGER
+ PROP_UI_MANAGER,
+ PROP_PLAYLIST_MANAGER
};
/* prefs */
@@ -392,6 +408,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);
@@ -534,7 +553,13 @@
"GtkUIManager object",
GTK_TYPE_UI_MANAGER,
G_PARAM_READABLE));
-
+ g_object_class_install_property (object_class,
+ PROP_PLAYLIST_MANAGER,
+ g_param_spec_object ("playlist-manager",
+ "RBPlaylistManager",
+ "RBPlaylistManager object",
+ RB_TYPE_PLAYLIST_MANAGER,
+ G_PARAM_READABLE));
}
@@ -674,6 +699,9 @@
case PROP_UI_MANAGER:
g_value_set_object (value, shell->priv->ui_manager);
break;
+ case PROP_PLAYLIST_MANAGER:
+ g_value_set_object (value, shell->priv->playlist_manager);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -995,12 +1023,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);
@@ -1107,6 +1138,9 @@
rb_statusbar_sync_state (shell->priv->statusbar);
rb_shell_sync_smalldisplay (shell);
gtk_widget_show (GTK_WIDGET (shell->priv->window));
+#ifdef WITH_DAAP_SUPPORT
+ daap_sharing_init (shell);
+#endif /* WITH_DAAP_SUPPORT */
return G_OBJECT (shell);
}
@@ -1281,9 +1315,10 @@
-static void
+void
rb_shell_append_source (RBShell *shell,
- RBSource *source)
+ RBSource *source,
+ RBSource *parent)
{
char *search_text;
@@ -1299,7 +1334,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));
@@ -1311,7 +1346,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
@@ -1440,7 +1475,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);
@@ -1772,8 +1812,19 @@
static void
rb_shell_quit (RBShell *shell)
{
+ gint i = 0;
+
rb_debug ("Quitting");
+ /* shutdown sources */
+ while (known_source_shutdowns[i] != NULL) {
+ known_source_shutdowns[i] (shell);
+ i++;
+ }
+
+#ifdef WITH_DAAP_SUPPORT
+ daap_sharing_shutdown (shell);
+#endif /* WITH_DAAP_SUPPORT */
rb_shell_shutdown (shell);
rb_shell_sync_state (shell);
g_object_unref (G_OBJECT (shell));
@@ -2000,6 +2051,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 -x ignore -ur rhythmbox/shell/rb-shell.h myrhythmbox/shell/rb-shell.h
--- rhythmbox/shell/rb-shell.h 2005-08-25 20:48:20.000000000 -0400
+++ myrhythmbox/shell/rb-shell.h 2005-08-26 15:09:54.000000000 -0400
@@ -76,6 +76,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 -x ignore -ur 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-23 14:11:29.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,81 @@
rb_debug ("No config widget for source %s", name);
}
+#ifdef WITH_DAAP_SUPPORT
+
+#define CONF_ENABLE_SHARING "/apps/rhythmbox/sharing/enable_sharing"
+#define CONF_NAME "/apps/rhythmbox/sharing/share_name"
+
+static void
+share_check_button_toggled_cb (GtkToggleButton *button, GtkWidget *hbox)
+{
+ gboolean b;
+
+ b = gtk_toggle_button_get_active (button);
+
+ eel_gconf_set_boolean (CONF_ENABLE_SHARING, b);
+
+ gtk_widget_set_sensitive (hbox, b);
+
+ return;
+}
+
+static gboolean
+share_name_entry_focus_out_event_cb (GtkEntry *entry, GdkEventFocus *event, gpointer data)
+{
+ const gchar *name;
+
+ name = gtk_entry_get_text (entry);
+
+ eel_gconf_set_string (CONF_NAME, name);
+
+ return FALSE;
+}
+
+static void
+add_daap_preferences (RBShellPreferences *shell_preferences)
+{
+ GtkWidget *vbox;
+ GtkWidget *check;
+ gboolean b;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *entry;
+ gchar *name;
+
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+
+ hbox = gtk_hbox_new (FALSE, 5);
+
+ check = gtk_check_button_new_with_mnemonic (_("_Share my music"));
+ gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
+
+ b = eel_gconf_get_boolean (CONF_ENABLE_SHARING);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), b);
+ g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (share_check_button_toggled_cb), hbox);
+
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new_with_mnemonic (_("Shared music _name"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+
+ name = eel_gconf_get_string (CONF_NAME);
+ gtk_entry_set_text (GTK_ENTRY (entry), name);
+ g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (share_name_entry_focus_out_event_cb), NULL);
+
+ gtk_widget_set_sensitive (hbox, b);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (shell_preferences->priv->notebook), vbox, gtk_label_new (_("Sharing")));
+
+ return;
+}
+#endif /* WITH_DAAP_SUPPORT */
+
GtkWidget *
rb_shell_preferences_new (GList *views)
{
@@ -290,6 +368,11 @@
name,
RB_SOURCE (views->data));
}
+
+#ifdef WITH_DAAP_SUPPORT
+ add_daap_preferences (shell_preferences);
+#endif /* WITH_DAAP_SUPPORT */
+
return GTK_WIDGET (shell_preferences);
}
diff -x cvs -x CVS -x ignore -ur 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-23 12:57:48.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,14 @@
INCLUDES += $(LIBNAUTILUS_BURN_CFLAGS)
endif
+if USE_DAAP
+libsourcesimpl_la_SOURCES += \
+ rb-daap-source.c \
+ rb-daap-source.h \
+ rb-daap-playlist-source.c \
+ rb-daap-playlist-source.h
+
+INCLUDES += $(SOUP_CFLAGS)
+
+endif
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/sources/rb-daap-playlist-source.c myrhythmbox/sources/rb-daap-playlist-source.c
--- rhythmbox/sources/rb-daap-playlist-source.c 2005-08-26 18:45:16.000000000 -0400
+++ myrhythmbox/sources/rb-daap-playlist-source.c 2005-08-20 23:06:25.000000000 -0400
@@ -0,0 +1,190 @@
+/*
+ * Implementation of DAAP (iTunes Music Sharing) playlist source object
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkicontheme.h>
+#include <string.h>
+#include "rhythmdb.h"
+#include "rb-shell.h"
+#include <libgnome/gnome-i18n.h>
+#include "eel-gconf-extensions.h"
+#include "rb-daap-playlist-source.h"
+#include "rb-stock-icons.h"
+#include "rb-debug.h"
+#include "rb-util.h"
+
+#include "rb-daap-connection.h"
+
+enum {
+ PROP_0,
+ PROP_PLAYLIST
+};
+
+static void
+rb_daap_playlist_source_init (RBDAAPPlaylistSource *source);
+static void
+rb_daap_playlist_source_finalize (GObject *object);
+static void
+rb_daap_playlist_source_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void
+rb_daap_playlist_source_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void
+rb_daap_playlist_source_class_init (RBDAAPPlaylistSourceClass *klass);
+
+struct RBDAAPPlaylistSourcePrivate
+{
+ RBDAAPPlaylist *playlist;
+};
+
+GType
+rb_daap_playlist_source_get_type (void)
+{
+ static GType rb_daap_playlist_source_type = 0;
+
+ if (rb_daap_playlist_source_type == 0)
+ {
+ static const GTypeInfo our_info =
+ {
+ sizeof (RBDAAPPlaylistSourceClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) rb_daap_playlist_source_class_init,
+ NULL,
+ NULL,
+ sizeof (RBDAAPPlaylistSource),
+ 0,
+ (GInstanceInitFunc) rb_daap_playlist_source_init
+ };
+
+ rb_daap_playlist_source_type = g_type_register_static (RB_TYPE_PLAYLIST_SOURCE,
+ "RBDAAPPlaylistSource",
+ &our_info, 0);
+
+ }
+
+ return rb_daap_playlist_source_type;
+}
+
+static void
+rb_daap_playlist_source_class_init (RBDAAPPlaylistSourceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+
+ object_class->set_property = rb_daap_playlist_source_set_property;
+ object_class->get_property = rb_daap_playlist_source_get_property;
+ object_class->finalize = rb_daap_playlist_source_finalize;
+
+ source_class->impl_can_rename = (RBSourceFeatureFunc) rb_false_function;
+ source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+ source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
+ source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
+
+ g_object_class_install_property (object_class,
+ PROP_PLAYLIST,
+ g_param_spec_pointer ("playlist",
+ "RBDAAPPlaylist",
+ "RBDAAPPlaylist structure",
+ G_PARAM_READWRITE));
+
+ return;
+}
+
+static void
+rb_daap_playlist_source_init (RBDAAPPlaylistSource *source)
+{
+ source->priv = g_new0 (RBDAAPPlaylistSourcePrivate, 1);
+
+ return;
+}
+
+static void
+rb_daap_playlist_source_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RBDAAPPlaylistSource *source = RB_DAAP_PLAYLIST_SOURCE (object);
+
+ switch (prop_id) {
+ case PROP_PLAYLIST:
+ {
+ GList *l;
+
+ source->priv->playlist = g_value_get_pointer (value);
+
+ for (l = source->priv->playlist->uris; l; l = l->next) {
+ const gchar *uri = (const gchar *)l->data;
+
+ rb_playlist_source_add_location (RB_PLAYLIST_SOURCE (source), uri);
+ }
+
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ return;
+}
+
+static void
+rb_daap_playlist_source_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ RBDAAPPlaylistSource *source = RB_DAAP_PLAYLIST_SOURCE (object);
+
+ switch (prop_id) {
+ case PROP_PLAYLIST:
+ g_value_set_pointer (value, source->priv->playlist);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ return;
+}
+
+static void
+rb_daap_playlist_source_finalize (GObject *object)
+{
+ RBDAAPPlaylistSource *source = RB_DAAP_PLAYLIST_SOURCE (object);
+
+ if (source->priv) {
+
+ g_free (source->priv);
+ source->priv = NULL;
+ }
+
+ return;
+}
diff -x cvs -x CVS -x ignore -ur rhythmbox/sources/rb-daap-playlist-source.h myrhythmbox/sources/rb-daap-playlist-source.h
--- rhythmbox/sources/rb-daap-playlist-source.h 2005-08-26 18:45:16.000000000 -0400
+++ myrhythmbox/sources/rb-daap-playlist-source.h 2005-08-20 21:51:23.000000000 -0400
@@ -0,0 +1,55 @@
+/*
+ * Header for DAAP (iTunes Music Sharing) playlist source object
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_DAAP_PLAYLIST_SOURCE_H
+#define __RB_DAAP_PLAYLIST_SOURCE_H
+
+#include "rb-playlist-source.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_DAAP_PLAYLIST_SOURCE (rb_daap_playlist_source_get_type ())
+#define RB_DAAP_PLAYLIST_SOURCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSource))
+#define RB_DAAP_PLAYLIST_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSourceClass))
+#define RB_IS_DAAP_PLAYLIST_SOURCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE))
+#define RB_IS_DAAP_PLAYLIST_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DAAP_PLAYLIST_SOURCE))
+#define RB_DAAP_PLAYLIST_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DAAP_PLAYLIST_SOURCE, RBDAAPPlaylistSourceClass))
+
+typedef struct RBDAAPPlaylistSourcePrivate RBDAAPPlaylistSourcePrivate;
+
+typedef struct
+{
+ RBPlaylistSource parent;
+
+ RBDAAPPlaylistSourcePrivate *priv;
+} RBDAAPPlaylistSource;
+
+typedef struct
+{
+ RBPlaylistSourceClass parent;
+} RBDAAPPlaylistSourceClass;
+
+GType
+rb_daap_playlist_source_get_type (void);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_PLAYLIST_SOURCE_H */
diff -x cvs -x CVS -x ignore -ur rhythmbox/sources/rb-daap-source.c myrhythmbox/sources/rb-daap-source.c
--- rhythmbox/sources/rb-daap-source.c 2005-08-26 18:45:16.000000000 -0400
+++ myrhythmbox/sources/rb-daap-source.c 2005-08-25 17:52:54.000000000 -0400
@@ -0,0 +1,578 @@
+/*
+ * Implementation of DAAP (iTunes Music Sharing) source object
+ *
+ * Copyright (C) 2005 Charles Schmidt <cschmidt2 emich edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkicontheme.h>
+#include <string.h>
+#include "rhythmdb.h"
+#include "rb-shell.h"
+#include <libgnome/gnome-i18n.h>
+#include "eel-gconf-extensions.h"
+#include "rb-daap-source.h"
+#include "rb-stock-icons.h"
+#include "rb-debug.h"
+#include "rb-util.h"
+#include "rb-dialog.h"
+
+#include "rb-daap-connection.h"
+#include "rb-daap-mdns.h"
+#include "rb-daap-src.h"
+
+#include "rb-daap-playlist-source.h"
+
+#define CONF_ENABLE_BROWSING "/apps/rhythmbox/sharing/enable_browsing"
+#define CONF_ENABLE_SHARING "/apps/rhythmbox/sharing/enable_sharing"
+#define CONF_SHARE_NAME "/apps/rhythmbox/sharing/share_name"
+
+static void rb_daap_source_init (RBDAAPSource *source);
+static void rb_daap_source_finalize (GObject *object);
+static void rb_daap_source_class_init (RBDAAPSourceClass *klass);
+static void rb_daap_source_activate (RBSource *source);
+static gboolean rb_daap_source_disconnect (RBSource *source);
+static gboolean rb_daap_source_show_popup (RBSource *source);
+
+struct RBDAAPSourcePrivate
+{
+ gboolean resolved;
+ gchar *host;
+ gint port;
+
+ RBDAAPConnection *connection;
+ GSList *playlist_sources;
+
+ gboolean connected;
+};
+
+
+static RhythmDBEntryType
+rhythmdb_entry_daap_type_new (void)
+{
+ return rhythmdb_entry_register_type ();
+}
+
+GType
+rb_daap_source_get_type (void)
+{
+ static GType rb_daap_source_type = 0;
+
+ if (rb_daap_source_type == 0) {
+ static const GTypeInfo our_info = {
+ sizeof (RBDAAPSourceClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) rb_daap_source_class_init,
+ NULL,
+ NULL,
+ sizeof (RBDAAPSource),
+ 0,
+ (GInstanceInitFunc) rb_daap_source_init
+ };
+
+ rb_daap_source_type = g_type_register_static (RB_TYPE_LIBRARY_SOURCE,
+ "RBDAAPSource",
+ &our_info, 0);
+
+ }
+
+ return rb_daap_source_type;
+}
+
+static void
+rb_daap_source_class_init (RBDAAPSourceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+
+ object_class->finalize = rb_daap_source_finalize;
+
+ source_class->impl_activate = rb_daap_source_activate;
+ source_class->impl_disconnect = rb_daap_source_disconnect;
+ source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
+ source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+ source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
+ source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
+ source_class->impl_paste = NULL;
+ source_class->impl_receive_drag = NULL;
+ source_class->impl_delete = NULL;
+ source_class->impl_receive_drag = NULL;
+ source_class->impl_show_popup = rb_daap_source_show_popup;
+ source_class->impl_get_config_widget = NULL;
+
+ return;
+}
+
+static void
+rb_daap_source_init (RBDAAPSource *source)
+{
+ source->priv = g_new0 (RBDAAPSourcePrivate, 1);
+
+ return;
+}
+
+
+static void
+rb_daap_source_finalize (GObject *object)
+{
+ RBDAAPSource *source = RB_DAAP_SOURCE (object);
+
+ rb_daap_source_disconnect (RB_SOURCE (source));
+
+ if (source->priv) {
+ g_free (source->priv->host);
+
+ g_free (source->priv);
+ source->priv = NULL;
+ }
+
+ return;
+}
+
+static GdkPixbuf *
+rb_daap_get_icon (void)
+{
+ GdkPixbuf *icon;
+ GtkIconTheme *theme;
+
+ theme = gtk_icon_theme_get_default ();
+ icon = gtk_icon_theme_load_icon (theme, "gnome-fs-network", 24, 0, NULL);
+
+ return icon;
+}
+
+static RBSource *
+rb_daap_source_new (RBShell *shell,
+ const gchar *name)
+{
+ RBSource *source;
+ RhythmDBEntryType type;
+ gchar *internal_name;
+
+ type = rhythmdb_entry_daap_type_new ();
+ internal_name = g_strconcat ("<daap-", name, ">", NULL);
+
+ source = RB_SOURCE (g_object_new (RB_TYPE_DAAP_SOURCE,
+ "name", name,
+ "entry-type", type,
+ "internal-name", internal_name,
+ "icon", rb_daap_get_icon (),
+ "shell", shell,
+ "visibility", TRUE,
+ NULL));
+
+ rb_shell_register_entry_type_for_source (shell, source,
+ type);
+
+ g_free (internal_name);
+ internal_name = NULL;
+
+ return source;
+}
+
+static RBDAAPmDNSBrowser browser = 0;
+static RBDAAPmDNSResolver resolver = 0;
+
+static GSList *sources = NULL;
+
+static RBSource *
+find_source_by_name (const gchar *name)
+{
+ GSList *l;
+ RBSource *source = NULL;
+
+ for (l = sources; l; l = l->next) {
+ gchar *s;
+
+ source = l->data;
+ g_object_get (G_OBJECT (source), "name", &s, NULL);
+
+ if (strcmp (name, s) == 0) {
+ g_free (s);
+ return source;
+ }
+
+ g_free (s);
+ }
+
+ return NULL;
+}
+
+static void
+browse_cb (RBDAAPmDNSBrowser b,
+ RBDAAPmDNSBrowserStatus status,
+ const gchar *name,
+ RBShell *shell)
+{
+ if (status == RB_DAAP_MDNS_BROWSER_ADD_SERVICE) {
+ RBSource *source = find_source_by_name (name);
+
+ rb_debug ("New DAAP (music sharing) source '%s' discovered", name);
+
+ if (source) {
+ rb_debug ("Ignoring duplicate DAAP source");
+ return;
+ }
+
+ /* Make sure it isn't us.
+ * This will fail if we're using howl for mDNS and someone
+ * has already taken our name, forcing us to use a suffix
+ * like (2). In that case, the other share (with our preferred
+ * name) will be removed from the sourcelist, and our share
+ * will show up. Stupid howl.
+ * http://lists.porchdogsoft.com/pipermail/howl-users/Week-of-Mon-20041206/000487.html
+ * FIXME try resolving it right away and seeing if its us
+ */
+ if (eel_gconf_get_boolean (CONF_ENABLE_SHARING)) {
+ gchar *my_name;
+
+ my_name = eel_gconf_get_string (CONF_SHARE_NAME);
+
+ if (strcmp (my_name, name) == 0) {
+ return;
+ }
+ }
+
+ source = rb_daap_source_new (shell, name);
+
+ sources = g_slist_prepend (sources, source);
+
+ rb_shell_append_source (shell, source, NULL);
+
+ } else if (status == RB_DAAP_MDNS_BROWSER_REMOVE_SERVICE) {
+ RBSource *source = find_source_by_name (name);
+
+ rb_debug ("DAAP source '%s' went away", name);
+ if (source == NULL) {
+ /* if this happens, its almost always because the user
+ * turned sharing off in the preferences, and we've
+ * been notified that our own daap server has gone away
+ * fancy that.
+ *
+ * probably no need for any error messages
+ */
+// g_warning ("notification of removed daap source that does not exist in rhythmbox");
+ return;
+ }
+
+ sources = g_slist_remove (sources, source);
+
+ rb_daap_source_disconnect (source);
+ rb_source_delete_thyself (source);
+ /* unref is done via gtk in rb_shell_source_delete_cb at
+ * gtk_notebook_remove_page */
+ }
+
+ return;
+}
+
+
+static void
+start_browsing (RBShell *shell)
+{
+ gboolean ret;
+
+ ret = rb_daap_mdns_browse (&browser,
+ (RBDAAPmDNSBrowserCallback) browse_cb,
+ shell);
+
+ if (ret == FALSE) {
+ g_warning ("Unable to start mDNS browsing");
+ return;
+ }
+
+ rb_daap_src_init ();
+
+ return;
+}
+
+static void
+stop_browsing (RBShell *shell)
+{
+ GSList *l;
+
+ for (l = sources; l; l = l->next) {
+ RBSource *source = l->data;
+
+ rb_daap_source_disconnect (source);
+ rb_source_delete_thyself (source);
+ }
+
+ sources = NULL;
+
+ if (browser) {
+ rb_daap_mdns_browse_cancel (browser);
+ browser = 0;
+ }
+
+ rb_daap_src_shutdown ();
+
+ 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)
+{
+ gboolean enabled;
+
+ g_object_ref (shell);
+
+ enabled = eel_gconf_get_boolean (CONF_ENABLE_BROWSING);
+
+ if (enabled) {
+ start_browsing (shell);
+ }
+
+ eel_gconf_notification_add (CONF_ENABLE_BROWSING,
+ (GConfClientNotifyFunc) enable_browsing_changed_cb,
+ shell);
+
+ return NULL;
+}
+
+void
+rb_daap_sources_shutdown (RBShell *shell)
+{
+ g_object_unref (shell);
+
+ if (browser) {
+ stop_browsing (shell);
+ }
+
+ return;
+}
+
+static void
+resolve_cb (RBDAAPmDNSResolver resolver,
+ RBDAAPmDNSResolverStatus status,
+ const gchar *name,
+ gchar *host,
+ guint port,
+ RBDAAPSource *daap_source)
+{
+ RBShell *shell = NULL;
+ RhythmDB *db = NULL;
+ RhythmDBEntryType type;
+
+ if (status == RB_DAAP_MDNS_RESOLVER_FOUND) {
+ daap_source->priv->host = host;
+ daap_source->priv->port = port;
+
+ daap_source->priv->resolved = TRUE;
+
+ if (daap_source->priv->connection == NULL) {
+ GSList *playlists;
+ GSList *l;
+
+ g_object_get (G_OBJECT (daap_source), "shell", &shell, "entry-type", &type, NULL);
+ g_object_get (G_OBJECT (shell), "db", &db, NULL);
+
+ /* FIXME FIXME FIXME
+ * this is the call that takes a long time
+ */
+ daap_source->priv->connection = rb_daap_connection_new (daap_source->priv->host, daap_source->priv->port, db, type);
+
+ playlists = rb_daap_connection_get_playlists (daap_source->priv->connection);
+ for (l = playlists; l; l = l->next) {
+ RBDAAPPlaylist *playlist = l->data;
+ RBSource *playlist_source;
+ gchar *internal_name;
+
+ internal_name = g_strconcat ("<daap-", name, "-", playlist->name, ">", NULL);
+
+ playlist_source = RB_SOURCE (g_object_new (RB_TYPE_DAAP_PLAYLIST_SOURCE,
+ "name", playlist->name,
+ "internal-name", internal_name,
+ "shell", shell,
+ "visibility", TRUE,
+ NULL));
+/* this is set here instead of in construction so that
+ * rb_playlist_source_constructor has a chance to be run to set things up */
+ g_object_set (G_OBJECT (playlist_source), "playlist", playlist, NULL);
+ g_free (internal_name);
+ internal_name = NULL;
+
+ rb_shell_append_source (shell, playlist_source, RB_SOURCE (daap_source));
+ daap_source->priv->playlist_sources = g_slist_prepend (daap_source->priv->playlist_sources, playlist_source);
+ }
+
+ g_object_unref (G_OBJECT (db));
+ g_object_unref (G_OBJECT (shell));
+
+ daap_source->priv->connected = TRUE;
+ }
+ } else if (status == RB_DAAP_MDNS_RESOLVER_TIMEOUT) {
+ g_warning ("Unable to resolve %s", name);
+ }
+
+ return;
+}
+
+static void
+rb_daap_source_activate (RBSource *source)
+{
+ RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
+ gchar *name;
+
+ g_object_get (G_OBJECT (source), "name", &name, NULL);
+
+ if (daap_source->priv->resolved == FALSE) {
+ gboolean ret;
+
+ ret = rb_daap_mdns_resolve (&resolver,
+ name,
+ (RBDAAPmDNSResolverCallback) resolve_cb,
+ source);
+
+ if (ret == FALSE) {
+ g_warning ("could not start resolving host");
+ }
+ }
+
+ g_free (name);
+
+ return;
+}
+
+static gboolean
+rb_daap_source_disconnect (RBSource *source)
+{
+ RBDAAPSource *daap_source = RB_DAAP_SOURCE (source);
+
+ if (daap_source->priv->connected) {
+ GSList *l;
+ RBShell *shell;
+ RhythmDB *db;
+ RhythmDBEntryType type;
+
+ g_object_get (G_OBJECT (source), "shell", &shell, "entry-type", &type, NULL);
+ g_object_get (G_OBJECT (shell), "db", &db, NULL);
+ rhythmdb_entry_delete_by_type (db, type);
+ rhythmdb_commit (db);
+ g_object_unref (G_OBJECT (db));
+ g_object_unref (G_OBJECT (shell));
+
+ for (l = daap_source->priv->playlist_sources; l; l = l->next) {
+ RBSource *playlist_source = RB_SOURCE (l->data);
+
+ rb_source_delete_thyself (playlist_source);
+ }
+
+ g_slist_free (daap_source->priv->playlist_sources);
+ daap_source->priv->playlist_sources = NULL;
+
+ if (daap_source->priv->connection) {
+ rb_daap_connection_destroy (daap_source->priv->connection);
+ daap_source->priv->connection = NULL;
+ }
+
+ daap_source->priv->connected = FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+rb_daap_source_show_popup (RBSource *source)
+{
+ _rb_source_show_popup (RB_SOURCE (source), "/DAAPSourcePopup");
+ return TRUE;
+}
+
+RBDAAPSource *
+rb_daap_source_find_for_uri (const gchar *uri)
+{
+ GSList *l;
+ gchar *ip;
+ gchar *s;
+ RBDAAPSource *found_source = NULL;
+
+ ip = strdup (uri + 7); // daap://
+ s = strchr (ip, ':');
+ *s = '\0';
+
+ for (l = sources; l; l = l->next) {
+ RBDAAPSource *source = l->data;
+
+ if (source->priv->resolved == TRUE && strcmp (ip, source->priv->host) == 0) {
+ found_source = source;
+
+ break;
+ }
+
+ }
+
+ g_free (ip);
+
+ return found_source;
+}
+
+gchar *
+rb_daap_source_get_headers (RBDAAPSource *source,
+ const gchar *uri,
+ glong time,
+ gint64 *bytes_out)
+{
+ gint64 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 kilobits per second
+ // a kilobit is 128 bytes
+ bytes = (time * bitrate) * 128;
+ }
+
+ *bytes_out = bytes;
+ return rb_daap_connection_get_headers (source->priv->connection, uri, bytes);
+}
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/sources/rb-daap-source.h myrhythmbox/sources/rb-daap-source.h
--- rhythmbox/sources/rb-daap-source.h 2005-08-26 18:45:16.000000000 -0400
+++ myrhythmbox/sources/rb-daap-source.h 2005-08-25 14:02:40.000000000 -0400
@@ -0,0 +1,70 @@
+/*
+ * 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,
+ gint64 *bytes);
+
+G_END_DECLS
+
+#endif /* __RB_DAAP_SOURCE_H */
diff -x cvs -x CVS -x ignore -ur rhythmbox/sources/rb-library-source.c myrhythmbox/sources/rb-library-source.c
--- rhythmbox/sources/rb-library-source.c 2005-08-17 12:32:54.000000000 -0400
+++ myrhythmbox/sources/rb-library-source.c 2005-08-23 16:36:03.000000000 -0400
@@ -616,16 +616,6 @@
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);
}
@@ -703,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 -x ignore -ur rhythmbox/sources/rb-library-source.h myrhythmbox/sources/rb-library-source.h
--- rhythmbox/sources/rb-library-source.h 2004-09-10 01:12:59.000000000 -0400
+++ myrhythmbox/sources/rb-library-source.h 2005-08-04 22:42:00.000000000 -0400
@@ -56,6 +56,8 @@
RBSource * rb_library_source_new (RBShell *shell);
+void rb_library_source_setup_prefs (RBLibrarySource *source);
+
void rb_library_source_show_browser (RBLibrarySource *source,
gboolean show);
diff -x cvs -x CVS -x ignore -ur rhythmbox/sources/rb-source.c myrhythmbox/sources/rb-source.c
--- rhythmbox/sources/rb-source.c 2005-07-07 10:30:20.000000000 -0400
+++ myrhythmbox/sources/rb-source.c 2005-08-04 22:51:30.000000000 -0400
@@ -59,6 +59,9 @@
static gboolean default_receive_drag (RBSource *source, GtkSelectionData *data);
static gboolean default_show_popup (RBSource *source);
static void default_delete_thyself (RBSource *source);
+static void default_activate (RBSource *source);
+static void default_deactivate (RBSource *source);
+static gboolean default_disconnect (RBSource *source);
struct RBSourcePrivate
{
@@ -147,6 +150,9 @@
klass->impl_receive_drag = default_receive_drag;
klass->impl_show_popup = default_show_popup;
klass->impl_delete_thyself = default_delete_thyself;
+ klass->impl_activate = default_activate;
+ klass->impl_deactivate = default_deactivate;
+ klass->impl_disconnect = default_disconnect;
g_object_class_install_property (object_class,
PROP_NAME,
@@ -482,7 +488,11 @@
{
RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
- return klass->impl_get_config_widget (source);
+ if (klass->impl_get_config_widget) {
+ return klass->impl_get_config_widget (source);
+ } else {
+ return NULL;
+ }
}
static gboolean
@@ -692,3 +702,44 @@
klass->impl_delete_thyself (source);
g_signal_emit (G_OBJECT (source), rb_source_signals[DELETED], 0);
}
+
+static void default_activate (RBSource *source)
+{
+ return;
+}
+
+static void default_deactivate (RBSource *source)
+{
+ return;
+}
+
+static gboolean default_disconnect (RBSource *source)
+{
+ return TRUE;
+}
+
+void rb_source_activate (RBSource *source)
+{
+ RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
+
+ klass->impl_activate (source);
+
+ return;
+}
+
+void rb_source_deactivate (RBSource *source)
+{
+ RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
+
+ klass->impl_deactivate (source);
+
+ return;
+}
+
+gboolean rb_source_disconnect (RBSource *source)
+{
+ RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
+
+ return klass->impl_disconnect (source);
+}
+
diff -x cvs -x CVS -x ignore -ur rhythmbox/sources/rb-source.h myrhythmbox/sources/rb-source.h
--- rhythmbox/sources/rb-source.h 2005-06-12 12:02:15.000000000 -0400
+++ myrhythmbox/sources/rb-source.h 2005-08-23 00:04:25.000000000 -0400
@@ -102,6 +102,9 @@
gboolean (*impl_show_popup) (RBSource *source);
void (*impl_delete_thyself) (RBSource *source);
+ void (*impl_activate) (RBSource *source);
+ void (*impl_deactivate) (RBSource *source);
+ gboolean (*impl_disconnect) (RBSource *source);
} RBSourceClass;
typedef gboolean (*RBSourceFeatureFunc) (RBSource *source);
@@ -163,6 +166,10 @@
void rb_source_delete_thyself (RBSource *source);
+void rb_source_activate (RBSource *source);
+void rb_source_deactivate (RBSource *source);
+gboolean rb_source_disconnect (RBSource *source);
+
/* Protected method, should only be used by objects inheriting from RBSource */
void _rb_source_show_popup (RBSource *source,
const char *ui_path);
diff -x cvs -x CVS -x ignore -ur rhythmbox/sources/rb-sourcelist.c myrhythmbox/sources/rb-sourcelist.c
--- rhythmbox/sources/rb-sourcelist.c 2005-08-24 22:47:33.000000000 -0400
+++ myrhythmbox/sources/rb-sourcelist.c 2005-08-25 17:37:26.000000000 -0400
@@ -80,6 +80,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,
@@ -368,7 +374,8 @@
void
rb_sourcelist_append (RBSourceList *sourcelist,
- RBSource *source)
+ RBSource *source,
+ RBSource *parent)
{
GtkTreeIter iter;
PangoAttrList *attrs = pango_attr_list_new ();
@@ -379,9 +386,15 @@
g_object_get (G_OBJECT (source), "name", &name, NULL);
- gtk_list_store_append (GTK_LIST_STORE (sourcelist->priv->real_model), &iter);
+ if (parent) {
+ GtkTreeIter parent_iter;
+ g_assert (rb_sourcelist_source_to_iter (sourcelist, parent, &parent_iter));
+ gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), &iter, &parent_iter);
+ } else {
+ gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), &iter, NULL);
+ }
- gtk_list_store_set (GTK_LIST_STORE (sourcelist->priv->real_model), &iter,
+ gtk_tree_store_set (GTK_TREE_STORE (sourcelist->priv->real_model), &iter,
RB_SOURCELIST_MODEL_COLUMN_PIXBUF, rb_source_get_pixbuf (source),
RB_SOURCELIST_MODEL_COLUMN_NAME, name,
RB_SOURCELIST_MODEL_COLUMN_SOURCE, source,
@@ -393,39 +406,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
@@ -459,13 +503,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);
}
}
@@ -476,7 +520,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),
@@ -626,7 +670,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 -x ignore -ur rhythmbox/sources/rb-sourcelist.h myrhythmbox/sources/rb-sourcelist.h
--- rhythmbox/sources/rb-sourcelist.h 2005-08-24 22:47:33.000000000 -0400
+++ myrhythmbox/sources/rb-sourcelist.h 2005-08-25 17:37:26.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 -x ignore -ur rhythmbox/sources/rb-sourcelist-model.c myrhythmbox/sources/rb-sourcelist-model.c
--- rhythmbox/sources/rb-sourcelist-model.c 2005-08-24 22:47:33.000000000 -0400
+++ myrhythmbox/sources/rb-sourcelist-model.c 2005-08-25 17:49:59.000000000 -0400
@@ -245,7 +245,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;
@@ -253,7 +253,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,
@@ -331,13 +331,13 @@
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
&real_dest_iter, &dest_iter);
if (pos == GTK_TREE_VIEW_DROP_AFTER)
- gtk_list_store_move_after (GTK_LIST_STORE (real_model),
+ gtk_tree_store_move_after (GTK_TREE_STORE (real_model),
&real_iter, &real_dest_iter);
else
- gtk_list_store_move_before (GTK_LIST_STORE (real_model),
+ gtk_tree_store_move_before (GTK_TREE_STORE (real_model),
&real_iter, &real_dest_iter);
} else
- gtk_list_store_move_before (GTK_LIST_STORE (real_model),
+ gtk_tree_store_move_before (GTK_TREE_STORE (real_model),
&real_iter, NULL);
g_free (path_str);
}
@@ -365,7 +365,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]