Add some initial support for HTTP to web service library
[platform/upstream/connman.git] / gweb / gweb.c
1 /*
2  *
3  *  Web service library with GLib integration
4  *
5  *  Copyright (C) 2009-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 <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34
35 #include "gweb.h"
36
37 struct web_session {
38         GWeb *web;
39
40         char *address;
41         char *host;
42         uint16_t port;
43         unsigned long flags;
44
45         GIOChannel *transport_channel;
46         guint transport_watch;
47 };
48
49 struct _GWeb {
50         gint ref_count;
51
52         guint next_query_id;
53
54         int index;
55         GList *session_list;
56
57         GWebDebugFunc debug_func;
58         gpointer debug_data;
59 };
60
61 static inline void debug(GWeb *web, const char *format, ...)
62 {
63         char str[256];
64         va_list ap;
65
66         if (web->debug_func == NULL)
67                 return;
68
69         va_start(ap, format);
70
71         if (vsnprintf(str, sizeof(str), format, ap) > 0)
72                 web->debug_func(str, web->debug_data);
73
74         va_end(ap);
75 }
76
77 static void free_session(struct web_session *session)
78 {
79         if (session == NULL)
80                 return;
81
82         if (session->transport_watch > 0)
83                 g_source_remove(session->transport_watch);
84
85         if (session->transport_channel != NULL)
86                 g_io_channel_unref(session->transport_channel);
87
88         g_free(session->host);
89         g_free(session->address);
90         g_free(session);
91 }
92
93 static void flush_sessions(GWeb *web)
94 {
95         GList *list;
96
97         for (list = g_list_first(web->session_list);
98                                         list; list = g_list_next(list))
99                 free_session(list->data);
100
101         g_list_free(web->session_list);
102         web->session_list = NULL;
103 }
104
105 GWeb *g_web_new(int index)
106 {
107         GWeb *web;
108
109         if (index < 0)
110                 return NULL;
111
112         web = g_try_new0(GWeb, 1);
113         if (web == NULL)
114                 return NULL;
115
116         web->ref_count = 1;
117
118         web->next_query_id = 1;
119
120         web->index = index;
121         web->session_list = NULL;
122
123         return web;
124 }
125
126 GWeb *g_web_ref(GWeb *web)
127 {
128         if (web == NULL)
129                 return NULL;
130
131         g_atomic_int_inc(&web->ref_count);
132
133         return web;
134 }
135
136 void g_web_unref(GWeb *web)
137 {
138         if (web == NULL)
139                 return;
140
141         if (g_atomic_int_dec_and_test(&web->ref_count) == FALSE)
142                 return;
143
144         flush_sessions(web);
145
146         g_free(web);
147 }
148
149 void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data)
150 {
151         if (web == NULL)
152                 return;
153
154         web->debug_func = func;
155         web->debug_data = user_data;
156 }
157
158 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
159                                                         gpointer user_data)
160 {
161         struct web_session *session = user_data;
162         unsigned char buf[4096];
163         int sk, len;
164
165         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
166                 session->transport_watch = 0;
167                 return FALSE;
168         }
169
170         sk = g_io_channel_unix_get_fd(session->transport_channel);
171
172         memset(buf, 0, sizeof(buf));
173         len = recv(sk, buf, sizeof(buf) - 1, 0);
174
175         printf("%s", buf);
176
177         return TRUE;
178 }
179
180 static int connect_session_transport(struct web_session *session)
181 {
182         struct sockaddr_in sin;
183         int sk;
184
185         sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
186         if (sk < 0)
187                 return -EIO;
188
189         memset(&sin, 0, sizeof(sin));
190         sin.sin_family = AF_INET;
191         sin.sin_port = htons(session->port);
192         sin.sin_addr.s_addr = inet_addr(session->address);
193
194         if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
195                 close(sk);
196                 return -EIO;
197         }
198
199         session->transport_channel = g_io_channel_unix_new(sk);
200         if (session->transport_channel == NULL) {
201                 close(sk);
202                 return -ENOMEM;
203         }
204
205         g_io_channel_set_close_on_unref(session->transport_channel, TRUE);
206
207         session->transport_watch = g_io_add_watch(session->transport_channel,
208                                                         G_IO_IN, received_data,
209                                                                 session);
210
211         return 0;
212 }
213
214 static void start_request(struct web_session *session, const char *request)
215 {
216         GString *buf;
217         char *str;
218         ssize_t len;
219         int sk;
220
221         debug(session->web, "request %s from %s", request, session->host);
222
223         sk = g_io_channel_unix_get_fd(session->transport_channel);
224
225         buf = g_string_new(NULL);
226         g_string_append_printf(buf, "GET %s HTTP/1.1\r\n", request);
227         g_string_append_printf(buf, "Host: %s\r\n", session->host);
228         g_string_append_printf(buf, "User-Agent: ConnMan/%s\r\n", VERSION);
229         g_string_append(buf, "Accept: */*\r\n");
230         g_string_append(buf, "\r\n");
231         str = g_string_free(buf, FALSE);
232
233         len = send(sk, str, strlen(str), 0);
234
235         printf("%s", str);
236
237         g_free(str);
238 }
239
240 static char *parse_url(struct web_session *session, const char *url)
241 {
242         char *scheme, *host, *port, *path, *request;
243
244         scheme = g_strdup(url);
245         if (scheme == NULL)
246                 return NULL;
247
248         host = strstr(scheme, "://");
249         if (host != NULL) {
250                 *host = '\0';
251                 host += 3;
252
253                 if (strcasecmp(scheme, "https") == 0)
254                         session->port = 443;
255                 else if (strcasecmp(scheme, "http") == 0)
256                         session->port = 80;
257                 else {
258                         g_free(scheme);
259                         return NULL;
260                 }
261         } else {
262                 host = scheme;
263                 session->port = 80;
264         }
265
266         path = strchr(host, '/');
267         if (path != NULL)
268                 *(path++) = '\0';
269
270         request = g_strdup_printf("/%s", path ? path : "");
271
272         port = strrchr(host, ':');
273         if (port != NULL) {
274                 char *end;
275                 int tmp = strtol(port + 1, &end, 10);
276
277                 if (*end == '\0') {
278                         *port = '\0';
279                         session->port = tmp;
280                 }
281         }
282
283         session->host = g_strdup(host);
284
285         g_free(scheme);
286
287         return request;
288 }
289
290 guint g_web_request(GWeb *web, GWebMethod method, const char *url,
291                                 GWebResultFunc func, gpointer user_data)
292 {
293         struct web_session *session;
294         char *request;
295
296         if (web == NULL || url == NULL)
297                 return 0;
298
299         debug(web, "request %s", url);
300
301         session = g_try_new0(struct web_session, 1);
302         if (session == NULL)
303                 return 0;
304
305         request = parse_url(session, url);
306         if (request == NULL) {
307                 g_free(session);
308                 return 0;
309         }
310
311         if (inet_aton(session->host, NULL) == 0) {
312                 g_free(session);
313                 return 0;
314         }
315
316         session->address = g_strdup(session->host);
317
318         debug(web, "host %s:%u", session->host, session->port);
319
320         if (connect_session_transport(session) < 0) {
321                 g_free(request);
322                 free_session(session);
323                 return FALSE;
324         }
325
326         session->web = web;
327
328         web->session_list = g_list_append(web->session_list, session);
329
330         debug(web, "creating session %s:%u", session->address, session->port);
331
332         start_request(session, request);
333
334         g_free(request);
335
336         return web->next_query_id++;
337 }