47
45
#include <libdmapsharing/dacp-share.h>
48
46
#include <libdmapsharing/dacp-player.h>
50
static void dacp_share_set_property (GObject *object,
54
static void dacp_share_get_property (GObject *object,
58
static void dacp_share_dispose (GObject *object);
59
const char *dacp_share_get_type_of_service (DMAPShare *share);
60
void dacp_share_ctrl_int (DMAPShare *share,
65
SoupClientContext *context);
66
void dacp_share_login (DMAPShare *share,
71
SoupClientContext *context);
48
static void dacp_share_set_property (GObject * object,
52
static void dacp_share_get_property (GObject * object,
54
GValue * value, GParamSpec * pspec);
55
static void dacp_share_dispose (GObject * object);
56
const char *dacp_share_get_type_of_service (DMAPShare * share);
57
void dacp_share_ctrl_int (DMAPShare * share,
59
SoupMessage * message,
61
GHashTable * query, SoupClientContext * context);
62
void dacp_share_login (DMAPShare * share,
64
SoupMessage * message,
66
GHashTable * query, SoupClientContext * context);
73
static gchar *dacp_share_pairing_code(DACPShare *share, gchar* pair_txt, gchar passcode[4]);
74
static void dacp_share_send_playstatusupdate (DACPShare *share);
75
static void dacp_share_fill_playstatusupdate (DACPShare *share, SoupMessage *message);
68
static gchar *dacp_share_pairing_code (DACPShare * share, gchar * pair_txt,
70
static void dacp_share_send_playstatusupdate (DACPShare * share);
71
static void dacp_share_fill_playstatusupdate (DACPShare * share,
72
SoupMessage * message);
77
74
#define DACP_TYPE_OF_SERVICE "_touch-able._tcp"
78
75
#define DACP_PORT 3689
80
struct DACPSharePrivate {
77
struct DACPSharePrivate
81
79
DMAPMdnsBrowser *mdns_browser;
83
81
gchar *library_name;
133
134
object_class->set_property = dacp_share_set_property;
134
135
object_class->dispose = dacp_share_dispose;
136
dmap_class->get_type_of_service = dacp_share_get_type_of_service;
137
dmap_class->get_type_of_service = dacp_share_get_type_of_service;
137
138
dmap_class->ctrl_int = dacp_share_ctrl_int;
138
139
dmap_class->login = dacp_share_login;
140
141
g_object_class_install_property (object_class,
142
g_param_spec_string ("library-name",
144
"Library name as will be shown in the Remote",
143
g_param_spec_string ("library-name",
145
"Library name as will be shown in the Remote",
148
149
g_object_class_install_property (object_class,
150
g_param_spec_object ("player",
154
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
151
g_param_spec_object ("player",
157
G_PARAM_CONSTRUCT_ONLY));
157
160
* DACPShare::remote-found
158
161
* @share: the #DACPShare that received the signal.
252
251
g_cclosure_marshal_VOID__STRING,
252
G_TYPE_NONE, 1, G_TYPE_STRING);
256
254
g_type_class_add_private (klass, sizeof (DACPSharePrivate));
260
dacp_share_init (DACPShare *share)
258
dacp_share_init (DACPShare * share)
262
260
share->priv = DACP_SHARE_GET_PRIVATE (share);
264
262
share->priv->current_revision = 2;
266
share->priv->remotes = g_hash_table_new_full ((GHashFunc)g_str_hash,
267
(GEqualFunc)g_str_equal,
268
(GDestroyNotify)g_free,
269
(GDestroyNotify)g_free);
264
share->priv->remotes = g_hash_table_new_full ((GHashFunc) g_str_hash,
267
(GDestroyNotify) g_free,
275
275
static gchar *dbid;
278
280
// Creates a service name 14 characters long concatenating the hostname
279
281
// hash hex value with itself.
280
282
// Idea taken from stereo.
281
283
name = g_string_new (NULL);
282
g_string_printf (name, "%.8x", g_str_hash(g_get_host_name ()));
284
g_string_printf (name, "%.8x",
285
g_str_hash (g_get_host_name ()));
283
286
g_string_ascii_up (name);
284
287
g_string_append_len (name, name->str, 4);
286
289
dbid = name->str;
288
291
g_string_free (name, FALSE);
294
dacp_share_update_txt_records (DACPShare *share)
297
dacp_share_update_txt_records (DACPShare * share)
296
299
gchar *dbid_record;
297
300
gchar *library_name_record;
299
library_name_record = g_strdup_printf ("CtlN=%s", share->priv->library_name);
300
dbid_record = g_strdup_printf("DbId=%s", get_dbid());
302
gchar *txt_records[] = {"Ver=131073",
302
library_name_record =
303
g_strdup_printf ("CtlN=%s", share->priv->library_name);
304
dbid_record = g_strdup_printf ("DbId=%s", get_dbid ());
306
gchar *txt_records[] = { "Ver=131073",
311
316
g_object_set (share, "txt-records", txt_records, NULL);
374
377
g_object_unref (share->priv->player);
376
379
g_slist_free (share->priv->update_queue);
378
381
g_hash_table_destroy (share->priv->remotes);
382
mdns_remote_added (DMAPMdnsBrowser *browser,
383
DMAPMdnsBrowserService *service,
385
mdns_remote_added (DMAPMdnsBrowser * browser,
386
DMAPMdnsBrowserService * service, DACPShare * share)
386
388
DACPRemoteInfo *remote_info;
388
390
remote_info = g_new (DACPRemoteInfo, 1);
389
391
remote_info->host = g_strdup (service->host);
390
392
remote_info->port = service->port;
391
393
remote_info->connection = NULL;
392
394
remote_info->pair_txt = g_strdup (service->pair);
394
396
g_debug ("New Remote found: %s name=%s host=%s port=%u pair=%s",
395
service->service_name,
399
remote_info->pair_txt);
397
service->service_name,
399
remote_info->host, remote_info->port, remote_info->pair_txt);
401
401
g_hash_table_insert (share->priv->remotes,
402
service->service_name,
405
g_signal_emit (share,
406
signals [REMOTE_FOUND],
408
service->service_name,
402
service->service_name, remote_info);
413
mdns_remote_removed (DMAPMdnsBrowser *browser,
414
const char *service_name,
417
404
g_signal_emit (share,
418
signals [REMOTE_LOST],
422
g_hash_table_remove (share->priv->remotes,
405
signals[REMOTE_FOUND],
406
0, service->service_name, service->name);
410
mdns_remote_removed (DMAPMdnsBrowser * browser,
411
const char *service_name, DACPShare * share)
413
g_signal_emit (share, signals[REMOTE_LOST], 0, service_name);
415
g_hash_table_remove (share->priv->remotes, service_name);
427
dacp_share_new (const gchar *library_name,
430
DMAPContainerDb *container_db)
419
dacp_share_new (const gchar * library_name,
421
DMAPDb * db, DMAPContainerDb * container_db)
432
423
DACPShare *share;
434
425
g_object_ref (db);
435
426
g_object_ref (container_db);
437
share = DACP_SHARE (g_object_new (TYPE_DACP_SHARE,
439
"library-name", library_name,
442
"container-db", container_db,
443
"player", G_OBJECT (player),
444
"transcode-mimetype", NULL,
447
g_debug("Starting DACP server");
428
share = DACP_SHARE (g_object_new (DACP_TYPE_SHARE,
430
"library-name", library_name,
433
"container-db", container_db,
434
"player", G_OBJECT (player),
435
"transcode-mimetype", NULL, NULL));
437
g_debug ("Starting DACP server");
448
438
_dmap_share_server_start (DMAP_SHARE (share));
449
439
_dmap_share_publish_start (DMAP_SHARE (share));
455
dacp_share_start_lookup (DACPShare *share)
445
dacp_share_start_lookup (DACPShare * share)
459
449
if (share->priv->mdns_browser) {
460
450
g_warning ("DACP browsing already started");
464
share->priv->mdns_browser = dmap_mdns_browser_new (DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP);
454
share->priv->mdns_browser =
455
dmap_mdns_browser_new (DMAP_MDNS_BROWSER_SERVICE_TYPE_DACP);
466
457
g_signal_connect_object (share->priv->mdns_browser,
468
G_CALLBACK (mdns_remote_added),
459
G_CALLBACK (mdns_remote_added), share, 0);
471
460
g_signal_connect_object (share->priv->mdns_browser,
472
461
"service-removed",
473
G_CALLBACK (mdns_remote_removed),
462
G_CALLBACK (mdns_remote_removed), share, 0);
478
465
dmap_mdns_browser_start (share->priv->mdns_browser, &error);
479
466
if (error != NULL) {
480
g_warning ("Unable to start Remote lookup: %s", error->message);
467
g_warning ("Unable to start Remote lookup: %s",
481
469
g_error_free (error);
486
remove_remotes_cb (gpointer service_name, gpointer remote_info, gpointer share)
474
remove_remotes_cb (gpointer service_name, gpointer remote_info,
488
g_signal_emit ((DACPShare*) share,
489
signals [REMOTE_LOST],
491
(gchar *) service_name);
477
g_signal_emit ((DACPShare *) share,
478
signals[REMOTE_LOST], 0, (gchar *) service_name);
496
dacp_share_stop_lookup (DACPShare *share)
483
dacp_share_stop_lookup (DACPShare * share)
500
487
if (!share->priv->mdns_browser) {
501
488
g_warning ("DACP browsing not started");
505
g_hash_table_foreach_remove (share->priv->remotes, remove_remotes_cb, share);
492
g_hash_table_foreach_remove (share->priv->remotes, remove_remotes_cb,
508
496
dmap_mdns_browser_stop (share->priv->mdns_browser, &error);
509
497
if (error != NULL) {
510
g_warning ("Unable to stop Remote lookup: %s", error->message);
498
g_warning ("Unable to stop Remote lookup: %s",
511
500
g_error_free (error);
514
503
share->priv->mdns_browser = NULL;
518
dacp_share_get_type_of_service (DMAPShare *share)
507
dacp_share_get_type_of_service (DMAPShare * share)
520
509
return DACP_TYPE_OF_SERVICE;
524
dacp_share_player_updated (DACPShare *share)
513
dacp_share_player_updated (DACPShare * share)
526
515
share->priv->current_revision++;
527
516
dacp_share_send_playstatusupdate (share);
531
status_update_message_finished (SoupMessage *message, DACPShare *share)
520
status_update_message_finished (SoupMessage * message, DACPShare * share)
533
share->priv->update_queue = g_slist_remove (share->priv->update_queue, message);
522
share->priv->update_queue =
523
g_slist_remove (share->priv->update_queue, message);
534
524
g_object_unref (message);
538
dacp_share_send_playstatusupdate (DACPShare *share)
528
dacp_share_send_playstatusupdate (DACPShare * share)
542
g_object_get (share, "server", &server, NULL);
543
for (list = share->priv->update_queue; list; list = list->next) {
544
dacp_share_fill_playstatusupdate (share, (SoupMessage*) list->data);
545
soup_server_unpause_message (server, (SoupMessage*) list->data);
531
SoupServer *server = NULL;
533
g_object_get (share, "server-ipv4", &server, NULL);
535
for (list = share->priv->update_queue; list;
537
dacp_share_fill_playstatusupdate (share,
540
soup_server_unpause_message (server,
544
g_object_unref (server);
549
g_object_get (share, "server-ipv6", &server, NULL);
551
for (list = share->priv->update_queue; list;
553
dacp_share_fill_playstatusupdate (share,
556
soup_server_unpause_message (server,
560
g_object_unref (server);
547
563
g_slist_free (share->priv->update_queue);
548
564
share->priv->update_queue = NULL;
549
g_object_unref (server);
553
dacp_share_fill_playstatusupdate (DACPShare *share, SoupMessage *message)
568
dacp_share_fill_playstatusupdate (DACPShare * share, SoupMessage * message)
556
571
DAAPRecord *record;
558
573
DACPRepeatState repeat_state;
559
574
gboolean shuffle_state;
560
575
guint playing_time;
562
g_object_get (share->priv->player,
563
"play-state", &play_state,
564
"repeat-state", &repeat_state,
565
"shuffle-state", &shuffle_state,
566
"playing-time", &playing_time,
577
g_object_get (share->priv->player,
578
"play-state", &play_state,
579
"repeat-state", &repeat_state,
580
"shuffle-state", &shuffle_state,
581
"playing-time", &playing_time, NULL);
569
583
record = dacp_player_now_playing_record (share->priv->player);
571
585
cmst = dmap_structure_add (NULL, DMAP_CC_CMST);
572
586
dmap_structure_add (cmst, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
573
dmap_structure_add (cmst, DMAP_CC_CMSR, share->priv->current_revision);
587
dmap_structure_add (cmst, DMAP_CC_CMSR,
588
share->priv->current_revision);
574
589
dmap_structure_add (cmst, DMAP_CC_CAPS, (gint32) play_state);
575
590
dmap_structure_add (cmst, DMAP_CC_CASH, shuffle_state ? 1 : 0);
576
591
dmap_structure_add (cmst, DMAP_CC_CARP, (gint32) repeat_state);
600
615
dmap_structure_add (cmst, DMAP_CC_CANG, "");
601
616
dmap_structure_add (cmst, DMAP_CC_ASAI, 0);
602
617
//dmap_structure_add (cmst, DMAP_CC_AEMK, 1);
603
g_debug ("Playing time: %u, Track time: %u", playing_time, track_time);
604
dmap_structure_add (cmst, DMAP_CC_CANT, track_time - playing_time);
618
g_debug ("Playing time: %u, Track time: %u", playing_time,
620
dmap_structure_add (cmst, DMAP_CC_CANT,
621
track_time - playing_time);
605
622
dmap_structure_add (cmst, DMAP_CC_CAST, track_time);
611
628
g_object_unref (record);
614
_dmap_share_message_set_from_dmap_structure (DMAP_SHARE (share), message, cmst);
631
_dmap_share_message_set_from_dmap_structure (DMAP_SHARE (share),
615
633
dmap_structure_destroy (cmst);
619
637
debug_param (gpointer key, gpointer val, gpointer user_data)
621
g_debug ("%s %s", (char *) key, (char *) val);
639
g_debug ("%s %s", (char *) key, (char *) val);
625
dacp_share_login (DMAPShare *share,
627
SoupMessage *message,
630
SoupClientContext *context)
643
dacp_share_login (DMAPShare * share,
645
SoupMessage * message,
647
GHashTable * query, SoupClientContext * context)
632
649
gchar *pairing_guid;
635
651
g_debug ("(DACP) Path is %s.", path);
637
653
g_hash_table_foreach (query, debug_param, NULL);
642
658
if (pairing_guid != NULL) {
643
659
gboolean allow_login;
645
g_signal_emit (share, signals [LOOKUP_GUID], 0, pairing_guid, &allow_login);
661
g_signal_emit (share, signals[LOOKUP_GUID], 0, pairing_guid,
647
664
if (!allow_login) {
648
665
g_debug ("Unknown remote trying to connect");
649
soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
666
soup_message_set_status (message,
667
SOUP_STATUS_FORBIDDEN);
654
672
_dmap_share_login (share, server, message, path, query, context);
658
dacp_share_ctrl_int (DMAPShare *share,
660
SoupMessage *message,
663
SoupClientContext *context)
676
dacp_share_ctrl_int (DMAPShare * share,
678
SoupMessage * message,
680
GHashTable * query, SoupClientContext * context)
665
682
const char *rest_of_path;
667
684
DACPShare *dacp_share = DACP_SHARE (share);
669
686
g_debug ("Path is %s.", path);
671
688
g_hash_table_foreach (query, debug_param, NULL);
674
691
rest_of_path = strchr (path + 1, '/');
676
693
/* If calling /ctrl-int without args, the client doesnt need a
677
session-id, otherwise it does and it should be validated. */
678
if ((rest_of_path != NULL) && (! _dmap_share_session_id_validate (share, context, message, query, NULL))) {
694
* session-id, otherwise it does and it should be validated. */
695
if ((rest_of_path != NULL)
697
(!_dmap_share_session_id_validate
698
(share, context, message, query, NULL))) {
679
699
soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
683
703
if (rest_of_path == NULL) {
687
* MTCO specified total count
688
* MRCO returned count
692
* CMIK Unknown (TRUE)
693
* CMSP Unknown (TRUE)
694
* CMSV Unknown (TRUE)
695
* CASS Unknown (TRUE)
696
* CASU Unknown (TRUE)
697
* CASG Unknown (TRUE)
707
* MTCO specified total count
708
* MRCO returned count
712
* CMIK Unknown (TRUE)
713
* CMSP Unknown (TRUE)
714
* CMSV Unknown (TRUE)
715
* CASS Unknown (TRUE)
716
* CASU Unknown (TRUE)
717
* CASG Unknown (TRUE)
704
724
// dacp.controlint
705
725
caci = dmap_structure_add (NULL, DMAP_CC_CACI);
707
dmap_structure_add (caci, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
727
dmap_structure_add (caci, DMAP_CC_MSTT,
728
(gint32) DMAP_STATUS_OK);
708
729
// dmap.updatetype
709
730
dmap_structure_add (caci, DMAP_CC_MUTY, 0);
710
731
// dmap.specifiedtotalcount
746
768
cmgt = dmap_structure_add (NULL, DMAP_CC_CMGT);
747
769
dmap_structure_add (cmgt, DMAP_CC_MSTT, DMAP_STATUS_OK);
749
771
properties = g_strsplit (properties_query, ",", -1);
750
772
for (property = properties; *property; property++) {
751
if (g_ascii_strcasecmp (*property, "dmcp.volume") == 0) {
773
if (g_ascii_strcasecmp (*property, "dmcp.volume") ==
753
g_object_get (dacp_share->priv->player, "volume", &volume, NULL);
777
g_object_get (dacp_share->priv->player,
778
"volume", &volume, NULL);
754
779
//g_debug ("Sending volume: %lu", volume);
755
dmap_structure_add (cmgt, DMAP_CC_CMVO, volume);
780
dmap_structure_add (cmgt, DMAP_CC_CMVO,
757
g_warning ("Unhandled property %s", *property);
783
g_warning ("Unhandled property %s",
761
788
g_strfreev (properties);
763
_dmap_share_message_set_from_dmap_structure (share, message, cmgt);
790
_dmap_share_message_set_from_dmap_structure (share, message,
764
792
dmap_structure_destroy (cmgt);
765
793
} else if (g_ascii_strcasecmp ("/1/setproperty", rest_of_path) == 0) {
766
794
if (g_hash_table_lookup (query, "dmcp.volume")) {
767
gdouble volume = strtod (g_hash_table_lookup (query, "dmcp.volume"), NULL);
768
g_object_set (dacp_share->priv->player, "volume", (gulong) volume, NULL);
796
strtod (g_hash_table_lookup
797
(query, "dmcp.volume"), NULL);
798
g_object_set (dacp_share->priv->player, "volume",
799
(gulong) volume, NULL);
770
801
soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
771
802
} else if (g_ascii_strcasecmp ("/1/getspeakers", rest_of_path) == 0) {
775
806
casp = dmap_structure_add (NULL, DMAP_CC_CASP);
776
dmap_structure_add (casp, DMAP_CC_MSTT, (gint32) DMAP_STATUS_OK);
807
dmap_structure_add (casp, DMAP_CC_MSTT,
808
(gint32) DMAP_STATUS_OK);
777
809
mdcl = dmap_structure_add (casp, DMAP_CC_MDCL);
779
811
dmap_structure_add (casp, DMAP_CC_CAIA, TRUE);
780
812
dmap_structure_add (casp, DMAP_CC_MINM, "Computer");
781
813
dmap_structure_add (casp, DMAP_CC_MSMA, (gint32) 0);
783
_dmap_share_message_set_from_dmap_structure (share, message, casp);
815
_dmap_share_message_set_from_dmap_structure (share, message,
784
817
dmap_structure_destroy (casp);
785
} else if (g_ascii_strcasecmp ("/1/playstatusupdate", rest_of_path) == 0) {
786
gchar *revision = g_hash_table_lookup (query, "revision-number");
818
} else if (g_ascii_strcasecmp ("/1/playstatusupdate", rest_of_path) ==
821
g_hash_table_lookup (query, "revision-number");
787
822
gint revision_number = atoi (revision);
789
824
if (revision_number >= dacp_share->priv->current_revision) {
790
825
g_object_ref (message);
791
dacp_share->priv->update_queue = g_slist_prepend (dacp_share->priv->update_queue, message);
792
g_signal_connect_object (message,
794
G_CALLBACK (status_update_message_finished),
826
dacp_share->priv->update_queue =
827
g_slist_prepend (dacp_share->
828
priv->update_queue, message);
829
g_signal_connect_object (message, "finished",
831
(status_update_message_finished),
796
833
soup_server_pause_message (server, message);
798
dacp_share_fill_playstatusupdate (dacp_share, message);
835
dacp_share_fill_playstatusupdate (dacp_share,
800
838
} else if (g_ascii_strcasecmp ("/1/playpause", rest_of_path) == 0) {
801
839
dacp_player_play_pause (dacp_share->priv->player);
809
847
} else if (g_ascii_strcasecmp ("/1/previtem", rest_of_path) == 0) {
810
848
dacp_player_prev_item (dacp_share->priv->player);
811
849
soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
812
} else if (g_ascii_strcasecmp ("/1/nowplayingartwork", rest_of_path) == 0) {
850
} else if (g_ascii_strcasecmp ("/1/nowplayingartwork", rest_of_path)
813
852
guint width = 320;
814
853
guint height = 320;
815
854
gchar *artwork_filename;
817
856
gsize buffer_len;
819
858
if (g_hash_table_lookup (query, "mw"))
820
859
width = atoi (g_hash_table_lookup (query, "mw"));
821
860
if (g_hash_table_lookup (query, "mh"))
822
861
height = atoi (g_hash_table_lookup (query, "mh"));
823
artwork_filename = dacp_player_now_playing_artwork (dacp_share->priv->player, width, height);
863
dacp_player_now_playing_artwork (dacp_share->
824
866
if (!artwork_filename) {
825
867
g_debug ("No artwork for currently playing song");
826
soup_message_set_status (message, SOUP_STATUS_NOT_FOUND);
868
soup_message_set_status (message,
869
SOUP_STATUS_NOT_FOUND);
829
872
#ifdef HAVE_GDKPIXBUF
830
GdkPixbuf *artwork = gdk_pixbuf_new_from_file_at_scale (artwork_filename, width, height, TRUE, NULL);
874
gdk_pixbuf_new_from_file_at_scale (artwork_filename,
832
879
g_debug ("Error loading image file");
833
880
g_free (artwork_filename);
834
soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
881
soup_message_set_status (message,
882
SOUP_STATUS_INTERNAL_SERVER_ERROR);
837
if (!gdk_pixbuf_save_to_buffer (artwork, &buffer, &buffer_len, "png", NULL, NULL)) {
885
if (!gdk_pixbuf_save_to_buffer
886
(artwork, &buffer, &buffer_len, "png", NULL, NULL)) {
838
887
g_debug ("Error saving artwork to PNG");
839
888
g_object_unref (artwork);
840
889
g_free (artwork_filename);
841
soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
890
soup_message_set_status (message,
891
SOUP_STATUS_INTERNAL_SERVER_ERROR);
844
894
g_object_unref (artwork);
846
if (!g_file_get_contents (artwork_filename, &buffer, &buffer_len, NULL)) {
896
if (!g_file_get_contents
897
(artwork_filename, &buffer, &buffer_len, NULL)) {
847
898
g_debug ("Error getting artwork data");
848
899
g_free (artwork_filename);
849
soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);
900
soup_message_set_status (message,
901
SOUP_STATUS_INTERNAL_SERVER_ERROR);
853
905
g_free (artwork_filename);
854
906
soup_message_set_status (message, SOUP_STATUS_OK);
855
soup_message_set_response (message, "image/png", SOUP_MEMORY_TAKE, buffer, buffer_len);
907
soup_message_set_response (message, "image/png",
908
SOUP_MEMORY_TAKE, buffer,
856
910
} else if (g_ascii_strcasecmp ("/1/cue", rest_of_path) == 0) {
859
913
command = g_hash_table_lookup (query, "command");
862
916
g_debug ("No CUE command specified");
863
soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
917
soup_message_set_status (message,
918
SOUP_STATUS_NO_CONTENT);
865
920
} else if (g_ascii_strcasecmp ("clear", command) == 0) {
866
921
dacp_player_cue_clear (dacp_share->priv->player);
867
soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
922
soup_message_set_status (message,
923
SOUP_STATUS_NO_CONTENT);
868
924
} else if (g_ascii_strcasecmp ("play", command) == 0) {
870
926
gchar *record_query;
882
939
sorted_records = g_hash_table_get_values (records);
883
940
sort_by = g_hash_table_lookup (query, "sort");
884
941
if (g_strcmp0 (sort_by, "album") == 0) {
885
sorted_records = g_list_sort_with_data (sorted_records, (GCompareDataFunc) daap_record_cmp_by_album, db);
943
g_list_sort_with_data (sorted_records,
945
daap_record_cmp_by_album,
886
947
} else if (sort_by != NULL) {
887
g_warning ("Unknown sort column: %s", sort_by);
948
g_warning ("Unknown sort column: %s",
890
dacp_player_cue_play (dacp_share->priv->player, sorted_records, index);
952
dacp_player_cue_play (dacp_share->priv->player,
953
sorted_records, index);
892
955
g_list_free (sorted_records);
893
956
g_hash_table_unref (records);
894
957
dmap_share_free_filter (filter_def);
896
959
cacr = dmap_structure_add (NULL, DMAP_CC_CACR);
897
dmap_structure_add (cacr, DMAP_CC_MSTT, DMAP_STATUS_OK);
960
dmap_structure_add (cacr, DMAP_CC_MSTT,
898
962
dmap_structure_add (cacr, DMAP_CC_MIID, index);
900
_dmap_share_message_set_from_dmap_structure (share, message, cacr);
964
_dmap_share_message_set_from_dmap_structure (share,
901
967
dmap_structure_destroy (cacr);
903
969
g_warning ("Unhandled cue command: %s", command);
904
soup_message_set_status (message, SOUP_STATUS_NO_CONTENT);
970
soup_message_set_status (message,
971
SOUP_STATUS_NO_CONTENT);
914
981
#define PASSCODE_LENGTH 4
917
dacp_share_pairing_code(DACPShare *share, gchar* pair_txt, gchar passcode[4]) {
984
dacp_share_pairing_code (DACPShare * share, gchar * pair_txt,
919
988
GString *pairing_code;
920
989
gchar *pairing_string;
923
992
/* The pairing code is the MD5 sum of the concatenation of pair_txt
924
with the passcode, but the passcode takes 16-bits unicodes characters */
925
pairing_string = g_strnfill(PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2, '\0');
926
g_strlcpy(pairing_string, pair_txt, PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2);
993
* with the passcode, but the passcode takes 16-bits unicodes characters */
995
g_strnfill (PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2, '\0');
996
g_strlcpy (pairing_string, pair_txt,
997
PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2);
927
998
for (i = 0; i < 4; i++) {
928
999
pairing_string[PAIR_TXT_LENGTH + i * 2] = passcode[i];
931
pairing_code = g_string_new (
932
g_compute_checksum_for_data(G_CHECKSUM_MD5,
933
(guchar*)pairing_string,
934
PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2));
1003
g_string_new (g_compute_checksum_for_data
1004
(G_CHECKSUM_MD5, (guchar *) pairing_string,
1005
PAIR_TXT_LENGTH + PASSCODE_LENGTH * 2));
935
1006
g_string_ascii_up (pairing_code);
936
1007
ret = pairing_code->str;
937
1008
g_string_free (pairing_code, FALSE);
943
connection_handler_cb (DMAPConnection *connection, guint status, GNode *structure, gpointer user_data)
1014
connection_handler_cb (DMAPConnection * connection, guint status,
1015
GNode * structure, gpointer user_data)
945
1017
gboolean connected;
946
1018
GHashTableIter iter;
958
1030
connected = FALSE;
961
1033
/* Get the pairing-guid to identify this remote in the future. */
963
1035
item = dmap_structure_find_item (structure, DMAP_CC_CMPG);
965
1037
guint64 guid = g_value_get_int64 (&(item->content));
966
pairing_guid = g_strdup_printf ("0x%.16" G_GINT64_MODIFIER "X", guid);
967
g_signal_emit (share, signals [ADD_GUID], 0, pairing_guid);
1040
g_strdup_printf ("0x%.16" G_GINT64_MODIFIER "X",
1042
g_signal_emit (share, signals[ADD_GUID], 0, pairing_guid);
968
1043
g_free (pairing_guid);
971
1046
/* Find the remote that initiated this connection */
972
1047
g_hash_table_iter_init (&iter, share->priv->remotes);
973
while (g_hash_table_iter_next (&iter, &key, &value))
975
if (((DACPRemoteInfo*) value)->connection == connection) {
1048
while (g_hash_table_iter_next (&iter, &key, &value)) {
1049
if (((DACPRemoteInfo *) value)->connection == connection) {
976
1050
service_name = (gchar *) key;
977
remote_info = (DACPRemoteInfo*) value;
1051
remote_info = (DACPRemoteInfo *) value;
987
1061
/* Frees the connection */
988
remote_info->connection = NULL;
1062
remote_info->connection = NULL;
989
1063
g_object_unref (connection);
991
1065
/* FIXME: Send more detailed error info, such as wrong pair code, etc */
992
g_signal_emit (share, signals [REMOTE_PAIRED], 0, service_name, connected);
1066
g_signal_emit (share, signals[REMOTE_PAIRED], 0, service_name,
996
dacp_share_pair (DACPShare *share, gchar *service_name, gchar passcode[4])
1071
dacp_share_pair (DACPShare * share, gchar * service_name, gchar passcode[4])
998
1073
gchar *pairing_code;
1001
1076
DACPRemoteInfo *remote_info;
1003
1078
remote_info = g_hash_table_lookup (share->priv->remotes,
1006
1081
if (remote_info == NULL) {
1007
1082
g_warning ("Remote %s not found.", service_name);
1012
1087
g_warning ("Already pairing remote %s.", service_name);
1016
1091
g_object_get (share, "name", &name, NULL);
1018
remote_info->connection = dmap_connection_new (name,
1093
remote_info->connection = dacp_connection_new (name,
1024
1097
/* This is required since we don't call DMAPConnection default handler */
1025
1098
dmap_connection_setup (remote_info->connection);
1027
1100
/* Get the remote path for pairing */
1028
pairing_code = dacp_share_pairing_code (share, remote_info->pair_txt, passcode);
1029
path = g_strdup_printf ("/pair?pairingcode=%s&servicename=%s",
1102
dacp_share_pairing_code (share, remote_info->pair_txt,
1104
path = g_strdup_printf ("/pair?pairingcode=%s&servicename=%s",
1105
pairing_code, name);
1032
1106
g_free (pairing_code);
1034
g_debug ("Pairing remote in %s:%d/%s", remote_info->host, remote_info->port, path);
1108
g_debug ("Pairing remote in %s:%d/%s", remote_info->host,
1109
remote_info->port, path);
1036
1111
/* Let DMAPConnection do the heavy work */
1037
dmap_connection_get (remote_info->connection, path, FALSE, connection_handler_cb, share);
1112
dmap_connection_get (remote_info->connection, path, FALSE,
1113
connection_handler_cb, share);