wispr: Remove service usage reset function
[platform/upstream/connman.git] / src / 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 <errno.h>
27 #include <stdlib.h>
28
29 #include <gweb/gweb.h>
30
31 #include "connman.h"
32
33 #define STATUS_URL_IPV4  "http://ipv4.connman.net/online/status.html"
34 #define STATUS_URL_IPV6  "http://ipv6.connman.net/online/status.html"
35
36 #define wispr_portal_context_unref(_wp_ctxt) \
37         wispr_portal_context_unref_debug(_wp_ctxt, __FILE__, __LINE__, __func__)
38
39 struct connman_wispr_message {
40         gboolean has_error;
41         const char *current_element;
42         int message_type;
43         int response_code;
44         char *login_url;
45         char *abort_login_url;
46         char *logoff_url;
47         char *access_procedure;
48         char *access_location;
49         char *location_name;
50 };
51
52 enum connman_wispr_result {
53         CONNMAN_WISPR_RESULT_UNKNOWN = 0,
54         CONNMAN_WISPR_RESULT_LOGIN   = 1,
55         CONNMAN_WISPR_RESULT_ONLINE  = 2,
56         CONNMAN_WISPR_RESULT_FAILED  = 3,
57 };
58
59 struct wispr_route {
60         char *address;
61         int if_index;
62 };
63
64 struct connman_wispr_portal_context {
65         int refcount;
66
67         struct connman_service *service;
68         enum connman_ipconfig_type type;
69         struct connman_wispr_portal *wispr_portal;
70
71         /* Portal/WISPr common */
72         GWeb *web;
73         unsigned int token;
74         guint request_id;
75
76         const char *status_url;
77
78         char *redirect_url;
79
80         /* WISPr specific */
81         GWebParser *wispr_parser;
82         struct connman_wispr_message wispr_msg;
83
84         char *wispr_username;
85         char *wispr_password;
86         char *wispr_formdata;
87
88         enum connman_wispr_result wispr_result;
89
90         GSList *route_list;
91 };
92
93 struct connman_wispr_portal {
94         struct connman_wispr_portal_context *ipv4_context;
95         struct connman_wispr_portal_context *ipv6_context;
96 };
97
98 static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data);
99
100 static GHashTable *wispr_portal_list = NULL;
101
102 static void connman_wispr_message_init(struct connman_wispr_message *msg)
103 {
104         DBG("");
105
106         msg->has_error = FALSE;
107         msg->current_element = NULL;
108
109         msg->message_type = -1;
110         msg->response_code = -1;
111
112         g_free(msg->login_url);
113         msg->login_url = NULL;
114
115         g_free(msg->abort_login_url);
116         msg->abort_login_url = NULL;
117
118         g_free(msg->logoff_url);
119         msg->logoff_url = NULL;
120
121         g_free(msg->access_procedure);
122         msg->access_procedure = NULL;
123
124         g_free(msg->access_location);
125         msg->access_location = NULL;
126
127         g_free(msg->location_name);
128         msg->location_name = NULL;
129 }
130
131 static void free_wispr_routes(struct connman_wispr_portal_context *wp_context)
132 {
133         while (wp_context->route_list != NULL) {
134                 struct wispr_route *route = wp_context->route_list->data;
135
136                 DBG("free route to %s if %d type %d", route->address,
137                                 route->if_index, wp_context->type);
138
139                 switch(wp_context->type) {
140                 case CONNMAN_IPCONFIG_TYPE_IPV4:
141                         connman_inet_del_host_route(route->if_index,
142                                         route->address);
143                         break;
144                 case CONNMAN_IPCONFIG_TYPE_IPV6:
145                         connman_inet_del_ipv6_host_route(route->if_index,
146                                         route->address);
147                         break;
148                 case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
149                         break;
150                 }
151
152                 g_free(route->address);
153                 g_free(route);
154
155                 wp_context->route_list =
156                         g_slist_delete_link(wp_context->route_list,
157                                         wp_context->route_list);
158         }
159 }
160
161 static void free_connman_wispr_portal_context(struct connman_wispr_portal_context *wp_context)
162 {
163         DBG("context %p", wp_context);
164
165         if (wp_context == NULL)
166                 return;
167
168         if (wp_context->wispr_portal != NULL) {
169                 if (wp_context->wispr_portal->ipv4_context == wp_context)
170                         wp_context->wispr_portal->ipv4_context = NULL;
171
172                 if (wp_context->wispr_portal->ipv6_context == wp_context)
173                         wp_context->wispr_portal->ipv6_context = NULL;
174         }
175
176         if (wp_context->service != NULL)
177                 connman_service_unref(wp_context->service);
178
179         if (wp_context->token > 0)
180                 connman_proxy_lookup_cancel(wp_context->token);
181
182         if (wp_context->request_id > 0)
183                 g_web_cancel_request(wp_context->web, wp_context->request_id);
184
185         if (wp_context->web != NULL)
186                 g_web_unref(wp_context->web);
187
188         g_free(wp_context->redirect_url);
189
190         if (wp_context->wispr_parser != NULL)
191                 g_web_parser_unref(wp_context->wispr_parser);
192
193         connman_wispr_message_init(&wp_context->wispr_msg);
194
195         g_free(wp_context->wispr_username);
196         g_free(wp_context->wispr_password);
197         g_free(wp_context->wispr_formdata);
198
199         free_wispr_routes(wp_context);
200
201         g_free(wp_context);
202 }
203
204 static struct connman_wispr_portal_context *
205 wispr_portal_context_unref_debug(struct connman_wispr_portal_context *wp_context,
206                                 const char *file, int line, const char *caller)
207 {
208         DBG("%p ref %d by %s:%d:%s()", wp_context, wp_context->refcount - 1,
209                                                         file, line, caller);
210
211         if (__sync_fetch_and_sub(&wp_context->refcount, 1) != 1)
212                 return wp_context;
213
214         free_connman_wispr_portal_context(wp_context);
215
216         return NULL;
217 }
218
219 static struct connman_wispr_portal_context *create_wispr_portal_context(void)
220 {
221         struct connman_wispr_portal_context *wp_context = NULL;
222
223          wp_context = g_try_new0(struct connman_wispr_portal_context, 1);
224          if (wp_context == NULL)
225                 return NULL;
226
227          wp_context->refcount = 1;
228
229          return wp_context;
230 }
231
232 static void free_connman_wispr_portal(gpointer data)
233 {
234         struct connman_wispr_portal *wispr_portal = data;
235
236         DBG("");
237
238         if (wispr_portal == NULL)
239                 return;
240
241         if (wispr_portal->ipv4_context != NULL)
242                 wispr_portal_context_unref(wispr_portal->ipv4_context);
243
244         if (wispr_portal->ipv6_context != NULL)
245                 wispr_portal_context_unref(wispr_portal->ipv6_context);
246
247         g_free(wispr_portal);
248 }
249
250 static const char *message_type_to_string(int message_type)
251 {
252         switch (message_type) {
253         case 100:
254                 return "Initial redirect message";
255         case 110:
256                 return "Proxy notification";
257         case 120:
258                 return "Authentication notification";
259         case 130:
260                 return "Logoff notification";
261         case 140:
262                 return "Response to Authentication Poll";
263         case 150:
264                 return "Response to Abort Login";
265         }
266
267         return NULL;
268 }
269
270 static const char *response_code_to_string(int response_code)
271 {
272         switch (response_code) {
273         case 0:
274                 return "No error";
275         case 50:
276                 return "Login succeeded";
277         case 100:
278                 return "Login failed";
279         case 102:
280                 return "RADIUS server error/timeout";
281         case 105:
282                 return "RADIUS server not enabled";
283         case 150:
284                 return "Logoff succeeded";
285         case 151:
286                 return "Login aborted";
287         case 200:
288                 return "Proxy detection/repeat operation";
289         case 201:
290                 return "Authentication pending";
291         case 255:
292                 return "Access gateway internal error";
293         }
294
295         return NULL;
296 }
297
298 static struct {
299         const char *str;
300         enum {
301                 WISPR_ELEMENT_NONE              = 0,
302                 WISPR_ELEMENT_ACCESS_PROCEDURE  = 1,
303                 WISPR_ELEMENT_ACCESS_LOCATION   = 2,
304                 WISPR_ELEMENT_LOCATION_NAME     = 3,
305                 WISPR_ELEMENT_LOGIN_URL         = 4,
306                 WISPR_ELEMENT_ABORT_LOGIN_URL   = 5,
307                 WISPR_ELEMENT_MESSAGE_TYPE      = 6,
308                 WISPR_ELEMENT_RESPONSE_CODE     = 7,
309                 WISPR_ELEMENT_NEXT_URL          = 8,
310                 WISPR_ELEMENT_DELAY             = 9,
311                 WISPR_ELEMENT_REPLY_MESSAGE     = 10,
312                 WISPR_ELEMENT_LOGIN_RESULTS_URL = 11,
313                 WISPR_ELEMENT_LOGOFF_URL        = 12,
314         } element;
315 } wispr_element_map[] = {
316         { "AccessProcedure",    WISPR_ELEMENT_ACCESS_PROCEDURE  },
317         { "AccessLocation",     WISPR_ELEMENT_ACCESS_LOCATION   },
318         { "LocationName",       WISPR_ELEMENT_LOCATION_NAME     },
319         { "LoginURL",           WISPR_ELEMENT_LOGIN_URL         },
320         { "AbortLoginURL",      WISPR_ELEMENT_ABORT_LOGIN_URL   },
321         { "MessageType",        WISPR_ELEMENT_MESSAGE_TYPE      },
322         { "ResponseCode",       WISPR_ELEMENT_RESPONSE_CODE     },
323         { "NextURL",            WISPR_ELEMENT_NEXT_URL          },
324         { "Delay",              WISPR_ELEMENT_DELAY             },
325         { "ReplyMessage",       WISPR_ELEMENT_REPLY_MESSAGE     },
326         { "LoginResultsURL",    WISPR_ELEMENT_LOGIN_RESULTS_URL },
327         { "LogoffURL",          WISPR_ELEMENT_LOGOFF_URL        },
328         { NULL,                 WISPR_ELEMENT_NONE              },
329 };
330
331 static void xml_wispr_start_element_handler(GMarkupParseContext *context,
332                                         const gchar *element_name,
333                                         const gchar **attribute_names,
334                                         const gchar **attribute_values,
335                                         gpointer user_data, GError **error)
336 {
337         struct connman_wispr_message *msg = user_data;
338
339         msg->current_element = element_name;
340 }
341
342 static void xml_wispr_end_element_handler(GMarkupParseContext *context,
343                                         const gchar *element_name,
344                                         gpointer user_data, GError **error)
345 {
346         struct connman_wispr_message *msg = user_data;
347
348         msg->current_element = NULL;
349 }
350
351 static void xml_wispr_text_handler(GMarkupParseContext *context,
352                                         const gchar *text, gsize text_len,
353                                         gpointer user_data, GError **error)
354 {
355         struct connman_wispr_message *msg = user_data;
356         int i;
357
358         if (msg->current_element == NULL)
359                 return;
360
361         for (i = 0; wispr_element_map[i].str; i++) {
362                 if (g_str_equal(wispr_element_map[i].str,
363                                         msg->current_element) == FALSE)
364                         continue;
365
366                 switch (wispr_element_map[i].element) {
367                 case WISPR_ELEMENT_NONE:
368                 case WISPR_ELEMENT_ACCESS_PROCEDURE:
369                         g_free(msg->access_procedure);
370                         msg->access_procedure = g_strdup(text);
371                         break;
372                 case WISPR_ELEMENT_ACCESS_LOCATION:
373                         g_free(msg->access_location);
374                         msg->access_location = g_strdup(text);
375                         break;
376                 case WISPR_ELEMENT_LOCATION_NAME:
377                         g_free(msg->location_name);
378                         msg->location_name = g_strdup(text);
379                         break;
380                 case WISPR_ELEMENT_LOGIN_URL:
381                         g_free(msg->login_url);
382                         msg->login_url = g_strdup(text);
383                         break;
384                 case WISPR_ELEMENT_ABORT_LOGIN_URL:
385                         g_free(msg->abort_login_url);
386                         msg->abort_login_url = g_strdup(text);
387                         break;
388                 case WISPR_ELEMENT_MESSAGE_TYPE:
389                         msg->message_type = atoi(text);
390                         break;
391                 case WISPR_ELEMENT_RESPONSE_CODE:
392                         msg->response_code = atoi(text);
393                         break;
394                 case WISPR_ELEMENT_NEXT_URL:
395                 case WISPR_ELEMENT_DELAY:
396                 case WISPR_ELEMENT_REPLY_MESSAGE:
397                 case WISPR_ELEMENT_LOGIN_RESULTS_URL:
398                         break;
399                 case WISPR_ELEMENT_LOGOFF_URL:
400                         g_free(msg->logoff_url);
401                         msg->logoff_url = g_strdup(text);
402                         break;
403                 }
404         }
405 }
406
407 static void xml_wispr_error_handler(GMarkupParseContext *context,
408                                         GError *error, gpointer user_data)
409 {
410         struct connman_wispr_message *msg = user_data;
411
412         msg->has_error = TRUE;
413 }
414
415 static const GMarkupParser xml_wispr_parser_handlers = {
416         xml_wispr_start_element_handler,
417         xml_wispr_end_element_handler,
418         xml_wispr_text_handler,
419         NULL,
420         xml_wispr_error_handler,
421 };
422
423 static void xml_wispr_parser_callback(const char *str, gpointer user_data)
424 {
425         struct connman_wispr_portal_context *wp_context = user_data;
426         GMarkupParseContext *parser_context = NULL;
427         gboolean result;
428
429         DBG("");
430
431         parser_context = g_markup_parse_context_new(&xml_wispr_parser_handlers,
432                                         G_MARKUP_TREAT_CDATA_AS_TEXT,
433                                         &(wp_context->wispr_msg), NULL);
434
435         result = g_markup_parse_context_parse(parser_context,
436                                         str, strlen(str), NULL);
437         if (result == TRUE)
438                 g_markup_parse_context_end_parse(parser_context, NULL);
439
440         g_markup_parse_context_free(parser_context);
441 }
442
443 static void web_debug(const char *str, void *data)
444 {
445         connman_info("%s: %s\n", (const char *) data, str);
446 }
447
448 static void wispr_portal_error(struct connman_wispr_portal_context *wp_context)
449 {
450         DBG("Failed to proceed wispr/portal web request");
451
452         wp_context->wispr_result = CONNMAN_WISPR_RESULT_FAILED;
453 }
454
455 static void portal_manage_status(GWebResult *result,
456                         struct connman_wispr_portal_context *wp_context)
457 {
458         const char *str = NULL;
459
460         DBG("");
461
462         /* We currently don't do anything with this info */
463         if (g_web_result_get_header(result, "X-ConnMan-Client-IP",
464                                 &str) == TRUE)
465                 connman_info("Client-IP: %s", str);
466
467         if (g_web_result_get_header(result, "X-ConnMan-Client-Country",
468                                 &str) == TRUE)
469                 connman_info("Client-Country: %s", str);
470
471         if (g_web_result_get_header(result, "X-ConnMan-Client-Region",
472                                 &str) == TRUE)
473                 connman_info("Client-Region: %s", str);
474
475         __connman_service_ipconfig_indicate_state(wp_context->service,
476                                                 CONNMAN_SERVICE_STATE_ONLINE,
477                                                 wp_context->type);
478 }
479
480 static gboolean wispr_route_request(const char *address, int ai_family,
481                 int if_index, gpointer user_data)
482 {
483         int result = -1;
484         struct connman_wispr_portal_context *wp_context = user_data;
485         const char *gateway;
486         struct wispr_route *route;
487
488         gateway = __connman_ipconfig_get_gateway_from_index(if_index,
489                 wp_context->type);
490
491         DBG("address %s if %d gw %s", address, if_index, gateway);
492
493         if (gateway == NULL)
494                 return FALSE;
495
496         route = g_try_new0(struct wispr_route, 1);
497         if (route == 0) {
498                 DBG("could not create struct");
499                 return FALSE;
500         }
501
502         switch(wp_context->type) {
503         case CONNMAN_IPCONFIG_TYPE_IPV4:
504                 result = connman_inet_add_host_route(if_index, address,
505                                 gateway);
506                 break;
507         case CONNMAN_IPCONFIG_TYPE_IPV6:
508                 result = connman_inet_add_ipv6_host_route(if_index, address,
509                                 gateway);
510                 break;
511         case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
512                 break;
513         }
514
515         if (result < 0) {
516                 g_free(route);
517                 return FALSE;
518         }
519
520         route->address = g_strdup(address);
521         route->if_index = if_index;
522         wp_context->route_list = g_slist_prepend(wp_context->route_list, route);
523
524         return TRUE;
525 }
526
527 static void wispr_portal_request_portal(struct connman_wispr_portal_context *wp_context)
528 {
529         DBG("");
530
531         wp_context->request_id = g_web_request_get(wp_context->web,
532                                         wp_context->status_url,
533                                         wispr_portal_web_result,
534                                         wispr_route_request,
535                                         wp_context);
536
537         if (wp_context->request_id == 0)
538                 wispr_portal_error(wp_context);
539 }
540
541 static gboolean wispr_input(const guint8 **data, gsize *length,
542                                                 gpointer user_data)
543 {
544         struct connman_wispr_portal_context *wp_context = user_data;
545         GString *buf;
546         gsize count;
547
548         DBG("");
549
550         buf = g_string_sized_new(100);
551
552         g_string_append(buf, "button=Login&UserName=");
553         g_string_append_uri_escaped(buf, wp_context->wispr_username,
554                                                                 NULL, FALSE);
555         g_string_append(buf, "&Password=");
556         g_string_append_uri_escaped(buf, wp_context->wispr_password,
557                                                                 NULL, FALSE);
558         g_string_append(buf, "&FNAME=0&OriginatingServer=");
559         g_string_append_uri_escaped(buf, wp_context->status_url, NULL, FALSE);
560
561         count = buf->len;
562
563         g_free(wp_context->wispr_formdata);
564         wp_context->wispr_formdata = g_string_free(buf, FALSE);
565
566         *data = (guint8 *) wp_context->wispr_formdata;
567         *length = count;
568
569         return FALSE;
570 }
571
572 static void wispr_portal_browser_reply_cb(struct connman_service *service,
573                                         connman_bool_t authentication_done,
574                                         const char *error, void *user_data)
575 {
576         struct connman_wispr_portal_context *wp_context = user_data;
577
578         DBG("");
579
580         if (service == NULL || wp_context == NULL)
581                 return;
582
583         if (authentication_done == FALSE) {
584                 wispr_portal_error(wp_context);
585                 free_wispr_routes(wp_context);
586                 return;
587         }
588
589         /* Restarting the test */
590         __connman_wispr_start(service, wp_context->type);
591 }
592
593 static void wispr_portal_request_wispr_login(struct connman_service *service,
594                                 connman_bool_t success,
595                                 const char *ssid, int ssid_len,
596                                 const char *username, const char *password,
597                                 gboolean wps, const char *wpspin,
598                                 const char *error, void *user_data)
599 {
600         struct connman_wispr_portal_context *wp_context = user_data;
601
602         DBG("");
603
604         if (error != NULL && g_strcmp0(error,
605                         "net.connman.Agent.Error.LaunchBrowser") == 0) {
606
607                 if (__connman_agent_request_browser(service,
608                                 wispr_portal_browser_reply_cb,
609                                 wp_context->redirect_url,
610                                 wp_context) != -EINPROGRESS)
611                         wispr_portal_context_unref(wp_context);
612
613                 return;
614         }
615
616         g_free(wp_context->wispr_username);
617         wp_context->wispr_username = g_strdup(username);
618
619         g_free(wp_context->wispr_password);
620         wp_context->wispr_password = g_strdup(password);
621
622         wp_context->request_id = g_web_request_post(wp_context->web,
623                                         wp_context->wispr_msg.login_url,
624                                         "application/x-www-form-urlencoded",
625                                         wispr_input, wispr_portal_web_result,
626                                         wp_context);
627
628         connman_wispr_message_init(&wp_context->wispr_msg);
629 }
630
631 static gboolean wispr_manage_message(GWebResult *result,
632                         struct connman_wispr_portal_context *wp_context)
633 {
634         DBG("Message type: %s (%d)",
635                 message_type_to_string(wp_context->wispr_msg.message_type),
636                                         wp_context->wispr_msg.message_type);
637         DBG("Response code: %s (%d)",
638                 response_code_to_string(wp_context->wispr_msg.response_code),
639                                         wp_context->wispr_msg.response_code);
640
641         if (wp_context->wispr_msg.access_procedure != NULL)
642                 DBG("Access procedure: %s",
643                         wp_context->wispr_msg.access_procedure);
644         if (wp_context->wispr_msg.access_location != NULL)
645                 DBG("Access location: %s",
646                         wp_context->wispr_msg.access_location);
647         if (wp_context->wispr_msg.location_name != NULL)
648                 DBG("Location name: %s",
649                         wp_context->wispr_msg.location_name);
650         if (wp_context->wispr_msg.login_url != NULL)
651                 DBG("Login URL: %s", wp_context->wispr_msg.login_url);
652         if (wp_context->wispr_msg.abort_login_url != NULL)
653                 DBG("Abort login URL: %s",
654                         wp_context->wispr_msg.abort_login_url);
655         if (wp_context->wispr_msg.logoff_url != NULL)
656                 DBG("Logoff URL: %s", wp_context->wispr_msg.logoff_url);
657
658         switch (wp_context->wispr_msg.message_type) {
659         case 100:
660                 DBG("Login required");
661
662                 wp_context->wispr_result = CONNMAN_WISPR_RESULT_LOGIN;
663
664                 if (__connman_agent_request_login_input(wp_context->service,
665                                         wispr_portal_request_wispr_login,
666                                         wp_context) != -EINPROGRESS) {
667                         wispr_portal_error(wp_context);
668                         wispr_portal_context_unref(wp_context);
669                 }
670
671                 break;
672         case 120: /* Falling down */
673         case 140:
674                 if (wp_context->wispr_msg.response_code == 50) {
675                         wp_context->wispr_result = CONNMAN_WISPR_RESULT_ONLINE;
676
677                         g_free(wp_context->wispr_username);
678                         wp_context->wispr_username = NULL;
679
680                         g_free(wp_context->wispr_password);
681                         wp_context->wispr_password = NULL;
682
683                         g_free(wp_context->wispr_formdata);
684                         wp_context->wispr_formdata = NULL;
685
686                         wispr_portal_request_portal(wp_context);
687
688                         return TRUE;
689                 } else
690                         wispr_portal_error(wp_context);
691
692                 break;
693         default:
694                 break;
695         }
696
697         return FALSE;
698 }
699
700 static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data)
701 {
702         struct connman_wispr_portal_context *wp_context = user_data;
703         const char *redirect = NULL;
704         const guint8 *chunk = NULL;
705         const char *str = NULL;
706         guint16 status;
707         gsize length;
708
709         DBG("");
710
711         if (wp_context->wispr_result != CONNMAN_WISPR_RESULT_ONLINE) {
712                 g_web_result_get_chunk(result, &chunk, &length);
713
714                 if (length > 0) {
715                         g_web_parser_feed_data(wp_context->wispr_parser,
716                                                                 chunk, length);
717                         return TRUE;
718                 }
719
720                 g_web_parser_end_data(wp_context->wispr_parser);
721
722                 if (wp_context->wispr_msg.message_type >= 0) {
723                         if (wispr_manage_message(result, wp_context) == TRUE)
724                                 goto done;
725                 }
726         }
727
728         status = g_web_result_get_status(result);
729
730         DBG("status: %03u", status);
731
732         switch (status) {
733         case 200:
734                 if (wp_context->wispr_msg.message_type >= 0)
735                         break;
736
737                 if (g_web_result_get_header(result, "X-ConnMan-Status",
738                                                 &str) == TRUE) {
739                         portal_manage_status(result, wp_context);
740                         wispr_portal_context_unref(wp_context);
741                         return FALSE;
742                 }
743                 else
744                         __connman_agent_request_browser(wp_context->service,
745                                         wispr_portal_browser_reply_cb,
746                                         wp_context->redirect_url, wp_context);
747
748                 break;
749         case 302:
750                 if (g_web_supports_tls() == FALSE ||
751                                 g_web_result_get_header(result, "Location",
752                                                         &redirect) == FALSE) {
753
754                         __connman_agent_request_browser(wp_context->service,
755                                         wispr_portal_browser_reply_cb,
756                                         wp_context->status_url, wp_context);
757                         break;
758                 }
759
760                 DBG("Redirect URL: %s", redirect);
761
762                 wp_context->redirect_url = g_strdup(redirect);
763
764                 wp_context->request_id = g_web_request_get(wp_context->web,
765                                 redirect, wispr_portal_web_result,
766                                 wispr_route_request, wp_context);
767
768                 goto done;
769         case 400:
770         case 404:
771                 if (__connman_service_online_check_failed(wp_context->service,
772                                                 wp_context->type) == 0) {
773                         wispr_portal_error(wp_context);
774                         wispr_portal_context_unref(wp_context);
775                         return FALSE;
776                 }
777
778                 break;
779         default:
780                 break;
781         }
782
783         free_wispr_routes(wp_context);
784         wp_context->request_id = 0;
785 done:
786         wp_context->wispr_msg.message_type = -1;
787         return FALSE;
788 }
789
790 static void proxy_callback(const char *proxy, void *user_data)
791 {
792         struct connman_wispr_portal_context *wp_context = user_data;
793
794         DBG("proxy %s", proxy);
795
796         if (wp_context == NULL)
797                 return;
798
799         wp_context->token = 0;
800
801         if (proxy != NULL && g_strcmp0(proxy, "DIRECT") != 0)
802                 g_web_set_proxy(wp_context->web, proxy);
803
804         g_web_set_accept(wp_context->web, NULL);
805         g_web_set_user_agent(wp_context->web, "ConnMan/%s wispr", VERSION);
806         g_web_set_close_connection(wp_context->web, TRUE);
807
808         connman_wispr_message_init(&wp_context->wispr_msg);
809
810         wp_context->wispr_parser = g_web_parser_new(
811                                         "<WISPAccessGatewayParam",
812                                         "WISPAccessGatewayParam>",
813                                         xml_wispr_parser_callback, wp_context);
814
815         wispr_portal_request_portal(wp_context);
816 }
817
818 static gboolean no_proxy_callback(gpointer user_data)
819 {
820         struct connman_wispr_portal_context *wp_context = user_data;
821
822         proxy_callback("DIRECT", wp_context);
823
824         return FALSE;
825 }
826
827 static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
828 {
829         enum connman_service_proxy_method proxy_method;
830         enum connman_service_type service_type;
831         char *interface = NULL;
832         char **nameservers = NULL;
833         int if_index;
834         int err = 0;
835         int i;
836
837         DBG("wispr/portal context %p", wp_context);
838         DBG("service %p", wp_context->service);
839
840         service_type = connman_service_get_type(wp_context->service);
841
842         switch (service_type) {
843         case CONNMAN_SERVICE_TYPE_ETHERNET:
844         case CONNMAN_SERVICE_TYPE_WIFI:
845         case CONNMAN_SERVICE_TYPE_WIMAX:
846         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
847         case CONNMAN_SERVICE_TYPE_CELLULAR:
848                 break;
849         case CONNMAN_SERVICE_TYPE_UNKNOWN:
850         case CONNMAN_SERVICE_TYPE_SYSTEM:
851         case CONNMAN_SERVICE_TYPE_GPS:
852         case CONNMAN_SERVICE_TYPE_VPN:
853         case CONNMAN_SERVICE_TYPE_GADGET:
854                 return -EOPNOTSUPP;
855         }
856
857         interface = connman_service_get_interface(wp_context->service);
858         if (interface == NULL)
859                 return -EINVAL;
860
861         DBG("interface %s", interface);
862
863         if_index = connman_inet_ifindex(interface);
864         if (if_index < 0) {
865                 DBG("Could not get ifindex");
866                 err = -EINVAL;
867                 goto done;
868         }
869
870         nameservers = connman_service_get_nameservers(wp_context->service);
871         if (nameservers == NULL) {
872                 DBG("Could not get nameservers");
873                 err = -EINVAL;
874                 goto done;
875         }
876
877         wp_context->web = g_web_new(if_index);
878         if (wp_context->web == NULL) {
879                 DBG("Could not set up GWeb");
880                 err = -ENOMEM;
881                 goto done;
882         }
883
884         if (getenv("CONNMAN_WEB_DEBUG"))
885                 g_web_set_debug(wp_context->web, web_debug, "WEB");
886
887         if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
888                 g_web_set_address_family(wp_context->web, AF_INET);
889                 wp_context->status_url = STATUS_URL_IPV4;
890         } else {
891                 g_web_set_address_family(wp_context->web, AF_INET6);
892                 wp_context->status_url = STATUS_URL_IPV6;
893         }
894
895         for (i = 0; nameservers[i] != NULL; i++)
896                 g_web_add_nameserver(wp_context->web, nameservers[i]);
897
898         proxy_method = connman_service_get_proxy_method(wp_context->service);
899
900         if (proxy_method != CONNMAN_SERVICE_PROXY_METHOD_DIRECT) {
901                 wp_context->token = connman_proxy_lookup(interface,
902                                                 wp_context->status_url,
903                                                 wp_context->service,
904                                                 proxy_callback, wp_context);
905
906                 if (wp_context->token == 0) {
907                         err = -EINVAL;
908                         wispr_portal_context_unref(wp_context);
909                 }
910         } else {
911                 g_timeout_add_seconds(0, no_proxy_callback, wp_context);
912         }
913
914 done:
915         g_strfreev(nameservers);
916
917         g_free(interface);
918         return err;
919 }
920
921 int __connman_wispr_start(struct connman_service *service,
922                                         enum connman_ipconfig_type type)
923 {
924         struct connman_wispr_portal_context *wp_context = NULL;
925         struct connman_wispr_portal *wispr_portal = NULL;
926         int index;
927
928         DBG("service %p", service);
929
930         if (wispr_portal_list == NULL)
931                 return -EINVAL;
932
933         index = __connman_service_get_index(service);
934         if (index < 0)
935                 return -EINVAL;
936
937         wispr_portal = g_hash_table_lookup(wispr_portal_list,
938                                         GINT_TO_POINTER(index));
939         if (wispr_portal == NULL) {
940                 wispr_portal = g_try_new0(struct connman_wispr_portal, 1);
941                 if (wispr_portal == NULL)
942                         return -ENOMEM;
943
944                 g_hash_table_replace(wispr_portal_list,
945                                         GINT_TO_POINTER(index), wispr_portal);
946         }
947
948         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
949                 wp_context = wispr_portal->ipv4_context;
950         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
951                 wp_context = wispr_portal->ipv6_context;
952         else
953                 return -EINVAL;
954
955         /* If there is already an existing context, we wipe it */
956         if (wp_context != NULL)
957                 wispr_portal_context_unref(wp_context);
958
959         wp_context = create_wispr_portal_context();
960         if (wp_context == NULL)
961                 return -ENOMEM;
962
963         connman_service_ref(service);
964
965         wp_context->service = service;
966         wp_context->type = type;
967         wp_context->wispr_portal = wispr_portal;
968
969         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
970                 wispr_portal->ipv4_context = wp_context;
971         else
972                 wispr_portal->ipv6_context = wp_context;
973
974         return wispr_portal_detect(wp_context);
975 }
976
977 void __connman_wispr_stop(struct connman_service *service)
978 {
979         int index;
980
981         DBG("service %p", service);
982
983         if (wispr_portal_list == NULL)
984                 return;
985
986         index = __connman_service_get_index(service);
987         if (index < 0)
988                 return;
989
990         g_hash_table_remove(wispr_portal_list, GINT_TO_POINTER(index));
991 }
992
993 int __connman_wispr_init(void)
994 {
995         DBG("");
996
997         wispr_portal_list = g_hash_table_new_full(g_direct_hash,
998                                                 g_direct_equal, NULL,
999                                                 free_connman_wispr_portal);
1000
1001         return 0;
1002 }
1003
1004 void __connman_wispr_cleanup(void)
1005 {
1006         DBG("");
1007
1008         g_hash_table_destroy(wispr_portal_list);
1009         wispr_portal_list = NULL;
1010 }