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