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