1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Implementation of DAAP (iTunes Music Sharing) hashing, parsing, connection
5 * Copyright (C) 2004-2005 Charles Schmidt <cschmidt2@emich.edu>
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.
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.
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.
27 #include <sys/types.h>
33 #include <glib/gi18n.h>
36 #include <libsoup/soup.h>
37 #include <libsoup/soup-connection.h>
38 #include <libsoup/soup-session-sync.h>
39 #include <libsoup/soup-uri.h>
41 #include "rb-daap-hash.h"
42 #include "rb-daap-connection.h"
43 #include "rb-daap-structure.h"
44 #include "rb-marshal.h"
49 #define RB_DAAP_USER_AGENT "iTunes/4.6 (Windows; N)"
51 static void rb_daap_connection_dispose (GObject
*obj
);
52 static void rb_daap_connection_set_property (GObject
*object
,
56 static void rb_daap_connection_get_property (GObject
*object
,
61 static gboolean
rb_daap_connection_do_something (RBDAAPConnection
*connection
);
62 static void rb_daap_connection_state_done (RBDAAPConnection
*connection
,
65 static gboolean
emit_progress_idle (RBDAAPConnection
*connection
);
67 G_DEFINE_TYPE (RBDAAPConnection
, rb_daap_connection
, G_TYPE_OBJECT
)
69 #define RB_DAAP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_DAAP_CONNECTION, RBDAAPConnectionPrivate))
71 typedef void (* RBDAAPResponseHandler
) (RBDAAPConnection
*connection
,
75 struct RBDAAPConnectionPrivate
{
77 gboolean password_protected
;
83 gboolean is_connected
;
84 gboolean is_connecting
;
97 guint reading_playlist
;
99 GHashTable
*item_id_to_uri
;
102 RhythmDBEntryType db_type
;
104 RBDAAPConnectionState state
;
105 RBDAAPResponseHandler response_handler
;
106 gboolean use_response_handler_thread
;
109 guint emit_progress_id
;
110 guint do_something_id
;
113 char *last_error_message
;
121 PROP_PASSWORD_PROTECTED
,
135 static guint signals
[LAST_SIGNAL
] = { 0, };
138 rb_daap_connection_finalize (GObject
*object
)
140 RBDAAPConnection
*connection
;
142 g_return_if_fail (object
!= NULL
);
143 g_return_if_fail (RB_IS_DAAP_CONNECTION (object
));
145 connection
= RB_DAAP_CONNECTION (object
);
147 g_return_if_fail (connection
->priv
!= NULL
);
149 rb_debug ("Finalize");
151 G_OBJECT_CLASS (rb_daap_connection_parent_class
)->finalize (object
);
155 rb_daap_connection_class_init (RBDAAPConnectionClass
*klass
)
157 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
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
;
164 g_type_class_add_private (klass
, sizeof (RBDAAPConnectionPrivate
));
166 g_object_class_install_property (object_class
,
168 g_param_spec_object ("db",
172 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
173 g_object_class_install_property (object_class
,
175 g_param_spec_boxed ("entry-type",
178 RHYTHMDB_TYPE_ENTRY_TYPE
,
179 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
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",
187 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
188 g_object_class_install_property (object_class
,
190 g_param_spec_string ("name",
194 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
195 g_object_class_install_property (object_class
,
197 g_param_spec_string ("host",
201 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
202 g_object_class_install_property (object_class
,
204 g_param_spec_uint ("port",
208 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
210 signals
[AUTHENTICATE
] = g_signal_new ("authenticate",
211 G_TYPE_FROM_CLASS (object_class
),
213 G_STRUCT_OFFSET (RBDAAPConnectionClass
, authenticate
),
216 rb_marshal_STRING__STRING
,
219 signals
[CONNECTING
] = g_signal_new ("connecting",
220 G_TYPE_FROM_CLASS (object_class
),
222 G_STRUCT_OFFSET (RBDAAPConnectionClass
, connecting
),
225 rb_marshal_VOID__ULONG_FLOAT
,
227 2, G_TYPE_ULONG
, G_TYPE_FLOAT
);
228 signals
[CONNECTED
] = g_signal_new ("connected",
229 G_TYPE_FROM_CLASS (object_class
),
231 G_STRUCT_OFFSET (RBDAAPConnectionClass
, connected
),
234 g_cclosure_marshal_VOID__VOID
,
237 signals
[DISCONNECTED
] = g_signal_new ("disconnected",
238 G_TYPE_FROM_CLASS (object_class
),
240 G_STRUCT_OFFSET (RBDAAPConnectionClass
, disconnected
),
243 g_cclosure_marshal_VOID__VOID
,
246 signals
[OPERATION_DONE
] = g_signal_new ("operation-done",
247 G_TYPE_FROM_CLASS (object_class
),
249 G_STRUCT_OFFSET (RBDAAPConnectionClass
, operation_done
),
252 g_cclosure_marshal_VOID__VOID
,
258 rb_daap_connection_init (RBDAAPConnection
*connection
)
260 connection
->priv
= RB_DAAP_CONNECTION_GET_PRIVATE (connection
);
262 connection
->priv
->username
= g_strdup_printf ("Rhythmbox_%s", VERSION
);
263 connection
->priv
->db_type
= RHYTHMDB_ENTRY_TYPE_INVALID
;
267 connection_get_password (RBDAAPConnection
*connection
)
269 char *password
= NULL
;;
271 GDK_THREADS_ENTER ();
272 g_signal_emit (connection
,
273 signals
[AUTHENTICATE
],
275 connection
->priv
->name
,
277 GDK_THREADS_LEAVE ();
283 connection_connected (RBDAAPConnection
*connection
)
285 rb_debug ("Emitting connected");
287 connection
->priv
->is_connected
= TRUE
;
289 GDK_THREADS_ENTER ();
290 g_signal_emit (connection
,
293 GDK_THREADS_LEAVE ();
297 connection_disconnected (RBDAAPConnection
*connection
)
299 rb_debug ("Emitting disconnected");
301 connection
->priv
->is_connected
= FALSE
;
303 GDK_THREADS_ENTER ();
304 g_signal_emit (connection
,
305 signals
[DISCONNECTED
],
307 GDK_THREADS_LEAVE ();
311 connection_operation_done (RBDAAPConnection
*connection
)
313 rb_debug ("Emitting operation done");
315 GDK_THREADS_ENTER ();
316 g_signal_emit (connection
,
317 signals
[OPERATION_DONE
],
319 GDK_THREADS_LEAVE ();
323 build_message (RBDAAPConnection
*connection
,
330 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
331 SoupMessage
*message
= NULL
;
334 uri
= soup_uri_new_with_base (priv
->base_uri
, path
);
339 message
= soup_message_new_from_uri (SOUP_METHOD_GET
, uri
);
340 soup_message_set_http_version (message
, SOUP_HTTP_1_1
);
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");
345 soup_message_add_header (message
->request_headers
, "Accept-Encoding", "gzip");
347 soup_message_add_header (message
->request_headers
, "Client-DAAP-Access-Index", "2");
349 if (priv
->password_protected
) {
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
);
361 soup_message_add_header (message
->request_headers
, "Authorization", h
);
366 gchar hash
[33] = {0};
367 gchar
*no_daap_path
= (gchar
*)path
;
369 if (g_strncasecmp (path
, "daap://", 7) == 0) {
370 no_daap_path
= strstr (path
, "/data");
373 rb_daap_hash_generate ((short)floor (version
), (const guchar
*)no_daap_path
, 2, (guchar
*)hash
, req_id
);
375 soup_message_add_header (message
->request_headers
, "Client-DAAP-Validation", hash
);
378 soup_message_add_header (message
->request_headers
, "Connection", "close");
388 *g_zalloc_wrapper (voidpf opaque
, uInt items
, uInt size
)
390 if ((items
!= 0) && (size
>= G_MAXUINT
/items
)) {
393 if ((size
!= 0) && (items
>= G_MAXUINT
/size
)) {
396 return g_malloc0 (items
* size
);
400 g_zfree_wrapper (voidpf opaque
, voidpf address
)
407 connection_set_error_message (RBDAAPConnection
*connection
,
410 /* FIXME: obtain a lock */
411 if (connection
->priv
->last_error_message
!= NULL
) {
412 g_free (connection
->priv
->last_error_message
);
414 connection
->priv
->last_error_message
= g_strdup (message
);
418 SoupMessage
*message
;
420 RBDAAPConnection
*connection
;
424 actual_http_response_handler (DAAPResponseData
*data
)
426 RBDAAPConnectionPrivate
*priv
;
429 const char *encoding_header
;
433 priv
= data
->connection
->priv
;
435 response
= data
->message
->response
.body
;
436 encoding_header
= NULL
;
437 response_length
= data
->message
->response
.length
;
439 message_path
= soup_uri_to_string (soup_message_get_uri (data
->message
), FALSE
);
441 rb_debug ("Received response from %s: %d, %s\n",
443 data
->message
->status_code
,
444 data
->message
->reason_phrase
);
446 if (data
->message
->response_headers
) {
447 encoding_header
= soup_message_get_header (data
->message
->response_headers
, "Content-Encoding");
450 if (SOUP_STATUS_IS_SUCCESSFUL (data
->status
) && encoding_header
&& strcmp (encoding_header
, "gzip") == 0) {
454 unsigned int factor
= 4;
455 unsigned int unc_size
= response_length
* factor
;
457 stream
.next_in
= (unsigned char *)response
;
458 stream
.avail_in
= response_length
;
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
;
469 rb_profile_start ("decompressing DAAP response");
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",
476 data
->status
= SOUP_STATUS_MALFORMED
;
477 rb_profile_end ("decompressing DAAP response (failed)");
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)");
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
);
492 rb_profile_end ("attempting inflate (error)");
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
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)");
508 rb_profile_end ("decompressing DAAP response (successful)");
511 response
= new_response
;
512 response_length
= stream
.total_out
;
515 rb_debug ("Received compressed response from %s but can't handle it",
517 data
->status
= SOUP_STATUS_MALFORMED
;
521 if (SOUP_STATUS_IS_SUCCESSFUL (data
->status
)) {
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
);
529 priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, data
->connection
);
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",
537 data
->status
= SOUP_STATUS_MALFORMED
;
538 rb_profile_end ("parsing DAAP response (failed)");
541 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MSTT
);
543 dmap_status
= g_value_get_int (&(item
->content
));
545 if (dmap_status
!= 200) {
546 rb_debug ("Error, dmap.status is not 200 in response from %s",
549 data
->status
= SOUP_STATUS_MALFORMED
;
551 rb_profile_end ("parsing DAAP response (successful)");
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
);
558 priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, data
->connection
);
561 rb_debug ("Error getting %s: %d, %s\n",
563 data
->message
->status_code
,
564 data
->message
->reason_phrase
);
565 connection_set_error_message (data
->connection
, data
->message
->reason_phrase
);
568 if (priv
->response_handler
) {
569 RBDAAPResponseHandler h
= priv
->response_handler
;
570 priv
->response_handler
= NULL
;
571 (*h
) (data
->connection
, data
->status
, structure
);
575 rb_daap_structure_destroy (structure
);
578 if (response
!= data
->message
->response
.body
) {
582 g_free (message_path
);
583 g_object_unref (G_OBJECT (data
->connection
));
584 g_object_unref (G_OBJECT (data
->message
));
589 http_response_handler (SoupMessage
*message
,
590 RBDAAPConnection
*connection
)
592 DAAPResponseData
*data
;
595 if (message
->status_code
== SOUP_STATUS_CANCELLED
) {
596 rb_debug ("Message cancelled");
600 data
= g_new0 (DAAPResponseData
, 1);
601 data
->status
= message
->status_code
;
602 response_length
= message
->response
.length
;
604 g_object_ref (G_OBJECT (connection
));
605 data
->connection
= connection
;
607 g_object_ref (G_OBJECT (message
));
608 data
->message
= message
;
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
614 data
->status
= SOUP_STATUS_MALFORMED
;
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
,
629 actual_http_response_handler (data
);
634 http_get (RBDAAPConnection
*connection
,
640 RBDAAPResponseHandler handler
,
643 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
644 SoupMessage
*message
;
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
,
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
,
660 rb_debug ("Queued message for http://%s:%d/%s",
661 priv
->base_uri
->host
,
662 priv
->base_uri
->port
,
668 entry_set_string_prop (RhythmDB
*db
,
669 RhythmDBEntry
*entry
,
670 RhythmDBPropType propid
,
676 if (str
== NULL
|| *str
== '\0' || !g_utf8_validate (str
, -1, NULL
)) {
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
);
689 emit_progress_idle (RBDAAPConnection
*connection
)
691 rb_debug ("Emitting progress");
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 ();
703 handle_server_info (RBDAAPConnection
*connection
,
707 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
708 RBDAAPItem
*item
= NULL
;
710 if (!SOUP_STATUS_IS_SUCCESSFUL (status
) || structure
== NULL
) {
711 rb_daap_connection_state_done (connection
, FALSE
);
715 /* get the daap version number */
716 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_APRO
);
718 rb_daap_connection_state_done (connection
, FALSE
);
722 priv
->daap_version
= g_value_get_double (&(item
->content
));
723 rb_daap_connection_state_done (connection
, TRUE
);
727 handle_login (RBDAAPConnection
*connection
,
731 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
732 RBDAAPItem
*item
= NULL
;
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
);
740 priv
->do_something_id
= g_idle_add ((GSourceFunc
) rb_daap_connection_do_something
, connection
);
744 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
745 rb_daap_connection_state_done (connection
, FALSE
);
749 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MLID
);
751 rb_debug ("Could not find daap.sessionid item in /login");
752 rb_daap_connection_state_done (connection
, FALSE
);
756 priv
->session_id
= (guint32
) g_value_get_int (&(item
->content
));
758 connection_connected (connection
);
760 rb_daap_connection_state_done (connection
, TRUE
);
764 handle_update (RBDAAPConnection
*connection
,
768 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
771 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
772 rb_daap_connection_state_done (connection
, FALSE
);
776 /* get a revision number */
777 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MUSR
);
779 rb_debug ("Could not find daap.serverrevision item in /update");
780 rb_daap_connection_state_done (connection
, FALSE
);
784 priv
->revision_number
= g_value_get_int (&(item
->content
));
785 rb_daap_connection_state_done (connection
, TRUE
);
789 handle_database_info (RBDAAPConnection
*connection
,
793 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
794 RBDAAPItem
*item
= NULL
;
796 gint n_databases
= 0;
798 /* get a list of databases, there should be only 1 */
800 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
801 rb_daap_connection_state_done (connection
, FALSE
);
805 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MRCO
);
807 rb_debug ("Could not find dmap.returnedcount item in /databases");
808 rb_daap_connection_state_done (connection
, FALSE
);
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");
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
);
824 item
= rb_daap_structure_find_item (listing_node
->children
, RB_DAAP_CC_MIID
);
826 rb_debug ("Could not find dmap.itemid item in /databases");
827 rb_daap_connection_state_done (connection
, FALSE
);
831 priv
->database_id
= g_value_get_int (&(item
->content
));
832 rb_daap_connection_state_done (connection
, TRUE
);
836 handle_song_listing (RBDAAPConnection
*connection
,
840 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
841 RBDAAPItem
*item
= NULL
;
846 gint specified_total_count
;
847 gboolean update_type
;
852 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
853 rb_daap_connection_state_done (connection
, FALSE
);
857 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MRCO
);
859 rb_debug ("Could not find dmap.returnedcount item in /databases/%d/items",
861 rb_daap_connection_state_done (connection
, FALSE
);
864 returned_count
= g_value_get_int (&(item
->content
));
865 if (returned_count
> 20) {
866 commit_batch
= returned_count
/ 20;
871 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MTCO
);
873 rb_debug ("Could not find dmap.specifiedtotalcount item in /databases/%d/items",
875 rb_daap_connection_state_done (connection
, FALSE
);
878 specified_total_count
= g_value_get_int (&(item
->content
));
880 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MUTY
);
882 rb_debug ("Could not find dmap.updatetype item in /databases/%d/items",
884 rb_daap_connection_state_done (connection
, FALSE
);
887 update_type
= g_value_get_char (&(item
->content
));
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",
893 rb_daap_connection_state_done (connection
, FALSE
);
897 priv
->item_id_to_uri
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, (GDestroyNotify
)rb_refstring_unref
);
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
);
904 connection
->priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, connection
);
906 for (i
= 0, n
= listing_node
->children
; n
; i
++, n
= n
->next
) {
908 RhythmDBEntry
*entry
= NULL
;
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
;
918 gint track_number
= 0;
919 gint disc_number
= 0;
924 for (n2
= n
->children
; n2
; n2
= n2
->next
) {
925 RBDAAPItem
*meta_item
;
927 meta_item
= n2
->data
;
929 switch (meta_item
->content_code
) {
930 case RB_DAAP_CC_MIID
:
931 item_id
= g_value_get_int (&(meta_item
->content
));
933 case RB_DAAP_CC_MINM
:
934 title
= g_value_get_string (&(meta_item
->content
));
936 case RB_DAAP_CC_ASAL
:
937 album
= g_value_get_string (&(meta_item
->content
));
939 case RB_DAAP_CC_ASAR
:
940 artist
= g_value_get_string (&(meta_item
->content
));
942 case RB_DAAP_CC_ASFM
:
943 format
= g_value_get_string (&(meta_item
->content
));
945 case RB_DAAP_CC_ASGN
:
946 genre
= g_value_get_string (&(meta_item
->content
));
948 case RB_DAAP_CC_ASTM
:
949 length
= g_value_get_int (&(meta_item
->content
));
951 case RB_DAAP_CC_ASTN
:
952 track_number
= g_value_get_int (&(meta_item
->content
));
954 case RB_DAAP_CC_ASDN
:
955 disc_number
= g_value_get_int (&(meta_item
->content
));
957 case RB_DAAP_CC_ASYR
:
958 year
= g_value_get_int (&(meta_item
->content
));
960 case RB_DAAP_CC_ASSZ
:
961 size
= g_value_get_int (&(meta_item
->content
));
963 case RB_DAAP_CC_ASBR
:
964 bitrate
= g_value_get_int (&(meta_item
->content
));
971 /*if (connection->daap_version == 3.0) {*/
972 uri
= g_strdup_printf ("%s/databases/%d/items/%d.%s?session-id=%u",
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
987 entry
= rhythmdb_entry_new (priv
->db
, priv
->db_type
, uri
);
989 rb_debug ("cannot create entry for daap track %s", uri
);
992 g_hash_table_insert (priv
->item_id_to_uri
, GINT_TO_POINTER (item_id
), rb_refstring_new (uri
));
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
);
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
);
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
);
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
);
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
);
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
);
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
);
1042 entry_set_string_prop (priv
->db
, entry
, RHYTHMDB_PROP_TITLE
, title
);
1045 entry_set_string_prop (priv
->db
, entry
, RHYTHMDB_PROP_ALBUM
, album
);
1048 entry_set_string_prop (priv
->db
, entry
, RHYTHMDB_PROP_ARTIST
, artist
);
1051 entry_set_string_prop (priv
->db
, entry
, RHYTHMDB_PROP_GENRE
, genre
);
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
);
1058 connection
->priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, connection
);
1059 rhythmdb_commit (priv
->db
);
1062 rhythmdb_commit (priv
->db
);
1063 rb_profile_end ("handling song listing");
1065 rb_daap_connection_state_done (connection
, TRUE
);
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.
1076 handle_playlists (RBDAAPConnection
*connection
,
1080 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1081 GNode
*listing_node
;
1085 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
1086 rb_daap_connection_state_done (connection
, FALSE
);
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",
1094 rb_daap_connection_state_done (connection
, FALSE
);
1098 for (i
= 0, n
= listing_node
->children
; n
; n
= n
->next
, i
++) {
1102 RBDAAPPlaylist
*playlist
;
1104 item
= rb_daap_structure_find_item (n
, RB_DAAP_CC_ABPL
);
1109 item
= rb_daap_structure_find_item (n
, RB_DAAP_CC_MIID
);
1111 rb_debug ("Could not find dmap.itemid item in /databases/%d/containers",
1115 id
= g_value_get_int (&(item
->content
));
1117 item
= rb_daap_structure_find_item (n
, RB_DAAP_CC_MINM
);
1119 rb_debug ("Could not find dmap.itemname item in /databases/%d/containers",
1123 name
= g_value_dup_string (&(item
->content
));
1125 playlist
= g_new0 (RBDAAPPlaylist
, 1);
1127 playlist
->name
= name
;
1128 rb_debug ("Got playlist %p: name %s, id %d", playlist
, playlist
->name
, playlist
->id
);
1130 priv
->playlists
= g_slist_prepend (priv
->playlists
, playlist
);
1132 priv
->playlists
= g_slist_reverse (priv
->playlists
);
1134 rb_daap_connection_state_done (connection
, TRUE
);
1138 handle_playlist_entries (RBDAAPConnection
*connection
,
1142 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1143 RBDAAPPlaylist
*playlist
;
1144 GNode
*listing_node
;
1147 GList
*playlist_uris
= NULL
;
1149 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
1150 rb_daap_connection_state_done (connection
, FALSE
);
1154 playlist
= (RBDAAPPlaylist
*)g_slist_nth_data (priv
->playlists
, priv
->reading_playlist
);
1155 g_assert (playlist
);
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
);
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
;
1171 item
= rb_daap_structure_find_item (node
, RB_DAAP_CC_MIID
);
1173 rb_debug ("Could not find dmap.itemid item in /databases/%d/containers/%d/items",
1174 priv
->database_id
, playlist
->id
);
1177 playlist_item_id
= g_value_get_int (&(item
->content
));
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
);
1186 playlist_uris
= g_list_prepend (playlist_uris
, rb_refstring_ref (item_uri
));
1188 rb_profile_end ("handling playlist entries");
1190 playlist
->uris
= playlist_uris
;
1191 rb_daap_connection_state_done (connection
, TRUE
);
1195 handle_logout (RBDAAPConnection
*connection
,
1199 connection_disconnected (connection
);
1201 /* is there any point handling errors here? */
1202 rb_daap_connection_state_done (connection
, TRUE
);
1206 rb_daap_connection_new (const char *name
,
1209 gboolean password_protected
,
1211 RhythmDBEntryType type
)
1213 return g_object_new (RB_TYPE_DAAP_CONNECTION
,
1216 "password-protected", password_protected
,
1224 rb_daap_connection_is_connected (RBDAAPConnection
*connection
)
1226 g_return_val_if_fail (RB_IS_DAAP_CONNECTION (connection
), FALSE
);
1228 return connection
->priv
->is_connected
;
1232 RBDAAPConnection
*connection
;
1233 RBDAAPConnectionCallback callback
;
1235 GDestroyNotify destroy
;
1236 } ConnectionResponseData
;
1239 connection_response_data_free (gpointer data
)
1241 ConnectionResponseData
*rdata
= data
;
1243 g_object_unref (rdata
->connection
);
1248 connected_cb (RBDAAPConnection
*connection
,
1249 ConnectionResponseData
*rdata
)
1253 rb_debug ("Connected callback");
1255 connection
->priv
->is_connecting
= FALSE
;
1257 g_signal_handlers_disconnect_by_func (connection
,
1258 G_CALLBACK (connected_cb
),
1261 /* if connected then we succeeded */
1262 result
= rb_daap_connection_is_connected (connection
);
1264 if (rdata
->callback
) {
1265 rdata
->callback (rdata
->connection
,
1267 rdata
->connection
->priv
->last_error_message
,
1271 if (rdata
->destroy
) {
1272 rdata
->destroy (rdata
);
1277 rb_daap_connection_connect (RBDAAPConnection
*connection
,
1278 RBDAAPConnectionCallback callback
,
1281 ConnectionResponseData
*rdata
;
1284 g_return_if_fail (RB_IS_DAAP_CONNECTION (connection
));
1285 g_return_if_fail (connection
->priv
->state
== DAAP_GET_INFO
);
1287 rb_debug ("Creating new DAAP connection to %s:%d", connection
->priv
->host
, connection
->priv
->port
);
1289 connection
->priv
->session
= soup_session_async_new ();
1291 path
= g_strdup_printf ("http://%s:%d", connection
->priv
->host
, connection
->priv
->port
);
1292 connection
->priv
->base_uri
= soup_uri_new (path
);
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 */
1301 connection
->priv
->daap_base_uri
= g_strdup_printf ("daap://%s:%d", connection
->priv
->host
, connection
->priv
->port
);
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
);
1310 if (connection
->priv
->do_something_id
!= 0) {
1311 g_source_remove (connection
->priv
->do_something_id
);
1314 connection
->priv
->is_connecting
= TRUE
;
1315 connection
->priv
->do_something_id
= g_idle_add ((GSourceFunc
) rb_daap_connection_do_something
, connection
);
1319 disconnected_cb (RBDAAPConnection
*connection
,
1320 ConnectionResponseData
*rdata
)
1324 rb_debug ("Disconnected callback");
1326 g_signal_handlers_disconnect_by_func (connection
,
1327 G_CALLBACK (disconnected_cb
),
1330 /* if not connected then we succeeded */
1331 result
= ! rb_daap_connection_is_connected (connection
);
1333 if (rdata
->callback
) {
1334 rdata
->callback (rdata
->connection
,
1336 rdata
->connection
->priv
->last_error_message
,
1340 if (rdata
->destroy
) {
1341 rdata
->destroy (rdata
);
1346 rb_daap_connection_finish (RBDAAPConnection
*connection
)
1348 g_return_if_fail (RB_IS_DAAP_CONNECTION (connection
));
1350 rb_debug ("DAAP finish");
1351 connection
->priv
->state
= DAAP_DONE
;
1352 connection
->priv
->progress
= 1.0f
;
1354 connection_operation_done (connection
);
1358 rb_daap_connection_disconnect (RBDAAPConnection
*connection
,
1359 RBDAAPConnectionCallback callback
,
1362 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1363 ConnectionResponseData
*rdata
;
1365 g_return_if_fail (RB_IS_DAAP_CONNECTION (connection
));
1367 rb_debug ("Disconnecting");
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
1373 priv
->state
= DAAP_DONE
;
1374 rb_daap_connection_finish (connection
);
1377 rdata
= g_new (ConnectionResponseData
, 1);
1378 rdata
->connection
= g_object_ref (connection
);
1379 rdata
->callback
= callback
;
1380 rdata
->data
= user_data
;
1381 rdata
->destroy
= connection_response_data_free
;
1383 g_signal_connect (connection
, "operation-done", G_CALLBACK (disconnected_cb
), rdata
);
1385 if (priv
->do_something_id
!= 0) {
1386 g_source_remove (priv
->do_something_id
);
1389 if (! connection
->priv
->is_connected
) {
1390 priv
->state
= DAAP_DONE
;
1391 rb_daap_connection_finish (connection
);
1393 priv
->state
= DAAP_LOGOUT
;
1395 priv
->do_something_id
= g_idle_add ((GSourceFunc
) rb_daap_connection_do_something
, connection
);
1400 rb_daap_connection_state_done (RBDAAPConnection
*connection
,
1403 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1405 rb_debug ("Transitioning to next state from %d", priv
->state
);
1407 if (result
== FALSE
) {
1408 priv
->state
= DAAP_DONE
;
1409 priv
->result
= FALSE
;
1411 switch (priv
->state
) {
1412 case DAAP_GET_PLAYLISTS
:
1413 if (priv
->playlists
== NULL
)
1414 priv
->state
= DAAP_DONE
;
1416 priv
->state
= DAAP_GET_PLAYLIST_ENTRIES
;
1418 case DAAP_GET_PLAYLIST_ENTRIES
:
1419 /* keep reading playlists until we've got them all */
1420 if (++priv
->reading_playlist
>= g_slist_length (priv
->playlists
))
1421 priv
->state
= DAAP_DONE
;
1425 priv
->state
= DAAP_DONE
;
1430 rb_debug ("This should never happen.");
1434 /* in most states, we just move on to the next */
1435 if (priv
->state
> DAAP_DONE
) {
1436 rb_debug ("This should REALLY never happen.");
1443 priv
->progress
= 1.0f
;
1444 if (connection
->priv
->emit_progress_id
!= 0) {
1445 g_source_remove (connection
->priv
->emit_progress_id
);
1447 connection
->priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, connection
);
1450 if (priv
->do_something_id
!= 0) {
1451 g_source_remove (priv
->do_something_id
);
1453 priv
->do_something_id
= g_idle_add ((GSourceFunc
) rb_daap_connection_do_something
, connection
);
1457 rb_daap_connection_do_something (RBDAAPConnection
*connection
)
1459 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1462 rb_debug ("Doing something for state: %d", priv
->state
);
1464 priv
->do_something_id
= 0;
1466 switch (priv
->state
) {
1468 rb_debug ("Getting DAAP server info");
1469 if (! http_get (connection
, "/server-info", FALSE
, 0.0, 0, FALSE
,
1470 (RBDAAPResponseHandler
) handle_server_info
, FALSE
)) {
1471 rb_debug ("Could not get DAAP connection info");
1472 rb_daap_connection_state_done (connection
, FALSE
);
1476 case DAAP_GET_PASSWORD
:
1477 if (priv
->password_protected
) {
1478 /* FIXME this bit is still synchronous */
1479 rb_debug ("Need a password for %s", priv
->name
);
1480 g_free (priv
->password
);
1481 priv
->password
= connection_get_password (connection
);
1483 if (priv
->password
== NULL
|| priv
->password
[0] == '\0') {
1484 rb_debug ("Password entry cancelled");
1485 priv
->result
= FALSE
;
1486 priv
->state
= DAAP_DONE
;
1487 rb_daap_connection_do_something (connection
);
1491 /* If the share went away while we were asking for the password,
1492 * don't bother trying to log in.
1494 if (priv
->state
!= DAAP_GET_PASSWORD
) {
1499 /* otherwise, fall through */
1500 priv
->state
= DAAP_LOGIN
;
1503 rb_debug ("Logging into DAAP server");
1504 if (! http_get (connection
, "/login", FALSE
, 0.0, 0, FALSE
,
1505 (RBDAAPResponseHandler
) handle_login
, FALSE
)) {
1506 rb_debug ("Could not login to DAAP server");
1507 /* FIXME: set state back to GET_PASSWORD to try again */
1508 rb_daap_connection_state_done (connection
, FALSE
);
1513 case DAAP_GET_REVISION_NUMBER
:
1514 rb_debug ("Getting DAAP server database revision number");
1515 path
= g_strdup_printf ("/update?session-id=%u&revision-number=1", priv
->session_id
);
1516 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1517 (RBDAAPResponseHandler
) handle_update
, FALSE
)) {
1518 rb_debug ("Could not get server database revision number");
1519 rb_daap_connection_state_done (connection
, FALSE
);
1524 case DAAP_GET_DB_INFO
:
1525 rb_debug ("Getting DAAP database info");
1526 path
= g_strdup_printf ("/databases?session-id=%u&revision-number=%d",
1527 priv
->session_id
, priv
->revision_number
);
1528 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1529 (RBDAAPResponseHandler
) handle_database_info
, FALSE
)) {
1530 rb_debug ("Could not get DAAP database info");
1531 rb_daap_connection_state_done (connection
, FALSE
);
1536 case DAAP_GET_SONGS
:
1537 rb_debug ("Getting DAAP song listing");
1538 path
= g_strdup_printf ("/databases/%i/items?session-id=%u&revision-number=%i"
1539 "&meta=dmap.itemid,dmap.itemname,daap.songalbum,"
1540 "daap.songartist,daap.daap.songgenre,daap.songsize,"
1541 "daap.songtime,daap.songtrackcount,daap.songtracknumber,"
1542 "daap.songyear,daap.songformat,daap.songgenre,"
1546 priv
->revision_number
);
1547 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1548 (RBDAAPResponseHandler
) handle_song_listing
, TRUE
)) {
1549 rb_debug ("Could not get DAAP song listing");
1550 rb_daap_connection_state_done (connection
, FALSE
);
1555 case DAAP_GET_PLAYLISTS
:
1556 rb_debug ("Getting DAAP playlists");
1557 path
= g_strdup_printf ("/databases/%d/containers?session-id=%u&revision-number=%d",
1560 priv
->revision_number
);
1561 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1562 (RBDAAPResponseHandler
) handle_playlists
, TRUE
)) {
1563 rb_debug ("Could not get DAAP playlists");
1564 rb_daap_connection_state_done (connection
, FALSE
);
1569 case DAAP_GET_PLAYLIST_ENTRIES
:
1571 RBDAAPPlaylist
*playlist
=
1572 (RBDAAPPlaylist
*) g_slist_nth_data (priv
->playlists
,
1573 priv
->reading_playlist
);
1574 g_assert (playlist
);
1575 rb_debug ("Reading DAAP playlist %d entries", priv
->reading_playlist
);
1576 path
= g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%u&revision-number=%d&meta=dmap.itemid",
1579 priv
->session_id
, priv
->revision_number
);
1580 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1581 (RBDAAPResponseHandler
) handle_playlist_entries
, TRUE
)) {
1582 rb_debug ("Could not get entries for DAAP playlist %d",
1583 priv
->reading_playlist
);
1584 rb_daap_connection_state_done (connection
, FALSE
);
1591 rb_debug ("Logging out of DAAP server");
1592 path
= g_strdup_printf ("/logout?session-id=%u", priv
->session_id
);
1593 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1594 (RBDAAPResponseHandler
) handle_logout
, FALSE
)) {
1595 rb_debug ("Could not log out of DAAP server");
1596 rb_daap_connection_state_done (connection
, FALSE
);
1603 rb_debug ("DAAP done");
1605 rb_daap_connection_finish (connection
);
1614 rb_daap_connection_get_headers (RBDAAPConnection
*connection
,
1618 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1620 char hash
[33] = {0};
1621 char *norb_daap_uri
= (char *)uri
;
1626 if (g_strncasecmp (uri
, "daap://", 7) == 0) {
1627 norb_daap_uri
= strstr (uri
, "/data");
1630 rb_daap_hash_generate ((short)floorf (priv
->daap_version
),
1631 (const guchar
*)norb_daap_uri
, 2,
1635 headers
= g_string_new ("Accept: */*\r\n"
1636 "Cache-Control: no-cache\r\n"
1637 "User-Agent: " RB_DAAP_USER_AGENT
"\r\n"
1638 "Accept-Language: en-us, en;q=5.0\r\n"
1639 "Client-DAAP-Access-Index: 2\r\n"
1640 "Client-DAAP-Version: 3.0\r\n");
1641 g_string_append_printf (headers
,
1642 "Client-DAAP-Validation: %s\r\n"
1643 "Client-DAAP-Request-ID: %d\r\n"
1644 "Connection: close\r\n",
1645 hash
, priv
->request_id
);
1647 if (priv
->password_protected
) {
1651 user_pass
= g_strdup_printf ("%s:%s", priv
->username
, priv
->password
);
1652 token
= soup_base64_encode (user_pass
, strlen (user_pass
));
1653 g_string_append_printf (headers
, "Authentication: Basic %s\r\n", token
);
1659 g_string_append_printf (headers
,"Range: bytes=%"G_GINT64_FORMAT
"-\r\n", bytes
);
1663 g_string_free (headers
, FALSE
);
1669 rb_daap_connection_get_playlists (RBDAAPConnection
*connection
)
1671 return connection
->priv
->playlists
;
1675 rb_daap_connection_dispose (GObject
*object
)
1677 RBDAAPConnectionPrivate
*priv
= RB_DAAP_CONNECTION (object
)->priv
;
1680 rb_debug ("DAAP connection dispose");
1682 if (priv
->emit_progress_id
!= 0) {
1683 g_source_remove (priv
->emit_progress_id
);
1684 priv
->emit_progress_id
= 0;
1687 if (priv
->do_something_id
!= 0) {
1688 g_source_remove (priv
->do_something_id
);
1689 priv
->do_something_id
= 0;
1693 g_free (priv
->name
);
1697 if (priv
->username
) {
1698 g_free (priv
->username
);
1699 priv
->username
= NULL
;
1702 if (priv
->password
) {
1703 g_free (priv
->password
);
1704 priv
->password
= NULL
;
1708 g_free (priv
->host
);
1712 if (priv
->playlists
) {
1713 for (l
= priv
->playlists
; l
; l
= l
->next
) {
1714 RBDAAPPlaylist
*playlist
= l
->data
;
1716 g_list_foreach (playlist
->uris
, (GFunc
)rb_refstring_unref
, NULL
);
1717 g_list_free (playlist
->uris
);
1718 g_free (playlist
->name
);
1722 g_slist_free (priv
->playlists
);
1723 priv
->playlists
= NULL
;
1726 if (priv
->item_id_to_uri
) {
1727 g_hash_table_destroy (priv
->item_id_to_uri
);
1728 priv
->item_id_to_uri
= NULL
;
1731 if (priv
->session
) {
1732 rb_debug ("Aborting all pending requests");
1733 soup_session_abort (priv
->session
);
1734 g_object_unref (G_OBJECT (priv
->session
));
1735 priv
->session
= NULL
;
1738 if (priv
->base_uri
) {
1739 soup_uri_free (priv
->base_uri
);
1740 priv
->base_uri
= NULL
;
1743 if (priv
->daap_base_uri
) {
1744 g_free (priv
->daap_base_uri
);
1745 priv
->daap_base_uri
= NULL
;
1749 g_object_unref (G_OBJECT (priv
->db
));
1753 if (priv
->last_error_message
!= NULL
) {
1754 g_free (priv
->last_error_message
);
1755 priv
->last_error_message
= NULL
;
1758 G_OBJECT_CLASS (rb_daap_connection_parent_class
)->dispose (object
);
1762 rb_daap_connection_set_property (GObject
*object
,
1764 const GValue
*value
,
1767 RBDAAPConnectionPrivate
*priv
= RB_DAAP_CONNECTION (object
)->priv
;
1771 g_free (priv
->name
);
1772 priv
->name
= g_value_dup_string (value
);
1775 if (priv
->db
!= NULL
) {
1776 g_object_unref (priv
->db
);
1778 priv
->db
= RHYTHMDB (g_value_dup_object (value
));
1780 case PROP_PASSWORD_PROTECTED
:
1781 priv
->password_protected
= g_value_get_boolean (value
);
1783 case PROP_ENTRY_TYPE
:
1784 priv
->db_type
= g_value_get_boxed (value
);
1787 g_free (priv
->host
);
1788 priv
->host
= g_value_dup_string (value
);
1791 priv
->port
= g_value_get_uint (value
);
1794 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1800 rb_daap_connection_get_property (GObject
*object
,
1805 RBDAAPConnectionPrivate
*priv
= RB_DAAP_CONNECTION (object
)->priv
;
1809 g_value_set_object (value
, priv
->db
);
1812 g_value_set_string (value
, priv
->name
);
1814 case PROP_ENTRY_TYPE
:
1815 g_value_set_boxed (value
, priv
->db_type
);
1817 case PROP_PASSWORD_PROTECTED
:
1818 g_value_set_boolean (value
, priv
->password_protected
);
1821 g_value_set_string (value
, priv
->host
);
1824 g_value_set_uint (value
, priv
->port
);
1827 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);