Tizen 2.0 Release
[framework/connectivity/bluez.git] / test / mpris-player.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
36 #include <dbus/dbus.h>
37 #include <glib.h>
38
39 static volatile sig_atomic_t __io_canceled = 0;
40 static volatile sig_atomic_t __io_terminated = 0;
41 static char *adapter = NULL;
42 static DBusConnection *sys = NULL;
43 static DBusConnection *session = NULL;
44
45 static void sig_term(int sig)
46 {
47         __io_canceled = 1;
48 }
49
50 static DBusMessage *get_all(DBusConnection *conn, const char *name)
51 {
52         DBusMessage *msg, *reply;
53         DBusError err;
54         const char *iface = "org.mpris.MediaPlayer2.Player";
55
56         msg = dbus_message_new_method_call(name, "/org/mpris/MediaPlayer2",
57                                         DBUS_INTERFACE_PROPERTIES, "GetAll");
58         if (!msg) {
59                 fprintf(stderr, "Can't allocate new method call\n");
60                 return NULL;
61         }
62
63         dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface,
64                                         DBUS_TYPE_INVALID);
65
66         dbus_error_init(&err);
67
68         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
69
70         dbus_message_unref(msg);
71
72         if (!reply) {
73                 fprintf(stderr, "Can't get default adapter\n");
74                 if (dbus_error_is_set(&err)) {
75                         fprintf(stderr, "%s\n", err.message);
76                         dbus_error_free(&err);
77                 }
78                 return NULL;
79         }
80
81         return reply;
82 }
83
84 static void append_variant(DBusMessageIter *iter, int type, void *val)
85 {
86         DBusMessageIter value;
87         char sig[2] = { type, '\0' };
88
89         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
90
91         dbus_message_iter_append_basic(&value, type, val);
92
93         dbus_message_iter_close_container(iter, &value);
94 }
95
96 static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
97                                                                 void *val)
98 {
99         DBusMessageIter entry;
100
101         if (type == DBUS_TYPE_STRING) {
102                 const char *str = *((const char **) val);
103                 if (str == NULL)
104                         return;
105         }
106
107         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
108                                                         NULL, &entry);
109
110         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
111
112         append_variant(&entry, type, val);
113
114         dbus_message_iter_close_container(dict, &entry);
115 }
116
117 static dbus_bool_t emit_property_changed(DBusConnection *conn,
118                                         const char *path,
119                                         const char *interface,
120                                         const char *name,
121                                         int type, void *value)
122 {
123         DBusMessage *signal;
124         DBusMessageIter iter;
125         dbus_bool_t result;
126
127         signal = dbus_message_new_signal(path, interface, "PropertyChanged");
128
129         if (!signal) {
130                 fprintf(stderr, "Unable to allocate new %s.PropertyChanged"
131                                                         " signal", interface);
132                 return FALSE;
133         }
134
135         dbus_message_iter_init_append(signal, &iter);
136
137         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
138
139         append_variant(&iter, type, value);
140
141         result = dbus_connection_send(conn, signal, NULL);
142         dbus_message_unref(signal);
143
144         return result;
145 }
146
147 static int parse_property(DBusConnection *conn, const char *path,
148                                                 const char *key,
149                                                 DBusMessageIter *entry,
150                                                 DBusMessageIter *properties)
151 {
152         DBusMessageIter var;
153
154         printf("property %s found\n", key);
155
156         if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
157                 return -EINVAL;
158
159         dbus_message_iter_recurse(entry, &var);
160
161         if (strcasecmp(key, "PlaybackStatus") == 0) {
162                 const char *value;
163
164                 if (dbus_message_iter_get_arg_type(&var) !=
165                                                         DBUS_TYPE_STRING)
166                         return -EINVAL;
167
168                 dbus_message_iter_get_basic(&var, &value);
169
170                 if (properties)
171                         dict_append_entry(properties, "Status",
172                                                 DBUS_TYPE_STRING, &value);
173                 else
174                         emit_property_changed(sys, path,
175                                         "org.bluez.MediaPlayer", "Status",
176                                         DBUS_TYPE_STRING, &value);
177         } else if (strcasecmp(key, "Position") == 0) {
178                 int64_t usec, msec;
179
180                 if (dbus_message_iter_get_arg_type(&var) !=
181                                                         DBUS_TYPE_INT64)
182                         return -EINVAL;
183
184                 dbus_message_iter_get_basic(&var, &usec);
185                 msec = usec / 1000;
186
187                 if (properties)
188                         dict_append_entry(properties, "Position",
189                                                 DBUS_TYPE_UINT32, &msec);
190                 else
191                         emit_property_changed(sys, path,
192                                         "org.bluez.MediaPlayer", "Position",
193                                         DBUS_TYPE_UINT32, &msec);
194         } else if (strcasecmp(key, "Shuffle") == 0) {
195                 dbus_bool_t value;
196                 const char *str;
197
198                 if (dbus_message_iter_get_arg_type(&var) !=
199                                                         DBUS_TYPE_BOOLEAN)
200                         return -EINVAL;
201
202                 dbus_message_iter_get_basic(&var, &value);
203
204                 str = value ? "on" : "off";
205                 if (properties)
206                         dict_append_entry(properties, "Shuffle",
207                                                 DBUS_TYPE_STRING, &str);
208                 else
209                         emit_property_changed(sys, path,
210                                         "org.bluez.MediaPlayer", "Shuffle",
211                                         DBUS_TYPE_UINT32, &str);
212         }
213
214         return 0;
215 }
216
217 static int parse_properties(DBusConnection *conn, const char *path,
218                                                 DBusMessageIter *args,
219                                                 DBusMessageIter *properties)
220 {
221         DBusMessageIter dict;
222         int ctype;
223
224         ctype = dbus_message_iter_get_arg_type(args);
225         if (ctype != DBUS_TYPE_ARRAY)
226                 return -EINVAL;
227
228         dbus_message_iter_recurse(args, &dict);
229
230         while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
231                                                         DBUS_TYPE_INVALID) {
232                 DBusMessageIter entry;
233                 const char *key;
234
235                 if (ctype != DBUS_TYPE_DICT_ENTRY)
236                         return -EINVAL;
237
238                 dbus_message_iter_recurse(&dict, &entry);
239                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
240                         return -EINVAL;
241
242                 dbus_message_iter_get_basic(&entry, &key);
243                 dbus_message_iter_next(&entry);
244
245                 if (parse_property(conn, path, key, &entry, properties) < 0)
246                         return -EINVAL;
247
248                 dbus_message_iter_next(&dict);
249         }
250
251         return 0;
252 }
253
254 static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
255                                                 DBusMessageIter *metadata)
256 {
257         DBusMessageIter var;
258
259         printf("metadata %s found\n", key);
260
261         if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
262                 return -EINVAL;
263
264         dbus_message_iter_recurse(entry, &var);
265
266         if (strcasecmp(key, "xesam:title") == 0) {
267                 const char *value;
268
269                 if (dbus_message_iter_get_arg_type(&var) !=
270                                                         DBUS_TYPE_STRING)
271                         return -EINVAL;
272
273                 dbus_message_iter_get_basic(&var, &value);
274                 dict_append_entry(metadata, "Title", DBUS_TYPE_STRING,
275                                                                 &value);
276         } else if (strcasecmp(key, "xesam:artist") == 0) {
277                 const char *value;
278                 DBusMessageIter array;
279
280                 if (dbus_message_iter_get_arg_type(&var) !=
281                                                         DBUS_TYPE_ARRAY)
282                         return -EINVAL;
283
284                 dbus_message_iter_recurse(&var, &array);
285
286                 if (dbus_message_iter_get_arg_type(&array) !=
287                                                         DBUS_TYPE_STRING)
288                         return -EINVAL;
289
290                 dbus_message_iter_get_basic(&array, &value);
291                 dict_append_entry(metadata, "Artist", DBUS_TYPE_STRING,
292                                                                 &value);
293         } else if (strcasecmp(key, "xesam:album") == 0) {
294                 const char *value;
295
296                 if (dbus_message_iter_get_arg_type(&var) !=
297                                                         DBUS_TYPE_STRING)
298                         return -EINVAL;
299
300                 dbus_message_iter_get_basic(&var, &value);
301                 dict_append_entry(metadata, "Album", DBUS_TYPE_STRING,
302                                                                 &value);
303         } else if (strcasecmp(key, "xesam:genre") == 0) {
304                 const char *value;
305                 DBusMessageIter array;
306
307                 if (dbus_message_iter_get_arg_type(&var) !=
308                                                         DBUS_TYPE_ARRAY)
309                         return -EINVAL;
310
311                 dbus_message_iter_recurse(&var, &array);
312
313                 if (dbus_message_iter_get_arg_type(&array) !=
314                                                         DBUS_TYPE_STRING)
315                         return -EINVAL;
316
317                 dbus_message_iter_get_basic(&array, &value);
318                 dict_append_entry(metadata, "Genre", DBUS_TYPE_STRING,
319                                                                 &value);
320         } else if (strcasecmp(key, "mpris:length") == 0) {
321                 int64_t usec, msec;
322
323                 if (dbus_message_iter_get_arg_type(&var) !=
324                                                         DBUS_TYPE_INT64)
325                         return -EINVAL;
326
327                 dbus_message_iter_get_basic(&var, &usec);
328                 msec = usec / 1000;
329
330                 dict_append_entry(metadata, "Duration", DBUS_TYPE_UINT32,
331                                                                 &msec);
332         } else if (strcasecmp(key, "xesam:trackNumber") == 0) {
333                 int32_t value;
334
335                 if (dbus_message_iter_get_arg_type(&var) !=
336                                                         DBUS_TYPE_INT32)
337                         return -EINVAL;
338
339                 dbus_message_iter_get_basic(&var, &value);
340
341                 dict_append_entry(metadata, "Number", DBUS_TYPE_UINT32,
342                                                                 &value);
343         }
344
345         return 0;
346 }
347
348 static int parse_track(DBusMessageIter *args, DBusMessageIter *metadata)
349 {
350         DBusMessageIter var, dict;
351         int ctype;
352
353         ctype = dbus_message_iter_get_arg_type(args);
354         if (ctype != DBUS_TYPE_VARIANT)
355                 return -EINVAL;
356
357         dbus_message_iter_recurse(args, &var);
358
359         ctype = dbus_message_iter_get_arg_type(&var);
360         if (ctype != DBUS_TYPE_ARRAY)
361                 return -EINVAL;
362
363         dbus_message_iter_recurse(&var, &dict);
364
365         while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
366                                                         DBUS_TYPE_INVALID) {
367                 DBusMessageIter entry;
368                 const char *key;
369
370                 if (ctype != DBUS_TYPE_DICT_ENTRY)
371                         return -EINVAL;
372
373                 dbus_message_iter_recurse(&dict, &entry);
374                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
375                         return -EINVAL;
376
377                 dbus_message_iter_get_basic(&entry, &key);
378                 dbus_message_iter_next(&entry);
379
380                 if (parse_metadata_entry(&entry, key, metadata) < 0)
381                         return -EINVAL;
382
383                 dbus_message_iter_next(&dict);
384         }
385
386         return 0;
387 }
388
389 static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
390 {
391         DBusMessageIter dict;
392         int ctype;
393
394         ctype = dbus_message_iter_get_arg_type(args);
395         if (ctype != DBUS_TYPE_ARRAY)
396                 return -EINVAL;
397
398         dbus_message_iter_recurse(args, &dict);
399
400         while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
401                                                         DBUS_TYPE_INVALID) {
402                 DBusMessageIter entry;
403                 const char *key;
404
405                 if (ctype != DBUS_TYPE_DICT_ENTRY)
406                         return -EINVAL;
407
408                 dbus_message_iter_recurse(&dict, &entry);
409                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
410                         return -EINVAL;
411
412                 dbus_message_iter_get_basic(&entry, &key);
413                 dbus_message_iter_next(&entry);
414
415                 if (strcasecmp(key, "Metadata") == 0)
416                         return parse_track(&entry, metadata);
417
418                 dbus_message_iter_next(&dict);
419         }
420
421         return -EINVAL;
422 }
423
424 static char *sender2path(const char *sender)
425 {
426         char *path;
427
428         path = g_strconcat("/", sender, NULL);
429         return g_strdelimit(path, ":.", '_');
430 }
431
432 static DBusHandlerResult player_message(DBusConnection *conn,
433                                                 DBusMessage *msg, void *data)
434 {
435         if (dbus_message_is_method_call(msg, "org.bluez.MediaPlayer",
436                                                                 "Release")) {
437                 printf("Release\n");
438                 exit(1);
439         }
440
441         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
442 }
443
444 static const DBusObjectPathVTable player_table = {
445         .message_function = player_message,
446 };
447
448 static void add_player(DBusConnection *conn, const char *name,
449                                                         const char *sender)
450 {
451         DBusMessage *reply = get_all(conn, name);
452         DBusMessage *msg;
453         DBusMessageIter iter, args, properties, metadata;
454         DBusError err;
455         char *path;
456
457         if (!reply)
458                 return;
459
460         msg = dbus_message_new_method_call("org.bluez", adapter,
461                                         "org.bluez.Media",
462                                         "RegisterPlayer");
463         if (!msg) {
464                 fprintf(stderr, "Can't allocate new method call\n");
465                 return;
466         }
467
468         dbus_message_iter_init_append(msg, &iter);
469
470         path = sender2path(sender);
471         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
472
473         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
474                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
475                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
476                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &properties);
477
478         dbus_message_iter_init(reply, &args);
479
480         if (parse_properties(conn, path, &args, &properties) < 0)
481                 goto done;
482
483         dbus_message_iter_close_container(&iter, &properties);
484
485         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
486                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
487                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
488                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
489
490         dbus_message_iter_init(reply, &args);
491
492         if (parse_metadata(&args, &metadata) < 0)
493                 goto done;
494
495         dbus_message_iter_close_container(&iter, &metadata);
496
497         dbus_message_unref(reply);
498
499         dbus_error_init(&err);
500
501         reply = dbus_connection_send_with_reply_and_block(sys, msg, -1, &err);
502         if (!reply) {
503                 fprintf(stderr, "Can't register player\n");
504                 if (dbus_error_is_set(&err)) {
505                         fprintf(stderr, "%s\n", err.message);
506                         dbus_error_free(&err);
507                 }
508                 goto done;
509         }
510
511         if (!dbus_connection_register_object_path(sys, path, &player_table,
512                                                                 NULL))
513                 fprintf(stderr, "Can't register object path for agent\n");
514
515 done:
516         if (reply)
517                 dbus_message_unref(reply);
518         dbus_message_unref(msg);
519         g_free(path);
520 }
521
522 static void remove_player(DBusConnection *conn, const char *sender)
523 {
524         DBusMessage *msg;
525         char *path;
526
527         msg = dbus_message_new_method_call("org.bluez", adapter,
528                                         "org.bluez.Media",
529                                         "UnregisterPlayer");
530         if (!msg) {
531                 fprintf(stderr, "Can't allocate new method call\n");
532                 return;
533         }
534
535         path = sender2path(sender);
536         dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
537                                         DBUS_TYPE_INVALID);
538
539         dbus_connection_send(sys, msg, NULL);
540
541         dbus_message_unref(msg);
542         g_free(path);
543 }
544
545 static DBusHandlerResult properties_changed(DBusConnection *conn,
546                                                         DBusMessage *msg)
547 {
548         DBusMessage *signal;
549         DBusMessageIter iter, entry, metadata;
550         const char *iface;
551         char *path;
552
553         dbus_message_iter_init(msg, &iter);
554
555         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
556                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
557
558         dbus_message_iter_get_basic(&iter, &iface);
559
560         printf("PropertiesChanged interface %s\n", iface);
561
562         dbus_message_iter_next(&iter);
563
564         path = sender2path(dbus_message_get_sender(msg));
565         parse_properties(conn, path, &iter, NULL);
566
567         signal = dbus_message_new_signal(path, "org.bluez.MediaPlayer",
568                                                         "TrackChanged");
569         if (!signal) {
570                 fprintf(stderr, "Unable to allocate new PropertyChanged"
571                                                         " signal\n");
572                 goto err;
573         }
574
575         dbus_message_iter_init_append(signal, &entry);
576
577         dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
578                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
579                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
580                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
581
582         dbus_message_iter_init(msg, &iter);
583         dbus_message_iter_next(&iter);
584
585         if (parse_metadata(&iter, &metadata) < 0)
586                 goto err;
587
588         dbus_message_iter_close_container(&entry, &metadata);
589
590         dbus_connection_send(sys, signal, NULL);
591         dbus_message_unref(signal);
592         g_free(path);
593
594         return DBUS_HANDLER_RESULT_HANDLED;
595
596 err:
597         if (signal)
598                 dbus_message_unref(signal);
599         g_free(path);
600         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
601 }
602
603 static DBusHandlerResult session_filter(DBusConnection *conn,
604                                                 DBusMessage *msg, void *data)
605 {
606         const char *name, *old, *new;
607
608         if (dbus_message_is_signal(msg, DBUS_INTERFACE_PROPERTIES,
609                                                 "PropertiesChanged"))
610                 return properties_changed(conn, msg);
611
612         if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
613                                                 "NameOwnerChanged"))
614                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
615
616         if (!dbus_message_get_args(msg, NULL,
617                                         DBUS_TYPE_STRING, &name,
618                                         DBUS_TYPE_STRING, &old,
619                                         DBUS_TYPE_STRING, &new,
620                                         DBUS_TYPE_INVALID)) {
621                 fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
622                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
623         }
624
625         if (!g_str_has_prefix(name, "org.mpris"))
626                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
627
628         if (*new == '\0') {
629                 printf("player %s at %s disappear\n", name, old);
630                 remove_player(conn, old);
631         } else {
632                 printf("player %s at %s found\n", name, new);
633                 add_player(conn, name, new);
634         }
635
636         return DBUS_HANDLER_RESULT_HANDLED;
637 }
638
639 static DBusHandlerResult system_filter(DBusConnection *conn,
640                                                 DBusMessage *msg, void *data)
641 {
642         const char *name, *old, *new;
643
644         if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
645                                                 "NameOwnerChanged"))
646                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
647
648         if (!dbus_message_get_args(msg, NULL,
649                                         DBUS_TYPE_STRING, &name,
650                                         DBUS_TYPE_STRING, &old,
651                                         DBUS_TYPE_STRING, &new,
652                                         DBUS_TYPE_INVALID)) {
653                 fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
654                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
655         }
656
657         if (!strcmp(name, "org.bluez") && *new == '\0') {
658                 fprintf(stderr, "bluetoothd disconnected\n");
659                 __io_terminated = 1;
660         }
661
662         return DBUS_HANDLER_RESULT_HANDLED;
663 }
664
665 static char *get_default_adapter(DBusConnection *conn)
666 {
667         DBusMessage *msg, *reply;
668         DBusError err;
669         const char *reply_path;
670         char *path;
671
672         msg = dbus_message_new_method_call("org.bluez", "/",
673                                         "org.bluez.Manager", "DefaultAdapter");
674
675         if (!msg) {
676                 fprintf(stderr, "Can't allocate new method call\n");
677                 return NULL;
678         }
679
680         dbus_error_init(&err);
681
682         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
683
684         dbus_message_unref(msg);
685
686         if (!reply) {
687                 fprintf(stderr, "Can't get default adapter\n");
688                 if (dbus_error_is_set(&err)) {
689                         fprintf(stderr, "%s\n", err.message);
690                         dbus_error_free(&err);
691                 }
692                 return NULL;
693         }
694
695         if (!dbus_message_get_args(reply, &err,
696                                         DBUS_TYPE_OBJECT_PATH, &reply_path,
697                                         DBUS_TYPE_INVALID)) {
698                 fprintf(stderr, "Can't get reply arguments\n");
699                 if (dbus_error_is_set(&err)) {
700                         fprintf(stderr, "%s\n", err.message);
701                         dbus_error_free(&err);
702                 }
703                 dbus_message_unref(reply);
704                 return NULL;
705         }
706
707         path = strdup(reply_path);
708
709         dbus_message_unref(reply);
710
711         dbus_connection_flush(conn);
712
713         return path;
714 }
715
716 static char *get_adapter(DBusConnection *conn, const char *adapter)
717 {
718         DBusMessage *msg, *reply;
719         DBusError err;
720         const char *reply_path;
721         char *path;
722
723         if (!adapter)
724                 return get_default_adapter(conn);
725
726         msg = dbus_message_new_method_call("org.bluez", "/",
727                                         "org.bluez.Manager", "FindAdapter");
728
729         if (!msg) {
730                 fprintf(stderr, "Can't allocate new method call\n");
731                 return NULL;
732         }
733
734         dbus_message_append_args(msg, DBUS_TYPE_STRING, &adapter,
735                                         DBUS_TYPE_INVALID);
736
737         dbus_error_init(&err);
738
739         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
740
741         dbus_message_unref(msg);
742
743         if (!reply) {
744                 fprintf(stderr, "Can't find adapter %s\n", adapter);
745                 if (dbus_error_is_set(&err)) {
746                         fprintf(stderr, "%s\n", err.message);
747                         dbus_error_free(&err);
748                 }
749                 return NULL;
750         }
751
752         if (!dbus_message_get_args(reply, &err,
753                                         DBUS_TYPE_OBJECT_PATH, &reply_path,
754                                         DBUS_TYPE_INVALID)) {
755                 fprintf(stderr, "Can't get reply arguments\n");
756                 if (dbus_error_is_set(&err)) {
757                         fprintf(stderr, "%s\n", err.message);
758                         dbus_error_free(&err);
759                 }
760                 dbus_message_unref(reply);
761                 return NULL;
762         }
763
764         path = strdup(reply_path);
765
766         dbus_message_unref(reply);
767
768         dbus_connection_flush(conn);
769
770         return path;
771 }
772
773 static char *get_name_owner(DBusConnection *conn, const char *name)
774 {
775         DBusMessage *msg, *reply;
776         DBusError err;
777         char *owner;
778
779         msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
780                                         DBUS_INTERFACE_DBUS, "GetNameOwner");
781
782         if (!msg) {
783                 fprintf(stderr, "Can't allocate new method call\n");
784                 return NULL;
785         }
786
787         dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
788                                                         DBUS_TYPE_INVALID);
789
790         dbus_error_init(&err);
791
792         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
793
794         dbus_message_unref(msg);
795
796         if (!reply) {
797                 fprintf(stderr, "Can't find adapter %s\n", adapter);
798                 if (dbus_error_is_set(&err)) {
799                         fprintf(stderr, "%s\n", err.message);
800                         dbus_error_free(&err);
801                 }
802                 return NULL;
803         }
804
805         if (!dbus_message_get_args(reply, NULL,
806                                         DBUS_TYPE_STRING, &owner,
807                                         DBUS_TYPE_INVALID)) {
808                 dbus_message_unref(reply);
809                 return NULL;
810         }
811
812         owner = g_strdup(owner);
813
814         dbus_message_unref(reply);
815
816         dbus_connection_flush(conn);
817
818         return owner;
819 }
820
821 static void parse_list_names(DBusConnection *conn, DBusMessageIter *args)
822 {
823         DBusMessageIter array;
824         int ctype;
825
826         ctype = dbus_message_iter_get_arg_type(args);
827         if (ctype != DBUS_TYPE_ARRAY)
828                 return;
829
830         dbus_message_iter_recurse(args, &array);
831
832         while ((ctype = dbus_message_iter_get_arg_type(&array)) !=
833                                                         DBUS_TYPE_INVALID) {
834                 const char *name;
835                 char *owner;
836
837                 if (ctype != DBUS_TYPE_STRING)
838                         goto next;
839
840                 dbus_message_iter_get_basic(&array, &name);
841
842                 if (!g_str_has_prefix(name, "org.mpris"))
843                         goto next;
844
845                 owner = get_name_owner(conn, name);
846
847                 if (owner == NULL)
848                         goto next;
849
850                 add_player(conn, name, owner);
851
852                 g_free(owner);
853 next:
854                 dbus_message_iter_next(&array);
855         }
856 }
857
858 static void list_names(DBusConnection *conn)
859 {
860         DBusMessage *msg, *reply;
861         DBusMessageIter iter;
862         DBusError err;
863
864         msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
865                                         DBUS_INTERFACE_DBUS, "ListNames");
866
867         if (!msg) {
868                 fprintf(stderr, "Can't allocate new method call\n");
869                 return;
870         }
871
872         dbus_error_init(&err);
873
874         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
875
876         dbus_message_unref(msg);
877
878         if (!reply) {
879                 fprintf(stderr, "Can't find adapter %s\n", adapter);
880                 if (dbus_error_is_set(&err)) {
881                         fprintf(stderr, "%s\n", err.message);
882                         dbus_error_free(&err);
883                 }
884                 return;
885         }
886
887         dbus_message_iter_init(reply, &iter);
888
889         parse_list_names(conn, &iter);
890
891         dbus_message_unref(reply);
892
893         dbus_connection_flush(conn);
894 }
895
896 static void usage(void)
897 {
898         printf("Bluetooth player ver %s\n\n", VERSION);
899
900         printf("Usage:\n"
901                 "\tplayer [--adapter adapter id]\n"
902                 "\n");
903 }
904
905 static struct option main_options[] = {
906         { "adapter",    1, 0, 'a' },
907         { 0, 0, 0, 0 }
908 };
909
910 int main(int argc, char *argv[])
911 {
912         struct sigaction sa;
913         char *adapter_id = NULL;
914         char match[128];
915         int opt;
916
917         while ((opt = getopt_long(argc, argv, "+a,h", main_options, NULL)) != EOF) {
918                 switch(opt) {
919                 case '1':
920                         adapter_id = optarg;
921                         break;
922                 case 'h':
923                         usage();
924                         exit(0);
925                 default:
926                         exit(1);
927                 }
928         }
929
930         sys = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
931         if (!sys) {
932                 fprintf(stderr, "Can't get on system bus");
933                 exit(1);
934         }
935
936         adapter = get_adapter(sys, adapter_id);
937         if (!adapter)
938                 exit(1);
939
940         if (!dbus_connection_add_filter(sys, system_filter, NULL, NULL)) {
941                 fprintf(stderr, "Can't add signal filter");
942                 exit(1);
943         }
944
945         snprintf(match, sizeof(match),
946                         "interface=%s,member=NameOwnerChanged,arg0=%s",
947                         DBUS_INTERFACE_DBUS, "org.bluez");
948
949         dbus_bus_add_match(sys, match, NULL);
950
951         session = dbus_bus_get(DBUS_BUS_SESSION, NULL);
952         if (!session) {
953                 fprintf(stderr, "Can't get on session bus");
954                 exit(1);
955         }
956
957         if (!dbus_connection_add_filter(session, session_filter, NULL, NULL)) {
958                 fprintf(stderr, "Can't add signal filter");
959                 exit(1);
960         }
961
962         snprintf(match, sizeof(match),
963                         "interface=%s,member=NameOwnerChanged",
964                         DBUS_INTERFACE_DBUS);
965
966         dbus_bus_add_match(session, match, NULL);
967
968         snprintf(match, sizeof(match),
969                         "interface=%s,member=PropertiesChanged,arg0=%s",
970                         DBUS_INTERFACE_PROPERTIES,
971                         "org.mpris.MediaPlayer2.Player");
972
973         list_names(session);
974
975         dbus_bus_add_match(session, match, NULL);
976
977         memset(&sa, 0, sizeof(sa));
978         sa.sa_flags   = SA_NOCLDSTOP;
979         sa.sa_handler = sig_term;
980         sigaction(SIGTERM, &sa, NULL);
981         sigaction(SIGINT,  &sa, NULL);
982
983         while (!__io_canceled && !__io_terminated) {
984                 if (dbus_connection_read_write_dispatch(sys, 500) != TRUE)
985                         break;
986                 if (dbus_connection_read_write_dispatch(session, 500) != TRUE)
987                         break;
988         }
989
990         dbus_connection_unref(sys);
991
992         return 0;
993 }