~snaggen/rhythmbox/bpm

« back to all changes in this revision

Viewing changes to daapsharing/rb-daap-connection.c

  • Committer: Jonathan Matthew
  • Author(s): Jonathan Matthew
  • Date: 2007-01-07 12:16:12 UTC
  • Revision ID: git-v1:5b6958ea3cf24f2c8bcfa15b9cab1fc7798b7305
Moved lots of files around. Now everything should be in the directory it's

2007-01-07  Jonathan Matthew  <jonathan@kaolin.wh9.net>

        Moved lots of files around.  Now everything should be in the directory
        it's built in and all plugin-specific files should be installed into
        the plugin directory.

        * plugins/audioscrobbler/rb-audioscrobbler-plugin.c:
        (impl_create_configure_dialog):
        * plugins/audioscrobbler/rb-audioscrobbler.c:
        (rb_audioscrobbler_class_init), (rb_audioscrobbler_dispose),
        (rb_audioscrobbler_finalize),
        (rb_audioscrobbler_get_config_widget):
        * plugins/audioscrobbler/rb-audioscrobbler.h:
        Use rb_plugin_find_file where needed.

        * widgets/rb-uri-dialog.c: (rb_uri_dialog_class_init),
        (rb_uri_dialog_init), (rb_uri_dialog_finalize),
        (rb_uri_dialog_new), (rb_uri_dialog_response_cb),
        (rb_uri_dialog_text_changed):
        * widgets/rb-uri-dialog.h:
        New common 'enter a URI' dialog, used by iradio and podcast code.

        * plugins/iradio/rb-iradio-plugin.c: (impl_activate):
        * plugins/iradio/rb-iradio-source.c:
        (rb_iradio_source_constructor), (rb_iradio_source_new),
        (impl_song_properties), (rb_iradio_source_first_time_changed),
        (new_station_location_added), (rb_iradio_source_cmd_new_station):
        * plugins/iradio/rb-iradio-source.h:
        * plugins/iradio/rb-station-properties-dialog.c:
        (rb_station_properties_dialog_class_init),
        (rb_station_properties_dialog_init),
        (rb_station_properties_dialog_constructor),
        (rb_station_properties_dialog_set_property),
        (rb_station_properties_dialog_get_property),
        (rb_station_properties_dialog_new):
        * plugins/iradio/rb-station-properties-dialog.h:
        Use the common URI dialog, use rb_plugin_find_file where needed.

        * sources/rb-podcast-source.c:
        (rb_podcast_source_location_added_cb),
        (rb_podcast_source_cmd_new_podcast):
        Use the common URI dialog.

svn path=/trunk/; revision=4725

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2
 
 *
3
 
 *  Implementation of DAAP (iTunes Music Sharing) hashing, parsing, connection
4
 
 *
5
 
 *  Copyright (C) 2004-2005 Charles Schmidt <cschmidt2@emich.edu>
6
 
 *
7
 
 *  This program is free software; you can redistribute it and/or modify
8
 
 *  it under the terms of the GNU General Public License as published by
9
 
 *  the Free Software Foundation; either version 2 of the License, or
10
 
 *  (at your option) any later version.
11
 
 *
12
 
 *  This program is distributed in the hope that it will be useful,
13
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
 *  GNU General Public License for more details.
16
 
 *
17
 
 *  You should have received a copy of the GNU General Public License
18
 
 *  along with this program; if not, write to the Free Software
19
 
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
20
 
 *
21
 
 */
22
 
 
23
 
#include "config.h"
24
 
 
25
 
#include <stdio.h>
26
 
#include <string.h>
27
 
#include <sys/types.h>
28
 
#include <math.h>
29
 
#ifdef HAVE_LIBZ
30
 
#include <zlib.h>
31
 
#endif
32
 
 
33
 
#include <glib/gi18n.h>
34
 
#include <gdk/gdk.h>
35
 
 
36
 
#include <libsoup/soup.h>
37
 
#include <libsoup/soup-connection.h>
38
 
#include <libsoup/soup-session-sync.h>
39
 
#include <libsoup/soup-uri.h>
40
 
 
41
 
#include "rb-daap-hash.h"
42
 
#include "rb-daap-connection.h"
43
 
#include "rb-daap-structure.h"
44
 
#include "rb-marshal.h"
45
 
 
46
 
#include "rb-debug.h"
47
 
#include "rb-util.h"
48
 
 
49
 
#define RB_DAAP_USER_AGENT "iTunes/4.6 (Windows; N)"
50
 
 
51
 
static void      rb_daap_connection_dispose      (GObject *obj);
52
 
static void      rb_daap_connection_set_property (GObject *object,
53
 
                                                  guint prop_id,
54
 
                                                  const GValue *value,
55
 
                                                  GParamSpec *pspec);
56
 
static void      rb_daap_connection_get_property (GObject *object,
57
 
                                                  guint prop_id,
58
 
                                                  GValue *value,
59
 
                                                  GParamSpec *pspec);
60
 
 
61
 
static gboolean rb_daap_connection_do_something  (RBDAAPConnection *connection);
62
 
static void     rb_daap_connection_state_done    (RBDAAPConnection *connection,
63
 
                                                  gboolean           result);
64
 
 
65
 
static gboolean emit_progress_idle (RBDAAPConnection *connection);
66
 
 
67
 
G_DEFINE_TYPE (RBDAAPConnection, rb_daap_connection, G_TYPE_OBJECT)
68
 
 
69
 
#define RB_DAAP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_DAAP_CONNECTION, RBDAAPConnectionPrivate))
70
 
 
71
 
typedef void (* RBDAAPResponseHandler) (RBDAAPConnection *connection,
72
 
                                        guint status,
73
 
                                        GNode *structure);
74
 
 
75
 
struct RBDAAPConnectionPrivate {
76
 
        char *name;
77
 
        gboolean password_protected;
78
 
        char *username;
79
 
        char *password;
80
 
        char *host;
81
 
        guint port;
82
 
 
83
 
        gboolean is_connected;
84
 
        gboolean is_connecting;
85
 
 
86
 
        SoupSession *session;
87
 
        SoupUri *base_uri;
88
 
        gchar *daap_base_uri;
89
 
 
90
 
        gdouble daap_version;
91
 
        guint32 session_id;
92
 
        gint revision_number;
93
 
 
94
 
        gint request_id;
95
 
        gint database_id;
96
 
 
97
 
        guint reading_playlist;
98
 
        GSList *playlists;
99
 
        GHashTable *item_id_to_uri;
100
 
 
101
 
        RhythmDB *db;
102
 
        RhythmDBEntryType db_type;
103
 
 
104
 
        RBDAAPConnectionState state;
105
 
        RBDAAPResponseHandler response_handler;
106
 
        gboolean use_response_handler_thread;
107
 
        float progress;
108
 
 
109
 
        guint emit_progress_id;
110
 
        guint do_something_id;
111
 
 
112
 
        gboolean result;
113
 
        char *last_error_message;
114
 
};
115
 
 
116
 
enum {
117
 
        PROP_0,
118
 
        PROP_DB,
119
 
        PROP_NAME,
120
 
        PROP_ENTRY_TYPE,
121
 
        PROP_PASSWORD_PROTECTED,
122
 
        PROP_HOST,
123
 
        PROP_PORT,
124
 
};
125
 
 
126
 
enum {
127
 
        AUTHENTICATE,
128
 
        CONNECTING,
129
 
        CONNECTED,
130
 
        DISCONNECTED,
131
 
        OPERATION_DONE,
132
 
        LAST_SIGNAL
133
 
};
134
 
 
135
 
static guint signals [LAST_SIGNAL] = { 0, };
136
 
 
137
 
static void
138
 
rb_daap_connection_finalize (GObject *object)
139
 
{
140
 
        RBDAAPConnection *connection;
141
 
 
142
 
        g_return_if_fail (object != NULL);
143
 
        g_return_if_fail (RB_IS_DAAP_CONNECTION (object));
144
 
 
145
 
        connection = RB_DAAP_CONNECTION (object);
146
 
 
147
 
        g_return_if_fail (connection->priv != NULL);
148
 
 
149
 
        rb_debug ("Finalize");
150
 
 
151
 
        G_OBJECT_CLASS (rb_daap_connection_parent_class)->finalize (object);
152
 
}
153
 
 
154
 
static void
155
 
rb_daap_connection_class_init (RBDAAPConnectionClass *klass)
156
 
