Migrate xmpp-caps.xml to XDG cache dir
[pidgin-git.git] / libpurple / http.c
blob6d32c143855fa3600e6d16adb9dbd21012f7a33b
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "http.h"
24 #include "internal.h"
25 #include "glibcompat.h"
28 #include "debug.h"
29 #include "ntlm.h"
30 #include "proxy.h"
31 #include "purple-socket.h"
33 #include <zlib.h>
34 #ifndef z_const
35 #define z_const
36 #endif
38 #define PURPLE_HTTP_URL_CREDENTIALS_CHARS "a-z0-9.,~_/*!&%?=+\\^-"
39 #define PURPLE_HTTP_MAX_RECV_BUFFER_LEN 10240
40 #define PURPLE_HTTP_MAX_READ_BUFFER_LEN 10240
41 #define PURPLE_HTTP_GZ_BUFF_LEN 1024
43 #define PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS 20
44 #define PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT 30
45 #define PURPLE_HTTP_REQUEST_DEFAULT_MAX_LENGTH 1048576
46 #define PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH G_MAXINT32-1
48 #define PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL 250000
50 typedef struct _PurpleHttpSocket PurpleHttpSocket;
52 typedef struct _PurpleHttpHeaders PurpleHttpHeaders;
54 typedef struct _PurpleHttpKeepaliveHost PurpleHttpKeepaliveHost;
56 typedef struct _PurpleHttpKeepaliveRequest PurpleHttpKeepaliveRequest;
58 typedef struct _PurpleHttpGzStream PurpleHttpGzStream;
60 struct _PurpleHttpSocket
62 PurpleSocket *ps;
64 gboolean is_busy;
65 guint use_count;
66 PurpleHttpKeepaliveHost *host;
69 struct _PurpleHttpRequest
71 int ref_count;
73 gchar *url;
74 gchar *method;
75 PurpleHttpHeaders *headers;
76 PurpleHttpCookieJar *cookie_jar;
77 PurpleHttpKeepalivePool *keepalive_pool;
79 gchar *contents;
80 int contents_length;
81 PurpleHttpContentReader contents_reader;
82 gpointer contents_reader_data;
83 PurpleHttpContentWriter response_writer;
84 gpointer response_writer_data;
86 int timeout;
87 int max_redirects;
88 gboolean http11;
89 guint max_length;
92 struct _PurpleHttpConnection
94 PurpleConnection *gc;
95 PurpleHttpCallback callback;
96 gpointer user_data;
97 gboolean is_reading;
98 gboolean is_keepalive;
99 gboolean is_cancelling;
101 PurpleHttpURL *url;
102 PurpleHttpRequest *request;
103 PurpleHttpResponse *response;
105 PurpleHttpKeepaliveRequest *socket_request;
106 PurpleHttpConnectionSet *connection_set;
107 PurpleHttpSocket *socket;
108 GString *request_header;
109 guint request_header_written, request_contents_written;
110 gboolean main_header_got, headers_got;
111 GString *response_buffer;
112 PurpleHttpGzStream *gz_stream;
114 GString *contents_reader_buffer;
115 gboolean contents_reader_requested;
117 int redirects_count;
119 int length_expected;
120 guint length_got, length_got_decompressed;
122 gboolean is_chunked, in_chunk, chunks_done;
123 int chunk_length, chunk_got;
125 GList *link_global, *link_gc;
127 guint timeout_handle;
129 PurpleHttpProgressWatcher watcher;
130 gpointer watcher_user_data;
131 guint watcher_interval_threshold;
132 gint64 watcher_last_call;
133 guint watcher_delayed_handle;
136 struct _PurpleHttpResponse
138 int code;
139 gchar *error;
141 GString *contents;
142 PurpleHttpHeaders *headers;
145 struct _PurpleHttpURL
147 gchar *protocol;
148 gchar *username;
149 gchar *password;
150 gchar *host;
151 int port;
152 gchar *path;
153 gchar *fragment;
156 struct _PurpleHttpHeaders
158 GList *list;
159 GHashTable *by_name;
162 typedef struct
164 time_t expires;
165 gchar *value;
166 } PurpleHttpCookie;
168 struct _PurpleHttpCookieJar
170 int ref_count;
172 GHashTable *tab;
175 struct _PurpleHttpKeepaliveRequest
177 PurpleConnection *gc;
178 PurpleSocketConnectCb cb;
179 gpointer user_data;
181 PurpleHttpKeepaliveHost *host;
182 PurpleHttpSocket *hs;
185 struct _PurpleHttpKeepaliveHost
187 PurpleHttpKeepalivePool *pool;
189 gchar *host;
190 int port;
191 gboolean is_ssl;
193 GSList *sockets; /* list of PurpleHttpSocket */
195 GSList *queue; /* list of PurpleHttpKeepaliveRequest */
196 guint process_queue_timeout;
199 struct _PurpleHttpKeepalivePool
201 gboolean is_destroying;
203 int ref_count;
205 guint limit_per_host;
207 /* key: purple_http_socket_hash, value: PurpleHttpKeepaliveHost */
208 GHashTable *by_hash;
211 struct _PurpleHttpConnectionSet
213 gboolean is_destroying;
215 GHashTable *connections;
218 struct _PurpleHttpGzStream
220 gboolean failed;
221 z_stream zs;
222 gsize max_output;
223 gsize decompressed;
224 GString *pending;
227 static time_t purple_http_rfc1123_to_time(const gchar *str);
229 static gboolean purple_http_request_is_method(PurpleHttpRequest *request,
230 const gchar *method);
232 static PurpleHttpConnection * purple_http_connection_new(
233 PurpleHttpRequest *request, PurpleConnection *gc);
234 static void purple_http_connection_terminate(PurpleHttpConnection *hc);
235 static void purple_http_conn_notify_progress_watcher(PurpleHttpConnection *hc);
236 static void
237 purple_http_conn_retry(PurpleHttpConnection *http_conn);
239 static PurpleHttpResponse * purple_http_response_new(void);
240 static void purple_http_response_free(PurpleHttpResponse *response);
242 static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar,
243 GList *values);
244 static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar);
245 gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar);
247 static PurpleHttpKeepaliveRequest *
248 purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
249 PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
250 PurpleSocketConnectCb cb, gpointer user_data);
251 static void
252 purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req);
253 static void
254 purple_http_keepalive_pool_release(PurpleHttpSocket *hs, gboolean invalidate);
256 static void
257 purple_http_connection_set_remove(PurpleHttpConnectionSet *set,
258 PurpleHttpConnection *http_conn);
260 static GRegex *purple_http_re_url, *purple_http_re_url_host,
261 *purple_http_re_rfc1123;
264 * Values: pointers to running PurpleHttpConnection.
266 static GList *purple_http_hc_list;
269 * Keys: pointers to PurpleConnection.
270 * Values: GList of pointers to running PurpleHttpConnection.
272 static GHashTable *purple_http_hc_by_gc;
275 * Keys: pointers to PurpleConnection.
276 * Values: gboolean TRUE.
278 static GHashTable *purple_http_cancelling_gc;
281 * Keys: pointers to PurpleHttpConnection.
282 * Values: pointers to links in purple_http_hc_list.
284 static GHashTable *purple_http_hc_by_ptr;
286 /*** Helper functions *********************************************************/
288 static time_t purple_http_rfc1123_to_time(const gchar *str)
290 static const gchar *months[13] = {
291 "jan", "feb", "mar", "apr", "may", "jun",
292 "jul", "aug", "sep", "oct", "nov", "dec", NULL
294 GMatchInfo *match_info;
295 gchar *d_date, *d_month, *d_year, *d_time;
296 int month;
297 gchar *iso_date;
298 time_t t;
300 g_return_val_if_fail(str != NULL, 0);
302 g_regex_match(purple_http_re_rfc1123, str, 0, &match_info);
303 if (!g_match_info_matches(match_info)) {
304 g_match_info_free(match_info);
305 return 0;
308 d_date = g_match_info_fetch(match_info, 1);
309 d_month = g_match_info_fetch(match_info, 2);
310 d_year = g_match_info_fetch(match_info, 3);
311 d_time = g_match_info_fetch(match_info, 4);
313 g_match_info_free(match_info);
315 month = 0;
316 while (months[month] != NULL) {
317 if (0 == g_ascii_strcasecmp(d_month, months[month]))
318 break;
319 month++;
321 month++;
323 iso_date = g_strdup_printf("%s-%02d-%sT%s+00:00",
324 d_year, month, d_date, d_time);
326 g_free(d_date);
327 g_free(d_month);
328 g_free(d_year);
329 g_free(d_time);
331 if (month > 12) {
332 purple_debug_warning("http", "Invalid month: %s\n", d_month);
333 g_free(iso_date);
334 return 0;
337 t = purple_str_to_time(iso_date, TRUE, NULL, NULL, NULL);
339 g_free(iso_date);
341 return t;
344 /*** GZip streams *************************************************************/
346 static PurpleHttpGzStream *
347 purple_http_gz_new(gsize max_output, gboolean is_deflate)
349 PurpleHttpGzStream *gzs = g_new0(PurpleHttpGzStream, 1);
350 int windowBits;
352 if (is_deflate)
353 windowBits = -MAX_WBITS;
354 else /* is gzip */
355 windowBits = MAX_WBITS + 32;
357 if (inflateInit2(&gzs->zs, windowBits) != Z_OK) {
358 purple_debug_error("http", "Cannot initialize zlib stream\n");
359 g_free(gzs);
360 return NULL;
363 gzs->max_output = max_output;
365 return gzs;
368 static GString *
369 purple_http_gz_put(PurpleHttpGzStream *gzs, const gchar *buf, gsize len)
371 const gchar *compressed_buff;
372 gsize compressed_len;
373 GString *ret;
374 z_stream *zs;
376 g_return_val_if_fail(gzs != NULL, NULL);
377 g_return_val_if_fail(buf != NULL, NULL);
379 if (gzs->failed)
380 return NULL;
382 zs = &gzs->zs;
384 if (gzs->pending) {
385 g_string_append_len(gzs->pending, buf, len);
386 compressed_buff = gzs->pending->str;
387 compressed_len = gzs->pending->len;
388 } else {
389 compressed_buff = buf;
390 compressed_len = len;
393 zs->next_in = (z_const Bytef*)compressed_buff;
394 zs->avail_in = compressed_len;
396 ret = g_string_new(NULL);
397 while (zs->avail_in > 0) {
398 int gzres;
399 gchar decompressed_buff[PURPLE_HTTP_GZ_BUFF_LEN];
400 gsize decompressed_len;
402 zs->next_out = (Bytef*)decompressed_buff;
403 zs->avail_out = sizeof(decompressed_buff);
404 decompressed_len = zs->avail_out = sizeof(decompressed_buff);
405 gzres = inflate(zs, Z_FULL_FLUSH);
406 decompressed_len -= zs->avail_out;
408 if (gzres == Z_OK || gzres == Z_STREAM_END) {
409 if (decompressed_len == 0)
410 break;
411 if (gzs->decompressed + decompressed_len >=
412 gzs->max_output)
414 purple_debug_warning("http", "Maximum amount of"
415 " decompressed data is reached\n");
416 decompressed_len = gzs->max_output -
417 gzs->decompressed;
418 gzres = Z_STREAM_END;
420 gzs->decompressed += decompressed_len;
421 g_string_append_len(ret, decompressed_buff,
422 decompressed_len);
423 if (gzres == Z_STREAM_END)
424 break;
425 } else {
426 purple_debug_error("http",
427 "Decompression failed (%d): %s\n", gzres,
428 zs->msg);
429 gzs->failed = TRUE;
430 return NULL;
434 if (gzs->pending) {
435 g_string_free(gzs->pending, TRUE);
436 gzs->pending = NULL;
439 if (zs->avail_in > 0) {
440 gzs->pending = g_string_new_len((gchar*)zs->next_in,
441 zs->avail_in);
444 return ret;
447 static void
448 purple_http_gz_free(PurpleHttpGzStream *gzs)
450 if (gzs == NULL)
451 return;
452 inflateEnd(&gzs->zs);
453 if (gzs->pending)
454 g_string_free(gzs->pending, TRUE);
455 g_free(gzs);
458 /*** HTTP Sockets *************************************************************/
460 static gchar *
461 purple_http_socket_hash(const gchar *host, int port, gboolean is_ssl)
463 return g_strdup_printf("%c:%s:%d", (is_ssl ? 'S' : 'R'), host, port);
466 static PurpleHttpSocket *
467 purple_http_socket_connect_new(PurpleConnection *gc, const gchar *host,
468 int port, gboolean is_ssl, PurpleSocketConnectCb cb, gpointer user_data)
470 PurpleHttpSocket *hs = g_new0(PurpleHttpSocket, 1);
472 hs->ps = purple_socket_new(gc);
473 purple_socket_set_data(hs->ps, "hs", hs);
474 purple_socket_set_tls(hs->ps, is_ssl);
475 purple_socket_set_host(hs->ps, host);
476 purple_socket_set_port(hs->ps, port);
477 if (!purple_socket_connect(hs->ps, cb, user_data)) {
478 purple_socket_destroy(hs->ps);
479 g_free(hs);
480 return NULL;
483 if (purple_debug_is_verbose())
484 purple_debug_misc("http", "new socket created: %p\n", hs);
486 return hs;
489 static void
490 purple_http_socket_close_free(PurpleHttpSocket *hs)
492 if (hs == NULL)
493 return;
495 if (purple_debug_is_verbose())
496 purple_debug_misc("http", "destroying socket: %p\n", hs);
498 purple_socket_destroy(hs->ps);
499 g_free(hs);
502 /*** Headers collection *******************************************************/
504 static PurpleHttpHeaders * purple_http_headers_new(void);
505 static void purple_http_headers_free(PurpleHttpHeaders *hdrs);
506 static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key,
507 const gchar *value);
508 static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs);
509 static GList * purple_http_headers_get_all_by_name(
510 PurpleHttpHeaders *hdrs, const gchar *key);
511 static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs,
512 const gchar *key);
513 static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs,
514 const gchar *key, int *dst);
515 static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs,
516 const gchar *key, const gchar *value);
517 static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs);
519 static PurpleHttpHeaders * purple_http_headers_new(void)
521 PurpleHttpHeaders *hdrs = g_new0(PurpleHttpHeaders, 1);
523 hdrs->by_name = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
524 (GDestroyNotify)g_list_free);
526 return hdrs;
529 static void purple_http_headers_free_kvp(PurpleKeyValuePair *kvp)
531 g_free(kvp->key);
532 g_free(kvp->value);
533 g_free(kvp);
536 static void purple_http_headers_free(PurpleHttpHeaders *hdrs)
538 if (hdrs == NULL)
539 return;
541 g_hash_table_destroy(hdrs->by_name);
542 g_list_free_full(hdrs->list,
543 (GDestroyNotify)purple_http_headers_free_kvp);
544 g_free(hdrs);
547 static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key,
548 const gchar *value)
550 PurpleKeyValuePair *kvp;
551 GList *named_values, *new_values;
552 gchar *key_low;
554 g_return_if_fail(hdrs != NULL);
555 g_return_if_fail(key != NULL);
556 g_return_if_fail(value != NULL);
558 kvp = g_new0(PurpleKeyValuePair, 1);
559 kvp->key = g_strdup(key);
560 kvp->value = g_strdup(value);
561 hdrs->list = g_list_append(hdrs->list, kvp);
563 key_low = g_ascii_strdown(key, -1);
564 named_values = g_hash_table_lookup(hdrs->by_name, key_low);
565 new_values = g_list_append(named_values, kvp->value);
566 if (named_values)
567 g_free(key_low);
568 else
569 g_hash_table_insert(hdrs->by_name, key_low, new_values);
572 static void purple_http_headers_remove(PurpleHttpHeaders *hdrs,
573 const gchar *key)
575 GList *it, *curr;
577 g_return_if_fail(hdrs != NULL);
578 g_return_if_fail(key != NULL);
580 if (!g_hash_table_remove(hdrs->by_name, key))
581 return;
583 /* Could be optimized to O(1). */
584 it = g_list_first(hdrs->list);
585 while (it) {
586 PurpleKeyValuePair *kvp = it->data;
587 curr = it;
588 it = g_list_next(it);
589 if (g_ascii_strcasecmp(kvp->key, key) != 0)
590 continue;
592 hdrs->list = g_list_delete_link(hdrs->list, curr);
593 purple_http_headers_free_kvp(kvp);
597 static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs)
599 g_return_val_if_fail(hdrs != NULL, NULL);
601 return hdrs->list;
604 /* return const */
605 static GList * purple_http_headers_get_all_by_name(
606 PurpleHttpHeaders *hdrs, const gchar *key)
608 GList *values;
609 gchar *key_low;
611 g_return_val_if_fail(hdrs != NULL, NULL);
612 g_return_val_if_fail(key != NULL, NULL);
614 key_low = g_ascii_strdown(key, -1);
615 values = g_hash_table_lookup(hdrs->by_name, key_low);
616 g_free(key_low);
618 return values;
621 static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs,
622 const gchar *key)
624 const GList *values = purple_http_headers_get_all_by_name(hdrs, key);
626 if (!values)
627 return NULL;
629 return values->data;
632 static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs,
633 const gchar *key, int *dst)
635 int val;
636 const gchar *str;
638 str = purple_http_headers_get(hdrs, key);
639 if (!str)
640 return FALSE;
642 if (1 != sscanf(str, "%d", &val))
643 return FALSE;
645 *dst = val;
646 return TRUE;
649 static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs,
650 const gchar *key, const gchar *value)
652 const gchar *str;
654 str = purple_http_headers_get(hdrs, key);
655 if (str == NULL || value == NULL)
656 return str == value;
658 return (g_ascii_strcasecmp(str, value) == 0);
661 static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs)
663 const GList *hdr;
665 GString *s = g_string_new("");
667 hdr = purple_http_headers_get_all(hdrs);
668 while (hdr) {
669 PurpleKeyValuePair *kvp = hdr->data;
670 hdr = g_list_next(hdr);
672 g_string_append_printf(s, "%s: %s%s", kvp->key,
673 (gchar*)kvp->value, hdr ? "\n" : "");
676 return g_string_free(s, FALSE);
679 /*** HTTP protocol backend ****************************************************/
681 static void _purple_http_disconnect(PurpleHttpConnection *hc,
682 gboolean is_graceful);
684 static void _purple_http_gen_headers(PurpleHttpConnection *hc);
685 static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd);
686 static void _purple_http_recv(gpointer _hc, gint fd,
687 PurpleInputCondition cond);
688 static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond);
690 /* closes current connection (if exists), estabilishes one and proceeds with
691 * request */
692 static gboolean _purple_http_reconnect(PurpleHttpConnection *hc);
694 static void _purple_http_error(PurpleHttpConnection *hc, const char *format,
695 ...) G_GNUC_PRINTF(2, 3);
697 static void _purple_http_error(PurpleHttpConnection *hc, const char *format,
698 ...)
700 va_list args;
702 va_start(args, format);
703 hc->response->error = g_strdup_vprintf(format, args);
704 va_end(args);
706 if (purple_debug_is_verbose())
707 purple_debug_warning("http", "error: %s\n", hc->response->error);
709 purple_http_conn_cancel(hc);
712 static void _purple_http_gen_headers(PurpleHttpConnection *hc)
714 GString *h;
715 PurpleHttpURL *url;
716 const GList *hdr;
717 PurpleHttpRequest *req;
718 PurpleHttpHeaders *hdrs;
719 gchar *request_url, *tmp_url = NULL;
721 PurpleProxyInfo *proxy;
722 gboolean proxy_http = FALSE;
723 const gchar *proxy_username, *proxy_password;
725 g_return_if_fail(hc != NULL);
727 if (hc->request_header != NULL)
728 return;
730 req = hc->request;
731 url = hc->url;
732 hdrs = req->headers;
733 proxy = purple_proxy_get_setup(hc->gc ?
734 purple_connection_get_account(hc->gc) : NULL);
736 proxy_http = (purple_proxy_info_get_proxy_type(proxy) == PURPLE_PROXY_HTTP ||
737 purple_proxy_info_get_proxy_type(proxy) == PURPLE_PROXY_USE_ENVVAR);
738 /* this is HTTP proxy, but used with tunelling with CONNECT */
739 if (proxy_http && url->port != 80)
740 proxy_http = FALSE;
742 hc->request_header = h = g_string_new("");
743 hc->request_header_written = 0;
744 hc->request_contents_written = 0;
746 if (proxy_http)
747 request_url = tmp_url = purple_http_url_print(url);
748 else
749 request_url = url->path;
751 g_string_append_printf(h, "%s %s HTTP/%s\r\n",
752 req->method ? req->method : "GET",
753 request_url,
754 req->http11 ? "1.1" : "1.0");
756 g_free(tmp_url);
758 if (!purple_http_headers_get(hdrs, "host"))
759 g_string_append_printf(h, "Host: %s\r\n", url->host);
760 if (!purple_http_headers_get(hdrs, "connection")) {
761 g_string_append(h, "Connection: ");
762 g_string_append(h, hc->is_keepalive ?
763 "Keep-Alive\r\n" : "close\r\n");
765 if (!purple_http_headers_get(hdrs, "accept"))
766 g_string_append(h, "Accept: */*\r\n");
767 if (!purple_http_headers_get(hdrs, "accept-encoding"))
768 g_string_append(h, "Accept-Encoding: gzip, deflate\r\n");
770 if (!purple_http_headers_get(hdrs, "content-length") && (
771 req->contents_length > 0 ||
772 purple_http_request_is_method(req, "post")))
774 g_string_append_printf(h, "Content-Length: %u\r\n",
775 req->contents_length);
778 if (proxy_http)
779 g_string_append(h, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */
781 proxy_username = purple_proxy_info_get_username(proxy);
782 if (proxy_http && proxy_username != NULL && proxy_username[0] != '\0') {
783 gchar *proxy_auth, *ntlm_type1, *tmp;
784 int len;
786 proxy_password = purple_proxy_info_get_password(proxy);
787 if (proxy_password == NULL)
788 proxy_password = "";
790 tmp = g_strdup_printf("%s:%s", proxy_username, proxy_password);
791 len = strlen(tmp);
792 proxy_auth = purple_base64_encode((const guchar *)tmp, len);
793 memset(tmp, 0, len);
794 g_free(tmp);
796 ntlm_type1 = purple_ntlm_gen_type1(purple_get_host_name(), "");
798 g_string_append_printf(h, "Proxy-Authorization: Basic %s\r\n",
799 proxy_auth);
800 g_string_append_printf(h, "Proxy-Authorization: NTLM %s\r\n",
801 ntlm_type1);
802 g_string_append(h, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */
804 memset(proxy_auth, 0, strlen(proxy_auth));
805 g_free(proxy_auth);
806 g_free(ntlm_type1);
809 hdr = purple_http_headers_get_all(hdrs);
810 while (hdr) {
811 PurpleKeyValuePair *kvp = hdr->data;
812 hdr = g_list_next(hdr);
814 g_string_append_printf(h, "%s: %s\r\n",
815 kvp->key, (gchar*)kvp->value);
818 if (!purple_http_cookie_jar_is_empty(req->cookie_jar)) {
819 gchar * cookies = purple_http_cookie_jar_gen(req->cookie_jar);
820 g_string_append_printf(h, "Cookie: %s\r\n", cookies);
821 g_free(cookies);
824 g_string_append_printf(h, "\r\n");
826 if (purple_debug_is_unsafe() && purple_debug_is_verbose()) {
827 purple_debug_misc("http", "Generated request headers:\n%s",
828 h->str);
832 static gboolean _purple_http_recv_headers(PurpleHttpConnection *hc,
833 const gchar *buf, int len)
835 gchar *eol, *delim;
837 if (hc->headers_got) {
838 purple_debug_error("http", "Headers already got\n");
839 _purple_http_error(hc, _("Error parsing HTTP"));
840 return FALSE;
843 g_string_append_len(hc->response_buffer, buf, len);
844 if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) {
845 purple_debug_error("http",
846 "Buffer too big when parsing headers\n");
847 _purple_http_error(hc, _("Error parsing HTTP"));
848 return FALSE;
851 while ((eol = strstr(hc->response_buffer->str, "\r\n"))
852 != NULL)
854 gchar *hdrline = hc->response_buffer->str;
855 int hdrline_len = eol - hdrline;
857 hdrline[hdrline_len] = '\0';
859 if (hdrline[0] == '\0') {
860 if (!hc->main_header_got) {
861 if (purple_debug_is_verbose() &&
862 hc->is_keepalive)
864 purple_debug_misc("http", "Got keep-"
865 "alive terminator from previous"
866 " request\n");
867 } else {
868 purple_debug_warning("http", "Got empty"
869 " line at the beginning - this "
870 "may be a HTTP server quirk\n");
872 } else /* hc->main_header_got */ {
873 hc->headers_got = TRUE;
874 if (purple_debug_is_verbose()) {
875 purple_debug_misc("http", "Got headers "
876 "end\n");
879 } else if (!hc->main_header_got) {
880 hc->main_header_got = TRUE;
881 delim = strchr(hdrline, ' ');
882 if (delim == NULL || 1 != sscanf(delim + 1, "%d",
883 &hc->response->code))
885 purple_debug_warning("http",
886 "Invalid response code\n");
887 _purple_http_error(hc, _("Error parsing HTTP"));
888 return FALSE;
890 if (purple_debug_is_verbose())
891 purple_debug_misc("http",
892 "Got main header with code %d\n",
893 hc->response->code);
894 } else {
895 if (purple_debug_is_verbose() &&
896 purple_debug_is_unsafe())
897 purple_debug_misc("http", "Got header: %s\n",
898 hdrline);
899 delim = strchr(hdrline, ':');
900 if (delim == NULL || delim == hdrline) {
901 purple_debug_warning("http",
902 "Bad header delimiter\n");
903 _purple_http_error(hc, _("Error parsing HTTP"));
904 return FALSE;
906 *delim++ = '\0';
907 while (*delim == ' ')
908 delim++;
910 purple_http_headers_add(hc->response->headers, hdrline, delim);
913 g_string_erase(hc->response_buffer, 0, hdrline_len + 2);
914 if (hc->headers_got)
915 break;
917 return TRUE;
920 static gboolean _purple_http_recv_body_data(PurpleHttpConnection *hc,
921 const gchar *buf, int len)
923 GString *decompressed = NULL;
925 if (hc->length_expected >= 0 &&
926 len + hc->length_got > (guint)hc->length_expected)
928 len = hc->length_expected - hc->length_got;
931 hc->length_got += len;
933 if (hc->gz_stream != NULL) {
934 decompressed = purple_http_gz_put(hc->gz_stream, buf, len);
935 if (decompressed == NULL) {
936 _purple_http_error(hc,
937 _("Error while decompressing data"));
938 return FALSE;
940 buf = decompressed->str;
941 len = decompressed->len;
944 g_assert(hc->request->max_length <=
945 PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH);
946 if (hc->length_got_decompressed + len > hc->request->max_length) {
947 purple_debug_warning("http",
948 "Maximum length exceeded, truncating\n");
949 len = hc->request->max_length - hc->length_got_decompressed;
950 hc->length_expected = hc->length_got;
952 hc->length_got_decompressed += len;
954 if (len == 0) {
955 if (decompressed != NULL)
956 g_string_free(decompressed, TRUE);
957 return TRUE;
960 if (hc->request->response_writer != NULL) {
961 gboolean succ;
962 succ = hc->request->response_writer(hc, hc->response, buf,
963 hc->length_got_decompressed, len,
964 hc->request->response_writer_data);
965 if (!succ) {
966 if (decompressed != NULL)
967 g_string_free(decompressed, TRUE);
968 purple_debug_error("http",
969 "Cannot write using callback\n");
970 _purple_http_error(hc,
971 _("Error handling retrieved data"));
972 return FALSE;
974 } else {
975 if (hc->response->contents == NULL)
976 hc->response->contents = g_string_new("");
977 g_string_append_len(hc->response->contents, buf, len);
980 if (decompressed != NULL)
981 g_string_free(decompressed, TRUE);
983 purple_http_conn_notify_progress_watcher(hc);
984 return TRUE;
987 static gboolean _purple_http_recv_body_chunked(PurpleHttpConnection *hc,
988 const gchar *buf, int len)
990 gchar *eol, *line;
991 int line_len;
993 if (hc->chunks_done)
994 return FALSE;
995 if (!hc->response_buffer)
996 hc->response_buffer = g_string_new("");
998 g_string_append_len(hc->response_buffer, buf, len);
999 if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) {
1000 purple_debug_error("http",
1001 "Buffer too big when searching for chunk\n");
1002 _purple_http_error(hc, _("Error parsing HTTP"));
1003 return FALSE;
1006 while (hc->response_buffer->len > 0) {
1007 if (hc->in_chunk) {
1008 int got_now = hc->response_buffer->len;
1009 if (hc->chunk_got + got_now > hc->chunk_length)
1010 got_now = hc->chunk_length - hc->chunk_got;
1011 hc->chunk_got += got_now;
1013 if (!_purple_http_recv_body_data(hc,
1014 hc->response_buffer->str, got_now))
1015 return FALSE;
1017 g_string_erase(hc->response_buffer, 0, got_now);
1018 hc->in_chunk = (hc->chunk_got < hc->chunk_length);
1020 continue;
1023 line = hc->response_buffer->str;
1024 eol = strstr(line, "\r\n");
1025 if (eol == line) {
1026 g_string_erase(hc->response_buffer, 0, 2);
1027 line = hc->response_buffer->str;
1028 eol = strstr(line, "\r\n");
1030 if (eol == NULL) {
1031 /* waiting for more data (unlikely, but possible) */
1032 if (hc->response_buffer->len > 20) {
1033 purple_debug_warning("http", "Chunk length not "
1034 "found (buffer too large)\n");
1035 _purple_http_error(hc, _("Error parsing HTTP"));
1036 return FALSE;
1038 return TRUE;
1040 line_len = eol - line;
1042 if (1 != sscanf(line, "%x", &hc->chunk_length)) {
1043 if (purple_debug_is_unsafe())
1044 purple_debug_warning("http",
1045 "Chunk length not found in [%s]\n",
1046 line);
1047 else
1048 purple_debug_warning("http",
1049 "Chunk length not found\n");
1050 _purple_http_error(hc, _("Error parsing HTTP"));
1051 return FALSE;
1053 hc->chunk_got = 0;
1054 hc->in_chunk = TRUE;
1056 if (purple_debug_is_verbose())
1057 purple_debug_misc("http", "Found chunk of length %d\n", hc->chunk_length);
1059 g_string_erase(hc->response_buffer, 0, line_len + 2);
1061 if (hc->chunk_length == 0) {
1062 hc->chunks_done = TRUE;
1063 hc->in_chunk = FALSE;
1064 return TRUE;
1068 return TRUE;
1071 static gboolean _purple_http_recv_body(PurpleHttpConnection *hc,
1072 const gchar *buf, int len)
1074 if (hc->is_chunked)
1075 return _purple_http_recv_body_chunked(hc, buf, len);
1077 return _purple_http_recv_body_data(hc, buf, len);
1080 static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd)
1082 int len;
1083 gchar buf[4096];
1084 gboolean got_anything;
1086 len = purple_socket_read(hc->socket->ps, (guchar*)buf, sizeof(buf));
1087 got_anything = (len > 0);
1089 if (len < 0 && errno == EAGAIN)
1090 return FALSE;
1092 if (len < 0) {
1093 _purple_http_error(hc, _("Error reading from %s: %s"),
1094 hc->url->host, g_strerror(errno));
1095 return FALSE;
1098 /* EOF */
1099 if (len == 0) {
1100 if (hc->request->max_length == 0) {
1101 /* It's definitely YHttpServer quirk. */
1102 purple_debug_warning("http", "Got EOF, but no data was "
1103 "expected (this may be a server quirk)\n");
1104 hc->length_expected = hc->length_got;
1106 if (hc->length_expected >= 0 &&
1107 hc->length_got < (guint)hc->length_expected)
1109 purple_debug_warning("http", "No more data while reading"
1110 " contents\n");
1111 _purple_http_error(hc, _("Error parsing HTTP"));
1112 return FALSE;
1114 if (hc->headers_got)
1115 hc->length_expected = hc->length_got;
1116 else if (hc->length_got == 0 && hc->socket->use_count > 1) {
1117 purple_debug_info("http", "Keep-alive connection "
1118 "expired (when reading), retrying...\n");
1119 purple_http_conn_retry(hc);
1120 return FALSE;
1121 } else {
1122 const gchar *server = purple_http_headers_get(
1123 hc->response->headers, "Server");
1124 if (server &&
1125 g_ascii_strcasecmp(server, "YHttpServer") == 0)
1127 purple_debug_warning("http", "No more data "
1128 "while parsing headers (YHttpServer "
1129 "quirk)\n");
1130 hc->headers_got = TRUE;
1131 hc->length_expected = hc->length_got = 0;
1132 hc->length_got_decompressed = 0;
1133 } else {
1134 purple_debug_warning("http", "No more data "
1135 "while parsing headers\n");
1136 _purple_http_error(hc, _("Error parsing HTTP"));
1137 return FALSE;
1142 if (!hc->headers_got && len > 0) {
1143 if (!_purple_http_recv_headers(hc, buf, len))
1144 return FALSE;
1145 len = 0;
1146 if (hc->headers_got) {
1147 gboolean is_gzip, is_deflate;
1148 if (!purple_http_headers_get_int(hc->response->headers,
1149 "Content-Length", &hc->length_expected))
1150 hc->length_expected = -1;
1151 hc->is_chunked = (purple_http_headers_match(
1152 hc->response->headers,
1153 "Transfer-Encoding", "chunked"));
1154 is_gzip = purple_http_headers_match(
1155 hc->response->headers, "Content-Encoding",
1156 "gzip");
1157 is_deflate = purple_http_headers_match(
1158 hc->response->headers, "Content-Encoding",
1159 "deflate");
1160 if (is_gzip || is_deflate) {
1161 hc->gz_stream = purple_http_gz_new(
1162 hc->request->max_length + 1,
1163 is_deflate);
1166 if (hc->headers_got && hc->response_buffer &&
1167 hc->response_buffer->len > 0)
1169 int buffer_len = hc->response_buffer->len;
1170 gchar *buffer = g_string_free(hc->response_buffer, FALSE);
1171 hc->response_buffer = NULL;
1172 _purple_http_recv_body(hc, buffer, buffer_len);
1173 g_free(buffer);
1175 if (!hc->headers_got)
1176 return got_anything;
1179 if (len > 0) {
1180 if (!_purple_http_recv_body(hc, buf, len))
1181 return FALSE;
1184 if (hc->is_chunked && hc->chunks_done && hc->length_expected < 0)
1185 hc->length_expected = hc->length_got;
1187 if (hc->length_expected >= 0 &&
1188 hc->length_got >= (guint)hc->length_expected)
1190 const gchar *redirect;
1192 if (hc->is_chunked && !hc->chunks_done) {
1193 if (len == 0) {
1194 _purple_http_error(hc, _("Chunked connection terminated"));
1195 return FALSE;
1197 if (purple_debug_is_verbose()) {
1198 purple_debug_misc("http",
1199 "I need the terminating empty chunk\n");
1201 return TRUE;
1204 if (!hc->headers_got) {
1205 hc->response->code = 0;
1206 purple_debug_warning("http", "No headers got\n");
1207 _purple_http_error(hc, _("Error parsing HTTP"));
1208 return FALSE;
1211 if (purple_debug_is_unsafe() && purple_debug_is_verbose()) {
1212 gchar *hdrs = purple_http_headers_dump(
1213 hc->response->headers);
1214 purple_debug_misc("http", "Got response headers: %s\n",
1215 hdrs);
1216 g_free(hdrs);
1219 purple_http_cookie_jar_parse(hc->request->cookie_jar,
1220 purple_http_headers_get_all_by_name(
1221 hc->response->headers, "Set-Cookie"));
1223 if (purple_debug_is_unsafe() && purple_debug_is_verbose() &&
1224 !purple_http_cookie_jar_is_empty(
1225 hc->request->cookie_jar))
1227 gchar *cookies = purple_http_cookie_jar_dump(
1228 hc->request->cookie_jar);
1229 purple_debug_misc("http", "Cookies: %s\n", cookies);
1230 g_free(cookies);
1233 if (hc->response->code == 407) {
1234 _purple_http_error(hc, _("Invalid proxy credentials"));
1235 return FALSE;
1238 redirect = purple_http_headers_get(hc->response->headers,
1239 "location");
1240 if (redirect && (hc->request->max_redirects == -1 ||
1241 hc->request->max_redirects > hc->redirects_count))
1243 PurpleHttpURL *url = purple_http_url_parse(redirect);
1245 hc->redirects_count++;
1247 if (!url) {
1248 if (purple_debug_is_unsafe())
1249 purple_debug_warning("http",
1250 "Invalid redirect to %s\n",
1251 redirect);
1252 else
1253 purple_debug_warning("http",
1254 "Invalid redirect\n");
1255 _purple_http_error(hc, _("Error parsing HTTP"));
1258 purple_http_url_relative(hc->url, url);
1259 purple_http_url_free(url);
1261 _purple_http_reconnect(hc);
1262 return FALSE;
1265 _purple_http_disconnect(hc, TRUE);
1266 purple_http_connection_terminate(hc);
1267 return FALSE;
1270 return got_anything;
1273 static void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond)
1275 PurpleHttpConnection *hc = _hc;
1277 while (_purple_http_recv_loopbody(hc, fd));
1280 static void _purple_http_send_got_data(PurpleHttpConnection *hc,
1281 gboolean success, gboolean eof, size_t stored)
1283 int estimated_length;
1285 g_return_if_fail(hc != NULL);
1287 if (!success) {
1288 _purple_http_error(hc, _("Error requesting data to write"));
1289 return;
1292 hc->contents_reader_requested = FALSE;
1293 g_string_set_size(hc->contents_reader_buffer, stored);
1294 if (!eof)
1295 return;
1297 estimated_length = hc->request_contents_written + stored;
1299 if (hc->request->contents_length != -1 &&
1300 hc->request->contents_length != estimated_length)
1302 purple_debug_warning("http",
1303 "Invalid amount of data has been written\n");
1305 hc->request->contents_length = estimated_length;
1308 static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond)
1310 PurpleHttpConnection *hc = _hc;
1311 int written, write_len;
1312 const gchar *write_from;
1313 gboolean writing_headers;
1315 /* Waiting for data. This could be written more efficiently, by removing
1316 * (and later, adding) hs->inpa. */
1317 if (hc->contents_reader_requested)
1318 return;
1320 _purple_http_gen_headers(hc);
1322 writing_headers =
1323 (hc->request_header_written < hc->request_header->len);
1324 if (writing_headers) {
1325 write_from = hc->request_header->str +
1326 hc->request_header_written;
1327 write_len = hc->request_header->len -
1328 hc->request_header_written;
1329 } else if (hc->request->contents_reader) {
1330 if (hc->contents_reader_requested)
1331 return; /* waiting for data */
1332 if (!hc->contents_reader_buffer)
1333 hc->contents_reader_buffer = g_string_new("");
1334 if (hc->contents_reader_buffer->len == 0) {
1335 hc->contents_reader_requested = TRUE;
1336 g_string_set_size(hc->contents_reader_buffer,
1337 PURPLE_HTTP_MAX_READ_BUFFER_LEN);
1338 hc->request->contents_reader(hc,
1339 hc->contents_reader_buffer->str,
1340 hc->request_contents_written,
1341 PURPLE_HTTP_MAX_READ_BUFFER_LEN,
1342 hc->request->contents_reader_data,
1343 _purple_http_send_got_data);
1344 return;
1346 write_from = hc->contents_reader_buffer->str;
1347 write_len = hc->contents_reader_buffer->len;
1348 } else {
1349 write_from = hc->request->contents +
1350 hc->request_contents_written;
1351 write_len = hc->request->contents_length -
1352 hc->request_contents_written;
1355 if (write_len == 0) {
1356 purple_debug_warning("http", "Nothing to write\n");
1357 written = 0;
1358 } else {
1359 written = purple_socket_write(hc->socket->ps,
1360 (const guchar*)write_from, write_len);
1363 if (written < 0 && errno == EAGAIN)
1364 return;
1366 if (written < 0) {
1367 if (hc->request_header_written == 0 &&
1368 hc->socket->use_count > 1)
1370 purple_debug_info("http", "Keep-alive connection "
1371 "expired (when writing), retrying...\n");
1372 purple_http_conn_retry(hc);
1373 return;
1376 _purple_http_error(hc, _("Error writing to %s: %s"),
1377 hc->url->host, g_strerror(errno));
1378 return;
1381 if (writing_headers) {
1382 hc->request_header_written += written;
1383 purple_http_conn_notify_progress_watcher(hc);
1384 if (hc->request_header_written < hc->request_header->len)
1385 return;
1386 if (hc->request->contents_length > 0)
1387 return;
1388 } else {
1389 hc->request_contents_written += written;
1390 purple_http_conn_notify_progress_watcher(hc);
1391 if (hc->contents_reader_buffer)
1392 g_string_erase(hc->contents_reader_buffer, 0, written);
1393 if (hc->request->contents_length > 0 &&
1394 hc->request_contents_written <
1395 (guint)hc->request->contents_length)
1397 return;
1401 /* request is completely written, let's read the response */
1402 hc->is_reading = TRUE;
1403 purple_socket_watch(hc->socket->ps, PURPLE_INPUT_READ,
1404 _purple_http_recv, hc);
1407 static void _purple_http_disconnect(PurpleHttpConnection *hc,
1408 gboolean is_graceful)
1410 g_return_if_fail(hc != NULL);
1412 if (hc->request_header)
1413 g_string_free(hc->request_header, TRUE);
1414 hc->request_header = NULL;
1416 if (hc->response_buffer)
1417 g_string_free(hc->response_buffer, TRUE);
1418 hc->response_buffer = NULL;
1420 if (hc->socket_request)
1421 purple_http_keepalive_pool_request_cancel(hc->socket_request);
1422 else {
1423 purple_http_keepalive_pool_release(hc->socket, !is_graceful);
1424 hc->socket = NULL;
1428 static void
1429 _purple_http_connected(PurpleSocket *ps, const gchar *error, gpointer _hc)
1431 PurpleHttpSocket *hs = NULL;
1432 PurpleHttpConnection *hc = _hc;
1434 if (ps != NULL)
1435 hs = purple_socket_get_data(ps, "hs");
1437 hc->socket_request = NULL;
1438 hc->socket = hs;
1440 if (error != NULL) {
1441 _purple_http_error(hc, _("Unable to connect to %s: %s"),
1442 hc->url->host, error);
1443 return;
1446 purple_socket_watch(ps, PURPLE_INPUT_WRITE, _purple_http_send, hc);
1449 static gboolean _purple_http_reconnect(PurpleHttpConnection *hc)
1451 PurpleHttpURL *url;
1452 gboolean is_ssl = FALSE;
1454 g_return_val_if_fail(hc != NULL, FALSE);
1455 g_return_val_if_fail(hc->url != NULL, FALSE);
1457 _purple_http_disconnect(hc, TRUE);
1459 if (purple_debug_is_verbose()) {
1460 if (purple_debug_is_unsafe()) {
1461 gchar *urlp = purple_http_url_print(hc->url);
1462 purple_debug_misc("http", "Connecting to %s...\n", urlp);
1463 g_free(urlp);
1464 } else
1465 purple_debug_misc("http", "Connecting to %s...\n",
1466 hc->url->host);
1469 url = hc->url;
1470 if (g_strcmp0(url->protocol, "") == 0 ||
1471 g_ascii_strcasecmp(url->protocol, "http") == 0)
1473 /* do nothing */
1474 } else if (g_ascii_strcasecmp(url->protocol, "https") == 0) {
1475 is_ssl = TRUE;
1476 } else {
1477 _purple_http_error(hc, _("Unsupported protocol: %s"),
1478 url->protocol);
1479 return FALSE;
1482 if (hc->request->keepalive_pool != NULL) {
1483 hc->socket_request = purple_http_keepalive_pool_request(
1484 hc->request->keepalive_pool, hc->gc, url->host,
1485 url->port, is_ssl, _purple_http_connected, hc);
1486 } else {
1487 hc->socket = purple_http_socket_connect_new(hc->gc, url->host,
1488 url->port, is_ssl, _purple_http_connected, hc);
1491 if (hc->socket_request == NULL && hc->socket == NULL) {
1492 _purple_http_error(hc, _("Unable to connect to %s"), url->host);
1493 return FALSE;
1496 purple_http_headers_free(hc->response->headers);
1497 hc->response->headers = purple_http_headers_new();
1498 hc->response_buffer = g_string_new("");
1499 hc->main_header_got = FALSE;
1500 hc->headers_got = FALSE;
1501 if (hc->response->contents != NULL)
1502 g_string_free(hc->response->contents, TRUE);
1503 hc->response->contents = NULL;
1504 hc->length_got = 0;
1505 hc->length_got_decompressed = 0;
1506 hc->length_expected = -1;
1507 hc->is_chunked = FALSE;
1508 hc->in_chunk = FALSE;
1509 hc->chunks_done = FALSE;
1511 purple_http_conn_notify_progress_watcher(hc);
1513 return TRUE;
1516 /*** Performing HTTP requests *************************************************/
1518 static gboolean purple_http_request_timeout(gpointer _hc)
1520 PurpleHttpConnection *hc = _hc;
1522 purple_debug_warning("http", "Timeout reached for request %p\n", hc);
1524 purple_http_conn_cancel(hc);
1526 return FALSE;
1529 PurpleHttpConnection * purple_http_get(PurpleConnection *gc,
1530 PurpleHttpCallback callback, gpointer user_data, const gchar *url)
1532 PurpleHttpRequest *request;
1533 PurpleHttpConnection *hc;
1535 g_return_val_if_fail(url != NULL, NULL);
1537 request = purple_http_request_new(url);
1538 hc = purple_http_request(gc, request, callback, user_data);
1539 purple_http_request_unref(request);
1541 return hc;
1544 PurpleHttpConnection * purple_http_get_printf(PurpleConnection *gc,
1545 PurpleHttpCallback callback, gpointer user_data,
1546 const gchar *format, ...)
1548 va_list args;
1549 gchar *value;
1550 PurpleHttpConnection *ret;
1552 g_return_val_if_fail(format != NULL, NULL);
1554 va_start(args, format);
1555 value = g_strdup_vprintf(format, args);
1556 va_end(args);
1558 ret = purple_http_get(gc, callback, user_data, value);
1559 g_free(value);
1561 return ret;
1564 PurpleHttpConnection * purple_http_request(PurpleConnection *gc,
1565 PurpleHttpRequest *request, PurpleHttpCallback callback,
1566 gpointer user_data)
1568 PurpleHttpConnection *hc;
1570 g_return_val_if_fail(request != NULL, NULL);
1572 if (request->url == NULL) {
1573 purple_debug_error("http", "Cannot perform new request - "
1574 "URL is not set\n");
1575 return NULL;
1578 if (g_hash_table_lookup(purple_http_cancelling_gc, gc)) {
1579 purple_debug_warning("http", "Cannot perform another HTTP "
1580 "request while cancelling all related with this "
1581 "PurpleConnection\n");
1582 return NULL;
1585 hc = purple_http_connection_new(request, gc);
1586 hc->callback = callback;
1587 hc->user_data = user_data;
1589 hc->url = purple_http_url_parse(request->url);
1591 if (purple_debug_is_unsafe())
1592 purple_debug_misc("http", "Performing new request %p for %s.\n",
1593 hc, request->url);
1594 else
1595 purple_debug_misc("http", "Performing new request %p to %s.\n",
1596 hc, hc->url ? hc->url->host : NULL);
1598 if (!hc->url || hc->url->host == NULL || hc->url->host[0] == '\0') {
1599 purple_debug_error("http", "Invalid URL requested.\n");
1600 purple_http_connection_terminate(hc);
1601 return NULL;
1604 _purple_http_reconnect(hc);
1606 hc->timeout_handle = purple_timeout_add_seconds(request->timeout,
1607 purple_http_request_timeout, hc);
1609 return hc;
1612 /*** HTTP connection API ******************************************************/
1614 static void purple_http_connection_free(PurpleHttpConnection *hc);
1615 static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc);
1617 static PurpleHttpConnection * purple_http_connection_new(
1618 PurpleHttpRequest *request, PurpleConnection *gc)
1620 PurpleHttpConnection *hc = g_new0(PurpleHttpConnection, 1);
1622 g_assert(request != NULL);
1624 hc->request = request;
1625 purple_http_request_ref(request);
1626 hc->response = purple_http_response_new();
1627 hc->is_keepalive = (request->keepalive_pool != NULL);
1629 hc->link_global = purple_http_hc_list =
1630 g_list_prepend(purple_http_hc_list, hc);
1631 g_hash_table_insert(purple_http_hc_by_ptr, hc, hc->link_global);
1632 if (gc) {
1633 GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc);
1634 g_hash_table_steal(purple_http_hc_by_gc, gc);
1635 hc->link_gc = gc_list = g_list_prepend(gc_list, hc);
1636 g_hash_table_insert(purple_http_hc_by_gc, gc, gc_list);
1637 hc->gc = gc;
1640 return hc;
1643 static void purple_http_connection_free(PurpleHttpConnection *hc)
1645 if (hc->timeout_handle)
1646 purple_timeout_remove(hc->timeout_handle);
1647 if (hc->watcher_delayed_handle)
1648 purple_timeout_remove(hc->watcher_delayed_handle);
1650 if (hc->connection_set != NULL)
1651 purple_http_connection_set_remove(hc->connection_set, hc);
1653 purple_http_url_free(hc->url);
1654 purple_http_request_unref(hc->request);
1655 purple_http_response_free(hc->response);
1657 if (hc->contents_reader_buffer)
1658 g_string_free(hc->contents_reader_buffer, TRUE);
1659 purple_http_gz_free(hc->gz_stream);
1661 if (hc->request_header)
1662 g_string_free(hc->request_header, TRUE);
1664 purple_http_hc_list = g_list_delete_link(purple_http_hc_list,
1665 hc->link_global);
1666 g_hash_table_remove(purple_http_hc_by_ptr, hc);
1667 if (hc->gc) {
1668 GList *gc_list, *gc_list_new;
1669 gc_list = g_hash_table_lookup(purple_http_hc_by_gc, hc->gc);
1670 g_assert(gc_list != NULL);
1672 gc_list_new = g_list_delete_link(gc_list, hc->link_gc);
1673 if (gc_list != gc_list_new) {
1674 g_hash_table_steal(purple_http_hc_by_gc, hc->gc);
1675 if (gc_list_new)
1676 g_hash_table_insert(purple_http_hc_by_gc,
1677 hc->gc, gc_list_new);
1681 g_free(hc);
1684 /* call callback and do the cleanup */
1685 static void purple_http_connection_terminate(PurpleHttpConnection *hc)
1687 g_return_if_fail(hc != NULL);
1689 purple_debug_misc("http", "Request %p performed %s.\n", hc,
1690 purple_http_response_is_successful(hc->response) ?
1691 "successfully" : "without success");
1693 if (hc->callback)
1694 hc->callback(hc, hc->response, hc->user_data);
1696 purple_http_connection_free(hc);
1699 void purple_http_conn_cancel(PurpleHttpConnection *http_conn)
1701 if (http_conn == NULL)
1702 return;
1704 if (http_conn->is_cancelling)
1705 return;
1706 http_conn->is_cancelling = TRUE;
1708 if (purple_debug_is_verbose()) {
1709 purple_debug_misc("http", "Cancelling connection %p...\n",
1710 http_conn);
1713 http_conn->response->code = 0;
1714 _purple_http_disconnect(http_conn, FALSE);
1715 purple_http_connection_terminate(http_conn);
1718 static void
1719 purple_http_conn_retry(PurpleHttpConnection *http_conn)
1721 if (http_conn == NULL)
1722 return;
1724 purple_debug_info("http", "Retrying connection %p...\n", http_conn);
1726 http_conn->response->code = 0;
1727 _purple_http_disconnect(http_conn, FALSE);
1728 _purple_http_reconnect(http_conn);
1731 void purple_http_conn_cancel_all(PurpleConnection *gc)
1733 GList *gc_list;
1735 if (purple_debug_is_verbose()) {
1736 purple_debug_misc("http", "Cancelling all running HTTP "
1737 "connections\n");
1740 gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc);
1742 g_hash_table_insert(purple_http_cancelling_gc, gc, GINT_TO_POINTER(TRUE));
1744 while (gc_list) {
1745 PurpleHttpConnection *hc = gc_list->data;
1746 gc_list = g_list_next(gc_list);
1747 purple_http_conn_cancel(hc);
1750 g_hash_table_remove(purple_http_cancelling_gc, gc);
1752 if (NULL != g_hash_table_lookup(purple_http_hc_by_gc, gc))
1753 purple_debug_fatal("http", "Couldn't cancel all connections "
1754 "related to gc=%p (it shouldn't happen)\n", gc);
1757 gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn)
1759 if (http_conn == NULL)
1760 return FALSE;
1761 return (NULL != g_hash_table_lookup(purple_http_hc_by_ptr, http_conn));
1764 PurpleHttpRequest * purple_http_conn_get_request(PurpleHttpConnection *http_conn)
1766 g_return_val_if_fail(http_conn != NULL, NULL);
1768 return http_conn->request;
1771 PurpleHttpCookieJar * purple_http_conn_get_cookie_jar(
1772 PurpleHttpConnection *http_conn)
1774 return purple_http_request_get_cookie_jar(purple_http_conn_get_request(
1775 http_conn));
1778 PurpleConnection * purple_http_conn_get_purple_connection(
1779 PurpleHttpConnection *http_conn)
1781 g_return_val_if_fail(http_conn != NULL, NULL);
1783 return http_conn->gc;
1786 void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn,
1787 PurpleHttpProgressWatcher watcher, gpointer user_data,
1788 gint interval_threshold)
1790 g_return_if_fail(http_conn != NULL);
1792 if (interval_threshold < 0) {
1793 interval_threshold =
1794 PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL;
1797 http_conn->watcher = watcher;
1798 http_conn->watcher_user_data = user_data;
1799 http_conn->watcher_interval_threshold = interval_threshold;
1802 static void purple_http_conn_notify_progress_watcher(
1803 PurpleHttpConnection *hc)
1805 gint64 now;
1806 gboolean reading_state;
1807 int processed, total;
1809 g_return_if_fail(hc != NULL);
1811 if (hc->watcher == NULL)
1812 return;
1814 reading_state = hc->is_reading;
1815 if (reading_state) {
1816 total = hc->length_expected;
1817 processed = hc->length_got;
1818 } else {
1819 total = hc->request->contents_length;
1820 processed = hc->request_contents_written;
1821 if (total == 0)
1822 total = -1;
1824 if (total != -1 && total < processed) {
1825 purple_debug_warning("http", "Processed too much\n");
1826 total = processed;
1829 now = g_get_monotonic_time();
1830 if (hc->watcher_last_call + hc->watcher_interval_threshold
1831 > now && processed != total)
1833 if (hc->watcher_delayed_handle)
1834 return;
1835 hc->watcher_delayed_handle = purple_timeout_add_seconds(
1836 1 + hc->watcher_interval_threshold / 1000000,
1837 purple_http_conn_notify_progress_watcher_timeout, hc);
1838 return;
1841 if (hc->watcher_delayed_handle)
1842 purple_timeout_remove(hc->watcher_delayed_handle);
1843 hc->watcher_delayed_handle = 0;
1845 hc->watcher_last_call = now;
1846 hc->watcher(hc, reading_state, processed, total, hc->watcher_user_data);
1849 static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc)
1851 PurpleHttpConnection *hc = _hc;
1853 purple_http_conn_notify_progress_watcher(hc);
1855 return FALSE;
1858 /*** Cookie jar API ***********************************************************/
1860 static PurpleHttpCookie * purple_http_cookie_new(const gchar *value);
1861 void purple_http_cookie_free(PurpleHttpCookie *cookie);
1863 static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar,
1864 const gchar *name, const gchar *value, time_t expires);
1866 static PurpleHttpCookie * purple_http_cookie_new(const gchar *value)
1868 PurpleHttpCookie *cookie = g_new0(PurpleHttpCookie, 1);
1870 cookie->value = g_strdup(value);
1871 cookie->expires = -1;
1873 return cookie;
1876 void purple_http_cookie_free(PurpleHttpCookie *cookie)
1878 g_free(cookie->value);
1879 g_free(cookie);
1882 void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar);
1884 PurpleHttpCookieJar * purple_http_cookie_jar_new(void)
1886 PurpleHttpCookieJar *cjar = g_new0(PurpleHttpCookieJar, 1);
1888 cjar->ref_count = 1;
1889 cjar->tab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
1890 (GDestroyNotify)purple_http_cookie_free);
1892 return cjar;
1895 void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar)
1897 g_hash_table_destroy(cookie_jar->tab);
1898 g_free(cookie_jar);
1901 void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar)
1903 g_return_if_fail(cookie_jar != NULL);
1905 cookie_jar->ref_count++;
1908 PurpleHttpCookieJar * purple_http_cookie_jar_unref(
1909 PurpleHttpCookieJar *cookie_jar)
1911 if (cookie_jar == NULL)
1912 return NULL;
1914 g_return_val_if_fail(cookie_jar->ref_count > 0, NULL);
1916 cookie_jar->ref_count--;
1917 if (cookie_jar->ref_count > 0)
1918 return cookie_jar;
1920 purple_http_cookie_jar_free(cookie_jar);
1921 return NULL;
1924 static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar,
1925 GList *values)
1927 values = g_list_first(values);
1928 while (values) {
1929 const gchar *cookie = values->data;
1930 const gchar *eqsign, *semicolon;
1931 gchar *name, *value;
1932 time_t expires = -1;
1933 values = g_list_next(values);
1935 eqsign = strchr(cookie, '=');
1936 semicolon = strchr(cookie, ';');
1938 if (eqsign == NULL || eqsign == cookie ||
1939 (semicolon != NULL && semicolon < eqsign))
1941 if (purple_debug_is_unsafe())
1942 purple_debug_warning("http",
1943 "Invalid cookie: [%s]\n", cookie);
1944 else
1945 purple_debug_warning("http", "Invalid cookie.");
1946 continue;
1949 name = g_strndup(cookie, eqsign - cookie);
1950 eqsign++;
1951 if (semicolon != NULL)
1952 value = g_strndup(eqsign, semicolon - eqsign);
1953 else
1954 value = g_strdup(eqsign);
1956 if (semicolon != NULL) {
1957 GMatchInfo *match_info;
1958 GRegex *re_expires = g_regex_new( /* XXX: make it static */
1959 "expires=([a-z0-9, :]+)",
1960 G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
1961 G_REGEX_MATCH_NOTEMPTY, NULL);
1963 g_regex_match(re_expires, semicolon, 0, &match_info);
1964 if (g_match_info_matches(match_info)) {
1965 gchar *expire_date =
1966 g_match_info_fetch(match_info, 1);
1967 expires = purple_http_rfc1123_to_time(
1968 expire_date);
1969 g_free(expire_date);
1971 g_match_info_free(match_info);
1973 g_regex_unref(re_expires);
1976 purple_http_cookie_jar_set_ext(cookie_jar, name, value, expires);
1978 g_free(name);
1979 g_free(value);
1983 static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar)
1985 GHashTableIter it;
1986 gchar *key;
1987 PurpleHttpCookie *cookie;
1988 GString *str;
1989 time_t now = time(NULL);
1991 g_return_val_if_fail(cookie_jar != NULL, NULL);
1993 str = g_string_new("");
1995 g_hash_table_iter_init(&it, cookie_jar->tab);
1996 while (g_hash_table_iter_next(&it, (gpointer*)&key,
1997 (gpointer*)&cookie))
1999 if (cookie->expires != -1 && cookie->expires != 0 && cookie->expires <= now)
2000 continue;
2001 g_string_append_printf(str, "%s=%s; ", key, cookie->value);
2004 if (str->len > 0)
2005 g_string_truncate(str, str->len - 2);
2006 return g_string_free(str, FALSE);
2009 void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar,
2010 const gchar *name, const gchar *value)
2012 gchar *escaped_name = g_strdup(purple_url_encode(name));
2013 gchar *escaped_value = NULL;
2015 if (value) {
2016 escaped_value = g_strdup(purple_url_encode(value));
2019 purple_http_cookie_jar_set_ext(cookie_jar, escaped_name, escaped_value, -1);
2021 g_free(escaped_name);
2022 g_free(escaped_value);
2025 static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar,
2026 const gchar *name, const gchar *value, time_t expires)
2028 g_return_if_fail(cookie_jar != NULL);
2029 g_return_if_fail(name != NULL);
2031 if (expires != -1 && expires != 0 && time(NULL) >= expires)
2032 value = NULL;
2034 if (value != NULL) {
2035 PurpleHttpCookie *cookie = purple_http_cookie_new(value);
2036 cookie->expires = expires;
2037 g_hash_table_insert(cookie_jar->tab, g_strdup(name), cookie);
2038 } else
2039 g_hash_table_remove(cookie_jar->tab, name);
2042 gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar,
2043 const gchar *name)
2045 PurpleHttpCookie *cookie;
2047 g_return_val_if_fail(cookie_jar != NULL, NULL);
2048 g_return_val_if_fail(name != NULL, NULL);
2050 cookie = g_hash_table_lookup(cookie_jar->tab, name);
2051 if (!cookie)
2052 return NULL;
2054 return g_strdup(purple_url_decode(cookie->value));
2057 gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar)
2059 GHashTableIter it;
2060 gchar *key;
2061 PurpleHttpCookie *cookie;
2062 GString *str = g_string_new("");
2064 g_hash_table_iter_init(&it, cjar->tab);
2065 while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&cookie))
2066 g_string_append_printf(str, "%s: %s (expires: %" G_GINT64_FORMAT
2067 ")\n", key, cookie->value, (gint64)cookie->expires);
2069 if (str->len > 0)
2070 g_string_truncate(str, str->len - 1);
2071 return g_string_free(str, FALSE);
2074 gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar)
2076 g_return_val_if_fail(cookie_jar != NULL, TRUE);
2078 return g_hash_table_size(cookie_jar->tab) == 0;
2081 /*** HTTP Keep-Alive pool API *************************************************/
2083 static void
2084 purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost *host);
2086 static void
2087 purple_http_keepalive_host_free(gpointer _host)
2089 PurpleHttpKeepaliveHost *host = _host;
2091 g_free(host->host);
2093 g_slist_free_full(host->queue,
2094 (GDestroyNotify)purple_http_keepalive_pool_request_cancel);
2095 g_slist_free_full(host->sockets,
2096 (GDestroyNotify)purple_http_socket_close_free);
2098 if (host->process_queue_timeout > 0) {
2099 purple_timeout_remove(host->process_queue_timeout);
2100 host->process_queue_timeout = 0;
2104 g_free(host);
2107 PurpleHttpKeepalivePool *
2108 purple_http_keepalive_pool_new(void)
2110 PurpleHttpKeepalivePool *pool = g_new0(PurpleHttpKeepalivePool, 1);
2112 pool->ref_count = 1;
2113 pool->by_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
2114 purple_http_keepalive_host_free);
2116 return pool;
2119 static void
2120 purple_http_keepalive_pool_free(PurpleHttpKeepalivePool *pool)
2122 g_return_if_fail(pool != NULL);
2124 if (pool->is_destroying)
2125 return;
2126 pool->is_destroying = TRUE;
2127 g_hash_table_destroy(pool->by_hash);
2128 g_free(pool);
2131 void
2132 purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool *pool)
2134 g_return_if_fail(pool != NULL);
2136 pool->ref_count++;
2139 PurpleHttpKeepalivePool *
2140 purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool *pool)
2142 if (pool == NULL)
2143 return NULL;
2145 g_return_val_if_fail(pool->ref_count > 0, NULL);
2147 pool->ref_count--;
2148 if (pool->ref_count > 0)
2149 return pool;
2151 purple_http_keepalive_pool_free(pool);
2152 return NULL;
2155 static PurpleHttpKeepaliveRequest *
2156 purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
2157 PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
2158 PurpleSocketConnectCb cb, gpointer user_data)
2160 PurpleHttpKeepaliveRequest *req;
2161 PurpleHttpKeepaliveHost *kahost;
2162 gchar *hash;
2164 g_return_val_if_fail(pool != NULL, NULL);
2165 g_return_val_if_fail(host != NULL, NULL);
2167 if (pool->is_destroying) {
2168 purple_debug_error("http", "pool is destroying\n");
2169 return NULL;
2172 hash = purple_http_socket_hash(host, port, is_ssl);
2173 kahost = g_hash_table_lookup(pool->by_hash, hash);
2175 if (kahost == NULL) {
2176 kahost = g_new0(PurpleHttpKeepaliveHost, 1);
2177 kahost->pool = pool;
2178 kahost->host = g_strdup(host);
2179 kahost->port = port;
2180 kahost->is_ssl = is_ssl;
2182 g_hash_table_insert(pool->by_hash, g_strdup(hash), kahost);
2185 g_free(hash);
2187 req = g_new0(PurpleHttpKeepaliveRequest, 1);
2188 req->gc = gc;
2189 req->cb = cb;
2190 req->user_data = user_data;
2191 req->host = kahost;
2193 kahost->queue = g_slist_append(kahost->queue, req);
2195 purple_http_keepalive_host_process_queue(kahost);
2197 return req;
2200 static void
2201 _purple_http_keepalive_socket_connected(PurpleSocket *ps,
2202 const gchar *error, gpointer _req)
2204 PurpleHttpSocket *hs = NULL;
2205 PurpleHttpKeepaliveRequest *req = _req;
2207 if (ps != NULL)
2208 hs = purple_socket_get_data(ps, "hs");
2210 if (hs != NULL)
2211 hs->use_count++;
2213 req->cb(ps, error, req->user_data);
2214 g_free(req);
2217 static gboolean
2218 _purple_http_keepalive_host_process_queue_cb(gpointer _host)
2220 PurpleHttpKeepaliveRequest *req;
2221 PurpleHttpKeepaliveHost *host = _host;
2222 PurpleHttpSocket *hs = NULL;
2223 GSList *it;
2224 guint sockets_count;
2226 g_return_val_if_fail(host != NULL, FALSE);
2228 host->process_queue_timeout = 0;
2230 if (host->queue == NULL)
2231 return FALSE;
2233 sockets_count = 0;
2234 it = host->sockets;
2235 while (it != NULL) {
2236 PurpleHttpSocket *hs_current = it->data;
2238 sockets_count++;
2240 if (!hs_current->is_busy) {
2241 hs = hs_current;
2242 break;
2245 it = g_slist_next(it);
2248 /* There are no free sockets and we cannot create another one. */
2249 if (hs == NULL && sockets_count >= host->pool->limit_per_host &&
2250 host->pool->limit_per_host > 0)
2252 return FALSE;
2255 req = host->queue->data;
2256 host->queue = g_slist_remove(host->queue, req);
2258 if (hs != NULL) {
2259 if (purple_debug_is_verbose()) {
2260 purple_debug_misc("http", "locking a (previously used) "
2261 "socket: %p\n", hs);
2264 hs->is_busy = TRUE;
2265 hs->use_count++;
2267 purple_http_keepalive_host_process_queue(host);
2269 req->cb(hs->ps, NULL, req->user_data);
2270 g_free(req);
2272 return FALSE;
2275 hs = purple_http_socket_connect_new(req->gc, req->host->host,
2276 req->host->port, req->host->is_ssl,
2277 _purple_http_keepalive_socket_connected, req);
2278 if (hs == NULL) {
2279 purple_debug_error("http", "failed creating new socket");
2280 return FALSE;
2283 req->hs = hs;
2284 hs->is_busy = TRUE;
2285 hs->host = host;
2287 if (purple_debug_is_verbose())
2288 purple_debug_misc("http", "locking a (new) socket: %p\n", hs);
2290 host->sockets = g_slist_append(host->sockets, hs);
2292 return FALSE;
2295 static void
2296 purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost *host)
2298 g_return_if_fail(host != NULL);
2300 if (host->process_queue_timeout > 0)
2301 return;
2303 host->process_queue_timeout = purple_timeout_add(0,
2304 _purple_http_keepalive_host_process_queue_cb, host);
2307 static void
2308 purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req)
2310 if (req == NULL)
2311 return;
2313 if (req->host != NULL)
2314 req->host->queue = g_slist_remove(req->host->queue, req);
2316 if (req->hs != NULL) {
2317 if (G_LIKELY(req->host)) {
2318 req->host->sockets = g_slist_remove(req->host->sockets,
2319 req->hs);
2321 purple_http_socket_close_free(req->hs);
2322 /* req should already be free'd here */
2323 } else {
2324 req->cb(NULL, _("Cancelled"), req->user_data);
2325 g_free(req);
2329 static void
2330 purple_http_keepalive_pool_release(PurpleHttpSocket *hs, gboolean invalidate)
2332 PurpleHttpKeepaliveHost *host;
2334 if (hs == NULL)
2335 return;
2337 if (purple_debug_is_verbose())
2338 purple_debug_misc("http", "releasing a socket: %p\n", hs);
2340 purple_socket_watch(hs->ps, 0, NULL, NULL);
2341 hs->is_busy = FALSE;
2342 host = hs->host;
2344 if (host == NULL) {
2345 purple_http_socket_close_free(hs);
2346 return;
2349 if (invalidate) {
2350 host->sockets = g_slist_remove(host->sockets, hs);
2351 purple_http_socket_close_free(hs);
2354 purple_http_keepalive_host_process_queue(host);
2357 void
2358 purple_http_keepalive_pool_set_limit_per_host(PurpleHttpKeepalivePool *pool,
2359 guint limit)
2361 g_return_if_fail(pool != NULL);
2363 pool->limit_per_host = limit;
2366 guint
2367 purple_http_keepalive_pool_get_limit_per_host(PurpleHttpKeepalivePool *pool)
2369 g_return_val_if_fail(pool != NULL, 0);
2371 return pool->limit_per_host;
2374 /*** HTTP connection set API **************************************************/
2376 PurpleHttpConnectionSet *
2377 purple_http_connection_set_new(void)
2379 PurpleHttpConnectionSet *set;
2381 set = g_new0(PurpleHttpConnectionSet, 1);
2382 set->connections = g_hash_table_new(g_direct_hash, g_direct_equal);
2384 return set;
2387 void
2388 purple_http_connection_set_destroy(PurpleHttpConnectionSet *set)
2390 if (set == NULL)
2391 return;
2393 set->is_destroying = TRUE;
2395 while (TRUE) {
2396 GHashTableIter iter;
2397 PurpleHttpConnection *http_conn;
2399 g_hash_table_iter_init(&iter, set->connections);
2400 if (!g_hash_table_iter_next(&iter, (gpointer*)&http_conn, NULL))
2401 break;
2403 purple_http_conn_cancel(http_conn);
2406 g_hash_table_destroy(set->connections);
2407 g_free(set);
2410 void
2411 purple_http_connection_set_add(PurpleHttpConnectionSet *set,
2412 PurpleHttpConnection *http_conn)
2414 if (set->is_destroying)
2415 return;
2416 if (http_conn->connection_set == set)
2417 return;
2418 if (http_conn->connection_set != NULL) {
2419 purple_http_connection_set_remove(http_conn->connection_set,
2420 http_conn);
2422 g_hash_table_insert(set->connections, http_conn, (gpointer)TRUE);
2423 http_conn->connection_set = set;
2426 static void
2427 purple_http_connection_set_remove(PurpleHttpConnectionSet *set,
2428 PurpleHttpConnection *http_conn)
2430 g_hash_table_remove(set->connections, http_conn);
2431 if (http_conn->connection_set == set)
2432 http_conn->connection_set = NULL;
2435 /*** Request API **************************************************************/
2437 static void purple_http_request_free(PurpleHttpRequest *request);
2439 PurpleHttpRequest * purple_http_request_new(const gchar *url)
2441 PurpleHttpRequest *request;
2443 request = g_new0(PurpleHttpRequest, 1);
2445 request->ref_count = 1;
2446 request->url = g_strdup(url);
2447 request->headers = purple_http_headers_new();
2448 request->cookie_jar = purple_http_cookie_jar_new();
2449 request->keepalive_pool = purple_http_keepalive_pool_new();
2451 request->timeout = PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT;
2452 request->max_redirects = PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS;
2453 request->http11 = TRUE;
2454 request->max_length = PURPLE_HTTP_REQUEST_DEFAULT_MAX_LENGTH;
2456 return request;
2459 static void purple_http_request_free(PurpleHttpRequest *request)
2461 purple_http_headers_free(request->headers);
2462 purple_http_cookie_jar_unref(request->cookie_jar);
2463 purple_http_keepalive_pool_unref(request->keepalive_pool);
2464 g_free(request->method);
2465 g_free(request->contents);
2466 g_free(request->url);
2467 g_free(request);
2470 void purple_http_request_ref(PurpleHttpRequest *request)
2472 g_return_if_fail(request != NULL);
2474 request->ref_count++;
2477 PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request)
2479 if (request == NULL)
2480 return NULL;
2482 g_return_val_if_fail(request->ref_count > 0, NULL);
2484 request->ref_count--;
2485 if (request->ref_count > 0)
2486 return request;
2488 purple_http_request_free(request);
2489 return NULL;
2492 void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url)
2494 g_return_if_fail(request != NULL);
2495 g_return_if_fail(url != NULL);
2497 g_free(request->url);
2498 request->url = g_strdup(url);
2501 void purple_http_request_set_url_printf(PurpleHttpRequest *request,
2502 const gchar *format, ...)
2504 va_list args;
2505 gchar *value;
2507 g_return_if_fail(request != NULL);
2508 g_return_if_fail(format != NULL);
2510 va_start(args, format);
2511 value = g_strdup_vprintf(format, args);
2512 va_end(args);
2514 purple_http_request_set_url(request, value);
2515 g_free(value);
2518 const gchar * purple_http_request_get_url(PurpleHttpRequest *request)
2520 g_return_val_if_fail(request != NULL, NULL);
2522 return request->url;
2525 void purple_http_request_set_method(PurpleHttpRequest *request, const gchar *method)
2527 g_return_if_fail(request != NULL);
2529 g_free(request->method);
2530 request->method = g_strdup(method);
2533 const gchar * purple_http_request_get_method(PurpleHttpRequest *request)
2535 g_return_val_if_fail(request != NULL, NULL);
2537 return request->method;
2540 static gboolean purple_http_request_is_method(PurpleHttpRequest *request,
2541 const gchar *method)
2543 const gchar *rmethod;
2545 g_return_val_if_fail(request != NULL, FALSE);
2546 g_return_val_if_fail(method != NULL, FALSE);
2548 rmethod = purple_http_request_get_method(request);
2549 if (rmethod == NULL)
2550 return (g_ascii_strcasecmp(method, "get") == 0);
2551 return (g_ascii_strcasecmp(method, rmethod) == 0);
2554 void
2555 purple_http_request_set_keepalive_pool(PurpleHttpRequest *request,
2556 PurpleHttpKeepalivePool *pool)
2558 g_return_if_fail(request != NULL);
2560 if (pool != NULL)
2561 purple_http_keepalive_pool_ref(pool);
2563 if (request->keepalive_pool != NULL) {
2564 purple_http_keepalive_pool_unref(request->keepalive_pool);
2565 request->keepalive_pool = NULL;
2568 if (pool != NULL)
2569 request->keepalive_pool = pool;
2572 PurpleHttpKeepalivePool *
2573 purple_http_request_get_keepalive_pool(PurpleHttpRequest *request)
2575 g_return_val_if_fail(request != NULL, FALSE);
2577 return request->keepalive_pool;
2580 void purple_http_request_set_contents(PurpleHttpRequest *request,
2581 const gchar *contents, int length)
2583 g_return_if_fail(request != NULL);
2584 g_return_if_fail(length >= -1);
2586 request->contents_reader = NULL;
2587 request->contents_reader_data = NULL;
2589 g_free(request->contents);
2590 if (contents == NULL || length == 0) {
2591 request->contents = NULL;
2592 request->contents_length = 0;
2593 return;
2596 if (length == -1)
2597 length = strlen(contents);
2598 request->contents = g_memdup(contents, length);
2599 request->contents_length = length;
2602 void purple_http_request_set_contents_reader(PurpleHttpRequest *request,
2603 PurpleHttpContentReader reader, int contents_length, gpointer user_data)
2605 g_return_if_fail(request != NULL);
2606 g_return_if_fail(reader != NULL);
2607 g_return_if_fail(contents_length >= -1);
2609 g_free(request->contents);
2610 request->contents = NULL;
2611 request->contents_length = contents_length;
2612 request->contents_reader = reader;
2613 request->contents_reader_data = user_data;
2616 void purple_http_request_set_response_writer(PurpleHttpRequest *request,
2617 PurpleHttpContentWriter writer, gpointer user_data)
2619 g_return_if_fail(request != NULL);
2621 if (writer == NULL)
2622 user_data = NULL;
2623 request->response_writer = writer;
2624 request->response_writer_data = user_data;
2627 void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout)
2629 g_return_if_fail(request != NULL);
2631 if (timeout < -1)
2632 timeout = -1;
2634 request->timeout = timeout;
2637 int purple_http_request_get_timeout(PurpleHttpRequest *request)
2639 g_return_val_if_fail(request != NULL, -1);
2641 return request->timeout;
2644 void purple_http_request_set_max_redirects(PurpleHttpRequest *request,
2645 int max_redirects)
2647 g_return_if_fail(request != NULL);
2649 if (max_redirects < -1)
2650 max_redirects = -1;
2652 request->max_redirects = max_redirects;
2655 int purple_http_request_get_max_redirects(PurpleHttpRequest *request)
2657 g_return_val_if_fail(request != NULL, -1);
2659 return request->max_redirects;
2662 void purple_http_request_set_cookie_jar(PurpleHttpRequest *request,
2663 PurpleHttpCookieJar *cookie_jar)
2665 g_return_if_fail(request != NULL);
2666 g_return_if_fail(cookie_jar != NULL);
2668 purple_http_cookie_jar_ref(cookie_jar);
2669 purple_http_cookie_jar_unref(request->cookie_jar);
2670 request->cookie_jar = cookie_jar;
2673 PurpleHttpCookieJar * purple_http_request_get_cookie_jar(
2674 PurpleHttpRequest *request)
2676 g_return_val_if_fail(request != NULL, NULL);
2678 return request->cookie_jar;
2681 void purple_http_request_set_http11(PurpleHttpRequest *request, gboolean http11)
2683 g_return_if_fail(request != NULL);
2685 request->http11 = http11;
2688 gboolean purple_http_request_is_http11(PurpleHttpRequest *request)
2690 g_return_val_if_fail(request != NULL, FALSE);
2692 return request->http11;
2695 void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len)
2697 g_return_if_fail(request != NULL);
2699 if (max_len < 0 || max_len > PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH)
2700 max_len = PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH;
2702 request->max_length = max_len;
2705 int purple_http_request_get_max_len(PurpleHttpRequest *request)
2707 g_return_val_if_fail(request != NULL, -1);
2709 return request->max_length;
2712 void purple_http_request_header_set(PurpleHttpRequest *request,
2713 const gchar *key, const gchar *value)
2715 g_return_if_fail(request != NULL);
2716 g_return_if_fail(key != NULL);
2718 purple_http_headers_remove(request->headers, key);
2719 if (value)
2720 purple_http_headers_add(request->headers, key, value);
2723 void purple_http_request_header_set_printf(PurpleHttpRequest *request,
2724 const gchar *key, const gchar *format, ...)
2726 va_list args;
2727 gchar *value;
2729 g_return_if_fail(request != NULL);
2730 g_return_if_fail(key != NULL);
2731 g_return_if_fail(format != NULL);
2733 va_start(args, format);
2734 value = g_strdup_vprintf(format, args);
2735 va_end(args);
2737 purple_http_request_header_set(request, key, value);
2738 g_free(value);
2741 void purple_http_request_header_add(PurpleHttpRequest *request,
2742 const gchar *key, const gchar *value)
2744 g_return_if_fail(request != NULL);
2745 g_return_if_fail(key != NULL);
2747 purple_http_headers_add(request->headers, key, value);
2750 /*** HTTP response API ********************************************************/
2752 static PurpleHttpResponse * purple_http_response_new(void)
2754 PurpleHttpResponse *response = g_new0(PurpleHttpResponse, 1);
2756 return response;
2759 static void purple_http_response_free(PurpleHttpResponse *response)
2761 if (response->contents != NULL)
2762 g_string_free(response->contents, TRUE);
2763 g_free(response->error);
2764 purple_http_headers_free(response->headers);
2765 g_free(response);
2768 gboolean purple_http_response_is_successful(PurpleHttpResponse *response)
2770 int code;
2772 g_return_val_if_fail(response != NULL, FALSE);
2774 code = response->code;
2776 if (code <= 0)
2777 return FALSE;
2779 /* TODO: HTTP/1.1 100 Continue */
2781 if (code / 100 == 2)
2782 return TRUE;
2784 return FALSE;
2787 int purple_http_response_get_code(PurpleHttpResponse *response)
2789 g_return_val_if_fail(response != NULL, 0);
2791 return response->code;
2794 const gchar * purple_http_response_get_error(PurpleHttpResponse *response)
2796 g_return_val_if_fail(response != NULL, NULL);
2798 if (response->error != NULL)
2799 return response->error;
2801 if (!purple_http_response_is_successful(response)) {
2802 static gchar errmsg[200];
2803 if (response->code <= 0) {
2804 g_snprintf(errmsg, sizeof(errmsg),
2805 _("Unknown HTTP error"));
2806 } else {
2807 g_snprintf(errmsg, sizeof(errmsg),
2808 _("Invalid HTTP response code (%d)"),
2809 response->code);
2811 return errmsg;
2814 return NULL;
2817 gsize purple_http_response_get_data_len(PurpleHttpResponse *response)
2819 g_return_val_if_fail(response != NULL, 0);
2821 if (response->contents == NULL)
2822 return 0;
2824 return response->contents->len;
2827 const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len)
2829 const gchar *ret = "";
2831 g_return_val_if_fail(response != NULL, "");
2833 if (response->contents != NULL) {
2834 ret = response->contents->str;
2835 if (len)
2836 *len = response->contents->len;
2837 } else {
2838 if (len)
2839 *len = 0;
2842 return ret;
2845 const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response)
2847 g_return_val_if_fail(response != NULL, NULL);
2849 return purple_http_headers_get_all(response->headers);
2852 const GList * purple_http_response_get_headers_by_name(
2853 PurpleHttpResponse *response, const gchar *name)
2855 g_return_val_if_fail(response != NULL, NULL);
2856 g_return_val_if_fail(name != NULL, NULL);
2858 return purple_http_headers_get_all_by_name(response->headers, name);
2861 const gchar * purple_http_response_get_header(PurpleHttpResponse *response,
2862 const gchar *name)
2864 g_return_val_if_fail(response != NULL, NULL);
2865 g_return_val_if_fail(name != NULL, NULL);
2867 return purple_http_headers_get(response->headers, name);
2870 /*** URL functions ************************************************************/
2872 PurpleHttpURL *
2873 purple_http_url_parse(const char *raw_url)
2875 PurpleHttpURL *url;
2876 GMatchInfo *match_info;
2878 gchar *host_full, *tmp;
2880 g_return_val_if_fail(raw_url != NULL, NULL);
2882 if (!g_regex_match(purple_http_re_url, raw_url, 0, &match_info)) {
2883 if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
2884 purple_debug_warning("http",
2885 "Invalid URL provided: %s\n",
2886 raw_url);
2888 return NULL;
2891 url = g_new0(PurpleHttpURL, 1);
2893 url->protocol = g_match_info_fetch(match_info, 1);
2894 host_full = g_match_info_fetch(match_info, 2);
2895 url->path = g_match_info_fetch(match_info, 3);
2896 url->fragment = g_match_info_fetch(match_info, 4);
2897 g_match_info_free(match_info);
2899 if (g_strcmp0(url->protocol, "") == 0) {
2900 g_free(url->protocol);
2901 url->protocol = NULL;
2902 } else if (url->protocol != NULL) {
2903 tmp = url->protocol;
2904 url->protocol = g_ascii_strdown(url->protocol, -1);
2905 g_free(tmp);
2907 if (host_full[0] == '\0') {
2908 g_free(host_full);
2909 host_full = NULL;
2911 if (url->path[0] == '\0') {
2912 g_free(url->path);
2913 url->path = NULL;
2915 if ((url->protocol == NULL) != (host_full == NULL))
2916 purple_debug_warning("http", "Protocol or host not present "
2917 "(unlikely case)\n");
2919 if (host_full) {
2920 gchar *port_str;
2922 if (!g_regex_match(purple_http_re_url_host, host_full, 0,
2923 &match_info))
2925 if (purple_debug_is_verbose() &&
2926 purple_debug_is_unsafe())
2928 purple_debug_warning("http",
2929 "Invalid host provided for URL: %s\n",
2930 raw_url);
2933 g_free(host_full);
2934 purple_http_url_free(url);
2935 return NULL;
2938 url->username = g_match_info_fetch(match_info, 1);
2939 url->password = g_match_info_fetch(match_info, 2);
2940 url->host = g_match_info_fetch(match_info, 3);
2941 port_str = g_match_info_fetch(match_info, 4);
2943 if (port_str && port_str[0])
2944 url->port = atoi(port_str);
2946 if (url->username[0] == '\0') {
2947 g_free(url->username);
2948 url->username = NULL;
2950 if (url->password[0] == '\0') {
2951 g_free(url->password);
2952 url->password = NULL;
2954 if (g_strcmp0(url->host, "") == 0) {
2955 g_free(url->host);
2956 url->host = NULL;
2957 } else if (url->host != NULL) {
2958 tmp = url->host;
2959 url->host = g_ascii_strdown(url->host, -1);
2960 g_free(tmp);
2963 g_free(port_str);
2964 g_match_info_free(match_info);
2966 g_free(host_full);
2967 host_full = NULL;
2970 if (url->host != NULL) {
2971 if (url->protocol == NULL)
2972 url->protocol = g_strdup("http");
2973 if (url->port == 0 && 0 == strcmp(url->protocol, "http"))
2974 url->port = 80;
2975 if (url->port == 0 && 0 == strcmp(url->protocol, "https"))
2976 url->port = 443;
2977 if (url->path == NULL)
2978 url->path = g_strdup("/");
2979 if (url->path[0] != '/')
2980 purple_debug_warning("http",
2981 "URL path doesn't start with slash\n");
2984 return url;
2987 void
2988 purple_http_url_free(PurpleHttpURL *parsed_url)
2990 if (parsed_url == NULL)
2991 return;
2993 g_free(parsed_url->protocol);
2994 g_free(parsed_url->username);
2995 g_free(parsed_url->password);
2996 g_free(parsed_url->host);
2997 g_free(parsed_url->path);
2998 g_free(parsed_url->fragment);
2999 g_free(parsed_url);
3002 void
3003 purple_http_url_relative(PurpleHttpURL *base_url, PurpleHttpURL *relative_url)
3005 g_return_if_fail(base_url != NULL);
3006 g_return_if_fail(relative_url != NULL);
3008 if (relative_url->host) {
3009 g_free(base_url->protocol);
3010 base_url->protocol = g_strdup(relative_url->protocol);
3011 g_free(base_url->username);
3012 base_url->username = g_strdup(relative_url->username);
3013 g_free(base_url->password);
3014 base_url->password = g_strdup(relative_url->password);
3015 g_free(base_url->host);
3016 base_url->host = g_strdup(relative_url->host);
3017 base_url->port = relative_url->port;
3019 g_free(base_url->path);
3020 base_url->path = NULL;
3023 if (relative_url->path) {
3024 if (relative_url->path[0] == '/' ||
3025 base_url->path == NULL)
3027 g_free(base_url->path);
3028 base_url->path = g_strdup(relative_url->path);
3029 } else {
3030 gchar *last_slash = strrchr(base_url->path, '/');
3031 gchar *tmp;
3032 if (last_slash == NULL)
3033 base_url->path[0] = '\0';
3034 else
3035 last_slash[1] = '\0';
3036 tmp = base_url->path;
3037 base_url->path = g_strconcat(base_url->path,
3038 relative_url->path, NULL);
3039 g_free(tmp);
3043 g_free(base_url->fragment);
3044 base_url->fragment = g_strdup(relative_url->fragment);
3047 gchar *
3048 purple_http_url_print(PurpleHttpURL *parsed_url)
3050 GString *url = g_string_new("");
3051 gboolean before_host_printed = FALSE, host_printed = FALSE;
3052 gboolean port_is_default = FALSE;
3054 if (parsed_url->protocol) {
3055 g_string_append_printf(url, "%s://", parsed_url->protocol);
3056 before_host_printed = TRUE;
3057 if (parsed_url->port == 80 && 0 == strcmp(parsed_url->protocol,
3058 "http"))
3059 port_is_default = TRUE;
3060 if (parsed_url->port == 443 && 0 == strcmp(parsed_url->protocol,
3061 "https"))
3062 port_is_default = TRUE;
3064 if (parsed_url->username || parsed_url->password) {
3065 if (parsed_url->username)
3066 g_string_append(url, parsed_url->username);
3067 g_string_append_printf(url, ":%s", parsed_url->password);
3068 g_string_append(url, "@");
3069 before_host_printed = TRUE;
3071 if (parsed_url->host || parsed_url->port) {
3072 if (!parsed_url->host)
3073 g_string_append_printf(url, "{???}:%d",
3074 parsed_url->port);
3075 else {
3076 g_string_append(url, parsed_url->host);
3077 if (!port_is_default)
3078 g_string_append_printf(url, ":%d",
3079 parsed_url->port);
3081 host_printed = TRUE;
3083 if (parsed_url->path) {
3084 if (!host_printed && before_host_printed)
3085 g_string_append(url, "{???}");
3086 g_string_append(url, parsed_url->path);
3088 if (parsed_url->fragment)
3089 g_string_append_printf(url, "#%s", parsed_url->fragment);
3091 return g_string_free(url, FALSE);
3094 const gchar *
3095 purple_http_url_get_protocol(const PurpleHttpURL *parsed_url)
3097 g_return_val_if_fail(parsed_url != NULL, NULL);
3099 return parsed_url->protocol;
3102 const gchar *
3103 purple_http_url_get_username(const PurpleHttpURL *parsed_url)
3105 g_return_val_if_fail(parsed_url != NULL, NULL);
3107 return parsed_url->username;
3110 const gchar *
3111 purple_http_url_get_password(const PurpleHttpURL *parsed_url)
3113 g_return_val_if_fail(parsed_url != NULL, NULL);
3115 return parsed_url->password;
3118 const gchar *
3119 purple_http_url_get_host(const PurpleHttpURL *parsed_url)
3121 g_return_val_if_fail(parsed_url != NULL, NULL);
3123 return parsed_url->host;
3127 purple_http_url_get_port(const PurpleHttpURL *parsed_url)
3129 g_return_val_if_fail(parsed_url != NULL, 0);
3131 return parsed_url->port;
3134 const gchar *
3135 purple_http_url_get_path(const PurpleHttpURL *parsed_url)
3137 g_return_val_if_fail(parsed_url != NULL, NULL);
3139 return parsed_url->path;
3142 const gchar *
3143 purple_http_url_get_fragment(const PurpleHttpURL *parsed_url)
3145 g_return_val_if_fail(parsed_url != NULL, NULL);
3147 return parsed_url->fragment;
3150 /*** HTTP Subsystem ***********************************************************/
3152 void purple_http_init(void)
3154 purple_http_re_url = g_regex_new("^"
3156 "(?:" /* host part beginning */
3157 "([a-z]+)\\:/*" /* protocol */
3158 "([^/]+)" /* username, password, host, port */
3159 ")?" /* host part ending */
3161 "([^#]*)" /* path */
3163 "(?:#" "(.*)" ")?" /* fragment */
3165 "$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
3166 G_REGEX_MATCH_NOTEMPTY, NULL);
3168 purple_http_re_url_host = g_regex_new("^"
3170 "(?:" /* user credentials part beginning */
3171 "([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+)" /* username */
3172 "(?::([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+))" /* password */
3173 "@)?" /* user credentials part ending */
3175 "([a-z0-9.-]+)" /* host */
3176 "(?::([0-9]+))?" /* port*/
3178 "$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
3179 G_REGEX_MATCH_NOTEMPTY, NULL);
3181 purple_http_re_rfc1123 = g_regex_new(
3182 "^[a-z]+, " /* weekday */
3183 "([0-9]+) " /* date */
3184 "([a-z]+) " /* month */
3185 "([0-9]+) " /* year */
3186 "([0-9]+:[0-9]+:[0-9]+) " /* time */
3187 "(?:GMT|UTC)$",
3188 G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
3189 G_REGEX_MATCH_NOTEMPTY, NULL);
3191 purple_http_hc_list = NULL;
3192 purple_http_hc_by_ptr = g_hash_table_new(g_direct_hash, g_direct_equal);
3193 purple_http_hc_by_gc = g_hash_table_new_full(g_direct_hash,
3194 g_direct_equal, NULL, (GDestroyNotify)g_list_free);
3195 purple_http_cancelling_gc = g_hash_table_new(g_direct_hash, g_direct_equal);
3198 static void purple_http_foreach_conn_cancel(gpointer _hc, gpointer user_data)
3200 PurpleHttpConnection *hc = _hc;
3201 purple_http_conn_cancel(hc);
3204 void purple_http_uninit(void)
3206 g_regex_unref(purple_http_re_url);
3207 purple_http_re_url = NULL;
3208 g_regex_unref(purple_http_re_url_host);
3209 purple_http_re_url_host = NULL;
3210 g_regex_unref(purple_http_re_rfc1123);
3211 purple_http_re_rfc1123 = NULL;
3213 g_list_foreach(purple_http_hc_list, purple_http_foreach_conn_cancel,
3214 NULL);
3216 if (purple_http_hc_list != NULL ||
3217 0 != g_hash_table_size(purple_http_hc_by_ptr) ||
3218 0 != g_hash_table_size(purple_http_hc_by_gc))
3219 purple_debug_warning("http",
3220 "Couldn't cleanup all connections.\n");
3222 g_list_free(purple_http_hc_list);
3223 purple_http_hc_list = NULL;
3224 g_hash_table_destroy(purple_http_hc_by_gc);
3225 purple_http_hc_by_gc = NULL;
3226 g_hash_table_destroy(purple_http_hc_by_ptr);
3227 purple_http_hc_by_ptr = NULL;
3228 g_hash_table_destroy(purple_http_cancelling_gc);
3229 purple_http_cancelling_gc = NULL;