Remove leftover defines
[framework/connectivity/connman.git] / plugins / portal.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 <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <netdb.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34
35 #include <glib.h>
36
37 #define CONNMAN_API_SUBJECT_TO_CHANGE
38 #include <connman/plugin.h>
39 #include <connman/location.h>
40 #include <connman/log.h>
41
42 #define PORT 80
43 #define PROXY_PORT 911
44 #define PAGE "/"
45 #define HOST "connman.net"
46 #define USER_APP "connman"
47 #define CONNMAN_NET_IP "174.36.13.145"
48 #define CONNMAN_MAX_IP_LENGTH   15
49 #define CONNECT_TIMEOUT         120
50 #define MAX_COUNTER             80
51
52 #define MAX_HEADER_LINES        13
53 #define PROXY_HEADER_LENGTH     7
54
55 enum get_page_status {
56         GET_PAGE_SUCCESS        = 0,
57         GET_PAGE_TIMEOUT        = 1,
58         GET_PAGE_FAILED         = 2,
59         GET_PAGE_REDIRECTED     = 3,
60 };
61
62 struct server_data {
63         char host[MAX_COUNTER];
64         char page[MAX_COUNTER];
65         char proxy[MAX_COUNTER];
66         GIOChannel *channel;
67         guint watch;
68         guint timeout;
69         int connection_ready;
70         int sock;
71         int proxy_port;
72         int (*get_page) (struct connman_location *location, char *page, int len,
73                                                 enum get_page_status status);
74 };
75
76 static int create_socket()
77 {
78         int sk;
79
80         sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
81         if (sk < 0)
82                 connman_error("Can not create TCP socket");
83
84         return sk;
85 }
86
87 static char *get_ip_from_host(char *host)
88 {
89         int ip_len = CONNMAN_MAX_IP_LENGTH;
90         char *ip;
91         struct hostent *host_ent;
92
93         DBG("Get ip for %s", host);
94         ip = g_try_malloc0(ip_len + 1);
95         if (ip == NULL)
96                 return NULL;
97
98         host_ent = gethostbyname(host);
99         if (host_ent == NULL) {
100                 connman_error("Can not get IP");
101                 goto failed;
102         }
103
104         if (inet_ntop(AF_INET, (void *) host_ent->h_addr_list[0],
105                                                         ip, ip_len) == NULL) {
106                 connman_error("Can not resolve host");
107                 goto failed;
108         }
109
110         return ip;
111 failed:
112         g_free(ip);
113
114         return NULL;
115 }
116
117 static char *build_get_query(char *host, char *page)
118 {
119         char *query;
120         char *host_page = page;
121         char *tpl = "GET /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\n\r\n";
122
123         if (host_page[0] == '/')
124                 host_page = host_page + 1;
125
126         query = g_try_malloc0(strlen(host) + strlen(host_page) +
127                                         strlen(USER_APP) + strlen(tpl) - 5);
128         sprintf(query, tpl, host_page, host, USER_APP);
129
130         return query;
131 }
132
133 static gboolean connect_timeout(gpointer user_data)
134 {
135         struct connman_location *location = user_data;
136         struct server_data *data = connman_location_get_data(location);
137
138         if (data == NULL)
139                 return FALSE;
140
141         data->timeout = 0;
142
143         if (data->get_page)
144                 data->get_page(location, NULL, 0, GET_PAGE_TIMEOUT);
145
146         return FALSE;
147 }
148
149 static void remove_timeout(struct server_data *data)
150 {
151         if (data && data->timeout > 0) {
152                 g_source_remove(data->timeout);
153                 data->timeout = 0;
154         }
155 }
156
157 static gboolean tcp_event(GIOChannel *channel, GIOCondition condition,
158                                                         gpointer user_data)
159 {
160         struct connman_location *location = user_data;
161         struct server_data *data = connman_location_get_data(location);
162         enum get_page_status status;
163         char buf[BUFSIZ+1];
164         int len;
165         int sk;
166
167         if (data == NULL)
168                 return FALSE;
169
170         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
171                 connman_error("TCP event error %d", condition);
172                 len = 0;
173                 status = GET_PAGE_FAILED;
174                 goto done;
175         }
176
177         sk = g_io_channel_unix_get_fd(channel);
178         len = recv(sk, buf, BUFSIZ, 0);
179
180         if (len > 0)
181                 status = GET_PAGE_SUCCESS;
182         else
183                 status = GET_PAGE_FAILED;
184
185 done:
186         remove_timeout(data);
187         data->watch = 0;
188         if (data->get_page)
189                 data->get_page(location, buf, len, status);
190
191         return FALSE;
192 }
193
194 static gboolean socket_event(GIOChannel *channel, GIOCondition condition,
195                                 gpointer user_data)
196 {
197         struct connman_location *location = user_data;
198         struct server_data *data = connman_location_get_data(location);
199         char *query;
200         int sk;
201         unsigned int send_counter = 0;
202         int ret;
203
204         if (data == NULL)
205                 return FALSE;
206
207         if (condition & G_IO_OUT && data->connection_ready == 0) {
208                 data->connection_ready = 1;
209                 sk = g_io_channel_unix_get_fd(channel);
210
211                 query = build_get_query(data->host, data->page);
212                 DBG("query is:\n%s\n", query);
213
214                 while (send_counter < strlen(query)) {
215                         ret = send(sk, query+send_counter,
216                                         strlen(query) - send_counter, 0);
217                         if (ret == -1) {
218                                 DBG("Error sending query");
219                                 remove_timeout(data);
220                                 if (data->get_page)
221                                         data->get_page(location, NULL, 0,
222                                                         GET_PAGE_FAILED);
223                                 g_free(query);
224                                 return FALSE;
225                         }
226                         send_counter += ret;
227                 }
228                 g_free(query);
229         } else if (condition & G_IO_IN)
230                 return tcp_event(channel, condition, user_data);
231
232         return TRUE;
233 }
234
235 static void remove_connection(struct connman_location *location)
236 {
237         struct server_data *data = connman_location_get_data(location);
238
239         data = connman_location_get_data(location);
240         if (data == NULL)
241                 return;
242
243         remove_timeout(data);
244         if (data->watch)
245                 g_source_remove(data->watch);
246
247         if (data->channel != NULL)
248                 g_io_channel_shutdown(data->channel, TRUE, NULL);
249
250         if (data->sock >= 0)
251                 close(data->sock);
252
253         g_free(data);
254         connman_location_set_data(location, NULL);
255 }
256
257 static int get_html(struct connman_location *location, int ms_time)
258 {
259         struct server_data *data;
260         struct sockaddr_in *remote_host = NULL;
261         int ret;
262         char *ip = NULL;
263
264         DBG("");
265
266         data = connman_location_get_data(location);
267         data->connection_ready = 0;
268         data->sock = create_socket();
269         if (data->sock < 0)
270                 goto error;
271
272         DBG("proxy %s port %d", data->proxy, data->proxy_port);
273
274         if (strlen(data->proxy) > 0)
275                 ip = get_ip_from_host(data->proxy);
276         else {
277                 ip = g_try_malloc0(16);
278                 if (ip != NULL)
279                         strcpy(ip, CONNMAN_NET_IP);
280         }
281
282         if (ip == NULL)
283                 goto error;
284
285         DBG("IP from host %s is %s", data->host, ip);
286
287         remote_host = g_try_new0(struct sockaddr_in, 1);
288         remote_host->sin_family = AF_INET;
289         ret = inet_pton(AF_INET, ip,
290                         (void *) (&(remote_host->sin_addr.s_addr)));
291         if (ret < 0) {
292                 connman_error("Error Calling inet_pton");
293                 goto error;
294         } else if (ret == 0) {
295                 connman_error("Wrong IP address %s", ip);
296                 goto error;
297         }
298         if (strlen(data->proxy) > 0)
299                 remote_host->sin_port = htons(data->proxy_port);
300         else
301                 remote_host->sin_port = htons(PORT);
302
303         data->channel = g_io_channel_unix_new(data->sock);
304         g_io_channel_set_flags(data->channel, G_IO_FLAG_NONBLOCK, NULL);
305         g_io_channel_set_close_on_unref(data->channel, TRUE);
306         data->watch = g_io_add_watch(data->channel, G_IO_OUT | G_IO_IN,
307                                                         socket_event, location);
308         data->timeout = g_timeout_add_seconds(ms_time, connect_timeout,
309                                                                 location);
310
311         ret = connect(data->sock, (struct sockaddr *)remote_host,
312                                                 sizeof(struct sockaddr));
313         if (ret < 0 && errno != EINPROGRESS) {
314                 connman_error("Could not connect");
315                 remove_timeout(data);
316                 goto error;
317         }
318
319         g_free(remote_host);
320         g_free(ip);
321         return 0;
322
323 error:
324         g_free(remote_host);
325         g_free(ip);
326
327         if (data->get_page)
328                 data->get_page(location, NULL, 0, GET_PAGE_FAILED);
329
330         return ret;
331 }
332
333 static int get_status(struct server_data *data, char *page, int len)
334 {
335         gchar **lines;
336         gchar *str;
337         int i;
338
339         /*
340          * Right now we are only looking at HTTP response header to figure
341          * out if AP redirected our HTTP request. In the future we are going
342          * to parse the HTTP body and look for certain fixed context.
343          * To figure out if we are redirected we look for some HTTP header line,
344          * if these header was found then we have our page otherwise we
345          * have a redirection page.
346          */
347         lines = g_strsplit(page, "\n", MAX_HEADER_LINES);
348
349         str = g_strrstr(lines[0], "200 OK");
350         if (str != NULL) {
351                 for (i = 0; lines[i] != NULL && i < 12; i++) {
352                         DBG("%s", lines[i]);
353                         str = g_strstr_len(lines[i], 12, "Set-Cookie");
354                         if (str != NULL) {
355                                 g_strfreev(lines);
356                                 DBG("success");
357                                 return GET_PAGE_SUCCESS;
358                         }
359                 }
360         }
361         g_strfreev(lines);
362
363         DBG("redirection");
364
365         return GET_PAGE_REDIRECTED;
366 }
367
368 static int get_page_cb(struct connman_location *location, char *page, int len,
369                 enum get_page_status status)
370 {
371         int ret;
372         struct server_data *data = connman_location_get_data(location);
373
374         remove_connection(location);
375
376         if (page && len > 0)
377                 ret = get_status(data, page, len);
378         else
379                 ret = status;
380
381         DBG("status %d", status);
382
383         switch (ret) {
384         case GET_PAGE_SUCCESS:
385                 connman_location_report_result(location,
386                                         CONNMAN_LOCATION_RESULT_ONLINE);
387                 DBG("Page fetched");
388                 break;
389         case GET_PAGE_REDIRECTED:
390                 connman_location_report_result(location,
391                                         CONNMAN_LOCATION_RESULT_PORTAL);
392                 DBG("Page redirected");
393                 break;
394         case GET_PAGE_FAILED:
395                 connman_location_report_result(location,
396                                         CONNMAN_LOCATION_RESULT_UNKNOWN);
397                 DBG("Could not get the page");
398                 break;
399         case GET_PAGE_TIMEOUT:
400                 connman_location_report_result(location,
401                                         CONNMAN_LOCATION_RESULT_UNKNOWN);
402                 DBG("Page timeout");
403                 break;
404         }
405
406         return ret;
407 }
408
409 static int location_detect(struct connman_location *location)
410 {
411         char *proxy;
412         struct server_data *data;
413         enum connman_service_type service_type;
414
415         service_type = connman_location_get_type(location);
416
417         DBG("service type %d", service_type);
418
419         switch (service_type) {
420         case CONNMAN_SERVICE_TYPE_ETHERNET:
421         case CONNMAN_SERVICE_TYPE_WIFI:
422         case CONNMAN_SERVICE_TYPE_WIMAX:
423         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
424         case CONNMAN_SERVICE_TYPE_CELLULAR:
425                 break;
426         case CONNMAN_SERVICE_TYPE_UNKNOWN:
427         case CONNMAN_SERVICE_TYPE_SYSTEM:
428         case CONNMAN_SERVICE_TYPE_GPS:
429         case CONNMAN_SERVICE_TYPE_VPN:
430                 return -EOPNOTSUPP;
431         }
432
433         data = g_try_new0(struct server_data, 1);
434         if (data == NULL)
435                 return -ENOMEM;
436
437         strcpy(data->host, HOST);
438         strcpy(data->page, PAGE);
439         data->get_page = get_page_cb;
440         data->timeout = 0;
441
442         proxy = getenv("http_proxy");
443         if (proxy) {
444                 char *delim;
445
446                 if (strncmp(proxy, "http://", PROXY_HEADER_LENGTH) == 0)
447                         strcpy(data->proxy, proxy + PROXY_HEADER_LENGTH);
448                 else
449                         strcpy(data->proxy, proxy);
450
451                 delim = strchr(data->proxy, ':');
452                 if (delim) {
453                         int len;
454
455                         len = delim - data->proxy;
456                         data->proxy[len] = '\0';
457
458                         data->proxy_port = atoi(delim + 1);
459                 } else
460                         data->proxy_port = PROXY_PORT;
461         }
462
463         connman_location_set_data(location, data);
464
465         return get_html(location, CONNECT_TIMEOUT);
466 }
467
468 static int location_finish(struct connman_location *location)
469 {
470
471         remove_connection(location);
472         return 0;
473 }
474
475 static struct connman_location_driver location = {
476         .name           = "wifi and ethernet location",
477         .type           = CONNMAN_SERVICE_TYPE_WIFI,
478         .priority       = CONNMAN_LOCATION_PRIORITY_HIGH,
479         .detect         = location_detect,
480         .finish         = location_finish,
481 };
482
483 static int portal_init(void)
484 {
485         return connman_location_driver_register(&location);
486 }
487
488 static void portal_exit(void)
489 {
490         connman_location_driver_unregister(&location);
491 }
492
493 CONNMAN_PLUGIN_DEFINE(portal, "Portal detection plugin", VERSION,
494                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, portal_init, portal_exit)