{
157
 
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
158
 
 
159
 
        object_class->finalize     = rb_daap_connection_finalize;
160
 
        object_class->dispose       = rb_daap_connection_dispose;
161
 
        object_class->set_property = rb_daap_connection_set_property;
162
 
        object_class->get_property = rb_daap_connection_get_property;
163
 
 
164
 
        g_type_class_add_private (klass, sizeof (RBDAAPConnectionPrivate));
165
 
 
166
 
        g_object_class_install_property (object_class,
167
 
                                         PROP_DB,
168
 
                                         g_param_spec_object ("db",
169
 
                                                              "RhythmDB",
170
 
                                                              "RhythmDB object",
171
 
                                                              RHYTHMDB_TYPE,
172
 
                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
173
 
        g_object_class_install_property (object_class,
174
 
                                         PROP_ENTRY_TYPE,
175
 
                                         g_param_spec_boxed ("entry-type",
176
 
                                                             "entry type",
177
 
                                                             "RhythmDBEntryType",
178
 
                                                             RHYTHMDB_TYPE_ENTRY_TYPE,
179
 
                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
180
 
 
181
 
        g_object_class_install_property (object_class,
182
 
                                         PROP_PASSWORD_PROTECTED,
183
 
                                         g_param_spec_boolean ("password-protected",
184
 
                                                               "password protected",
185
 
                                                               "connection is password protected",
186
 
                                                               FALSE,
187
 
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
188
 
        g_object_class_install_property (object_class,
189
 
                                         PROP_NAME,
190
 
                                         g_param_spec_string ("name",
191
 
                                                              "connection name",
192
 
                                                              "connection name",
193
 
                                                              NULL,
194
 
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
195
 
        g_object_class_install_property (object_class,
196
 
                                         PROP_HOST,
197
 
                                         g_param_spec_string ("host",
198
 
                                                              "host",
199
 
                                                              "host",
200
 
                                                              NULL,
201
 
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
202
 
        g_object_class_install_property (object_class,
203
 
                                         PROP_PORT,
204
 
                                         g_param_spec_uint ("port",
205
 
                                                            "port",
206
 
                                                            "port",
207
 
                                                            0, G_MAXINT, 0,
208
 
                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
209
 
 
210
 
        signals [AUTHENTICATE] = g_signal_new ("authenticate",
211
 
                                               G_TYPE_FROM_CLASS (object_class),
212
 
                                               G_SIGNAL_RUN_LAST,
213
 
                                               G_STRUCT_OFFSET (RBDAAPConnectionClass, authenticate),
214
 
                                               NULL,
215
 
                                               NULL,
216
 
                                               rb_marshal_STRING__STRING,
217
 
                                               G_TYPE_STRING,
218
 
                                               1, G_TYPE_STRING);
219
 
        signals [CONNECTING] = g_signal_new ("connecting",
220
 
                                             G_TYPE_FROM_CLASS (object_class),
221
 
                                             G_SIGNAL_RUN_LAST,
222
 
                                             G_STRUCT_OFFSET (RBDAAPConnectionClass, connecting),
223
 
                                             NULL,
224
 
                                             NULL,
225
 
                                             rb_marshal_VOID__ULONG_FLOAT,
226
 
                                             G_TYPE_NONE,
227
 
                                             2, G_TYPE_ULONG, G_TYPE_FLOAT);
228
 
        signals [CONNECTED] = g_signal_new ("connected",
229
 
                                            G_TYPE_FROM_CLASS (object_class),
230
 
                                            G_SIGNAL_RUN_LAST,
231
 
                                            G_STRUCT_OFFSET (RBDAAPConnectionClass, connected),
232
 
                                            NULL,
233
 
                                            NULL,
234
 
                                            g_cclosure_marshal_VOID__VOID,
235
 
                                               G_TYPE_NONE,
236
 
                                            0);
237
 
        signals [DISCONNECTED] = g_signal_new ("disconnected",
238
 
                                               G_TYPE_FROM_CLASS (object_class),
239
 
                                               G_SIGNAL_RUN_LAST,
240
 
                                               G_STRUCT_OFFSET (RBDAAPConnectionClass, disconnected),
241
 
                                               NULL,
242
 
                                               NULL,
243
 
                                               g_cclosure_marshal_VOID__VOID,
244
 
                                               G_TYPE_NONE,
245
 
                                               0);
246
 
        signals [OPERATION_DONE] = g_signal_new ("operation-done",
247
 
                                                 G_TYPE_FROM_CLASS (object_class),
248
 
                                                 G_SIGNAL_RUN_FIRST,
249
 
                                                 G_STRUCT_OFFSET (RBDAAPConnectionClass, operation_done),
250
 
                                                 NULL,
251
 
                                                 NULL,
252
 
                                                 g_cclosure_marshal_VOID__VOID,
253
 
                                                 G_TYPE_NONE,
254
 
                                                 0);
255
 
}
256
 
 
257
 
static void
258
 
rb_daap_connection_init (RBDAAPConnection *connection)
259
 
{
260
 
        connection->priv = RB_DAAP_CONNECTION_GET_PRIVATE (connection);
261
 
 
262
 
        connection->priv->username = g_strdup_printf ("Rhythmbox_%s", VERSION);
263
 
        connection->priv->db_type = RHYTHMDB_ENTRY_TYPE_INVALID;
264
 
}
265
 
 
266
 
static char *
267
 
connection_get_password (RBDAAPConnection *connection)
268
 
{
269
 
        char *password = NULL;;
270
 
 
271
 
        GDK_THREADS_ENTER ();
272
 
        g_signal_emit (connection,
273
 
                       signals [AUTHENTICATE],
274
 
                       0,
275
 
                       connection->priv->name,
276
 
                       &password);
277
 
        GDK_THREADS_LEAVE ();
278
 
 
279
 
        return password;
280
 
}
281
 
 
282
 
static void
283
 
connection_connected (RBDAAPConnection *connection)
284
 
{
285
 
        rb_debug ("Emitting connected");
286
 
 
287
 
        connection->priv->is_connected = TRUE;
288
 
 
289
 
        GDK_THREADS_ENTER ();
290
 
        g_signal_emit (connection,
291
 
                       signals [CONNECTED],
292
 
                       0);
293
 
        GDK_THREADS_LEAVE ();
294
 
}
295
 
 
296
 
static void
297
 
connection_disconnected (RBDAAPConnection *connection)
298
 
{
299
 
        rb_debug ("Emitting disconnected");
300
 
 
301
 
        connection->priv->is_connected = FALSE;
302
 
 
303
 
        GDK_THREADS_ENTER ();
304
 
        g_signal_emit (connection,
305
 
                       signals [DISCONNECTED],
306
 
                       0);
307
 
        GDK_THREADS_LEAVE ();
308
 
}
309
 
 
310
 
static void
311
 
connection_operation_done (RBDAAPConnection *connection)
312
 
{
313
 
        rb_debug ("Emitting operation done");
314
 
 
315
 
        GDK_THREADS_ENTER ();
316
 
        g_signal_emit (connection,
317
 
                       signals [OPERATION_DONE],
318
 
                       0);
319
 
        GDK_THREADS_LEAVE ();
320
 
}
321
 
 
322
 
static SoupMessage *
323
 
build_message (RBDAAPConnection *connection,
324
 
               const char       *path,
325
 
               gboolean          need_hash,
326
 
               gdouble           version,
327
 
               gint              req_id,
328
 
               gboolean          send_close)
329
 
{
330
 
        RBDAAPConnectionPrivate *priv = connection->priv;
331
 
        SoupMessage *message = NULL;
332
 
        SoupUri *uri = NULL;
333
 
 
334
 
        uri = soup_uri_new_with_base (priv->base_uri, path);
335
 
        if (uri == NULL) {
336
 
                return NULL;
337
 
        }
338
 
 
339
 
        message = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
340
 
        soup_message_set_http_version (message, SOUP_HTTP_1_1);
341
 
 
342
 
        soup_message_add_header (message->request_headers, "Client-DAAP-Version",       "3.0");
343
 
        soup_message_add_header (message->request_headers, "Accept-Language",           "en-us, en;q=5.0");
344
 
#ifdef HAVE_LIBZ
345
 
        soup_message_add_header (message->request_headers, "Accept-Encoding",           "gzip");
346
 
#endif
347
 
        soup_message_add_header (message->request_headers, "Client-DAAP-Access-Index",  "2");
348
 
 
349
 
        if (priv->password_protected) {
350
 
                char *h;
351
 
                char *user_pass;
352
 
                char *token;
353
 
 
354
 
                user_pass = g_strdup_printf ("%s:%s", priv->username, priv->password);
355
 
                token = soup_base64_encode (user_pass, strlen (user_pass));
356
 
                h = g_strdup_printf ("Basic %s", token);
357
 
 
358
 
                g_free (token);
359
 
                g_free (user_pass);
360
 
 
361
 
                soup_message_add_header (message->request_headers, "Authorization", h);
362
 
                g_free (h);
363
 
        }
364
 
 
365
 
        if (need_hash) {
366
 
                gchar hash[33] = {0};
367
 
                gchar *no_daap_path = (gchar *)path;
368
 
 
369
 
                if (g_strncasecmp (path, "daap://", 7) == 0) {
370
 
                        no_daap_path = strstr (path, "/data");
371
 
                }
372
 
 
373
 
                rb_daap_hash_generate ((short)floor (version), (const guchar*)no_daap_path, 2, (guchar*)hash, req_id);
374
 
 
375
 
                soup_message_add_header (message->request_headers, "Client-DAAP-Validation", hash);
376
 
        }
377
 
        if (send_close) {
378
 
                soup_message_add_header (message->request_headers, "Connection", "close");
379
 
        }
380
 
 
381
 
        soup_uri_free (uri);
382
 
 
383
 
        return message;
384
 
}
385
 
 
386
 
#ifdef HAVE_LIBZ
387
 
static void
388
 
*g_zalloc_wrapper (voidpf opaque, uInt items, uInt size)
389
 
{
390
 
        if ((items != 0) && (size >= G_MAXUINT/items)) {
391
 
                return Z_NULL;
392
 
        }
393
 
        if ((size != 0) && (items >= G_MAXUINT/size)) {
394
 
                return Z_NULL;
395
 
        }
396
 
        return g_malloc0 (items * size);
397
 
}
398
 
 
399
 
static void
400
 
g_zfree_wrapper (voidpf opaque, voidpf address)
401
 
{
402
 
        g_free (address);
403
 
}
404
 
#endif
405
 
 
406
 
static void
407
 
connection_set_error_message (RBDAAPConnection *connection,
408
 
                              const char       *message)
409
 
{
410
 
        /* FIXME: obtain a lock */
411
 
        if (connection->priv->last_error_message != NULL) {
412
 
                g_free (connection->priv->last_error_message);
413
 
        }
414
 
        connection->priv->last_error_message = g_strdup (message);
415
 
}
416
 
 
417
 
typedef struct {
418
 
        SoupMessage *message;
419
 
        int status;
420
 
        RBDAAPConnection *connection;
421
 
} DAAPResponseData;
422
 
 
423
 
static void
424
 
actual_http_response_handler (DAAPResponseData *data)
425
 
{
426
 
        RBDAAPConnectionPrivate *priv;
427
 
        GNode *structure;
428
 
        char *response;
429
 
        const char *encoding_header;
430
 
        char *message_path;
431
 
        int response_length;
432
 
 
433
 
        priv = data->connection->priv;
434
 
        structure = NULL;
435
 
        response = data->message->response.body;
436
 
        encoding_header = NULL;
437
 
        response_length = data->message->response.length;
438
 
 
439
 
        message_path = soup_uri_to_string (soup_message_get_uri (data->message), FALSE);
440
 
 
441
 
        rb_debug ("Received response from %s: %d, %s\n",
442
 
                  message_path,
443
 
                  data->message->status_code,
444
 
                  data->message->reason_phrase);
445
 
 
446
 
        if (data->message->response_headers) {
447
 
                encoding_header = soup_message_get_header (data->message->response_headers, "Content-Encoding");
448
 
        }
449
 
 
450
 
        if (SOUP_STATUS_IS_SUCCESSFUL (data->status) && encoding_header && strcmp (encoding_header, "gzip") == 0) {
451
 
#ifdef HAVE_LIBZ
452
 
                z_stream stream;
453
 
                char *new_response;
454
 
                unsigned int factor = 4;
455
 
                unsigned int unc_size = response_length * factor;
456
 
 
457
 
                stream.next_in = (unsigned char *)response;
458
 
                stream.avail_in = response_length;
459
 
                stream.total_in = 0;
460
 
 
461
 
                new_response = g_malloc (unc_size + 1);
462
 
                stream.next_out = (unsigned char *)new_response;
463
 
                stream.avail_out = unc_size;
464
 
                stream.total_out = 0;
465
 
                stream.zalloc = g_zalloc_wrapper;
466
 
                stream.zfree = g_zfree_wrapper;
467
 
                stream.opaque = NULL;
468
 
 
469
 
                rb_profile_start ("decompressing DAAP response");
470
 
 
471
 
                if (inflateInit2 (&stream, 32 /* auto-detect */ + 15 /* max */ ) != Z_OK) {
472
 
                        inflateEnd (&stream);
473
 
                        g_free (new_response);
474
 
                        rb_debug ("Unable to decompress response from %s",
475
 
                                  message_path);
476
 
                        data->status = SOUP_STATUS_MALFORMED;
477
 
                        rb_profile_end ("decompressing DAAP response (failed)");
478
 
                } else {
479
 
                        do {
480
 
                                int z_res;
481
 
 
482
 
                                rb_profile_start ("attempting inflate");
483
 
                                z_res = inflate (&stream, Z_FINISH);
484
 
                                if (z_res == Z_STREAM_END) {
485
 
                                        rb_profile_end ("attempting inflate (done)");
486
 
                                        break;
487
 
                                }
488
 
                                if ((z_res != Z_OK && z_res != Z_BUF_ERROR) || stream.avail_out != 0 || unc_size > 40*1000*1000) {
489
 
                                        inflateEnd (&stream);
490
 
                                        g_free (new_response);
491
 
                                        new_response = NULL;
492
 
                                        rb_profile_end ("attempting inflate (error)");
493
 
                                        break;
494
 
                                }
495
 
 
496
 
                                factor *= 4;
497
 
                                unc_size = (response_length * factor);
498
 
                                /* unc_size can't grow bigger than 40MB, so
499
 
                                 * unc_size can't overflow, and this realloc
500
 
                                 * call is safe
501
 
                                 */
502
 
                                new_response = g_realloc (new_response, unc_size + 1);
503
 
                                stream.next_out = (unsigned char *)(new_response + stream.total_out);
504
 
                                stream.avail_out = unc_size - stream.total_out;
505
 
                                rb_profile_end ("attempting inflate (incomplete)");
506
 
                        } while (1);
507
 
                }
508
 
                rb_profile_end ("decompressing DAAP response (successful)");
509
 
 
510
 
                if (new_response) {
511
 
                        response = new_response;
512
 
                        response_length = stream.total_out;
513
 
                }
514
 
#else
515
 
                rb_debug ("Received compressed response from %s but can't handle it",
516
 
                          message_path);
517
 
                data->status = SOUP_STATUS_MALFORMED;
518
 
#endif
519
 
        }
520
 
 
521
 
        if (SOUP_STATUS_IS_SUCCESSFUL (data->status)) {
522
 
                RBDAAPItem *item;
523
 
 
524
 
                if (!rb_is_main_thread ()) {
525
 
                        priv->progress = -1.0f;
526
 
                        if (priv->emit_progress_id != 0) {
527
 
                                g_source_remove (priv->emit_progress_id);
528
 
                        }
529
 
                        priv->emit_progress_id = g_idle_add ((GSourceFunc) emit_progress_idle, data->connection);
530
 
                }
531
 
                rb_profile_start ("parsing DAAP response");
532
 
                structure = rb_daap_structure_parse (response, response_length);
533
 
                if (structure == NULL) {
534
 
                        rb_debug ("No daap structure returned from %s",
535
 
                                  message_path);
536
 
 
537
 
                        data->status = SOUP_STATUS_MALFORMED;
538
 
                        rb_profile_end ("parsing DAAP response (failed)");
539
 
                } else {
540
 
                        int dmap_status = 0;
541
 
                        item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MSTT);
542
 
                        if (item)
543
 
                                dmap_status = g_value_get_int (&(item->content));
544
 
 
545
 
                        if (dmap_status != 200) {
546
 
                                rb_debug ("Error, dmap.status is not 200 in response from %s",
547
 
                                          message_path);
548
 
 
549
 
                                data->status = SOUP_STATUS_MALFORMED;
550
 
                        }
551
 
                        rb_profile_end ("parsing DAAP response (successful)");
552
 
                }
553
 
                if (! rb_is_main_thread ()) {
554
 
                        priv->progress = 1.0f;
555
 
                        if (priv->emit_progress_id != 0) {
556
 
                                g_source_remove (priv->emit_progress_id);
557
 
                        }
558
 
                        priv->emit_progress_id = g_idle_add ((GSourceFunc) emit_progress_idle, data->connection);
559
 
                }
560
 
        } else {
561
 
                rb_debug ("Error getting %s: %d, %s\n",
562
 
                          message_path,
563
 
                          data->message->status_code,
564
 
                          data->message->reason_phrase);
565
 
                connection_set_error_message (data->connection, data->message->reason_phrase);
566
 
        }
567
 
 
568
 
        if (priv->response_handler) {
569
 
                RBDAAPResponseHandler h = priv->response_handler;
570
 
                priv->response_handler = NULL;
571
 
                (*h) (data->connection, data->status, structure);
572
 
        }
573
 
 
574
 
        if (structure) {
575
 
                rb_daap_structure_destroy (structure);
576
 
        }
577
 
 
578
 
        if (response != data->message->response.body) {
579
 
                g_free (response);
580
 
        }
581
 
 
582
 
        g_free (message_path);
583
 
        g_object_unref (G_OBJECT (data->connection));
584
 
        g_object_unref (G_OBJECT (data->message));
585
 
        g_free (data);
586
 
}
587
 
 
588
 
static void
589
 
http_response_handler (SoupMessage      *message,
590
 
                       RBDAAPConnection *connection)
591
 
{
592
 
        DAAPResponseData *data;
593
 
        int response_length;
594
 
 
595
 
        if (message->status_code == SOUP_STATUS_CANCELLED) {
596
 
                rb_debug ("Message cancelled");
597
 
                return;
598
 
        }
599
 
 
600
 
        data = g_new0 (DAAPResponseData, 1);
601
 
        data->status = message->status_code;
602
 
        response_length = message->response.length;
603
 
 
604
 
        g_object_ref (G_OBJECT (connection));
605
 
        data->connection = connection;
606
 
 
607
 
        g_object_ref (G_OBJECT (message));
608
 
        data->message = message;
609
 
 
610
 
        if (response_length >= G_MAXUINT/4 - 1) {
611
 
                /* If response_length is too big,
612
 
                 * the g_malloc (unc_size + 1) below would overflow
613
 
                 */
614
 
                data->status = SOUP_STATUS_MALFORMED;
615
 
        }
616
 
 
617
 
        /* to avoid blocking the UI, handle big responses in a separate thread */
618
 
        if (SOUP_STATUS_IS_SUCCESSFUL (data->status) && connection->priv->use_response_handler_thread) {
619
 
                GError *error = NULL;
620
 
                rb_debug ("creating thread to handle daap response");
621
 
                g_thread_create ((GThreadFunc) actual_http_response_handler,
622
 
                                 data,
623
 
                                 FALSE,
624
 
                                 &error);
625
 
                if (error) {
626
 
                        g_warning ("fuck");
627
 
                }
628
 
        } else {
629
 
                actual_http_response_handler (data);
630
 
        }
631
 
}
632
 
 
633
 
static gboolean
634
 
http_get (RBDAAPConnection     *connection,
635
 
          const char           *path,
636
 
          gboolean              need_hash,
637
 
          gdouble               version,
638
 
          gint                  req_id,
639
 
          gboolean              send_close,
640
 
          RBDAAPResponseHandler handler,
641
 
          gboolean              use_thread)
642
 
{
643
 
        RBDAAPConnectionPrivate *priv = connection->priv;
644
 
        SoupMessage *message;
645
 
 
646
 
        message = build_message (connection, path, need_hash, version, req_id, send_close);
647
 
        if (message == NULL) {
648
 
                rb_debug ("Error building message for http://%s:%d/%s",
649
 
                          priv->base_uri->host,
650
 
                          priv->base_uri->port,
651
 
                          path);
652
 
                return FALSE;
653
 
        }
654
 
 
655
 
        priv->use_response_handler_thread = use_thread;
656
 
        priv->response_handler = handler;
657
 
        soup_session_queue_message (priv->session, message,
658
 
                                    (SoupMessageCallbackFn) http_response_handler,
659
 
                                    connection);
660
 
        rb_debug ("Queued message for http://%s:%d/%s",
661
 
                  priv->base_uri->host,
662
 
                  priv->base_uri->port,
663
 
                  path);
664
 
        return TRUE;
665
 
}
666
 
 
667
 
static void
668
 
entry_set_string_prop (RhythmDB        *db,
669
 
                       RhythmDBEntry   *entry,
670
 
                       RhythmDBPropType propid,
671
 
                       const char      *str)
672
 
{
673
 
        GValue value = {0,};
674
 
        const gchar *tmp;
675
 
 
676
 
        if (str == NULL || *str == '\0' || !g_utf8_validate (str, -1, NULL)) {
677
 
                tmp = _("Unknown");
678
 
        } else {
679
 
                tmp = str;
680
 
        }
681
 
 
682
 
        g_value_init (&value, G_TYPE_STRING);
683
 
        g_value_set_string (&value, tmp);
684
 
        rhythmdb_entry_set (RHYTHMDB (db), entry, propid, &value);
685
 
        g_value_unset (&value);
686
 
}
687
 
 
688
 
static gboolean
689
 
emit_progress_idle (RBDAAPConnection *connection)
690
 
{
691
 
        rb_debug ("Emitting progress");
692
 
 
693
 
        GDK_THREADS_ENTER ();
694
 
        g_signal_emit (G_OBJECT (connection), signals[CONNECTING], 0,
695
 
                       connection->priv->state,
696
 
                       connection->priv->progress);
697
 
        connection->priv->emit_progress_id = 0;
698
 
        GDK_THREADS_LEAVE ();
699
 
        return FALSE;
700
 
}
701
 
 
702
 
static void
703
 
handle_server_info (RBDAAPConnection *connection,
704
 
                    guint             status,
705
 
                    GNode            *structure)
706
 
{
707
 
        RBDAAPConnectionPrivate *priv = connection->priv;
708
 
        RBDAAPItem *item = NULL;
709
 
 
710
 
        if (!SOUP_STATUS_IS_SUCCESSFUL (status) || structure == NULL) {
711
 
                rb_daap_connection_state_done (connection, FALSE);
712
 
                return;
713
 
        }
714
 
 
715
 
        /* get the daap version number */
716
 
        item = rb_daap_structure_find_item (structure, RB_DAAP_CC_APRO);
717
 
        if (item == NULL) {
718
 
                rb_daap_connection_state_done (connection, FALSE);
719
 
                return;
720
 
        }
721
 
 
722
 
        priv->daap_version = g_value_get_double (&(item->content));
723
 
        rb_daap_connection_state_done (connection, TRUE);
724
 
}
725
 
 
726
 
static void
727
 
handle_login (RBDAAPConnection *connection,
728
 
              guint             status,
729
 
              GNode            *structure)
730
 
{
731
 
        RBDAAPConnectionPrivate *priv = connection->priv;
732
 
        RBDAAPItem *item = NULL;
733
 
 
734
 
        if (status == SOUP_STATUS_UNAUTHORIZED || status == SOUP_STATUS_FORBIDDEN) {
735
 
                rb_debug ("Incorrect password");
736
 
                priv->state = DAAP_GET_PASSWORD;
737
 
                if (priv->do_something_id != 0) {
738
 
                        g_source_remove (priv->do_something_id);
739
 
                }
740
 
                priv->do_something_id = g_idle_add ((GSourceFunc) rb_daap_connection_do_something, connection);
741
 
                return;
742
 
        }
743
 
 
744
 
        if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
745
 
                rb_daap_connection_state_done (connection, FALSE);
746
 
                return;
747
 
        }
748
 
 
749
 
        item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MLID);
750
 
        if (item == NULL) {
751
 
                rb_debug ("Could not find daap.sessionid item in /login");
752
 
                rb_daap_connection_state_done (connection, FALSE);
753
 
                return;
754
 
        }
755
 
 
756
 
        priv->session_id = (guint32) g_value_get_int (&(item->content));
757
 
 
758
 
        connection_connected (connection);
759
 
 
760
 
        rb_daap_connection_state_done (connection, TRUE);
761
 
}
762
 
 
763
 
static void
764
 
handle_update (RBDAAPConnection *connection,
765
 
               guint             status,
766
 
               GNode            *structure)
767
 
{
768
 
        RBDAAPConnectionPrivate *priv = connection->priv;
769
 
        RBDAAPItem *item;
770
 
 
771
 
        if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
772
 
                rb_daap_connection_state_done (connection, FALSE);
773
 
                return;
774
 
        }
775
 
 
776
 
        /* get a revision number */
777
 
        item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUSR);
778
 
        if (item == NULL) {
779
 
                rb_debug ("Could not find daap.serverrevision item in /update");
780
 
                rb_daap_connection_state_done (connection, FALSE);
781
 
                return;
782
 
        }
783
 
 
784
 
        priv->revision_number = g_value_get_int (&(item->content));
785
 
        rb_daap_connection_state_done (connection, TRUE);
786
 
}
787
 
 
788
 
static void
789
 
handle_database_info (RBDAAPConnection *connection,
790
 
                      guint             status,
791
 
                      GNode            *structure)
792
 
{
793
 
        RBDAAPConnectionPrivate *priv = connection->priv;
794
 
        RBDAAPItem *item = NULL;
795
 
        GNode *listing_node;
796
 
        gint n_databases = 0;
797
 
 
798
 
        /* get a list of databases, there should be only 1 */
799
 
 
800
 
        if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
801
 
                rb_daap_connection_state_done (connection, FALSE);
802
 
                return;
803
 
        }
804
 
 
805
 
        item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
806
 
        if (item == NULL) {
807
 
                rb_debug ("Could not find dmap.returnedcount item in /databases");
808
 
                rb_daap_connection_state_done (connection, FALSE);
809
 
                return;
810
 
        }
811
 
 
812
 
        n_databases = g_value_get_int (&(item->content));
813
 
        if (n_databases != 1) {
814
 
                rb_debug ("Host seems to have more than 1 database, how strange\n");
815
 
        }
816
 
 
817
 
        listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
818
 
        if (listing_node == NULL) {
819
 
                rb_debug ("Could not find dmap.listing item in /databases");
820
 
                rb_daap_connection_state_done (connection, FALSE);
821
 
                return;
822
 
        }
823
 
 
824
 
        item = rb_daap_structure_find_item (listing_node->children, RB_DAAP_CC_MIID);
825
 
        if (item == NULL) {
826
 
                rb_debug ("Could not find dmap.itemid item in /databases");
827
 
                rb_daap_connection_state_done (connection, FALSE);
828
 
                return;
829
 
        }
830
 
 
831
 
        priv->database_id = g_value_get_int (&(item->content));
832
 
        rb_daap_connection_state_done (connection, TRUE);
833
 
}
834
 
 
835
 
static void
836
 
handle_song_listing (RBDAAPConnection *connection,
837
 
                     guint             status,
838
 
                     GNode            *structure)
839
 
{
840
 
        RBDAAPConnectionPrivate *priv = connection->priv;
841
 
        RBDAAPItem *item = NULL;
842
 
        GNode *listing_node;
843
 
        gint returned_count;
844
 
        gint i;
845
 
        GNode *n;
846
 
        gint specified_total_count;
847
 
        gboolean update_type;
848
 
        gint commit_batch;
849
 
 
850
 
        /* get the songs */
851
 
 
852
 
        if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
853
 
                rb_daap_connection_state_done (connection, FALSE);
854
 
                return;
855
 
        }
856
 
 
857
 
        item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MRCO);
858
 
        if (item == NULL) {
859
 
                rb_debug ("Could not find dmap.returnedcount item in /databases/%d/items",
860
 
                          priv->database_id);
861
 
                rb_daap_connection_state_done (connection, FALSE);
862
 
                return;
863
 
        }
864
 
        returned_count = g_value_get_int (&(item->content));
865
 
        if (returned_count > 20) {
866
 
                commit_batch = returned_count / 20;
867
 
        } else {
868
 
                commit_batch = 1;
869
 
        }
870
 
 
871
 
        item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MTCO);
872
 
        if (item == NULL) {
873
 
                rb_debug ("Could not find dmap.specifiedtotalcount item in /databases/%d/items",
874
 
                          priv->database_id);
875
 
                rb_daap_connection_state_done (connection, FALSE);
876
 
                return;
877
 
        }
878
 
        specified_total_count = g_value_get_int (&(item->content));
879
 
 
880
 
        item = rb_daap_structure_find_item (structure, RB_DAAP_CC_MUTY);
881
 
        if (item == NULL) {
882
 
                rb_debug ("Could not find dmap.updatetype item in /databases/%d/items",
883
 
                          priv->database_id);
884
 
                rb_daap_connection_state_done (connection, FALSE);
885
 
                return;
886
 
        }
887
 
        update_type = g_value_get_char (&(item->content));
888
 
 
889
 
        listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
890
 
        if (listing_node == NULL) {
891
 
                rb_debug ("Could not find dmap.listing item in /databases/%d/items",
892
 
                          priv->database_id);
893
 
                rb_daap_connection_state_done (connection, FALSE);
894
 
                return;
895
 
        }
896
 
 
897
 
        priv->item_id_to_uri = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rb_refstring_unref);
898
 
 
899
 
        rb_profile_start ("handling song listing");
900
 
        priv->progress = 0.0f;
901
 
        if (priv->emit_progress_id != 0) {
902
 
                g_source_remove (priv->emit_progress_id);
903
 
        }
904
 
        connection->priv->emit_progress_id = g_idle_add ((GSourceFunc) emit_progress_idle, connection);
905
 
 
906
 
        for (i = 0, n = listing_node->children; n; i++, n = n->next) {
907
 
                GNode *n2;
908
 
                RhythmDBEntry *entry = NULL;
909
 
                GValue value = {0,};
910
 
                gchar *uri = NULL;
911
 
                gint item_id = 0;
912
 
                const gchar *title = NULL;
913
 
                const gchar *album = NULL;
914
 
                const gchar *artist = NULL;
915
 
                const gchar *format = NULL;
916
 
                const gchar *genre = NULL;
917
 
                gint length = 0;
918
 
                gint track_number = 0;
919
 
                gint disc_number = 0;
920
 
                gint year = 0;
921
 
                gint size = 0;
922
 
                gint bitrate = 0;
923
 
 
924
 
                for (n2 = n->children; n2; n2 = n2->next) {
925
 
                        RBDAAPItem *meta_item;
926
 
 
927
 
                        meta_item = n2->data;
928
 
 
929
 
                        switch (meta_item->content_code) {
930
 
                                case RB_DAAP_CC_MIID:
931
 
                                        item_id = g_value_get_int (&(meta_item->content));
932
 
                                        break;
933
 
                                case RB_DAAP_CC_MINM:
934
 
                                        title = g_value_get_string (&(meta_item->content));
935
 
                                        break;
936
 
                                case RB_DAAP_CC_ASAL:
937
 
                                        album = g_value_get_string (&(meta_item->content));
938
 
                                        break;
939
 
                                case RB_DAAP_CC_ASAR:
940
 
                                        artist = g_value_get_string (&(meta_item->content));
941
 
                                        break;
942
 
                                case RB_DAAP_CC_ASFM:
943
 
                                        format = g_value_get_string (&(meta_item->content));
944
 
                                        break;
945
 
                                case RB_DAAP_CC_ASGN:
946
 
                                        genre = g_value_get_string (&(meta_item->content));
947
 
                                        break;
948
 
                                case RB_DAAP_CC_ASTM:
949
 
                                        length = g_value_get_int (&(meta_item->content));
950
 
                                        break;
951
 
                                case RB_DAAP_CC_ASTN:
952
 
                                        track_number = g_value_get_int (&(meta_item->content));
953
 
                                        break;
954
 
                                case RB_DAAP_CC_ASDN:
955
 
                                        disc_number = g_value_get_int (&(meta_item->content));
956
 
                                        break;
957
 
                                case RB_DAAP_CC_ASYR:
958
 
                                        year = g_value_get_int (&(meta_item->content));
959
 
                                        break;
960
 
                                case RB_DAAP_CC_ASSZ:
961
 
                                        size = g_value_get_int (&(meta_item->content));
962
 
                                        break;
963
 
                                case RB_DAAP_CC_ASBR:
964
 
                                        bitrate = g_value_get_int (&(meta_item->content));
965
 
                                        break;
966
 
                                default:
967
 
                                        break;
968
 
                        }
969
 
                }
970
 
 
971
 
                /*if (connection->daap_version == 3.0) {*/
972
 
                        uri = g_strdup_printf ("%s/databases/%d/items/%d.%s?session-id=%u",
973
 
                                               priv->daap_base_uri,
974
 
                                               priv->database_id,
975
 
                                               item_id, format,
976
 
                                               priv->session_id);
977
 
                /*} else {*/
978
 
                /* uri should be
979
 
                 * "/databases/%d/items/%d.%s?session-id=%u&revision-id=%d";
980
 
                 * but its not going to work cause the other parts of the code
981
 
                 * depend on the uri to have the ip address so that the
982
 
                 * RBDAAPSource can be found to ++request_id
983
 
                 * maybe just /dont/ support older itunes.  doesn't seem
984
 
                 * unreasonable to me, honestly
985
 
                 */
986
 
                /*}*/
987
 
                entry = rhythmdb_entry_new (priv->db, priv->db_type, uri);
988
 
                if (entry == NULL) {
989
 
                        rb_debug ("cannot create entry for daap track %s", uri);
990
 
                        continue;
991
 
                }
992
 
                g_hash_table_insert (priv->item_id_to_uri, GINT_TO_POINTER (item_id), rb_refstring_new (uri));
993
 
                g_free (uri);
994
 
 
995
 
                /* year */
996
 
                if (year != 0) {
997
 
                        GDate *date;
998
 
                        gulong julian;
999
 
 
1000
 
                        /* create dummy date with given year */
1001
 
                        date = g_date_new_dmy (1, G_DATE_JANUARY, year);
1002
 
                        julian = g_date_get_julian (date);
1003
 
                        g_date_free (date);
1004
 
 
1005
 
                        g_value_init (&value, G_TYPE_ULONG);
1006
 
                        g_value_set_ulong (&value,julian);
1007
 
                        rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_DATE, &value);
1008
 
                        g_value_unset (&value);
1009
 
                }
1010
 
 
1011
 
                /* track number */
1012
 
                g_value_init (&value, G_TYPE_ULONG);
1013
 
                g_value_set_ulong (&value,(gulong)track_number);
1014
 
                rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_TRACK_NUMBER, &value);
1015
 
                g_value_unset (&value);
1016
 
 
1017
 
                /* disc number */
1018
 
                g_value_init (&value, G_TYPE_ULONG);
1019
 
                g_value_set_ulong (&value,(gulong)disc_number);
1020
 
                rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_DISC_NUMBER, &value);
1021
 
                g_value_unset (&value);
1022
 
 
1023
 
                /* bitrate */
1024
 
                g_value_init (&value, G_TYPE_ULONG);
1025
 
                g_value_set_ulong (&value,(gulong)bitrate);
1026
 
                rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_BITRATE, &value);
