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 g_signal_emit (connection
,
295 connection_disconnected (RBDAAPConnection
*connection
)
297 rb_debug ("Emitting disconnected");
299 connection
->priv
->is_connected
= FALSE
;
301 g_signal_emit (connection
,
302 signals
[DISCONNECTED
],
307 connection_operation_done (RBDAAPConnection
*connection
)
309 rb_debug ("Emitting operation done");
311 g_signal_emit (connection
,
312 signals
[OPERATION_DONE
],
317 build_message (RBDAAPConnection
*connection
,
324 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
325 SoupMessage
*message
= NULL
;
328 uri
= soup_uri_new_with_base (priv
->base_uri
, path
);
333 message
= soup_message_new_from_uri (SOUP_METHOD_GET
, uri
);
334 soup_message_set_http_version (message
, SOUP_HTTP_1_1
);
336 soup_message_add_header (message
->request_headers
, "Client-DAAP-Version", "3.0");
337 soup_message_add_header (message
->request_headers
, "Accept-Language", "en-us, en;q=5.0");
339 soup_message_add_header (message
->request_headers
, "Accept-Encoding", "gzip");
341 soup_message_add_header (message
->request_headers
, "Client-DAAP-Access-Index", "2");
343 if (priv
->password_protected
) {
348 user_pass
= g_strdup_printf ("%s:%s", priv
->username
, priv
->password
);
349 token
= soup_base64_encode (user_pass
, strlen (user_pass
));
350 h
= g_strdup_printf ("Basic %s", token
);
355 soup_message_add_header (message
->request_headers
, "Authorization", h
);
360 gchar hash
[33] = {0};
361 gchar
*no_daap_path
= (gchar
*)path
;
363 if (g_strncasecmp (path
, "daap://", 7) == 0) {
364 no_daap_path
= strstr (path
, "/data");
367 rb_daap_hash_generate ((short)floor (version
), (const guchar
*)no_daap_path
, 2, (guchar
*)hash
, req_id
);
369 soup_message_add_header (message
->request_headers
, "Client-DAAP-Validation", hash
);
372 soup_message_add_header (message
->request_headers
, "Connection", "close");
382 *g_zalloc_wrapper (voidpf opaque
, uInt items
, uInt size
)
384 if ((items
!= 0) && (size
>= G_MAXUINT
/items
)) {
387 if ((size
!= 0) && (items
>= G_MAXUINT
/size
)) {
390 return g_malloc0 (items
* size
);
394 g_zfree_wrapper (voidpf opaque
, voidpf address
)
401 connection_set_error_message (RBDAAPConnection
*connection
,
404 /* FIXME: obtain a lock */
405 if (connection
->priv
->last_error_message
!= NULL
) {
406 g_free (connection
->priv
->last_error_message
);
408 connection
->priv
->last_error_message
= g_strdup (message
);
412 SoupMessage
*message
;
414 RBDAAPConnection
*connection
;
418 actual_http_response_handler (DAAPResponseData
*data
)
420 RBDAAPConnectionPrivate
*priv
;
423 const char *encoding_header
;
427 priv
= data
->connection
->priv
;
429 response
= data
->message
->response
.body
;
430 encoding_header
= NULL
;
431 response_length
= data
->message
->response
.length
;
433 message_path
= soup_uri_to_string (soup_message_get_uri (data
->message
), FALSE
);
435 rb_debug ("Received response from %s: %d, %s\n",
437 data
->message
->status_code
,
438 data
->message
->reason_phrase
);
440 if (data
->message
->response_headers
) {
441 encoding_header
= soup_message_get_header (data
->message
->response_headers
, "Content-Encoding");
444 if (SOUP_STATUS_IS_SUCCESSFUL (data
->status
) && encoding_header
&& strcmp (encoding_header
, "gzip") == 0) {
448 unsigned int factor
= 4;
449 unsigned int unc_size
= response_length
* factor
;
451 stream
.next_in
= (unsigned char *)response
;
452 stream
.avail_in
= response_length
;
455 new_response
= g_malloc (unc_size
+ 1);
456 stream
.next_out
= (unsigned char *)new_response
;
457 stream
.avail_out
= unc_size
;
458 stream
.total_out
= 0;
459 stream
.zalloc
= g_zalloc_wrapper
;
460 stream
.zfree
= g_zfree_wrapper
;
461 stream
.opaque
= NULL
;
463 rb_profile_start ("decompressing DAAP response");
465 if (inflateInit2 (&stream
, 32 /* auto-detect */ + 15 /* max */ ) != Z_OK
) {
466 inflateEnd (&stream
);
467 g_free (new_response
);
468 rb_debug ("Unable to decompress response from %s",
470 data
->status
= SOUP_STATUS_MALFORMED
;
471 rb_profile_end ("decompressing DAAP response (failed)");
476 rb_profile_start ("attempting inflate");
477 z_res
= inflate (&stream
, Z_FINISH
);
478 if (z_res
== Z_STREAM_END
) {
479 rb_profile_end ("attempting inflate (done)");
482 if ((z_res
!= Z_OK
&& z_res
!= Z_BUF_ERROR
) || stream
.avail_out
!= 0 || unc_size
> 40*1000*1000) {
483 inflateEnd (&stream
);
484 g_free (new_response
);
486 rb_profile_end ("attempting inflate (error)");
491 unc_size
= (response_length
* factor
);
492 /* unc_size can't grow bigger than 40MB, so
493 * unc_size can't overflow, and this realloc
496 new_response
= g_realloc (new_response
, unc_size
+ 1);
497 stream
.next_out
= (unsigned char *)(new_response
+ stream
.total_out
);
498 stream
.avail_out
= unc_size
- stream
.total_out
;
499 rb_profile_end ("attempting inflate (incomplete)");
502 rb_profile_end ("decompressing DAAP response (successful)");
505 response
= new_response
;
506 response_length
= stream
.total_out
;
509 rb_debug ("Received compressed response from %s but can't handle it",
511 data
->status
= SOUP_STATUS_MALFORMED
;
515 if (SOUP_STATUS_IS_SUCCESSFUL (data
->status
)) {
518 if (!rb_is_main_thread ()) {
519 priv
->progress
= -1.0f
;
520 if (priv
->emit_progress_id
!= 0) {
521 g_source_remove (priv
->emit_progress_id
);
523 priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, data
->connection
);
525 rb_profile_start ("parsing DAAP response");
526 structure
= rb_daap_structure_parse (response
, response_length
);
527 if (structure
== NULL
) {
528 rb_debug ("No daap structure returned from %s",
531 data
->status
= SOUP_STATUS_MALFORMED
;
532 rb_profile_end ("parsing DAAP response (failed)");
535 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MSTT
);
537 dmap_status
= g_value_get_int (&(item
->content
));
539 if (dmap_status
!= 200) {
540 rb_debug ("Error, dmap.status is not 200 in response from %s",
543 data
->status
= SOUP_STATUS_MALFORMED
;
545 rb_profile_end ("parsing DAAP response (successful)");
547 if (! rb_is_main_thread ()) {
548 priv
->progress
= 1.0f
;
549 if (priv
->emit_progress_id
!= 0) {
550 g_source_remove (priv
->emit_progress_id
);
552 priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, data
->connection
);
555 rb_debug ("Error getting %s: %d, %s\n",
557 data
->message
->status_code
,
558 data
->message
->reason_phrase
);
559 connection_set_error_message (data
->connection
, data
->message
->reason_phrase
);
562 if (priv
->response_handler
) {
563 RBDAAPResponseHandler h
= priv
->response_handler
;
564 priv
->response_handler
= NULL
;
565 (*h
) (data
->connection
, data
->status
, structure
);
569 rb_daap_structure_destroy (structure
);
572 if (response
!= data
->message
->response
.body
) {
576 g_free (message_path
);
577 g_object_unref (G_OBJECT (data
->connection
));
578 g_object_unref (G_OBJECT (data
->message
));
583 http_response_handler (SoupMessage
*message
,
584 RBDAAPConnection
*connection
)
586 DAAPResponseData
*data
;
589 if (message
->status_code
== SOUP_STATUS_CANCELLED
) {
590 rb_debug ("Message cancelled");
594 data
= g_new0 (DAAPResponseData
, 1);
595 data
->status
= message
->status_code
;
596 response_length
= message
->response
.length
;
598 g_object_ref (G_OBJECT (connection
));
599 data
->connection
= connection
;
601 g_object_ref (G_OBJECT (message
));
602 data
->message
= message
;
604 if (response_length
>= G_MAXUINT
/4 - 1) {
605 /* If response_length is too big,
606 * the g_malloc (unc_size + 1) below would overflow
608 data
->status
= SOUP_STATUS_MALFORMED
;
611 /* to avoid blocking the UI, handle big responses in a separate thread */
612 if (SOUP_STATUS_IS_SUCCESSFUL (data
->status
) && connection
->priv
->use_response_handler_thread
) {
613 GError
*error
= NULL
;
614 rb_debug ("creating thread to handle daap response");
615 g_thread_create ((GThreadFunc
) actual_http_response_handler
,
623 actual_http_response_handler (data
);
628 http_get (RBDAAPConnection
*connection
,
634 RBDAAPResponseHandler handler
,
637 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
638 SoupMessage
*message
;
640 message
= build_message (connection
, path
, need_hash
, version
, req_id
, send_close
);
641 if (message
== NULL
) {
642 rb_debug ("Error building message for http://%s:%d/%s",
643 priv
->base_uri
->host
,
644 priv
->base_uri
->port
,
649 priv
->use_response_handler_thread
= use_thread
;
650 priv
->response_handler
= handler
;
651 soup_session_queue_message (priv
->session
, message
,
652 (SoupMessageCallbackFn
) http_response_handler
,
654 rb_debug ("Queued message for http://%s:%d/%s",
655 priv
->base_uri
->host
,
656 priv
->base_uri
->port
,
662 entry_set_string_prop (RhythmDB
*db
,
663 RhythmDBEntry
*entry
,
664 RhythmDBPropType propid
,
670 if (str
== NULL
|| *str
== '\0' || !g_utf8_validate (str
, -1, NULL
)) {
676 g_value_init (&value
, G_TYPE_STRING
);
677 g_value_set_string (&value
, tmp
);
678 rhythmdb_entry_set (RHYTHMDB (db
), entry
, propid
, &value
);
679 g_value_unset (&value
);
683 emit_progress_idle (RBDAAPConnection
*connection
)
685 rb_debug ("Emitting progress");
687 GDK_THREADS_ENTER ();
688 g_signal_emit (G_OBJECT (connection
), signals
[CONNECTING
], 0,
689 connection
->priv
->state
,
690 connection
->priv
->progress
);
691 connection
->priv
->emit_progress_id
= 0;
692 GDK_THREADS_LEAVE ();
697 handle_server_info (RBDAAPConnection
*connection
,
701 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
702 RBDAAPItem
*item
= NULL
;
704 if (!SOUP_STATUS_IS_SUCCESSFUL (status
) || structure
== NULL
) {
705 rb_daap_connection_state_done (connection
, FALSE
);
709 /* get the daap version number */
710 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_APRO
);
712 rb_daap_connection_state_done (connection
, FALSE
);
716 priv
->daap_version
= g_value_get_double (&(item
->content
));
717 rb_daap_connection_state_done (connection
, TRUE
);
721 handle_login (RBDAAPConnection
*connection
,
725 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
726 RBDAAPItem
*item
= NULL
;
728 if (status
== SOUP_STATUS_UNAUTHORIZED
|| status
== SOUP_STATUS_FORBIDDEN
) {
729 rb_debug ("Incorrect password");
730 priv
->state
= DAAP_GET_PASSWORD
;
731 if (priv
->do_something_id
!= 0) {
732 g_source_remove (priv
->do_something_id
);
734 priv
->do_something_id
= g_idle_add ((GSourceFunc
) rb_daap_connection_do_something
, connection
);
738 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
739 rb_daap_connection_state_done (connection
, FALSE
);
743 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MLID
);
745 rb_debug ("Could not find daap.sessionid item in /login");
746 rb_daap_connection_state_done (connection
, FALSE
);
750 priv
->session_id
= (guint32
) g_value_get_int (&(item
->content
));
752 connection_connected (connection
);
754 rb_daap_connection_state_done (connection
, TRUE
);
758 handle_update (RBDAAPConnection
*connection
,
762 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
765 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
766 rb_daap_connection_state_done (connection
, FALSE
);
770 /* get a revision number */
771 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MUSR
);
773 rb_debug ("Could not find daap.serverrevision item in /update");
774 rb_daap_connection_state_done (connection
, FALSE
);
778 priv
->revision_number
= g_value_get_int (&(item
->content
));
779 rb_daap_connection_state_done (connection
, TRUE
);
783 handle_database_info (RBDAAPConnection
*connection
,
787 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
788 RBDAAPItem
*item
= NULL
;
790 gint n_databases
= 0;
792 /* get a list of databases, there should be only 1 */
794 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
795 rb_daap_connection_state_done (connection
, FALSE
);
799 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MRCO
);
801 rb_debug ("Could not find dmap.returnedcount item in /databases");
802 rb_daap_connection_state_done (connection
, FALSE
);
806 n_databases
= g_value_get_int (&(item
->content
));
807 if (n_databases
!= 1) {
808 rb_debug ("Host seems to have more than 1 database, how strange\n");
811 listing_node
= rb_daap_structure_find_node (structure
, RB_DAAP_CC_MLCL
);
812 if (listing_node
== NULL
) {
813 rb_debug ("Could not find dmap.listing item in /databases");
814 rb_daap_connection_state_done (connection
, FALSE
);
818 item
= rb_daap_structure_find_item (listing_node
->children
, RB_DAAP_CC_MIID
);
820 rb_debug ("Could not find dmap.itemid item in /databases");
821 rb_daap_connection_state_done (connection
, FALSE
);
825 priv
->database_id
= g_value_get_int (&(item
->content
));
826 rb_daap_connection_state_done (connection
, TRUE
);
830 handle_song_listing (RBDAAPConnection
*connection
,
834 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
835 RBDAAPItem
*item
= NULL
;
840 gint specified_total_count
;
841 gboolean update_type
;
846 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
847 rb_daap_connection_state_done (connection
, FALSE
);
851 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MRCO
);
853 rb_debug ("Could not find dmap.returnedcount item in /databases/%d/items",
855 rb_daap_connection_state_done (connection
, FALSE
);
858 returned_count
= g_value_get_int (&(item
->content
));
859 if (returned_count
> 20) {
860 commit_batch
= returned_count
/ 20;
865 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MTCO
);
867 rb_debug ("Could not find dmap.specifiedtotalcount item in /databases/%d/items",
869 rb_daap_connection_state_done (connection
, FALSE
);
872 specified_total_count
= g_value_get_int (&(item
->content
));
874 item
= rb_daap_structure_find_item (structure
, RB_DAAP_CC_MUTY
);
876 rb_debug ("Could not find dmap.updatetype item in /databases/%d/items",
878 rb_daap_connection_state_done (connection
, FALSE
);
881 update_type
= g_value_get_char (&(item
->content
));
883 listing_node
= rb_daap_structure_find_node (structure
, RB_DAAP_CC_MLCL
);
884 if (listing_node
== NULL
) {
885 rb_debug ("Could not find dmap.listing item in /databases/%d/items",
887 rb_daap_connection_state_done (connection
, FALSE
);
891 priv
->item_id_to_uri
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, (GDestroyNotify
)rb_refstring_unref
);
893 rb_profile_start ("handling song listing");
894 priv
->progress
= 0.0f
;
895 if (priv
->emit_progress_id
!= 0) {
896 g_source_remove (priv
->emit_progress_id
);
898 connection
->priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, connection
);
900 for (i
= 0, n
= listing_node
->children
; n
; i
++, n
= n
->next
) {
902 RhythmDBEntry
*entry
= NULL
;
906 const gchar
*title
= NULL
;
907 const gchar
*album
= NULL
;
908 const gchar
*artist
= NULL
;
909 const gchar
*format
= NULL
;
910 const gchar
*genre
= NULL
;
912 gint track_number
= 0;
913 gint disc_number
= 0;
918 for (n2
= n
->children
; n2
; n2
= n2
->next
) {
919 RBDAAPItem
*meta_item
;
921 meta_item
= n2
->data
;
923 switch (meta_item
->content_code
) {
924 case RB_DAAP_CC_MIID
:
925 item_id
= g_value_get_int (&(meta_item
->content
));
927 case RB_DAAP_CC_MINM
:
928 title
= g_value_get_string (&(meta_item
->content
));
930 case RB_DAAP_CC_ASAL
:
931 album
= g_value_get_string (&(meta_item
->content
));
933 case RB_DAAP_CC_ASAR
:
934 artist
= g_value_get_string (&(meta_item
->content
));
936 case RB_DAAP_CC_ASFM
:
937 format
= g_value_get_string (&(meta_item
->content
));
939 case RB_DAAP_CC_ASGN
:
940 genre
= g_value_get_string (&(meta_item
->content
));
942 case RB_DAAP_CC_ASTM
:
943 length
= g_value_get_int (&(meta_item
->content
));
945 case RB_DAAP_CC_ASTN
:
946 track_number
= g_value_get_int (&(meta_item
->content
));
948 case RB_DAAP_CC_ASDN
:
949 disc_number
= g_value_get_int (&(meta_item
->content
));
951 case RB_DAAP_CC_ASYR
:
952 year
= g_value_get_int (&(meta_item
->content
));
954 case RB_DAAP_CC_ASSZ
:
955 size
= g_value_get_int (&(meta_item
->content
));
957 case RB_DAAP_CC_ASBR
:
958 bitrate
= g_value_get_int (&(meta_item
->content
));
965 /*if (connection->daap_version == 3.0) {*/
966 uri
= g_strdup_printf ("%s/databases/%d/items/%d.%s?session-id=%u",
973 * "/databases/%d/items/%d.%s?session-id=%u&revision-id=%d";
974 * but its not going to work cause the other parts of the code
975 * depend on the uri to have the ip address so that the
976 * RBDAAPSource can be found to ++request_id
977 * maybe just /dont/ support older itunes. doesn't seem
978 * unreasonable to me, honestly
981 entry
= rhythmdb_entry_new (priv
->db
, priv
->db_type
, uri
);
983 rb_debug ("cannot create entry for daap track %s", uri
);
986 g_hash_table_insert (priv
->item_id_to_uri
, GINT_TO_POINTER (item_id
), rb_refstring_new (uri
));
994 /* create dummy date with given year */
995 date
= g_date_new_dmy (1, G_DATE_JANUARY
, year
);
996 julian
= g_date_get_julian (date
);
999 g_value_init (&value
, G_TYPE_ULONG
);
1000 g_value_set_ulong (&value
,julian
);
1001 rhythmdb_entry_set (priv
->db
, entry
, RHYTHMDB_PROP_DATE
, &value
);
1002 g_value_unset (&value
);
1006 g_value_init (&value
, G_TYPE_ULONG
);
1007 g_value_set_ulong (&value
,(gulong
)track_number
);
1008 rhythmdb_entry_set (priv
->db
, entry
, RHYTHMDB_PROP_TRACK_NUMBER
, &value
);
1009 g_value_unset (&value
);
1012 g_value_init (&value
, G_TYPE_ULONG
);
1013 g_value_set_ulong (&value
,(gulong
)disc_number
);
1014 rhythmdb_entry_set (priv
->db
, entry
, RHYTHMDB_PROP_DISC_NUMBER
, &value
);
1015 g_value_unset (&value
);
1018 g_value_init (&value
, G_TYPE_ULONG
);
1019 g_value_set_ulong (&value
,(gulong
)bitrate
);
1020 rhythmdb_entry_set (priv
->db
, entry
, RHYTHMDB_PROP_BITRATE
, &value
);
1021 g_value_unset (&value
);
1024 g_value_init (&value
, G_TYPE_ULONG
);
1025 g_value_set_ulong (&value
,(gulong
)length
/ 1000);
1026 rhythmdb_entry_set (priv
->db
, entry
, RHYTHMDB_PROP_DURATION
, &value
);
1027 g_value_unset (&value
);
1030 g_value_init (&value
, G_TYPE_UINT64
);
1031 g_value_set_uint64(&value
,(gint64
)size
);
1032 rhythmdb_entry_set (priv
->db
, entry
, RHYTHMDB_PROP_FILE_SIZE
, &value
);
1033 g_value_unset (&value
);
1036 entry_set_string_prop (priv
->db
, entry
, RHYTHMDB_PROP_TITLE
, title
);
1039 entry_set_string_prop (priv
->db
, entry
, RHYTHMDB_PROP_ALBUM
, album
);
1042 entry_set_string_prop (priv
->db
, entry
, RHYTHMDB_PROP_ARTIST
, artist
);
1045 entry_set_string_prop (priv
->db
, entry
, RHYTHMDB_PROP_GENRE
, genre
);
1047 if (i
% commit_batch
== 0) {
1048 connection
->priv
->progress
= ((float)i
/ (float)returned_count
);
1049 if (priv
->emit_progress_id
!= 0) {
1050 g_source_remove (connection
->priv
->emit_progress_id
);
1052 connection
->priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, connection
);
1053 rhythmdb_commit (priv
->db
);
1056 rb_profile_end ("handling song listing");
1058 rb_daap_connection_state_done (connection
, TRUE
);
1062 * what we really should do is only get a list of playlists and their ids
1063 * then when they are clicked on ('activate'd) by the user, get a list of
1064 * the files that are actually in them. This will speed up initial daap
1065 * connection times and reduce memory consumption.
1069 handle_playlists (RBDAAPConnection
*connection
,
1073 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1074 GNode
*listing_node
;
1078 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
1079 rb_daap_connection_state_done (connection
, FALSE
);
1083 listing_node
= rb_daap_structure_find_node (structure
, RB_DAAP_CC_MLCL
);
1084 if (listing_node
== NULL
) {
1085 rb_debug ("Could not find dmap.listing item in /databases/%d/containers",
1087 rb_daap_connection_state_done (connection
, FALSE
);
1091 for (i
= 0, n
= listing_node
->children
; n
; n
= n
->next
, i
++) {
1095 RBDAAPPlaylist
*playlist
;
1097 item
= rb_daap_structure_find_item (n
, RB_DAAP_CC_ABPL
);
1102 item
= rb_daap_structure_find_item (n
, RB_DAAP_CC_MIID
);
1104 rb_debug ("Could not find dmap.itemid item in /databases/%d/containers",
1108 id
= g_value_get_int (&(item
->content
));
1110 item
= rb_daap_structure_find_item (n
, RB_DAAP_CC_MINM
);
1112 rb_debug ("Could not find dmap.itemname item in /databases/%d/containers",
1116 name
= g_value_dup_string (&(item
->content
));
1118 playlist
= g_new0 (RBDAAPPlaylist
, 1);
1120 playlist
->name
= name
;
1121 rb_debug ("Got playlist %p: name %s, id %d", playlist
, playlist
->name
, playlist
->id
);
1123 priv
->playlists
= g_slist_prepend (priv
->playlists
, playlist
);
1126 rb_daap_connection_state_done (connection
, TRUE
);
1130 handle_playlist_entries (RBDAAPConnection
*connection
,
1134 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1135 RBDAAPPlaylist
*playlist
;
1136 GNode
*listing_node
;
1139 GList
*playlist_uris
= NULL
;
1141 if (structure
== NULL
|| SOUP_STATUS_IS_SUCCESSFUL (status
) == FALSE
) {
1142 rb_daap_connection_state_done (connection
, FALSE
);
1146 playlist
= (RBDAAPPlaylist
*)g_slist_nth_data (priv
->playlists
, priv
->reading_playlist
);
1147 g_assert (playlist
);
1149 listing_node
= rb_daap_structure_find_node (structure
, RB_DAAP_CC_MLCL
);
1150 if (listing_node
== NULL
) {
1151 rb_debug ("Could not find dmap.listing item in /databases/%d/containers/%d/items",
1152 priv
->database_id
, playlist
->id
);
1153 rb_daap_connection_state_done (connection
, FALSE
);
1157 rb_profile_start ("handling playlist entries");
1158 for (i
= 0, node
= listing_node
->children
; node
; node
= node
->next
, i
++) {
1159 RBRefString
*item_uri
;
1160 gint playlist_item_id
;
1163 item
= rb_daap_structure_find_item (node
, RB_DAAP_CC_MIID
);
1165 rb_debug ("Could not find dmap.itemid item in /databases/%d/containers/%d/items",
1166 priv
->database_id
, playlist
->id
);
1169 playlist_item_id
= g_value_get_int (&(item
->content
));
1171 item_uri
= g_hash_table_lookup (priv
->item_id_to_uri
, GINT_TO_POINTER (playlist_item_id
));
1172 if (item_uri
== NULL
) {
1173 rb_debug ("Entry %d in playlist %s doesn't exist in the database\n",
1174 playlist_item_id
, playlist
->name
);
1178 playlist_uris
= g_list_prepend (playlist_uris
, rb_refstring_ref (item_uri
));
1180 rb_profile_end ("handling playlist entries");
1182 playlist
->uris
= playlist_uris
;
1183 rb_daap_connection_state_done (connection
, TRUE
);
1187 handle_logout (RBDAAPConnection
*connection
,
1191 connection_disconnected (connection
);
1193 /* is there any point handling errors here? */
1194 rb_daap_connection_state_done (connection
, TRUE
);
1198 rb_daap_connection_new (const char *name
,
1201 gboolean password_protected
,
1203 RhythmDBEntryType type
)
1205 return g_object_new (RB_TYPE_DAAP_CONNECTION
,
1208 "password-protected", password_protected
,
1216 rb_daap_connection_is_connected (RBDAAPConnection
*connection
)
1218 g_return_val_if_fail (RB_IS_DAAP_CONNECTION (connection
), FALSE
);
1220 return connection
->priv
->is_connected
;
1224 RBDAAPConnection
*connection
;
1225 RBDAAPConnectionCallback callback
;
1227 GDestroyNotify destroy
;
1228 } ConnectionResponseData
;
1231 connection_response_data_free (gpointer data
)
1233 ConnectionResponseData
*rdata
= data
;
1235 g_object_unref (rdata
->connection
);
1240 connected_cb (RBDAAPConnection
*connection
,
1241 ConnectionResponseData
*rdata
)
1245 rb_debug ("Connected callback");
1247 connection
->priv
->is_connecting
= FALSE
;
1249 g_signal_handlers_disconnect_by_func (connection
,
1250 G_CALLBACK (connected_cb
),
1253 /* if connected then we succeeded */
1254 result
= rb_daap_connection_is_connected (connection
);
1256 if (rdata
->callback
) {
1257 rdata
->callback (rdata
->connection
,
1259 rdata
->connection
->priv
->last_error_message
,
1263 if (rdata
->destroy
) {
1264 rdata
->destroy (rdata
);
1269 rb_daap_connection_connect (RBDAAPConnection
*connection
,
1270 RBDAAPConnectionCallback callback
,
1273 ConnectionResponseData
*rdata
;
1276 g_return_if_fail (RB_IS_DAAP_CONNECTION (connection
));
1277 g_return_if_fail (connection
->priv
->state
== DAAP_GET_INFO
);
1279 rb_debug ("Creating new DAAP connection to %s:%d", connection
->priv
->host
, connection
->priv
->port
);
1281 connection
->priv
->session
= soup_session_async_new ();
1283 path
= g_strdup_printf ("http://%s:%d", connection
->priv
->host
, connection
->priv
->port
);
1284 connection
->priv
->base_uri
= soup_uri_new (path
);
1287 if (connection
->priv
->base_uri
== NULL
) {
1288 rb_debug ("Error parsing http://%s:%d", connection
->priv
->host
, connection
->priv
->port
);
1289 /* FIXME: do callback */
1293 connection
->priv
->daap_base_uri
= g_strdup_printf ("daap://%s:%d", connection
->priv
->host
, connection
->priv
->port
);
1295 rdata
= g_new (ConnectionResponseData
, 1);
1296 rdata
->connection
= g_object_ref (connection
);
1297 rdata
->callback
= callback
;
1298 rdata
->data
= user_data
;
1299 rdata
->destroy
= connection_response_data_free
;
1300 g_signal_connect (connection
, "operation-done", G_CALLBACK (connected_cb
), rdata
);
1302 if (connection
->priv
->do_something_id
!= 0) {
1303 g_source_remove (connection
->priv
->do_something_id
);
1306 connection
->priv
->is_connecting
= TRUE
;
1307 connection
->priv
->do_something_id
= g_idle_add ((GSourceFunc
) rb_daap_connection_do_something
, connection
);
1311 disconnected_cb (RBDAAPConnection
*connection
,
1312 ConnectionResponseData
*rdata
)
1316 rb_debug ("Disconnected callback");
1318 g_signal_handlers_disconnect_by_func (connection
,
1319 G_CALLBACK (disconnected_cb
),
1322 /* if not connected then we succeeded */
1323 result
= ! rb_daap_connection_is_connected (connection
);
1325 if (rdata
->callback
) {
1326 rdata
->callback (rdata
->connection
,
1328 rdata
->connection
->priv
->last_error_message
,
1332 if (rdata
->destroy
) {
1333 rdata
->destroy (rdata
);
1338 rb_daap_connection_finish (RBDAAPConnection
*connection
)
1340 g_return_if_fail (RB_IS_DAAP_CONNECTION (connection
));
1342 rb_debug ("DAAP finish");
1343 connection
->priv
->state
= DAAP_DONE
;
1344 connection
->priv
->progress
= 1.0f
;
1346 connection_operation_done (connection
);
1350 rb_daap_connection_disconnect (RBDAAPConnection
*connection
,
1351 RBDAAPConnectionCallback callback
,
1354 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1355 ConnectionResponseData
*rdata
;
1357 g_return_if_fail (RB_IS_DAAP_CONNECTION (connection
));
1359 rb_debug ("Disconnecting");
1361 if (connection
->priv
->is_connecting
) {
1362 /* this is a special case where the async connection
1363 hasn't returned yet so we need to force the connection
1365 priv
->state
= DAAP_DONE
;
1366 rb_daap_connection_finish (connection
);
1369 rdata
= g_new (ConnectionResponseData
, 1);
1370 rdata
->connection
= g_object_ref (connection
);
1371 rdata
->callback
= callback
;
1372 rdata
->data
= user_data
;
1373 rdata
->destroy
= connection_response_data_free
;
1375 g_signal_connect (connection
, "operation-done", G_CALLBACK (disconnected_cb
), rdata
);
1377 if (priv
->do_something_id
!= 0) {
1378 g_source_remove (priv
->do_something_id
);
1381 if (! connection
->priv
->is_connected
) {
1382 priv
->state
= DAAP_DONE
;
1383 rb_daap_connection_finish (connection
);
1385 priv
->state
= DAAP_LOGOUT
;
1387 priv
->do_something_id
= g_idle_add ((GSourceFunc
) rb_daap_connection_do_something
, connection
);
1392 rb_daap_connection_state_done (RBDAAPConnection
*connection
,
1395 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1397 rb_debug ("Transitioning to next state from %d", priv
->state
);
1399 if (result
== FALSE
) {
1400 priv
->state
= DAAP_DONE
;
1401 priv
->result
= FALSE
;
1403 switch (priv
->state
) {
1404 case DAAP_GET_PLAYLISTS
:
1405 if (priv
->playlists
== NULL
)
1406 priv
->state
= DAAP_DONE
;
1408 priv
->state
= DAAP_GET_PLAYLIST_ENTRIES
;
1410 case DAAP_GET_PLAYLIST_ENTRIES
:
1411 /* keep reading playlists until we've got them all */
1412 if (++priv
->reading_playlist
>= g_slist_length (priv
->playlists
))
1413 priv
->state
= DAAP_DONE
;
1417 priv
->state
= DAAP_DONE
;
1422 rb_debug ("This should never happen.");
1426 /* in most states, we just move on to the next */
1427 if (priv
->state
> DAAP_DONE
) {
1428 rb_debug ("This should REALLY never happen.");
1435 priv
->progress
= 1.0f
;
1436 if (connection
->priv
->emit_progress_id
!= 0) {
1437 g_source_remove (connection
->priv
->emit_progress_id
);
1439 connection
->priv
->emit_progress_id
= g_idle_add ((GSourceFunc
) emit_progress_idle
, connection
);
1442 if (priv
->do_something_id
!= 0) {
1443 g_source_remove (priv
->do_something_id
);
1445 priv
->do_something_id
= g_idle_add ((GSourceFunc
) rb_daap_connection_do_something
, connection
);
1449 rb_daap_connection_do_something (RBDAAPConnection
*connection
)
1451 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1454 rb_debug ("Doing something for state: %d", priv
->state
);
1456 priv
->do_something_id
= 0;
1458 switch (priv
->state
) {
1460 rb_debug ("Getting DAAP server info");
1461 if (! http_get (connection
, "/server-info", FALSE
, 0.0, 0, FALSE
,
1462 (RBDAAPResponseHandler
) handle_server_info
, FALSE
)) {
1463 rb_debug ("Could not get DAAP connection info");
1464 rb_daap_connection_state_done (connection
, FALSE
);
1468 case DAAP_GET_PASSWORD
:
1469 if (priv
->password_protected
) {
1470 /* FIXME this bit is still synchronous */
1471 rb_debug ("Need a password for %s", priv
->name
);
1472 g_free (priv
->password
);
1473 priv
->password
= connection_get_password (connection
);
1475 if (priv
->password
== NULL
|| priv
->password
[0] == '\0') {
1476 rb_debug ("Password entry cancelled");
1477 priv
->result
= FALSE
;
1478 priv
->state
= DAAP_DONE
;
1479 rb_daap_connection_do_something (connection
);
1483 /* If the share went away while we were asking for the password,
1484 * don't bother trying to log in.
1486 if (priv
->state
!= DAAP_GET_PASSWORD
) {
1491 /* otherwise, fall through */
1492 priv
->state
= DAAP_LOGIN
;
1495 rb_debug ("Logging into DAAP server");
1496 if (! http_get (connection
, "/login", FALSE
, 0.0, 0, FALSE
,
1497 (RBDAAPResponseHandler
) handle_login
, FALSE
)) {
1498 rb_debug ("Could not login to DAAP server");
1499 /* FIXME: set state back to GET_PASSWORD to try again */
1500 rb_daap_connection_state_done (connection
, FALSE
);
1505 case DAAP_GET_REVISION_NUMBER
:
1506 rb_debug ("Getting DAAP server database revision number");
1507 path
= g_strdup_printf ("/update?session-id=%u&revision-number=1", priv
->session_id
);
1508 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1509 (RBDAAPResponseHandler
) handle_update
, FALSE
)) {
1510 rb_debug ("Could not get server database revision number");
1511 rb_daap_connection_state_done (connection
, FALSE
);
1516 case DAAP_GET_DB_INFO
:
1517 rb_debug ("Getting DAAP database info");
1518 path
= g_strdup_printf ("/databases?session-id=%u&revision-number=%d",
1519 priv
->session_id
, priv
->revision_number
);
1520 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1521 (RBDAAPResponseHandler
) handle_database_info
, FALSE
)) {
1522 rb_debug ("Could not get DAAP database info");
1523 rb_daap_connection_state_done (connection
, FALSE
);
1528 case DAAP_GET_SONGS
:
1529 rb_debug ("Getting DAAP song listing");
1530 path
= g_strdup_printf ("/databases/%i/items?session-id=%u&revision-number=%i"
1531 "&meta=dmap.itemid,dmap.itemname,daap.songalbum,"
1532 "daap.songartist,daap.daap.songgenre,daap.songsize,"
1533 "daap.songtime,daap.songtrackcount,daap.songtracknumber,"
1534 "daap.songyear,daap.songformat,daap.songgenre,"
1538 priv
->revision_number
);
1539 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1540 (RBDAAPResponseHandler
) handle_song_listing
, TRUE
)) {
1541 rb_debug ("Could not get DAAP song listing");
1542 rb_daap_connection_state_done (connection
, FALSE
);
1547 case DAAP_GET_PLAYLISTS
:
1548 rb_debug ("Getting DAAP playlists");
1549 path
= g_strdup_printf ("/databases/%d/containers?session-id=%u&revision-number=%d",
1552 priv
->revision_number
);
1553 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1554 (RBDAAPResponseHandler
) handle_playlists
, TRUE
)) {
1555 rb_debug ("Could not get DAAP playlists");
1556 rb_daap_connection_state_done (connection
, FALSE
);
1561 case DAAP_GET_PLAYLIST_ENTRIES
:
1563 RBDAAPPlaylist
*playlist
=
1564 (RBDAAPPlaylist
*) g_slist_nth_data (priv
->playlists
,
1565 priv
->reading_playlist
);
1566 g_assert (playlist
);
1567 rb_debug ("Reading DAAP playlist %d entries", priv
->reading_playlist
);
1568 path
= g_strdup_printf ("/databases/%d/containers/%d/items?session-id=%u&revision-number=%d&meta=dmap.itemid",
1571 priv
->session_id
, priv
->revision_number
);
1572 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1573 (RBDAAPResponseHandler
) handle_playlist_entries
, TRUE
)) {
1574 rb_debug ("Could not get entries for DAAP playlist %d",
1575 priv
->reading_playlist
);
1576 rb_daap_connection_state_done (connection
, FALSE
);
1583 rb_debug ("Logging out of DAAP server");
1584 path
= g_strdup_printf ("/logout?session-id=%u", priv
->session_id
);
1585 if (! http_get (connection
, path
, TRUE
, priv
->daap_version
, 0, FALSE
,
1586 (RBDAAPResponseHandler
) handle_logout
, FALSE
)) {
1587 rb_debug ("Could not log out of DAAP server");
1588 rb_daap_connection_state_done (connection
, FALSE
);
1595 rb_debug ("DAAP done");
1597 rb_daap_connection_finish (connection
);
1606 rb_daap_connection_get_headers (RBDAAPConnection
*connection
,
1610 RBDAAPConnectionPrivate
*priv
= connection
->priv
;
1612 char hash
[33] = {0};
1613 char *norb_daap_uri
= (char *)uri
;
1618 if (g_strncasecmp (uri
, "daap://", 7) == 0) {
1619 norb_daap_uri
= strstr (uri
, "/data");
1622 rb_daap_hash_generate ((short)floorf (priv
->daap_version
),
1623 (const guchar
*)norb_daap_uri
, 2,
1627 headers
= g_string_new ("Accept: */*\r\n"
1628 "Cache-Control: no-cache\r\n"
1629 "User-Agent: " RB_DAAP_USER_AGENT
"\r\n"
1630 "Accept-Language: en-us, en;q=5.0\r\n"
1631 "Client-DAAP-Access-Index: 2\r\n"
1632 "Client-DAAP-Version: 3.0\r\n");
1633 g_string_append_printf (headers
,
1634 "Client-DAAP-Validation: %s\r\n"
1635 "Client-DAAP-Request-ID: %d\r\n"
1636 "Connection: close\r\n",
1637 hash
, priv
->request_id
);
1639 if (priv
->password_protected
) {
1643 user_pass
= g_strdup_printf ("%s:%s", priv
->username
, priv
->password
);
1644 token
= soup_base64_encode (user_pass
, strlen (user_pass
));
1645 g_string_append_printf (headers
, "Authentication: Basic %s\r\n", token
);
1651 g_string_append_printf (headers
,"Range: bytes=%"G_GINT64_FORMAT
"-\r\n", bytes
);
1655 g_string_free (headers
, FALSE
);
1661 rb_daap_connection_get_playlists (RBDAAPConnection
*connection
)
1663 return connection
->priv
->playlists
;
1667 rb_daap_connection_dispose (GObject
*object
)
1669 RBDAAPConnectionPrivate
*priv
= RB_DAAP_CONNECTION (object
)->priv
;
1672 rb_debug ("DAAP connection dispose");
1674 if (priv
->emit_progress_id
!= 0) {
1675 g_source_remove (priv
->emit_progress_id
);
1676 priv
->emit_progress_id
= 0;
1679 if (priv
->do_something_id
!= 0) {
1680 g_source_remove (priv
->do_something_id
);
1681 priv
->do_something_id
= 0;
1685 g_free (priv
->name
);
1689 if (priv
->username
) {
1690 g_free (priv
->username
);
1691 priv
->username
= NULL
;
1694 if (priv
->password
) {
1695 g_free (priv
->password
);
1696 priv
->password
= NULL
;
1700 g_free (priv
->host
);
1704 if (priv
->playlists
) {
1705 for (l
= priv
->playlists
; l
; l
= l
->next
) {
1706 RBDAAPPlaylist
*playlist
= l
->data
;
1708 g_list_foreach (playlist
->uris
, (GFunc
)rb_refstring_unref
, NULL
);
1709 g_list_free (playlist
->uris
);
1710 g_free (playlist
->name
);
1714 g_slist_free (priv
->playlists
);
1715 priv
->playlists
= NULL
;
1718 if (priv
->item_id_to_uri
) {
1719 g_hash_table_destroy (priv
->item_id_to_uri
);
1720 priv
->item_id_to_uri
= NULL
;
1723 if (priv
->session
) {
1724 rb_debug ("Aborting all pending requests");
1725 soup_session_abort (priv
->session
);
1726 g_object_unref (G_OBJECT (priv
->session
));
1727 priv
->session
= NULL
;
1730 if (priv
->base_uri
) {
1731 soup_uri_free (priv
->base_uri
);
1732 priv
->base_uri
= NULL
;
1735 if (priv
->daap_base_uri
) {
1736 g_free (priv
->daap_base_uri
);
1737 priv
->daap_base_uri
= NULL
;
1741 g_object_unref (G_OBJECT (priv
->db
));
1745 if (priv
->last_error_message
!= NULL
) {
1746 g_free (priv
->last_error_message
);
1747 priv
->last_error_message
= NULL
;
1750 G_OBJECT_CLASS (rb_daap_connection_parent_class
)->dispose (object
);
1754 rb_daap_connection_set_property (GObject
*object
,
1756 const GValue
*value
,
1759 RBDAAPConnectionPrivate
*priv
= RB_DAAP_CONNECTION (object
)->priv
;
1763 g_free (priv
->name
);
1764 priv
->name
= g_value_dup_string (value
);
1767 if (priv
->db
!= NULL
) {
1768 g_object_unref (priv
->db
);
1770 priv
->db
= RHYTHMDB (g_value_dup_object (value
));
1772 case PROP_PASSWORD_PROTECTED
:
1773 priv
->password_protected
= g_value_get_boolean (value
);
1775 case PROP_ENTRY_TYPE
:
1776 priv
->db_type
= g_value_get_boxed (value
);
1779 g_free (priv
->host
);
1780 priv
->host
= g_value_dup_string (value
);
1783 priv
->port
= g_value_get_uint (value
);
1786 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1792 rb_daap_connection_get_property (GObject
*object
,
1797 RBDAAPConnectionPrivate
*priv
= RB_DAAP_CONNECTION (object
)->priv
;
1801 g_value_set_object (value
, priv
->db
);
1804 g_value_set_string (value
, priv
->name
);
1806 case PROP_ENTRY_TYPE
:
1807 g_value_set_boxed (value
, priv
->db_type
);
1809 case PROP_PASSWORD_PROTECTED
:
1810 g_value_set_boolean (value
, priv
->password_protected
);
1813 g_value_set_string (value
, priv
->host
);
1816 g_value_set_uint (value
, priv
->port
);
1819 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);