fe357bfe09d67ec697b212ba58cc8d0dee6c15a9
[platform/upstream/connman.git] / client / agent.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 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 as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 #include <gdbus.h>
36
37 #include "input.h"
38 #include "dbus_helpers.h"
39 #include "agent.h"
40
41 static bool agent_registered = false;
42 static DBusMessage *agent_message = NULL;
43 static struct {
44         DBusMessage *reply;
45         DBusMessageIter iter;
46         DBusMessageIter dict;
47 } agent_reply = { };
48
49 #define AGENT_INTERFACE      "net.connman.Agent"
50
51 static void request_input_ssid_return(char *input);
52 static void request_input_passphrase_return(char *input);
53 static void request_input_string_return(char *input);
54
55 static int confirm_input(char *input)
56 {
57         int i;
58
59         if (input == NULL)
60                 return -1;
61
62         for (i = 0; input[i] != '\0'; i++)
63                 if (isspace(input[i]) == 0)
64                         break;
65
66         if (strcasecmp(&input[i], "yes") == 0 ||
67                         strcasecmp(&input[i], "y") == 0)
68                 return 1;
69
70         if (strcasecmp(&input[i], "no") == 0 ||
71                         strcasecmp(&input[i], "n") == 0)
72                 return 0;
73
74         return -1;
75 }
76
77 static char *strip_path(char *path)
78 {
79         char *name = strrchr(path, '/');
80         if (name != NULL)
81                 name++;
82         else
83                 name = path;
84
85         return name;
86 }
87
88 static char *agent_path(void)
89 {
90         static char *path = NULL;
91
92         if (path == NULL)
93                 path = g_strdup_printf("/net/connman/connmanctl%d", getpid());
94
95         return path;
96 }
97
98 static void pending_message_remove()
99 {
100         if (agent_message != NULL) {
101                 dbus_message_unref(agent_message);
102                 agent_message = NULL;
103         }
104
105         if (agent_reply.reply != NULL) {
106                 dbus_message_unref(agent_reply.reply);
107                 agent_reply.reply = NULL;
108         }
109 }
110
111 static void pending_command_complete(char *message)
112 {
113         __connmanctl_save_rl();
114
115         fprintf(stdout, "%s", message);
116
117         __connmanctl_redraw_rl();
118
119         if (__connmanctl_is_interactive() == true)
120                 __connmanctl_command_mode();
121         else
122                 __connmanctl_agent_mode("", NULL);
123 }
124
125 static DBusMessage *agent_release(DBusConnection *connection,
126                 DBusMessage *message, void *user_data)
127 {
128         g_dbus_unregister_interface(connection, agent_path(), AGENT_INTERFACE);
129         agent_registered = false;
130
131         pending_message_remove();
132         pending_command_complete("Agent unregistered by ConnMan\n");
133
134         if (__connmanctl_is_interactive() == false)
135                 __connmanctl_quit();
136
137         return dbus_message_new_method_return(message);
138 }
139
140 static DBusMessage *agent_cancel(DBusConnection *connection,
141                 DBusMessage *message, void *user_data)
142 {
143         pending_message_remove();
144         pending_command_complete("Agent request cancelled by ConnMan\n");
145
146         return dbus_message_new_method_return(message);
147 }
148
149 static DBusConnection *agent_connection = NULL;
150
151 static void request_browser_return(char *input)
152 {
153         switch (confirm_input(input)) {
154         case 1:
155                 g_dbus_send_reply(agent_connection, agent_message,
156                                 DBUS_TYPE_INVALID);
157                 break;
158         case 0:
159                 g_dbus_send_error(agent_connection, agent_message,
160                                 "net.connman.Agent.Error.Canceled", NULL);
161                 break;
162         default:
163                 return;
164         }
165
166         pending_message_remove();
167         pending_command_complete("");
168 }
169
170 static DBusMessage *agent_request_browser(DBusConnection *connection,
171                 DBusMessage *message, void *user_data)
172 {
173         DBusMessageIter iter;
174         char *service, *url;
175
176         dbus_message_iter_init(message, &iter);
177
178         dbus_message_iter_get_basic(&iter, &service);
179         dbus_message_iter_next(&iter);
180         dbus_message_iter_get_basic(&iter, &url);
181
182         __connmanctl_save_rl();
183         fprintf(stdout, "Agent RequestBrowser %s\n", strip_path(service));
184         fprintf(stdout, "  %s\n", url);
185         __connmanctl_redraw_rl();
186
187         agent_connection = connection;
188         agent_message = dbus_message_ref(message);
189         __connmanctl_agent_mode("Connected (yes/no)? ",
190                         request_browser_return);
191
192         return NULL;
193 }
194
195 static void report_error_return(char *input)
196 {
197         switch (confirm_input(input)) {
198         case 1:
199                 g_dbus_send_error(agent_connection, agent_message,
200                                 "net.connman.Agent.Error.Retry", NULL);
201                 break;
202         case 0:
203                 g_dbus_send_reply(agent_connection, agent_message,
204                                 DBUS_TYPE_INVALID);
205                 break;
206         default:
207                 return;
208         }
209
210         pending_message_remove();
211         pending_command_complete("");
212 }
213
214 static DBusMessage *agent_report_error(DBusConnection *connection,
215                 DBusMessage *message, void *user_data)
216 {
217         DBusMessageIter iter;
218         char *path, *service, *error;
219
220         dbus_message_iter_init(message, &iter);
221
222         dbus_message_iter_get_basic(&iter, &path);
223         service = strip_path(path);
224
225         dbus_message_iter_next(&iter);
226         dbus_message_iter_get_basic(&iter, &error);
227
228         __connmanctl_save_rl();
229         fprintf(stdout, "Agent ReportError %s\n", service);
230         fprintf(stdout, "  %s\n", error);
231         __connmanctl_redraw_rl();
232
233         agent_connection = connection;
234         agent_message = dbus_message_ref(message);
235         __connmanctl_agent_mode("Retry (yes/no)? ", report_error_return);
236
237         return NULL;
238 }
239
240 enum requestinput {
241         SSID                    = 0,
242         IDENTITY                = 1,
243         PASSPHRASE              = 2,
244         WPS                     = 3,
245         WISPR_USERNAME          = 4,
246         WISPR_PASSPHRASE        = 5,
247         REQUEST_INPUT_MAX       = 6,
248 };
249
250 static struct {
251         const char *attribute;
252         bool requested;
253         char *prompt;
254         connmanctl_input_func_t *func;
255 } agent_input[] = {
256         { "Name", false, "Hidden SSID name? ", request_input_ssid_return },
257         { "Identity", false, "EAP username? ", request_input_string_return },
258         { "Passphrase", false, "Passphrase? ",
259           request_input_passphrase_return },
260         { "WPS", false, "WPS PIN (empty line for pushbutton)? " ,
261           request_input_string_return },
262         { "Username", false, "WISPr username? ", request_input_string_return },
263         { "Password", false, "WISPr password? ", request_input_string_return },
264         { },
265 };
266
267 static void request_input_next(void)
268 {
269         int i;
270
271         for (i = 0; agent_input[i].attribute != NULL; i++) {
272                 if (agent_input[i].requested == true) {
273                         if(agent_input[i].func != NULL)
274                                 __connmanctl_agent_mode(agent_input[i].prompt,
275                                                 agent_input[i].func);
276                         else
277                                 agent_input[i].requested = false;
278                         return;
279                 }
280         }
281
282         dbus_message_iter_close_container(&agent_reply.iter,
283                         &agent_reply.dict);
284
285         g_dbus_send_message(agent_connection, agent_reply.reply);
286         agent_reply.reply = NULL;
287
288         pending_message_remove();
289         pending_command_complete("");
290
291         __connmanctl_redraw_rl();
292 }
293
294 static void request_input_append(const char *attribute, char *value)
295 {
296         __connmanctl_dbus_append_dict_entry(&agent_reply.dict, attribute,
297                         DBUS_TYPE_STRING, &value);
298 }
299
300 static void request_input_ssid_return(char *input)
301 {
302         int len = 0;
303
304         if (input != NULL)
305                 len = strlen(input);
306
307         if (len > 0 && len <= 32) {
308                 agent_input[SSID].requested = false;
309                 request_input_append(agent_input[SSID].attribute, input);
310
311                 request_input_next();
312         }
313 }
314
315 static void request_input_passphrase_return(char *input)
316 {
317         /* TBD passphrase length checking */
318
319         if (input != NULL && strlen(input) > 0) {
320                 agent_input[PASSPHRASE].requested = false;
321                 request_input_append(agent_input[PASSPHRASE].attribute, input);
322
323                 agent_input[WPS].requested = false;
324
325                 request_input_next();
326         }
327 }
328
329 static void request_input_string_return(char *input)
330 {
331         int i;
332
333         for (i = 0; agent_input[i].attribute != NULL; i++) {
334                 if (agent_input[i].requested == true) {
335                         request_input_append(agent_input[i].attribute, input);
336                         agent_input[i].requested = false;
337                         break;
338                 }
339         }
340
341         request_input_next();
342 }
343
344 static DBusMessage *agent_request_input(DBusConnection *connection,
345                 DBusMessage *message, void *user_data)
346 {
347         DBusMessageIter iter, dict, entry, variant;
348         char *service, *str, *field;
349         DBusMessageIter dict_entry, field_entry, field_value;
350         char *argument, *value, *attr_type;
351
352         int i;
353
354         dbus_message_iter_init(message, &iter);
355
356         dbus_message_iter_get_basic(&iter, &str);
357         service = strip_path(str);
358
359         dbus_message_iter_next(&iter);
360         dbus_message_iter_recurse(&iter, &dict);
361
362         __connmanctl_save_rl();
363         fprintf(stdout, "Agent RequestInput %s\n", service);
364         __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
365         fprintf(stdout, "\n");
366
367         dbus_message_iter_recurse(&iter, &dict);
368
369         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
370
371                 dbus_message_iter_recurse(&dict, &entry);
372
373                 dbus_message_iter_get_basic(&entry, &field);
374
375                 dbus_message_iter_next(&entry);
376
377                 dbus_message_iter_recurse(&entry, &variant);
378                 dbus_message_iter_recurse(&variant, &dict_entry);
379
380                 while (dbus_message_iter_get_arg_type(&dict_entry)
381                                 == DBUS_TYPE_DICT_ENTRY) {
382                         dbus_message_iter_recurse(&dict_entry, &field_entry);
383
384                         dbus_message_iter_get_basic(&field_entry, &argument);
385
386                         dbus_message_iter_next(&field_entry);
387
388                         dbus_message_iter_recurse(&field_entry, &field_value);
389
390                         if (strcmp(argument, "Type") == 0) {
391                                 dbus_message_iter_get_basic(&field_value,
392                                                 &value);
393                                 attr_type = g_strdup(value);
394                         }
395
396                         dbus_message_iter_next(&dict_entry);
397                 }
398
399                 for (i = 0; agent_input[i].attribute != NULL; i++) {
400                         if (strcmp(field, agent_input[i].attribute) == 0) {
401                                 agent_input[i].requested = true;
402                                 break;
403                         }
404                 }
405
406                 g_free(attr_type);
407                 attr_type = NULL;
408
409                 dbus_message_iter_next(&dict);
410         }
411
412         agent_connection = connection;
413         agent_reply.reply = dbus_message_new_method_return(message);
414         dbus_message_iter_init_append(agent_reply.reply, &agent_reply.iter);
415
416         dbus_message_iter_open_container(&agent_reply.iter, DBUS_TYPE_ARRAY,
417                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
418                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
419                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &agent_reply.dict);
420
421         request_input_next();
422
423         return NULL;
424 }
425
426 static const GDBusMethodTable agent_methods[] = {
427         { GDBUS_METHOD("Release", NULL, NULL, agent_release) },
428         { GDBUS_METHOD("Cancel", NULL, NULL, agent_cancel) },
429         { GDBUS_ASYNC_METHOD("RequestBrowser",
430                                 GDBUS_ARGS({ "service", "o" },
431                                         { "url", "s" }),
432                                 NULL, agent_request_browser) },
433         { GDBUS_ASYNC_METHOD("ReportError",
434                                 GDBUS_ARGS({ "service", "o" },
435                                         { "error", "s" }),
436                                 NULL, agent_report_error) },
437         { GDBUS_ASYNC_METHOD("RequestInput",
438                                 GDBUS_ARGS({ "service", "o" },
439                                         { "fields", "a{sv}" }),
440                                 GDBUS_ARGS({ "fields", "a{sv}" }),
441                                 agent_request_input) },
442         { },
443 };
444
445 static int agent_register_return(DBusMessageIter *iter, const char *error,
446                 void *user_data)
447 {
448         DBusConnection *connection = user_data;
449
450         if (error != NULL) {
451                 g_dbus_unregister_interface(connection, agent_path(),
452                                 AGENT_INTERFACE);
453                 fprintf(stderr, "Error registering Agent: %s\n", error);
454                 return 0;
455         }
456
457         agent_registered = true;
458         fprintf(stdout, "Agent registered\n");
459
460         return -EINPROGRESS;
461 }
462
463 int __connmanctl_agent_register(DBusConnection *connection)
464 {
465         char *path = agent_path();
466         int result;
467
468         if (agent_registered == true) {
469                 fprintf(stderr, "Agent already registered\n");
470                 return -EALREADY;
471         }
472
473         if (g_dbus_register_interface(connection, path,
474                                         AGENT_INTERFACE, agent_methods,
475                                         NULL, NULL, connection,
476                                         NULL) == FALSE) {
477                 fprintf(stderr, "Error: Failed to register Agent callbacks\n");
478                 return 0;
479         }
480
481         result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
482                         CONNMAN_PATH, "net.connman.Manager", "RegisterAgent",
483                         agent_register_return, connection,
484                         DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
485
486         if (result != -EINPROGRESS) {
487                 g_dbus_unregister_interface(connection, agent_path(),
488                                 AGENT_INTERFACE);
489
490                 fprintf(stderr, "Error: Failed to register Agent\n");
491         }
492
493         return result;
494 }
495
496 static int agent_unregister_return(DBusMessageIter *iter, const char *error,
497                 void *user_data)
498 {
499         if (error != NULL) {
500                 fprintf(stderr, "Error unregistering Agent: %s\n", error);
501                 return 0;
502         }
503
504         agent_registered = false;
505         fprintf(stdout, "Agent unregistered\n");
506
507         return 0;
508 }
509
510 int __connmanctl_agent_unregister(DBusConnection *connection)
511 {
512         char *path = agent_path();
513         int result;
514
515         if (agent_registered == false) {
516                 fprintf(stderr, "Agent not registered\n");
517                 return -EALREADY;
518         }
519
520         g_dbus_unregister_interface(connection, agent_path(), AGENT_INTERFACE);
521
522         result = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
523                         CONNMAN_PATH, "net.connman.Manager", "UnregisterAgent",
524                         agent_unregister_return, NULL,
525                         DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
526
527         if (result != -EINPROGRESS)
528                 fprintf(stderr, "Error: Failed to unregister Agent\n");
529
530         return result;
531 }