1027
 
                g_value_unset (&value);
1028
 
 
1029
 
                /* length */
1030
 
                g_value_init (&value, G_TYPE_ULONG);
1031
 
                g_value_set_ulong (&value,(gulong)length / 1000);
1032
 
                rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_DURATION, &value);
1033
 
                g_value_unset (&value);
1034
 
 
1035
 
                /* file size */
1036
 
                g_value_init (&value, G_TYPE_UINT64);
1037
 
                g_value_set_uint64(&value,(gint64)size);
1038
 
                rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_FILE_SIZE, &value);
1039
 
                g_value_unset (&value);
1040
 
 
1041
 
                /* title */
1042
 
                entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_TITLE, title);
1043
 
 
1044
 
                /* album */
1045
 
                entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_ALBUM, album);
1046
 
 
1047
 
                /* artist */
1048
 
                entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_ARTIST, artist);
1049
 
 
1050
 
                /* genre */
1051
 
                entry_set_string_prop (priv->db, entry, RHYTHMDB_PROP_GENRE, genre);
1052
 
 
1053
 
                if (i % commit_batch == 0) {
1054
 
                        connection->priv->progress = ((float)i / (float)returned_count);
1055
 
                        if (priv->emit_progress_id != 0) {
1056
 
                                g_source_remove (connection->priv->emit_progress_id);
1057
 
                        }
1058
 
                        connection->priv->emit_progress_id = g_idle_add ((GSourceFunc) emit_progress_idle, connection);
1059
 
                        rhythmdb_commit (priv->db);
1060
 
                }
