2
* Implementation of DAAP (iTunes Music Sharing) hashing, parsing, connection
4
* Copyright (C) 2004,2005 Charles Schmidt <cschmidt2@emich.edu>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
#include "rb-daap-connection.h"
23
#include "rb-daap-structure.h"
24
#include "rb-daap-dialog.h"
26
#include <libgnome/gnome-i18n.h>
33
/* hashing - based on/copied from libopendaap
34
* Copyright (c) 2004 David Hammerton
39
#include <sys/types.h>
49
* This code implements the MD5 message-digest algorithm.
50
* The algorithm is due to Ron Rivest. This code was
51
* written by Colin Plumb in 1993, no copyright is claimed.
52
* This code is in the public domain; do with it what you wish.
54
* Equivalent code is available from RSA Data Security, Inc.
55
* This code has been tested against that, and is equivalent,
56
* except that you don't need to include two pages of legalese
59
* To compute the message digest of a chunk of bytes, declare an MD5Context
60
* structure, pass it to OpenDaap_MD5Init, call OpenDaap_MD5Update as needed
61
* on buffers full of bytes, and then call OpenDaap_MD5Final, which will fill
62
* a supplied 16-byte array with the digest.
65
MD5Transform (guint32 buf[4],
68
/* for some reason we still have to reverse bytes on bigendian machines
69
* I don't really know why... but otherwise it fails..
70
* Any MD5 gurus out there know why???
72
#if 0 //ndef WORDS_BIGENDIAN /* was: HIGHFIRST */
73
#define byteReverse(buf, len) /* Nothing */
76
byteReverse (unsigned char *buf,
81
* Note: this code is harmless on little-endian machines.
84
byteReverse (unsigned char *buf,
89
t = (guint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
90
((unsigned) buf[1] << 8 | buf[0]);
95
#endif /* ! ASM_MD5 */
99
OpenDaap_MD5Init (MD5_CTX *ctx,
102
memset (ctx, 0, sizeof (MD5_CTX));
103
ctx->buf[0] = 0x67452301;
104
ctx->buf[1] = 0xefcdab89;
105
ctx->buf[2] = 0x98badcfe;
106
ctx->buf[3] = 0x10325476;
111
ctx->version = version;
115
OpenDaap_MD5Update (MD5_CTX *ctx,
116
unsigned char const *buf,
121
/* Update bitcount */
124
if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
125
ctx->bits[1]++; /* Carry from low to high */
126
ctx->bits[1] += len >> 29;
128
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
130
/* Handle any leading odd-sized chunks */
133
unsigned char *p = (unsigned char *) ctx->in + t;
137
memcpy (p, buf, len);
141
byteReverse (ctx->in, 16);
142
MD5Transform (ctx->buf, (guint32 *) ctx->in, ctx->version);
146
/* Process data in 64-byte chunks */
149
memcpy (ctx->in, buf, 64);
150
byteReverse (ctx->in, 16);
151
MD5Transform (ctx->buf, (guint32 *) ctx->in, ctx->version);
156
/* Handle any remaining bytes of data. */
158
memcpy (ctx->in, buf, len);
163
OpenDaap_MD5Final (MD5_CTX *ctx,
164
unsigned char digest[16])
169
/* Compute number of bytes mod 64 */
170
count = (ctx->bits[0] >> 3) & 0x3F;
172
/* Set the first char of padding to 0x80. This is safe since there is
173
always at least one byte free */
177
/* Bytes of padding needed to make 64 bytes */
178
count = 64 - 1 - count;
180
/* Pad out to 56 mod 64 */
182
/* Two lots of padding: Pad the first block to 64 bytes */
183
memset (p, 0, count);
184
byteReverse (ctx->in, 16);
185
MD5Transform (ctx->buf, (guint32 *) ctx->in, ctx->version);
187
/* Now fill the next block with 56 bytes */
188
memset (ctx->in, 0, 56);
190
/* Pad block to 56 bytes */
191
memset (p, 0, count - 8);
193
byteReverse (ctx->in, 14);
195
/* Append length in bits and transform */
196
((guint32 *) ctx->in)[14] = ctx->bits[0];
197
((guint32 *) ctx->in)[15] = ctx->bits[1];
199
MD5Transform (ctx->buf, (guint32 *) ctx->in, ctx->version);
200
byteReverse ((unsigned char *) ctx->buf, 4);
201
memcpy (digest, ctx->buf, 16);
202
memset (ctx, 0, sizeof(ctx)); /* In case it's sensitive */
209
/* The four core functions - F1 is optimized somewhat */
211
/* #define F1(x, y, z) (x & y | ~x & z) */
212
#define F1(x, y, z) (z ^ (x & (y ^ z)))
213
#define F2(x, y, z) F1(z, x, y)
214
#define F3(x, y, z) (x ^ y ^ z)
215
#define F4(x, y, z) (y ^ (x | ~z))
217
/* This is the central step in the MD5 algorithm. */
218
#define MD5STEP(f, w, x, y, z, data, s) \
219
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
222
* The core of the MD5 algorithm, this alters an existing MD5 hash to reflect
223
* the addition of 16 longwords of new data. OpenDaap_MD5Update blocks the
224
* data and converts bytes into longwords for this routine.
227
MD5Transform (guint32 buf[4],
228
guint32 const in[16],
238
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
239
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
240
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
241
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
242
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
243
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
244
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
245
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
246
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
247
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
248
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
249
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
250
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
251
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
252
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
253
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
255
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
256
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
257
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
258
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
259
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
260
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
261
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
262
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
263
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
264
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
265
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
269
MD5STEP(F2, b, c, d, a, in[8] + 0x445a14ed, 20);
273
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
275
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
276
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
277
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
278
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
280
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
281
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
282
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
283
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
284
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
285
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
286
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
287
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
288
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
289
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
290
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
291
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
292
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
293
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
294
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
295
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
297
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
298
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
299
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
300
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
301
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
302
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
303
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
304
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
305
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
306
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
307
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
308
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
309
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
310
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
311
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
312
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
326
static int staticHashDone = 0;
327
static unsigned char staticHash_42[256*65] = {0};
328
static unsigned char staticHash_45[256*65] = {0};
330
static const char hexchars[] = "0123456789ABCDEF";
331
static char ac[] = "Dpqzsjhiu!3114!Bqqmf!Dpnqvufs-!Jod/"; /* +1 */
332
static gboolean ac_unfudged = FALSE;
335
DigestToString (const unsigned char *digest,
339
for (i = 0; i < 16; i++)
341
unsigned char tmp = digest[i];
342
string[i*2+1] = hexchars[tmp & 0x0f];
343
string[i*2] = hexchars[(tmp >> 4) & 0x0f];
351
unsigned char *p = staticHash_42;
353
unsigned char buf[16];
355
for (i = 0; i < 256; i++)
357
OpenDaap_MD5Init (&ctx, 0);
359
#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, (unsigned char const *)str, strlen(str))
362
MD5_STRUPDATE("Accept-Language");
364
MD5_STRUPDATE("user-agent");
367
MD5_STRUPDATE("max-age");
369
MD5_STRUPDATE("Authorization");
372
MD5_STRUPDATE("Client-DAAP-Version");
374
MD5_STRUPDATE("Accept-Encoding");
377
MD5_STRUPDATE("daap.protocolversion");
379
MD5_STRUPDATE("daap.songartist");
382
MD5_STRUPDATE("daap.songcomposer");
384
MD5_STRUPDATE("daap.songdatemodified");
387
MD5_STRUPDATE("daap.songdiscnumber");
389
MD5_STRUPDATE("daap.songdisabled");
392
MD5_STRUPDATE("playlist-item-spec");
394
MD5_STRUPDATE("revision-number");
397
MD5_STRUPDATE("session-id");
399
MD5_STRUPDATE("content-codes");
402
OpenDaap_MD5Final (&ctx, buf);
403
DigestToString (buf, (char *)p);
408
static void GenerateStatic_45()
411
unsigned char *p = staticHash_45;
413
unsigned char buf[16];
415
for (i = 0; i < 256; i++)
417
OpenDaap_MD5Init (&ctx, 1);
419
#define MD5_STRUPDATE(str) OpenDaap_MD5Update(&ctx, (unsigned char const *)str, strlen(str))
422
MD5_STRUPDATE("eqwsdxcqwesdc");
424
MD5_STRUPDATE("op[;lm,piojkmn");
427
MD5_STRUPDATE("876trfvb 34rtgbvc");
429
MD5_STRUPDATE("=-0ol.,m3ewrdfv");
432
MD5_STRUPDATE("87654323e4rgbv ");
434
MD5_STRUPDATE("1535753690868867974342659792");
437
MD5_STRUPDATE("Song Name");
439
MD5_STRUPDATE("DAAP-CLIENT-ID:");
442
MD5_STRUPDATE("111222333444555");
444
MD5_STRUPDATE("4089961010");
447
MD5_STRUPDATE("playlist-item-spec");
449
MD5_STRUPDATE("revision-number");
452
MD5_STRUPDATE("session-id");
454
MD5_STRUPDATE("content-codes");
457
MD5_STRUPDATE("IUYHGFDCXWEDFGHN");
459
MD5_STRUPDATE("iuytgfdxwerfghjm");
463
OpenDaap_MD5Final (&ctx, buf);
464
DigestToString (buf, (char *)p);
470
rb_daap_hash_generate (short version_major,
476
unsigned char buf[16];
480
unsigned char *hashTable = (version_major == 3) ?
481
staticHash_45 : staticHash_42;
485
GenerateStatic_42 ();
486
GenerateStatic_45 ();
490
OpenDaap_MD5Init (&ctx, (version_major == 3) ? 1 : 0);
492
OpenDaap_MD5Update (&ctx, url, strlen ((const gchar*)url));
493
if (ac_unfudged == FALSE) {
494
for (i = 0; i < strlen (ac); i++) {
499
OpenDaap_MD5Update (&ctx, (const guchar*)ac, strlen (ac));
501
OpenDaap_MD5Update (&ctx, &hashTable[hash_select * 65], 32);
503
if (request_id && version_major == 3)
506
sprintf (scribble, "%u", request_id);
507
OpenDaap_MD5Update (&ctx, (const guchar*)scribble, strlen (scribble));
510
OpenDaap_MD5Final (&ctx, buf);
511
DigestToString (buf, (char *)out);
521
#include <libsoup/soup.h>
522
#include <libsoup/soup-connection.h>
523
#include <libsoup/soup-session-sync.h>
525
#include <libsoup/soup-uri.h>
527
#define RB_DAAP_USER_AGENT "iTunes/4.6 (Windows; N)"
530
static GObject * rb_daap_connection_constructor (GType type, guint n_construct_properties,
531
GObjectConstructParam *construct_properties);
532
static void rb_daap_connection_dispose (GObject *obj);
533
static void rb_daap_connection_set_property (GObject *object,
537
static void rb_daap_connection_get_property (GObject *object,
542
static void rb_daap_connection_do_something (RBDAAPConnection *connection);
543
static void rb_daap_connection_state_done (RBDAAPConnection *connection, gboolean result);
546
G_DEFINE_TYPE (RBDAAPConnection, rb_daap_connection, G_TYPE_OBJECT)
547
#define DAAP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_DAAP_CONNECTION, RBDaapConnectionPrivate))
549
typedef void (*RBDAAPResponseHandler) (RBDAAPConnection *connection,
555
gboolean password_protected;
560
SoupSession *session;
562
gchar *daap_base_uri;
564
gdouble daap_version;
566
gint revision_number;
571
guint reading_playlist;
573
GHashTable *item_id_to_uri;
576
RhythmDBEntryType db_type;
582
DAAP_GET_REVISION_NUMBER,
586
DAAP_GET_PLAYLIST_ENTRIES,
590
RBDAAPResponseHandler response_handler;
593
RBDAAPConnectionCallback callback;
594
gpointer callback_user_data;
595
} RBDaapConnectionPrivate;
605
PROP_PASSWORD_PROTECTED,
611
rb_daap_connection_class_init (RBDAAPConnectionClass *klass)
613
GObjectClass *object_class = G_OBJECT_CLASS (klass);
615
object_class->constructor = rb_daap_connection_constructor;
616
object_class->dispose = rb_daap_connection_dispose;
617
object_class->set_property = rb_daap_connection_set_property;
618
object_class->get_property = rb_daap_connection_get_property;
620
g_type_class_add_private (klass, sizeof (RBDaapConnectionPrivate));
622
g_object_class_install_property (object_class,
624
g_param_spec_object ("db",
628
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
629
g_object_class_install_property (object_class,
631
g_param_spec_pointer ("callback",
636
g_object_class_install_property (object_class,
638
g_param_spec_pointer ("callback-data",
640
"callback user data",
642
g_object_class_install_property (object_class,
644
g_param_spec_uint ("entry-type",
648
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
649
g_object_class_install_property (object_class,
650
PROP_PASSWORD_PROTECTED,
651
g_param_spec_boolean ("password-protected",
652
"password protected",
653
"connection is password protected",
655
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
656
g_object_class_install_property (object_class,
658
g_param_spec_string ("name",
662
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
663
g_object_class_install_property (object_class,
665
g_param_spec_string ("host",
669
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
670
g_object_class_install_property (object_class,
672
g_param_spec_uint ("port",
676
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
680
rb_daap_connection_init (RBDAAPConnection *connection)
687
connection_get_password (RBDAAPConnection *connection)
689
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
691
return rb_daap_password_dialog_new_run (priv->name);
696
build_message (RBDAAPConnection *connection,
703
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
704
SoupMessage *message = NULL;
707
uri = soup_uri_new_with_base (priv->base_uri, path);
712
message = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
713
soup_message_set_http_version (message, SOUP_HTTP_1_1);
715
soup_message_add_header (message->request_headers, "Client-DAAP-Version", "3.0");
716
soup_message_add_header (message->request_headers, "Accept-Language", "en-us, en;q=5.0");
718
soup_message_add_header (message->request_headers, "Accept-Encoding", "gzip");
720
soup_message_add_header (message->request_headers, "Client-DAAP-Access-Index", "2");
721
if (priv->password_protected) {
722
gchar *h = g_strconcat ("Basic ", priv->password, NULL);
724
soup_message_add_header (message->request_headers, "Authorization", h);
729
gchar hash[33] = {0};
730
gchar *no_daap_path = (gchar *)path;
732
if (g_strncasecmp (path, "daap://", 7) == 0) {
733
no_daap_path = strstr (path, "/data");
736
rb_daap_hash_generate ((short)floor (version), (const guchar*)no_daap_path, 2, (guchar*)hash, req_id);
738
soup_message_add_header (message->request_headers, "Client-DAAP-Validation", hash);
741
soup_message_add_header (message->request_headers, "Connection", "close");
750
static void *g_zalloc_wrapper (voidpf opaque, uInt items, uInt size)
752
return g_malloc0 (items * size);
755
static void g_zfree_wrapper (voidpf opaque, voidpf address)
762
http_response_handler (SoupMessage *message,
763
RBDAAPConnection *connection)
765
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
766
GNode *structure = NULL;
767
guint status = message->status_code;
768
char *response = message->response.body;
769
int response_length = message->response.length;
770
const char *encoding_header = NULL;
772
if (message->response_headers)
773
encoding_header = soup_message_get_header (message->response_headers, "Content-Encoding");
775
if (SOUP_STATUS_IS_SUCCESSFUL (status) && encoding_header && strcmp(encoding_header, "gzip") == 0) {
780
int unc_size = response_length * factor;
782
stream.next_in = (unsigned char *)response;
783
stream.avail_in = response_length;
786
new_response = g_malloc (unc_size + 1);
787
stream.next_out = (unsigned char *)new_response;
788
stream.avail_out = unc_size;
789
stream.total_out = 0;
790
stream.zalloc = g_zalloc_wrapper;
791
stream.zfree = g_zfree_wrapper;
792
stream.opaque = NULL;
794
if (inflateInit2 (&stream, 32 /* auto-detect */ + 15 /* max */ ) != Z_OK) {
795
inflateEnd (&stream);
796
g_free (new_response);
797
rb_debug ("Unable to decompress response from http://%s:%d/%s",
798
priv->base_uri->host,
799
priv->base_uri->port,
800
priv->base_uri->path);
801
status = SOUP_STATUS_MALFORMED;
804
int z_res = inflate (&stream, Z_FINISH);
805
if (z_res == Z_STREAM_END)
807
if ((z_res != Z_OK && z_res != Z_BUF_ERROR) || stream.avail_out != 0 || unc_size > 40*1000*1000) {
808
inflateEnd (&stream);
809
g_free (new_response);
815
unc_size = (response_length * factor);
816
new_response = g_realloc (new_response, unc_size + 1);
817
stream.next_out = (unsigned char *)(new_response + stream.total_out);
818
stream.avail_out = unc_size - stream.total_out;
823
response = new_response;
824
response_length = stream.total_out;
827
rb_debug ("Received compressed response from http://%s:%d/%s but can't handle it",
828
priv->base_uri->host,
829
priv->base_uri->port,
830
priv->base_uri->path);
831
status = SOUP_STATUS_MALFORMED;
835
if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
838
structure = rb_daap_structure_parse (response, response_length);
839
if (structure == NULL) {
840
rb_debug ("No daap structure returned from http://%s:%d/%s",
841
priv->base_uri->host,
842
priv->base_uri->port,
843
priv->base_uri->path);
844
status = SOUP_STATUS_MALFORMED;
847
item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MSTT);
849
dmap_status = g_value_get_int (&(item->content));
851
if (dmap_status != 200) {
852
rb_debug ("Error, dmap.status is not 200 in response from http://%s:%d/%s",
853
priv->base_uri->host,
854
priv->base_uri->port,
855
priv->base_uri->path);
856
status = SOUP_STATUS_MALFORMED;
860
rb_debug ("Error getting http://%s:%d/%s: %d, %s\n",
861
priv->base_uri->host,
862
priv->base_uri->port,
863
priv->base_uri->path,
864
message->status_code, message->reason_phrase);
867
if (priv->response_handler) {
868
RBDAAPResponseHandler h = priv->response_handler;
869
priv->response_handler = NULL;
870
(*h) (connection, status, structure);
874
rb_daap_structure_destroy (structure);
876
if (response != message->response.body)
881
http_get (RBDAAPConnection *connection,
887
RBDAAPResponseHandler handler)
889
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
890
SoupMessage *message;
892
message = build_message (connection, path, need_hash, version, req_id, send_close);
893
if (message == NULL) {
894
rb_debug ("Error building message for http://%s:%d/%s",
895
priv->base_uri->host,
896
priv->base_uri->port,
901
priv->response_handler = handler;
902
soup_session_queue_message (priv->session, message,
903
(SoupMessageCallbackFn) http_response_handler,
905
rb_debug ("Queued message for http://%s:%d/%s",
906
priv->base_uri->host,
907
priv->base_uri->port,
914
entry_set_string_prop (RhythmDB *db,
915
RhythmDBEntry *entry,
916
RhythmDBPropType propid,
923
tmp = g_strdup (_("Unknown"));
925
tmp = g_strdup (str);
928
g_value_init (&value, G_TYPE_STRING);
929
g_value_set_string_take_ownership (&value, tmp);
930
rhythmdb_entry_set_uninserted (RHYTHMDB (db), entry, propid, &value);
931
g_value_unset (&value);
935
handle_server_info (RBDAAPConnection *connection, guint status, GNode *structure)
937
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
938
RBDAAPItem *item = NULL;
940
if (!SOUP_STATUS_IS_SUCCESSFUL (status) || structure == NULL) {
941
rb_daap_connection_state_done (connection, FALSE);
945
/* get the daap version number */
946
item = rb_daap_structure_find_item (structure, RB_DAAP_CC_APRO);
948
rb_daap_connection_state_done (connection, FALSE);
952
priv->daap_version = g_value_get_double (&(item->content));
953
rb_daap_connection_state_done (connection, TRUE);
957
handle_login (RBDAAPConnection *connection, guint status, GNode *structure)
959
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
960
RBDAAPItem *item = NULL;
962
if (status == SOUP_STATUS_UNAUTHORIZED || status == SOUP_STATUS_FORBIDDEN) {
963
rb_debug ("Incorrect password");
964
priv->state = DAAP_GET_PASSWORD;
965
rb_daap_connection_do_something (connection);
968
if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
969
rb_daap_connection_state_done (connection, FALSE);
973
item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MLID);
975
rb_debug ("Could not find daap.sessionid item in /login");
976
rb_daap_connection_state_done (connection, FALSE);
980
priv->session_id = g_value_get_int (&(item->content));
981
rb_daap_connection_state_done (connection, TRUE);
985
handle_update (RBDAAPConnection *connection, guint status, GNode *structure)
987
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
990
if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
991
rb_daap_connection_state_done (connection, FALSE);
995
/* get a revision number */
996
item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUSR);
998
rb_debug ("Could not find daap.serverrevision item in /update");
999
rb_daap_connection_state_done (connection, FALSE);
1003
priv->revision_number = g_value_get_int (&(item->content));
1004
rb_daap_connection_state_done (connection, TRUE);
1008
handle_database_info (RBDAAPConnection *connection, guint status, GNode *structure)
1010
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1011
RBDAAPItem *item = NULL;
1012
GNode *listing_node;
1013
gint n_databases = 0;
1015
/* get a list of databases, there should be only 1 */
1017
if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
1018
rb_daap_connection_state_done (connection, FALSE);
1022
item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
1024
rb_debug ("Could not find dmap.returnedcount item in /databases");
1025
rb_daap_connection_state_done (connection, FALSE);
1029
n_databases = g_value_get_int (&(item->content));
1030
if (n_databases != 1) {
1031
rb_debug ("Host seems to have more than 1 database, how strange\n");
1034
listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
1035
if (listing_node == NULL) {
1036
rb_debug ("Could not find dmap.listing item in /databases");
1037
rb_daap_connection_state_done (connection, FALSE);
1041
item = rb_daap_structure_find_item (listing_node->children, RB_DAAP_CC_MIID);
1043
rb_debug ("Could not find dmap.itemid item in /databases");
1044
rb_daap_connection_state_done (connection, FALSE);
1048
priv->database_id = g_value_get_int (&(item->content));
1049
rb_daap_connection_state_done (connection, TRUE);
1053
handle_song_listing (RBDAAPConnection *connection, guint status, GNode *structure)
1055
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1056
RBDAAPItem *item = NULL;
1057
GNode *listing_node;
1058
gint returned_count;
1061
gint specified_total_count;
1062
gboolean update_type;
1066
if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
1067
rb_daap_connection_state_done (connection, FALSE);
1071
item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
1073
rb_debug ("Could not find dmap.returnedcount item in /databases/%d/items",
1075
rb_daap_connection_state_done (connection, FALSE);
1078
returned_count = g_value_get_int (&(item->content));
1080
item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MTCO);
1082
rb_debug ("Could not find dmap.specifiedtotalcount item in /databases/%d/items",
1084
rb_daap_connection_state_done (connection, FALSE);
1087
specified_total_count = g_value_get_int (&(item->content));
1089
item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUTY);
1091
rb_debug ("Could not find dmap.updatetype item in /databases/%d/items",
1093
rb_daap_connection_state_done (connection, FALSE);
1096
update_type = g_value_get_char (&(item->content));
1098
listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
1099
if (listing_node == NULL) {
1100
rb_debug ("Could not find dmap.listing item in /databases/%d/items",
1102
rb_daap_connection_state_done (connection, FALSE);
1106
priv->item_id_to_uri = g_hash_table_new_full ((GHashFunc)g_direct_hash,(GEqualFunc)g_direct_equal, NULL, g_free);
1108
for (i = 0, n = listing_node->children; n; i++, n = n->next) {
1110
RhythmDBEntry *entry = NULL;
1111
GValue value = {0,};
1114
const gchar *title = NULL;
1115
const gchar *album = NULL;
1116
const gchar *artist = NULL;
1117
const gchar *format = NULL;
1118
const gchar *genre = NULL;
1120
gint track_number = 0;
1121
gint disc_number = 0;
1126
for (n2 = n->children; n2; n2 = n2->next) {
1127
RBDAAPItem *meta_item;
1129
meta_item = n2->data;
1131
switch (meta_item->content_code) {
1132
case RB_DAAP_CC_MIID:
1133
item_id = g_value_get_int (&(meta_item->content));
1135
case RB_DAAP_CC_MINM:
1136
title = g_value_get_string (&(meta_item->content));
1138
case RB_DAAP_CC_ASAL:
1139
album = g_value_get_string (&(meta_item->content));
1141
case RB_DAAP_CC_ASAR:
1142
artist = g_value_get_string (&(meta_item->content));
1144
case RB_DAAP_CC_ASFM:
1145
format = g_value_get_string (&(meta_item->content));
1147
case RB_DAAP_CC_ASGN:
1148
genre = g_value_get_string (&(meta_item->content));
1150
case RB_DAAP_CC_ASTM:
1151
length = g_value_get_int (&(meta_item->content));
1153
case RB_DAAP_CC_ASTN:
1154
track_number = g_value_get_int (&(meta_item->content));
1156
case RB_DAAP_CC_ASDN:
1157
disc_number = g_value_get_int (&(meta_item->content));
1159
case RB_DAAP_CC_ASYR:
1160
year = g_value_get_int (&(meta_item->content));
1162
case RB_DAAP_CC_ASSZ:
1163
size = g_value_get_int (&(meta_item->content));
1165
case RB_DAAP_CC_ASBR:
1166
bitrate = g_value_get_int (&(meta_item->content));
1173
// if (connection->daap_version == 3.0) {
1174
uri = g_strdup_printf ("%s/databases/%d/items/%d.%s?session-id=%d",
1175
priv->daap_base_uri,
1183
// "/databases/%d/items/%d.%s?session-id=%d&revision-id=%d";
1184
// but its not going to work cause the other parts of the code
1185
// depend on the uri to have the ip address so that the
1186
// RBDAAPSource can be found to ++request_id
1187
// maybe just /dont/ support older itunes. doesn't seem
1188
// unreasonable to me, honestly
1190
entry = rhythmdb_entry_new (priv->db, priv->db_type, uri);
1191
g_hash_table_insert (priv->item_id_to_uri, GINT_TO_POINTER (item_id), uri);
1194
g_value_init (&value, G_TYPE_ULONG);
1195
g_value_set_ulong (&value,(gulong)track_number);
1196
rhythmdb_entry_set_uninserted (priv->db, entry, RHYTHMDB_PROP_TRACK_NUMBER, &value);
1197
g_value_unset (&value);
1200
g_value_init (&value, G_TYPE_ULONG);
1201
g_value_set_ulong (&value,(gulong)disc_number);
1202
rhythmdb_entry_set_uninserted (priv->db, entry, RHYTHMDB_PROP_DISC_NUMBER, &value);
1203
g_value_unset (&value);
1206
g_value_init (&value, G_TYPE_ULONG);
1207
g_value_set_ulong (&value,(gulong)bitrate);
1208
rhythmdb_entry_set_uninserted (priv->db, entry, RHYTHMDB_PROP_BITRATE, &value);
1209
g_value_unset (&value);
1212
g_value_init (&value, G_TYPE_ULONG);
1213
g_value_set_ulong (&value,(gulong)length / 1000);
1214
rhythmdb_entry_set_uninserted (priv->db, entry, RHYTHMDB_PROP_DURATION, &value);
1215
g_value_unset (&value);
1218
g_value_init (&value, G_TYPE_UINT64);
1219
g_value_set_uint64(&value,(gint64)size);
1220
rhythmdb_entry_set_uninserted (priv->db, entry, RHYTHMDB_PROP_FILE_SIZE, &value);
1221
g_value_unset (&value);
1224
entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_TITLE, title);
1227
entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_ALBUM, album);
1230
entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_ARTIST, artist);
1233
entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_GENRE, genre);
1236
rhythmdb_commit (priv->db);
1238
rb_daap_connection_state_done (connection, TRUE);
1242
* what we really should do is only get a list of playlists and their ids
1243
* then when they are clicked on ('activate'd) by the user, get a list of
1244
* the files that are actually in them. This will speed up initial daap
1245
* connection times and reduce memory consumption.
1249
handle_playlists (RBDAAPConnection *connection, guint status, GNode *structure)
1251
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1252
GNode *listing_node;
1256
if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
1257
rb_daap_connection_state_done (connection, FALSE);
1261
listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
1262
if (listing_node == NULL) {
1263
rb_debug ("Could not find dmap.listing item in /databases/%d/containers",
1265
rb_daap_connection_state_done (connection, FALSE);
1269
for (i = 0, n = listing_node->children; n; n = n->next, i++) {
1273
RBDAAPPlaylist *playlist;
1275
item = rb_daap_structure_find_item (n, RB_DAAP_CC_ABPL);
1280
item = rb_daap_structure_find_item (n, RB_DAAP_CC_MIID);
1282
rb_debug ("Could not find dmap.itemid item in /databases/%d/containers",
1286
id = g_value_get_int (&(item->content));
1288
item = rb_daap_structure_find_item (n, RB_DAAP_CC_MINM);
1290
rb_debug ("Could not find dmap.itemname item in /databases/%d/containers",
1294
name = g_value_dup_string (&(item->content));
1296
playlist = g_new0 (RBDAAPPlaylist, 1);
1298
playlist->name = name;
1299
rb_debug ("Got playlist %p: name %s, id %d", playlist, playlist->name, playlist->id);
1301
priv->playlists = g_slist_prepend (priv->playlists, playlist);
1304
rb_daap_connection_state_done (connection, TRUE);
1308
handle_playlist_entries (RBDAAPConnection *connection, guint status, GNode *structure)
1310
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1311
RBDAAPPlaylist *playlist;
1312
GNode *listing_node;
1315
GList *playlist_uris = NULL;
1317
if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
1318
rb_daap_connection_state_done (connection, FALSE);
1322
playlist = (RBDAAPPlaylist *)g_slist_nth_data (priv->playlists, priv->reading_playlist);
1323
g_assert (playlist);
1325
listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
1326
if (listing_node == NULL) {
1327
rb_debug ("Could not find dmap.listing item in /databases/%d/containers/%d/items",
1328
priv->database_id, playlist->id);
1329
rb_daap_connection_state_done (connection, FALSE);
1333
for (i = 0, node = listing_node->children; node; node = node->next, i++) {
1335
gint playlist_item_id;
1338
item = rb_daap_structure_find_item (node, RB_DAAP_CC_MIID);
1340
rb_debug ("Could not find dmap.itemid item in /databases/%d/containers/%d/items",
1341
priv->database_id, playlist->id);
1344
playlist_item_id = g_value_get_int (&(item->content));
1346
item_uri = g_hash_table_lookup (priv->item_id_to_uri, GINT_TO_POINTER (playlist_item_id));
1347
if (item_uri == NULL) {
1348
rb_debug ("Entry %d in playlist %s doesn't exist in the database\n",
1349
playlist_item_id, playlist->name);
1353
playlist_uris = g_list_prepend (playlist_uris, g_strdup (item_uri));
1356
playlist->uris = playlist_uris;
1357
rb_daap_connection_state_done (connection, TRUE);
1361
handle_logout (RBDAAPConnection *connection, guint status, GNode *structure)
1363
/* is there any point handling errors here? */
1364
rb_daap_connection_state_done (connection, TRUE);
1368
rb_daap_connection_new (const gchar *name,
1371
gboolean password_protected,
1373
RhythmDBEntryType type,
1374
RBDAAPConnectionCallback callback,
1377
return g_object_new (RB_TYPE_DAAP_CONNECTION,
1380
"password-protected", password_protected,
1381
"callback", callback,
1382
"callback-data", user_data,
1390
rb_daap_connection_constructor (GType type, guint n_construct_properties,
1391
GObjectConstructParam *construct_properties)
1393
RBDAAPConnection *connection;
1394
RBDaapConnectionPrivate *priv;
1397
connection = RB_DAAP_CONNECTION (G_OBJECT_CLASS(rb_daap_connection_parent_class)->
1398
constructor (type, n_construct_properties, construct_properties));
1400
priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1401
priv->result = TRUE;
1403
rb_debug ("Creating new DAAP connection to %s:%d", priv->host, priv->port);
1405
priv->session = soup_session_async_new ();
1406
path = g_strdup_printf ("http://%s:%d", priv->host, priv->port);
1407
priv->base_uri = soup_uri_new (path);
1410
if (priv->base_uri == NULL) {
1411
rb_debug ("Error parsing http://%s:%d", priv->host, priv->port);
1412
g_object_unref (G_OBJECT (connection));
1416
priv->daap_base_uri = g_strdup_printf ("daap://%s:%d", priv->host, priv->port);
1418
priv->state = DAAP_GET_INFO;
1419
rb_daap_connection_do_something (connection);
1421
return G_OBJECT (connection);
1425
rb_daap_connection_logout (RBDAAPConnection *connection,
1426
RBDAAPConnectionCallback callback,
1429
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1431
if (priv->state == DAAP_LOGOUT)
1434
/* FIXME what to do if we get asked to log out before login has completed? */
1435
g_assert (priv->state == DAAP_DONE);
1437
priv->callback = callback;
1438
priv->callback_user_data = user_data;
1439
priv->result = TRUE;
1441
priv->state = DAAP_LOGOUT;
1442
rb_daap_connection_do_something (connection);
1446
rb_daap_connection_state_done (RBDAAPConnection *connection, gboolean result)
1448
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1450
if (result == FALSE) {
1451
priv->state = DAAP_DONE;
1452
priv->result = FALSE;
1454
switch (priv->state) {
1455
case DAAP_GET_PLAYLISTS:
1456
if (priv->playlists == NULL)
1457
priv->state = DAAP_DONE;
1459
priv->state = DAAP_GET_PLAYLIST_ENTRIES;
1461
case DAAP_GET_PLAYLIST_ENTRIES:
1462
/* keep reading playlists until we've got them all */
1463
if (++priv->reading_playlist >= g_slist_length (priv->playlists))
1464
priv->state = DAAP_DONE;
1468
priv->state = DAAP_DONE;
1473
rb_debug ("This should never happen.");
1477
/* in most states, we just move on to the next */
1478
if (priv->state > DAAP_DONE) {
1479
rb_debug ("This should REALLY never happen.");
1487
rb_daap_connection_do_something (connection);
1491
rb_daap_connection_do_something (RBDAAPConnection *connection)
1493
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1496
switch (priv->state) {
1498
rb_debug ("Getting DAAP server info");
1499
if (!http_get (connection, "/server-info", FALSE, 0.0, 0, FALSE,
1500
(RBDAAPResponseHandler) handle_server_info)) {
1501
rb_debug ("Could not get DAAP connection info");
1502
rb_daap_connection_state_done (connection, FALSE);
1506
case DAAP_GET_PASSWORD:
1507
if (priv->password_protected) {
1508
/* FIXME this bit is still synchronous */
1509
rb_debug ("Need a password for %s", priv->name);
1510
priv->password = connection_get_password (connection);
1511
if (priv->password == NULL || priv->password[0] == '\0') {
1512
rb_debug ("Password entry canceled");
1513
priv->result = FALSE;
1514
priv->state = DAAP_DONE;
1515
rb_daap_connection_do_something (connection);
1520
/* otherwise, fall through */
1521
priv->state = DAAP_LOGIN;
1524
rb_debug ("Logging into DAAP server");
1525
if (!http_get (connection, "/login", FALSE, 0.0, 0, FALSE,
1526
(RBDAAPResponseHandler) handle_login)) {
1527
rb_debug ("Could not login to DAAP server");
1528
rb_daap_connection_state_done (connection, FALSE);
1532
case DAAP_GET_REVISION_NUMBER:
1533
rb_debug ("Getting DAAP server database revision number");
1534
path = g_strdup_printf ("/update?session-id=%d&revision-number=1", priv->session_id);
1535
if (!http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1536
(RBDAAPResponseHandler) handle_update)) {
1537
rb_debug ("Could not get server database revision number");
1538
rb_daap_connection_state_done (connection, FALSE);
1543
case DAAP_GET_DB_INFO:
1544
rb_debug ("Getting DAAP database info");
1545
path = g_strdup_printf ("/databases?session-id=%d&revision-number=%d",
1546
priv->session_id, priv->revision_number);
1547
if (!http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1548
(RBDAAPResponseHandler) handle_database_info)) {
1549
rb_debug ("Could not get DAAP database info");
1550
rb_daap_connection_state_done (connection, FALSE);
1555
case DAAP_GET_SONGS:
1556
rb_debug ("Getting DAAP song listing");
1557
path = g_strdup_printf ("/databases/%i/items?session-id=%i&revision-number=%i"
1558
"&meta=dmap.itemid,dmap.itemname,daap.songalbum,"
1559
"daap.songartist,daap.daap.songgenre,daap.songsize,"
1560
"daap.songtime,daap.songtrackcount,daap.songtracknumber,"
1561
"daap.songyear,daap.songformat,daap.songgenre,"
1565
priv->revision_number);
1566
if (!http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1567
(RBDAAPResponseHandler) handle_song_listing)) {
1568
rb_debug ("Could not get DAAP song listing");
1569
rb_daap_connection_state_done (connection, FALSE);
1574
case DAAP_GET_PLAYLISTS:
1575
rb_debug ("Getting DAAP playlists");
1576
path = g_strdup_printf ("/databases/%d/containers?session-id=%d&revision-number=%d",
1579
priv->revision_number);
1580
if (!http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1581
(RBDAAPResponseHandler) handle_playlists)) {
1582
rb_debug ("Could not get DAAP playlists");
1583
rb_daap_connection_state_done (connection, FALSE);
1588
case DAAP_GET_PLAYLIST_ENTRIES:
1590
RBDAAPPlaylist *playlist =
1591
(RBDAAPPlaylist *) g_slist_nth_data (priv->playlists,
1592
priv->reading_playlist);
1593
g_assert (playlist);
1594
rb_debug ("Reading DAAP playlist %d entries", priv->reading_playlist);
1595
path = g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%d&revision-number=%d&meta=dmap.itemid",
1598
priv->session_id, priv->revision_number);
1599
if (!http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1600
(RBDAAPResponseHandler) handle_playlist_entries)) {
1601
rb_debug ("Could not get entries for DAAP playlist %d",
1602
priv->reading_playlist);
1603
rb_daap_connection_state_done (connection, FALSE);
1610
rb_debug ("Logging out of DAAP server");
1611
path = g_strdup_printf ("/logout?session-id=%d", priv->session_id);
1612
if (!http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1613
(RBDAAPResponseHandler) handle_logout)) {
1614
rb_debug ("Could not log out of DAAP server");
1615
rb_daap_connection_state_done (connection, FALSE);
1621
if (priv->callback) {
1622
/* do it this way, in case the callback sets another one or destroys the object */
1623
RBDAAPConnectionCallback callback = priv->callback;
1624
priv->callback = NULL;
1625
(*callback) (connection, priv->result, priv->callback_user_data);
1632
rb_daap_connection_get_headers (RBDAAPConnection *connection,
1636
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1638
gchar hash[33] = {0};
1639
gchar *norb_daap_uri = (gchar *)uri;
1644
if (g_strncasecmp (uri,"daap://",7) == 0) {
1645
norb_daap_uri = strstr (uri,"/data");
1648
rb_daap_hash_generate ((short)floorf (priv->daap_version),
1649
(const guchar*)norb_daap_uri, 2,
1653
headers = g_string_new ("Accept: */*\r\n"
1654
"Cache-Control: no-cache\r\n"
1655
"User-Agent: " RB_DAAP_USER_AGENT "\r\n"
1656
"Accept-Language: en-us, en;q=5.0\r\n"
1657
"Client-DAAP-Access-Index: 2\r\n"
1658
"Client-DAAP-Version: 3.0\r\n");
1659
g_string_append_printf (headers,
1660
"Client-DAAP-Validation: %s\r\n"
1661
"Client-DAAP-Request-ID: %d\r\n"
1662
"Connection: close\r\n",
1663
hash, priv->request_id);
1664
if (priv->password_protected) {
1665
g_string_append_printf (headers, "Authentication: Basic %s\r\n", priv->password);
1669
g_string_append_printf (headers,"Range: bytes=%"G_GINT64_FORMAT"-\r\n", bytes);
1673
g_string_free (headers, FALSE);
1679
rb_daap_connection_get_playlists (RBDAAPConnection *connection)
1681
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (connection);
1683
return priv->playlists;
1688
rb_daap_connection_dispose (GObject *object)
1690
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (object);
1693
g_assert (priv->callback == NULL);
1696
g_free (priv->name);
1700
if (priv->password) {
1701
g_free (priv->password);
1702
priv->password = NULL;
1706
g_free (priv->host);
1710
if (priv->playlists) {
1711
for (l = priv->playlists; l; l = l->next) {
1712
RBDAAPPlaylist *playlist = l->data;
1714
g_list_foreach (playlist->uris, (GFunc)g_free, NULL);
1715
g_list_free (playlist->uris);
1716
g_free (playlist->name);
1720
g_slist_free (priv->playlists);
1721
priv->playlists = NULL;
1724
if (priv->item_id_to_uri) {
1725
g_hash_table_destroy (priv->item_id_to_uri);
1726
priv->item_id_to_uri = NULL;
1729
if (priv->session) {
1730
g_object_unref (G_OBJECT (priv->session));
1731
priv->session = NULL;
1734
if (priv->base_uri) {
1735
soup_uri_free (priv->base_uri);
1736
priv->base_uri = NULL;
1739
if (priv->daap_base_uri) {
1740
g_free (priv->daap_base_uri);
1741
priv->daap_base_uri = NULL;
1745
g_object_unref (G_OBJECT (priv->db));
1749
G_OBJECT_CLASS (rb_daap_connection_parent_class)->dispose (object);
1753
rb_daap_connection_set_property (GObject *object,
1755
const GValue *value,
1758
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (object);
1763
g_free (priv->name);
1764
priv->name = g_value_dup_string (value);
1767
priv->db = RHYTHMDB (g_value_dup_object (value));
1769
case PROP_PASSWORD_PROTECTED:
1770
priv->password_protected = g_value_get_boolean (value);
1772
case PROP_ENTRY_TYPE:
1773
priv->db_type = g_value_get_uint (value);
1776
priv->callback = g_value_get_pointer (value);
1778
case PROP_CALLBACK_DATA:
1779
priv->callback_user_data = g_value_get_pointer (value);
1782
g_free (priv->host);
1783
priv->host = g_value_dup_string (value);
1786
priv->port = g_value_get_uint (value);
1789
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1795
rb_daap_connection_get_property (GObject *object,
1800
RBDaapConnectionPrivate *priv = DAAP_CONNECTION_GET_PRIVATE (object);
1805
g_value_set_object (value, priv->db);
1808
g_value_set_string (value, priv->name);
1811
g_value_set_pointer (value, priv->callback);
1813
case PROP_CALLBACK_DATA:
1814
g_value_set_pointer (value, priv->callback_user_data);
1816
case PROP_ENTRY_TYPE:
1817
g_value_set_uint (value, priv->db_type);
1819
case PROP_PASSWORD_PROTECTED:
1820
g_value_set_boolean (value, priv->password_protected);
1823
g_value_set_string (value, priv->host);
1826
g_value_set_uint (value, priv->port);
1829
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);