Tizen 2.0 Release
[framework/connectivity/bluez.git] / test / agent.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
38 static char *passkey_value = NULL;
39 static int passkey_delay = 0;
40 static int do_reject = 0;
41
42 static volatile sig_atomic_t __io_canceled = 0;
43 static volatile sig_atomic_t __io_terminated = 0;
44 static volatile sig_atomic_t exit_on_release = 1;
45
46 static void sig_term(int sig)
47 {
48         __io_canceled = 1;
49 }
50
51 static DBusHandlerResult agent_filter(DBusConnection *conn,
52                                                 DBusMessage *msg, void *data)
53 {
54         const char *name, *old, *new;
55
56         if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
57                                                 "NameOwnerChanged"))
58                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
59
60         if (!dbus_message_get_args(msg, NULL,
61                                         DBUS_TYPE_STRING, &name,
62                                         DBUS_TYPE_STRING, &old,
63                                         DBUS_TYPE_STRING, &new,
64                                         DBUS_TYPE_INVALID)) {
65                 fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
66                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
67         }
68
69         if (!strcmp(name, "org.bluez") && *new == '\0') {
70                 fprintf(stderr, "Agent has been terminated\n");
71                 __io_terminated = 1;
72         }
73
74         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
75 }
76
77 static DBusHandlerResult request_pincode_message(DBusConnection *conn,
78                                                 DBusMessage *msg, void *data)
79 {
80         DBusMessage *reply;
81         const char *path;
82
83         if (!passkey_value)
84                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
85
86         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
87                                                         DBUS_TYPE_INVALID)) {
88                 fprintf(stderr, "Invalid arguments for RequestPinCode method");
89                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
90         }
91
92         if (do_reject) {
93                 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
94                 goto send;
95         }
96
97         reply = dbus_message_new_method_return(msg);
98         if (!reply) {
99                 fprintf(stderr, "Can't create reply message\n");
100                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
101         }
102
103         printf("Pincode request for device %s\n", path);
104
105         if (passkey_delay) {
106                 printf("Waiting for %d seconds\n", passkey_delay);
107                 sleep(passkey_delay);
108         }
109
110         dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey_value,
111                                                         DBUS_TYPE_INVALID);
112
113 send:
114         dbus_connection_send(conn, reply, NULL);
115
116         dbus_connection_flush(conn);
117
118         dbus_message_unref(reply);
119
120         return DBUS_HANDLER_RESULT_HANDLED;
121 }
122
123 static DBusHandlerResult request_passkey_message(DBusConnection *conn,
124                                                 DBusMessage *msg, void *data)
125 {
126         DBusMessage *reply;
127         const char *path;
128         unsigned int passkey;
129
130         if (!passkey_value)
131                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
132
133         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
134                                                         DBUS_TYPE_INVALID)) {
135                 fprintf(stderr, "Invalid arguments for RequestPasskey method");
136                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
137         }
138
139         if (do_reject) {
140                 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
141                 goto send;
142         }
143
144         reply = dbus_message_new_method_return(msg);
145         if (!reply) {
146                 fprintf(stderr, "Can't create reply message\n");
147                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
148         }
149
150         printf("Passkey request for device %s\n", path);
151
152         if (passkey_delay) {
153                 printf("Waiting for %d seconds\n", passkey_delay);
154                 sleep(passkey_delay);
155         }
156
157         passkey = strtoul(passkey_value, NULL, 10);
158
159         dbus_message_append_args(reply, DBUS_TYPE_UINT32, &passkey,
160                                                         DBUS_TYPE_INVALID);
161
162 send:
163         dbus_connection_send(conn, reply, NULL);
164
165         dbus_connection_flush(conn);
166
167         dbus_message_unref(reply);
168
169         return DBUS_HANDLER_RESULT_HANDLED;
170 }
171
172 static DBusHandlerResult request_confirmation_message(DBusConnection *conn,
173                                                 DBusMessage *msg, void *data)
174 {
175         DBusMessage *reply;
176         const char *path;
177         unsigned int passkey;
178
179         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
180                                                 DBUS_TYPE_UINT32, &passkey,
181                                                         DBUS_TYPE_INVALID)) {
182                 fprintf(stderr, "Invalid arguments for RequestPasskey method");
183                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
184         }
185
186         if (do_reject) {
187                 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
188                 goto send;
189         }
190
191         reply = dbus_message_new_method_return(msg);
192         if (!reply) {
193                 fprintf(stderr, "Can't create reply message\n");
194                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
195         }
196
197         printf("Confirmation request of %u for device %s\n", passkey, path);
198
199         if (passkey_delay) {
200                 printf("Waiting for %d seconds\n", passkey_delay);
201                 sleep(passkey_delay);
202         }
203
204 send:
205         dbus_connection_send(conn, reply, NULL);
206
207         dbus_connection_flush(conn);
208
209         dbus_message_unref(reply);
210
211         return DBUS_HANDLER_RESULT_HANDLED;
212 }
213
214 static DBusHandlerResult authorize_message(DBusConnection *conn,
215                                                 DBusMessage *msg, void *data)
216 {
217         DBusMessage *reply;
218         const char *path, *uuid;
219
220         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
221                                                 DBUS_TYPE_STRING, &uuid,
222                                                         DBUS_TYPE_INVALID)) {
223                 fprintf(stderr, "Invalid arguments for Authorize method");
224                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
225         }
226
227         if (do_reject) {
228                 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
229                 goto send;
230         }
231
232         reply = dbus_message_new_method_return(msg);
233         if (!reply) {
234                 fprintf(stderr, "Can't create reply message\n");
235                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
236         }
237
238         printf("Authorizing request for %s\n", path);
239
240 send:
241         dbus_connection_send(conn, reply, NULL);
242
243         dbus_connection_flush(conn);
244
245         dbus_message_unref(reply);
246
247         return DBUS_HANDLER_RESULT_HANDLED;
248 }
249
250 static DBusHandlerResult cancel_message(DBusConnection *conn,
251                                                 DBusMessage *msg, void *data)
252 {
253         DBusMessage *reply;
254
255         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
256                 fprintf(stderr, "Invalid arguments for passkey Confirm method");
257                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
258         }
259
260         printf("Request canceled\n");
261
262         reply = dbus_message_new_method_return(msg);
263         if (!reply) {
264                 fprintf(stderr, "Can't create reply message\n");
265                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
266         }
267
268         dbus_connection_send(conn, reply, NULL);
269
270         dbus_connection_flush(conn);
271
272         dbus_message_unref(reply);
273
274         return DBUS_HANDLER_RESULT_HANDLED;
275 }
276
277 static DBusHandlerResult release_message(DBusConnection *conn,
278                                                 DBusMessage *msg, void *data)
279 {
280         DBusMessage *reply;
281
282         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
283                 fprintf(stderr, "Invalid arguments for Release method");
284                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
285         }
286
287         if (!__io_canceled)
288                 fprintf(stderr, "Agent has been released\n");
289
290         if (exit_on_release)
291                 __io_terminated = 1;
292
293         reply = dbus_message_new_method_return(msg);
294         if (!reply) {
295                 fprintf(stderr, "Can't create reply message\n");
296                 return DBUS_HANDLER_RESULT_NEED_MEMORY;
297         }
298
299         dbus_connection_send(conn, reply, NULL);
300
301         dbus_connection_flush(conn);
302
303         dbus_message_unref(reply);
304
305         return DBUS_HANDLER_RESULT_HANDLED;
306 }
307
308 static DBusHandlerResult agent_message(DBusConnection *conn,
309                                                 DBusMessage *msg, void *data)
310 {
311         if (dbus_message_is_method_call(msg, "org.bluez.Agent",
312                                                         "RequestPinCode"))
313                 return request_pincode_message(conn, msg, data);
314
315         if (dbus_message_is_method_call(msg, "org.bluez.Agent",
316                                                         "RequestPasskey"))
317                 return request_passkey_message(conn, msg, data);
318
319         if (dbus_message_is_method_call(msg, "org.bluez.Agent",
320                                                         "RequestConfirmation"))
321                 return request_confirmation_message(conn, msg, data);
322
323         if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Authorize"))
324                 return authorize_message(conn, msg, data);
325
326         if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Cancel"))
327                 return cancel_message(conn, msg, data);
328
329         if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Release"))
330                 return release_message(conn, msg, data);
331
332         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
333 }
334
335 static const DBusObjectPathVTable agent_table = {
336         .message_function = agent_message,
337 };
338
339 static int register_agent(DBusConnection *conn, const char *adapter_path,
340                                                 const char *agent_path,
341                                                 const char *capabilities)
342 {
343         DBusMessage *msg, *reply;
344         DBusError err;
345
346         msg = dbus_message_new_method_call("org.bluez", adapter_path,
347                                         "org.bluez.Adapter", "RegisterAgent");
348         if (!msg) {
349                 fprintf(stderr, "Can't allocate new method call\n");
350                 return -1;
351         }
352
353         dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
354                                         DBUS_TYPE_STRING, &capabilities,
355                                         DBUS_TYPE_INVALID);
356
357         dbus_error_init(&err);
358
359         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
360
361         dbus_message_unref(msg);
362
363         if (!reply) {
364                 fprintf(stderr, "Can't register agent\n");
365                 if (dbus_error_is_set(&err)) {
366                         fprintf(stderr, "%s\n", err.message);
367                         dbus_error_free(&err);
368                 }
369                 return -1;
370         }
371
372         dbus_message_unref(reply);
373
374         dbus_connection_flush(conn);
375
376         return 0;
377 }
378
379 static int unregister_agent(DBusConnection *conn, const char *adapter_path,
380                                                         const char *agent_path)
381 {
382         DBusMessage *msg, *reply;
383         DBusError err;
384
385         msg = dbus_message_new_method_call("org.bluez", adapter_path,
386                                         "org.bluez.Adapter", "UnregisterAgent");
387         if (!msg) {
388                 fprintf(stderr, "Can't allocate new method call\n");
389                 return -1;
390         }
391
392         dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
393                                                         DBUS_TYPE_INVALID);
394
395         dbus_error_init(&err);
396
397         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
398
399         dbus_message_unref(msg);
400
401         if (!reply) {
402                 fprintf(stderr, "Can't unregister agent\n");
403                 if (dbus_error_is_set(&err)) {
404                         fprintf(stderr, "%s\n", err.message);
405                         dbus_error_free(&err);
406                 }
407                 return -1;
408         }
409
410         dbus_message_unref(reply);
411
412         dbus_connection_flush(conn);
413
414         dbus_connection_unregister_object_path(conn, agent_path);
415
416         return 0;
417 }
418
419 static void create_paired_device_reply(DBusPendingCall *pending,
420                                                         void *user_data)
421 {
422         __io_terminated = 1;
423         return;
424 }
425
426 static int create_paired_device(DBusConnection *conn, const char *adapter_path,
427                                                 const char *agent_path,
428                                                 const char *capabilities,
429                                                 const char *device)
430 {
431         dbus_bool_t success;
432         DBusMessage *msg;
433         DBusPendingCall *pending;
434
435         msg = dbus_message_new_method_call("org.bluez", adapter_path,
436                                                 "org.bluez.Adapter",
437                                                 "CreatePairedDevice");
438         if (!msg) {
439                 fprintf(stderr, "Can't allocate new method call\n");
440                 return -1;
441         }
442
443         dbus_message_append_args(msg, DBUS_TYPE_STRING, &device,
444                                         DBUS_TYPE_OBJECT_PATH, &agent_path,
445                                         DBUS_TYPE_STRING, &capabilities,
446                                         DBUS_TYPE_INVALID);
447
448         exit_on_release = 0;
449         success = dbus_connection_send_with_reply(conn, msg, &pending, -1);
450         if (pending)
451                 dbus_pending_call_set_notify(pending,
452                                                 create_paired_device_reply,
453                                                 NULL, NULL);
454
455         dbus_message_unref(msg);
456
457         if (!success) {
458                 fprintf(stderr, "Not enough memory for message send\n");
459                 return -1;
460         }
461
462         dbus_connection_flush(conn);
463
464         return 0;
465 }
466
467 static char *get_default_adapter_path(DBusConnection *conn)
468 {
469         DBusMessage *msg, *reply;
470         DBusError err;
471         const char *reply_path;
472         char *path;
473
474         msg = dbus_message_new_method_call("org.bluez", "/",
475                                         "org.bluez.Manager", "DefaultAdapter");
476
477         if (!msg) {
478                 fprintf(stderr, "Can't allocate new method call\n");
479                 return NULL;
480         }
481
482         dbus_error_init(&err);
483
484         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
485
486         dbus_message_unref(msg);
487
488         if (!reply) {
489                 fprintf(stderr,
490                         "Can't get default adapter\n");
491                 if (dbus_error_is_set(&err)) {
492                         fprintf(stderr, "%s\n", err.message);
493                         dbus_error_free(&err);
494                 }
495                 return NULL;
496         }
497
498         if (!dbus_message_get_args(reply, &err,
499                                         DBUS_TYPE_OBJECT_PATH, &reply_path,
500                                         DBUS_TYPE_INVALID)) {
501                 fprintf(stderr,
502                         "Can't get reply arguments\n");
503                 if (dbus_error_is_set(&err)) {
504                         fprintf(stderr, "%s\n", err.message);
505                         dbus_error_free(&err);
506                 }
507                 dbus_message_unref(reply);
508                 return NULL;
509         }
510
511         path = strdup(reply_path);
512
513         dbus_message_unref(reply);
514
515         dbus_connection_flush(conn);
516
517         return path;
518 }
519
520 static char *get_adapter_path(DBusConnection *conn, const char *adapter)
521 {
522         DBusMessage *msg, *reply;
523         DBusError err;
524         const char *reply_path;
525         char *path;
526
527         if (!adapter)
528                 return get_default_adapter_path(conn);
529
530         msg = dbus_message_new_method_call("org.bluez", "/",
531                                         "org.bluez.Manager", "FindAdapter");
532
533         if (!msg) {
534                 fprintf(stderr, "Can't allocate new method call\n");
535                 return NULL;
536         }
537
538         dbus_message_append_args(msg, DBUS_TYPE_STRING, &adapter,
539                                         DBUS_TYPE_INVALID);
540
541         dbus_error_init(&err);
542
543         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
544
545         dbus_message_unref(msg);
546
547         if (!reply) {
548                 fprintf(stderr,
549                         "Can't find adapter %s\n", adapter);
550                 if (dbus_error_is_set(&err)) {
551                         fprintf(stderr, "%s\n", err.message);
552                         dbus_error_free(&err);
553                 }
554                 return NULL;
555         }
556
557         if (!dbus_message_get_args(reply, &err,
558                                         DBUS_TYPE_OBJECT_PATH, &reply_path,
559                                         DBUS_TYPE_INVALID)) {
560                 fprintf(stderr,
561                         "Can't get reply arguments\n");
562                 if (dbus_error_is_set(&err)) {
563                         fprintf(stderr, "%s\n", err.message);
564                         dbus_error_free(&err);
565                 }
566                 dbus_message_unref(reply);
567                 return NULL;
568         }
569
570         path = strdup(reply_path);
571
572         dbus_message_unref(reply);
573
574         dbus_connection_flush(conn);
575
576         return path;
577 }
578
579 static void usage(void)
580 {
581         printf("Bluetooth agent ver %s\n\n", VERSION);
582
583         printf("Usage:\n"
584                 "\tagent [--adapter adapter-path] [--path agent-path] <passkey> [<device>]\n"
585                 "\n");
586 }
587
588 static struct option main_options[] = {
589         { "adapter",    1, 0, 'a' },
590         { "path",       1, 0, 'p' },
591         { "capabilites",1, 0, 'c' },
592         { "delay",      1, 0, 'd' },
593         { "reject",     0, 0, 'r' },
594         { "help",       0, 0, 'h' },
595         { 0, 0, 0, 0 }
596 };
597
598 int main(int argc, char *argv[])
599 {
600         const char *capabilities = "DisplayYesNo";
601         struct sigaction sa;
602         DBusConnection *conn;
603         char match_string[128], default_path[128], *adapter_id = NULL;
604         char *adapter_path = NULL, *agent_path = NULL, *device = NULL;
605         int opt;
606
607         snprintf(default_path, sizeof(default_path),
608                                         "/org/bluez/agent_%d", getpid());
609
610         while ((opt = getopt_long(argc, argv, "+a:p:c:d:rh", main_options, NULL)) != EOF) {
611                 switch(opt) {
612                 case 'a':
613                         adapter_id = optarg;
614                         break;
615                 case 'p':
616                         if (optarg[0] != '/') {
617                                 fprintf(stderr, "Invalid path\n");
618                                 exit(1);
619                         }
620                         agent_path = strdup(optarg);
621                         break;
622                 case 'c':
623                         capabilities = optarg;
624                         break;
625                 case 'd':
626                         passkey_delay = atoi(optarg);
627                         break;
628                 case 'r':
629                         do_reject = 1;
630                         break;
631                 case 'h':
632                         usage();
633                         exit(0);
634                 default:
635                         exit(1);
636                 }
637         }
638
639         argc -= optind;
640         argv += optind;
641         optind = 0;
642
643         if (argc < 1) {
644                 usage();
645                 exit(1);
646         }
647
648         passkey_value = strdup(argv[0]);
649
650         if (argc > 1)
651                 device = strdup(argv[1]);
652
653         if (!agent_path)
654                 agent_path = strdup(default_path);
655
656         conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
657         if (!conn) {
658                 fprintf(stderr, "Can't get on system bus");
659                 exit(1);
660         }
661
662         adapter_path = get_adapter_path(conn, adapter_id);
663         if (!adapter_path)
664                 exit(1);
665
666         if (!dbus_connection_register_object_path(conn, agent_path,
667                                                         &agent_table, NULL)) {
668                 fprintf(stderr, "Can't register object path for agent\n");
669                 exit(1);
670         }
671
672         if (device) {
673                 if (create_paired_device(conn, adapter_path, agent_path,
674                                                 capabilities, device) < 0) {
675                         dbus_connection_unref(conn);
676                         exit(1);
677                 }
678         } else {
679                 if (register_agent(conn, adapter_path, agent_path,
680                                                         capabilities) < 0) {
681                         dbus_connection_unref(conn);
682                         exit(1);
683                 }
684         }
685
686         if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL))
687                 fprintf(stderr, "Can't add signal filter");
688
689         snprintf(match_string, sizeof(match_string),
690                         "interface=%s,member=NameOwnerChanged,arg0=%s",
691                         DBUS_INTERFACE_DBUS, "org.bluez");
692
693         dbus_bus_add_match(conn, match_string, NULL);
694
695         memset(&sa, 0, sizeof(sa));
696         sa.sa_flags   = SA_NOCLDSTOP;
697         sa.sa_handler = sig_term;
698         sigaction(SIGTERM, &sa, NULL);
699         sigaction(SIGINT,  &sa, NULL);
700
701         while (!__io_canceled && !__io_terminated) {
702                 if (dbus_connection_read_write_dispatch(conn, 500) != TRUE)
703                         break;
704         }
705
706         if (!__io_terminated && !device)
707                 unregister_agent(conn, adapter_path, agent_path);
708
709         free(adapter_path);
710         free(agent_path);
711
712         free(passkey_value);
713
714         dbus_connection_unref(conn);
715
716         return 0;
717 }