1061
 
        }
1062
 
        rhythmdb_commit (priv->db);
1063
 
        rb_profile_end ("handling song listing");
1064
 
 
1065
 
        rb_daap_connection_state_done (connection, TRUE);
1066
 
}
1067
 
 
1068
 
/* FIXME
1069
 
 * what we really should do is only get a list of playlists and their ids
1070
 
 * then when they are clicked on ('activate'd) by the user, get a list of
1071
 
 * the files that are actually in them.  This will speed up initial daap
1072
 
 * connection times and reduce memory consumption.
1073
 
 */
1074
 
 
1075
 
static void
1076
 
handle_playlists (RBDAAPConnection *connection,
1077
 
                  guint             status,
1078
 
                  GNode            *structure)
1079
 
{
1080
 
        RBDAAPConnectionPrivate *priv = connection->priv;
1081
 
        GNode *listing_node;
1082
 
        gint i;
1083
 
        GNode *n;
1084
 
 
1085
 
        if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
1086
 
                rb_daap_connection_state_done (connection, FALSE);
1087
 
                return;
1088
 
        }
1089
 
 
1090
 
        listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
1091
 
        if (listing_node == NULL) {
1092
 
                rb_debug ("Could not find dmap.listing item in /databases/%d/containers",
1093
 
                          priv->database_id);
1094
 
                rb_daap_connection_state_done (connection, FALSE);
1095
 
                return;
1096
 
        }
