3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2006-2010 Nokia Corporation
6 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include <sys/socket.h>
33 #include <sys/ioctl.h>
35 #include <bluetooth/bluetooth.h>
36 #include <bluetooth/sdp.h>
39 #include <dbus/dbus.h>
49 #define REQUEST_TIMEOUT (60 * 1000) /* 60 seconds */
52 AGENT_REQUEST_PASSKEY,
53 AGENT_REQUEST_CONFIRMATION,
54 AGENT_REQUEST_PINCODE,
55 AGENT_REQUEST_AUTHORIZE,
56 AGENT_REQUEST_CONFIRM_MODE
57 } agent_request_type_t;
60 struct btd_adapter *adapter;
64 struct agent_request *request;
66 agent_remove_cb remove_cb;
71 struct agent_request {
72 agent_request_type_t type;
75 DBusPendingCall *call;
78 GDestroyNotify destroy;
81 static DBusConnection *connection = NULL;
83 static int request_fallback(struct agent_request *req,
84 DBusPendingCallNotifyFunction function);
86 static void agent_release(struct agent *agent)
90 DBG("Releasing agent %s, %s", agent->name, agent->path);
95 message = dbus_message_new_method_call(agent->name, agent->path,
96 "org.bluez.Agent", "Release");
97 if (message == NULL) {
98 error("Couldn't allocate D-Bus message");
102 g_dbus_send_message(connection, message);
105 static int send_cancel_request(struct agent_request *req)
107 DBusMessage *message;
109 DBG("Sending Cancel request to %s, %s", req->agent->name,
112 message = dbus_message_new_method_call(req->agent->name, req->agent->path,
113 "org.bluez.Agent", "Cancel");
114 if (message == NULL) {
115 error("Couldn't allocate D-Bus message");
119 g_dbus_send_message(connection, message);
124 static void agent_request_free(struct agent_request *req, gboolean destroy)
127 dbus_message_unref(req->msg);
129 dbus_pending_call_unref(req->call);
130 if (req->agent && req->agent->request)
131 req->agent->request = NULL;
132 if (destroy && req->destroy)
133 req->destroy(req->user_data);
137 static void agent_exited(DBusConnection *conn, void *user_data)
139 struct agent *agent = user_data;
141 DBG("Agent exited without calling Unregister");
143 agent->exited = TRUE;
148 void agent_free(struct agent *agent)
153 if (agent->remove_cb)
154 agent->remove_cb(agent, agent->remove_cb_data);
156 if (agent->request) {
158 agent_pincode_cb pincode_cb;
161 dbus_error_init(&err);
162 dbus_set_error_const(&err, "org.bluez.Error.Failed", "Canceled");
164 switch (agent->request->type) {
165 case AGENT_REQUEST_PINCODE:
166 pincode_cb = agent->request->cb;
167 pincode_cb(agent, &err, NULL, agent->request->user_data);
170 cb = agent->request->cb;
171 cb(agent, &err, agent->request->user_data);
174 dbus_error_free(&err);
179 if (!agent->exited) {
180 g_dbus_remove_watch(connection, agent->listener_id);
181 agent_release(agent);
190 struct agent *agent_create(struct btd_adapter *adapter, const char *name,
191 const char *path, uint8_t capability,
192 agent_remove_cb cb, void *remove_cb_data)
196 agent = g_new0(struct agent, 1);
198 agent->adapter = adapter;
199 agent->name = g_strdup(name);
200 agent->path = g_strdup(path);
201 agent->capability = capability;
202 agent->remove_cb = cb;
203 agent->remove_cb_data = remove_cb_data;
205 agent->listener_id = g_dbus_add_disconnect_watch(connection, name,
212 static struct agent_request *agent_request_new(struct agent *agent,
213 agent_request_type_t type,
216 GDestroyNotify destroy)
218 struct agent_request *req;
220 req = g_new0(struct agent_request, 1);
225 req->user_data = user_data;
226 req->destroy = destroy;
231 int agent_cancel(struct agent *agent)
236 if (agent->request->call)
237 dbus_pending_call_cancel(agent->request->call);
240 send_cancel_request(agent->request);
242 agent_request_free(agent->request, TRUE);
243 agent->request = NULL;
248 static void simple_agent_reply(DBusPendingCall *call, void *user_data)
250 struct agent_request *req = user_data;
251 struct agent *agent = req->agent;
252 DBusMessage *message;
254 agent_cb cb = req->cb;
256 /* steal_reply will always return non-NULL since the callback
257 * is only called after a reply has been received */
258 message = dbus_pending_call_steal_reply(call);
260 dbus_error_init(&err);
261 if (dbus_set_error_from_message(&err, message)) {
262 if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
263 g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
264 request_fallback(req, simple_agent_reply) == 0) {
265 dbus_error_free(&err);
269 error("Agent replied with an error: %s, %s",
270 err.name, err.message);
272 #ifdef __TIZEN_PATCH__
273 if (strcmp(err.message, "CanceledbyUser") == 0)
275 set_cancel_from_authentication_req(req->user_data);
278 cb(agent, &err, req->user_data);
280 if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
282 dbus_message_unref(message);
283 dbus_error_free(&err);
287 dbus_error_free(&err);
291 dbus_error_init(&err);
292 if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
293 error("Wrong reply signature: %s", err.message);
294 cb(agent, &err, req->user_data);
295 dbus_error_free(&err);
299 cb(agent, NULL, req->user_data);
301 dbus_message_unref(message);
303 agent->request = NULL;
304 agent_request_free(req, TRUE);
307 static int agent_call_authorize(struct agent_request *req,
308 const char *device_path,
311 struct agent *agent = req->agent;
313 req->msg = dbus_message_new_method_call(agent->name, agent->path,
314 "org.bluez.Agent", "Authorize");
316 error("Couldn't allocate D-Bus message");
320 dbus_message_append_args(req->msg,
321 DBUS_TYPE_OBJECT_PATH, &device_path,
322 DBUS_TYPE_STRING, &uuid,
325 if (dbus_connection_send_with_reply(connection, req->msg,
326 &req->call, REQUEST_TIMEOUT) == FALSE) {
327 error("D-Bus send failed");
331 dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
335 int agent_authorize(struct agent *agent,
340 GDestroyNotify destroy)
342 struct agent_request *req;
345 info("agent_authorize");
349 req = agent_request_new(agent, AGENT_REQUEST_AUTHORIZE, cb,
352 err = agent_call_authorize(req, path, uuid);
354 agent_request_free(req, FALSE);
358 agent->request = req;
360 DBG("authorize request was sent for %s", path);
365 static void pincode_reply(DBusPendingCall *call, void *user_data)
367 struct agent_request *req = user_data;
368 struct agent *agent = req->agent;
369 struct btd_adapter *adapter = agent->adapter;
370 agent_pincode_cb cb = req->cb;
371 DBusMessage *message;
377 adapter_get_address(adapter, &sba);
379 /* steal_reply will always return non-NULL since the callback
380 * is only called after a reply has been received */
381 message = dbus_pending_call_steal_reply(call);
383 dbus_error_init(&err);
384 if (dbus_set_error_from_message(&err, message)) {
385 if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
386 g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
387 request_fallback(req, pincode_reply) == 0) {
388 dbus_error_free(&err);
392 error("Agent replied with an error: %s, %s",
393 err.name, err.message);
395 #ifdef __TIZEN_PATCH__
396 if (strcmp(err.message, "CanceledbyUser") == 0)
398 set_cancel_from_authentication_req(req->user_data);
402 cb(agent, &err, NULL, req->user_data);
403 dbus_error_free(&err);
407 dbus_error_init(&err);
408 if (!dbus_message_get_args(message, &err,
409 DBUS_TYPE_STRING, &pin,
410 DBUS_TYPE_INVALID)) {
411 error("Wrong passkey reply signature: %s", err.message);
412 cb(agent, &err, NULL, req->user_data);
413 dbus_error_free(&err);
419 dbus_error_init(&err);
420 if (len > 16 || len < 1) {
421 error("Invalid PIN length (%zu) from agent", len);
422 dbus_set_error_const(&err, "org.bluez.Error.InvalidArgs",
423 "Invalid passkey length");
424 cb(agent, &err, NULL, req->user_data);
425 dbus_error_free(&err);
429 cb(agent, NULL, pin, req->user_data);
433 dbus_message_unref(message);
435 dbus_pending_call_cancel(req->call);
436 agent->request = NULL;
437 agent_request_free(req, TRUE);
440 static int pincode_request_new(struct agent_request *req, const char *device_path,
443 struct agent *agent = req->agent;
445 req->msg = dbus_message_new_method_call(agent->name, agent->path,
446 "org.bluez.Agent", "RequestPinCode");
447 if (req->msg == NULL) {
448 error("Couldn't allocate D-Bus message");
452 dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
455 if (dbus_connection_send_with_reply(connection, req->msg,
456 &req->call, REQUEST_TIMEOUT) == FALSE) {
457 error("D-Bus send failed");
461 dbus_pending_call_set_notify(req->call, pincode_reply, req, NULL);
465 int agent_request_pincode(struct agent *agent, struct btd_device *device,
466 agent_pincode_cb cb, void *user_data,
467 GDestroyNotify destroy)
469 struct agent_request *req;
470 const gchar *dev_path = device_get_path(device);
476 req = agent_request_new(agent, AGENT_REQUEST_PINCODE, cb,
479 err = pincode_request_new(req, dev_path, FALSE);
483 agent->request = req;
492 static int confirm_mode_change_request_new(struct agent_request *req,
495 struct agent *agent = req->agent;
497 req->msg = dbus_message_new_method_call(agent->name, agent->path,
498 "org.bluez.Agent", "ConfirmModeChange");
499 if (req->msg == NULL) {
500 error("Couldn't allocate D-Bus message");
504 dbus_message_append_args(req->msg,
505 DBUS_TYPE_STRING, &mode,
508 if (dbus_connection_send_with_reply(connection, req->msg,
509 &req->call, REQUEST_TIMEOUT) == FALSE) {
510 error("D-Bus send failed");
514 dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
518 int agent_confirm_mode_change(struct agent *agent, const char *new_mode,
519 agent_cb cb, void *user_data,
520 GDestroyNotify destroy)
522 struct agent_request *req;
528 DBG("Calling Agent.ConfirmModeChange: name=%s, path=%s, mode=%s",
529 agent->name, agent->path, new_mode);
531 req = agent_request_new(agent, AGENT_REQUEST_CONFIRM_MODE,
532 cb, user_data, destroy);
534 err = confirm_mode_change_request_new(req, new_mode);
538 agent->request = req;
543 agent_request_free(req, FALSE);
547 static void passkey_reply(DBusPendingCall *call, void *user_data)
549 struct agent_request *req = user_data;
550 struct agent *agent = req->agent;
551 agent_passkey_cb cb = req->cb;
552 DBusMessage *message;
556 /* steal_reply will always return non-NULL since the callback
557 * is only called after a reply has been received */
558 message = dbus_pending_call_steal_reply(call);
560 dbus_error_init(&err);
561 if (dbus_set_error_from_message(&err, message)) {
562 if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
563 g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
564 request_fallback(req, passkey_reply) == 0) {
565 dbus_error_free(&err);
569 error("Agent replied with an error: %s, %s",
570 err.name, err.message);
571 #ifdef __TIZEN_PATCH__
572 if (strcmp(err.message, "CanceledbyUser") == 0)
574 set_cancel_from_authentication_req(req->user_data);
578 cb(agent, &err, 0, req->user_data);
579 dbus_error_free(&err);
583 dbus_error_init(&err);
584 if (!dbus_message_get_args(message, &err,
585 DBUS_TYPE_UINT32, &passkey,
586 DBUS_TYPE_INVALID)) {
587 error("Wrong passkey reply signature: %s", err.message);
588 cb(agent, &err, 0, req->user_data);
589 dbus_error_free(&err);
593 cb(agent, NULL, passkey, req->user_data);
597 dbus_message_unref(message);
599 dbus_pending_call_cancel(req->call);
600 agent->request = NULL;
601 agent_request_free(req, TRUE);
604 static int passkey_request_new(struct agent_request *req,
605 const char *device_path)
607 struct agent *agent = req->agent;
609 req->msg = dbus_message_new_method_call(agent->name, agent->path,
610 "org.bluez.Agent", "RequestPasskey");
611 if (req->msg == NULL) {
612 error("Couldn't allocate D-Bus message");
616 dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
619 if (dbus_connection_send_with_reply(connection, req->msg,
620 &req->call, REQUEST_TIMEOUT) == FALSE) {
621 error("D-Bus send failed");
625 dbus_pending_call_set_notify(req->call, passkey_reply, req, NULL);
629 int agent_request_passkey(struct agent *agent, struct btd_device *device,
630 agent_passkey_cb cb, void *user_data,
631 GDestroyNotify destroy)
633 struct agent_request *req;
634 const gchar *dev_path = device_get_path(device);
640 DBG("Calling Agent.RequestPasskey: name=%s, path=%s",
641 agent->name, agent->path);
643 req = agent_request_new(agent, AGENT_REQUEST_PASSKEY, cb,
646 err = passkey_request_new(req, dev_path);
650 agent->request = req;
655 agent_request_free(req, FALSE);
659 static int confirmation_request_new(struct agent_request *req,
660 const char *device_path,
663 struct agent *agent = req->agent;
665 req->msg = dbus_message_new_method_call(agent->name, agent->path,
666 "org.bluez.Agent", "RequestConfirmation");
667 if (req->msg == NULL) {
668 error("Couldn't allocate D-Bus message");
672 dbus_message_append_args(req->msg,
673 DBUS_TYPE_OBJECT_PATH, &device_path,
674 DBUS_TYPE_UINT32, &passkey,
677 if (dbus_connection_send_with_reply(connection, req->msg,
678 &req->call, REQUEST_TIMEOUT) == FALSE) {
679 error("D-Bus send failed");
683 dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
688 int agent_request_confirmation(struct agent *agent, struct btd_device *device,
689 uint32_t passkey, agent_cb cb,
690 void *user_data, GDestroyNotify destroy)
692 struct agent_request *req;
693 const gchar *dev_path = device_get_path(device);
699 DBG("Calling Agent.RequestConfirmation: name=%s, path=%s, passkey=%06u",
700 agent->name, agent->path, passkey);
702 req = agent_request_new(agent, AGENT_REQUEST_CONFIRMATION, cb,
705 err = confirmation_request_new(req, dev_path, passkey);
709 agent->request = req;
714 agent_request_free(req, FALSE);
718 static int request_fallback(struct agent_request *req,
719 DBusPendingCallNotifyFunction function)
721 struct btd_adapter *adapter = req->agent->adapter;
722 struct agent *adapter_agent = adapter_get_agent(adapter);
725 if (req->agent == adapter_agent || adapter_agent == NULL)
728 dbus_pending_call_cancel(req->call);
729 dbus_pending_call_unref(req->call);
731 msg = dbus_message_copy(req->msg);
733 dbus_message_set_destination(msg, adapter_agent->name);
734 dbus_message_set_path(msg, adapter_agent->path);
736 if (dbus_connection_send_with_reply(connection, msg,
737 &req->call, REQUEST_TIMEOUT) == FALSE) {
738 error("D-Bus send failed");
739 dbus_message_unref(msg);
743 req->agent->request = NULL;
744 req->agent = adapter_agent;
745 req->agent->request = req;
747 dbus_message_unref(req->msg);
750 dbus_pending_call_set_notify(req->call, function, req, NULL);
755 int agent_display_passkey(struct agent *agent, struct btd_device *device,
758 DBusMessage *message;
759 const gchar *dev_path = device_get_path(device);
761 message = dbus_message_new_method_call(agent->name, agent->path,
762 "org.bluez.Agent", "DisplayPasskey");
764 error("Couldn't allocate D-Bus message");
768 dbus_message_append_args(message,
769 DBUS_TYPE_OBJECT_PATH, &dev_path,
770 DBUS_TYPE_UINT32, &passkey,
773 if (!g_dbus_send_message(connection, message)) {
774 error("D-Bus send failed");
775 dbus_message_unref(message);
782 uint8_t agent_get_io_capability(struct agent *agent)
784 return agent->capability;
787 gboolean agent_matches(struct agent *agent, const char *name, const char *path)
789 if (g_str_equal(agent->name, name) && g_str_equal(agent->path, path))
795 gboolean agent_is_busy(struct agent *agent, void *user_data)
800 if (user_data && user_data != agent->request->user_data)
806 void agent_exit(void)
808 dbus_connection_unref(connection);
812 void agent_init(void)
814 connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);