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
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
25 #include "glibcompat.h"
31 #include "purple-socket.h"
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
66 PurpleHttpKeepaliveHost
*host
;
69 struct _PurpleHttpRequest
75 PurpleHttpHeaders
*headers
;
76 PurpleHttpCookieJar
*cookie_jar
;
77 PurpleHttpKeepalivePool
*keepalive_pool
;
81 PurpleHttpContentReader contents_reader
;
82 gpointer contents_reader_data
;
83 PurpleHttpContentWriter response_writer
;
84 gpointer response_writer_data
;
92 struct _PurpleHttpConnection
95 PurpleHttpCallback callback
;
98 gboolean is_keepalive
;
99 gboolean is_cancelling
;
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
;
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
142 PurpleHttpHeaders
*headers
;
145 struct _PurpleHttpURL
156 struct _PurpleHttpHeaders
168 struct _PurpleHttpCookieJar
175 struct _PurpleHttpKeepaliveRequest
177 PurpleConnection
*gc
;
178 PurpleSocketConnectCb cb
;
181 PurpleHttpKeepaliveHost
*host
;
182 PurpleHttpSocket
*hs
;
185 struct _PurpleHttpKeepaliveHost
187 PurpleHttpKeepalivePool
*pool
;
193 GSList
*sockets
; /* list of PurpleHttpSocket */
195 GSList
*queue
; /* list of PurpleHttpKeepaliveRequest */
196 guint process_queue_timeout
;
199 struct _PurpleHttpKeepalivePool
201 gboolean is_destroying
;
205 guint limit_per_host
;
207 /* key: purple_http_socket_hash, value: PurpleHttpKeepaliveHost */
211 struct _PurpleHttpConnectionSet
213 gboolean is_destroying
;
215 GHashTable
*connections
;
218 struct _PurpleHttpGzStream
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
);
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
,
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
);
252 purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest
*req
);
254 purple_http_keepalive_pool_release(PurpleHttpSocket
*hs
, gboolean invalidate
);
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
;
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
);
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
);
316 while (months
[month
] != NULL
) {
317 if (0 == g_ascii_strcasecmp(d_month
, months
[month
]))
323 iso_date
= g_strdup_printf("%s-%02d-%sT%s+00:00",
324 d_year
, month
, d_date
, d_time
);
332 purple_debug_warning("http", "Invalid month: %s\n", d_month
);
337 t
= purple_str_to_time(iso_date
, TRUE
, NULL
, NULL
, NULL
);
344 /*** GZip streams *************************************************************/
346 static PurpleHttpGzStream
*
347 purple_http_gz_new(gsize max_output
, gboolean is_deflate
)
349 PurpleHttpGzStream
*gzs
= g_new0(PurpleHttpGzStream
, 1);
353 windowBits
= -MAX_WBITS
;
355 windowBits
= MAX_WBITS
+ 32;
357 if (inflateInit2(&gzs
->zs
, windowBits
) != Z_OK
) {
358 purple_debug_error("http", "Cannot initialize zlib stream\n");
363 gzs
->max_output
= max_output
;
369 purple_http_gz_put(PurpleHttpGzStream
*gzs
, const gchar
*buf
, gsize len
)
371 const gchar
*compressed_buff
;
372 gsize compressed_len
;
376 g_return_val_if_fail(gzs
!= NULL
, NULL
);
377 g_return_val_if_fail(buf
!= NULL
, NULL
);
385 g_string_append_len(gzs
->pending
, buf
, len
);
386 compressed_buff
= gzs
->pending
->str
;
387 compressed_len
= gzs
->pending
->len
;
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) {
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)
411 if (gzs
->decompressed
+ decompressed_len
>=
414 purple_debug_warning("http", "Maximum amount of"
415 " decompressed data is reached\n");
416 decompressed_len
= gzs
->max_output
-
418 gzres
= Z_STREAM_END
;
420 gzs
->decompressed
+= decompressed_len
;
421 g_string_append_len(ret
, decompressed_buff
,
423 if (gzres
== Z_STREAM_END
)
426 purple_debug_error("http",
427 "Decompression failed (%d): %s\n", gzres
,
435 g_string_free(gzs
->pending
, TRUE
);
439 if (zs
->avail_in
> 0) {
440 gzs
->pending
= g_string_new_len((gchar
*)zs
->next_in
,
448 purple_http_gz_free(PurpleHttpGzStream
*gzs
)
452 inflateEnd(&gzs
->zs
);
454 g_string_free(gzs
->pending
, TRUE
);
458 /*** HTTP Sockets *************************************************************/
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
);
483 if (purple_debug_is_verbose())
484 purple_debug_misc("http", "new socket created: %p\n", hs
);
490 purple_http_socket_close_free(PurpleHttpSocket
*hs
)
495 if (purple_debug_is_verbose())
496 purple_debug_misc("http", "destroying socket: %p\n", hs
);
498 purple_socket_destroy(hs
->ps
);
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
,
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
,
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
);
529 static void purple_http_headers_free_kvp(PurpleKeyValuePair
*kvp
)
536 static void purple_http_headers_free(PurpleHttpHeaders
*hdrs
)
541 g_hash_table_destroy(hdrs
->by_name
);
542 g_list_free_full(hdrs
->list
,
543 (GDestroyNotify
)purple_http_headers_free_kvp
);
547 static void purple_http_headers_add(PurpleHttpHeaders
*hdrs
, const gchar
*key
,
550 PurpleKeyValuePair
*kvp
;
551 GList
*named_values
, *new_values
;
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
);
569 g_hash_table_insert(hdrs
->by_name
, key_low
, new_values
);
572 static void purple_http_headers_remove(PurpleHttpHeaders
*hdrs
,
577 g_return_if_fail(hdrs
!= NULL
);
578 g_return_if_fail(key
!= NULL
);
580 if (!g_hash_table_remove(hdrs
->by_name
, key
))
583 /* Could be optimized to O(1). */
584 it
= g_list_first(hdrs
->list
);
586 PurpleKeyValuePair
*kvp
= it
->data
;
588 it
= g_list_next(it
);
589 if (g_ascii_strcasecmp(kvp
->key
, key
) != 0)
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
);
605 static GList
* purple_http_headers_get_all_by_name(
606 PurpleHttpHeaders
*hdrs
, const gchar
*key
)
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
);
621 static const gchar
* purple_http_headers_get(PurpleHttpHeaders
*hdrs
,
624 const GList
*values
= purple_http_headers_get_all_by_name(hdrs
, key
);
632 static gboolean
purple_http_headers_get_int(PurpleHttpHeaders
*hdrs
,
633 const gchar
*key
, int *dst
)
638 str
= purple_http_headers_get(hdrs
, key
);
642 if (1 != sscanf(str
, "%d", &val
))
649 static gboolean
purple_http_headers_match(PurpleHttpHeaders
*hdrs
,
650 const gchar
*key
, const gchar
*value
)
654 str
= purple_http_headers_get(hdrs
, key
);
655 if (str
== NULL
|| value
== NULL
)
658 return (g_ascii_strcasecmp(str
, value
) == 0);
661 static gchar
* purple_http_headers_dump(PurpleHttpHeaders
*hdrs
)
665 GString
*s
= g_string_new("");
667 hdr
= purple_http_headers_get_all(hdrs
);
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
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
,
702 va_start(args
, format
);
703 hc
->response
->error
= g_strdup_vprintf(format
, 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
)
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
)
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)
742 hc
->request_header
= h
= g_string_new("");
743 hc
->request_header_written
= 0;
744 hc
->request_contents_written
= 0;
747 request_url
= tmp_url
= purple_http_url_print(url
);
749 request_url
= url
->path
;
751 g_string_append_printf(h
, "%s %s HTTP/%s\r\n",
752 req
->method
? req
->method
: "GET",
754 req
->http11
? "1.1" : "1.0");
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
);
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
;
786 proxy_password
= purple_proxy_info_get_password(proxy
);
787 if (proxy_password
== NULL
)
790 tmp
= g_strdup_printf("%s:%s", proxy_username
, proxy_password
);
792 proxy_auth
= purple_base64_encode((const guchar
*)tmp
, len
);
796 ntlm_type1
= purple_ntlm_gen_type1(purple_get_host_name(), "");
798 g_string_append_printf(h
, "Proxy-Authorization: Basic %s\r\n",
800 g_string_append_printf(h
, "Proxy-Authorization: NTLM %s\r\n",
802 g_string_append(h
, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */
804 memset(proxy_auth
, 0, strlen(proxy_auth
));
809 hdr
= purple_http_headers_get_all(hdrs
);
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
);
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",
832 static gboolean
_purple_http_recv_headers(PurpleHttpConnection
*hc
,
833 const gchar
*buf
, int len
)
837 if (hc
->headers_got
) {
838 purple_debug_error("http", "Headers already got\n");
839 _purple_http_error(hc
, _("Error parsing HTTP"));
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"));
851 while ((eol
= strstr(hc
->response_buffer
->str
, "\r\n"))
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() &&
864 purple_debug_misc("http", "Got keep-"
865 "alive terminator from previous"
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 "
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"));
890 if (purple_debug_is_verbose())
891 purple_debug_misc("http",
892 "Got main header with code %d\n",
895 if (purple_debug_is_verbose() &&
896 purple_debug_is_unsafe())
897 purple_debug_misc("http", "Got header: %s\n",
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"));
907 while (*delim
== ' ')
910 purple_http_headers_add(hc
->response
->headers
, hdrline
, delim
);
913 g_string_erase(hc
->response_buffer
, 0, hdrline_len
+ 2);
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"));
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
;
955 if (decompressed
!= NULL
)
956 g_string_free(decompressed
, TRUE
);
960 if (hc
->request
->response_writer
!= NULL
) {
962 succ
= hc
->request
->response_writer(hc
, hc
->response
, buf
,
963 hc
->length_got_decompressed
, len
,
964 hc
->request
->response_writer_data
);
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"));
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
);
987 static gboolean
_purple_http_recv_body_chunked(PurpleHttpConnection
*hc
,
988 const gchar
*buf
, int len
)
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"));
1006 while (hc
->response_buffer
->len
> 0) {
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
))
1017 g_string_erase(hc
->response_buffer
, 0, got_now
);
1018 hc
->in_chunk
= (hc
->chunk_got
< hc
->chunk_length
);
1023 line
= hc
->response_buffer
->str
;
1024 eol
= strstr(line
, "\r\n");
1026 g_string_erase(hc
->response_buffer
, 0, 2);
1027 line
= hc
->response_buffer
->str
;
1028 eol
= strstr(line
, "\r\n");
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"));
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",
1048 purple_debug_warning("http",
1049 "Chunk length not found\n");
1050 _purple_http_error(hc
, _("Error parsing HTTP"));
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
;
1071 static gboolean
_purple_http_recv_body(PurpleHttpConnection
*hc
,
1072 const gchar
*buf
, int len
)
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
)
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
)
1093 _purple_http_error(hc
, _("Error reading from %s: %s"),
1094 hc
->url
->host
, g_strerror(errno
));
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"
1111 _purple_http_error(hc
, _("Error parsing HTTP"));
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
);
1122 const gchar
*server
= purple_http_headers_get(
1123 hc
->response
->headers
, "Server");
1125 g_ascii_strcasecmp(server
, "YHttpServer") == 0)
1127 purple_debug_warning("http", "No more data "
1128 "while parsing headers (YHttpServer "
1130 hc
->headers_got
= TRUE
;
1131 hc
->length_expected
= hc
->length_got
= 0;
1132 hc
->length_got_decompressed
= 0;
1134 purple_debug_warning("http", "No more data "
1135 "while parsing headers\n");
1136 _purple_http_error(hc
, _("Error parsing HTTP"));
1142 if (!hc
->headers_got
&& len
> 0) {
1143 if (!_purple_http_recv_headers(hc
, buf
, len
))
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",
1157 is_deflate
= purple_http_headers_match(
1158 hc
->response
->headers
, "Content-Encoding",
1160 if (is_gzip
|| is_deflate
) {
1161 hc
->gz_stream
= purple_http_gz_new(
1162 hc
->request
->max_length
+ 1,
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
);
1175 if (!hc
->headers_got
)
1176 return got_anything
;
1180 if (!_purple_http_recv_body(hc
, buf
, len
))
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
) {
1194 _purple_http_error(hc
, _("Chunked connection terminated"));
1197 if (purple_debug_is_verbose()) {
1198 purple_debug_misc("http",
1199 "I need the terminating empty chunk\n");
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"));
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",
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
);
1233 if (hc
->response
->code
== 407) {
1234 _purple_http_error(hc
, _("Invalid proxy credentials"));
1238 redirect
= purple_http_headers_get(hc
->response
->headers
,
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
++;
1248 if (purple_debug_is_unsafe())
1249 purple_debug_warning("http",
1250 "Invalid redirect to %s\n",
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
);
1265 _purple_http_disconnect(hc
, TRUE
);
1266 purple_http_connection_terminate(hc
);
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
);
1288 _purple_http_error(hc
, _("Error requesting data to write"));
1292 hc
->contents_reader_requested
= FALSE
;
1293 g_string_set_size(hc
->contents_reader_buffer
, stored
);
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
)
1320 _purple_http_gen_headers(hc
);
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
);
1346 write_from
= hc
->contents_reader_buffer
->str
;
1347 write_len
= hc
->contents_reader_buffer
->len
;
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");
1359 written
= purple_socket_write(hc
->socket
->ps
,
1360 (const guchar
*)write_from
, write_len
);
1363 if (written
< 0 && errno
== EAGAIN
)
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
);
1376 _purple_http_error(hc
, _("Error writing to %s: %s"),
1377 hc
->url
->host
, g_strerror(errno
));
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
)
1386 if (hc
->request
->contents_length
> 0)
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
)
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
);
1423 purple_http_keepalive_pool_release(hc
->socket
, !is_graceful
);
1429 _purple_http_connected(PurpleSocket
*ps
, const gchar
*error
, gpointer _hc
)
1431 PurpleHttpSocket
*hs
= NULL
;
1432 PurpleHttpConnection
*hc
= _hc
;
1435 hs
= purple_socket_get_data(ps
, "hs");
1437 hc
->socket_request
= NULL
;
1440 if (error
!= NULL
) {
1441 _purple_http_error(hc
, _("Unable to connect to %s: %s"),
1442 hc
->url
->host
, error
);
1446 purple_socket_watch(ps
, PURPLE_INPUT_WRITE
, _purple_http_send
, hc
);
1449 static gboolean
_purple_http_reconnect(PurpleHttpConnection
*hc
)
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
);
1465 purple_debug_misc("http", "Connecting to %s...\n",
1470 if (g_strcmp0(url
->protocol
, "") == 0 ||
1471 g_ascii_strcasecmp(url
->protocol
, "http") == 0)
1474 } else if (g_ascii_strcasecmp(url
->protocol
, "https") == 0) {
1477 _purple_http_error(hc
, _("Unsupported protocol: %s"),
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
);
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
);
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
;
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
);
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
);
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
);
1544 PurpleHttpConnection
* purple_http_get_printf(PurpleConnection
*gc
,
1545 PurpleHttpCallback callback
, gpointer user_data
,
1546 const gchar
*format
, ...)
1550 PurpleHttpConnection
*ret
;
1552 g_return_val_if_fail(format
!= NULL
, NULL
);
1554 va_start(args
, format
);
1555 value
= g_strdup_vprintf(format
, args
);
1558 ret
= purple_http_get(gc
, callback
, user_data
, value
);
1564 PurpleHttpConnection
* purple_http_request(PurpleConnection
*gc
,
1565 PurpleHttpRequest
*request
, PurpleHttpCallback callback
,
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");
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");
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",
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
);
1604 _purple_http_reconnect(hc
);
1606 hc
->timeout_handle
= purple_timeout_add_seconds(request
->timeout
,
1607 purple_http_request_timeout
, 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
);
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
);
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
,
1666 g_hash_table_remove(purple_http_hc_by_ptr
, hc
);
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
);
1676 g_hash_table_insert(purple_http_hc_by_gc
,
1677 hc
->gc
, gc_list_new
);
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");
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
)
1704 if (http_conn
->is_cancelling
)
1706 http_conn
->is_cancelling
= TRUE
;
1708 if (purple_debug_is_verbose()) {
1709 purple_debug_misc("http", "Cancelling connection %p...\n",
1713 http_conn
->response
->code
= 0;
1714 _purple_http_disconnect(http_conn
, FALSE
);
1715 purple_http_connection_terminate(http_conn
);
1719 purple_http_conn_retry(PurpleHttpConnection
*http_conn
)
1721 if (http_conn
== NULL
)
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
)
1735 if (purple_debug_is_verbose()) {
1736 purple_debug_misc("http", "Cancelling all running HTTP "
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
));
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
)
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(
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
)
1806 gboolean reading_state
;
1807 int processed
, total
;
1809 g_return_if_fail(hc
!= NULL
);
1811 if (hc
->watcher
== NULL
)
1814 reading_state
= hc
->is_reading
;
1815 if (reading_state
) {
1816 total
= hc
->length_expected
;
1817 processed
= hc
->length_got
;
1819 total
= hc
->request
->contents_length
;
1820 processed
= hc
->request_contents_written
;
1824 if (total
!= -1 && total
< processed
) {
1825 purple_debug_warning("http", "Processed too much\n");
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
)
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
);
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
);
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;
1876 void purple_http_cookie_free(PurpleHttpCookie
*cookie
)
1878 g_free(cookie
->value
);
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
);
1895 void purple_http_cookie_jar_free(PurpleHttpCookieJar
*cookie_jar
)
1897 g_hash_table_destroy(cookie_jar
->tab
);
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
)
1914 g_return_val_if_fail(cookie_jar
->ref_count
> 0, NULL
);
1916 cookie_jar
->ref_count
--;
1917 if (cookie_jar
->ref_count
> 0)
1920 purple_http_cookie_jar_free(cookie_jar
);
1924 static void purple_http_cookie_jar_parse(PurpleHttpCookieJar
*cookie_jar
,
1927 values
= g_list_first(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
);
1945 purple_debug_warning("http", "Invalid cookie.");
1949 name
= g_strndup(cookie
, eqsign
- cookie
);
1951 if (semicolon
!= NULL
)
1952 value
= g_strndup(eqsign
, semicolon
- eqsign
);
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(
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
);
1983 static gchar
* purple_http_cookie_jar_gen(PurpleHttpCookieJar
*cookie_jar
)
1987 PurpleHttpCookie
*cookie
;
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
)
2001 g_string_append_printf(str
, "%s=%s; ", key
, cookie
->value
);
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
;
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
)
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
);
2039 g_hash_table_remove(cookie_jar
->tab
, name
);
2042 gchar
* purple_http_cookie_jar_get(PurpleHttpCookieJar
*cookie_jar
,
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
);
2054 return g_strdup(purple_url_decode(cookie
->value
));
2057 gchar
* purple_http_cookie_jar_dump(PurpleHttpCookieJar
*cjar
)
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
);
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 *************************************************/
2084 purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost
*host
);
2087 purple_http_keepalive_host_free(gpointer _host
)
2089 PurpleHttpKeepaliveHost
*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;
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
);
2120 purple_http_keepalive_pool_free(PurpleHttpKeepalivePool
*pool
)
2122 g_return_if_fail(pool
!= NULL
);
2124 if (pool
->is_destroying
)
2126 pool
->is_destroying
= TRUE
;
2127 g_hash_table_destroy(pool
->by_hash
);
2132 purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool
*pool
)
2134 g_return_if_fail(pool
!= NULL
);
2139 PurpleHttpKeepalivePool
*
2140 purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool
*pool
)
2145 g_return_val_if_fail(pool
->ref_count
> 0, NULL
);
2148 if (pool
->ref_count
> 0)
2151 purple_http_keepalive_pool_free(pool
);
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
;
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");
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
);
2187 req
= g_new0(PurpleHttpKeepaliveRequest
, 1);
2190 req
->user_data
= user_data
;
2193 kahost
->queue
= g_slist_append(kahost
->queue
, req
);
2195 purple_http_keepalive_host_process_queue(kahost
);
2201 _purple_http_keepalive_socket_connected(PurpleSocket
*ps
,
2202 const gchar
*error
, gpointer _req
)
2204 PurpleHttpSocket
*hs
= NULL
;
2205 PurpleHttpKeepaliveRequest
*req
= _req
;
2208 hs
= purple_socket_get_data(ps
, "hs");
2213 req
->cb(ps
, error
, req
->user_data
);
2218 _purple_http_keepalive_host_process_queue_cb(gpointer _host
)
2220 PurpleHttpKeepaliveRequest
*req
;
2221 PurpleHttpKeepaliveHost
*host
= _host
;
2222 PurpleHttpSocket
*hs
= NULL
;
2224 guint sockets_count
;
2226 g_return_val_if_fail(host
!= NULL
, FALSE
);
2228 host
->process_queue_timeout
= 0;
2230 if (host
->queue
== NULL
)
2235 while (it
!= NULL
) {
2236 PurpleHttpSocket
*hs_current
= it
->data
;
2240 if (!hs_current
->is_busy
) {
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)
2255 req
= host
->queue
->data
;
2256 host
->queue
= g_slist_remove(host
->queue
, req
);
2259 if (purple_debug_is_verbose()) {
2260 purple_debug_misc("http", "locking a (previously used) "
2261 "socket: %p\n", hs
);
2267 purple_http_keepalive_host_process_queue(host
);
2269 req
->cb(hs
->ps
, NULL
, req
->user_data
);
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
);
2279 purple_debug_error("http", "failed creating new socket");
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
);
2296 purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost
*host
)
2298 g_return_if_fail(host
!= NULL
);
2300 if (host
->process_queue_timeout
> 0)
2303 host
->process_queue_timeout
= purple_timeout_add(0,
2304 _purple_http_keepalive_host_process_queue_cb
, host
);
2308 purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest
*req
)
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
,
2321 purple_http_socket_close_free(req
->hs
);
2322 /* req should already be free'd here */
2324 req
->cb(NULL
, _("Cancelled"), req
->user_data
);
2330 purple_http_keepalive_pool_release(PurpleHttpSocket
*hs
, gboolean invalidate
)
2332 PurpleHttpKeepaliveHost
*host
;
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
;
2345 purple_http_socket_close_free(hs
);
2350 host
->sockets
= g_slist_remove(host
->sockets
, hs
);
2351 purple_http_socket_close_free(hs
);
2354 purple_http_keepalive_host_process_queue(host
);
2358 purple_http_keepalive_pool_set_limit_per_host(PurpleHttpKeepalivePool
*pool
,
2361 g_return_if_fail(pool
!= NULL
);
2363 pool
->limit_per_host
= limit
;
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
);
2388 purple_http_connection_set_destroy(PurpleHttpConnectionSet
*set
)
2393 set
->is_destroying
= 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
))
2403 purple_http_conn_cancel(http_conn
);
2406 g_hash_table_destroy(set
->connections
);
2411 purple_http_connection_set_add(PurpleHttpConnectionSet
*set
,
2412 PurpleHttpConnection
*http_conn
)
2414 if (set
->is_destroying
)
2416 if (http_conn
->connection_set
== set
)
2418 if (http_conn
->connection_set
!= NULL
) {
2419 purple_http_connection_set_remove(http_conn
->connection_set
,
2422 g_hash_table_insert(set
->connections
, http_conn
, (gpointer
)TRUE
);
2423 http_conn
->connection_set
= set
;
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
;
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
);
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
)
2482 g_return_val_if_fail(request
->ref_count
> 0, NULL
);
2484 request
->ref_count
--;
2485 if (request
->ref_count
> 0)
2488 purple_http_request_free(request
);
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
, ...)
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
);
2514 purple_http_request_set_url(request
, 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);
2555 purple_http_request_set_keepalive_pool(PurpleHttpRequest
*request
,
2556 PurpleHttpKeepalivePool
*pool
)
2558 g_return_if_fail(request
!= 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
;
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;
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
);
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
);
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
,
2647 g_return_if_fail(request
!= NULL
);
2649 if (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
);
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
, ...)
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
);
2737 purple_http_request_header_set(request
, key
, 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);
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
);
2768 gboolean
purple_http_response_is_successful(PurpleHttpResponse
*response
)
2772 g_return_val_if_fail(response
!= NULL
, FALSE
);
2774 code
= response
->code
;
2779 /* TODO: HTTP/1.1 100 Continue */
2781 if (code
/ 100 == 2)
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"));
2807 g_snprintf(errmsg
, sizeof(errmsg
),
2808 _("Invalid HTTP response code (%d)"),
2817 gsize
purple_http_response_get_data_len(PurpleHttpResponse
*response
)
2819 g_return_val_if_fail(response
!= NULL
, 0);
2821 if (response
->contents
== NULL
)
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
;
2836 *len
= response
->contents
->len
;
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
,
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 ************************************************************/
2873 purple_http_url_parse(const char *raw_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",
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);
2907 if (host_full
[0] == '\0') {
2911 if (url
->path
[0] == '\0') {
2915 if ((url
->protocol
== NULL
) != (host_full
== NULL
))
2916 purple_debug_warning("http", "Protocol or host not present "
2917 "(unlikely case)\n");
2922 if (!g_regex_match(purple_http_re_url_host
, host_full
, 0,
2925 if (purple_debug_is_verbose() &&
2926 purple_debug_is_unsafe())
2928 purple_debug_warning("http",
2929 "Invalid host provided for URL: %s\n",
2934 purple_http_url_free(url
);
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) {
2957 } else if (url
->host
!= NULL
) {
2959 url
->host
= g_ascii_strdown(url
->host
, -1);
2964 g_match_info_free(match_info
);
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"))
2975 if (url
->port
== 0 && 0 == strcmp(url
->protocol
, "https"))
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");
2988 purple_http_url_free(PurpleHttpURL
*parsed_url
)
2990 if (parsed_url
== NULL
)
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
);
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
);
3030 gchar
*last_slash
= strrchr(base_url
->path
, '/');
3032 if (last_slash
== NULL
)
3033 base_url
->path
[0] = '\0';
3035 last_slash
[1] = '\0';
3036 tmp
= base_url
->path
;
3037 base_url
->path
= g_strconcat(base_url
->path
,
3038 relative_url
->path
, NULL
);
3043 g_free(base_url
->fragment
);
3044 base_url
->fragment
= g_strdup(relative_url
->fragment
);
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
,
3059 port_is_default
= TRUE
;
3060 if (parsed_url
->port
== 443 && 0 == strcmp(parsed_url
->protocol
,
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",
3076 g_string_append(url
, parsed_url
->host
);
3077 if (!port_is_default
)
3078 g_string_append_printf(url
, ":%d",
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
);
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
;
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
;
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
;
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
;
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
;
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 */
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
,
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
;