1097
 
 
1098
 
        for (i = 0, n = listing_node->children; n; n = n->next, i++) {
1099
 
                RBDAAPItem *item;
1100
 
                gint id;
1101
 
                gchar *name;
1102
 
                RBDAAPPlaylist *playlist;
1103
 
 
1104
 
                item = rb_daap_structure_find_item (n, RB_DAAP_CC_ABPL);
1105
 
                if (item != NULL) {
1106
 
                        continue;
1107
 
                }
1108
 
 
1109
 
                item = rb_daap_structure_find_item (n, RB_DAAP_CC_MIID);
1110
 
                if (item == NULL) {
1111
 
                        rb_debug ("Could not find dmap.itemid item in /databases/%d/containers",
1112
 
                                  priv->database_id);
1113
 
                        continue;
1114
 
                }
1115
 
                id = g_value_get_int (&(item->content));
1116
 
 
1117
 
                item = rb_daap_structure_find_item (n, RB_DAAP_CC_MINM);
1118
 
                if (item == NULL) {
1119
 
                        rb_debug ("Could not find dmap.itemname item in /databases/%d/containers",
1120
 
                                  priv->database_id);
1121
 
                        continue;
1122
 
                }
1123
 
                name = g_value_dup_string (&(item->content));
1124
 
 
1125
 
                playlist = g_new0 (RBDAAPPlaylist, 1);
1126
 
                playlist->id = id;
1127
 
                playlist->name = name;
1128
 
                rb_debug ("Got playlist %p: name %s, id %d", playlist, playlist->name, playlist->id);
1129
 
 
1130
 
                priv->playlists = g_slist_prepend (priv->playlists, playlist);
1131
 
        }
1132
 
        priv->playlists = g_slist_reverse (priv->playlists);
1133
 
 
1134
 
        rb_daap_connection_state_done (connection, TRUE);
1135
 
}
1136
 
 
1137
 
static void
1138
 
handle_playlist_entries (RBDAAPConnection *connection,
1139
 
                         guint             status,
1140
 
                         GNode            *structure)
1141
 
