agent: Fixes refcounting issue
[platform/upstream/connman.git] / src / agent.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <gdbus.h>
31
32 #include "connman.h"
33
34 static DBusConnection *connection = NULL;
35 static guint agent_watch = 0;
36 static gchar *agent_path = NULL;
37 static gchar *agent_sender = NULL;
38
39 typedef void (*agent_queue_cb)(DBusMessage *reply, void *user_data);
40
41 struct agent_data {
42         struct connman_service *service;
43         DBusMessage *msg;
44         DBusPendingCall *call;
45         int timeout;
46         agent_queue_cb callback;
47         void *user_data;
48 };
49
50 static GList *agent_queue = NULL;
51 static struct agent_data *agent_request = NULL;
52
53 static void agent_free(void)
54 {
55         agent_watch = 0;
56
57         g_free(agent_sender);
58         agent_sender = NULL;
59
60         g_free(agent_path);
61         agent_path = NULL;
62
63         __connman_agent_cancel(NULL);
64 }
65
66 static void agent_disconnect(DBusConnection *conn, void *data)
67 {
68         DBG("data %p", data);
69
70         agent_free();
71 }
72
73 int __connman_agent_register(const char *sender, const char *path)
74 {
75         DBG("sender %s path %s", sender, path);
76
77         if (agent_path != NULL)
78                 return -EEXIST;
79
80         agent_sender = g_strdup(sender);
81         agent_path = g_strdup(path);
82
83         agent_watch = g_dbus_add_disconnect_watch(connection, sender,
84                                                 agent_disconnect, NULL, NULL);
85
86         return 0;
87 }
88
89 int __connman_agent_unregister(const char *sender, const char *path)
90 {
91         DBG("sender %s path %s", sender, path);
92
93         if (agent_path == NULL)
94                 return -ESRCH;
95
96         if (agent_watch > 0)
97                 g_dbus_remove_watch(connection, agent_watch);
98
99         agent_free();
100
101         return 0;
102 }
103
104 static void agent_data_free(struct agent_data *data)
105 {
106         if (data == NULL)
107                 return;
108         if (data->service != NULL)
109                 connman_service_unref(data->service);
110         if (data->msg != NULL)
111                 dbus_message_unref(data->msg);
112         if (data->call != NULL)
113                 dbus_pending_call_cancel(data->call);
114
115         g_free(data);
116 }
117
118 static void agent_receive_message(DBusPendingCall *call, void *user_data);
119
120 static int agent_send_next_request(void)
121 {
122         if (agent_request != NULL)
123                 return -EBUSY;
124
125         if (agent_queue == NULL)
126                 return 0;
127
128         agent_request = agent_queue->data;
129         agent_queue = g_list_remove(agent_queue, agent_request);
130
131         if (dbus_connection_send_with_reply(connection, agent_request->msg,
132                                         &agent_request->call,
133                                         agent_request->timeout)
134                         == FALSE)
135                 goto fail;
136
137         if (agent_request->call == NULL)
138                 goto fail;
139
140         if (dbus_pending_call_set_notify(agent_request->call,
141                                         agent_receive_message, agent_request,
142                                         NULL) == FALSE)
143                 goto fail;
144
145         dbus_message_unref(agent_request->msg);
146         agent_request->msg = NULL;
147         return 0;
148
149 fail:
150         agent_data_free(agent_request);
151         agent_request = NULL;
152         return -ESRCH;
153 }
154
155 static int agent_send_cancel(void)
156 {
157         DBusMessage *message;
158
159         if (agent_sender == NULL)
160                 return 0;
161
162         message = dbus_message_new_method_call(agent_sender, agent_path,
163                         CONNMAN_AGENT_INTERFACE, "Cancel");
164         if (message != NULL) {
165                 dbus_message_set_no_reply(message, TRUE);
166                 g_dbus_send_message(connection, message);
167                 return 0;
168         }
169
170         connman_warn("Failed to send Cancel message to agent");
171         return -ESRCH;
172 }
173
174 static void agent_receive_message(DBusPendingCall *call, void *user_data)
175 {
176         struct agent_data *queue_data = user_data;
177         DBusMessage *reply;
178
179         DBG("waiting for %p received %p", agent_request, queue_data);
180
181         if (agent_request != queue_data) {
182                 connman_error("Agent callback expected %p got %p",
183                                 agent_request, queue_data);
184                 return;
185         }
186
187         reply = dbus_pending_call_steal_reply(call);
188         dbus_pending_call_unref(call);
189         queue_data->call = NULL;
190
191         if (dbus_message_is_error(reply, "org.freedesktop.DBus.Error.Timeout")
192                         == TRUE || dbus_message_is_error(reply,
193                                         "org.freedesktop.DBus.Error.TimedOut")
194                         == TRUE) {
195                 agent_send_cancel();
196         }
197
198         queue_data->callback(reply, queue_data->user_data);
199         dbus_message_unref(reply);
200
201         agent_data_free(queue_data);
202         agent_request = NULL;
203
204         agent_send_next_request();
205 }
206
207 static int agent_queue_message(struct connman_service *service,
208                 DBusMessage *msg, int timeout,
209                 agent_queue_cb callback, void *user_data)
210 {
211         struct agent_data *queue_data;
212
213         if (service == NULL || callback == NULL)
214                 return -EBADMSG;
215
216         queue_data = g_new0(struct agent_data, 1);
217         if (queue_data == NULL)
218                 return -ENOMEM;
219
220         queue_data->service = connman_service_ref(service);
221         queue_data->msg = dbus_message_ref(msg);
222         queue_data->timeout = timeout;
223         queue_data->callback = callback;
224         queue_data->user_data = user_data;
225         agent_queue = g_list_append(agent_queue, queue_data);
226
227         return agent_send_next_request();
228 }
229
230 void __connman_agent_cancel(struct connman_service *service)
231 {
232         GList *item, *next;
233         struct agent_data *queued_req;
234
235         DBG("service %p", service);
236
237         item = agent_queue;
238
239         while (item != NULL) {
240                 next = g_list_next(item);
241                 queued_req = item->data;
242
243                 if (queued_req->service == service || service == NULL) {
244                         agent_data_free(queued_req);
245                         agent_queue = g_list_delete_link(agent_queue, item);
246                 }
247
248                 item = next;
249         }
250
251         if (agent_request == NULL)
252                 return;
253
254         if (agent_request->service != service && service != NULL)
255                 return;
256
257         agent_data_free(agent_request);
258         agent_request = NULL;
259
260         agent_send_cancel();
261
262         agent_send_next_request();
263 }
264
265 static connman_bool_t check_reply_has_dict(DBusMessage *reply)
266 {
267         const char *signature = DBUS_TYPE_ARRAY_AS_STRING
268                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
269                 DBUS_TYPE_STRING_AS_STRING
270                 DBUS_TYPE_VARIANT_AS_STRING
271                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
272
273         if (dbus_message_has_signature(reply, signature) == TRUE)
274                 return TRUE;
275
276         connman_warn("Reply %s to %s from %s has wrong signature %s",
277                         signature,
278                         dbus_message_get_interface(reply),
279                         dbus_message_get_sender(reply),
280                         dbus_message_get_signature(reply));
281
282         return FALSE;
283 }
284
285 struct request_input_reply {
286         struct connman_service *service;
287         authentication_cb_t callback;
288         void *user_data;
289 };
290
291 static void request_input_passphrase_reply(DBusMessage *reply, void *user_data)
292 {
293         struct request_input_reply *passphrase_reply = user_data;
294         connman_bool_t values_received = FALSE;
295         connman_bool_t wps = FALSE;
296         const char *error = NULL;
297         char *identity = NULL;
298         char *passphrase = NULL;
299         char *wpspin = NULL;
300         char *key;
301         char *name = NULL;
302         int name_len = 0;
303         DBusMessageIter iter, dict;
304
305         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
306                 error = dbus_message_get_error_name(reply);
307                 goto done;
308         }
309
310         if (check_reply_has_dict(reply) == FALSE)
311                 goto done;
312
313         values_received = TRUE;
314
315         dbus_message_iter_init(reply, &iter);
316         dbus_message_iter_recurse(&iter, &dict);
317         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
318                 DBusMessageIter entry, value;
319
320                 dbus_message_iter_recurse(&dict, &entry);
321                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
322                         break;
323
324                 dbus_message_iter_get_basic(&entry, &key);
325
326                 if (g_str_equal(key, "Identity")) {
327                         dbus_message_iter_next(&entry);
328                         if (dbus_message_iter_get_arg_type(&entry)
329                                                         != DBUS_TYPE_VARIANT)
330                                 break;
331                         dbus_message_iter_recurse(&entry, &value);
332                         dbus_message_iter_get_basic(&value, &identity);
333
334                 } else if (g_str_equal(key, "Passphrase")) {
335                         dbus_message_iter_next(&entry);
336                         if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
337                                 break;
338                         dbus_message_iter_recurse(&entry, &value);
339                         dbus_message_iter_get_basic(&value, &passphrase);
340
341                 } else if (g_str_equal(key, "WPS")) {
342                         wps = TRUE;
343
344                         dbus_message_iter_next(&entry);
345                         if (dbus_message_iter_get_arg_type(&entry)
346                                                         != DBUS_TYPE_VARIANT)
347                                 break;
348                         dbus_message_iter_recurse(&entry, &value);
349                         dbus_message_iter_get_basic(&value, &wpspin);
350                         break;
351                 } else if (g_str_equal(key, "Name")) {
352                         dbus_message_iter_next(&entry);
353                         if (dbus_message_iter_get_arg_type(&entry)
354                                                         != DBUS_TYPE_VARIANT)
355                                 break;
356                         dbus_message_iter_recurse(&entry, &value);
357                         dbus_message_iter_get_basic(&value, &name);
358                         name_len = strlen(name);
359                 } else if (g_str_equal(key, "SSID")) {
360                         dbus_message_iter_next(&entry);
361                         if (dbus_message_iter_get_arg_type(&entry)
362                                                         != DBUS_TYPE_VARIANT)
363                                 break;
364                         dbus_message_iter_recurse(&entry, &value);
365                         if (dbus_message_iter_get_arg_type(&value)
366                                                         != DBUS_TYPE_VARIANT)
367                                 break;
368                         if (dbus_message_iter_get_element_type(&value)
369                                                         != DBUS_TYPE_VARIANT)
370                                 break;
371                         dbus_message_iter_get_fixed_array(&value, &name,
372                                                         &name_len);
373                 }
374                 dbus_message_iter_next(&dict);
375         }
376
377 done:
378         passphrase_reply->callback(passphrase_reply->service, values_received,
379                                 name, name_len,
380                                 identity, passphrase,
381                                 wps, wpspin, error,
382                                 passphrase_reply->user_data);
383         g_free(passphrase_reply);
384 }
385
386 static void request_input_append_alternates(DBusMessageIter *iter,
387                                                         void *user_data)
388 {
389         const char *str = user_data;
390         char **alternates, **alternative;
391
392         if (str == NULL)
393                 return;
394
395         alternates = g_strsplit(str, ",", 0);
396         if (alternates == NULL)
397                 return;
398
399         for (alternative = alternates; *alternative != NULL; alternative++)
400                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
401                                                                 alternative);
402
403         g_strfreev(alternates);
404 }
405
406 static void request_input_append_identity(DBusMessageIter *iter,
407                                                         void *user_data)
408 {
409         char *str = "string";
410
411         connman_dbus_dict_append_basic(iter, "Type",
412                                 DBUS_TYPE_STRING, &str);
413         str = "mandatory";
414         connman_dbus_dict_append_basic(iter, "Requirement",
415                                 DBUS_TYPE_STRING, &str);
416 }
417
418 static void request_input_append_passphrase(DBusMessageIter *iter,
419                                                         void *user_data)
420 {
421         struct connman_service *service = user_data;
422         char *value;
423         const char *phase2;
424
425         switch (__connman_service_get_security(service)) {
426         case CONNMAN_SERVICE_SECURITY_WEP:
427                 value = "wep";
428                 break;
429         case CONNMAN_SERVICE_SECURITY_PSK:
430                 value = "psk";
431                 break;
432         case CONNMAN_SERVICE_SECURITY_8021X:
433                 phase2 = __connman_service_get_phase2(service);
434
435                 if (phase2 != NULL && (
436                                 g_str_has_suffix(phase2, "GTC") == TRUE ||
437                                 g_str_has_suffix(phase2, "OTP") == TRUE))
438                         value = "response";
439                 else
440                         value = "passphrase";
441
442                 break;
443         default:
444                 value = "string";
445                 break;
446         }
447         connman_dbus_dict_append_basic(iter, "Type",
448                                 DBUS_TYPE_STRING, &value);
449         value = "mandatory";
450         connman_dbus_dict_append_basic(iter, "Requirement",
451                                 DBUS_TYPE_STRING, &value);
452
453         if (__connman_service_wps_enabled(service) == TRUE) {
454                 connman_dbus_dict_append_array(iter, "Alternates",
455                                         DBUS_TYPE_STRING,
456                                         request_input_append_alternates,
457                                         "WPS");
458         }
459 }
460
461 static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
462 {
463         const char *str = "wpspin";
464
465         connman_dbus_dict_append_basic(iter, "Type",
466                                 DBUS_TYPE_STRING, &str);
467         str = "alternate";
468         connman_dbus_dict_append_basic(iter, "Requirement",
469                                 DBUS_TYPE_STRING, &str);
470 }
471
472 static void request_input_append_name(DBusMessageIter *iter, void *user_data)
473 {
474         const char *str = "string";
475
476         connman_dbus_dict_append_basic(iter, "Type",
477                                 DBUS_TYPE_STRING, &str);
478         str = "mandatory";
479         connman_dbus_dict_append_basic(iter, "Requirement",
480                                 DBUS_TYPE_STRING, &str);
481         connman_dbus_dict_append_array(iter, "Alternates",
482                                 DBUS_TYPE_STRING,
483                                 request_input_append_alternates,
484                                 "SSID");
485 }
486
487 static void request_input_append_ssid(DBusMessageIter *iter, void *user_data)
488 {
489         const char *str = "ssid";
490
491         connman_dbus_dict_append_basic(iter, "Type",
492                                 DBUS_TYPE_STRING, &str);
493         str = "alternate";
494         connman_dbus_dict_append_basic(iter, "Requirement",
495                                 DBUS_TYPE_STRING, &str);
496 }
497
498 static void request_input_append_password(DBusMessageIter *iter,
499                                                         void *user_data)
500 {
501         char *str = "passphrase";
502
503         connman_dbus_dict_append_basic(iter, "Type",
504                                 DBUS_TYPE_STRING, &str);
505         str = "mandatory";
506         connman_dbus_dict_append_basic(iter, "Requirement",
507                                 DBUS_TYPE_STRING, &str);
508 }
509
510 struct previous_passphrase_data {
511         const char *passphrase;
512         const char *type;
513 };
514
515 static void request_input_append_previouspassphrase(DBusMessageIter *iter,
516                                                         void *user_data)
517 {
518         struct previous_passphrase_data *data = user_data;
519         const char *requirement = "informational";
520
521         connman_dbus_dict_append_basic(iter, "Type",
522                                 DBUS_TYPE_STRING, &data->type);
523
524         connman_dbus_dict_append_basic(iter, "Requirement",
525                                 DBUS_TYPE_STRING, &requirement);
526
527         connman_dbus_dict_append_basic(iter, "Value",
528                                 DBUS_TYPE_STRING, &data->passphrase);
529 }
530
531 static void previous_passphrase_handler(DBusMessageIter *iter,
532                                         struct connman_service *service)
533 {
534         enum connman_service_security security;
535         struct previous_passphrase_data data;
536         struct connman_network *network;
537
538         network = __connman_service_get_network(service);
539         data.passphrase = connman_network_get_string(network, "WiFi.PinWPS");
540
541         if (connman_network_get_bool(network, "WiFi.UseWPS") == TRUE &&
542                                                 data.passphrase != NULL) {
543                 data.type = "wpspin";
544         } else {
545                 data.passphrase = __connman_service_get_passphrase(service);
546                 if (data.passphrase == NULL)
547                         return;
548
549                 security = __connman_service_get_security(service);
550                 switch (security) {
551                 case CONNMAN_SERVICE_SECURITY_WEP:
552                         data.type = "wep";
553                         break;
554                 case CONNMAN_SERVICE_SECURITY_PSK:
555                         data.type  = "psk";
556                         break;
557                 /*
558                  * This should never happen: no passphrase is set if security
559                  * is not one of the above. */
560                 default:
561                         break;
562                 }
563         }
564
565         connman_dbus_dict_append_dict(iter, "PreviousPassphrase",
566                         request_input_append_previouspassphrase, &data);
567 }
568
569 static void request_input_login_reply(DBusMessage *reply, void *user_data)
570 {
571         struct request_input_reply *username_password_reply = user_data;
572         const char *error = NULL;
573         connman_bool_t values_received = FALSE;
574         char *username = NULL;
575         char *password = NULL;
576         char *key;
577         DBusMessageIter iter, dict;
578
579         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
580                 error = dbus_message_get_error_name(reply);
581                 goto done;
582         }
583
584         if (check_reply_has_dict(reply) == FALSE)
585                 goto done;
586
587         values_received = TRUE;
588
589         dbus_message_iter_init(reply, &iter);
590         dbus_message_iter_recurse(&iter, &dict);
591         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
592                 DBusMessageIter entry, value;
593
594                 dbus_message_iter_recurse(&dict, &entry);
595                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
596                         break;
597
598                 dbus_message_iter_get_basic(&entry, &key);
599
600                 if (g_str_equal(key, "Username")) {
601                         dbus_message_iter_next(&entry);
602                         if (dbus_message_iter_get_arg_type(&entry)
603                                                         != DBUS_TYPE_VARIANT)
604                                 break;
605                         dbus_message_iter_recurse(&entry, &value);
606                         dbus_message_iter_get_basic(&value, &username);
607
608                 } else if (g_str_equal(key, "Password")) {
609                         dbus_message_iter_next(&entry);
610                         if (dbus_message_iter_get_arg_type(&entry) !=
611                                                         DBUS_TYPE_VARIANT)
612                                 break;
613                         dbus_message_iter_recurse(&entry, &value);
614                         dbus_message_iter_get_basic(&value, &password);
615                 }
616
617                 dbus_message_iter_next(&dict);
618         }
619
620 done:
621         username_password_reply->callback(username_password_reply->service,
622                                         values_received, NULL, 0,
623                                         username, password,
624                                         FALSE, NULL, error,
625                                         username_password_reply->user_data);
626         g_free(username_password_reply);
627 }
628
629 int __connman_agent_request_passphrase_input(struct connman_service *service,
630                                 authentication_cb_t callback, void *user_data)
631 {
632         DBusMessage *message;
633         const char *path;
634         DBusMessageIter iter;
635         DBusMessageIter dict;
636         struct request_input_reply *passphrase_reply;
637         int err;
638
639         if (service == NULL || agent_path == NULL || callback == NULL)
640                 return -ESRCH;
641
642         message = dbus_message_new_method_call(agent_sender, agent_path,
643                                         CONNMAN_AGENT_INTERFACE,
644                                         "RequestInput");
645         if (message == NULL)
646                 return -ENOMEM;
647
648         dbus_message_iter_init_append(message, &iter);
649
650         path = __connman_service_get_path(service);
651         dbus_message_iter_append_basic(&iter,
652                                 DBUS_TYPE_OBJECT_PATH, &path);
653
654         connman_dbus_dict_open(&iter, &dict);
655
656         if (__connman_service_is_hidden(service)) {
657                 connman_dbus_dict_append_dict(&dict, "Name",
658                                         request_input_append_name, NULL);
659                 connman_dbus_dict_append_dict(&dict, "SSID",
660                                         request_input_append_ssid, NULL);
661         }
662
663         if (__connman_service_get_security(service) ==
664                         CONNMAN_SERVICE_SECURITY_8021X) {
665                 connman_dbus_dict_append_dict(&dict, "Identity",
666                                         request_input_append_identity, service);
667         }
668
669         if (__connman_service_get_security(service) !=
670                         CONNMAN_SERVICE_SECURITY_NONE) {
671                 connman_dbus_dict_append_dict(&dict, "Passphrase",
672                                         request_input_append_passphrase, service);
673
674                 previous_passphrase_handler(&dict, service);
675         }
676
677         if (__connman_service_wps_enabled(service) == TRUE) {
678             connman_dbus_dict_append_dict(&dict, "WPS",
679                                 request_input_append_wps, NULL);
680         }
681
682         connman_dbus_dict_close(&iter, &dict);
683
684         passphrase_reply = g_try_new0(struct request_input_reply, 1);
685         if (passphrase_reply == NULL) {
686                 dbus_message_unref(message);
687                 return -ENOMEM;
688         }
689
690         passphrase_reply->service = service;
691         passphrase_reply->callback = callback;
692         passphrase_reply->user_data = user_data;
693
694         err = agent_queue_message(service, message,
695                         connman_timeout_input_request(),
696                         request_input_passphrase_reply,
697                         passphrase_reply);
698
699         if (err < 0 && err != -EBUSY) {
700                 DBG("error %d sending agent message", err);
701                 dbus_message_unref(message);
702                 g_free(passphrase_reply);
703                 return err;
704         }
705
706         dbus_message_unref(message);
707
708         return -EINPROGRESS;
709 }
710
711 int __connman_agent_request_login_input(struct connman_service *service,
712                                 authentication_cb_t callback, void *user_data)
713 {
714         DBusMessage *message;
715         const char *path;
716         DBusMessageIter iter;
717         DBusMessageIter dict;
718         struct request_input_reply *username_password_reply;
719         int err;
720
721         if (service == NULL || agent_path == NULL || callback == NULL)
722                 return -ESRCH;
723
724         message = dbus_message_new_method_call(agent_sender, agent_path,
725                                         CONNMAN_AGENT_INTERFACE,
726                                         "RequestInput");
727         if (message == NULL)
728                 return -ENOMEM;
729
730         dbus_message_iter_init_append(message, &iter);
731
732         path = __connman_service_get_path(service);
733         dbus_message_iter_append_basic(&iter,
734                                 DBUS_TYPE_OBJECT_PATH, &path);
735
736         connman_dbus_dict_open(&iter, &dict);
737
738         connman_dbus_dict_append_dict(&dict, "Username",
739                                 request_input_append_identity, service);
740
741         connman_dbus_dict_append_dict(&dict, "Password",
742                                 request_input_append_password, service);
743
744         connman_dbus_dict_close(&iter, &dict);
745
746         username_password_reply = g_try_new0(struct request_input_reply, 1);
747         if (username_password_reply == NULL) {
748                 dbus_message_unref(message);
749                 return -ENOMEM;
750         }
751
752         username_password_reply->service = service;
753         username_password_reply->callback = callback;
754         username_password_reply->user_data = user_data;
755
756         err = agent_queue_message(service, message,
757                         connman_timeout_input_request(),
758                         request_input_login_reply, username_password_reply);
759         if (err < 0 && err != -EBUSY) {
760                 DBG("error %d sending agent request", err);
761                 dbus_message_unref(message);
762                 g_free(username_password_reply);
763                 return err;
764         }
765
766         dbus_message_unref(message);
767
768         return -EINPROGRESS;
769 }
770
771 struct request_browser_reply_data {
772         struct connman_service *service;
773         browser_authentication_cb_t callback;
774         void *user_data;
775 };
776
777 static void request_browser_reply(DBusMessage *reply, void *user_data)
778 {
779         struct request_browser_reply_data *browser_reply_data = user_data;
780         connman_bool_t result = FALSE;
781         const char *error = NULL;
782
783         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
784                 error = dbus_message_get_error_name(reply);
785                 goto done;
786         }
787
788         result = TRUE;
789
790 done:
791         browser_reply_data->callback(browser_reply_data->service, result,
792                                         error, browser_reply_data->user_data);
793         g_free(browser_reply_data);
794 }
795
796 int __connman_agent_request_browser(struct connman_service *service,
797                                 browser_authentication_cb_t callback,
798                                 const char *url, void *user_data)
799 {
800         struct request_browser_reply_data *browser_reply_data;
801         DBusMessage *message;
802         DBusMessageIter iter;
803         const char *path;
804         int err;
805
806         if (service == NULL || agent_path == NULL || callback == NULL)
807                 return -ESRCH;
808
809         if (url == NULL)
810                 url = "";
811
812         message = dbus_message_new_method_call(agent_sender, agent_path,
813                                         CONNMAN_AGENT_INTERFACE,
814                                         "RequestBrowser");
815         if (message == NULL)
816                 return -ENOMEM;
817
818         dbus_message_iter_init_append(message, &iter);
819
820         path = __connman_service_get_path(service);
821         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
822
823         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &url);
824
825         browser_reply_data = g_try_new0(struct request_browser_reply_data, 1);
826         if (browser_reply_data == NULL) {
827                 dbus_message_unref(message);
828                 return -ENOMEM;
829         }
830
831         browser_reply_data->service = service;
832         browser_reply_data->callback = callback;
833         browser_reply_data->user_data = user_data;
834
835         err = agent_queue_message(service, message,
836                         connman_timeout_browser_launch(),
837                         request_browser_reply, browser_reply_data);
838
839         if (err < 0 && err != -EBUSY) {
840                 DBG("error %d sending browser request", err);
841                 dbus_message_unref(message);
842                 g_free(browser_reply_data);
843                 return err;
844         }
845
846         dbus_message_unref(message);
847
848         return -EINPROGRESS;
849 }
850
851 struct report_error_data {
852         struct connman_service *service;
853         report_error_cb_t callback;
854         void *user_data;
855 };
856
857 static void report_error_reply(DBusMessage *reply, void *user_data)
858 {
859         struct report_error_data *report_error = user_data;
860         gboolean retry = FALSE;
861         const char *dbus_err;
862
863         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
864                 dbus_err = dbus_message_get_error_name(reply);
865                 if (dbus_err != NULL &&
866                         strcmp(dbus_err,
867                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
868                         retry = TRUE;
869         }
870
871         report_error->callback(report_error->service, retry,
872                         report_error->user_data);
873         g_free(report_error);
874 }
875
876 int __connman_agent_report_error(struct connman_service *service,
877                                 const char *error,
878                                 report_error_cb_t callback, void *user_data)
879 {
880         DBusMessage *message;
881         DBusMessageIter iter;
882         const char *path;
883         struct report_error_data *report_error;
884         int err;
885
886         if (service == NULL || agent_path == NULL || error == NULL ||
887                 callback == NULL)
888                 return -ESRCH;
889
890         message = dbus_message_new_method_call(agent_sender, agent_path,
891                                         CONNMAN_AGENT_INTERFACE,
892                                         "ReportError");
893         if (message == NULL)
894                 return -ENOMEM;
895
896         dbus_message_iter_init_append(message, &iter);
897
898         path = __connman_service_get_path(service);
899         dbus_message_iter_append_basic(&iter,
900                                 DBUS_TYPE_OBJECT_PATH, &path);
901         dbus_message_iter_append_basic(&iter,
902                                 DBUS_TYPE_STRING, &error);
903
904         report_error = g_try_new0(struct report_error_data, 1);
905         if (report_error == NULL) {
906                 dbus_message_unref(message);
907                 return -ENOMEM;
908         }
909
910         report_error->service = service;
911         report_error->callback = callback;
912         report_error->user_data = user_data;
913
914         err = agent_queue_message(service, message,
915                         connman_timeout_input_request(),
916                         report_error_reply, report_error);
917         if (err < 0 && err != -EBUSY) {
918                 DBG("error %d sending error request", err);
919                 g_free(report_error);
920                 dbus_message_unref(message);
921                 return -ESRCH;
922         }
923
924         dbus_message_unref(message);
925
926         return -EINPROGRESS;
927 }
928
929 int __connman_agent_init(void)
930 {
931         DBG("");
932
933         connection = connman_dbus_get_connection();
934         if (connection == NULL)
935                 return -1;
936
937         return 0;
938 }
939
940 void __connman_agent_cleanup(void)
941 {
942         DBusMessage *message;
943
944         DBG("");
945
946         if (connection == NULL)
947                 return;
948
949         if (agent_watch > 0)
950                 g_dbus_remove_watch(connection, agent_watch);
951
952         if (agent_path != NULL) {
953                 message = dbus_message_new_method_call(agent_sender, agent_path,
954                                 CONNMAN_AGENT_INTERFACE, "Release");
955                 if (message != NULL) {
956                         dbus_message_set_no_reply(message, TRUE);
957                         g_dbus_send_message(connection, message);
958                 }
959
960                 agent_free();
961         }
962
963         dbus_connection_unref(connection);
964 }