Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / tools / mpris-proxy.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <getopt.h>
34 #include <string.h>
35 #include <inttypes.h>
36
37 #include <dbus/dbus.h>
38 #include <glib.h>
39
40 #include "gdbus/gdbus.h"
41
42 #define BLUEZ_BUS_NAME "org.bluez"
43 #define BLUEZ_PATH "/org/bluez"
44 #define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1"
45 #define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
46 #define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
47 #define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
48 #define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
49 #define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
50 #define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
51 #define MPRIS_INTERFACE "org.mpris.MediaPlayer2"
52 #define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
53 #define MPRIS_TRACKLIST_INTERFACE "org.mpris.MediaPlayer2.TrackList"
54 #define MPRIS_PLAYLISTS_INTERFACE "org.mpris.MediaPlayer2.Playlists"
55 #define MPRIS_PLAYER_PATH "/org/mpris/MediaPlayer2"
56 #define ERROR_INTERFACE "org.mpris.MediaPlayer2.Error"
57
58 static GMainLoop *main_loop;
59 static GDBusProxy *adapter = NULL;
60 static DBusConnection *sys = NULL;
61 static DBusConnection *session = NULL;
62 static GDBusClient *client = NULL;
63 static GSList *players = NULL;
64 static GSList *transports = NULL;
65
66 static gboolean option_version = FALSE;
67 static gboolean option_export = FALSE;
68
69 struct tracklist {
70         GDBusProxy *proxy;
71         GSList *items;
72 };
73
74 struct player {
75         char *bus_name;
76         DBusConnection *conn;
77         GDBusProxy *proxy;
78         GDBusProxy *folder;
79         GDBusProxy *device;
80         GDBusProxy *transport;
81         GDBusProxy *playlist;
82         struct tracklist *tracklist;
83 };
84
85 typedef int (* parse_metadata_func) (DBusMessageIter *iter, const char *key,
86                                                 DBusMessageIter *metadata);
87
88 static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
89                                                                 void *val);
90
91 static void sig_term(int sig)
92 {
93         g_main_loop_quit(main_loop);
94 }
95
96 static DBusMessage *get_all(DBusConnection *conn, const char *name)
97 {
98         DBusMessage *msg, *reply;
99         DBusError err;
100         const char *iface = MPRIS_PLAYER_INTERFACE;
101
102         msg = dbus_message_new_method_call(name, MPRIS_PLAYER_PATH,
103                                         DBUS_INTERFACE_PROPERTIES, "GetAll");
104         if (!msg) {
105                 fprintf(stderr, "Can't allocate new method call\n");
106                 return NULL;
107         }
108
109         dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface,
110                                         DBUS_TYPE_INVALID);
111
112         dbus_error_init(&err);
113
114         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
115
116         dbus_message_unref(msg);
117
118         if (!reply) {
119                 if (dbus_error_is_set(&err)) {
120                         fprintf(stderr, "%s\n", err.message);
121                         dbus_error_free(&err);
122                 }
123                 return NULL;
124         }
125
126         return reply;
127 }
128
129 static void append_variant(DBusMessageIter *iter, int type, void *val)
130 {
131         DBusMessageIter value;
132         char sig[2] = { type, '\0' };
133
134         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
135
136         dbus_message_iter_append_basic(&value, type, val);
137
138         dbus_message_iter_close_container(iter, &value);
139 }
140
141 static void append_array_variant(DBusMessageIter *iter, int type, void *val,
142                                                         int n_elements)
143 {
144         DBusMessageIter variant, array;
145         char type_sig[2] = { type, '\0' };
146         char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
147
148         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
149                                                 array_sig, &variant);
150
151         dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
152                                                 type_sig, &array);
153
154         if (dbus_type_is_fixed(type) == TRUE) {
155                 dbus_message_iter_append_fixed_array(&array, type, val,
156                                                         n_elements);
157         } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
158                 const char ***str_array = val;
159                 int i;
160
161                 for (i = 0; i < n_elements; i++)
162                         dbus_message_iter_append_basic(&array, type,
163                                                         &((*str_array)[i]));
164         }
165
166         dbus_message_iter_close_container(&variant, &array);
167
168         dbus_message_iter_close_container(iter, &variant);
169 }
170
171 static void dict_append_array(DBusMessageIter *dict, const char *key, int type,
172                         void *val, int n_elements)
173 {
174         DBusMessageIter entry;
175
176         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
177                                                 NULL, &entry);
178
179         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
180
181         append_array_variant(&entry, type, val, n_elements);
182
183         dbus_message_iter_close_container(dict, &entry);
184 }
185
186 static void append_basic(DBusMessageIter *base, DBusMessageIter *iter,
187                                                                 int type)
188 {
189         const void *value;
190
191         dbus_message_iter_get_basic(iter, &value);
192         dbus_message_iter_append_basic(base, type, &value);
193 }
194
195 static void append_iter(DBusMessageIter *base, DBusMessageIter *iter);
196 static void append_container(DBusMessageIter *base, DBusMessageIter *iter,
197                                                                 int type)
198 {
199         DBusMessageIter iter_sub, base_sub;
200         char *sig;
201
202         dbus_message_iter_recurse(iter, &iter_sub);
203
204         switch (type) {
205         case DBUS_TYPE_ARRAY:
206         case DBUS_TYPE_VARIANT:
207                 sig = dbus_message_iter_get_signature(&iter_sub);
208                 break;
209         default:
210                 sig = NULL;
211                 break;
212         }
213
214         dbus_message_iter_open_container(base, type, sig, &base_sub);
215
216         if (sig != NULL)
217                 dbus_free(sig);
218
219         append_iter(&base_sub, &iter_sub);
220
221         dbus_message_iter_close_container(base, &base_sub);
222 }
223
224 static void append_iter(DBusMessageIter *base, DBusMessageIter *iter)
225 {
226         int type;
227
228         while ((type = dbus_message_iter_get_arg_type(iter)) !=
229                                                         DBUS_TYPE_INVALID) {
230                 if (dbus_type_is_basic(type))
231                         append_basic(base, iter, type);
232                 else if (dbus_type_is_container(type))
233                         append_container(base, iter, type);
234
235                 dbus_message_iter_next(iter);
236         }
237 }
238
239 static void dict_append_iter(DBusMessageIter *dict, const char *key,
240                                                 DBusMessageIter *iter)
241 {
242         DBusMessageIter entry;
243
244         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
245                                                 NULL, &entry);
246
247         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
248
249         append_iter(&entry, iter);
250
251         dbus_message_iter_close_container(dict, &entry);
252 }
253
254 static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
255                                                 DBusMessageIter *metadata)
256 {
257         if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
258                 return -EINVAL;
259
260         dict_append_iter(metadata, key, entry);
261
262         return 0;
263 }
264
265 static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata,
266                                                 parse_metadata_func func)
267 {
268         DBusMessageIter dict;
269         int ctype;
270
271         ctype = dbus_message_iter_get_arg_type(args);
272         if (ctype != DBUS_TYPE_ARRAY)
273                 return -EINVAL;
274
275         dbus_message_iter_recurse(args, &dict);
276
277         while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
278                                                         DBUS_TYPE_INVALID) {
279                 DBusMessageIter entry;
280                 const char *key;
281
282                 if (ctype != DBUS_TYPE_DICT_ENTRY)
283                         return -EINVAL;
284
285                 dbus_message_iter_recurse(&dict, &entry);
286                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
287                         return -EINVAL;
288
289                 dbus_message_iter_get_basic(&entry, &key);
290                 dbus_message_iter_next(&entry);
291
292                 if (func(&entry, key, metadata) < 0)
293                         return -EINVAL;
294
295                 dbus_message_iter_next(&dict);
296         }
297
298         return 0;
299 }
300
301 static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict,
302                                                 parse_metadata_func func)
303 {
304         DBusMessageIter value, metadata;
305
306         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}",
307                                                                 &value);
308
309         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
310                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
311                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
312                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
313
314         parse_metadata(dict, &metadata, func);
315
316         dbus_message_iter_close_container(&value, &metadata);
317         dbus_message_iter_close_container(iter, &value);
318 }
319
320 static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
321                                                                 void *val)
322 {
323         DBusMessageIter entry;
324
325         if (type == DBUS_TYPE_STRING) {
326                 const char *str = *((const char **) val);
327                 if (str == NULL)
328                         return;
329         }
330
331         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
332                                                         NULL, &entry);
333
334         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
335
336         if (strcasecmp(key, "Metadata") == 0)
337                 append_metadata(&entry, val, parse_metadata_entry);
338         else
339                 append_variant(&entry, type, val);
340
341         dbus_message_iter_close_container(dict, &entry);
342 }
343
344 static char *sender2path(const char *sender)
345 {
346         char *path;
347
348         path = g_strconcat("/", sender, NULL);
349         return g_strdelimit(path, ":.", '_');
350 }
351
352 static void copy_reply(DBusPendingCall *call, void *user_data)
353 {
354         DBusMessage *msg = user_data;
355         DBusMessage *reply = dbus_pending_call_steal_reply(call);
356         DBusMessage *copy;
357         DBusMessageIter args, iter;
358
359         copy = dbus_message_new_method_return(msg);
360         if (copy == NULL) {
361                 dbus_message_unref(reply);
362                 return;
363         }
364
365         dbus_message_iter_init_append(copy, &iter);
366
367         if (!dbus_message_iter_init(reply, &args))
368                 goto done;
369
370         append_iter(&iter, &args);
371
372         dbus_connection_send(sys, copy, NULL);
373
374 done:
375         dbus_message_unref(copy);
376         dbus_message_unref(reply);
377 }
378
379 static DBusHandlerResult player_message(DBusConnection *conn,
380                                                 DBusMessage *msg, void *data)
381 {
382         char *owner = data;
383         DBusMessage *copy;
384         DBusMessageIter args, iter;
385         DBusPendingCall *call;
386
387         dbus_message_iter_init(msg, &args);
388
389         copy = dbus_message_new_method_call(owner,
390                                         MPRIS_PLAYER_PATH,
391                                         dbus_message_get_interface(msg),
392                                         dbus_message_get_member(msg));
393         if (copy == NULL)
394                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
395
396         dbus_message_iter_init_append(copy, &iter);
397         append_iter(&iter, &args);
398
399         if (!dbus_connection_send_with_reply(session, copy, &call, -1))
400                 goto done;
401
402         dbus_message_ref(msg);
403         dbus_pending_call_set_notify(call, copy_reply, msg, NULL);
404         dbus_pending_call_unref(call);
405
406 done:
407         dbus_message_unref(copy);
408
409         return DBUS_HANDLER_RESULT_HANDLED;
410 }
411
412 static struct player *find_player_by_bus_name(const char *name)
413 {
414         GSList *l;
415
416         for (l = players; l; l = l->next) {
417                 struct player *player = l->data;
418
419                 if (strcmp(player->bus_name, name) == 0)
420                         return player;
421         }
422
423         return NULL;
424 }
425
426 static const DBusObjectPathVTable player_table = {
427         .message_function = player_message,
428 };
429
430 static void add_player(DBusConnection *conn, const char *name,
431                                                         const char *sender)
432 {
433         DBusMessage *reply = NULL;
434         DBusMessage *msg;
435         DBusMessageIter iter, args;
436         DBusError err;
437         char *path, *owner;
438         struct player *player;
439
440         if (!adapter)
441                 return;
442
443         player = find_player_by_bus_name(name);
444         if (player == NULL) {
445                 reply = get_all(conn, name);
446                 if (reply == NULL)
447                         return;
448                 dbus_message_iter_init(reply, &args);
449         }
450
451         msg = dbus_message_new_method_call(BLUEZ_BUS_NAME,
452                                         g_dbus_proxy_get_path(adapter),
453                                         BLUEZ_MEDIA_INTERFACE,
454                                         "RegisterPlayer");
455         if (!msg) {
456                 fprintf(stderr, "Can't allocate new method call\n");
457                 return;
458         }
459
460         path = sender2path(sender);
461         dbus_connection_get_object_path_data(sys, path, (void **) &owner);
462
463         if (owner != NULL)
464                 goto done;
465
466         dbus_message_iter_init_append(msg, &iter);
467
468         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
469
470         if (player != NULL) {
471                 if (!g_dbus_get_properties(player->conn,
472                                                 MPRIS_PLAYER_PATH,
473                                                 MPRIS_PLAYER_INTERFACE,
474                                                 &iter))
475                         goto done;
476         } else {
477                 append_iter(&iter, &args);
478                 dbus_message_unref(reply);
479         }
480
481         dbus_error_init(&err);
482
483         owner = strdup(sender);
484
485         if (!dbus_connection_register_object_path(sys, path, &player_table,
486                                                                 owner)) {
487                 fprintf(stderr, "Can't register object path for player\n");
488                 free(owner);
489                 goto done;
490         }
491
492         reply = dbus_connection_send_with_reply_and_block(sys, msg, -1, &err);
493         if (!reply) {
494                 fprintf(stderr, "Can't register player\n");
495                 free(owner);
496                 if (dbus_error_is_set(&err)) {
497                         fprintf(stderr, "%s\n", err.message);
498                         dbus_error_free(&err);
499                 }
500         }
501
502 done:
503         if (reply)
504                 dbus_message_unref(reply);
505         dbus_message_unref(msg);
506         g_free(path);
507 }
508
509 static void remove_player(DBusConnection *conn, const char *sender)
510 {
511         DBusMessage *msg;
512         char *path, *owner;
513
514         if (!adapter)
515                 return;
516
517         path = sender2path(sender);
518         dbus_connection_get_object_path_data(sys, path, (void **) &owner);
519
520         if (owner == NULL) {
521                 g_free(path);
522                 return;
523         }
524
525         msg = dbus_message_new_method_call(BLUEZ_BUS_NAME,
526                                         g_dbus_proxy_get_path(adapter),
527                                         BLUEZ_MEDIA_INTERFACE,
528                                         "UnregisterPlayer");
529         if (!msg) {
530                 fprintf(stderr, "Can't allocate new method call\n");
531                 g_free(path);
532                 return;
533         }
534
535         dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
536                                         DBUS_TYPE_INVALID);
537
538         dbus_connection_send(sys, msg, NULL);
539
540         dbus_connection_unregister_object_path(sys, path);
541
542         dbus_message_unref(msg);
543         g_free(path);
544         g_free(owner);
545 }
546
547 static gboolean player_signal(DBusConnection *conn, DBusMessage *msg,
548                                                                 void *user_data)
549 {
550         DBusMessage *signal;
551         DBusMessageIter iter, args;
552         char *path, *owner;
553
554         dbus_message_iter_init(msg, &iter);
555
556         path = sender2path(dbus_message_get_sender(msg));
557         dbus_connection_get_object_path_data(sys, path, (void **) &owner);
558
559         if (owner == NULL)
560                 goto done;
561
562         signal = dbus_message_new_signal(path, dbus_message_get_interface(msg),
563                                                 dbus_message_get_member(msg));
564         if (signal == NULL) {
565                 fprintf(stderr, "Unable to allocate new %s.%s signal",
566                                                 dbus_message_get_interface(msg),
567                                                 dbus_message_get_member(msg));
568                 goto done;
569         }
570
571         dbus_message_iter_init_append(signal, &args);
572
573         append_iter(&args, &iter);
574
575         dbus_connection_send(sys, signal, NULL);
576         dbus_message_unref(signal);
577
578 done:
579         g_free(path);
580
581         return TRUE;
582 }
583
584 static gboolean name_owner_changed(DBusConnection *conn,
585                                                 DBusMessage *msg, void *data)
586 {
587         const char *name, *old, *new;
588
589         if (!dbus_message_get_args(msg, NULL,
590                                         DBUS_TYPE_STRING, &name,
591                                         DBUS_TYPE_STRING, &old,
592                                         DBUS_TYPE_STRING, &new,
593                                         DBUS_TYPE_INVALID)) {
594                 fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
595                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
596         }
597
598         if (!g_str_has_prefix(name, "org.mpris"))
599                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
600
601         if (*new == '\0') {
602                 printf("player %s at %s disappear\n", name, old);
603                 remove_player(conn, old);
604         } else if (option_export || find_player_by_bus_name(name) == NULL) {
605                 printf("player %s at %s found\n", name, new);
606                 add_player(conn, name, new);
607         }
608
609         return TRUE;
610 }
611
612 static char *get_name_owner(DBusConnection *conn, const char *name)
613 {
614         DBusMessage *msg, *reply;
615         DBusError err;
616         char *owner;
617
618         msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
619                                         DBUS_INTERFACE_DBUS, "GetNameOwner");
620
621         if (!msg) {
622                 fprintf(stderr, "Can't allocate new method call\n");
623                 return NULL;
624         }
625
626         dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
627                                                         DBUS_TYPE_INVALID);
628
629         dbus_error_init(&err);
630
631         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
632
633         dbus_message_unref(msg);
634
635         if (!reply) {
636                 if (dbus_error_is_set(&err)) {
637                         fprintf(stderr, "%s\n", err.message);
638                         dbus_error_free(&err);
639                 }
640                 return NULL;
641         }
642
643         if (!dbus_message_get_args(reply, NULL,
644                                         DBUS_TYPE_STRING, &owner,
645                                         DBUS_TYPE_INVALID)) {
646                 dbus_message_unref(reply);
647                 return NULL;
648         }
649
650         owner = g_strdup(owner);
651
652         dbus_message_unref(reply);
653
654         dbus_connection_flush(conn);
655
656         return owner;
657 }
658
659 static void parse_list_names(DBusConnection *conn, DBusMessageIter *args)
660 {
661         DBusMessageIter array;
662         int ctype;
663
664         ctype = dbus_message_iter_get_arg_type(args);
665         if (ctype != DBUS_TYPE_ARRAY)
666                 return;
667
668         dbus_message_iter_recurse(args, &array);
669
670         while ((ctype = dbus_message_iter_get_arg_type(&array)) !=
671                                                         DBUS_TYPE_INVALID) {
672                 const char *name;
673                 char *owner;
674
675                 if (ctype != DBUS_TYPE_STRING)
676                         goto next;
677
678                 dbus_message_iter_get_basic(&array, &name);
679
680                 if (!g_str_has_prefix(name, "org.mpris"))
681                         goto next;
682
683                 owner = get_name_owner(conn, name);
684
685                 if (owner == NULL)
686                         goto next;
687
688                 printf("player %s at %s found\n", name, owner);
689
690                 add_player(conn, name, owner);
691
692                 g_free(owner);
693 next:
694                 dbus_message_iter_next(&array);
695         }
696 }
697
698 static void list_names(DBusConnection *conn)
699 {
700         DBusMessage *msg, *reply;
701         DBusMessageIter iter;
702         DBusError err;
703
704         msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
705                                         DBUS_INTERFACE_DBUS, "ListNames");
706
707         if (!msg) {
708                 fprintf(stderr, "Can't allocate new method call\n");
709                 return;
710         }
711
712         dbus_error_init(&err);
713
714         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
715
716         dbus_message_unref(msg);
717
718         if (!reply) {
719                 if (dbus_error_is_set(&err)) {
720                         fprintf(stderr, "%s\n", err.message);
721                         dbus_error_free(&err);
722                 }
723                 return;
724         }
725
726         dbus_message_iter_init(reply, &iter);
727
728         parse_list_names(conn, &iter);
729
730         dbus_message_unref(reply);
731
732         dbus_connection_flush(conn);
733 }
734
735 static void usage(void)
736 {
737         printf("Bluetooth mpris-player ver %s\n\n", VERSION);
738
739         printf("Usage:\n");
740 }
741
742 static GOptionEntry options[] = {
743         { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
744                                 "Show version information and exit" },
745         { "export", 'e', 0, G_OPTION_ARG_NONE, &option_export,
746                                 "Export remote players" },
747         { NULL },
748 };
749
750 static void connect_handler(DBusConnection *connection, void *user_data)
751 {
752         printf("org.bluez appeared\n");
753 }
754
755 static void disconnect_handler(DBusConnection *connection, void *user_data)
756 {
757         printf("org.bluez disappeared\n");
758 }
759
760 static void unregister_tracklist(struct player *player)
761 {
762         struct tracklist *tracklist = player->tracklist;
763
764         g_slist_free(tracklist->items);
765         g_dbus_proxy_unref(tracklist->proxy);
766         g_free(tracklist);
767         player->tracklist = NULL;
768 }
769
770 static void player_free(void *data)
771 {
772         struct player *player = data;
773
774         if (player->tracklist != NULL)
775                 unregister_tracklist(player);
776
777         if (player->conn) {
778                 dbus_connection_close(player->conn);
779                 dbus_connection_unref(player->conn);
780         }
781
782         g_dbus_proxy_unref(player->device);
783         g_dbus_proxy_unref(player->proxy);
784
785         if (player->transport)
786                 g_dbus_proxy_unref(player->transport);
787
788         if (player->playlist)
789                 g_dbus_proxy_unref(player->playlist);
790
791         g_free(player->bus_name);
792         g_free(player);
793 }
794
795 struct pending_call {
796         struct player *player;
797         DBusMessage *msg;
798 };
799
800 static void pending_call_free(void *data)
801 {
802         struct pending_call *p = data;
803
804         if (p->msg)
805                 dbus_message_unref(p->msg);
806
807         g_free(p);
808 }
809
810 static void player_reply(DBusMessage *message, void *user_data)
811 {
812         struct pending_call *p = user_data;
813         struct player *player = p->player;
814         DBusMessage *msg = p->msg;
815         DBusMessage *reply;
816         DBusError err;
817
818         dbus_error_init(&err);
819         if (dbus_set_error_from_message(&err, message)) {
820                 fprintf(stderr, "error: %s", err.name);
821                 reply = g_dbus_create_error(msg, err.name, "%s", err.message);
822                 dbus_error_free(&err);
823         } else
824                 reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
825
826         g_dbus_send_message(player->conn, reply);
827 }
828
829 static void player_control(struct player *player, DBusMessage *msg,
830                                                         const char *name)
831 {
832         struct pending_call *p;
833
834         p = g_new0(struct pending_call, 1);
835         p->player = player;
836         p->msg = dbus_message_ref(msg);
837
838         g_dbus_proxy_method_call(player->proxy, name, NULL, player_reply,
839                                                 p, pending_call_free);
840 }
841
842 static const char *status_to_playback(const char *status)
843 {
844         if (strcasecmp(status, "playing") == 0)
845                 return "Playing";
846         else if (strcasecmp(status, "paused") == 0)
847                 return "Paused";
848         else
849                 return "Stopped";
850 }
851
852 static const char *player_get_status(struct player *player)
853 {
854         const char *status;
855         DBusMessageIter value;
856
857         if (g_dbus_proxy_get_property(player->proxy, "Status", &value)) {
858                 dbus_message_iter_get_basic(&value, &status);
859                 return status_to_playback(status);
860         }
861
862         if (player->transport == NULL)
863                 goto done;
864
865         if (!g_dbus_proxy_get_property(player->transport, "State", &value))
866                 goto done;
867
868         dbus_message_iter_get_basic(&value, &status);
869
870         if (strcasecmp(status, "active") == 0)
871                 return "Playing";
872
873 done:
874         return "Stopped";
875 }
876
877 static DBusMessage *player_toggle(DBusConnection *conn, DBusMessage *msg,
878                                                                 void *data)
879 {
880         struct player *player = data;
881         const char *status;
882
883         status = player_get_status(player);
884
885         if (strcasecmp(status, "Playing") == 0)
886                 player_control(player, msg, "Pause");
887         else
888                 player_control(player, msg, "Play");
889
890         return NULL;
891 }
892
893 static DBusMessage *player_play(DBusConnection *conn, DBusMessage *msg,
894                                                                 void *data)
895 {
896         struct player *player = data;
897
898         player_control(player, msg, "Play");
899
900         return NULL;
901 }
902
903 static DBusMessage *player_pause(DBusConnection *conn, DBusMessage *msg,
904                                                                 void *data)
905 {
906         struct player *player = data;
907
908         player_control(player, msg, "Pause");
909
910         return NULL;
911 }
912
913 static DBusMessage *player_stop(DBusConnection *conn, DBusMessage *msg,
914                                                                 void *data)
915 {
916         struct player *player = data;
917
918         player_control(player, msg, "Stop");
919
920         return NULL;
921 }
922
923 static DBusMessage *player_next(DBusConnection *conn, DBusMessage *msg,
924                                                                 void *data)
925 {
926         struct player *player = data;
927
928         player_control(player, msg, "Next");
929
930         return NULL;
931 }
932
933 static DBusMessage *player_previous(DBusConnection *conn, DBusMessage *msg,
934                                                                 void *data)
935 {
936         struct player *player = data;
937
938         player_control(player, msg, "Previous");
939
940         return NULL;
941 }
942
943 static gboolean status_exists(const GDBusPropertyTable *property, void *data)
944 {
945         struct player *player = data;
946
947         return player_get_status(player) != NULL;
948 }
949
950 static gboolean get_status(const GDBusPropertyTable *property,
951                                         DBusMessageIter *iter, void *data)
952 {
953         struct player *player = data;
954         const char *status;
955
956         status = player_get_status(player);
957
958         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
959
960         return TRUE;
961 }
962
963 static gboolean repeat_exists(const GDBusPropertyTable *property, void *data)
964 {
965         DBusMessageIter iter;
966         struct player *player = data;
967
968         return g_dbus_proxy_get_property(player->proxy, "Repeat", &iter);
969 }
970
971 static const char *repeat_to_loopstatus(const char *value)
972 {
973         if (strcasecmp(value, "off") == 0)
974                 return "None";
975         else if (strcasecmp(value, "singletrack") == 0)
976                 return "Track";
977         else if (strcasecmp(value, "alltracks") == 0)
978                 return "Playlist";
979
980         return NULL;
981 }
982
983 static gboolean get_repeat(const GDBusPropertyTable *property,
984                                         DBusMessageIter *iter, void *data)
985 {
986         struct player *player = data;
987         DBusMessageIter value;
988         const char *status;
989
990         if (!g_dbus_proxy_get_property(player->proxy, "Repeat", &value))
991                 return FALSE;
992
993         dbus_message_iter_get_basic(&value, &status);
994
995         status = repeat_to_loopstatus(status);
996         if (status == NULL)
997                 return FALSE;
998
999         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
1000
1001         return TRUE;
1002 }
1003
1004 static const char *loopstatus_to_repeat(const char *value)
1005 {
1006         if (strcasecmp(value, "None") == 0)
1007                 return "off";
1008         else if (strcasecmp(value, "Track") == 0)
1009                 return "singletrack";
1010         else if (strcasecmp(value, "Playlist") == 0)
1011                 return "alltracks";
1012
1013         return NULL;
1014 }
1015
1016 static void property_result(const DBusError *err, void *user_data)
1017 {
1018         GDBusPendingPropertySet id = GPOINTER_TO_UINT(user_data);
1019
1020         if (!dbus_error_is_set(err))
1021                 return g_dbus_pending_property_success(id);
1022
1023         g_dbus_pending_property_error(id, err->name, err->message);
1024 }
1025
1026 static void set_repeat(const GDBusPropertyTable *property,
1027                         DBusMessageIter *iter, GDBusPendingPropertySet id,
1028                         void *data)
1029 {
1030         struct player *player = data;
1031         const char *value;
1032
1033         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
1034                 g_dbus_pending_property_error(id,
1035                                         ERROR_INTERFACE ".InvalidArguments",
1036                                         "Invalid arguments in method call");
1037                 return;
1038         }
1039
1040         dbus_message_iter_get_basic(iter, &value);
1041
1042         value = loopstatus_to_repeat(value);
1043         if (value == NULL) {
1044                 g_dbus_pending_property_error(id,
1045                                         ERROR_INTERFACE ".InvalidArguments",
1046                                         "Invalid arguments in method call");
1047                 return;
1048         }
1049
1050         g_dbus_proxy_set_property_basic(player->proxy, "Repeat",
1051                                         DBUS_TYPE_STRING, &value,
1052                                         property_result, GUINT_TO_POINTER(id),
1053                                         NULL);
1054 }
1055
1056 static gboolean get_double(const GDBusPropertyTable *property,
1057                                         DBusMessageIter *iter, void *data)
1058 {
1059         double value = 1.0;
1060
1061         dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
1062
1063         return TRUE;
1064 }
1065
1066 static gboolean shuffle_exists(const GDBusPropertyTable *property, void *data)
1067 {
1068         DBusMessageIter iter;
1069         struct player *player = data;
1070
1071         return g_dbus_proxy_get_property(player->proxy, "Shuffle", &iter);
1072 }
1073
1074 static gboolean get_shuffle(const GDBusPropertyTable *property,
1075                                         DBusMessageIter *iter, void *data)
1076 {
1077         struct player *player = data;
1078         DBusMessageIter value;
1079         const char *string;
1080         dbus_bool_t shuffle;
1081
1082         if (!g_dbus_proxy_get_property(player->proxy, "Shuffle", &value))
1083                 return FALSE;
1084
1085         dbus_message_iter_get_basic(&value, &string);
1086
1087         shuffle = strcmp(string, "off") != 0;
1088
1089         dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &shuffle);
1090
1091         return TRUE;
1092 }
1093
1094 static void set_shuffle(const GDBusPropertyTable *property,
1095                         DBusMessageIter *iter, GDBusPendingPropertySet id,
1096                         void *data)
1097 {
1098         struct player *player = data;
1099         dbus_bool_t shuffle;
1100         const char *value;
1101
1102         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
1103                 g_dbus_pending_property_error(id,
1104                                         ERROR_INTERFACE ".InvalidArguments",
1105                                         "Invalid arguments in method call");
1106                 return;
1107         }
1108
1109         dbus_message_iter_get_basic(iter, &shuffle);
1110         value = shuffle ? "alltracks" : "off";
1111
1112         g_dbus_proxy_set_property_basic(player->proxy, "Shuffle",
1113                                         DBUS_TYPE_STRING, &value,
1114                                         property_result, GUINT_TO_POINTER(id),
1115                                         NULL);
1116 }
1117
1118 static gboolean position_exists(const GDBusPropertyTable *property, void *data)
1119 {
1120         DBusMessageIter iter;
1121         struct player *player = data;
1122
1123         return g_dbus_proxy_get_property(player->proxy, "Position", &iter);
1124 }
1125
1126 static gboolean get_position(const GDBusPropertyTable *property,
1127                                         DBusMessageIter *iter, void *data)
1128 {
1129         struct player *player = data;
1130         DBusMessageIter var;
1131         uint32_t position;
1132         int64_t value;
1133
1134         if (!g_dbus_proxy_get_property(player->proxy, "Position", &var))
1135                 return FALSE;
1136
1137         dbus_message_iter_get_basic(&var, &position);
1138
1139         value = position * 1000ll;
1140
1141         dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &value);
1142
1143         return TRUE;
1144 }
1145
1146 static gboolean track_exists(const GDBusPropertyTable *property, void *data)
1147 {
1148         DBusMessageIter iter;
1149         struct player *player = data;
1150
1151         return g_dbus_proxy_get_property(player->proxy, "Track", &iter);
1152 }
1153
1154 static gboolean parse_string_metadata(DBusMessageIter *iter, const char *key,
1155                                                 DBusMessageIter *metadata)
1156 {
1157         const char *value;
1158
1159         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
1160                 return FALSE;
1161
1162         dbus_message_iter_get_basic(iter, &value);
1163
1164         dict_append_entry(metadata, key, DBUS_TYPE_STRING, &value);
1165
1166         return TRUE;
1167 }
1168
1169 static gboolean parse_array_metadata(DBusMessageIter *iter, const char *key,
1170                                                 DBusMessageIter *metadata)
1171 {
1172         char **value;
1173
1174         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
1175                 return FALSE;
1176
1177         value = dbus_malloc0(sizeof(char *));
1178
1179         dbus_message_iter_get_basic(iter, &(value[0]));
1180
1181         dict_append_array(metadata, key, DBUS_TYPE_STRING, &value, 1);
1182
1183         dbus_free(value);
1184
1185         return TRUE;
1186 }
1187
1188 static gboolean parse_int64_metadata(DBusMessageIter *iter, const char *key,
1189                                                 DBusMessageIter *metadata)
1190 {
1191         uint32_t duration;
1192         int64_t value;
1193
1194         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
1195                 return FALSE;
1196
1197         dbus_message_iter_get_basic(iter, &duration);
1198
1199         value = duration * 1000ll;
1200
1201         dict_append_entry(metadata, key, DBUS_TYPE_INT64, &value);
1202
1203         return TRUE;
1204 }
1205
1206 static gboolean parse_int32_metadata(DBusMessageIter *iter, const char *key,
1207                                                 DBusMessageIter *metadata)
1208 {
1209         uint32_t value;
1210
1211         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
1212                 return FALSE;
1213
1214         dbus_message_iter_get_basic(iter, &value);
1215
1216         dict_append_entry(metadata, key, DBUS_TYPE_INT32, &value);
1217
1218         return TRUE;
1219 }
1220
1221 static gboolean parse_path_metadata(DBusMessageIter *iter, const char *key,
1222                                                 DBusMessageIter *metadata)
1223 {
1224         const char *value;
1225
1226         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH)
1227                 return FALSE;
1228
1229         dbus_message_iter_get_basic(iter, &value);
1230
1231         dict_append_entry(metadata, key, DBUS_TYPE_OBJECT_PATH, &value);
1232
1233         return TRUE;
1234 }
1235
1236 static int parse_track_entry(DBusMessageIter *entry, const char *key,
1237                                                 DBusMessageIter *metadata)
1238 {
1239         DBusMessageIter var;
1240
1241         if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
1242                 return -EINVAL;
1243
1244         dbus_message_iter_recurse(entry, &var);
1245
1246         if (strcasecmp(key, "Title") == 0) {
1247                 if (!parse_string_metadata(&var, "xesam:title", metadata))
1248                         return -EINVAL;
1249         } else if (strcasecmp(key, "Artist") == 0) {
1250                 if (!parse_array_metadata(&var, "xesam:artist", metadata))
1251                         return -EINVAL;
1252         } else if (strcasecmp(key, "Album") == 0) {
1253                 if (!parse_string_metadata(&var, "xesam:album", metadata))
1254                         return -EINVAL;
1255         } else if (strcasecmp(key, "Genre") == 0) {
1256                 if (!parse_array_metadata(&var, "xesam:genre", metadata))
1257                         return -EINVAL;
1258         } else if (strcasecmp(key, "Duration") == 0) {
1259                 if (!parse_int64_metadata(&var, "mpris:length", metadata))
1260                         return -EINVAL;
1261         } else if (strcasecmp(key, "TrackNumber") == 0) {
1262                 if (!parse_int32_metadata(&var, "xesam:trackNumber", metadata))
1263                         return -EINVAL;
1264         } else if (strcasecmp(key, "Item") == 0) {
1265                 if (!parse_path_metadata(&var, "mpris:trackid", metadata))
1266                         return -EINVAL;
1267         }
1268
1269         return 0;
1270 }
1271
1272 static gboolean get_track(const GDBusPropertyTable *property,
1273                                         DBusMessageIter *iter, void *data)
1274 {
1275         struct player *player = data;
1276         DBusMessageIter var, metadata;
1277
1278         if (!g_dbus_proxy_get_property(player->proxy, "Track", &var))
1279                 return FALSE;
1280
1281         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1282                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1283                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
1284                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
1285
1286         parse_metadata(&var, &metadata, parse_track_entry);
1287
1288         dbus_message_iter_close_container(iter, &metadata);
1289
1290         return TRUE;
1291 }
1292
1293 static gboolean get_enable(const GDBusPropertyTable *property,
1294                                         DBusMessageIter *iter, void *data)
1295 {
1296         dbus_bool_t value = TRUE;
1297
1298         dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1299
1300         return TRUE;
1301 }
1302
1303
1304 static gboolean get_volume(const GDBusPropertyTable *property,
1305                                         DBusMessageIter *iter, void *data)
1306 {
1307         struct player *player = data;
1308         double value = 0.0;
1309         uint16_t volume;
1310         DBusMessageIter var;
1311
1312         if (player->transport == NULL)
1313                 goto done;
1314
1315         if (!g_dbus_proxy_get_property(player->transport, "Volume", &var))
1316                 goto done;
1317
1318         dbus_message_iter_get_basic(&var, &volume);
1319
1320         value = (double) volume / 127;
1321
1322 done:
1323         dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
1324
1325         return TRUE;
1326 }
1327
1328 static const GDBusMethodTable player_methods[] = {
1329         { GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
1330         { GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
1331         { GDBUS_ASYNC_METHOD("Pause", NULL, NULL, player_pause) },
1332         { GDBUS_ASYNC_METHOD("Stop", NULL, NULL, player_stop) },
1333         { GDBUS_ASYNC_METHOD("Next", NULL, NULL, player_next) },
1334         { GDBUS_ASYNC_METHOD("Previous", NULL, NULL, player_previous) },
1335         { }
1336 };
1337
1338 static const GDBusSignalTable player_signals[] = {
1339         { GDBUS_SIGNAL("Seeked", GDBUS_ARGS({"Position", "x"})) },
1340         { }
1341 };
1342
1343 static const GDBusPropertyTable player_properties[] = {
1344         { "PlaybackStatus", "s", get_status, NULL, status_exists },
1345         { "LoopStatus", "s", get_repeat, set_repeat, repeat_exists },
1346         { "Rate", "d", get_double, NULL, NULL },
1347         { "MinimumRate", "d", get_double, NULL, NULL },
1348         { "MaximumRate", "d", get_double, NULL, NULL },
1349         { "Shuffle", "b", get_shuffle, set_shuffle, shuffle_exists },
1350         { "Position", "x", get_position, NULL, position_exists },
1351         { "Metadata", "a{sv}", get_track, NULL, track_exists },
1352         { "Volume", "d", get_volume, NULL, NULL },
1353         { "CanGoNext", "b", get_enable, NULL, NULL },
1354         { "CanGoPrevious", "b", get_enable, NULL, NULL },
1355         { "CanPlay", "b", get_enable, NULL, NULL },
1356         { "CanPause", "b", get_enable, NULL, NULL },
1357         { "CanSeek", "b", get_enable, NULL, NULL },
1358         { "CanControl", "b", get_enable, NULL, NULL },
1359         { }
1360 };
1361
1362 static gboolean get_disable(const GDBusPropertyTable *property,
1363                                         DBusMessageIter *iter, void *data)
1364 {
1365         dbus_bool_t value = FALSE;
1366
1367         dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1368
1369         return TRUE;
1370 }
1371
1372 static gboolean get_name(const GDBusPropertyTable *property,
1373                                         DBusMessageIter *iter, void *data)
1374 {
1375         struct player *player = data;
1376         DBusMessageIter var;
1377         const char *alias;
1378         char *name;
1379
1380         if (!g_dbus_proxy_get_property(player->device, "Alias", &var))
1381                 return FALSE;
1382
1383         dbus_message_iter_get_basic(&var, &alias);
1384
1385         if (g_dbus_proxy_get_property(player->proxy, "Name", &var)) {
1386                 dbus_message_iter_get_basic(&var, &name);
1387                 name = g_strconcat(alias, " ", name, NULL);
1388         } else
1389                 name = g_strdup(alias);
1390
1391         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
1392
1393         g_free(name);
1394
1395         return TRUE;
1396 }
1397
1398 static const GDBusMethodTable mpris_methods[] = {
1399         { }
1400 };
1401
1402 static gboolean get_tracklist(const GDBusPropertyTable *property,
1403                                         DBusMessageIter *iter, void *data)
1404 {
1405         struct player *player = data;
1406         dbus_bool_t value;
1407
1408         value = player->tracklist != NULL ? TRUE : FALSE;
1409
1410         dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1411
1412         return TRUE;
1413 }
1414
1415 static const GDBusPropertyTable mpris_properties[] = {
1416         { "CanQuit", "b", get_disable, NULL, NULL },
1417         { "Fullscreen", "b", get_disable, NULL, NULL },
1418         { "CanSetFullscreen", "b", get_disable, NULL, NULL },
1419         { "CanRaise", "b", get_disable, NULL, NULL },
1420         { "HasTrackList", "b", get_tracklist, NULL, NULL },
1421         { "Identity", "s", get_name, NULL, NULL },
1422         { }
1423 };
1424
1425 static GDBusProxy *find_item(struct player *player, const char *path)
1426 {
1427         struct tracklist *tracklist = player->tracklist;
1428         GSList *l;
1429
1430         for (l = tracklist->items; l; l = l->next) {
1431                 GDBusProxy *proxy = l->data;
1432                 const char *p = g_dbus_proxy_get_path(proxy);
1433
1434                 if (g_str_equal(path, p))
1435                         return proxy;
1436         }
1437
1438         return NULL;
1439 }
1440
1441 static void append_item_metadata(void *data, void *user_data)
1442 {
1443         GDBusProxy *item = data;
1444         DBusMessageIter *iter = user_data;
1445         DBusMessageIter var, metadata;
1446         const char *path = g_dbus_proxy_get_path(item);
1447
1448         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1449                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1450                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
1451                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
1452
1453         dict_append_entry(&metadata, "mpris:trackid", DBUS_TYPE_OBJECT_PATH,
1454                                                                         &path);
1455
1456         if (g_dbus_proxy_get_property(item, "Metadata", &var))
1457                 parse_metadata(&var, &metadata, parse_track_entry);
1458
1459         dbus_message_iter_close_container(iter, &metadata);
1460
1461         return;
1462 }
1463
1464 static DBusMessage *tracklist_get_metadata(DBusConnection *conn,
1465                                                 DBusMessage *msg, void *data)
1466 {
1467         struct player *player = data;
1468         DBusMessage *reply;
1469         DBusMessageIter args, array;
1470         GSList *l = NULL;
1471
1472         dbus_message_iter_init(msg, &args);
1473
1474         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
1475                 return g_dbus_create_error(msg,
1476                                         ERROR_INTERFACE ".InvalidArguments",
1477                                         "Invalid Arguments");
1478
1479         dbus_message_iter_recurse(&args, &array);
1480
1481         while (dbus_message_iter_get_arg_type(&array) ==
1482                                                 DBUS_TYPE_OBJECT_PATH) {
1483                 const char *path;
1484                 GDBusProxy *item;
1485
1486                 dbus_message_iter_get_basic(&array, &path);
1487
1488                 item = find_item(player, path);
1489                 if (item == NULL)
1490                         return g_dbus_create_error(msg,
1491                                         ERROR_INTERFACE ".InvalidArguments",
1492                                         "Invalid Arguments");
1493
1494                 l = g_slist_append(l, item);
1495
1496                 dbus_message_iter_next(&array);
1497         }
1498
1499         reply = dbus_message_new_method_return(msg);
1500
1501         dbus_message_iter_init_append(reply, &args);
1502
1503         dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
1504                                         DBUS_TYPE_ARRAY_AS_STRING
1505                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1506                                         DBUS_TYPE_STRING_AS_STRING
1507                                         DBUS_TYPE_VARIANT_AS_STRING
1508                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1509                                         &array);
1510
1511         g_slist_foreach(l, append_item_metadata, &array);
1512
1513         dbus_message_iter_close_container(&args, &array);
1514
1515         return reply;
1516 }
1517
1518 static void item_play_reply(DBusMessage *message, void *user_data)
1519 {
1520         struct pending_call *p = user_data;
1521         struct player *player = p->player;
1522         DBusMessage *msg = p->msg;
1523         DBusMessage *reply;
1524         DBusError err;
1525
1526         dbus_error_init(&err);
1527         if (dbus_set_error_from_message(&err, message)) {
1528                 fprintf(stderr, "error: %s", err.name);
1529                 reply = g_dbus_create_error(msg, err.name, "%s", err.message);
1530                 dbus_error_free(&err);
1531         } else
1532                 reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1533
1534         g_dbus_send_message(player->conn, reply);
1535 }
1536
1537 static void item_play(struct player *player, DBusMessage *msg,
1538                                                         GDBusProxy *item)
1539 {
1540         struct pending_call *p;
1541
1542         p = g_new0(struct pending_call, 1);
1543         p->player = player;
1544         p->msg = dbus_message_ref(msg);
1545
1546         g_dbus_proxy_method_call(item, "Play", NULL, item_play_reply,
1547                                                 p, pending_call_free);
1548 }
1549
1550 static DBusMessage *tracklist_goto(DBusConnection *conn,
1551                                                 DBusMessage *msg, void *data)
1552 {
1553         struct player *player = data;
1554         GDBusProxy *item;
1555         const char *path;
1556
1557         if (!dbus_message_get_args(msg, NULL,
1558                                         DBUS_TYPE_OBJECT_PATH, &path,
1559                                         DBUS_TYPE_INVALID))
1560                 return g_dbus_create_error(msg,
1561                                         ERROR_INTERFACE ".InvalidArguments",
1562                                         "Invalid arguments");
1563
1564         item = find_item(player, path);
1565         if (item == NULL)
1566                 return g_dbus_create_error(msg,
1567                                         ERROR_INTERFACE ".InvalidArguments",
1568                                         "Invalid arguments");
1569
1570         item_play(player, msg, item);
1571
1572         return NULL;
1573 }
1574
1575 static DBusMessage *tracklist_add_track(DBusConnection *conn,
1576                                                 DBusMessage *msg, void *data)
1577 {
1578         return g_dbus_create_error(msg, ERROR_INTERFACE ".NotImplemented",
1579                                         "Not implemented");
1580 }
1581
1582 static DBusMessage *tracklist_remove_track(DBusConnection *conn,
1583                                                 DBusMessage *msg, void *data)
1584 {
1585         return g_dbus_create_error(msg, ERROR_INTERFACE ".NotImplemented",
1586                                         "Not implemented");
1587 }
1588
1589 static const GDBusMethodTable tracklist_methods[] = {
1590         { GDBUS_METHOD("GetTracksMetadata",
1591                         GDBUS_ARGS({ "tracks", "ao" }),
1592                         GDBUS_ARGS({ "metadata", "aa{sv}" }),
1593                         tracklist_get_metadata) },
1594         { GDBUS_METHOD("AddTrack",
1595                         GDBUS_ARGS({ "uri", "s" }, { "after", "o" },
1596                                                 { "current", "b" }),
1597                         NULL,
1598                         tracklist_add_track) },
1599         { GDBUS_METHOD("RemoveTrack",
1600                         GDBUS_ARGS({ "track", "o" }), NULL,
1601                         tracklist_remove_track) },
1602         { GDBUS_ASYNC_METHOD("GoTo",
1603                         GDBUS_ARGS({ "track", "o" }), NULL,
1604                         tracklist_goto) },
1605         { },
1606 };
1607
1608 static const GDBusSignalTable tracklist_signals[] = {
1609         { GDBUS_SIGNAL("TrackAdded", GDBUS_ARGS({"metadata", "a{sv}"},
1610                                                 {"after", "o"})) },
1611         { GDBUS_SIGNAL("TrackRemoved", GDBUS_ARGS({"track", "o"})) },
1612         { GDBUS_SIGNAL("TrackMetadataChanged", GDBUS_ARGS({"track", "o"},
1613                                                 {"metadata", "a{sv}"})) },
1614         { }
1615 };
1616
1617 static gboolean tracklist_exists(const GDBusPropertyTable *property, void *data)
1618 {
1619         struct player *player = data;
1620
1621         return player->tracklist != NULL;
1622 }
1623
1624 static void append_path(gpointer data, gpointer user_data)
1625 {
1626         GDBusProxy *proxy = data;
1627         DBusMessageIter *iter = user_data;
1628         const char *path = g_dbus_proxy_get_path(proxy);
1629
1630         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
1631 }
1632
1633 static gboolean get_tracks(const GDBusPropertyTable *property,
1634                                         DBusMessageIter *iter, void *data)
1635 {
1636         struct player *player = data;
1637         struct tracklist *tracklist = player->tracklist;
1638         DBusMessageIter value;
1639
1640         if (tracklist == NULL)
1641                 return FALSE;
1642
1643         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1644                                         DBUS_TYPE_OBJECT_PATH_AS_STRING,
1645                                         &value);
1646         g_slist_foreach(player->tracklist->items, append_path, &value);
1647         dbus_message_iter_close_container(iter, &value);
1648
1649         return TRUE;
1650 }
1651
1652 static const GDBusPropertyTable tracklist_properties[] = {
1653         { "Tracks", "ao", get_tracks, NULL, tracklist_exists },
1654         { "CanEditTracks", "b", get_disable, NULL, NULL },
1655         { }
1656 };
1657
1658 static void list_items_setup(DBusMessageIter *iter, void *user_data)
1659 {
1660         DBusMessageIter dict;
1661
1662         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1663                                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1664                                         DBUS_TYPE_STRING_AS_STRING
1665                                         DBUS_TYPE_VARIANT_AS_STRING
1666                                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1667                                         &dict);
1668         dbus_message_iter_close_container(iter, &dict);
1669 }
1670
1671 static void change_folder_reply(DBusMessage *message, void *user_data)
1672 {
1673         struct player *player = user_data;
1674         struct tracklist *tracklist = player->tracklist;
1675         DBusError err;
1676
1677         dbus_error_init(&err);
1678         if (dbus_set_error_from_message(&err, message)) {
1679                 fprintf(stderr, "error: %s", err.name);
1680                 return;
1681         }
1682
1683         g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
1684                                                 MPRIS_PLAYLISTS_INTERFACE,
1685                                                 "ActivePlaylist");
1686
1687         g_dbus_proxy_method_call(tracklist->proxy, "ListItems",
1688                                         list_items_setup, NULL, NULL, NULL);
1689 }
1690
1691 static void change_folder_setup(DBusMessageIter *iter, void *user_data)
1692 {
1693         struct player *player = user_data;
1694         const char *path;
1695
1696         path = g_dbus_proxy_get_path(player->playlist);
1697
1698         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
1699 }
1700
1701 static DBusMessage *playlist_activate(DBusConnection *conn,
1702                                                 DBusMessage *msg, void *data)
1703 {
1704         struct player *player = data;
1705         struct tracklist *tracklist = player->tracklist;
1706         const char *path;
1707
1708         if (player->playlist == NULL || tracklist == NULL)
1709                 return g_dbus_create_error(msg,
1710                                         ERROR_INTERFACE ".InvalidArguments",
1711                                         "Invalid Arguments");
1712
1713         if (!dbus_message_get_args(msg, NULL,
1714                                         DBUS_TYPE_OBJECT_PATH, &path,
1715                                         DBUS_TYPE_INVALID))
1716                 return g_dbus_create_error(msg,
1717                                         ERROR_INTERFACE ".InvalidArguments",
1718                                         "Invalid Arguments");
1719
1720         if (!g_str_equal(path, g_dbus_proxy_get_path(player->playlist)))
1721                 return g_dbus_create_error(msg,
1722                                         ERROR_INTERFACE ".InvalidArguments",
1723                                         "Invalid Arguments");
1724
1725         g_dbus_proxy_method_call(tracklist->proxy, "ChangeFolder",
1726                                 change_folder_setup, change_folder_reply,
1727                                 player, NULL);
1728
1729         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1730 }
1731
1732 static DBusMessage *playlist_get(DBusConnection *conn, DBusMessage *msg,
1733                                                                 void *data)
1734 {
1735         struct player *player = data;
1736         uint32_t index, count;
1737         const char *order;
1738         dbus_bool_t reverse;
1739         DBusMessage *reply;
1740         DBusMessageIter iter, entry, value, name;
1741         const char *string, *path;
1742         const char *empty = "";
1743
1744         if (player->playlist == NULL)
1745                 return g_dbus_create_error(msg,
1746                                         ERROR_INTERFACE ".InvalidArguments",
1747                                         "Invalid Arguments");
1748
1749         if (!dbus_message_get_args(msg, NULL,
1750                                         DBUS_TYPE_UINT32, &index,
1751                                         DBUS_TYPE_UINT32, &count,
1752                                         DBUS_TYPE_STRING, &order,
1753                                         DBUS_TYPE_BOOLEAN, &reverse,
1754                                         DBUS_TYPE_INVALID))
1755                 return g_dbus_create_error(msg,
1756                                         ERROR_INTERFACE ".InvalidArguments",
1757                                         "Invalid Arguments");
1758
1759         path = g_dbus_proxy_get_path(player->playlist);
1760
1761         reply = dbus_message_new_method_return(msg);
1762
1763         dbus_message_iter_init_append(reply, &iter);
1764
1765         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(oss)",
1766                                                                 &entry);
1767         dbus_message_iter_open_container(&entry, DBUS_TYPE_STRUCT, NULL,
1768                                                                 &value);
1769         dbus_message_iter_append_basic(&value, DBUS_TYPE_OBJECT_PATH, &path);
1770         if (g_dbus_proxy_get_property(player->playlist, "Name", &name)) {
1771                 dbus_message_iter_get_basic(&name, &string);
1772                 dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING,
1773                                                                 &string);
1774         } else {
1775                 dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING,
1776                                                                 &path);
1777         }
1778         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &empty);
1779         dbus_message_iter_close_container(&entry, &value);
1780         dbus_message_iter_close_container(&iter, &entry);
1781
1782         return reply;
1783 }
1784
1785 static const GDBusMethodTable playlist_methods[] = {
1786         { GDBUS_METHOD("ActivatePlaylist",
1787                         GDBUS_ARGS({ "playlist", "o" }), NULL,
1788                         playlist_activate) },
1789         { GDBUS_METHOD("GetPlaylists",
1790                         GDBUS_ARGS({ "index", "u" }, { "maxcount", "u"},
1791                                         { "order", "s" }, { "reverse", "b" }),
1792                         GDBUS_ARGS({ "playlists", "a(oss)"}),
1793                         playlist_get) },
1794         { },
1795 };
1796
1797 static gboolean playlist_exists(const GDBusPropertyTable *property, void *data)
1798 {
1799         struct player *player = data;
1800
1801         return player->playlist != NULL;
1802 }
1803
1804 static gboolean get_playlist_count(const GDBusPropertyTable *property,
1805                                         DBusMessageIter *iter, void *data)
1806 {
1807         struct player *player = data;
1808         uint32_t count = 1;
1809
1810         if (player->playlist == NULL)
1811                 return FALSE;
1812
1813         dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &count);
1814
1815         return TRUE;
1816 }
1817
1818 static gboolean get_orderings(const GDBusPropertyTable *property,
1819                                         DBusMessageIter *iter, void *data)
1820 {
1821         DBusMessageIter value;
1822         const char *order = "User";
1823
1824         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1825                                         DBUS_TYPE_OBJECT_PATH_AS_STRING,
1826                                         &value);
1827         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &order);
1828         dbus_message_iter_close_container(iter, &value);
1829
1830         return TRUE;
1831 }
1832
1833 static gboolean get_active_playlist(const GDBusPropertyTable *property,
1834                                         DBusMessageIter *iter, void *data)
1835 {
1836         struct player *player = data;
1837         DBusMessageIter value, entry;
1838         dbus_bool_t enabled = TRUE;
1839         const char *path, *empty = "";
1840
1841         if (player->playlist == NULL)
1842                 return FALSE;
1843
1844         path = g_dbus_proxy_get_path(player->playlist);
1845
1846         dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
1847                                                         NULL, &value);
1848         dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &enabled);
1849         dbus_message_iter_open_container(&value, DBUS_TYPE_STRUCT, NULL,
1850                                                                 &entry);
1851         dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path);
1852         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &path);
1853         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &empty);
1854         dbus_message_iter_close_container(&value, &entry);
1855         dbus_message_iter_close_container(iter, &value);
1856
1857         return TRUE;
1858 }
1859
1860 static const GDBusPropertyTable playlist_properties[] = {
1861         { "PlaylistCount", "u", get_playlist_count, NULL, playlist_exists },
1862         { "Orderings", "as", get_orderings, NULL, NULL },
1863         { "ActivePlaylist", "(b(oss))", get_active_playlist, NULL,
1864                                                         playlist_exists },
1865         { }
1866 };
1867
1868 #define a_z "abcdefghijklmnopqrstuvwxyz"
1869 #define A_Z "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1870 #define _0_9 "_0123456789"
1871
1872 static char *mpris_busname(char *name)
1873 {
1874         if (g_ascii_isdigit(name[0]))
1875                 return g_strconcat(MPRIS_BUS_NAME, "bt_",
1876                                 g_strcanon(name, A_Z a_z _0_9, '_'), NULL);
1877         else
1878                 return g_strconcat(MPRIS_BUS_NAME,
1879                                 g_strcanon(name, A_Z a_z _0_9, '_'), NULL);
1880 }
1881
1882 static GDBusProxy *find_transport_by_path(const char *path)
1883 {
1884         GSList *l;
1885
1886         for (l = transports; l; l = l->next) {
1887                 GDBusProxy *transport = l->data;
1888                 DBusMessageIter iter;
1889                 const char *value;
1890
1891                 if (!g_dbus_proxy_get_property(transport, "Device", &iter))
1892                         continue;
1893
1894                 dbus_message_iter_get_basic(&iter, &value);
1895
1896                 if (strcmp(path, value) == 0)
1897                         return transport;
1898         }
1899
1900         return NULL;
1901 }
1902
1903 static struct player *find_player(GDBusProxy *proxy)
1904 {
1905         GSList *l;
1906
1907         for (l = players; l; l = l->next) {
1908                 struct player *player = l->data;
1909                 const char *path, *p;
1910
1911                 if (player->proxy == proxy)
1912                         return player;
1913
1914                 path = g_dbus_proxy_get_path(proxy);
1915                 p = g_dbus_proxy_get_path(player->proxy);
1916                 if (g_str_equal(path, p))
1917                         return player;
1918         }
1919
1920         return NULL;
1921 }
1922
1923 static void register_tracklist(GDBusProxy *proxy)
1924 {
1925         struct player *player;
1926         struct tracklist *tracklist;
1927
1928         player = find_player(proxy);
1929         if (player == NULL)
1930                 return;
1931
1932         if (player->tracklist != NULL)
1933                 return;
1934
1935         tracklist = g_new0(struct tracklist, 1);
1936         tracklist->proxy = g_dbus_proxy_ref(proxy);
1937
1938         player->tracklist = tracklist;
1939
1940         g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
1941                                                 MPRIS_INTERFACE,
1942                                                 "HasTrackList");
1943
1944         if (player->playlist == NULL)
1945                 return;
1946
1947         g_dbus_proxy_method_call(player->tracklist->proxy, "ChangeFolder",
1948                                 change_folder_setup, change_folder_reply,
1949                                 player, NULL);
1950 }
1951
1952 static void register_player(GDBusProxy *proxy)
1953 {
1954         struct player *player;
1955         DBusMessageIter iter;
1956         const char *path, *alias, *name;
1957         char *busname;
1958         GDBusProxy *device, *transport;
1959
1960         if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
1961                 return;
1962
1963         dbus_message_iter_get_basic(&iter, &path);
1964
1965         device = g_dbus_proxy_new(client, path, "org.bluez.Device1");
1966         if (device == NULL)
1967                 return;
1968
1969         if (!g_dbus_proxy_get_property(device, "Alias", &iter))
1970                 return;
1971
1972         dbus_message_iter_get_basic(&iter, &alias);
1973
1974         if (g_dbus_proxy_get_property(proxy, "Name", &iter)) {
1975                 dbus_message_iter_get_basic(&iter, &name);
1976                 busname = g_strconcat(alias, " ", name, NULL);
1977         } else
1978                 busname = g_strdup(alias);
1979
1980         player = g_new0(struct player, 1);
1981         player->bus_name = mpris_busname(busname);
1982         player->proxy = g_dbus_proxy_ref(proxy);
1983         player->device = device;
1984
1985         g_free(busname);
1986
1987         players = g_slist_prepend(players, player);
1988
1989         printf("Player %s created\n", player->bus_name);
1990
1991         player->conn = g_dbus_setup_private(DBUS_BUS_SESSION, player->bus_name,
1992                                                                         NULL);
1993         if (!session) {
1994                 fprintf(stderr, "Could not register bus name %s",
1995                                                         player->bus_name);
1996                 goto fail;
1997         }
1998
1999         if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
2000                                                 MPRIS_INTERFACE,
2001                                                 mpris_methods,
2002                                                 NULL,
2003                                                 mpris_properties,
2004                                                 player, NULL)) {
2005                 fprintf(stderr, "Could not register interface %s",
2006                                                 MPRIS_INTERFACE);
2007                 goto fail;
2008         }
2009
2010         if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
2011                                                 MPRIS_PLAYER_INTERFACE,
2012                                                 player_methods,
2013                                                 player_signals,
2014                                                 player_properties,
2015                                                 player, player_free)) {
2016                 fprintf(stderr, "Could not register interface %s",
2017                                                 MPRIS_PLAYER_INTERFACE);
2018                 goto fail;
2019         }
2020
2021         if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
2022                                                 MPRIS_TRACKLIST_INTERFACE,
2023                                                 tracklist_methods,
2024                                                 tracklist_signals,
2025                                                 tracklist_properties,
2026                                                 player, NULL)) {
2027                 fprintf(stderr, "Could not register interface %s",
2028                                                 MPRIS_TRACKLIST_INTERFACE);
2029                 goto fail;
2030         }
2031
2032         if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
2033                                                 MPRIS_PLAYLISTS_INTERFACE,
2034                                                 playlist_methods,
2035                                                 NULL,
2036                                                 playlist_properties,
2037                                                 player, NULL)) {
2038                 fprintf(stderr, "Could not register interface %s",
2039                                                 MPRIS_PLAYLISTS_INTERFACE);
2040                 goto fail;
2041         }
2042
2043         transport = find_transport_by_path(path);
2044         if (transport)
2045                 player->transport = g_dbus_proxy_ref(transport);
2046
2047         return;
2048
2049 fail:
2050         players = g_slist_remove(players, player);
2051         player_free(player);
2052 }
2053
2054 static struct player *find_player_by_device(const char *device)
2055 {
2056         GSList *l;
2057
2058         for (l = players; l; l = l->next) {
2059                 struct player *player = l->data;
2060                 const char *path = g_dbus_proxy_get_path(player->device);
2061
2062                 if (g_strcmp0(device, path) == 0)
2063                         return player;
2064         }
2065
2066         return NULL;
2067 }
2068
2069 static void register_transport(GDBusProxy *proxy)
2070 {
2071         struct player *player;
2072         DBusMessageIter iter;
2073         const char *path;
2074
2075         if (g_slist_find(transports, proxy) != NULL)
2076                 return;
2077
2078         if (!g_dbus_proxy_get_property(proxy, "Volume", &iter))
2079                 return;
2080
2081         if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
2082                 return;
2083
2084         dbus_message_iter_get_basic(&iter, &path);
2085
2086         transports = g_slist_append(transports, proxy);
2087
2088         player = find_player_by_device(path);
2089         if (player == NULL || player->transport != NULL)
2090                 return;
2091
2092         player->transport = g_dbus_proxy_ref(proxy);
2093 }
2094
2095 static struct player *find_player_by_item(const char *item)
2096 {
2097         GSList *l;
2098
2099         for (l = players; l; l = l->next) {
2100                 struct player *player = l->data;
2101                 const char *path = g_dbus_proxy_get_path(player->proxy);
2102
2103                 if (g_str_has_prefix(item, path))
2104                         return player;
2105         }
2106
2107         return NULL;
2108 }
2109
2110 static void register_playlist(struct player *player, GDBusProxy *proxy)
2111 {
2112         const char *path;
2113         DBusMessageIter iter;
2114
2115         if (!g_dbus_proxy_get_property(player->proxy, "Playlist", &iter))
2116                 return;
2117
2118         dbus_message_iter_get_basic(&iter, &path);
2119
2120         if (!g_str_equal(path, g_dbus_proxy_get_path(proxy)))
2121                 return;
2122
2123         player->playlist = g_dbus_proxy_ref(proxy);
2124
2125         g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
2126                                                 MPRIS_PLAYLISTS_INTERFACE,
2127                                                 "PlaylistCount");
2128
2129         if (player->tracklist == NULL)
2130                 return;
2131
2132         g_dbus_proxy_method_call(player->tracklist->proxy, "ChangeFolder",
2133                                 change_folder_setup, change_folder_reply,
2134                                 player, NULL);
2135 }
2136
2137 static void register_item(struct player *player, GDBusProxy *proxy)
2138 {
2139         struct tracklist *tracklist;
2140         const char *path, *playlist;
2141         DBusMessage *signal;
2142         DBusMessageIter iter, args, metadata;
2143         GSList *l;
2144         GDBusProxy *after;
2145
2146         if (player->playlist == NULL) {
2147                 register_playlist(player, proxy);
2148                 return;
2149         }
2150
2151         tracklist = player->tracklist;
2152         if (tracklist == NULL)
2153                 return;
2154
2155         path = g_dbus_proxy_get_path(proxy);
2156         playlist = g_dbus_proxy_get_path(player->playlist);
2157         if (!g_str_has_prefix(path, playlist))
2158                 return;
2159
2160         l = g_slist_last(tracklist->items);
2161         tracklist->items = g_slist_append(tracklist->items, proxy);
2162
2163         g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
2164                                                 MPRIS_TRACKLIST_INTERFACE,
2165                                                 "Tracks");
2166
2167         if (l == NULL)
2168                 return;
2169
2170         signal = dbus_message_new_signal(MPRIS_PLAYER_PATH,
2171                                         MPRIS_TRACKLIST_INTERFACE,
2172                                         "TrackAdded");
2173         if (!signal) {
2174                 fprintf(stderr, "Unable to allocate new %s.TrackAdded signal",
2175                                                 MPRIS_TRACKLIST_INTERFACE);
2176                 return;
2177         }
2178
2179         dbus_message_iter_init_append(signal, &args);
2180
2181         if (!g_dbus_proxy_get_property(proxy, "Metadata", &iter)) {
2182                 dbus_message_unref(signal);
2183                 return;
2184         }
2185
2186         dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
2187                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2188                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
2189                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
2190
2191         parse_metadata(&iter, &metadata, parse_track_entry);
2192
2193         dbus_message_iter_close_container(&args, &metadata);
2194
2195         after = l->data;
2196         path = g_dbus_proxy_get_path(after);
2197         dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &path);
2198
2199         g_dbus_send_message(player->conn, signal);
2200 }
2201
2202 static void proxy_added(GDBusProxy *proxy, void *user_data)
2203 {
2204         const char *interface;
2205         const char *path;
2206
2207         interface = g_dbus_proxy_get_interface(proxy);
2208         path = g_dbus_proxy_get_path(proxy);
2209
2210         if (!strcmp(interface, BLUEZ_ADAPTER_INTERFACE)) {
2211                 if (adapter != NULL)
2212                         return;
2213
2214                 printf("Bluetooth Adapter %s found\n", path);
2215                 adapter = proxy;
2216                 list_names(session);
2217         } else if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) {
2218                 printf("Bluetooth Player %s found\n", path);
2219                 register_player(proxy);
2220         } else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
2221                 printf("Bluetooth Transport %s found\n", path);
2222                 register_transport(proxy);
2223         } else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE)) {
2224                 printf("Bluetooth Folder %s found\n", path);
2225                 register_tracklist(proxy);
2226         } else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE)) {
2227                 struct player *player;
2228
2229                 player = find_player_by_item(path);
2230                 if (player == NULL)
2231                         return;
2232
2233                 printf("Bluetooth Item %s found\n", path);
2234                 register_item(player, proxy);
2235         }
2236 }
2237
2238 static void unregister_player(struct player *player)
2239 {
2240         players = g_slist_remove(players, player);
2241
2242         if (player->tracklist != NULL) {
2243                 g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
2244                                                 MPRIS_PLAYLISTS_INTERFACE);
2245                 g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
2246                                                 MPRIS_TRACKLIST_INTERFACE);
2247         }
2248
2249         g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
2250                                                 MPRIS_INTERFACE);
2251
2252         g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
2253                                                 MPRIS_PLAYER_INTERFACE);
2254 }
2255
2256 static struct player *find_player_by_transport(GDBusProxy *proxy)
2257 {
2258         GSList *l;
2259
2260         for (l = players; l; l = l->next) {
2261                 struct player *player = l->data;
2262
2263                 if (player->transport == proxy)
2264                         return player;
2265         }
2266
2267         return NULL;
2268 }
2269
2270 static void unregister_transport(GDBusProxy *proxy)
2271 {
2272         struct player *player;
2273
2274         if (g_slist_find(transports, proxy) == NULL)
2275                 return;
2276
2277         transports = g_slist_remove(transports, proxy);
2278
2279         player = find_player_by_transport(proxy);
2280         if (player == NULL)
2281                 return;
2282
2283         g_dbus_proxy_unref(player->transport);
2284         player->transport = NULL;
2285 }
2286
2287 static void unregister_item(struct player *player, GDBusProxy *proxy)
2288 {
2289         struct tracklist *tracklist = player->tracklist;
2290         const char *path;
2291
2292         if (tracklist == NULL)
2293                 return;
2294
2295         if (g_slist_find(tracklist->items, proxy) == NULL)
2296                 return;
2297
2298         path = g_dbus_proxy_get_path(proxy);
2299
2300         tracklist->items = g_slist_remove(tracklist->items, proxy);
2301
2302         g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
2303                                                 MPRIS_TRACKLIST_INTERFACE,
2304                                                 "Tracks");
2305
2306         g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH,
2307                                 MPRIS_TRACKLIST_INTERFACE, "TrackRemoved",
2308                                 DBUS_TYPE_OBJECT_PATH, &path,
2309                                 DBUS_TYPE_INVALID);
2310 }
2311
2312 static void remove_players(DBusConnection *conn)
2313 {
2314         char **paths;
2315         int i;
2316
2317         dbus_connection_list_registered(conn, "/", &paths);
2318
2319         for (i = 0; paths[i]; i++) {
2320                 char *path;
2321                 void *data;
2322
2323                 path = g_strdup_printf("/%s", paths[i]);
2324                 dbus_connection_get_object_path_data(sys, path, &data);
2325                 dbus_connection_unregister_object_path(sys, path);
2326
2327                 g_free(path);
2328                 g_free(data);
2329         }
2330
2331         dbus_free_string_array(paths);
2332 }
2333
2334 static void proxy_removed(GDBusProxy *proxy, void *user_data)
2335 {
2336         const char *interface;
2337         const char *path;
2338
2339         if (adapter == NULL)
2340                 return;
2341
2342         interface = g_dbus_proxy_get_interface(proxy);
2343         path = g_dbus_proxy_get_path(proxy);
2344
2345         if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0) {
2346                 if (adapter != proxy)
2347                         return;
2348                 printf("Bluetooth Adapter %s removed\n", path);
2349                 adapter = NULL;
2350                 remove_players(sys);
2351         } else if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0) {
2352                 struct player *player;
2353
2354                 player = find_player(proxy);
2355                 if (player == NULL)
2356                         return;
2357
2358                 printf("Bluetooth Player %s removed\n", path);
2359                 unregister_player(player);
2360         } else if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) {
2361                 printf("Bluetooth Transport %s removed\n", path);
2362                 unregister_transport(proxy);
2363         } else if (strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE) == 0) {
2364                 struct player *player;
2365
2366                 player = find_player_by_item(path);
2367                 if (player == NULL)
2368                         return;
2369
2370                 printf("Bluetooth Item %s removed\n", path);
2371                 unregister_item(player, proxy);
2372         }
2373 }
2374
2375 static const char *property_to_mpris(const char *property)
2376 {
2377         if (strcasecmp(property, "Repeat") == 0)
2378                 return "LoopStatus";
2379         else if (strcasecmp(property, "Shuffle") == 0)
2380                 return "Shuffle";
2381         else if (strcasecmp(property, "Status") == 0)
2382                 return "PlaybackStatus";
2383         else if (strcasecmp(property, "Position") == 0)
2384                 return "Position";
2385         else if (strcasecmp(property, "Track") == 0)
2386                 return "Metadata";
2387
2388         return NULL;
2389 }
2390
2391 static void player_property_changed(GDBusProxy *proxy, const char *name,
2392                                         DBusMessageIter *iter, void *user_data)
2393 {
2394         struct player *player;
2395         const char *property;
2396         uint32_t position;
2397         uint64_t value;
2398
2399         player = find_player(proxy);
2400         if (player == NULL)
2401                 return;
2402
2403         property = property_to_mpris(name);
2404         if (property == NULL)
2405                 return;
2406
2407         g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
2408                                                 MPRIS_PLAYER_INTERFACE,
2409                                                 property);
2410
2411         if (strcasecmp(name, "Position") != 0)
2412                 return;
2413
2414         dbus_message_iter_get_basic(iter, &position);
2415
2416         value = position * 1000ll;
2417
2418         g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH,
2419                                         MPRIS_PLAYER_INTERFACE, "Seeked",
2420                                         DBUS_TYPE_INT64, &value,
2421                                         DBUS_TYPE_INVALID);
2422 }
2423
2424 static void transport_property_changed(GDBusProxy *proxy, const char *name,
2425                                         DBusMessageIter *iter, void *user_data)
2426 {
2427         struct player *player;
2428         DBusMessageIter var;
2429         const char *path;
2430
2431         if (strcasecmp(name, "Volume") != 0 && strcasecmp(name, "State") != 0)
2432                 return;
2433
2434         if (!g_dbus_proxy_get_property(proxy, "Device", &var))
2435                 return;
2436
2437         dbus_message_iter_get_basic(&var, &path);
2438
2439         player = find_player_by_device(path);
2440         if (player == NULL)
2441                 return;
2442
2443         if (strcasecmp(name, "State") == 0) {
2444                 if (!g_dbus_proxy_get_property(player->proxy, "Status", &var))
2445                         g_dbus_emit_property_changed(player->conn,
2446                                                 MPRIS_PLAYER_PATH,
2447                                                 MPRIS_PLAYER_INTERFACE,
2448                                                 "PlaybackStatus");
2449                 return;
2450         }
2451
2452         g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
2453                                                 MPRIS_PLAYER_INTERFACE,
2454                                                 name);
2455 }
2456
2457 static void item_property_changed(GDBusProxy *proxy, const char *name,
2458                                         DBusMessageIter *iter, void *user_data)
2459 {
2460         struct player *player;
2461         DBusMessage *signal;
2462         DBusMessageIter args;
2463         const char *path;
2464
2465         path = g_dbus_proxy_get_path(proxy);
2466
2467         player = find_player_by_item(path);
2468         if (player == NULL)
2469                 return;
2470
2471         if (strcasecmp(name, "Metadata") != 0)
2472                 return;
2473
2474         signal = dbus_message_new_signal(MPRIS_PLAYER_PATH,
2475                                         MPRIS_TRACKLIST_INTERFACE,
2476                                         "TrackMetadataChanged");
2477         if (!signal) {
2478                 fprintf(stderr, "Unable to allocate new %s.TrackAdded signal",
2479                                                 MPRIS_TRACKLIST_INTERFACE);
2480                 return;
2481         }
2482
2483         dbus_message_iter_init_append(signal, &args);
2484
2485         dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &path);
2486
2487         append_iter(&args, iter);
2488
2489         g_dbus_send_message(player->conn, signal);
2490 }
2491
2492 static void property_changed(GDBusProxy *proxy, const char *name,
2493                                         DBusMessageIter *iter, void *user_data)
2494 {
2495         const char *interface;
2496
2497         interface = g_dbus_proxy_get_interface(proxy);
2498
2499         if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0)
2500                 return player_property_changed(proxy, name, iter, user_data);
2501
2502         if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0)
2503                 return transport_property_changed(proxy, name, iter,
2504                                                                 user_data);
2505
2506         if (strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE) == 0)
2507                 return item_property_changed(proxy, name, iter, user_data);
2508 }
2509
2510 int main(int argc, char *argv[])
2511 {
2512         GOptionContext *context;
2513         GError *error = NULL;
2514         guint owner_watch, properties_watch, signal_watch;
2515         struct sigaction sa;
2516
2517         context = g_option_context_new(NULL);
2518         g_option_context_add_main_entries(context, options, NULL);
2519
2520         if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
2521                 if (error != NULL) {
2522                         g_printerr("%s\n", error->message);
2523                         g_error_free(error);
2524                 } else
2525                         g_printerr("An unknown error occurred\n");
2526                 exit(1);
2527         }
2528
2529         g_option_context_free(context);
2530
2531         if (option_version == TRUE) {
2532                 usage();
2533                 exit(0);
2534         }
2535
2536         main_loop = g_main_loop_new(NULL, FALSE);
2537
2538         sys = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
2539         if (!sys) {
2540                 fprintf(stderr, "Can't get on system bus");
2541                 exit(1);
2542         }
2543
2544         session = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL);
2545         if (!session) {
2546                 fprintf(stderr, "Can't get on session bus");
2547                 exit(1);
2548         }
2549
2550         owner_watch = g_dbus_add_signal_watch(session, NULL, NULL,
2551                                                 DBUS_INTERFACE_DBUS,
2552                                                 "NameOwnerChanged",
2553                                                 name_owner_changed,
2554                                                 NULL, NULL);
2555
2556         properties_watch = g_dbus_add_properties_watch(session, NULL, NULL,
2557                                                         MPRIS_PLAYER_INTERFACE,
2558                                                         player_signal,
2559                                                         NULL, NULL);
2560
2561         signal_watch = g_dbus_add_signal_watch(session, NULL, NULL,
2562                                                         MPRIS_PLAYER_INTERFACE,
2563                                                         NULL, player_signal,
2564                                                         NULL, NULL);
2565
2566         memset(&sa, 0, sizeof(sa));
2567         sa.sa_flags   = SA_NOCLDSTOP;
2568         sa.sa_handler = sig_term;
2569         sigaction(SIGTERM, &sa, NULL);
2570         sigaction(SIGINT,  &sa, NULL);
2571
2572         client = g_dbus_client_new(sys, BLUEZ_BUS_NAME, BLUEZ_PATH);
2573
2574         g_dbus_client_set_connect_watch(client, connect_handler, NULL);
2575         g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
2576
2577         g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
2578                                                 property_changed, NULL);
2579
2580         g_main_loop_run(main_loop);
2581
2582         g_dbus_remove_watch(session, owner_watch);
2583         g_dbus_remove_watch(session, properties_watch);
2584         g_dbus_remove_watch(session, signal_watch);
2585
2586         g_dbus_client_unref(client);
2587
2588         dbus_connection_unref(session);
2589         dbus_connection_unref(sys);
2590
2591         g_main_loop_unref(main_loop);
2592
2593         return 0;
2594 }