{
1142
 
        RBDAAPConnectionPrivate *priv = connection->priv;
1143
 
        RBDAAPPlaylist *playlist;
1144
 
        GNode *listing_node;
1145
 
        GNode *node;
1146
 
        gint i;
1147
 
        GList *playlist_uris = NULL;
1148
 
 
1149
 
        if (structure == NULL || SOUP_STATUS_IS_SUCCESSFUL (status) == FALSE) {
1150
 
                rb_daap_connection_state_done (connection, FALSE);
1151
 
                return;
1152
 
        }
1153
 
 
1154
 
        playlist = (RBDAAPPlaylist *)g_slist_nth_data (priv->playlists, priv->reading_playlist);
1155
 
        g_assert (playlist);
1156
 
 
1157
 
        listing_node = rb_daap_structure_find_node (structure, RB_DAAP_CC_MLCL);
1158
 
        if (listing_node == NULL) {
1159
 
                rb_debug ("Could not find dmap.listing item in /databases/%d/containers/%d/items",
1160
 
                          priv->database_id, playlist->id);
1161
 
                rb_daap_connection_state_done (connection, FALSE);
1162
 
                return;
1163
 
        }
1164
 
 
1165
 
        rb_profile_start ("handling playlist entries");
1166
 
        for (i = 0, node = listing_node->children; node; node = node->next, i++) {
1167
 
                RBRefString *item_uri;
1168
 
                gint playlist_item_id;
1169
 
                RBDAAPItem *item;
1170
 
 
1171
 
                item = rb_daap_structure_find_item (node, RB_DAAP_CC_MIID);
1172
 
                if (item == NULL) {
1173
 
                        rb_debug ("Could not find dmap.itemid item in /databases/%d/containers/%d/items",
1174
 
                                  priv->database_id, playlist->id);
1175
 
                        continue;
1176
 
                }
1177
 
                playlist_item_id = g_value_get_int (&(item->content));
1178
 
 
1179
 
                item_uri = g_hash_table_lookup (priv->item_id_to_uri, GINT_TO_POINTER (playlist_item_id));
1180
 
                if (item_uri == NULL) {
1181
 
                        rb_debug ("Entry %d in playlist %s doesn't exist in the database\n",
1182
 
                                  playlist_item_id, playlist->name);
1183
 
                        continue;
1184
 
                }
1185
 
 
1186
 
                playlist_uris = g_list_prepend (playlist_uris, rb_refstring_ref (item_uri));
1187
 
        }
1188
 
        rb_profile_end ("handling playlist entries");
1189
 
 
1190
 
        playlist->uris = playlist_uris;
1191
 
        rb_daap_connection_state_done (connection, TRUE);
1192
 
}
1193
 
 
1194
 
static void
1195
 
handle_logout (RBDAAPConnection *connection,
1196
 
               guint             status,
1197
 
               GNode            *structure)
1198
 
{
1199
 
        connection_disconnected (connection);
1200
 
 
1201
 
        /* is there any point handling errors here? */
1202
 
        rb_daap_connection_state_done (connection, TRUE);
1203
 
}
1204
 
 
1205
 
RBDAAPConnection *
1206
 
rb_daap_connection_new (const char       *name,
1207
 
                        const char       *host,
1208
 
                        int               port,
1209
 
                        gboolean          password_protected,
1210
 
                        RhythmDB         *db,
1211
 
                        RhythmDBEntryType type)
1212
 
{
1213
 
        return g_object_new (RB_TYPE_DAAP_CONNECTION,
1214
 
                             "name", name,
1215
 
                             "entry-type", type,
1216
 
                             "password-protected", password_protected,
1217
 
                             "db", db,
1218
 
                             "host", host,
1219
 
                             "port", port,
1220
 
                             NULL);
1221
 
}
1222
 
 
1223
 
gboolean
1224
 
rb_daap_connection_is_connected (RBDAAPConnection *connection)
1225
 
{
1226
 
        g_return_val_if_fail (RB_IS_DAAP_CONNECTION (connection), FALSE);
1227
 
 
1228
 
        return connection->priv->is_connected;
1229
 
}
1230
 
 
1231
 
typedef struct {
1232
 
        RBDAAPConnection        *connection;
1233
 
        RBDAAPConnectionCallback callback;
1234
 
        gpointer                 data;
1235
 
        GDestroyNotify           destroy;
1236
 
} ConnectionResponseData;
1237
 
 
1238
 
static void
1239
 
connection_response_data_free (gpointer data)
1240
 
{
1241
 
        ConnectionResponseData *rdata = data;
1242
 
 
1243
 
        g_object_unref (rdata->connection);
1244
 
        g_free (rdata);
1245
 
}
1246
 
 
1247
 
static void
1248
 
connected_cb (RBDAAPConnection       *connection,
1249
 
              ConnectionResponseData *rdata)
1250
 
{
1251
 
        gboolean result;
1252
 
 
1253
 
        rb_debug ("Connected callback");
1254
 
 
1255
 
        connection->priv->is_connecting = FALSE;
1256
 
 
1257
 
        g_signal_handlers_disconnect_by_func (connection,
1258
 
                                              G_CALLBACK (connected_cb),
1259
 
                                              rdata);
1260
 
 
1261
 
        /* if connected then we succeeded */
1262
 
        result = rb_daap_connection_is_connected (connection);
1263
 
 
1264
 
        if (rdata->callback) {
1265
 
                rdata->callback (rdata->connection,
1266
 
                                 result,
1267
 
                                 rdata->connection->priv->last_error_message,
1268
 
                                 rdata->data);
1269
 
        }
1270
 
 
1271
 
        if (rdata->destroy) {
1272
 
                rdata->destroy (rdata);
1273
 
        }
1274
 
}
1275
 
 
1276
 
void
1277
 
rb_daap_connection_connect (RBDAAPConnection        *connection,
1278
 
                            RBDAAPConnectionCallback callback,
1279
 
                            gpointer                 user_data)
1280
 
{
1281
 
        ConnectionResponseData *rdata;
1282
 
        char                   *path;
1283
 
 
1284
 
        g_return_if_fail (RB_IS_DAAP_CONNECTION (connection));
1285
 
        g_return_if_fail (connection->priv->state == DAAP_GET_INFO);
1286
 
 
1287
 
        rb_debug ("Creating new DAAP connection to %s:%d", connection->priv->host, connection->priv->port);
1288
 
 
1289
 
        connection->priv->session = soup_session_async_new ();
1290
 
 
1291
 
        path = g_strdup_printf ("http://%s:%d", connection->priv->host, connection->priv->port);
1292
 
        connection->priv->base_uri = soup_uri_new (path);
1293
 
        g_free (path);
1294
 
 
1295
 
        if (connection->priv->base_uri == NULL) {
1296
 
                rb_debug ("Error parsing http://%s:%d", connection->priv->host, connection->priv->port);
1297
 
                /* FIXME: do callback */
1298
 
                return;
1299
 
        }
1300
 
 
1301
 
        connection->priv->daap_base_uri = g_strdup_printf ("daap://%s:%d", connection->priv->host, connection->priv->port);
1302
 
 
1303
 
        rdata = g_new (ConnectionResponseData, 1);
1304
 
        rdata->connection = g_object_ref (connection);
1305
 
        rdata->callback = callback;
1306
 
        rdata->data = user_data;
1307
 
        rdata->destroy = connection_response_data_free;
1308
 
        g_signal_connect (connection, "operation-done", G_CALLBACK (connected_cb), rdata);
1309
 
 
1310
 
        if (connection->priv->do_something_id != 0) {
1311
 
                g_source_remove (connection->priv->do_something_id);
1312
 
        }
1313
 
 
1314
 
        connection->priv->is_connecting = TRUE;
1315
 
        connection->priv->do_something_id = g_idle_add ((GSourceFunc) rb_daap_connection_do_something, connection);
1316
 
}
1317
 
 
1318
 
static void
1319
 
disconnected_cb (RBDAAPConnection       *connection,
1320
 
                 ConnectionResponseData *rdata)
1321
 
{
1322
 
        gboolean result;
1323
 
 
1324
 
        rb_debug ("Disconnected callback");
1325
 
 
1326
 
        g_signal_handlers_disconnect_by_func (connection,
1327
 
                                              G_CALLBACK (disconnected_cb),
1328
 
                                              rdata);
1329
 
 
1330
 
        /* if not connected then we succeeded */
1331
 
        result = ! rb_daap_connection_is_connected (connection);
1332
 
 
1333
 
        if (rdata->callback) {
1334
 
                rdata->callback (rdata->connection,
1335
 
                                 result,
1336
 
                                 rdata->connection->priv->last_error_message,
1337
 
                                 rdata->data);
1338
 
        }
1339
 
 
1340
 
        if (rdata->destroy) {
1341
 
                rdata->destroy (rdata);
1342
 
        }
1343
 
}
1344
 
 
1345
 
static void
1346
 
rb_daap_connection_finish (RBDAAPConnection *connection)
1347
 
{
1348
 
        g_return_if_fail (RB_IS_DAAP_CONNECTION (connection));
1349
 
 
1350
 
        rb_debug ("DAAP finish");
1351
 
        connection->priv->state = DAAP_DONE;
1352
 
        connection->priv->progress = 1.0f;
1353
 
 
1354
 
        connection_operation_done (connection);
1355
 
}
1356
 
 
1357
 
void
1358
 
rb_daap_connection_disconnect (RBDAAPConnection        *connection,
1359
 
                               RBDAAPConnectionCallback callback,
1360
 
                               gpointer                 user_data)
1361
 
