Imported Upstream version 1.35
[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                 value = "psk";
257                 break;
258         case CONNMAN_SERVICE_SECURITY_8021X:
259                 phase2 = __connman_service_get_phase2(service);
260
261                 if (phase2 && (
262                                 g_str_has_suffix(phase2, "GTC") ||
263                                 g_str_has_suffix(phase2, "OTP")))
264                         value = "response";
265                 else
266                         value = "passphrase";
267
268                 break;
269         default:
270                 value = "string";
271                 break;
272         }
273         connman_dbus_dict_append_basic(iter, "Type",
274                                 DBUS_TYPE_STRING, &value);
275         value = "mandatory";
276         connman_dbus_dict_append_basic(iter, "Requirement",
277                                 DBUS_TYPE_STRING, &value);
278
279         if (__connman_service_wps_enabled(service)) {
280                 connman_dbus_dict_append_array(iter, "Alternates",
281                                         DBUS_TYPE_STRING,
282                                         request_input_append_alternates,
283                                         "WPS");
284         }
285 }
286
287 struct request_wps_data {
288         bool peer;
289 };
290
291 static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
292 {
293         struct request_wps_data *wps = user_data;
294         const char *str = "wpspin";
295
296         connman_dbus_dict_append_basic(iter, "Type",
297                                 DBUS_TYPE_STRING, &str);
298         if (wps && wps->peer)
299                 str = "mandatory";
300         else
301                 str = "alternate";
302         connman_dbus_dict_append_basic(iter, "Requirement",
303                                 DBUS_TYPE_STRING, &str);
304 }
305
306 static void request_input_append_name(DBusMessageIter *iter, void *user_data)
307 {
308         const char *str = "string";
309
310         connman_dbus_dict_append_basic(iter, "Type",
311                                 DBUS_TYPE_STRING, &str);
312         str = "mandatory";
313         connman_dbus_dict_append_basic(iter, "Requirement",
314                                 DBUS_TYPE_STRING, &str);
315         connman_dbus_dict_append_array(iter, "Alternates",
316                                 DBUS_TYPE_STRING,
317                                 request_input_append_alternates,
318                                 "SSID");
319 }
320
321 static void request_input_append_ssid(DBusMessageIter *iter, void *user_data)
322 {
323         const char *str = "ssid";
324
325         connman_dbus_dict_append_basic(iter, "Type",
326                                 DBUS_TYPE_STRING, &str);
327         str = "alternate";
328         connman_dbus_dict_append_basic(iter, "Requirement",
329                                 DBUS_TYPE_STRING, &str);
330 }
331
332 static void request_input_append_password(DBusMessageIter *iter,
333                                                         void *user_data)
334 {
335         char *str = "passphrase";
336
337         connman_dbus_dict_append_basic(iter, "Type",
338                                 DBUS_TYPE_STRING, &str);
339         str = "mandatory";
340         connman_dbus_dict_append_basic(iter, "Requirement",
341                                 DBUS_TYPE_STRING, &str);
342 }
343
344 struct previous_passphrase_data {
345         const char *passphrase;
346         const char *type;
347 };
348
349 static void request_input_append_previouspassphrase(DBusMessageIter *iter,
350                                                         void *user_data)
351 {
352         struct previous_passphrase_data *data = user_data;
353         const char *requirement = "informational";
354
355         connman_dbus_dict_append_basic(iter, "Type",
356                                 DBUS_TYPE_STRING, &data->type);
357
358         connman_dbus_dict_append_basic(iter, "Requirement",
359                                 DBUS_TYPE_STRING, &requirement);
360
361         connman_dbus_dict_append_basic(iter, "Value",
362                                 DBUS_TYPE_STRING, &data->passphrase);
363 }
364
365 static void previous_passphrase_handler(DBusMessageIter *iter,
366                                         struct connman_service *service)
367 {
368         enum connman_service_security security;
369         struct previous_passphrase_data data;
370         struct connman_network *network;
371
372         network = __connman_service_get_network(service);
373         data.passphrase = connman_network_get_string(network, "WiFi.PinWPS");
374
375         if (connman_network_get_bool(network, "WiFi.UseWPS") &&
376                                                 data.passphrase) {
377                 data.type = "wpspin";
378         } else {
379                 data.passphrase = __connman_service_get_passphrase(service);
380                 if (!data.passphrase)
381                         return;
382
383                 security = __connman_service_get_security(service);
384                 switch (security) {
385                 case CONNMAN_SERVICE_SECURITY_WEP:
386                         data.type = "wep";
387                         break;
388                 case CONNMAN_SERVICE_SECURITY_PSK:
389                         data.type  = "psk";
390                         break;
391                 /*
392                  * This should never happen: no passphrase is set if security
393                  * is not one of the above. */
394                 default:
395                         break;
396                 }
397         }
398
399         connman_dbus_dict_append_dict(iter, "PreviousPassphrase",
400                         request_input_append_previouspassphrase, &data);
401 }
402
403 static void request_input_login_reply(DBusMessage *reply, void *user_data)
404 {
405         struct request_input_reply *username_password_reply = user_data;
406         const char *error = NULL;
407         bool values_received = false;
408         char *username = NULL;
409         char *password = NULL;
410         char *key;
411         DBusMessageIter iter, dict;
412
413         if (!reply) {
414                 error = CONNMAN_ERROR_INTERFACE ".OperationAborted";
415                 goto done;
416         }
417
418         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
419                 error = dbus_message_get_error_name(reply);
420                 goto done;
421         }
422
423         if (!check_reply_has_dict(reply))
424                 goto done;
425
426         values_received = true;
427
428         dbus_message_iter_init(reply, &iter);
429         dbus_message_iter_recurse(&iter, &dict);
430         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
431                 DBusMessageIter entry, value;
432
433                 dbus_message_iter_recurse(&dict, &entry);
434                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
435                         break;
436
437                 dbus_message_iter_get_basic(&entry, &key);
438
439                 if (g_str_equal(key, "Username")) {
440                         dbus_message_iter_next(&entry);
441                         if (dbus_message_iter_get_arg_type(&entry)
442                                                         != DBUS_TYPE_VARIANT) {
443                                 error = CONNMAN_ERROR_INTERFACE ".InvalidArguments";
444                                 break;
445                         }
446
447                         dbus_message_iter_recurse(&entry, &value);
448                         if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) {
449                                 error = CONNMAN_ERROR_INTERFACE ".InvalidArguments";
450                                 break;
451                         }
452
453                         dbus_message_iter_get_basic(&value, &username);
454
455                 } else if (g_str_equal(key, "Password")) {
456                         dbus_message_iter_next(&entry);
457                         if (dbus_message_iter_get_arg_type(&entry) !=
458                                                         DBUS_TYPE_VARIANT) {
459                                 error = CONNMAN_ERROR_INTERFACE ".InvalidArguments";
460                                 break;
461                         }
462
463                         dbus_message_iter_recurse(&entry, &value);
464                         if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) {
465                                 error = CONNMAN_ERROR_INTERFACE ".InvalidArguments";
466                                 break;
467                         }
468
469                         dbus_message_iter_get_basic(&value, &password);
470                 }
471
472                 dbus_message_iter_next(&dict);
473         }
474
475 done:
476         username_password_reply->service_callback(
477                         username_password_reply->service, values_received,
478                         NULL, 0, username, password, FALSE, NULL, error,
479                         username_password_reply->user_data);
480
481         g_free(username_password_reply);
482 }
483
484 int __connman_agent_request_passphrase_input(struct connman_service *service,
485                                 authentication_cb_t callback,
486                                 const char *dbus_sender, void *user_data)
487 {
488         DBusMessage *message;
489         const char *path, *agent_sender, *agent_path;
490         DBusMessageIter iter;
491         DBusMessageIter dict;
492         struct request_input_reply *passphrase_reply;
493         int err;
494         void *agent;
495
496         agent = connman_agent_get_info(dbus_sender, &agent_sender,
497                                                         &agent_path);
498
499         DBG("agent %p service %p path %s", agent, service, agent_path);
500
501         if (!service || !agent || !agent_path || !callback)
502                 return -ESRCH;
503
504         message = dbus_message_new_method_call(agent_sender, agent_path,
505                                         CONNMAN_AGENT_INTERFACE,
506                                         "RequestInput");
507         if (!message)
508                 return -ENOMEM;
509
510         dbus_message_iter_init_append(message, &iter);
511
512         path = __connman_service_get_path(service);
513         dbus_message_iter_append_basic(&iter,
514                                 DBUS_TYPE_OBJECT_PATH, &path);
515
516         connman_dbus_dict_open(&iter, &dict);
517
518         if (__connman_service_is_hidden(service)) {
519                 connman_dbus_dict_append_dict(&dict, "Name",
520                                         request_input_append_name, NULL);
521                 connman_dbus_dict_append_dict(&dict, "SSID",
522                                         request_input_append_ssid, NULL);
523         }
524
525         if (__connman_service_get_security(service) ==
526                         CONNMAN_SERVICE_SECURITY_8021X) {
527                 connman_dbus_dict_append_dict(&dict, "Identity",
528                                         request_input_append_identity, service);
529         }
530
531         if (__connman_service_get_security(service) !=
532                         CONNMAN_SERVICE_SECURITY_NONE) {
533                 connman_dbus_dict_append_dict(&dict, "Passphrase",
534                                 request_input_append_passphrase, service);
535
536                 previous_passphrase_handler(&dict, service);
537         }
538
539         if (__connman_service_wps_enabled(service))
540                 connman_dbus_dict_append_dict(&dict, "WPS",
541                                 request_input_append_wps, NULL);
542
543         connman_dbus_dict_close(&iter, &dict);
544
545         passphrase_reply = g_try_new0(struct request_input_reply, 1);
546         if (!passphrase_reply) {
547                 dbus_message_unref(message);
548                 return -ENOMEM;
549         }
550
551         passphrase_reply->service = service;
552         passphrase_reply->service_callback = callback;
553         passphrase_reply->user_data = user_data;
554
555         err = connman_agent_queue_message(service, message,
556                         connman_timeout_input_request(),
557                         request_input_passphrase_reply,
558                         passphrase_reply, agent);
559
560         if (err < 0 && err != -EBUSY) {
561                 DBG("error %d sending agent message", err);
562                 dbus_message_unref(message);
563                 g_free(passphrase_reply);
564                 return err;
565         }
566
567         dbus_message_unref(message);
568
569         return -EINPROGRESS;
570 }
571
572 int __connman_agent_request_login_input(struct connman_service *service,
573                                 authentication_cb_t callback, void *user_data)
574 {
575         DBusMessage *message;
576         const char *path, *agent_sender, *agent_path;
577         DBusMessageIter iter;
578         DBusMessageIter dict;
579         struct request_input_reply *username_password_reply;
580         int err;
581         void *agent;
582
583         agent = connman_agent_get_info(NULL, &agent_sender, &agent_path);
584
585         if (!service || !agent || !agent_path || !callback)
586                 return -ESRCH;
587
588         message = dbus_message_new_method_call(agent_sender, agent_path,
589                                         CONNMAN_AGENT_INTERFACE,
590                                         "RequestInput");
591         if (!message)
592                 return -ENOMEM;
593
594         dbus_message_iter_init_append(message, &iter);
595
596         path = __connman_service_get_path(service);
597         dbus_message_iter_append_basic(&iter,
598                                 DBUS_TYPE_OBJECT_PATH, &path);
599
600         connman_dbus_dict_open(&iter, &dict);
601
602         connman_dbus_dict_append_dict(&dict, "Username",
603                                 request_input_append_identity, service);
604
605         connman_dbus_dict_append_dict(&dict, "Password",
606                                 request_input_append_password, service);
607
608         connman_dbus_dict_close(&iter, &dict);
609
610         username_password_reply = g_try_new0(struct request_input_reply, 1);
611         if (!username_password_reply) {
612                 dbus_message_unref(message);
613                 return -ENOMEM;
614         }
615
616         username_password_reply->service = service;
617         username_password_reply->service_callback = callback;
618         username_password_reply->user_data = user_data;
619
620         err = connman_agent_queue_message(service, message,
621                         connman_timeout_input_request(),
622                         request_input_login_reply, username_password_reply,
623                         agent);
624         if (err < 0 && err != -EBUSY) {
625                 DBG("error %d sending agent request", err);
626                 dbus_message_unref(message);
627                 g_free(username_password_reply);
628                 return err;
629         }
630
631         dbus_message_unref(message);
632
633         return -EINPROGRESS;
634 }
635
636 struct request_browser_reply_data {
637         struct connman_service *service;
638         browser_authentication_cb_t callback;
639         void *user_data;
640 };
641
642 static void request_browser_reply(DBusMessage *reply, void *user_data)
643 {
644         struct request_browser_reply_data *browser_reply_data = user_data;
645         bool result = false;
646         const char *error = NULL;
647
648         if (!reply) {
649                 error = CONNMAN_ERROR_INTERFACE ".OperationAborted";
650                 goto done;
651         }
652
653         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
654                 error = dbus_message_get_error_name(reply);
655                 goto done;
656         }
657
658         result = true;
659
660 done:
661         browser_reply_data->callback(browser_reply_data->service, result,
662                                         error, browser_reply_data->user_data);
663         g_free(browser_reply_data);
664 }
665
666 int __connman_agent_request_browser(struct connman_service *service,
667                                 browser_authentication_cb_t callback,
668                                 const char *url, void *user_data)
669 {
670         struct request_browser_reply_data *browser_reply_data;
671         DBusMessage *message;
672         DBusMessageIter iter;
673         const char *path, *agent_sender, *agent_path;
674         int err;
675         void *agent;
676
677         agent = connman_agent_get_info(NULL, &agent_sender, &agent_path);
678
679         if (!service || !agent || !agent_path || !callback)
680                 return -ESRCH;
681
682         if (!url)
683                 url = "";
684
685         message = dbus_message_new_method_call(agent_sender, agent_path,
686                                         CONNMAN_AGENT_INTERFACE,
687                                         "RequestBrowser");
688         if (!message)
689                 return -ENOMEM;
690
691         dbus_message_iter_init_append(message, &iter);
692
693         path = __connman_service_get_path(service);
694         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
695
696         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &url);
697
698         browser_reply_data = g_try_new0(struct request_browser_reply_data, 1);
699         if (!browser_reply_data) {
700                 dbus_message_unref(message);
701                 return -ENOMEM;
702         }
703
704         browser_reply_data->service = service;
705         browser_reply_data->callback = callback;
706         browser_reply_data->user_data = user_data;
707
708         err = connman_agent_queue_message(service, message,
709                                 connman_timeout_browser_launch(),
710                                 request_browser_reply, browser_reply_data,
711                                 agent);
712
713         if (err < 0 && err != -EBUSY) {
714                 DBG("error %d sending browser request", err);
715                 dbus_message_unref(message);
716                 g_free(browser_reply_data);
717                 return err;
718         }
719
720         dbus_message_unref(message);
721
722         return -EINPROGRESS;
723 }
724
725 int __connman_agent_report_peer_error(struct connman_peer *peer,
726                                         const char *path, const char *error,
727                                         report_error_cb_t callback,
728                                         const char *dbus_sender,
729                                         void *user_data)
730 {
731         return connman_agent_report_error_full(peer, path, "ReportPeerError",
732                                 error, callback, dbus_sender, user_data);
733 }
734
735 static void request_peer_authorization_reply(DBusMessage *reply,
736                                                         void *user_data)
737 {
738         struct request_input_reply *auth_reply = user_data;
739         DBusMessageIter iter, dict;
740         const char *error = NULL;
741         bool choice_done = false;
742         char *wpspin = NULL;
743         char *key;
744
745         if (!reply) {
746                 error = CONNMAN_ERROR_INTERFACE ".OperationAborted";
747                 goto done;
748         }
749
750         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
751                 error = dbus_message_get_error_name(reply);
752                 goto done;
753         }
754
755         if (!check_reply_has_dict(reply))
756                 goto done;
757
758         dbus_message_iter_init(reply, &iter);
759         dbus_message_iter_recurse(&iter, &dict);
760         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
761                 DBusMessageIter entry, value;
762
763                 dbus_message_iter_recurse(&dict, &entry);
764                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) {
765                         error = CONNMAN_ERROR_INTERFACE ".InvalidArguments";
766                         break;
767                 }
768
769                 dbus_message_iter_get_basic(&entry, &key);
770
771                 if (g_str_equal(key, "WPS")) {
772                         choice_done = true;
773
774                         dbus_message_iter_next(&entry);
775                         if (dbus_message_iter_get_arg_type(&entry)
776                                                         != DBUS_TYPE_VARIANT) {
777                                 error = CONNMAN_ERROR_INTERFACE ".InvalidArguments";
778                                 break;
779                         }
780
781                         dbus_message_iter_recurse(&entry, &value);
782                         if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING) {
783                                 error = CONNMAN_ERROR_INTERFACE ".InvalidArguments";
784                                 break;
785                         }
786
787                         dbus_message_iter_get_basic(&value, &wpspin);
788                         break;
789                 }
790                 dbus_message_iter_next(&dict);
791         }
792
793         if (!auth_reply->wps_requested)
794                 choice_done = true;
795
796 done:
797         auth_reply->peer_callback(auth_reply->peer, choice_done, wpspin,
798                                                 error, auth_reply->user_data);
799
800         g_free(auth_reply);
801 }
802
803 int __connman_agent_request_peer_authorization(struct connman_peer *peer,
804                                                 peer_wps_cb_t callback,
805                                                 bool wps_requested,
806                                                 const char *dbus_sender,
807                                                 void *user_data)
808 {
809         struct request_wps_data wps = { .peer = true };
810         const char *path, *agent_sender, *agent_path;
811         struct request_input_reply *auth_reply;
812         DBusMessageIter dict, iter;
813         DBusMessage *message;
814         void *agent;
815         int err;
816
817         agent = connman_agent_get_info(dbus_sender, &agent_sender,
818                                                         &agent_path);
819         DBG("agent %p peer %p path %s", agent, peer, agent_path);
820
821         if (!peer || !agent || !agent_path || !callback)
822                 return -ESRCH;
823
824         message = dbus_message_new_method_call(agent_sender, agent_path,
825                         CONNMAN_AGENT_INTERFACE, "RequestPeerAuthorization");
826         if (!message)
827                 return -ENOMEM;
828
829         dbus_message_iter_init_append(message, &iter);
830
831         path = __connman_peer_get_path(peer);
832         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
833
834         connman_dbus_dict_open(&iter, &dict);
835
836         if (wps_requested)
837                 connman_dbus_dict_append_dict(&dict, "WPS",
838                                         request_input_append_wps, &wps);
839
840         connman_dbus_dict_close(&iter, &dict);
841
842         auth_reply = g_try_new0(struct request_input_reply, 1);
843         if (!auth_reply) {
844                 dbus_message_unref(message);
845                 return -ENOMEM;
846         }
847
848         auth_reply->peer = peer;
849         auth_reply->peer_callback = callback;
850         auth_reply->wps_requested = wps_requested;
851         auth_reply->user_data = user_data;
852
853         err = connman_agent_queue_message(peer, message,
854                                         connman_timeout_input_request(),
855                                         request_peer_authorization_reply,
856                                         auth_reply, agent);
857         if (err < 0 && err != -EBUSY) {
858                 DBG("error %d sending agent message", err);
859                 dbus_message_unref(message);
860                 g_free(auth_reply);
861                 return err;
862         }
863
864         dbus_message_unref(message);
865
866         return -EINPROGRESS;
867 }