DA: Skip initializing failed_bssids list when eapol failure case
[platform/upstream/connman.git] / tools / wispr.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 <stdio.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <signal.h>
32 #include <termios.h>
33 #include <netdb.h>
34
35 #include <gweb/gweb.h>
36
37 #define DEFAULT_URL  "http://www.connman.net/online/status.html"
38
39 static GTimer *timer;
40
41 static GMainLoop *main_loop;
42
43 static void web_debug(const char *str, void *data)
44 {
45         g_print("%s: %s\n", (const char *) data, str);
46 }
47
48 static void sig_term(int sig)
49 {
50         g_main_loop_quit(main_loop);
51 }
52
53 static const char *message_type_to_string(int message_type)
54 {
55         switch (message_type) {
56         case 100:
57                 return "Initial redirect message";
58         case 110:
59                 return "Proxy notification";
60         case 120:
61                 return "Authentication notification";
62         case 130:
63                 return "Logoff notification";
64         case 140:
65                 return "Response to Authentication Poll";
66         case 150:
67                 return "Response to Abort Login";
68         }
69
70         return NULL;
71 }
72
73 static const char *response_code_to_string(int response_code)
74 {
75         switch (response_code) {
76         case 0:
77                 return "No error";
78         case 50:
79                 return "Login succeeded";
80         case 100:
81                 return "Login failed";
82         case 102:
83                 return "RADIUS server error/timeout";
84         case 105:
85                 return "RADIUS server not enabled";
86         case 150:
87                 return "Logoff succeeded";
88         case 151:
89                 return "Login aborted";
90         case 200:
91                 return "Proxy detection/repeat operation";
92         case 201:
93                 return "Authentication pending";
94         case 255:
95                 return "Access gateway internal error";
96         }
97
98         return NULL;
99 }
100
101 struct wispr_msg {
102         bool has_error;
103         const char *current_element;
104         int message_type;
105         int response_code;
106         char *login_url;
107         char *abort_login_url;
108         char *logoff_url;
109         char *access_procedure;
110         char *access_location;
111         char *location_name;
112 };
113
114 static inline void wispr_msg_init(struct wispr_msg *msg)
115 {
116         msg->has_error = false;
117         msg->current_element = NULL;
118
119         msg->message_type = -1;
120         msg->response_code = -1;
121
122         g_free(msg->login_url);
123         msg->login_url = NULL;
124
125         g_free(msg->abort_login_url);
126         msg->abort_login_url = NULL;
127
128         g_free(msg->logoff_url);
129         msg->logoff_url = NULL;
130
131         g_free(msg->access_procedure);
132         msg->access_procedure = NULL;
133
134         g_free(msg->access_location);
135         msg->access_location = NULL;
136
137         g_free(msg->location_name);
138         msg->location_name = NULL;
139 }
140
141 struct wispr_session {
142         GWeb *web;
143         GWebParser *parser;
144         guint request;
145         struct wispr_msg msg;
146         char *username;
147         char *password;
148         char *originurl;
149         char *formdata;
150 };
151
152 static gboolean execute_login(gpointer user_data);
153
154 static struct {
155         const char *str;
156         enum {
157                 WISPR_ELEMENT_NONE,
158                 WISPR_ELEMENT_ACCESS_PROCEDURE,
159                 WISPR_ELEMENT_ACCESS_LOCATION,
160                 WISPR_ELEMENT_LOCATION_NAME,
161                 WISPR_ELEMENT_LOGIN_URL,
162                 WISPR_ELEMENT_ABORT_LOGIN_URL,
163                 WISPR_ELEMENT_MESSAGE_TYPE,
164                 WISPR_ELEMENT_RESPONSE_CODE,
165                 WISPR_ELEMENT_NEXT_URL,
166                 WISPR_ELEMENT_DELAY,
167                 WISPR_ELEMENT_REPLY_MESSAGE,
168                 WISPR_ELEMENT_LOGIN_RESULTS_URL,
169                 WISPR_ELEMENT_LOGOFF_URL,
170         } element;
171 } wispr_element_map[] = {
172         { "AccessProcedure",    WISPR_ELEMENT_ACCESS_PROCEDURE  },
173         { "AccessLocation",     WISPR_ELEMENT_ACCESS_LOCATION   },
174         { "LocationName",       WISPR_ELEMENT_LOCATION_NAME     },
175         { "LoginURL",           WISPR_ELEMENT_LOGIN_URL         },
176         { "AbortLoginURL",      WISPR_ELEMENT_ABORT_LOGIN_URL   },
177         { "MessageType",        WISPR_ELEMENT_MESSAGE_TYPE      },
178         { "ResponseCode",       WISPR_ELEMENT_RESPONSE_CODE     },
179         { "NextURL",            WISPR_ELEMENT_NEXT_URL          },
180         { "Delay",              WISPR_ELEMENT_DELAY             },
181         { "ReplyMessage",       WISPR_ELEMENT_REPLY_MESSAGE     },
182         { "LoginResultsURL",    WISPR_ELEMENT_LOGIN_RESULTS_URL },
183         { "LogoffURL",          WISPR_ELEMENT_LOGOFF_URL        },
184         { NULL,                 WISPR_ELEMENT_NONE              },
185 };
186
187 static void start_element_handler(GMarkupParseContext *context,
188                                         const gchar *element_name,
189                                         const gchar **attribute_names,
190                                         const gchar **attribute_values,
191                                         gpointer user_data, GError **error)
192 {
193         struct wispr_msg *msg = user_data;
194
195         msg->current_element = element_name;
196 }
197
198 static void end_element_handler(GMarkupParseContext *context,
199                                         const gchar *element_name,
200                                         gpointer user_data, GError **error)
201 {
202         struct wispr_msg *msg = user_data;
203
204         msg->current_element = NULL;
205 }
206
207 static void text_handler(GMarkupParseContext *context,
208                                         const gchar *text, gsize text_len,
209                                         gpointer user_data, GError **error)
210 {
211         struct wispr_msg *msg = user_data;
212         int i;
213
214         if (!msg->current_element)
215                 return;
216
217         for (i = 0; wispr_element_map[i].str; i++) {
218                 if (!g_str_equal(wispr_element_map[i].str, msg->current_element))
219                         continue;
220
221                 switch (wispr_element_map[i].element) {
222                 case WISPR_ELEMENT_NONE:
223                 case WISPR_ELEMENT_ACCESS_PROCEDURE:
224                         g_free(msg->access_procedure);
225                         msg->access_procedure = g_strdup(text);
226                         break;
227                 case WISPR_ELEMENT_ACCESS_LOCATION:
228                         g_free(msg->access_location);
229                         msg->access_location = g_strdup(text);
230                         break;
231                 case WISPR_ELEMENT_LOCATION_NAME:
232                         g_free(msg->location_name);
233                         msg->location_name = g_strdup(text);
234                         break;
235                 case WISPR_ELEMENT_LOGIN_URL:
236                         g_free(msg->login_url);
237                         msg->login_url = g_strdup(text);
238                         break;
239                 case WISPR_ELEMENT_ABORT_LOGIN_URL:
240                         g_free(msg->abort_login_url);
241                         msg->abort_login_url = g_strdup(text);
242                         break;
243                 case WISPR_ELEMENT_MESSAGE_TYPE:
244                         msg->message_type = atoi(text);
245                         break;
246                 case WISPR_ELEMENT_RESPONSE_CODE:
247                         msg->response_code = atoi(text);
248                         break;
249                 case WISPR_ELEMENT_NEXT_URL:
250                 case WISPR_ELEMENT_DELAY:
251                 case WISPR_ELEMENT_REPLY_MESSAGE:
252                 case WISPR_ELEMENT_LOGIN_RESULTS_URL:
253                         break;
254                 case WISPR_ELEMENT_LOGOFF_URL:
255                         g_free(msg->logoff_url);
256                         msg->logoff_url = g_strdup(text);
257                         break;
258                 }
259         }
260 }
261
262 static void error_handler(GMarkupParseContext *context,
263                                         GError *error, gpointer user_data)
264 {
265         struct wispr_msg *msg = user_data;
266
267         msg->has_error = true;
268 }
269
270 static const GMarkupParser wispr_parser = {
271         start_element_handler,
272         end_element_handler,
273         text_handler,
274         NULL,
275         error_handler,
276 };
277
278 static void parser_callback(const char *str, gpointer user_data)
279 {
280         struct wispr_session *wispr = user_data;
281         GMarkupParseContext *context;
282         bool result;
283
284         //printf("%s\n", str);
285
286         context = g_markup_parse_context_new(&wispr_parser,
287                         G_MARKUP_TREAT_CDATA_AS_TEXT, &wispr->msg, NULL);
288
289         result = g_markup_parse_context_parse(context, str, strlen(str), NULL);
290         if (result)
291                 g_markup_parse_context_end_parse(context, NULL);
292
293         g_markup_parse_context_free(context);
294 }
295
296 typedef void (*user_input_cb)(const char *value, gpointer user_data);
297
298 struct user_input_data {
299         GString *str;
300         user_input_cb cb;
301         gpointer user_data;
302         bool hidden;
303         int fd;
304         struct termios saved_termios;
305 };
306
307 static void user_callback(struct user_input_data *data)
308 {
309         char *value;
310
311         if (data->hidden) {
312                 ssize_t len;
313
314                 len = write(data->fd, "\n", 1);
315                 if (len < 0)
316                         return;
317         }
318
319         tcsetattr(data->fd, TCSADRAIN, &data->saved_termios);
320
321         close(data->fd);
322
323         value = g_string_free(data->str, FALSE);
324
325         if (data->cb)
326                 data->cb(value, data->user_data);
327
328         g_free(value);
329
330         g_free(data);
331 }
332
333 static gboolean keyboard_input(GIOChannel *channel, GIOCondition condition,
334                                                         gpointer user_data)
335 {
336         struct user_input_data *data = user_data;
337         char buf[1];
338         int len;
339
340         len = read(data->fd, buf, 1);
341
342         if (len != 1)
343                 return TRUE;
344
345         if (buf[0] == '\n') {
346                 user_callback(data);
347                 return FALSE;
348         }
349
350         g_string_append_c(data->str, buf[0]);
351
352         if (data->hidden)
353                 len = write(data->fd, "*", 1);
354
355         return TRUE;
356 }
357
358 static bool user_input(const char *label, bool hidden,
359                                 user_input_cb func, gpointer user_data)
360 {
361         struct user_input_data *data;
362         struct termios new_termios;
363         GIOChannel *channel;
364         guint watch;
365         ssize_t len;
366
367         data = g_try_new0(struct user_input_data, 1);
368         if (!data)
369                 return false;
370
371         data->str = g_string_sized_new(32);
372         data->cb = func;
373         data->user_data = user_data;
374         data->hidden = hidden;
375
376         data->fd = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC);
377         if (data->fd < 0)
378                 goto error;
379
380         if (tcgetattr(data->fd, &data->saved_termios) < 0) {
381                 close(data->fd);
382                 goto error;
383         }
384
385         new_termios = data->saved_termios;
386         if (data->hidden)
387                 new_termios.c_lflag &= ~(ICANON|ECHO);
388         else
389                 new_termios.c_lflag &= ~ICANON;
390         new_termios.c_cc[VMIN] = 1;
391         new_termios.c_cc[VTIME] = 0;
392
393         tcsetattr(data->fd, TCSADRAIN, &new_termios);
394
395         channel = g_io_channel_unix_new(data->fd);
396         g_io_channel_set_encoding(channel, NULL, NULL);
397         g_io_channel_set_buffered(channel, FALSE);
398         watch = g_io_add_watch(channel, G_IO_IN, keyboard_input, data);
399         g_io_channel_unref(channel);
400
401         if (watch == 0)
402                 goto error;
403
404         len = write(data->fd, label, strlen(label));
405         if (len < 0)
406                 goto error;
407
408         len = write(data->fd, ": ", 2);
409         if (len < 0)
410                 goto error;
411
412         return true;
413
414 error:
415         g_string_free(data->str, TRUE);
416         g_free(data);
417
418         return false;
419 }
420
421 static void password_callback(const char *value, gpointer user_data)
422 {
423         struct wispr_session *wispr = user_data;
424
425         g_free(wispr->password);
426         wispr->password = g_strdup(value);
427
428         printf("\n");
429
430         execute_login(wispr);
431 }
432
433 static void username_callback(const char *value, gpointer user_data)
434 {
435         struct wispr_session *wispr = user_data;
436
437         g_free(wispr->username);
438         wispr->username = g_strdup(value);
439
440         if (!wispr->password) {
441                 user_input("Password", true, password_callback, wispr);
442                 return;
443         }
444
445         printf("\n");
446
447         execute_login(wispr);
448 }
449
450 static bool wispr_input(const guint8 **data, gsize *length,
451                                                 gpointer user_data)
452 {
453         struct wispr_session *wispr = user_data;
454         GString *buf;
455         gsize count;
456
457         buf = g_string_sized_new(100);
458
459         g_string_append(buf, "button=Login&UserName=");
460         g_string_append_uri_escaped(buf, wispr->username, NULL, FALSE);
461         g_string_append(buf, "&Password=");
462         g_string_append_uri_escaped(buf, wispr->password, NULL, FALSE);
463         g_string_append(buf, "&FNAME=0&OriginatingServer=");
464         g_string_append_uri_escaped(buf, wispr->originurl, NULL, FALSE);
465
466         count = buf->len;
467
468         g_free(wispr->formdata);
469         wispr->formdata = g_string_free(buf, FALSE);
470
471         *data = (guint8 *) wispr->formdata;
472         *length = count;
473
474         return false;
475 }
476
477 static bool wispr_route(const char *addr, int ai_family, int if_index,
478                 gpointer user_data)
479 {
480         char *family = "unknown";
481
482         if (ai_family == AF_INET)
483                 family = "IPv4";
484         else if (ai_family == AF_INET6)
485                 family = "IPv6";
486
487         printf("Route request: %s %s index %d\n", family, addr, if_index);
488
489         if (ai_family != AF_INET && ai_family != AF_INET6)
490                 return false;
491
492         return true;
493 }
494
495 static bool wispr_result(GWebResult *result, gpointer user_data)
496 {
497         struct wispr_session *wispr = user_data;
498         const guint8 *chunk;
499         gsize length;
500         guint16 status;
501         gdouble elapsed;
502
503         g_web_result_get_chunk(result, &chunk, &length);
504
505         if (length > 0) {
506                 //printf("%s\n", (char *) chunk);
507                 g_web_parser_feed_data(wispr->parser, chunk, length);
508                 return true;
509         }
510
511         g_web_parser_end_data(wispr->parser);
512
513         status = g_web_result_get_status(result);
514
515         g_print("status: %03u\n", status);
516
517         elapsed = g_timer_elapsed(timer, NULL);
518
519         g_print("elapse: %f seconds\n", elapsed);
520
521         if (wispr->msg.message_type < 0) {
522                 const char *redirect;
523
524                 if (status != 302)
525                         goto done;
526
527                 if (!g_web_result_get_header(result, "Location", &redirect))
528                         goto done;
529
530                 printf("Redirect URL: %s\n", redirect);
531                 printf("\n");
532
533                 wispr->request = g_web_request_get(wispr->web, redirect,
534                                 wispr_result, wispr_route, wispr);
535
536                 return false;
537         }
538
539         printf("Message type: %s (%d)\n",
540                         message_type_to_string(wispr->msg.message_type),
541                                                 wispr->msg.message_type);
542         printf("Response code: %s (%d)\n",
543                         response_code_to_string(wispr->msg.response_code),
544                                                 wispr->msg.response_code);
545         if (wispr->msg.access_procedure)
546                 printf("Access procedure: %s\n", wispr->msg.access_procedure);
547         if (wispr->msg.access_location)
548                 printf("Access location: %s\n", wispr->msg.access_location);
549         if (wispr->msg.location_name)
550                 printf("Location name: %s\n", wispr->msg.location_name);
551         if (wispr->msg.login_url)
552                 printf("Login URL: %s\n", wispr->msg.login_url);
553         if (wispr->msg.abort_login_url)
554                 printf("Abort login URL: %s\n", wispr->msg.abort_login_url);
555         if (wispr->msg.logoff_url)
556                 printf("Logoff URL: %s\n", wispr->msg.logoff_url);
557         printf("\n");
558
559         if (status != 200 && status != 302 && status != 404)
560                 goto done;
561
562         if (wispr->msg.message_type == 100) {
563                 if (!wispr->username) {
564                         user_input("Username", false,
565                                    username_callback, wispr);
566                         return false;
567                 }
568
569                 if (!wispr->password) {
570                         user_input("Password", true, password_callback, wispr);
571                         return false;
572                 }
573
574                 g_idle_add(execute_login, wispr);
575                 return false;
576         } else if (wispr->msg.message_type == 120 ||
577                                         wispr->msg.message_type == 140) {
578                 int code = wispr->msg.response_code;
579                 printf("Login process: %s\n",
580                                         code == 50 ? "SUCCESS" : "FAILURE");
581         }
582
583         if (status == 302) {
584                 const char *redirect;
585
586                 if (!g_web_result_get_header(result, "Location", &redirect))
587                         goto done;
588
589                 printf("\n");
590                 printf("Redirect URL: %s\n", redirect);
591                 printf("\n");
592
593                 wispr->request = g_web_request_get(wispr->web, redirect,
594                                 wispr_result, NULL, wispr);
595
596                 return false;
597         }
598
599 done:
600         g_main_loop_quit(main_loop);
601
602         return false;
603 }
604
605 static gboolean execute_login(gpointer user_data)
606 {
607         struct wispr_session *wispr = user_data;
608
609         wispr->request = g_web_request_post(wispr->web, wispr->msg.login_url,
610                                         "application/x-www-form-urlencoded",
611                                         wispr_input, wispr_result, wispr);
612
613         wispr_msg_init(&wispr->msg);
614
615         return FALSE;
616 }
617
618 static bool option_debug = false;
619 static gchar *option_nameserver = NULL;
620 static gchar *option_username = NULL;
621 static gchar *option_password = NULL;
622 static gchar *option_url = NULL;
623
624 static GOptionEntry options[] = {
625         { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
626                                         "Enable debug output" },
627         { "nameserver", 'n', 0, G_OPTION_ARG_STRING, &option_nameserver,
628                                         "Specify nameserver", "ADDRESS" },
629         { "username", 'u', 0, G_OPTION_ARG_STRING, &option_username,
630                                         "Specify username", "USERNAME" },
631         { "password", 'p', 0, G_OPTION_ARG_STRING, &option_password,
632                                         "Specify password", "PASSWORD" },
633         { "url", 'U', 0, G_OPTION_ARG_STRING, &option_url,
634                                         "Specify arbitrary request", "URL" },
635         { NULL },
636 };
637
638 int main(int argc, char *argv[])
639 {
640         GOptionContext *context;
641         GError *error = NULL;
642         struct sigaction sa;
643         struct wispr_session wispr;
644         int index = 0;
645
646         context = g_option_context_new(NULL);
647         g_option_context_add_main_entries(context, options, NULL);
648
649         if (!g_option_context_parse(context, &argc, &argv, &error)) {
650                 if (error) {
651                         g_printerr("%s\n", error->message);
652                         g_error_free(error);
653                 } else
654                         g_printerr("An unknown error occurred\n");
655                 return 1;
656         }
657
658         g_option_context_free(context);
659
660         memset(&wispr, 0, sizeof(wispr));
661         wispr_msg_init(&wispr.msg);
662
663         wispr.web = g_web_new(index);
664         if (!wispr.web) {
665                 fprintf(stderr, "Failed to create web service\n");
666                 return 1;
667         }
668
669         if (option_debug)
670                 g_web_set_debug(wispr.web, web_debug, "WEB");
671
672         main_loop = g_main_loop_new(NULL, FALSE);
673
674         if (option_nameserver) {
675                 g_web_add_nameserver(wispr.web, option_nameserver);
676                 g_free(option_nameserver);
677         }
678
679         g_web_set_accept(wispr.web, NULL);
680         g_web_set_user_agent(wispr.web, "SmartClient/%s wispr", VERSION);
681         g_web_set_close_connection(wispr.web, TRUE);
682
683         if (!option_url)
684                 option_url = g_strdup(DEFAULT_URL);
685
686         wispr.username = option_username;
687         wispr.password = option_password;
688         wispr.originurl = option_url;
689
690         timer = g_timer_new();
691
692         wispr.parser = g_web_parser_new("<WISPAccessGatewayParam",
693                                                 "WISPAccessGatewayParam>",
694                                                 parser_callback, &wispr);
695
696         wispr.request = g_web_request_get(wispr.web, option_url,
697                         wispr_result, wispr_route, &wispr);
698
699         if (wispr.request == 0) {
700                 fprintf(stderr, "Failed to start request\n");
701                 return 1;
702         }
703
704         memset(&sa, 0, sizeof(sa));
705         sa.sa_handler = sig_term;
706         sigaction(SIGINT, &sa, NULL);
707         sigaction(SIGTERM, &sa, NULL);
708
709         g_main_loop_run(main_loop);
710
711         g_timer_destroy(timer);
712
713         if (wispr.request > 0)
714                 g_web_cancel_request(wispr.web, wispr.request);
715
716         g_web_parser_unref(wispr.parser);
717         g_web_unref(wispr.web);
718
719         g_main_loop_unref(main_loop);
720
721         g_free(wispr.username);
722         g_free(wispr.password);
723         g_free(wispr.originurl);
724
725         return 0;
726 }