{
1362
 
        RBDAAPConnectionPrivate *priv = connection->priv;
1363
 
        ConnectionResponseData  *rdata;
1364
 
 
1365
 
        g_return_if_fail (RB_IS_DAAP_CONNECTION (connection));
1366
 
 
1367
 
        rb_debug ("Disconnecting");
1368
 
 
1369
 
        if (connection->priv->is_connecting) {
1370
 
                /* this is a special case where the async connection
1371
 
                   hasn't returned yet so we need to force the connection
1372
 
                   to finish */
1373
 
                priv->state = DAAP_DONE;
1374
 
                GDK_THREADS_LEAVE ();
1375
 
                rb_daap_connection_finish (connection);
1376
 
                GDK_THREADS_ENTER ();
1377
 
        }
1378
 
 
1379
 
        rdata = g_new (ConnectionResponseData, 1);
1380
 
        rdata->connection = g_object_ref (connection);
1381
 
        rdata->callback = callback;
1382
 
        rdata->data = user_data;
1383
 
        rdata->destroy = connection_response_data_free;
1384
 
 
1385
 
        g_signal_connect (connection, "operation-done", G_CALLBACK (disconnected_cb), rdata);
1386
 
 
1387
 
        if (priv->do_something_id != 0) {
1388
 
                g_source_remove (priv->do_something_id);
1389
 
        }
1390
 
 
1391
 
        if (! connection->priv->is_connected) {
1392
 
                priv->state = DAAP_DONE;
1393
 
                GDK_THREADS_LEAVE ();
1394
 
                rb_daap_connection_finish (connection);
1395
 
                GDK_THREADS_ENTER ();
1396
 
        } else {
1397
 
                priv->state = DAAP_LOGOUT;
1398
 
 
1399
 
                priv->do_something_id = g_idle_add ((GSourceFunc) rb_daap_connection_do_something, connection);
1400
 
        }
1401
 
}
1402
 
 
1403
 
static void
1404
 
rb_daap_connection_state_done (RBDAAPConnection *connection,
1405
 
                               gboolean          result)
1406
 
{
1407
 
        RBDAAPConnectionPrivate *priv = connection->priv;
1408
 
 
1409
 
        rb_debug ("Transitioning to next state from %d", priv->state);
1410
 
 
1411
 
        if (result == FALSE) {
1412
 
                priv->state = DAAP_DONE;
1413
 
                priv->result = FALSE;
1414
 
        } else {
1415
 
                switch (priv->state) {
1416
 
                case DAAP_GET_PLAYLISTS:
1417
 
                        if (priv->playlists == NULL)
1418
 
                                priv->state = DAAP_DONE;
1419
 
                        else
1420
 
                                priv->state = DAAP_GET_PLAYLIST_ENTRIES;
1421
 
                        break;
1422
 
                case DAAP_GET_PLAYLIST_ENTRIES:
1423
 
                        /* keep reading playlists until we've got them all */
1424
 
                        if (++priv->reading_playlist >= g_slist_length (priv->playlists))
1425
 
                                priv->state = DAAP_DONE;
1426
 
                        break;
1427
 
 
1428
 
                case DAAP_LOGOUT:
1429
 
                        priv->state = DAAP_DONE;
1430
 
                        break;
1431
 
 
1432
 
                case DAAP_DONE:
1433
 
                        /* uhh.. */
1434
 
                        rb_debug ("This should never happen.");
1435
 
                        break;
1436
 
 
1437
 
                default:
1438
 
                        /* in most states, we just move on to the next */
1439
 
                        if (priv->state > DAAP_DONE) {
1440
 
                                rb_debug ("This should REALLY never happen.");
1441
 
                                return;
1442
 
                        }
1443
 
                        priv->state++;
1444
 
                        break;
1445
 
                }
1446
 
 
1447
 
                priv->progress = 1.0f;
1448
 
                if (connection->priv->emit_progress_id != 0) {
1449
 
                        g_source_remove (connection->priv->emit_progress_id);
1450
 
                }
1451
 
                connection->priv->emit_progress_id = g_idle_add ((GSourceFunc) emit_progress_idle, connection);
1452
 
        }
1453
 
 
1454
 
        if (priv->do_something_id != 0) {
1455
 
                g_source_remove (priv->do_something_id);
1456
 
        }
1457
 
        priv->do_something_id = g_idle_add ((GSourceFunc) rb_daap_connection_do_something, connection);
1458
 
}
1459
 
 
1460
 
static gboolean
1461
 
rb_daap_connection_do_something (RBDAAPConnection *connection)
1462
 
{
1463
 
        RBDAAPConnectionPrivate *priv = connection->priv;
1464
 
        char *path;
1465
 
 
1466
 
        rb_debug ("Doing something for state: %d", priv->state);
1467
 
 
1468
 
        priv->do_something_id = 0;
1469
 
 
1470
 
        switch (priv->state) {
1471
 
        case DAAP_GET_INFO:
1472
 
                rb_debug ("Getting DAAP server info");
1473
 
                if (! http_get (connection, "/server-info", FALSE, 0.0, 0, FALSE,
1474
 
                                (RBDAAPResponseHandler) handle_server_info, FALSE)) {
1475
 
                        rb_debug ("Could not get DAAP connection info");
1476
 
                        rb_daap_connection_state_done (connection, FALSE);
1477
 
                }
1478
 
                break;
1479
 
 
1480
 
        case DAAP_GET_PASSWORD:
1481
 
                if (priv->password_protected) {
1482
 
                        /* FIXME this bit is still synchronous */
1483
 
                        rb_debug ("Need a password for %s", priv->name);
1484
 
                        g_free (priv->password);
1485
 
                        priv->password = connection_get_password (connection);
1486
 
 
1487
 
                        if (priv->password == NULL || priv->password[0] == '\0') {
1488
 
                                rb_debug ("Password entry cancelled");
1489
 
                                priv->result = FALSE;
1490
 
                                priv->state = DAAP_DONE;
1491
 
                                rb_daap_connection_do_something (connection);
1492
 
                                return FALSE;
1493
 
                        }
1494
 
 
1495
 
                        /* If the share went away while we were asking for the password,
1496
 
                         * don't bother trying to log in.
1497
 
                         */
1498
 
                        if (priv->state != DAAP_GET_PASSWORD) {
1499
 
                                return FALSE;
1500
 
                        }
1501
 
                }
1502
 
 
1503
 
                /* otherwise, fall through */
1504
 
                priv->state = DAAP_LOGIN;
1505
 
 
1506
 
        case DAAP_LOGIN:
1507
 
                rb_debug ("Logging into DAAP server");
1508
 
                if (! http_get (connection, "/login", FALSE, 0.0, 0, FALSE,
1509
 
                               (RBDAAPResponseHandler) handle_login, FALSE)) {
1510
 
                        rb_debug ("Could not login to DAAP server");
1511
 
                        /* FIXME: set state back to GET_PASSWORD to try again */
1512
 
                        rb_daap_connection_state_done (connection, FALSE);
1513
 
                }
1514
 
 
1515
 
                break;
1516
 
 
1517
 
        case DAAP_GET_REVISION_NUMBER:
1518
 
                rb_debug ("Getting DAAP server database revision number");
1519
 
                path = g_strdup_printf ("/update?session-id=%u&revision-number=1", priv->session_id);
1520
 
                if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1521
 
                               (RBDAAPResponseHandler) handle_update, FALSE)) {
1522
 
                        rb_debug ("Could not get server database revision number");
1523
 
                        rb_daap_connection_state_done (connection, FALSE);
1524
 
                }
1525
 
                g_free (path);
1526
 
                break;
1527
 
 
1528
 
        case DAAP_GET_DB_INFO:
1529
 
                rb_debug ("Getting DAAP database info");
1530
 
                path = g_strdup_printf ("/databases?session-id=%u&revision-number=%d",
1531
 
                                        priv->session_id, priv->revision_number);
1532
 
                if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1533
 
                               (RBDAAPResponseHandler) handle_database_info, FALSE)) {
1534
 
                        rb_debug ("Could not get DAAP database info");
1535
 
                        rb_daap_connection_state_done (connection, FALSE);
1536
 
                }
1537
 
                g_free (path);
1538
 
                break;
1539
 
 
1540
 
        case DAAP_GET_SONGS:
1541
 
                rb_debug ("Getting DAAP song listing");
1542
 
                path = g_strdup_printf ("/databases/%i/items?session-id=%u&revision-number=%i"
1543
 
                                        "&meta=dmap.itemid,dmap.itemname,daap.songalbum,"
1544
 
                                        "daap.songartist,daap.daap.songgenre,daap.songsize,"
1545
 
                                        "daap.songtime,daap.songtrackcount,daap.songtracknumber,"
1546
 
                                        "daap.songyear,daap.songformat,daap.songgenre,"
1547
 
                                        "daap.songbitrate,daap.songdiscnumber",
1548
 
                                        priv->database_id,
1549
 
                                        priv->session_id,
1550
 
                                        priv->revision_number);
1551
 
                if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1552
 
                               (RBDAAPResponseHandler) handle_song_listing, TRUE)) {
1553
 
                        rb_debug ("Could not get DAAP song listing");
1554
 
                        rb_daap_connection_state_done (connection, FALSE);
1555
 
                }
1556
 
                g_free (path);
1557
 
                break;
1558
 
 
1559
 
        case DAAP_GET_PLAYLISTS:
1560
 
                rb_debug ("Getting DAAP playlists");
1561
 
                path = g_strdup_printf ("/databases/%d/containers?session-id=%u&revision-number=%d",
1562
 
                                        priv->database_id,
1563
 
                                        priv->session_id,
1564
 
                                        priv->revision_number);
1565
 
                if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1566
 
                               (RBDAAPResponseHandler) handle_playlists, TRUE)) {
1567
 
                        rb_debug ("Could not get DAAP playlists");
1568
 
                        rb_daap_connection_state_done (connection, FALSE);
1569
 
                }
1570
 
                g_free (path);
1571
 
                break;
1572
 
 
1573
 
        case DAAP_GET_PLAYLIST_ENTRIES:
1574
 
                {
1575
 
                        RBDAAPPlaylist *playlist =
1576
 
                                (RBDAAPPlaylist *) g_slist_nth_data (priv->playlists,
1577
 
                                                                     priv->reading_playlist);
1578
 
                        g_assert (playlist);
1579
 
                        rb_debug ("Reading DAAP playlist %d entries", priv->reading_playlist);
1580
 
                        path = g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%u&revision-number=%d&meta=dmap.itemid",
1581
 
                                                priv->database_id,
1582
 
                                                playlist->id,
1583
 
                                                priv->session_id, priv->revision_number);
1584
 
                        if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1585
 
                                       (RBDAAPResponseHandler) handle_playlist_entries, TRUE)) {
1586
 
                                rb_debug ("Could not get entries for DAAP playlist %d",
1587
 
                                          priv->reading_playlist);
1588
 
                                rb_daap_connection_state_done (connection, FALSE);
1589
 
                        }
