Add basic update for GWeb request API
[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 "gresolv.h"
36 #include "gweb.h"
37
38 struct _GWebResult {
39 };
40
41 struct web_session {
42         GWeb *web;
43
44         char *address;
45         char *host;
46         uint16_t port;
47         unsigned long flags;
48
49         GIOChannel *transport_channel;
50         guint transport_watch;
51
52         guint resolv_action;
53         char *request;
54
55         GWebResult *result;
56
57         GWebResultFunc result_func;
58         gpointer result_data;
59 };
60
61 struct _GWeb {
62         gint ref_count;
63
64         guint next_query_id;
65
66         int index;
67         GList *session_list;
68
69         GResolv *resolv;
70
71         GWebDebugFunc debug_func;
72         gpointer debug_data;
73 };
74
75 static inline void debug(GWeb *web, const char *format, ...)
76 {
77         char str[256];
78         va_list ap;
79
80         if (web->debug_func == NULL)
81                 return;
82
83         va_start(ap, format);
84
85         if (vsnprintf(str, sizeof(str), format, ap) > 0)
86                 web->debug_func(str, web->debug_data);
87
88         va_end(ap);
89 }
90
91 static void free_session(struct web_session *session)
92 {
93         GWeb *web = session->web;
94
95         if (session == NULL)
96                 return;
97
98         g_free(session->request);
99
100         if (session->resolv_action > 0)
101                 g_resolv_cancel_lookup(web->resolv, session->resolv_action);
102
103         if (session->transport_watch > 0)
104                 g_source_remove(session->transport_watch);
105
106         if (session->transport_channel != NULL)
107                 g_io_channel_unref(session->transport_channel);
108
109         g_free(session->host);
110         g_free(session->address);
111         g_free(session);
112 }
113
114 static void flush_sessions(GWeb *web)
115 {
116         GList *list;
117
118         for (list = g_list_first(web->session_list);
119                                         list; list = g_list_next(list))
120                 free_session(list->data);
121
122         g_list_free(web->session_list);
123         web->session_list = NULL;
124 }
125
126 GWeb *g_web_new(int index)
127 {
128         GWeb *web;
129
130         if (index < 0)
131                 return NULL;
132
133         web = g_try_new0(GWeb, 1);
134         if (web == NULL)
135                 return NULL;
136
137         web->ref_count = 1;
138
139         web->next_query_id = 1;
140
141         web->index = index;
142         web->session_list = NULL;
143
144         web->resolv = g_resolv_new(index);
145         if (web->resolv == NULL) {
146                 g_free(web);
147                 return NULL;
148         }
149
150         return web;
151 }
152
153 GWeb *g_web_ref(GWeb *web)
154 {
155         if (web == NULL)
156                 return NULL;
157
158         g_atomic_int_inc(&web->ref_count);
159
160         return web;
161 }
162
163 void g_web_unref(GWeb *web)
164 {
165         if (web == NULL)
166                 return;
167
168         if (g_atomic_int_dec_and_test(&web->ref_count) == FALSE)
169                 return;
170
171         flush_sessions(web);
172
173         g_resolv_unref(web->resolv);
174
175         g_free(web);
176 }
177
178 void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data)
179 {
180         if (web == NULL)
181                 return;
182
183         web->debug_func = func;
184         web->debug_data = user_data;
185
186         g_resolv_set_debug(web->resolv, func, user_data);
187 }
188
189 gboolean g_web_add_nameserver(GWeb *web, const char *address)
190 {
191         if (web == NULL)
192                 return FALSE;
193
194         g_resolv_add_nameserver(web->resolv, address, 53, 0);
195
196         return TRUE;
197 }
198
199 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
200                                                         gpointer user_data)
201 {
202         struct web_session *session = user_data;
203         unsigned char buf[4096];
204         int sk, len;
205
206         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
207                 session->transport_watch = 0;
208                 if (session->result_func != NULL)
209                         session->result_func(400, NULL, session->result_data);
210                 return FALSE;
211         }
212
213         sk = g_io_channel_unix_get_fd(session->transport_channel);
214
215         memset(buf, 0, sizeof(buf));
216         len = recv(sk, buf, sizeof(buf) - 1, 0);
217
218         if (len == 0) {
219                 session->transport_watch = 0;
220                 if (session->result_func != NULL)
221                         session->result_func(200, NULL, session->result_data);
222                 return FALSE;
223         }
224         printf("%s", buf);
225
226         return TRUE;
227 }
228
229 static int connect_session_transport(struct web_session *session)
230 {
231         struct sockaddr_in sin;
232         int sk;
233
234         sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
235         if (sk < 0)
236                 return -EIO;
237
238         memset(&sin, 0, sizeof(sin));
239         sin.sin_family = AF_INET;
240         sin.sin_port = htons(session->port);
241         sin.sin_addr.s_addr = inet_addr(session->address);
242
243         if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
244                 close(sk);
245                 return -EIO;
246         }
247
248         session->transport_channel = g_io_channel_unix_new(sk);
249         if (session->transport_channel == NULL) {
250                 close(sk);
251                 return -ENOMEM;
252         }
253
254         g_io_channel_set_close_on_unref(session->transport_channel, TRUE);
255
256         session->transport_watch = g_io_add_watch(session->transport_channel,
257                                                         G_IO_IN, received_data,
258                                                                 session);
259
260         return 0;
261 }
262
263 static void start_request(struct web_session *session)
264 {
265         GString *buf;
266         char *str;
267         ssize_t len;
268         int sk;
269
270         debug(session->web, "request %s from %s",
271                                         session->request, session->host);
272
273         sk = g_io_channel_unix_get_fd(session->transport_channel);
274
275         buf = g_string_new(NULL);
276         g_string_append_printf(buf, "GET %s HTTP/1.1\r\n", session->request);
277         g_string_append_printf(buf, "Host: %s\r\n", session->host);
278         g_string_append_printf(buf, "User-Agent: ConnMan/%s\r\n", VERSION);
279         g_string_append(buf, "Accept: */*\r\n");
280         g_string_append(buf, "\r\n");
281         str = g_string_free(buf, FALSE);
282
283         len = send(sk, str, strlen(str), 0);
284
285         printf("%s", str);
286
287         g_free(str);
288 }
289
290 static int parse_url(struct web_session *session, const char *url)
291 {
292         char *scheme, *host, *port, *path;
293
294         scheme = g_strdup(url);
295         if (scheme == NULL)
296                 return -EINVAL;
297
298         host = strstr(scheme, "://");
299         if (host != NULL) {
300                 *host = '\0';
301                 host += 3;
302
303                 if (strcasecmp(scheme, "https") == 0)
304                         session->port = 443;
305                 else if (strcasecmp(scheme, "http") == 0)
306                         session->port = 80;
307                 else {
308                         g_free(scheme);
309                         return -EINVAL;
310                 }
311         } else {
312                 host = scheme;
313                 session->port = 80;
314         }
315
316         path = strchr(host, '/');
317         if (path != NULL)
318                 *(path++) = '\0';
319
320         session->request = g_strdup_printf("/%s", path ? path : "");
321
322         port = strrchr(host, ':');
323         if (port != NULL) {
324                 char *end;
325                 int tmp = strtol(port + 1, &end, 10);
326
327                 if (*end == '\0') {
328                         *port = '\0';
329                         session->port = tmp;
330                 }
331         }
332
333         session->host = g_strdup(host);
334
335         g_free(scheme);
336
337         return 0;
338 }
339
340 static void resolv_result(GResolvResultStatus status,
341                                         char **results, gpointer user_data)
342 {
343         struct web_session *session = user_data;
344
345         if (results == NULL || results[0] == NULL) {
346                 if (session->result_func != NULL)
347                         session->result_func(404, NULL, session->result_data);
348                 return;
349         }
350
351         debug(session->web, "address %s", results[0]);
352
353         if (inet_aton(results[0], NULL) == 0) {
354                 if (session->result_func != NULL)
355                         session->result_func(400, NULL, session->result_data);
356                 return;
357         }
358
359         session->address = g_strdup(results[0]);
360
361         if (connect_session_transport(session) < 0) {
362                 if (session->result_func != NULL)
363                         session->result_func(409, NULL, session->result_data);
364                 return;
365         }
366
367         debug(session->web, "creating session %s:%u",
368                                         session->address, session->port);
369
370         start_request(session);
371 }
372
373 guint g_web_request(GWeb *web, GWebMethod method, const char *url,
374                                 GWebResultFunc func, gpointer user_data)
375 {
376         struct web_session *session;
377
378         if (web == NULL || url == NULL)
379                 return 0;
380
381         debug(web, "request %s", url);
382
383         session = g_try_new0(struct web_session, 1);
384         if (session == NULL)
385                 return 0;
386
387         if (parse_url(session, url) < 0) {
388                 free_session(session);
389                 return 0;
390         }
391
392         debug(web, "host %s:%u", session->host, session->port);
393
394         session->web = web;
395
396         session->result_func = func;
397         session->result_data = user_data;
398
399         session->resolv_action = g_resolv_lookup_hostname(web->resolv,
400                                         session->host, resolv_result, session);
401         if (session->resolv_action == 0) {
402                 free_session(session);
403                 return 0;
404         }
405
406         web->session_list = g_list_append(web->session_list, session);
407
408         return web->next_query_id++;
409 }