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