tizen 2.3.1 release
[framework/connectivity/bluez.git] / client / agent.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012  Intel Corporation. All rights reserved.
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 <stdlib.h>
30 #include <readline/readline.h>
31
32 #include "gdbus/gdbus.h"
33 #include "display.h"
34 #include "agent.h"
35
36 #define AGENT_PATH "/org/bluez/agent"
37 #define AGENT_INTERFACE "org.bluez.Agent1"
38
39 #define AGENT_PROMPT    COLOR_RED "[agent]" COLOR_OFF " "
40
41 static gboolean agent_registered = FALSE;
42 static const char *agent_capability = NULL;
43 static DBusMessage *pending_message = NULL;
44 static char *agent_saved_prompt = NULL;
45 static int agent_saved_point = 0;
46
47 static void agent_prompt(const char *msg)
48 {
49         char *prompt;
50
51         /* Normal use should not prompt for user input to the agent a second
52          * time before it releases the prompt, but we take a safe action. */
53         if (agent_saved_prompt)
54                 return;
55
56         agent_saved_point = rl_point;
57         agent_saved_prompt = g_strdup(rl_prompt);
58
59         rl_set_prompt("");
60         rl_redisplay();
61
62         prompt = g_strdup_printf(AGENT_PROMPT "%s", msg);
63         rl_set_prompt(prompt);
64         g_free(prompt);
65
66         rl_replace_line("", 0);
67         rl_redisplay();
68 }
69
70 static void agent_release_prompt(void)
71 {
72         if (!agent_saved_prompt)
73                 return;
74
75         /* This will cause rl_expand_prompt to re-run over the last prompt, but
76          * our prompt doesn't expand anyway. */
77         rl_set_prompt(agent_saved_prompt);
78         rl_replace_line("", 0);
79         rl_point = agent_saved_point;
80         rl_redisplay();
81
82         g_free(agent_saved_prompt);
83         agent_saved_prompt = NULL;
84 }
85
86 dbus_bool_t agent_completion(void)
87 {
88         if (!pending_message)
89                 return FALSE;
90
91         return TRUE;
92 }
93
94 static void pincode_response(DBusConnection *conn, const char *input)
95 {
96         g_dbus_send_reply(conn, pending_message, DBUS_TYPE_STRING, &input,
97                                                         DBUS_TYPE_INVALID);
98 }
99
100 static void passkey_response(DBusConnection *conn, const char *input)
101 {
102         dbus_uint32_t passkey;
103         if (sscanf(input, "%u", &passkey) == 1)
104                 g_dbus_send_reply(conn, pending_message, DBUS_TYPE_UINT32,
105                                                 &passkey, DBUS_TYPE_INVALID);
106         else if (!strcmp(input, "no"))
107                 g_dbus_send_error(conn, pending_message,
108                                         "org.bluez.Error.Rejected", NULL);
109         else
110                 g_dbus_send_error(conn, pending_message,
111                                         "org.bluez.Error.Canceled", NULL);
112 }
113
114 static void confirm_response(DBusConnection *conn, const char *input)
115 {
116         if (!strcmp(input, "yes"))
117                 g_dbus_send_reply(conn, pending_message, DBUS_TYPE_INVALID);
118         else if (!strcmp(input, "no"))
119                 g_dbus_send_error(conn, pending_message,
120                                         "org.bluez.Error.Rejected", NULL);
121         else
122                 g_dbus_send_error(conn, pending_message,
123                                         "org.bluez.Error.Canceled", NULL);
124 }
125
126 dbus_bool_t agent_input(DBusConnection *conn, const char *input)
127 {
128         const char *member;
129
130         if (!pending_message)
131                 return FALSE;
132
133         agent_release_prompt();
134
135         member = dbus_message_get_member(pending_message);
136
137         if (!strcmp(member, "RequestPinCode"))
138                 pincode_response(conn, input);
139         else if (!strcmp(member, "RequestPasskey"))
140                 passkey_response(conn, input);
141         else if (!strcmp(member, "RequestConfirmation"))
142                 confirm_response(conn, input);
143         else if (!strcmp(member, "RequestAuthorization"))
144                 confirm_response(conn, input);
145         else if (!strcmp(member, "AuthorizeService"))
146                 confirm_response(conn, input);
147         else
148                 g_dbus_send_error(conn, pending_message,
149                                         "org.bluez.Error.Canceled", NULL);
150
151         dbus_message_unref(pending_message);
152         pending_message = NULL;
153
154         return TRUE;
155 }
156
157 static void agent_release(DBusConnection *conn)
158 {
159         agent_registered = FALSE;
160         agent_capability = NULL;
161
162         if (pending_message) {
163                 dbus_message_unref(pending_message);
164                 pending_message = NULL;
165         }
166
167         agent_release_prompt();
168
169         g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE);
170 }
171
172 static DBusMessage *release_agent(DBusConnection *conn,
173                                         DBusMessage *msg, void *user_data)
174 {
175         rl_printf("Agent released\n");
176
177         agent_release(conn);
178
179         return dbus_message_new_method_return(msg);
180 }
181
182 static DBusMessage *request_pincode(DBusConnection *conn,
183                                         DBusMessage *msg, void *user_data)
184 {
185         const char *device;
186
187         rl_printf("Request PIN code\n");
188
189         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
190                                                         DBUS_TYPE_INVALID);
191
192         agent_prompt("Enter PIN code: ");
193
194         pending_message = dbus_message_ref(msg);
195
196         return NULL;
197 }
198
199 static DBusMessage *display_pincode(DBusConnection *conn,
200                                         DBusMessage *msg, void *user_data)
201 {
202         const char *device;
203         const char *pincode;
204
205         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
206                                 DBUS_TYPE_STRING, &pincode, DBUS_TYPE_INVALID);
207
208         rl_printf(AGENT_PROMPT "PIN code: %s\n", pincode);
209
210         return dbus_message_new_method_return(msg);
211 }
212
213 static DBusMessage *request_passkey(DBusConnection *conn,
214                                         DBusMessage *msg, void *user_data)
215 {
216         const char *device;
217
218         rl_printf("Request passkey\n");
219
220         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
221                                                         DBUS_TYPE_INVALID);
222
223         agent_prompt("Enter passkey (number in 0-999999): ");
224
225         pending_message = dbus_message_ref(msg);
226
227         return NULL;
228 }
229
230 static DBusMessage *display_passkey(DBusConnection *conn,
231                                         DBusMessage *msg, void *user_data)
232 {
233         const char *device;
234         dbus_uint32_t passkey;
235         dbus_uint16_t entered;
236         char passkey_full[7];
237
238         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
239                         DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_UINT16, &entered,
240                                                         DBUS_TYPE_INVALID);
241
242         snprintf(passkey_full, sizeof(passkey_full), "%.6u", passkey);
243         passkey_full[6] = '\0';
244
245         if (entered > strlen(passkey_full))
246                 entered = strlen(passkey_full);
247
248         rl_printf(AGENT_PROMPT "Passkey: "
249                         COLOR_BOLDGRAY "%.*s" COLOR_BOLDWHITE "%s\n" COLOR_OFF,
250                                 entered, passkey_full, passkey_full + entered);
251
252         return dbus_message_new_method_return(msg);
253 }
254
255 static DBusMessage *request_confirmation(DBusConnection *conn,
256                                         DBusMessage *msg, void *user_data)
257 {
258         const char *device;
259         dbus_uint32_t passkey;
260         char *str;
261
262         rl_printf("Request confirmation\n");
263
264         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
265                                 DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID);
266
267         str = g_strdup_printf("Confirm passkey %06u (yes/no): ", passkey);
268         agent_prompt(str);
269         g_free(str);
270
271         pending_message = dbus_message_ref(msg);
272
273         return NULL;
274 }
275
276 static DBusMessage *request_authorization(DBusConnection *conn,
277                                         DBusMessage *msg, void *user_data)
278 {
279         const char *device;
280
281         rl_printf("Request authorization\n");
282
283         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
284                                                         DBUS_TYPE_INVALID);
285
286         agent_prompt("Accept pairing (yes/no): ");
287
288         pending_message = dbus_message_ref(msg);
289
290         return NULL;
291 }
292
293 static DBusMessage *authorize_service(DBusConnection *conn,
294                                         DBusMessage *msg, void *user_data)
295 {
296         const char *device, *uuid;
297         char *str;
298
299         rl_printf("Authorize service\n");
300
301         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
302                                 DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID);
303
304         str = g_strdup_printf("Authorize service %s (yes/no): ", uuid);
305         agent_prompt(str);
306         g_free(str);
307
308         pending_message = dbus_message_ref(msg);
309
310         return NULL;
311 }
312
313 static DBusMessage *cancel_request(DBusConnection *conn,
314                                         DBusMessage *msg, void *user_data)
315 {
316         rl_printf("Request canceled\n");
317
318         agent_release_prompt();
319         dbus_message_unref(pending_message);
320         pending_message = NULL;
321
322         return dbus_message_new_method_return(msg);
323 }
324
325 static const GDBusMethodTable methods[] = {
326         { GDBUS_METHOD("Release", NULL, NULL, release_agent) },
327         { GDBUS_ASYNC_METHOD("RequestPinCode",
328                         GDBUS_ARGS({ "device", "o" }),
329                         GDBUS_ARGS({ "pincode", "s" }), request_pincode) },
330         { GDBUS_METHOD("DisplayPinCode",
331                         GDBUS_ARGS({ "device", "o" }, { "pincode", "s" }),
332                         NULL, display_pincode) },
333         { GDBUS_ASYNC_METHOD("RequestPasskey",
334                         GDBUS_ARGS({ "device", "o" }),
335                         GDBUS_ARGS({ "passkey", "u" }), request_passkey) },
336         { GDBUS_METHOD("DisplayPasskey",
337                         GDBUS_ARGS({ "device", "o" }, { "passkey", "u" },
338                                                         { "entered", "q" }),
339                         NULL, display_passkey) },
340         { GDBUS_ASYNC_METHOD("RequestConfirmation",
341                         GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }),
342                         NULL, request_confirmation) },
343         { GDBUS_ASYNC_METHOD("RequestAuthorization",
344                         GDBUS_ARGS({ "device", "o" }),
345                         NULL, request_authorization) },
346         { GDBUS_ASYNC_METHOD("AuthorizeService",
347                         GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }),
348                         NULL,  authorize_service) },
349         { GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) },
350         { }
351 };
352
353 static void register_agent_setup(DBusMessageIter *iter, void *user_data)
354 {
355         const char *path = AGENT_PATH;
356         const char *capability = agent_capability;
357
358         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
359         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &capability);
360 }
361
362 static void register_agent_reply(DBusMessage *message, void *user_data)
363 {
364         DBusConnection *conn = user_data;
365         DBusError error;
366
367         dbus_error_init(&error);
368
369         if (dbus_set_error_from_message(&error, message) == FALSE) {
370                 agent_registered = TRUE;
371                 rl_printf("Agent registered\n");
372         } else {
373                 rl_printf("Failed to register agent: %s\n", error.name);
374                 dbus_error_free(&error);
375
376                 if (g_dbus_unregister_interface(conn, AGENT_PATH,
377                                                 AGENT_INTERFACE) == FALSE)
378                         rl_printf("Failed to unregister agent object\n");
379         }
380 }
381
382 void agent_register(DBusConnection *conn, GDBusProxy *manager,
383                                                 const char *capability)
384
385 {
386         if (agent_registered == TRUE) {
387                 rl_printf("Agent is already registered\n");
388                 return;
389         }
390
391         agent_capability = capability;
392
393         if (g_dbus_register_interface(conn, AGENT_PATH,
394                                         AGENT_INTERFACE, methods,
395                                         NULL, NULL, NULL, NULL) == FALSE) {
396                 rl_printf("Failed to register agent object\n");
397                 return;
398         }
399
400         if (g_dbus_proxy_method_call(manager, "RegisterAgent",
401                                                 register_agent_setup,
402                                                 register_agent_reply,
403                                                 conn, NULL) == FALSE) {
404                 rl_printf("Failed to call register agent method\n");
405                 return;
406         }
407
408         agent_capability = NULL;
409 }
410
411 static void unregister_agent_setup(DBusMessageIter *iter, void *user_data)
412 {
413         const char *path = AGENT_PATH;
414
415         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
416 }
417
418 static void unregister_agent_reply(DBusMessage *message, void *user_data)
419 {
420         DBusConnection *conn = user_data;
421         DBusError error;
422
423         dbus_error_init(&error);
424
425         if (dbus_set_error_from_message(&error, message) == FALSE) {
426                 rl_printf("Agent unregistered\n");
427                 agent_release(conn);
428         } else {
429                 rl_printf("Failed to unregister agent: %s\n", error.name);
430                 dbus_error_free(&error);
431         }
432 }
433
434 void agent_unregister(DBusConnection *conn, GDBusProxy *manager)
435 {
436         if (agent_registered == FALSE) {
437                 rl_printf("No agent is registered\n");
438                 return;
439         }
440
441         if (!manager) {
442                 rl_printf("Agent unregistered\n");
443                 agent_release(conn);
444                 return;
445         }
446
447         if (g_dbus_proxy_method_call(manager, "UnregisterAgent",
448                                                 unregister_agent_setup,
449                                                 unregister_agent_reply,
450                                                 conn, NULL) == FALSE) {
451                 rl_printf("Failed to call unregister agent method\n");
452                 return;
453         }
454 }
455
456 static void request_default_setup(DBusMessageIter *iter, void *user_data)
457 {
458         const char *path = AGENT_PATH;
459
460         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
461 }
462
463 static void request_default_reply(DBusMessage *message, void *user_data)
464 {
465         DBusError error;
466
467         dbus_error_init(&error);
468
469         if (dbus_set_error_from_message(&error, message) == TRUE) {
470                 rl_printf("Failed to request default agent: %s\n", error.name);
471                 dbus_error_free(&error);
472                 return;
473         }
474
475         rl_printf("Default agent request successful\n");
476 }
477
478 void agent_default(DBusConnection *conn, GDBusProxy *manager)
479 {
480         if (agent_registered == FALSE) {
481                 rl_printf("No agent is registered\n");
482                 return;
483         }
484
485         if (g_dbus_proxy_method_call(manager, "RequestDefaultAgent",
486                                                 request_default_setup,
487                                                 request_default_reply,
488                                                 NULL, NULL) == FALSE) {
489                 rl_printf("Failed to call request default agent method\n");
490                 return;
491         }
492 }