1590
 
                        g_free (path);
1591
 
                }
1592
 
                break;
1593
 
 
1594
 
        case DAAP_LOGOUT:
1595
 
                rb_debug ("Logging out of DAAP server");
1596
 
                path = g_strdup_printf ("/logout?session-id=%u", priv->session_id);
1597
 
                if (! http_get (connection, path, TRUE, priv->daap_version, 0, FALSE,
1598
 
                               (RBDAAPResponseHandler) handle_logout, FALSE)) {
1599
 
                        rb_debug ("Could not log out of DAAP server");
1600
 
                        rb_daap_connection_state_done (connection, FALSE);
1601
 
                }
1602
 
 
1603
 
                g_free (path);
1604
 
                break;
1605
 
 
1606
 
        case DAAP_DONE:
1607
 
                rb_debug ("DAAP done");
1608
 
 
1609
 
                rb_daap_connection_finish (connection);
1610
 
 
1611
 
                break;
1612
 
        }
1613
 
 
1614
 
        return FALSE;
1615
 
}
1616
 
 
1617
 
char *
1618
 
rb_daap_connection_get_headers (RBDAAPConnection *connection,
1619
 
                                const gchar *uri,
1620
 
                                gint64 bytes)
1621
 
{
1622
 
        RBDAAPConnectionPrivate *priv = connection->priv;
1623
 
        GString *headers;
1624
 
        char hash[33] = {0};
1625
 
        char *norb_daap_uri = (char *)uri;
1626
 
        char *s;
1627
 
 
1628
 
        priv->request_id++;
1629
 
 
1630
 
        if (g_strncasecmp (uri, "daap://", 7) == 0) {
1631
 
                norb_daap_uri = strstr (uri, "/data");
1632
 
        }
1633
 
 
1634
 
        rb_daap_hash_generate ((short)floorf (priv->daap_version),
1635
 
                               (const guchar*)norb_daap_uri, 2,
1636
 
                               (guchar*)hash,
1637
 
                               priv->request_id);
1638
 
 
1639
 
        headers = g_string_new ("Accept: */*\r\n"
1640
 
                                "Cache-Control: no-cache\r\n"
1641
 
                                "User-Agent: " RB_DAAP_USER_AGENT "\r\n"
1642
 
                                "Accept-Language: en-us, en;q=5.0\r\n"
1643
 
                                "Client-DAAP-Access-Index: 2\r\n"
1644
 
                                "Client-DAAP-Version: 3.0\r\n");
1645
 
        g_string_append_printf (headers,
1646
 
                                "Client-DAAP-Validation: %s\r\n"
1647
 
                                "Client-DAAP-Request-ID: %d\r\n"
1648
 
                                "Connection: close\r\n",
1649
 
                                hash, priv->request_id);
1650
 
 
1651
 
        if (priv->password_protected) {
1652
 
                char *user_pass;
1653
 
                char *token;
1654
 
 
1655
 
                user_pass = g_strdup_printf ("%s:%s", priv->username, priv->password);
1656
 
                token = soup_base64_encode (user_pass, strlen (user_pass));
1657
 
                g_string_append_printf (headers, "Authentication: Basic %s\r\n", token);
1658
 
                g_free (token);
1659
 
                g_free (user_pass);
1660
 
        }
1661
 
 
1662
 
        if (bytes != 0) {
1663
 
                g_string_append_printf (headers,"Range: bytes=%"G_GINT64_FORMAT"-\r\n", bytes);
1664
 
        }
1665
 
 
1666
 
        s = headers->str;
1667
 
        g_string_free (headers, FALSE);
1668
 
 
1669
 
        return s;
1670
 
}
1671
 
 
1672
 
GSList *
1673
 
rb_daap_connection_get_playlists (RBDAAPConnection *connection)
1674
 
{
1675
 
        return connection->priv->playlists;
1676
 
}
1677
 
 
1678
 
static void
1679
 
rb_daap_connection_dispose (GObject *object)
1680
 
{
1681
 
        RBDAAPConnectionPrivate *priv = RB_DAAP_CONNECTION (object)->priv;
1682
 
        GSList *l;
1683
 
 
1684
 
        rb_debug ("DAAP connection dispose");
1685
 
 
1686
 
        if (priv->emit_progress_id != 0) {
1687
 
                g_source_remove (priv->emit_progress_id);
1688
 
                priv->emit_progress_id = 0;
1689
 
        }
1690
 
 
1691
 
        if (priv->do_something_id != 0) {
1692
 
                g_source_remove (priv->do_something_id);
1693
 
                priv->do_something_id = 0;
1694
 
        }
1695
 
 
1696
 
        if (priv->name) {
1697
 
                g_free (priv->name);
1698
 
                priv->name = NULL;
1699
 
        }
1700
 
 
1701
 
        if (priv->username) {
1702
 
                g_free (priv->username);
1703
 
                priv->username = NULL;
1704
 
        }
1705
 
 
1706
 
        if (priv->password) {
1707
 
                g_free (priv->password);
1708
 
                priv->password = NULL;
1709
 
        }
1710
 
 
1711
 
        if (priv->host) {
1712
 
                g_free (priv->host);
1713
 
                priv->host = NULL;
1714
 
        }
1715
 
 
1716
 
        if (priv->playlists) {
1717
 
                for (l = priv->playlists; l; l = l->next) {
1718
 
                        RBDAAPPlaylist *playlist = l->data;
1719
 
 
1720
 
                        g_list_foreach (playlist->uris, (GFunc)rb_refstring_unref, NULL);
1721
 
                        g_list_free (playlist->uris);
1722
 
                        g_free (playlist->name);
1723
 
                        g_free (playlist);
1724
 
                        l->data = NULL;
1725
 
                }
1726
 
                g_slist_free (priv->playlists);
1727
 
                priv->playlists = NULL;
1728
 
        }
1729
 
 
1730
 
        if (priv->item_id_to_uri) {
1731
 
                g_hash_table_destroy (priv->item_id_to_uri);
1732
 
                priv->item_id_to_uri = NULL;
1733
 
        }
1734
 
 
1735
 
        if (priv->session) {
1736
 
                rb_debug ("Aborting all pending requests");
1737
 
                soup_session_abort (priv->session);
1738
 
                g_object_unref (G_OBJECT (priv->session));
1739
 
                priv->session = NULL;
1740
 
        }
1741
 
 
1742
 
        if (priv->base_uri) {
1743
 
                soup_uri_free (priv->base_uri);
1744
 
                priv->base_uri = NULL;
1745
 
        }
1746
 
 
1747
 
        if (priv->daap_base_uri) {
1748
 
                g_free (priv->daap_base_uri);
1749
 
                priv->daap_base_uri = NULL;
1750
 
        }
1751
 
 
1752
 
        if (priv->db) {
1753
 
                g_object_unref (G_OBJECT (priv->db));
1754
 
                priv->db = NULL;
1755
 
        }
1756
 
 
1757
 
        if (priv->last_error_message != NULL) {
1758
 
                g_free (priv->last_error_message);
1759
 
                priv->last_error_message = NULL;
1760
 
        }
1761
 
 
1762
 
        G_OBJECT_CLASS (rb_daap_connection_parent_class)->dispose (object);
1763
 
}
1764
 
 
1765
 
static void
1766
 
rb_daap_connection_set_property (GObject *object,
1767
 
                                 guint prop_id,
1768
 
                                 const GValue *value,
1769
 
                                 GParamSpec *pspec)
1770
 
{
1771
 
        RBDAAPConnectionPrivate *priv = RB_DAAP_CONNECTION (object)->priv;
1772
 
 
1773
 
        switch (prop_id) {
1774
 
        case PROP_NAME:
1775
 
                g_free (priv->name);
1776
 
                priv->name = g_value_dup_string (value);
1777
 
                break;
1778
 
        case PROP_DB:
1779
 
                if (priv->db != NULL) {
1780
 
                        g_object_unref (priv->db);
1781
 
                }
1782
 
                priv->db = RHYTHMDB (g_value_dup_object (value));
1783
 
                break;
1784
 
        case PROP_PASSWORD_PROTECTED:
1785
 
                priv->password_protected = g_value_get_boolean (value);
1786
 
                break;
1787
 
        case PROP_ENTRY_TYPE:
1788
 
                priv->db_type = g_value_get_boxed (value);
1789
 
                break;
1790
 
        case PROP_HOST:
1791
 
                g_free (priv->host);
1792
 
                priv->host = g_value_dup_string (value);
1793
 
                break;
1794
 
        case PROP_PORT:
1795
 
                priv->port = g_value_get_uint (value);
1796
 
                break;
1797
 
        default:
1798
 
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1799
 
                break;
1800
 
        }
1801
 
}
1802
 
 
1803
 
static void
1804
 
rb_daap_connection_get_property (GObject *object,
1805
 
                                 guint prop_id,
1806
 
                                 GValue *value,
1807
 
                                 GParamSpec *pspec)
1808
 
{
1809
 
        RBDAAPConnectionPrivate *priv = RB_DAAP_CONNECTION (object)->priv;
1810
 
 
1811
 
        switch (prop_id) {
1812
 
        case PROP_DB:
1813
 
                g_value_set_object (value, priv->db);
1814
 
                break;
1815
 
        case PROP_NAME:
1816
 
                g_value_set_string (value, priv->name);
1817
 
                break;
1818
 
        case PROP_ENTRY_TYPE:
1819
 
                g_value_set_boxed (value, priv->db_type);
1820
 
                break;
1821
 
        case PROP_PASSWORD_PROTECTED:
1822
 
                g_value_set_boolean (value, priv->password_protected);
1823
 
                break;
1824
 
        case PROP_HOST:
1825
 
                g_value_set_string (value, priv->host);
1826
 
                break;
1827
 
        case PROP_PORT:
1828
 
                g_value_set_uint (value, priv->port);
1829
 
                break;
1830
 
        default:
1831
 
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1832
 
                break;
1833
 
        }
1834
 
}