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