wispr: Added portal web request and result handling logic
[platform/upstream/connman.git] / src / wispr.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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  "http://www.connman.net/online/status.html"
34
35 struct connman_wispr_portal_context {
36         struct connman_service *service;
37         enum connman_ipconfig_type type;
38
39         /* Portal/WISPr common */
40         GWeb *web;
41         unsigned int token;
42         guint request_id;
43 };
44
45 struct connman_wispr_portal {
46         struct connman_wispr_portal_context *ipv4_context;
47         struct connman_wispr_portal_context *ipv6_context;
48 };
49
50 static GHashTable *wispr_portal_list = NULL;
51
52 static void free_connman_wispr_portal_context(struct connman_wispr_portal_context *wp_context)
53 {
54         DBG("");
55
56         if (wp_context == NULL)
57                 return;
58
59         if (wp_context->token > 0)
60                 connman_proxy_lookup_cancel(wp_context->token);
61
62         if (wp_context->request_id > 0)
63                 g_web_cancel_request(wp_context->web, wp_context->request_id);
64
65         g_web_unref(wp_context->web);
66
67         g_free(wp_context);
68 }
69
70 static void free_connman_wispr_portal(gpointer data)
71 {
72         struct connman_wispr_portal *wispr_portal = data;
73
74         DBG("");
75
76         if (wispr_portal == NULL)
77                 return;
78
79         free_connman_wispr_portal_context(wispr_portal->ipv4_context);
80         free_connman_wispr_portal_context(wispr_portal->ipv6_context);
81
82         g_free(wispr_portal);
83 }
84
85 static void web_debug(const char *str, void *data)
86 {
87         connman_info("%s: %s\n", (const char *) data, str);
88 }
89
90 static void wispr_portal_error(struct connman_wispr_portal_context *wp_context)
91 {
92         DBG("Failed to proceed wispr/portal web request");
93 }
94
95 static void portal_manage_status(GWebResult *result,
96                         struct connman_wispr_portal_context *wp_context)
97 {
98         const char *str = NULL;
99
100         DBG("");
101
102         /* We currently don't do anything with this info */
103         if (g_web_result_get_header(result, "X-ConnMan-Client-IP",
104                                 &str) == TRUE)
105                 connman_info("Client-IP: %s", str);
106
107         if (g_web_result_get_header(result, "X-ConnMan-Client-Country",
108                                 &str) == TRUE)
109                 connman_info("Client-Country: %s", str);
110
111         if (g_web_result_get_header(result, "X-ConnMan-Client-Region",
112                                 &str) == TRUE)
113                 connman_info("Client-Region: %s", str);
114
115         __connman_service_ipconfig_indicate_state(wp_context->service,
116                                                 CONNMAN_SERVICE_STATE_ONLINE,
117                                                 wp_context->type);
118 }
119
120 static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data)
121 {
122         struct connman_wispr_portal_context *wp_context = user_data;
123         const char *redirect = NULL;
124         const char *str = NULL;
125         guint16 status;
126
127         DBG("");
128
129         if (wp_context->request_id == 0)
130                 return FALSE;
131
132         status = g_web_result_get_status(result);
133
134         DBG("status: %03u", status);
135
136         switch (status) {
137         case 200:
138                 if (g_web_result_get_header(result, "X-ConnMan-Status",
139                                                                 &str) == TRUE)
140                         portal_manage_status(result, wp_context);
141
142                 break;
143         case 302:
144                 if (g_web_result_get_header(result, "Location",
145                                                 &redirect) == FALSE)
146                         break;
147
148                 DBG("Redirect URL: %s", redirect);
149
150                 goto done;
151         case 404:
152                 wispr_portal_error(wp_context);
153
154                 break;
155         default:
156                 break;
157         }
158
159         wp_context->request_id = 0;
160 done:
161         return FALSE;
162 }
163
164 static void wispr_portal_request_portal(struct connman_wispr_portal_context *wp_context)
165 {
166         DBG("");
167
168         wp_context->request_id = g_web_request_get(wp_context->web,
169                         STATUS_URL, wispr_portal_web_result, wp_context);
170
171         if (wp_context->request_id == 0)
172                 wispr_portal_error(wp_context);
173 }
174
175 static void proxy_callback(const char *proxy, void *user_data)
176 {
177         struct connman_wispr_portal_context *wp_context = user_data;
178
179         DBG("proxy %s", proxy);
180
181         wp_context->token = 0;
182
183         if (proxy == NULL)
184                 proxy = getenv("http_proxy");
185
186         if (getenv("CONNMAN_WEB_DEBUG"))
187                 g_web_set_debug(wp_context->web, web_debug, "WEB");
188
189         if (proxy != NULL && g_strcmp0(proxy, "DIRECT") != 0)
190                 g_web_set_proxy(wp_context->web, proxy);
191
192         g_web_set_accept(wp_context->web, NULL);
193         g_web_set_user_agent(wp_context->web, "ConnMan/%s", VERSION);
194         g_web_set_close_connection(wp_context->web, TRUE);
195
196         wispr_portal_request_portal(wp_context);
197 }
198
199 static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
200 {
201         enum connman_service_type service_type;
202         char *interface = NULL;
203         int err = 0;
204
205         DBG("wispr/portal context %p", wp_context);
206         DBG("service %p", wp_context->service);
207
208         service_type = connman_service_get_type(wp_context->service);
209
210         switch (service_type) {
211         case CONNMAN_SERVICE_TYPE_ETHERNET:
212         case CONNMAN_SERVICE_TYPE_WIFI:
213         case CONNMAN_SERVICE_TYPE_WIMAX:
214         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
215         case CONNMAN_SERVICE_TYPE_CELLULAR:
216                 break;
217         case CONNMAN_SERVICE_TYPE_UNKNOWN:
218         case CONNMAN_SERVICE_TYPE_SYSTEM:
219         case CONNMAN_SERVICE_TYPE_GPS:
220         case CONNMAN_SERVICE_TYPE_VPN:
221         case CONNMAN_SERVICE_TYPE_GADGET:
222                 return -EOPNOTSUPP;
223         }
224
225         interface = connman_service_get_interface(wp_context->service);
226         if (interface == NULL)
227                 return -EINVAL;
228
229         DBG("interface %s", interface);
230
231         wp_context->web = g_web_new(0);
232         if (wp_context->web == NULL) {
233                 err = -ENOMEM;
234                 goto done;
235         }
236
237         if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4)
238                 g_web_set_address_family(wp_context->web, AF_INET);
239         else
240                 g_web_set_address_family(wp_context->web, AF_INET6);
241
242         wp_context->token = connman_proxy_lookup(interface,
243                                         STATUS_URL, wp_context->service,
244                                         proxy_callback, wp_context);
245         if (wp_context->token == 0)
246                 err = -EINVAL;
247
248 done:
249         g_free(interface);
250         return err;
251 }
252
253 int __connman_wispr_start(struct connman_service *service,
254                                         enum connman_ipconfig_type type)
255 {
256         struct connman_wispr_portal_context *wp_context = NULL;
257         struct connman_wispr_portal *wispr_portal = NULL;
258         int index;
259
260         DBG("service %p", service);
261
262         if (wispr_portal_list == NULL)
263                 return -EINVAL;
264
265         index = __connman_service_get_index(service);
266         if (index < 0)
267                 return -EINVAL;
268
269         wispr_portal = g_hash_table_lookup(wispr_portal_list,
270                                         GINT_TO_POINTER(index));
271         if (wispr_portal == NULL) {
272                 wispr_portal = g_try_new0(struct connman_wispr_portal, 1);
273                 if (wispr_portal == NULL)
274                         return -ENOMEM;
275
276                 g_hash_table_replace(wispr_portal_list,
277                                         GINT_TO_POINTER(index), wispr_portal);
278         }
279
280         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
281                 wp_context = wispr_portal->ipv4_context;
282         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
283                 wp_context = wispr_portal->ipv6_context;
284         else
285                 return -EINVAL;
286
287         if (wp_context == NULL) {
288                 wp_context = g_try_new0(struct connman_wispr_portal_context, 1);
289                 if (wp_context == NULL)
290                         return -ENOMEM;
291
292                 wp_context->service = service;
293                 wp_context->type = type;
294
295                 if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
296                         wispr_portal->ipv4_context = wp_context;
297                 else
298                         wispr_portal->ipv6_context = wp_context;
299
300                 return wispr_portal_detect(wp_context);
301         }
302
303         return 0;
304 }
305
306 void __connman_wispr_stop(struct connman_service *service)
307 {
308         int index;
309
310         DBG("service %p", service);
311
312         if (wispr_portal_list == NULL)
313                 return;
314
315         index = __connman_service_get_index(service);
316         if (index < 0)
317                 return;
318
319         g_hash_table_remove(wispr_portal_list, GINT_TO_POINTER(index));
320 }
321
322 int __connman_wispr_init(void)
323 {
324         DBG("");
325
326         wispr_portal_list = g_hash_table_new_full(g_direct_hash,
327                                                 g_direct_equal, NULL,
328                                                 free_connman_wispr_portal);
329
330         return 0;
331 }
332
333 void __connman_wispr_cleanup(void)
334 {
335         DBG("");
336
337         g_hash_table_destroy(wispr_portal_list);
338         wispr_portal_list = NULL;
339 }