1 /* GNet - Networking library
2  * Copyright (C) 2000  David Helder
3  * Copyright (C) 2004  Tim-Philipp Müller <tim centricular net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA  02111-1307, USA.
19  */
20 
21 /***************************************************************************
22  *                                                                         *
23  *   TODO:                                                                 *
24  *    - support http_proxy environment variable (possible also via API)    *
25  *    - support authentication                                             *
26  *    - read RFC for Http/1.1 from cover to cover and check compliance     *
27  *    - think about how to make an HTTPS easy - maybe add public vfuncs?   *
28  *                                                                         *
29  ***************************************************************************/
30 
31 #include "conn-http.h"
32 #include "gnetconfig.h"
33 
34 #include <string.h>
35 #include <stdlib.h>
36 
37 #define GNET_CONN_HTTP_DEFAULT_MAX_REDIRECTS  5
38 #define GNET_CONN_HTTP_BUF_INCREMENT          8192    /* 8kB */
39 
40 #define CONN_HTTP_MAGIC_SEQUENCE  499138271
41 #define GNET_IS_CONN_HTTP(conn)  ((conn)&&(((GConnHttp*)(conn))->stamp == CONN_HTTP_MAGIC_SEQUENCE))
42 
43 typedef enum
44 {
45 	STATUS_NONE = 0,
46 	STATUS_SENT_REQUEST,
47 	STATUS_RECV_HEADERS,
48 	STATUS_RECV_BODY_NONCHUNKED,
49 	STATUS_RECV_CHUNK_SIZE,
50 	STATUS_RECV_CHUNK_BODY,
51 	STATUS_ERROR,
52 	STATUS_DONE
53 } GConnHttpStatus;
54 
55 struct _GConnHttp
56 {
57 	guint                stamp;           /* magic cookie instead of a type system */
58 
59 	GInetAddrNewAsyncID  ia_id;
60 	GInetAddr           *ia;
61 
62 	GConn               *conn;
63 
64 	gboolean             connection_close; /* Connection: close header was given */
65 
66 	GConnHttpFunc        func;
67 	gpointer             func_data;
68 
69 	guint                num_redirects;
70 	guint                max_redirects;
71 	gchar               *redirect_location; /* set if auto-redirection is
72 	                                           required after body is received */
73 
74 	GURI                *uri;
75 	GList               *req_headers;  /* request headers we send */
76 	GList               *resp_headers; /* response headers we got */
77 
78 	guint                response_code;
79 
80 	GConnHttpMethod      method;
81 	GConnHttpStatus      status;
82 
83 	guint                timeout;
84 
85 	gchar               *post_data;
86 	gsize                post_data_len;
87 	gsize                post_data_term_len; /* for extra \n\r etc. */
88 
89 	gsize                content_length;
90 	gsize                content_recv;
91 	gboolean             got_content_length; /* set if we got a content_length header */
92 
93 	gboolean             tenc_chunked;  /* Transfer-Encoding: chunked */
94 
95 	gchar               *buffer;
96 	gsize                bufalloc; /* number of bytes allocated             */
97 	gsize                buflen;   /* number of bytes of data in the buffer */
98 
99 	GMainContext        *context;  /* we hold a reference, may be NULL */
100 
101 	GMainLoop           *loop;
102 
103 	guint                refcount; /* used internally only, if >1 we are in the callback */
104 };
105 
106 typedef struct _GConnHttpHdr GConnHttpHdr;
107 
108 struct _GConnHttpHdr
109 {
110 	gchar  *field;
111 	gchar  *value;  /* NULL = not set */
112 };
113 
114 static const gchar *gen_headers[] =
115 {
116 	"Cache-Control", "Connection", "Date", "Pragma", "Trailer",
117 	"Transfer-Encoding", "Upgrade", "Via", "Warning"
118 };
119 
120 static const gchar *req_headers[] =
121 {
122 	"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language",
123 	"Authorization", "Expect", "From", "Host", "If-Match", "If-Modified-Since",
124 	"If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards",
125 	"Proxy-Authorization", "Range", "Referer", "TE", "User-Agent",
126 	"Content-Length" /* for POST request */
127 };
128 
129 #define is_general_header(field)  (is_in_str_arr(gen_headers,G_N_ELEMENTS(gen_headers),field))
130 #define is_request_header(field)  (is_in_str_arr(req_headers,G_N_ELEMENTS(req_headers),field))
131 
132 
133 static void       gnet_conn_http_delete_internal (GConnHttp *conn);
134 
135 /***************************************************************************
136  *
137  *   gnet_conn_http_append_to_buf
138  *
139  ***************************************************************************/
140 
141 static void
gnet_conn_http_append_to_buf(GConnHttp * conn,const gchar * data,gsize datalen)142 gnet_conn_http_append_to_buf (GConnHttp *conn, const gchar *data, gsize datalen)
143 {
144 	g_return_if_fail (conn != NULL);
145 	g_return_if_fail (data != NULL);
146 
147 	if (conn->buflen + datalen >= conn->bufalloc)
148 	{
149 		while (conn->buflen + datalen >= conn->bufalloc)
150 			conn->bufalloc += GNET_CONN_HTTP_BUF_INCREMENT;
151 
152 		/* alloc one more to make sure buffer can be NUL-terminated later */
153 		conn->buffer = g_realloc (conn->buffer, conn->bufalloc + 1);
154 	}
155 
156 	if (datalen > 0)
157 	{
158 		memcpy (conn->buffer + conn->buflen, data, datalen);
159 		conn->buflen += datalen;
160 	}
161 }
162 
163 /***************************************************************************
164  *
165  *   gnet_conn_http_reset
166  *
167  *   reset certain values, but not the connection or URI or address
168  *
169  ***************************************************************************/
170 
171 static void
gnet_conn_http_reset(GConnHttp * conn)172 gnet_conn_http_reset (GConnHttp *conn)
173 {
174 	GList *node;
175 
176 	conn->num_redirects  = 0;
177 	conn->max_redirects  = GNET_CONN_HTTP_DEFAULT_MAX_REDIRECTS;
178 	g_free(conn->redirect_location);
179 	conn->redirect_location = NULL;
180 
181 	conn->connection_close = FALSE;
182 
183 	conn->content_length = 0;
184 	conn->content_recv = 0;
185 	conn->tenc_chunked = FALSE;
186 	conn->got_content_length = FALSE;
187 
188 	/* Note: we keep the request headers as they are*/
189 	for (node = conn->resp_headers;  node;  node = node->next)
190 	{
191 		GConnHttpHdr *hdr = (GConnHttpHdr*)node->data;
192 		g_free(hdr->field);
193 		g_free(hdr->value);
194 		memset(hdr, 0xff, sizeof(GConnHttpHdr));
195 		g_free(hdr);
196 	}
197 	g_list_free(conn->resp_headers);
198 	conn->resp_headers = NULL;
199 
200 	conn->response_code = 0;
201 	if (conn->method != GNET_CONN_HTTP_METHOD_POST)
202 	{
203 		g_free(conn->post_data);
204 		conn->post_data = NULL;
205 		conn->post_data_len = 0;
206 	}
207 
208 	/* alloc one more to make sure buffer can be NUL-terminated later */
209 	conn->buffer   = g_realloc (conn->buffer, GNET_CONN_HTTP_BUF_INCREMENT + 1);
210 	conn->bufalloc = GNET_CONN_HTTP_BUF_INCREMENT;
211 	conn->buflen   = 0;
212 
213 	conn->status = STATUS_NONE;
214 }
215 
216 /**
217  *  gnet_conn_http_new
218  *
219  *  Creates a #GConnHttp.
220  *
221  *  Returns: a #GConnHttp.
222  *
223  **/
224 
225 GConnHttp *
gnet_conn_http_new(void)226 gnet_conn_http_new (void)
227 {
228 	GConnHttp  *conn;
229 
230 	conn = g_new0 (GConnHttp, 1);
231 
232 	conn->stamp = CONN_HTTP_MAGIC_SEQUENCE;
233 
234 	/* alloc one more to make sure buffer can be NUL-terminated later */
235 	conn->buffer   = g_malloc (GNET_CONN_HTTP_BUF_INCREMENT + 1);
236 	conn->bufalloc = GNET_CONN_HTTP_BUF_INCREMENT;
237 	conn->buflen   = 0;
238 
239 	/* set default user agent */
240 	gnet_conn_http_set_user_agent (conn, NULL);
241 
242 	gnet_conn_http_set_method (conn, GNET_CONN_HTTP_METHOD_GET, NULL, 0);
243 
244 	gnet_conn_http_set_header (conn, "Accept", "*/*", 0);
245 	gnet_conn_http_set_header (conn, "Connection", "Keep-Alive", 0);
246 
247 	gnet_conn_http_set_timeout (conn, 30*1000); /* 30 secs */
248 
249 	conn->refcount = 1;
250 
251 	return conn;
252 }
253 
254 /***************************************************************************
255  *
256  *   gnet_conn_http_new_event
257  *
258  ***************************************************************************/
259 
260 static GConnHttpEvent *
gnet_conn_http_new_event(GConnHttpEventType type)261 gnet_conn_http_new_event (GConnHttpEventType type)
262 {
263 	GConnHttpEvent *event = NULL;
264 	gsize           stsize = 0;
265 
266 	switch (type)
267 	{
268 		case GNET_CONN_HTTP_RESOLVED:
269 			stsize = sizeof(GConnHttpEventResolved);
270 			break;
271 
272 		case GNET_CONN_HTTP_RESPONSE:
273 			stsize = sizeof(GConnHttpEventResponse);
274 			break;
275 
276 		case GNET_CONN_HTTP_REDIRECT:
277 			stsize = sizeof(GConnHttpEventRedirect);
278 			break;
279 
280 		case GNET_CONN_HTTP_DATA_PARTIAL:
281 		case GNET_CONN_HTTP_DATA_COMPLETE:
282 			stsize = sizeof(GConnHttpEventData);
283 			break;
284 
285 		case GNET_CONN_HTTP_ERROR:
286 			stsize = sizeof(GConnHttpEventError);
287 			break;
288 
289 		case GNET_CONN_HTTP_CONNECTED:
290 		default:
291 			stsize = sizeof(GConnHttpEvent);
292 			break;
293 	}
294 
295 	event = (GConnHttpEvent*) g_malloc0(stsize);
296 	event->type = type;
297 	event->stsize = stsize;
298 
299 	return event;
300 }
301 
302 /***************************************************************************
303  *
304  *   gnet_conn_http_free_event
305  *
306  ***************************************************************************/
307 
308 static void
gnet_conn_http_free_event(GConnHttpEvent * event)309 gnet_conn_http_free_event (GConnHttpEvent *event)
310 {
311 	g_return_if_fail (event != NULL);
312 	g_return_if_fail (event->stsize > 0);
313 
314 	switch (event->type) {
315 		case GNET_CONN_HTTP_RESPONSE:
316 			g_strfreev(((GConnHttpEventResponse*)event)->header_fields);
317 			g_strfreev(((GConnHttpEventResponse*)event)->header_values);
318 			break;
319 		case GNET_CONN_HTTP_REDIRECT:
320 			g_free(((GConnHttpEventRedirect*)event)->new_location);
321 			break;
322 		case GNET_CONN_HTTP_ERROR:
323 			g_free(((GConnHttpEventError*)event)->message);
324 			break;
325 		default:
326 			break;
327 	}
328 	/* poison struct */
329 	memset(event, 0xff, event->stsize);
330 	g_free(event);
331 }
332 
333 /***************************************************************************
334  *
335  *   gnet_conn_http_emit_event
336  *
337  *   Calls user callback with given event. You MUST check whether the
338  *    connection has been deleted from the user callback via checking
339  *    conn->recount == 0 and calling _delete_internal() if so at some
340  *    point before giving control back to the main loop.
341  *
342  ***************************************************************************/
343 
344 static void
gnet_conn_http_emit_event(GConnHttp * conn,GConnHttpEvent * event)345 gnet_conn_http_emit_event (GConnHttp *conn, GConnHttpEvent *event)
346 {
347 	g_return_if_fail (conn != NULL);
348 	g_return_if_fail (event != NULL);
349 
350 	++conn->refcount;
351 
352 	if (conn->func)
353 		conn->func (conn, event, conn->func_data);
354 
355 	g_return_if_fail (conn->refcount > 0);
356 
357 	--conn->refcount;
358 }
359 
360 /***************************************************************************
361  *
362  *   gnet_conn_http_emit_error_event
363  *
364  ***************************************************************************/
365 
366 static void
gnet_conn_http_emit_error_event(GConnHttp * conn,GConnHttpError code,const gchar * format,...)367 gnet_conn_http_emit_error_event (GConnHttp *conn, GConnHttpError code, const gchar *format, ...)
368 {
369 	GConnHttpEventError *ev_err;
370 	GConnHttpEvent      *ev;
371 	va_list              args;
372 
373 	g_return_if_fail (conn != NULL);
374 
375 	conn->status = STATUS_ERROR;
376 
377 	ev = gnet_conn_http_new_event(GNET_CONN_HTTP_ERROR);
378 	ev_err = (GConnHttpEventError*)ev;
379 
380 	ev_err->code = code;
381 
382 	va_start(args, format);
383 	ev_err->message = g_strdup_vprintf(format, args);
384 	va_end(args);
385 
386 	gnet_conn_http_emit_event(conn, ev);
387 	gnet_conn_http_free_event(ev);
388 
389 	if (conn->loop)
390 		g_main_loop_quit(conn->loop);
391 }
392 
393 /***************************************************************************
394  *
395  *   is_in_str_arr
396  *
397  ***************************************************************************/
398 
399 static gboolean
is_in_str_arr(const gchar ** arr,guint num,const gchar * field)400 is_in_str_arr (const gchar **arr, guint num, const gchar *field)
401 {
402 	guint i;
403 
404 	g_return_val_if_fail (arr   != NULL, FALSE);
405 	g_return_val_if_fail (field != NULL, FALSE);
406 
407 	for (i = 0;  i < num;  ++i)
408 	{
409 		if (g_ascii_strcasecmp(arr[i], field) == 0)
410 			return TRUE;
411 	}
412 
413 	return FALSE;
414 }
415 
416 
417 /**
418  *  gnet_conn_http_set_header
419  *  @conn: a #GConnHttp
420  *  @field: a header field, e.g. "Accept"
421  *  @value: the header field value, e.g. "text/html"
422  *  @flags: one or more flags of #GConnHttpHeaderFlags, or 0
423  *
424  *  Set header field to send with the HTTP request.
425  *
426  *  Returns: TRUE if the header field has been set or changed
427  *
428  **/
429 
430 gboolean
gnet_conn_http_set_header(GConnHttp * conn,const gchar * field,const gchar * value,GConnHttpHeaderFlags flags)431 gnet_conn_http_set_header (GConnHttp   *conn,
432                            const gchar *field,
433                            const gchar *value,
434                            GConnHttpHeaderFlags flags)
435 {
436 	GConnHttpHdr *hdr;
437 	GList        *node;
438 
439 	g_return_val_if_fail (conn  != NULL, FALSE);
440 	g_return_val_if_fail (field != NULL, FALSE);
441 	g_return_val_if_fail (GNET_IS_CONN_HTTP (conn), FALSE);
442 
443 	/* Don't allow 'Host' to be set explicitely
444 	 *  we'll do that ourselves */
445 	if (g_ascii_strcasecmp(field, "Host") == 0)
446 		return FALSE;
447 
448 	/* only allow uncommon header fields if
449 	 * explicitely allowed via the flags */
450 	if ((flags & GNET_CONN_HTTP_FLAG_SKIP_HEADER_CHECK) == 0)
451 	{
452 		if (!(is_general_header(field) || is_request_header(field)))
453 			return FALSE;
454 	}
455 
456 	for (node = conn->req_headers;  node;  node = node->next)
457 	{
458 		hdr = (GConnHttpHdr*)node->data;
459 		if (g_str_equal(hdr->field, field))
460 		{
461 			g_free(hdr->value);
462 			hdr->value = g_strdup(value);
463 			return TRUE;
464 		}
465 	}
466 
467 	hdr = g_new0 (GConnHttpHdr, 1);
468 	hdr->field = g_strdup(field);
469 	hdr->value = g_strdup(value);
470 
471 	conn->req_headers = g_list_append(conn->req_headers, hdr);
472 
473 	return TRUE;
474 }
475 
476 
477 /**
478  *  gnet_conn_http_set_user_agent
479  *  @conn: a #GConnHttp
480  *  @agent: the user agent string to send (will be supplemented by a GNet
481  *          version number comment)
482  *
483  *  Convenience function. Wraps gnet_conn_http_set_header().
484  *
485  *  Returns: TRUE if the user agent string has been changed.
486  *
487  **/
488 
489 gboolean
gnet_conn_http_set_user_agent(GConnHttp * conn,const gchar * agent)490 gnet_conn_http_set_user_agent (GConnHttp *conn, const gchar *agent)
491 {
492 	gboolean ret;
493 	gchar *full;
494 
495 	g_return_val_if_fail (GNET_IS_CONN_HTTP (conn), FALSE);
496 
497 	if (agent == NULL)
498 	{
499 		agent = (const gchar *) g_get_prgname ();
500 		if (agent == NULL)
501 			agent = "GNet";
502 	}
503 
504 	full = g_strdup_printf ("%s (GNet-%u.%u.%u)", agent, GNET_MAJOR_VERSION,
505 	                        GNET_MINOR_VERSION, GNET_MICRO_VERSION);
506 
507 	ret = gnet_conn_http_set_header (conn, "User-Agent", full, 0);
508 	g_free (full);
509 
510 	return ret;
511 }
512 
513 static gboolean
gnet_conn_http_set_uri_internal(GConnHttp * conn,const gchar * uri,gboolean uri_is_escaped)514 gnet_conn_http_set_uri_internal (GConnHttp *conn, const gchar *uri,
515     gboolean uri_is_escaped)
516 {
517 	gchar *old_hostname = NULL;
518 
519 	g_assert (conn != NULL && uri != NULL);
520 
521 	if (conn->uri)
522 	{
523 		old_hostname = g_strdup(conn->uri->hostname);
524 		gnet_uri_delete(conn->uri);
525 		conn->uri = NULL;
526 	}
527 
528 	/* Add 'http://' prefix if no scheme/protocol is specified */
529 	if (strstr(uri,"://") == NULL)
530 	{
531 		gchar *full_uri = g_strconcat("http://", uri, NULL);
532 		conn->uri = gnet_uri_new(full_uri);
533 		g_free(full_uri);
534 	}
535 	else
536 	{
537 		if (g_ascii_strncasecmp(uri, "http:", 5) != 0)
538 			return FALSE; /* unsupported protocol */
539 
540 		conn->uri = gnet_uri_new(uri);
541 	}
542 
543 	if (conn->uri && old_hostname && g_ascii_strcasecmp(conn->uri->hostname, old_hostname) != 0)
544 	{
545 		if (conn->ia)
546 		{
547 			gnet_inetaddr_delete(conn->ia);
548 			conn->ia = NULL;
549 		}
550 		if (conn->conn)
551 		{
552 			gnet_conn_unref(conn->conn);
553 			conn->conn = NULL;
554 		}
555 	}
556 
557 	g_free(old_hostname);
558 
559 	if (conn->uri == NULL)
560 		return FALSE;
561 
562 	gnet_uri_set_scheme(conn->uri, "http");
563 
564 	if (!uri_is_escaped)
565 		gnet_uri_escape (conn->uri);
566 
567 	return TRUE;
568 }
569 
570 /**
571  *  gnet_conn_http_set_uri
572  *  @conn: a #GConnHttp
573  *  @uri: URI string
574  *
575  *  Sets the URI to GET or POST, e.g. http://www.foo.com/bar.html. @uri is
576  *  assumed to be unescaped, so all special URI characters will be escaped.
577  *
578  *  Returns: TRUE if the URI has been accepted.
579  *
580  **/
581 
582 gboolean
gnet_conn_http_set_uri(GConnHttp * conn,const gchar * uri)583 gnet_conn_http_set_uri (GConnHttp *conn, const gchar *uri)
584 {
585 	g_return_val_if_fail (conn != NULL, FALSE);
586 	g_return_val_if_fail (uri  != NULL, FALSE);
587 	g_return_val_if_fail (GNET_IS_CONN_HTTP (conn), FALSE);
588 
589 	return gnet_conn_http_set_uri_internal (conn, uri, FALSE);
590 }
591 
592 /**
593  *  gnet_conn_http_set_escaped_uri
594  *  @conn: a #GConnHttp
595  *  @uri: URI string with special characters already escaped
596  *
597  *  Sets the URI to GET or POST, e.g. http://www.foo.com/My%%20Documents/bar.txt
598  *
599  *  Returns: TRUE if the URI has been accepted.
600  *
601  **/
602 
603 gboolean
gnet_conn_http_set_escaped_uri(GConnHttp * conn,const gchar * uri)604 gnet_conn_http_set_escaped_uri (GConnHttp *conn, const gchar *uri)
605 {
606 	g_return_val_if_fail (conn != NULL, FALSE);
607 	g_return_val_if_fail (uri  != NULL, FALSE);
608 	g_return_val_if_fail (GNET_IS_CONN_HTTP (conn), FALSE);
609 
610 	return gnet_conn_http_set_uri_internal (conn, uri, TRUE);
611 }
612 
613 /**
614  *  gnet_conn_http_set_method
615  *  @conn: a #GConnHttp
616  *  @method: a #GConnHttpMethod
617  *  @post_data: post data to send with POST method, or NULL
618  *  @post_data_len: the length of the post data to send with POST method, or 0
619  *
620  *  Sets the HTTP request method. Default is the GET method.
621  *
622  *  Returns: TRUE if the method has been changed successfully.
623  *
624  **/
625 
626 gboolean
gnet_conn_http_set_method(GConnHttp * conn,GConnHttpMethod method,const gchar * post_data,gsize post_data_len)627 gnet_conn_http_set_method (GConnHttp        *conn,
628                            GConnHttpMethod   method,
629                            const gchar      *post_data,
630                            gsize             post_data_len)
631 {
632 	g_return_val_if_fail (conn != NULL, FALSE);
633 	g_return_val_if_fail (GNET_IS_CONN_HTTP (conn), FALSE);
634 
635 	switch (method)
636 	{
637 		case GNET_CONN_HTTP_METHOD_GET:
638 			conn->method = method;
639 			return TRUE;
640 
641 		case GNET_CONN_HTTP_METHOD_POST:
642 		{
643 			g_return_val_if_fail (post_data != NULL, FALSE);
644 			g_return_val_if_fail (post_data_len > 0, FALSE);
645 
646 			conn->method = method;
647 
648 			g_free(conn->post_data);
649 			conn->post_data = g_memdup(post_data, post_data_len);
650 			conn->post_data = g_realloc(conn->post_data, post_data_len + 2 + 2 + 1);
651 			conn->post_data_len = post_data_len;
652 
653 			conn->post_data[conn->post_data_len+0] = '\r';
654 			conn->post_data[conn->post_data_len+1] = '\n';
655 			conn->post_data[conn->post_data_len+2] = '\r';
656 			conn->post_data[conn->post_data_len+3] = '\n';
657 			conn->post_data[conn->post_data_len+4] = '\000';
658 
659 			conn->post_data_term_len = 0;
660 			while (conn->post_data_len < 4
661 			    || !g_str_equal(conn->post_data + conn->post_data_len + conn->post_data_term_len - 4, "\r\n\r\n"))
662 				conn->post_data_term_len += 2;
663 
664 			return TRUE;
665 		}
666 
667 		default:
668 			break;
669 	}
670 
671 	return FALSE;
672 }
673 
674 
675 /***************************************************************************
676  *
677  *   gnet_conn_http_conn_connected
678  *
679  ***************************************************************************/
680 
681 static void
gnet_conn_http_conn_connected(GConnHttp * conn)682 gnet_conn_http_conn_connected (GConnHttp *conn)
683 {
684 	const gchar *resource;
685 	GString     *request;
686 	GList       *node;
687 	gchar       *res;
688 
689 	gnet_conn_http_reset(conn);
690 	gnet_conn_timeout(conn->conn, conn->timeout);
691 
692 	request = g_string_new(NULL);
693 
694 	res = gnet_uri_get_string(conn->uri);
695 	resource = res + strlen(conn->uri->scheme) + strlen("://") + strlen(conn->uri->hostname);
696 
697 	if (*resource == ':')
698 	{
699 		resource = strchr(resource, '/');
700 		if (resource == NULL || *resource == 0x00)
701 			resource = "/";
702 	}
703 
704 
705 	switch (conn->method)
706 	{
707 		case GNET_CONN_HTTP_METHOD_GET:
708 			g_string_append_printf (request, "GET %s HTTP/1.1\r\n", resource);
709 		break;
710 
711 		case GNET_CONN_HTTP_METHOD_POST:
712 		{
713 			gchar  buf[16];
714 
715 
716 			/* Note: this must be 1.1 */
717 			g_string_append_printf (request, "POST %s HTTP/1.1\r\n", resource);
718 
719 			g_snprintf(buf, sizeof(buf), "%u", (guint) conn->post_data_len);
720 
721 			gnet_conn_http_set_header (conn, "Expect", "100-continue", 0);
722 			gnet_conn_http_set_header (conn, "Content-Length", buf, 0);
723 		}
724 		break;
725 
726 		default:
727 			g_warning("Unknown http method in %s\n", __FUNCTION__);
728 			return;
729 	}
730 
731 	for (node = conn->req_headers;  node;  node = node->next)
732 	{
733 		GConnHttpHdr *hdr = (GConnHttpHdr*)node->data;
734 		if (hdr->field && hdr->value && *hdr->field && *hdr->value)
735 		{
736 			g_string_append_printf(request, "%s: %s\r\n", hdr->field, hdr->value);
737 		}
738 	}
739 
740 	if (conn->uri->port == 80)
741 	{
742 		g_string_append_printf(request, "Host: %s\r\n",
743 		                       conn->uri->hostname);
744 	}
745 	else
746 	{
747 		g_string_append_printf(request, "Host: %s:%u\r\n",
748 		                       conn->uri->hostname,
749 		                       conn->uri->port);
750 	}
751 
752 	g_string_append(request, "\r\n");
753 
754 /*	g_print ("Sending:\n%s\n", request->str); */
755 	gnet_conn_write(conn->conn, request->str, request->len);
756 	conn->status = STATUS_SENT_REQUEST;
757 
758 	/* read response */
759 	gnet_conn_readline(conn->conn);
760 
761 	g_string_free(request, TRUE);
762 	g_free(res);
763 }
764 
765 /***************************************************************************
766  *
767  *   gnet_conn_http_done
768  *
769  *   Finished receiving data
770  *
771  ***************************************************************************/
772 
773 static void
gnet_conn_http_done(GConnHttp * conn)774 gnet_conn_http_done (GConnHttp *conn)
775 {
776 	GConnHttpEventData *ev_data;
777 	GConnHttpEvent     *ev;
778 
779 	conn->status = STATUS_DONE;
780 
781 	gnet_conn_timeout (conn->conn, 0);
782 
783 	/* we don't want to emit a DATA_COMPLETE event
784 	 *  if we are getting redirected, do we? */
785 	if (conn->redirect_location == NULL)
786 	{
787 		ev = gnet_conn_http_new_event (GNET_CONN_HTTP_DATA_COMPLETE);
788 		ev_data = (GConnHttpEventData*)ev;
789 		ev_data->buffer         = conn->buffer;
790 		ev_data->buffer_length  = conn->buflen;
791 		ev_data->content_length = conn->content_length;
792 		ev_data->data_received  = conn->content_recv;
793 		gnet_conn_http_emit_event (conn, ev);
794 		gnet_conn_http_free_event (ev);
795 	}
796 
797 	if (conn->connection_close)
798 		gnet_conn_disconnect(conn->conn);
799 
800 	/* need to do auto-redirect now? */
801 	if (conn->redirect_location && conn->refcount > 0)
802 	{
803 		if (gnet_conn_http_set_uri (conn, conn->redirect_location))
804 		{
805 			/* send request with new URI */
806 			gnet_conn_http_run_async (conn, conn->func, conn->func_data);
807 
808 			return; /* do not quit own loop just yet */
809 		}
810 
811 		gnet_conn_http_emit_error_event (conn, GNET_CONN_HTTP_ERROR_UNSPECIFIED,
812 		                                 "Auto-redirect failed for some reason.");
813 	}
814 
815 	if (conn->loop)
816 		g_main_loop_quit(conn->loop);
817 }
818 
819 /***************************************************************************
820  *
821  *   gnet_conn_http_conn_parse_response_headers
822  *
823  ***************************************************************************/
824 
825 static void
gnet_conn_http_conn_parse_response_headers(GConnHttp * conn)826 gnet_conn_http_conn_parse_response_headers (GConnHttp *conn)
827 {
828 	GConnHttpEventRedirect *ev_redirect;
829 	GConnHttpEventResponse *ev_response;
830 	GConnHttpEvent         *ev;
831 	const gchar            *new_location = NULL;
832 	guint                   num_headers, n;
833 
834 	GList *node;
835 
836 	num_headers = g_list_length(conn->resp_headers);
837 
838 	ev = gnet_conn_http_new_event (GNET_CONN_HTTP_RESPONSE);
839 	ev_response = (GConnHttpEventResponse*)ev;
840 	ev_response->header_fields = g_new0(gchar *, num_headers+1);
841 	ev_response->header_values = g_new0(gchar *, num_headers+1);
842 	ev_response->response_code = conn->response_code;
843 
844 	n = 0;
845 	conn->tenc_chunked = FALSE;
846 	for (node = conn->resp_headers;  node;  node = node->next)
847 	{
848 		GConnHttpHdr *hdr = (GConnHttpHdr*)node->data;
849 
850 		ev_response->header_fields[n] = g_strdup(hdr->field); /* better safe than sorry */
851 		ev_response->header_values[n] = g_strdup(hdr->value);
852 
853 		if (g_ascii_strcasecmp(hdr->field, "Content-Length") == 0)
854 		{
855 			conn->content_length = atoi(hdr->value);
856 			conn->got_content_length = TRUE;
857 		}
858 		else if (g_ascii_strcasecmp(hdr->field, "Transfer-Encoding") == 0
859 		      && g_ascii_strcasecmp(hdr->value, "chunked") == 0)
860 		{
861 			conn->tenc_chunked = TRUE;
862 		}
863 		else if (g_ascii_strcasecmp(hdr->field, "Location") == 0)
864 		{
865 			new_location = hdr->value;
866 		}
867 		/* Note: amazon sends garbled 'Connection' string, but it
868 		 *  might also be some apache module problem */
869 		else if (g_ascii_strcasecmp(hdr->field, "Connection") == 0
870 		      || g_ascii_strcasecmp(hdr->field, "Cneonction") == 0
871 		      || g_ascii_strcasecmp(hdr->field, "nnCoection") == 0)
872 		{
873 			conn->connection_close = (g_ascii_strcasecmp(hdr->value, "close") == 0);
874 		}
875 		else
876 		{
877 			;
878 		}
879 
880 		++n;
881 	}
882 
883 	/* send generic response event first */
884 	gnet_conn_http_emit_event (conn, ev);
885 	gnet_conn_http_free_event (ev);
886 
887 	/* If not a redirection code, continue normally */
888 	if (conn->response_code < 300 || conn->response_code >= 400)
889 		return; /* continue */
890 
891 	ev = gnet_conn_http_new_event (GNET_CONN_HTTP_REDIRECT);
892 	ev_redirect = (GConnHttpEventRedirect*)ev;
893 
894 	ev_redirect->num_redirects = conn->num_redirects;
895 	ev_redirect->max_redirects = conn->max_redirects;
896 	ev_redirect->auto_redirect = TRUE;
897 
898 	if (conn->response_code == 301 && conn->method == GNET_CONN_HTTP_METHOD_POST)
899 		ev_redirect->auto_redirect = FALSE;
900 
901 	if (conn->num_redirects >= conn->max_redirects)
902 		ev_redirect->auto_redirect = FALSE;
903 
904 	/* No Location: header field? tough luck, can't do much */
905 	ev_redirect->new_location = g_strdup(new_location);
906 	if (new_location == NULL)
907 		ev_redirect->auto_redirect = FALSE;
908 
909 	/* send generic redirect event (dispatch first to give
910 	 *  the client a chance to stop us before we do the
911 	 *  automatic redirection) */
912 	gnet_conn_http_emit_event (conn, ev);
913 
914 	/* do the redirect later after receiving the body (if any) */
915 	if (ev_redirect->auto_redirect)
916 	{
917 		g_free (conn->redirect_location);
918 		conn->redirect_location = g_strdup(new_location);
919 	}
920 
921 	gnet_conn_http_free_event (ev);
922 
923 	return; /* continue and receive body */
924 }
925 
926 
927 /***************************************************************************
928  *
929  *   gnet_conn_http_conn_recv_response
930  *
931  ***************************************************************************/
932 
933 static void
gnet_conn_http_conn_recv_response(GConnHttp * conn,gchar * data,gsize len)934 gnet_conn_http_conn_recv_response (GConnHttp *conn, gchar *data, gsize len)
935 {
936 	gchar *endptr, *start;
937 
938 	/* after a 100 Continue response, skip everything until we get a
939 	 * proper next response (there may be headers and there should
940 	 * definitely be an empty line following the 100 Continue response) */
941 	if (conn->method == GNET_CONN_HTTP_METHOD_POST &&
942 	    conn->response_code == 100 &&
943 	    !g_str_has_prefix (data, "HTTP/") &&
944 	    !g_str_has_prefix (data, "http/")) {
945 		gnet_conn_readline(conn->conn);
946 		return;
947 	}
948 
949 	/* This should be a response line */
950 	if (g_ascii_strncasecmp (data, "ICY ", 4) != 0 &&
951 	    g_ascii_strncasecmp (data, "HTTP/", 5) != 0) {
952 		goto unsupported_proto;
953 	}
954 
955 	start = strchr (data, ' ');
956 	if (start == NULL)
957 		goto malformed_response;
958 
959 	/* FIXME: should we also check the HTTP protocol version? */
960 	conn->response_code = (guint) strtol(start+1, &endptr, 10);
961 
962 	/* read first header */
963 	gnet_conn_readline(conn->conn);
964 
965 	/* may we continue the POST request? */
966 	if (conn->response_code == 100 && conn->method == GNET_CONN_HTTP_METHOD_POST)
967 	{
968 		gnet_conn_write(conn->conn, conn->post_data, conn->post_data_len + conn->post_data_term_len);
969 		conn->status = STATUS_SENT_REQUEST; /* expecting the response for the content next */
970 		return;
971 	}
972 
973 	/* note: redirection is handled after we have all headers */
974 
975 	conn->status = STATUS_RECV_HEADERS; /* response ok - expect headers next */
976 	return;
977 
978 /* ERRORS */
979 unsupported_proto:
980   {
981     conn->response_code = 0;
982     gnet_conn_http_emit_error_event (conn,
983         GNET_CONN_HTTP_ERROR_PROTOCOL_UNSUPPORTED,
984         "Not a HTTP or ICY protocol response: '%s'", data);
985     return;
986   }
987 malformed_response:
988   {
989     conn->response_code = 0;
990     gnet_conn_http_emit_error_event (conn,
991         GNET_CONN_HTTP_ERROR_PROTOCOL_UNSUPPORTED,
992         "Malformed response: '%s'", data);
993     return;
994   }
995 }
996 
997 /***************************************************************************
998  *
999  *   gnet_conn_http_conn_recv_headers
1000  *
1001  ***************************************************************************/
1002 
1003 static void
gnet_conn_http_conn_recv_headers(GConnHttp * conn,gchar * data,gsize len)1004 gnet_conn_http_conn_recv_headers (GConnHttp *conn, gchar *data, gsize len)
1005 {
1006 	gchar *colon;
1007 
1008 	/* End of headers? */
1009 	if (*data == 0x00 || g_str_equal(data,"\r\n") || g_str_equal(data,"\r") || g_str_equal(data,"\n"))
1010 	{
1011 		gnet_conn_http_conn_parse_response_headers (conn);
1012 
1013 		if (conn->got_content_length && conn->content_length == 0)
1014 		{
1015 			/* no body to receive */
1016 			gnet_conn_http_done (conn);
1017 			return;
1018 		}
1019 		else if (conn->tenc_chunked)
1020 		{
1021 			/* read line with chunk size of first data chunk */
1022 			gnet_conn_readline(conn->conn);
1023 			conn->status = STATUS_RECV_CHUNK_SIZE;
1024 			return;
1025 		}
1026 		else
1027 		{
1028 			gnet_conn_read(conn->conn);
1029 			conn->status = STATUS_RECV_BODY_NONCHUNKED;
1030 			return;
1031 		}
1032 
1033 		g_return_if_reached();
1034 	}
1035 
1036 	/* this is a normal header line then */
1037 	colon = strchr(data, ':');
1038 	if (colon)
1039 	{
1040 		GConnHttpHdr *hdr;
1041 
1042 		*colon = 0x00;
1043 
1044 		hdr = g_new0 (GConnHttpHdr, 1);
1045 		hdr->field = g_strdup(data);
1046 		hdr->value = g_strstrip(g_strdup(colon+1));
1047 
1048 		conn->resp_headers = g_list_append(conn->resp_headers, hdr);
1049 	}
1050 
1051 	/* read next header line */
1052 	gnet_conn_readline(conn->conn);
1053 }
1054 
1055 /***************************************************************************
1056  *
1057  *   gnet_conn_http_conn_recv_chunk_size
1058  *
1059  ***************************************************************************/
1060 
1061 static void
gnet_conn_http_conn_recv_chunk_size(GConnHttp * conn,gchar * data,gsize len)1062 gnet_conn_http_conn_recv_chunk_size (GConnHttp *conn, gchar *data, gsize len)
1063 {
1064 	gchar *endptr;
1065 	gsize  chunksize;
1066 
1067 	chunksize = (gsize) strtol (data, &endptr, 16);
1068 
1069 	if (chunksize == 0)
1070 	{
1071 		gnet_conn_http_done(conn);
1072 		return;
1073 	}
1074 
1075 	gnet_conn_readn(conn->conn, chunksize+2); /* including \r\n */
1076 	conn->status = STATUS_RECV_CHUNK_BODY;
1077 }
1078 
1079 /***************************************************************************
1080  *
1081  *   gnet_conn_http_conn_recv_chunk_body
1082  *
1083  ***************************************************************************/
1084 
1085 static void
gnet_conn_http_conn_recv_chunk_body(GConnHttp * conn,gchar * data,gsize len)1086 gnet_conn_http_conn_recv_chunk_body (GConnHttp *conn, gchar *data, gsize len)
1087 {
1088 	/* do not count the '\r\n' at the end of a block */
1089 	if (len >=2 && data[len-2] == '\r' && data[len-1] == '\n')
1090 		len -= 2;
1091 
1092 	conn->content_recv += len;
1093 	gnet_conn_http_append_to_buf(conn, data, len);
1094 
1095 	/* we don't want to emit data events if we're
1096 	 *  getting redirected anyway, do we? */
1097 	if (conn->redirect_location == NULL)
1098 	{
1099 		GConnHttpEventData *ev_data;
1100 		GConnHttpEvent     *ev;
1101 
1102 		ev = gnet_conn_http_new_event(GNET_CONN_HTTP_DATA_PARTIAL);
1103 		ev_data = (GConnHttpEventData*)ev;
1104 		ev_data->buffer = conn->buffer;
1105 		ev_data->buffer_length = conn->buflen;
1106 		ev_data->content_length = conn->content_length;
1107 		ev_data->data_received  = conn->content_recv;
1108 		gnet_conn_http_emit_event(conn, ev);
1109 		gnet_conn_http_free_event(ev);
1110 	}
1111 
1112 	/* read line with chunk size of next chunk */
1113 	gnet_conn_readline(conn->conn);
1114 
1115 	conn->status = STATUS_RECV_CHUNK_SIZE;
1116 }
1117 
1118 /***************************************************************************
1119  *
1120  *   gnet_conn_http_conn_recv_nonchunked_data
1121  *
1122  ***************************************************************************/
1123 
1124 static void
gnet_conn_http_conn_recv_nonchunked_data(GConnHttp * conn,gchar * data,gsize len)1125 gnet_conn_http_conn_recv_nonchunked_data (GConnHttp *conn, gchar *data, gsize len)
1126 {
1127 	if (conn->content_length > 0)
1128 	{
1129 		conn->content_recv += len;
1130 		gnet_conn_http_append_to_buf(conn, data, len);
1131 
1132 		if (conn->content_recv >= conn->content_length)
1133 		{
1134 			gnet_conn_http_done(conn);
1135 			return;
1136 		}
1137 
1138 		gnet_conn_read(conn->conn);
1139 	}
1140 	else
1141 	{
1142 		conn->content_recv += len;
1143 		gnet_conn_http_append_to_buf (conn, data, len);
1144 		gnet_conn_read (conn->conn);
1145 	}
1146 
1147 	/* we don't want to emit data events if we're
1148 	 *  getting redirected anyway, do we? */
1149 	if (conn->redirect_location == NULL)
1150 	{
1151 		GConnHttpEventData *ev_data;
1152 		GConnHttpEvent     *ev;
1153 
1154 		ev = gnet_conn_http_new_event(GNET_CONN_HTTP_DATA_PARTIAL);
1155 		ev_data = (GConnHttpEventData*)ev;
1156 		ev_data->buffer = conn->buffer;
1157 		ev_data->buffer_length = conn->buflen;
1158 		ev_data->content_length = conn->content_length;
1159 		ev_data->data_received  = conn->content_recv;
1160 		gnet_conn_http_emit_event(conn, ev);
1161 		gnet_conn_http_free_event(ev);
1162 	}
1163 }
1164 
1165 /***************************************************************************
1166  *
1167  *   gnet_conn_http_conn_got_data
1168  *
1169  ***************************************************************************/
1170 
1171 static void
gnet_conn_http_conn_got_data(GConnHttp * conn,gchar * data,gsize len)1172 gnet_conn_http_conn_got_data (GConnHttp *conn, gchar *data, gsize len)
1173 {
1174 	gnet_conn_timeout (conn->conn, conn->timeout);
1175 
1176 	switch (conn->status)
1177 	{
1178 		/* sent request - so this must be the response code */
1179 		case STATUS_SENT_REQUEST:
1180 			gnet_conn_http_conn_recv_response(conn, data, len);
1181 			break;
1182 
1183 		case STATUS_RECV_HEADERS:
1184 			gnet_conn_http_conn_recv_headers(conn, data, len);
1185 			break;
1186 
1187 		case STATUS_RECV_BODY_NONCHUNKED:
1188 			gnet_conn_http_conn_recv_nonchunked_data(conn, data, len);
1189 			break;
1190 
1191 		case STATUS_RECV_CHUNK_SIZE:
1192 			gnet_conn_http_conn_recv_chunk_size(conn, data, len);
1193 			break;
1194 
1195 		case STATUS_RECV_CHUNK_BODY:
1196 			gnet_conn_http_conn_recv_chunk_body(conn, data, len);
1197 			break;
1198 
1199 		default:
1200 			gnet_conn_http_emit_error_event(conn, GNET_CONN_HTTP_ERROR_UNSPECIFIED,
1201 			                                "%s: should not be reached. conn->status = %u",
1202 			                                G_STRLOC, conn->status);
1203 			break;
1204 	}
1205 }
1206 
1207 /***************************************************************************
1208  *
1209  *   gnet_conn_http_conn_cb
1210  *
1211  *   GConn callback function
1212  *
1213  ***************************************************************************/
1214 
1215 static void
gnet_conn_http_conn_cb(GConn * c,GConnEvent * event,GConnHttp * httpconn)1216 gnet_conn_http_conn_cb (GConn *c, GConnEvent *event, GConnHttp *httpconn)
1217 {
1218 	GConnHttpEvent *ev;
1219 
1220 	g_return_if_fail (GNET_IS_CONN_HTTP (httpconn));
1221 
1222 	switch (event->type)
1223 	{
1224 		case GNET_CONN_ERROR:
1225 			gnet_conn_disconnect(httpconn->conn);
1226 			gnet_conn_http_emit_error_event(httpconn, GNET_CONN_HTTP_ERROR_UNSPECIFIED,
1227 			                                "GNET_CONN_ERROR (unspecified Gnet GConn error)");
1228 			if (httpconn->loop)
1229 				g_main_loop_quit(httpconn->loop);
1230 			break;
1231 
1232 		case GNET_CONN_CONNECT:
1233 			gnet_conn_http_conn_connected(httpconn);
1234 			break;
1235 
1236 		case GNET_CONN_CLOSE:
1237 			gnet_conn_disconnect(httpconn->conn);
1238 			/* _done() will take care of redirection and main loop quitting */
1239 			gnet_conn_http_done(httpconn);
1240 			break;
1241 
1242 		case GNET_CONN_TIMEOUT:
1243 			ev = gnet_conn_http_new_event(GNET_CONN_HTTP_TIMEOUT);
1244 			gnet_conn_http_emit_event(httpconn, ev);
1245 			gnet_conn_http_free_event(ev);
1246 			if (httpconn->loop)
1247 				g_main_loop_quit(httpconn->loop);
1248 			break;
1249 
1250 		case GNET_CONN_READ:
1251 			/* Note: GConn translates reads of 0 bytes into CLOSE for us */
1252 			gnet_conn_http_conn_got_data(httpconn, event->buffer, event->length);
1253 			break;
1254 
1255 		case GNET_CONN_WRITE:
1256 		case GNET_CONN_READABLE:
1257 		case GNET_CONN_WRITABLE:
1258 			break;
1259 	}
1260 
1261 	if (httpconn->refcount == 0)
1262 		gnet_conn_http_delete_internal (httpconn);
1263 }
1264 
1265 /***************************************************************************
1266  *
1267  *   gnet_conn_http_ia_cb
1268  *
1269  *   GInetAddr async resolve callback function
1270  *
1271  ***************************************************************************/
1272 
1273 static void
gnet_conn_http_ia_cb(GInetAddr * ia,GConnHttp * conn)1274 gnet_conn_http_ia_cb (GInetAddr *ia, GConnHttp *conn)
1275 {
1276 	conn->ia_id = 0;
1277 
1278 	g_return_if_fail (GNET_IS_CONN_HTTP (conn));
1279 
1280 	if (ia != conn->ia || ia == NULL)
1281 	{
1282 		GConnHttpEventResolved *ev_resolved;
1283 		GConnHttpEvent         *ev;
1284 
1285 		conn->ia = ia;
1286 
1287 		ev = gnet_conn_http_new_event (GNET_CONN_HTTP_RESOLVED);
1288 		ev_resolved = (GConnHttpEventResolved*)ev;
1289 		ev_resolved->ia = conn->ia;
1290 		gnet_conn_http_emit_event (conn, ev);
1291 		gnet_conn_http_free_event (ev);
1292 
1293 		if (conn->refcount == 0)
1294 		{
1295 			gnet_conn_http_delete_internal (conn);
1296 			return;
1297 		}
1298 	}
1299 
1300 	/* could not resolve hostname? => emit error event and quit */
1301 	if (ia == NULL)
1302 	{
1303 		if (conn->loop)
1304 			g_main_loop_quit(conn->loop);
1305 
1306 		gnet_conn_http_emit_error_event (conn, GNET_CONN_HTTP_ERROR_HOSTNAME_RESOLUTION,
1307 		                                 "Could not resolve hostname '%s'",
1308 		                                 conn->uri->hostname);
1309 
1310 		return;
1311 	}
1312 
1313 	if (conn->conn == NULL)
1314 	{
1315 		conn->conn = gnet_conn_new_inetaddr (ia, (GConnFunc) gnet_conn_http_conn_cb, conn);
1316 
1317 		if (conn->conn == NULL)
1318 		{
1319 			gnet_conn_http_emit_error_event(conn, GNET_CONN_HTTP_ERROR_UNSPECIFIED,
1320 			                                "%s: Could not create GConn object.",
1321 			                                G_STRLOC);
1322 			return;
1323 		}
1324 
1325 		gnet_conn_set_main_context (conn->conn, conn->context);
1326 		gnet_conn_timeout(conn->conn, conn->timeout);
1327 		gnet_conn_connect(conn->conn);
1328 		gnet_conn_set_watch_error(conn->conn, TRUE);
1329 		return;
1330 	}
1331 
1332 	/* re-use existing connection? */
1333 	if (!gnet_conn_is_connected (conn->conn))
1334 	{
1335 		gnet_conn_timeout(conn->conn, conn->timeout);
1336 		gnet_conn_connect(conn->conn);
1337 	}
1338 	else
1339 	{
1340 		gnet_conn_http_conn_connected(conn);
1341 	}
1342 }
1343 
1344 /**
1345  *  gnet_conn_http_run_async
1346  *  @conn: a #GConnHttp
1347  *  @func: callback function to communicate progress and errors, or NULL
1348  *  @user_data: user data to pass to callback function, or NULL
1349  *
1350  *  Starts connecting and sending the specified http request. Will
1351  *   return immediately. Assumes there is an existing and running
1352  *   default Gtk/GLib/Gnome main loop.
1353  *
1354  **/
1355 
1356 void
gnet_conn_http_run_async(GConnHttp * conn,GConnHttpFunc func,gpointer user_data)1357 gnet_conn_http_run_async (GConnHttp        *conn,
1358                           GConnHttpFunc     func,
1359                           gpointer          user_data)
1360 {
1361 	g_return_if_fail (conn != NULL);
1362 	g_return_if_fail (GNET_IS_CONN_HTTP (conn));
1363 	g_return_if_fail (func != NULL || user_data == NULL);
1364 	g_return_if_fail (conn->uri != NULL);
1365 	g_return_if_fail (conn->ia_id == 0);
1366 
1367 	conn->func = func;
1368 	conn->func_data = user_data;
1369 
1370 	if (conn->uri->port == 0)
1371 		gnet_uri_set_port(conn->uri, 80);  // FIXME: 8080 for http proxies
1372 
1373 	if (conn->ia == NULL)
1374 	{
1375 		conn->ia_id = gnet_inetaddr_new_async_full (conn->uri->hostname,
1376 		    conn->uri->port, (GInetAddrNewAsyncFunc) gnet_conn_http_ia_cb,
1377 		    conn, (GDestroyNotify) NULL, conn->context, G_PRIORITY_DEFAULT);
1378 	}
1379 	else
1380 	{
1381 		gnet_conn_http_ia_cb(conn->ia, conn);
1382 	}
1383 }
1384 
1385 /**
1386  *  gnet_conn_http_run
1387  *  @conn: a #GConnHttp
1388  *  @func: callback function to communicate progress and errors, or NULL
1389  *  @user_data: user data to pass to callback function, or NULL
1390  *
1391  *  Starts connecting and sending the specified http request. Will
1392  *   return once the operation has finished and either an error has
1393  *   occured, or the data has been received in full.
1394  *
1395  *   This function will run its own main loop in the default GLib main context
1396  *   (or the user-specified main context, if one was specified with
1397  *   gnet_conn_http_set_main_context()), which means that if your application
1398  *   is based on Gtk+ or sets up GLib timeouts or idle callbacks, it is
1399  *   possible that those callback functions are invoked while you are waiting
1400  *   for gnet_conn_http_run() to return. This means you shouldn't make
1401  *   assumptions about any state you set up before calling this function,
1402  *   because it might have been changed again from within a callback in the
1403  *   mean time (if this can happen or not depends on your callbacks and what
1404  *   they do of course).
1405  *
1406  *   Returns: TRUE if no error occured before connecting
1407  *
1408  **/
1409 
1410 gboolean
gnet_conn_http_run(GConnHttp * conn,GConnHttpFunc func,gpointer user_data)1411 gnet_conn_http_run (GConnHttp        *conn,
1412                     GConnHttpFunc     func,
1413                     gpointer          user_data)
1414 {
1415 	g_return_val_if_fail (conn      != NULL, FALSE);
1416 	g_return_val_if_fail (GNET_IS_CONN_HTTP (conn), FALSE);
1417 	g_return_val_if_fail (conn->uri != NULL, FALSE);
1418 	g_return_val_if_fail (conn->ia_id == 0, FALSE);
1419 
1420 	conn->func = func;
1421 	conn->func_data = user_data;
1422 
1423 	if (conn->uri->port == 0)
1424 		gnet_uri_set_port(conn->uri, 80);  // FIXME: 8080 for http proxies
1425 
1426 	if (conn->ia == NULL)
1427 	{
1428 		conn->ia_id = gnet_inetaddr_new_async_full (conn->uri->hostname,
1429 		    conn->uri->port, (GInetAddrNewAsyncFunc) gnet_conn_http_ia_cb,
1430 		    conn, (GDestroyNotify) NULL, conn->context, G_PRIORITY_DEFAULT);
1431 	}
1432 	else
1433 	{
1434 		gnet_conn_http_ia_cb(conn->ia, conn);
1435 	}
1436 
1437 	conn->loop = g_main_loop_new (NULL, FALSE);
1438 
1439 	g_main_loop_run (conn->loop);
1440 
1441 	if (conn->status == STATUS_DONE)
1442 	{
1443 		if (conn->content_length > 0)
1444 			return (conn->content_recv >= conn->content_length);
1445 
1446 		return (conn->content_recv > 0);
1447 	}
1448 
1449 	return FALSE;
1450 }
1451 
1452 
1453 /**
1454  *  gnet_conn_http_steal_buffer
1455  *  @conn: a #GConnHttp
1456  *  @buffer: where to store a pointer to the buffer data
1457  *  @length: where to store the length of the buffer data
1458  *
1459  *  Empties the current buffer and returns the contents. The
1460  *   main purpose of this function is to make it possible to
1461  *   just use gnet_conn_http_run(), check its return value, and
1462  *   then get the buffer data without having to install a
1463  *   callback function. Also needed to empty the buffer regularly
1464  *   while receiving large amounts of data.
1465  *
1466  *  The caller (you) needs to free the buffer with g_free() when done.
1467  *
1468  *  Returns: TRUE if buffer and length have been set
1469  *
1470  **/
1471 
1472 gboolean
gnet_conn_http_steal_buffer(GConnHttp * conn,gchar ** buffer,gsize * length)1473 gnet_conn_http_steal_buffer (GConnHttp        *conn,
1474                              gchar           **buffer,
1475                              gsize            *length)
1476 {
1477 	g_return_val_if_fail (conn   != NULL, FALSE);
1478 	g_return_val_if_fail (buffer != NULL, FALSE);
1479 	g_return_val_if_fail (length != NULL, FALSE);
1480 	g_return_val_if_fail (GNET_IS_CONN_HTTP (conn), FALSE);
1481 
1482 	if (conn->status == STATUS_NONE
1483 	 || conn->status == STATUS_SENT_REQUEST
1484 	 || conn->status == STATUS_ERROR)
1485 		return FALSE;
1486 
1487 	*length = conn->buflen;
1488 	*buffer = conn->buffer;
1489 
1490 	/* we allocated +1 bytes earlier on, so that there
1491 	 *  is always space for an additional NUL here */
1492 	conn->buffer[conn->buflen] = '\0';
1493 
1494 	/* alloc one more to make sure buffer can be NUL-terminated later */
1495 	conn->buffer   = g_malloc (GNET_CONN_HTTP_BUF_INCREMENT + 1);
1496 	conn->bufalloc = GNET_CONN_HTTP_BUF_INCREMENT;
1497 	conn->buflen   = 0;
1498 
1499 	return TRUE;
1500 }
1501 
1502 
1503 /**
1504  *  gnet_conn_http_set_max_redirects
1505  *  @conn: a #GConnHttp
1506  *  @num: the maximum number of allowed automatic redirects
1507  *
1508  *  Sets the maximum allowed number of automatic redirects.
1509  *   Note that the HTTP protocol specification (RFC2616) specifies
1510  *   occasions where the client must not redirect automatically
1511  *   without user intervention. In those cases, no automatic redirect
1512  *   will be performed, even if the limit has not been reached yet.
1513  *
1514  **/
1515 
1516 void
gnet_conn_http_set_max_redirects(GConnHttp * conn,guint num)1517 gnet_conn_http_set_max_redirects (GConnHttp *conn, guint num)
1518 {
1519 	g_return_if_fail (conn != NULL);
1520 	g_return_if_fail (num > 100);
1521 
1522 	conn->max_redirects = num;
1523 }
1524 
1525 /***************************************************************************
1526  *
1527  *   gnet_conn_http_delete_internal
1528  *
1529  ***************************************************************************/
1530 
1531 static void
gnet_conn_http_delete_internal(GConnHttp * conn)1532 gnet_conn_http_delete_internal (GConnHttp *conn)
1533 {
1534 	g_return_if_fail (conn != NULL);
1535 	g_return_if_fail (conn->refcount == 0);
1536 
1537 	if (conn->ia_id > 0)
1538 		gnet_inetaddr_new_async_cancel(conn->ia_id);
1539 
1540 	if (conn->ia)
1541 		gnet_inetaddr_delete(conn->ia);
1542 
1543 	if (conn->conn)
1544 		gnet_conn_unref(conn->conn);
1545 
1546 	/* concatenate them into resp_headers, so that
1547 	 *  they will be freed in gnet_conn_http_reset() */
1548 	conn->resp_headers = g_list_concat(conn->resp_headers, conn->req_headers);
1549 	conn->req_headers = NULL;
1550 
1551 	gnet_conn_http_reset(conn);
1552 
1553 	if (conn->uri)
1554 		gnet_uri_delete(conn->uri);
1555 
1556 	if (conn->loop != NULL  &&  g_main_loop_is_running(conn->loop))
1557 	{
1558 		g_warning("conn->loop != NULL and still running. This indicates"
1559 		          "\ta bug in your code! You are not allowed to call\n"
1560 		          "\tgnet_conn_http_delete() before gnet_conn_http_run()\n"
1561 		          "\thas returned. Use gnet_conn_http_cancel() instead.\n");
1562 	}
1563 
1564 	if (conn->loop)
1565 		g_main_loop_unref(conn->loop);
1566 
1567 	if (conn->context)
1568 		g_main_context_unref (conn->context);
1569 
1570 	g_free(conn->post_data);
1571 
1572 	g_free(conn->buffer);
1573 
1574 	memset(conn, 0xff, sizeof(GConnHttp));
1575 	g_free(conn);
1576 }
1577 
1578 /**
1579  *  gnet_conn_http_delete
1580  *  @conn: a #GConnHttp
1581  *
1582  *  Deletes a #GConnHttp and frees all associated resources.
1583  *
1584  **/
1585 
1586 void
gnet_conn_http_delete(GConnHttp * conn)1587 gnet_conn_http_delete (GConnHttp *conn)
1588 {
1589 	g_return_if_fail (conn != NULL);
1590 	g_return_if_fail (GNET_IS_CONN_HTTP (conn));
1591 	g_return_if_fail (conn->refcount > 0);
1592 
1593 	--conn->refcount;
1594 
1595 	/* if the refcount was greater than 1, we are
1596 	 *  still calling out into the user callback.
1597 	 *  In that case, keep the object alive for now,
1598 	 *  but invalidate it */
1599 	if (conn->refcount > 0)
1600 	{
1601 		conn->stamp = 0;
1602 		return;
1603 	}
1604 
1605 	gnet_conn_http_delete_internal (conn);
1606 }
1607 
1608 
1609 /**
1610  *  gnet_conn_http_cancel
1611  *  @conn: a #GConnHttp
1612  *
1613  *  Cancels the current http transfer (if any) and makes
1614  *   gnet_conn_http_run() return immediately. Will do nothing
1615  *   if the transfer was started with gnet_conn_http_run_async().
1616  *
1617  **/
1618 
1619 void
gnet_conn_http_cancel(GConnHttp * conn)1620 gnet_conn_http_cancel (GConnHttp *conn)
1621 {
1622 	g_return_if_fail (conn != NULL);
1623 	g_return_if_fail (GNET_IS_CONN_HTTP (conn));
1624 
1625 	if (conn->loop)
1626 		g_main_loop_quit(conn->loop);
1627 }
1628 
1629 
1630 /**
1631  *  gnet_conn_http_set_timeout
1632  *  @conn: a #GConnHttp
1633  *  @timeout: timeout in milliseconds
1634  *
1635  *  Sets a timeout on the http connection.
1636  *
1637  **/
1638 
1639 void
gnet_conn_http_set_timeout(GConnHttp * conn,guint timeout)1640 gnet_conn_http_set_timeout (GConnHttp *conn, guint timeout)
1641 {
1642 	g_return_if_fail (conn != NULL);
1643 	g_return_if_fail (GNET_IS_CONN_HTTP (conn));
1644 
1645 	conn->timeout = timeout;
1646 }
1647 
1648 /***************************************************************************
1649  *
1650  *   gnet_http_get_cb
1651  *
1652  ***************************************************************************/
1653 
1654 static void
gnet_http_get_cb(GConnHttp * conn,GConnHttpEvent * event,gpointer user_data)1655 gnet_http_get_cb (GConnHttp *conn, GConnHttpEvent *event, gpointer user_data)
1656 {
1657 	guint *p_response = (guint*) user_data;
1658 
1659 	if (p_response != NULL  &&  event->type == GNET_CONN_HTTP_RESPONSE)
1660 	{
1661 		GConnHttpEventResponse *revent;
1662                 revent = (GConnHttpEventResponse*) event;
1663 		*p_response = revent->response_code;
1664 	}
1665 }
1666 
1667 /**
1668  *  gnet_http_get
1669  *  @url: a URI, e.g. http://www.foo.com
1670  *  @buffer: where to store a pointer to the data retrieved
1671  *  @length: where to store the length of the data retrieved
1672  *  @response: where to store the last HTTP response code
1673  *  received from the HTTP server, or NULL.
1674  *
1675  *  Convenience function that just retrieves
1676  *   the provided URI without the need to
1677  *   set up a full #GConnHttp. Uses
1678  *   gnet_conn_http_run() internally.
1679  *
1680  *  Caller (you) needs to free the buffer with g_free() when
1681  *   no longer needed.
1682  *
1683  *  This function will run its own main loop in the default GLib main context,
1684  *   which means that if your application is based on Gtk+ or sets up GLib
1685  *   timeouts or idle callbacks, it is possible that those callback functions
1686  *   are invoked while you are waiting for gnet_http_get() to return. This
1687  *   means you shouldn't make assumptions about any state you set up before
1688  *   calling this function, because it might have been changed again from
1689  *   within a callback in the mean time (if this can happen or not depends on
1690  *   your callbacks and what they do of course).
1691  *
1692  *  Returns: TRUE if @buffer, @length and @response are set,
1693  *   otherwise FALSE.
1694  *
1695  **/
1696 
1697 gboolean
gnet_http_get(const gchar * url,gchar ** buffer,gsize * length,guint * response)1698 gnet_http_get (const gchar  *url,
1699                gchar       **buffer,
1700                gsize        *length,
1701                guint        *response)
1702 {
1703 	GConnHttp *conn;
1704 	gboolean   ret;
1705 
1706 	g_return_val_if_fail (url != NULL && *url != 0x00, FALSE);
1707 	g_return_val_if_fail (buffer != NULL, FALSE);
1708 	g_return_val_if_fail (length != NULL, FALSE);
1709 
1710 	if (response)
1711 		*response = 0;
1712 
1713 	ret = FALSE;
1714 	conn = gnet_conn_http_new();
1715 
1716 	if (gnet_conn_http_set_uri(conn,url))
1717 	{
1718 		if (gnet_conn_http_run(conn, gnet_http_get_cb, response))
1719 		{
1720 			if (gnet_conn_http_steal_buffer(conn, buffer, length))
1721 			{
1722 				ret = TRUE;
1723 			}
1724 		}
1725 	}
1726 
1727 	gnet_conn_http_delete(conn);
1728 
1729 	return ret;
1730 }
1731 
1732 /**
1733  *  gnet_conn_http_set_main_context:
1734  *  @conn: a #GConnHttp
1735  *  @context: a #GMainContext, or NULL to use the default GLib main context
1736  *
1737  *  Sets the GLib #GMainContext to use for asynchronous operations. You should
1738  *  call this function right after you create @conn. You must not call this
1739  *  function after the actual connection process has started.
1740  *
1741  *  You are very unlikely to ever need this function.
1742  *
1743  *  Returns: TRUE on success, FALSE on failure.
1744  *
1745  *  Since: 2.0.8
1746  **/
1747 gboolean
gnet_conn_http_set_main_context(GConnHttp * conn,GMainContext * context)1748 gnet_conn_http_set_main_context (GConnHttp * conn, GMainContext * context)
1749 {
1750   g_return_val_if_fail (conn != NULL, FALSE);
1751   g_return_val_if_fail (GNET_IS_CONN_HTTP (conn), FALSE);
1752   g_return_val_if_fail (conn->conn == NULL && conn->ia_id == NULL, FALSE);
1753 
1754   if (conn->context != context) {
1755     if (conn->context)
1756       g_main_context_unref (conn->context);
1757     if (context)
1758       conn->context = g_main_context_ref (context);
1759     else
1760       conn->context = NULL;
1761   }
1762 
1763   return TRUE;
1764 }
1765 
1766