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"
30 #include "purple-gio.h"
32 #define PURPLE_HTTP_URL_CREDENTIALS_CHARS "a-z0-9.,~_/*!&%?=+\\^-"
33 #define PURPLE_HTTP_MAX_RECV_BUFFER_LEN 102400
34 #define PURPLE_HTTP_MAX_READ_BUFFER_LEN 102400
35 #define PURPLE_HTTP_GZ_BUFF_LEN 1024
37 #define PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS 20
38 #define PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT 30
39 #define PURPLE_HTTP_REQUEST_DEFAULT_MAX_LENGTH 1048576
40 #define PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH (G_MAXINT32 - 1)
42 #define PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL 250000
44 #define PURPLE_HTTP_GET_ACCOUNT(gc) (gc ? purple_connection_get_account(gc) : NULL)
46 typedef struct _PurpleHttpSocket PurpleHttpSocket
;
48 typedef struct _PurpleHttpHeaders PurpleHttpHeaders
;
50 typedef struct _PurpleHttpKeepaliveHost PurpleHttpKeepaliveHost
;
52 typedef struct _PurpleHttpKeepaliveRequest PurpleHttpKeepaliveRequest
;
54 typedef struct _PurpleHttpGzStream PurpleHttpGzStream
;
56 typedef void (*PurpleHttpSocketConnectCb
)(PurpleHttpSocket
*hs
,
57 const gchar
*error
, gpointer _hc
);
59 struct _PurpleHttpSocket
61 GSocketConnection
*conn
;
62 GCancellable
*cancellable
;
68 PurpleHttpKeepaliveHost
*host
;
71 struct _PurpleHttpRequest
77 PurpleHttpHeaders
*headers
;
78 PurpleHttpCookieJar
*cookie_jar
;
79 PurpleHttpKeepalivePool
*keepalive_pool
;
83 PurpleHttpContentReader contents_reader
;
84 gpointer contents_reader_data
;
85 PurpleHttpContentWriter response_writer
;
86 gpointer response_writer_data
;
94 struct _PurpleHttpConnection
97 PurpleHttpCallback callback
;
100 gboolean is_keepalive
;
101 gboolean is_cancelling
;
104 PurpleHttpRequest
*request
;
105 PurpleHttpResponse
*response
;
107 PurpleHttpKeepaliveRequest
*socket_request
;
108 PurpleHttpConnectionSet
*connection_set
;
109 PurpleHttpSocket
*socket
;
110 GString
*request_header
;
111 guint request_header_written
, request_contents_written
;
112 gboolean main_header_got
, headers_got
;
113 GString
*response_buffer
;
114 PurpleHttpGzStream
*gz_stream
;
116 GString
*contents_reader_buffer
;
117 gboolean contents_reader_requested
;
122 guint length_got
, length_got_decompressed
;
124 gboolean is_chunked
, in_chunk
, chunks_done
;
125 int chunk_length
, chunk_got
;
127 GList
*link_global
, *link_gc
;
129 guint timeout_handle
;
131 PurpleHttpProgressWatcher watcher
;
132 gpointer watcher_user_data
;
133 guint watcher_interval_threshold
;
134 gint64 watcher_last_call
;
135 guint watcher_delayed_handle
;
138 struct _PurpleHttpResponse
144 PurpleHttpHeaders
*headers
;
147 struct _PurpleHttpURL
158 struct _PurpleHttpHeaders
170 struct _PurpleHttpCookieJar
177 struct _PurpleHttpKeepaliveRequest
179 PurpleConnection
*gc
;
180 PurpleHttpSocketConnectCb cb
;
183 PurpleHttpKeepaliveHost
*host
;
184 PurpleHttpSocket
*hs
;
187 struct _PurpleHttpKeepaliveHost
189 PurpleHttpKeepalivePool
*pool
;
195 GSList
*sockets
; /* list of PurpleHttpSocket */
197 GSList
*queue
; /* list of PurpleHttpKeepaliveRequest */
198 guint process_queue_timeout
;
201 struct _PurpleHttpKeepalivePool
203 gboolean is_destroying
;
207 guint limit_per_host
;
209 /* key: purple_http_socket_hash, value: PurpleHttpKeepaliveHost */
213 struct _PurpleHttpConnectionSet
215 gboolean is_destroying
;
217 GHashTable
*connections
;
220 struct _PurpleHttpGzStream
223 GZlibDecompressor
*decompressor
;
229 struct _ntlm_type1_message
{
230 guint8 protocol
[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' */
231 guint32 type
; /* 0x00000001 */
232 guint32 flags
; /* 0x0000b203 */
234 guint16 dom_len1
; /* domain string length */
235 guint16 dom_len2
; /* domain string length */
236 guint32 dom_off
; /* domain string offset */
238 guint16 host_len1
; /* host string length */
239 guint16 host_len2
; /* host string length */
240 guint32 host_off
; /* host string offset (always 0x00000020) */
243 static time_t purple_http_rfc1123_to_time(const gchar
*str
);
245 static gboolean
purple_http_request_is_method(PurpleHttpRequest
*request
,
246 const gchar
*method
);
248 static PurpleHttpConnection
* purple_http_connection_new(
249 PurpleHttpRequest
*request
, PurpleConnection
*gc
);
250 static void purple_http_connection_terminate(PurpleHttpConnection
*hc
);
251 static void purple_http_conn_notify_progress_watcher(PurpleHttpConnection
*hc
);
253 purple_http_conn_retry(PurpleHttpConnection
*http_conn
);
255 static PurpleHttpResponse
* purple_http_response_new(void);
256 static void purple_http_response_free(PurpleHttpResponse
*response
);
258 static void purple_http_cookie_jar_parse(PurpleHttpCookieJar
*cookie_jar
,
260 static gchar
* purple_http_cookie_jar_gen(PurpleHttpCookieJar
*cookie_jar
);
261 gchar
* purple_http_cookie_jar_dump(PurpleHttpCookieJar
*cjar
);
263 static PurpleHttpKeepaliveRequest
*
264 purple_http_keepalive_pool_request(PurpleHttpKeepalivePool
*pool
,
265 PurpleConnection
*gc
, const gchar
*host
, int port
, gboolean is_ssl
,
266 PurpleHttpSocketConnectCb cb
, gpointer user_data
);
268 purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest
*req
);
270 purple_http_keepalive_pool_release(PurpleHttpSocket
*hs
, gboolean invalidate
);
273 purple_http_connection_set_remove(PurpleHttpConnectionSet
*set
,
274 PurpleHttpConnection
*http_conn
);
276 static GRegex
*purple_http_re_url
, *purple_http_re_url_host
,
277 *purple_http_re_rfc1123
;
280 * Values: pointers to running PurpleHttpConnection.
282 static GList
*purple_http_hc_list
;
285 * Keys: pointers to PurpleConnection.
286 * Values: GList of pointers to running PurpleHttpConnection.
288 static GHashTable
*purple_http_hc_by_gc
;
291 * Keys: pointers to PurpleConnection.
292 * Values: gboolean TRUE.
294 static GHashTable
*purple_http_cancelling_gc
;
297 * Keys: pointers to PurpleHttpConnection.
298 * Values: pointers to links in purple_http_hc_list.
300 static GHashTable
*purple_http_hc_by_ptr
;
302 /*** Helper functions *********************************************************/
304 static time_t purple_http_rfc1123_to_time(const gchar
*str
)
306 static const gchar
*months
[13] = {
307 "jan", "feb", "mar", "apr", "may", "jun",
308 "jul", "aug", "sep", "oct", "nov", "dec", NULL
310 GMatchInfo
*match_info
;
311 gchar
*d_date
, *d_month
, *d_year
, *d_time
;
316 g_return_val_if_fail(str
!= NULL
, 0);
318 g_regex_match(purple_http_re_rfc1123
, str
, 0, &match_info
);
319 if (!g_match_info_matches(match_info
)) {
320 g_match_info_free(match_info
);
324 d_date
= g_match_info_fetch(match_info
, 1);
325 d_month
= g_match_info_fetch(match_info
, 2);
326 d_year
= g_match_info_fetch(match_info
, 3);
327 d_time
= g_match_info_fetch(match_info
, 4);
329 g_match_info_free(match_info
);
332 while (months
[month
] != NULL
) {
333 if (0 == g_ascii_strcasecmp(d_month
, months
[month
]))
339 iso_date
= g_strdup_printf("%s-%02d-%sT%s+00:00",
340 d_year
, month
, d_date
, d_time
);
347 purple_debug_warning("http", "Invalid month: %s\n", d_month
);
355 t
= purple_str_to_time(iso_date
, TRUE
, NULL
, NULL
, NULL
);
362 /*** GZip streams *************************************************************/
364 static PurpleHttpGzStream
*
365 purple_http_gz_new(gsize max_output
, gboolean is_deflate
)
367 PurpleHttpGzStream
*gzs
= g_new0(PurpleHttpGzStream
, 1);
368 GZlibCompressorFormat format
;
371 format
= G_ZLIB_COMPRESSOR_FORMAT_RAW
;
373 format
= G_ZLIB_COMPRESSOR_FORMAT_GZIP
;
375 gzs
->decompressor
= g_zlib_decompressor_new(format
);
376 gzs
->max_output
= max_output
;
382 purple_http_gz_put(PurpleHttpGzStream
*gzs
, const gchar
*buf
, gsize len
)
384 const gchar
*compressed_buff
;
385 gsize compressed_len
;
388 g_return_val_if_fail(gzs
!= NULL
, NULL
);
389 g_return_val_if_fail(buf
!= NULL
, NULL
);
395 g_string_append_len(gzs
->pending
, buf
, len
);
396 compressed_buff
= gzs
->pending
->str
;
397 compressed_len
= gzs
->pending
->len
;
399 compressed_buff
= buf
;
400 compressed_len
= len
;
403 ret
= g_string_new(NULL
);
404 while (compressed_len
> 0) {
405 GConverterResult gzres
;
406 gchar decompressed_buff
[PURPLE_HTTP_GZ_BUFF_LEN
];
407 gsize decompressed_len
= 0;
408 gsize bytes_read
= 0;
409 GError
*error
= NULL
;
411 gzres
= g_converter_convert(G_CONVERTER(gzs
->decompressor
),
412 compressed_buff
, compressed_len
,
413 decompressed_buff
, sizeof(decompressed_buff
),
414 G_CONVERTER_NO_FLAGS
,
419 compressed_buff
+= bytes_read
;
420 compressed_len
-= bytes_read
;
422 if (gzres
== G_CONVERTER_CONVERTED
|| gzres
== G_CONVERTER_FINISHED
) {
423 if (decompressed_len
== 0)
425 if (gzs
->decompressed
+ decompressed_len
>=
428 purple_debug_warning("http", "Maximum amount of"
429 " decompressed data is reached\n");
430 decompressed_len
= gzs
->max_output
-
432 gzres
= G_CONVERTER_FINISHED
;
434 gzs
->decompressed
+= decompressed_len
;
435 g_string_append_len(ret
, decompressed_buff
,
437 if (gzres
== G_CONVERTER_FINISHED
)
440 purple_debug_error("http",
441 "Decompression failed (%d): %s\n", gzres
,
443 g_clear_error(&error
);
445 g_string_free(ret
, TRUE
);
451 g_string_free(gzs
->pending
, TRUE
);
455 if (compressed_len
> 0) {
456 gzs
->pending
= g_string_new_len(compressed_buff
,
464 purple_http_gz_free(PurpleHttpGzStream
*gzs
)
468 g_object_unref(gzs
->decompressor
);
470 g_string_free(gzs
->pending
, TRUE
);
474 /*** NTLM *********************************************************************/
477 * purple_ntlm_gen_type1:
478 * @hostname: Your hostname
479 * @domain: The domain to authenticate to
481 * Generates the base64 encoded type 1 message needed for NTLM authentication
483 * Returns: base64 encoded string to send to the server. This should
484 * be g_free'd by the caller.
487 purple_http_ntlm_gen_type1(const gchar
*hostname
, const gchar
*domain
)
489 int hostnamelen
,host_off
;
490 int domainlen
,dom_off
;
492 struct _ntlm_type1_message
*tmsg
;
495 hostnamelen
= strlen(hostname
);
496 domainlen
= strlen(domain
);
497 host_off
= sizeof(struct _ntlm_type1_message
);
498 dom_off
= sizeof(struct _ntlm_type1_message
) + hostnamelen
;
499 msg
= g_malloc0(sizeof(struct _ntlm_type1_message
) + hostnamelen
+ domainlen
);
500 tmsg
= (struct _ntlm_type1_message
*)(gpointer
)msg
;
501 tmsg
->protocol
[0] = 'N';
502 tmsg
->protocol
[1] = 'T';
503 tmsg
->protocol
[2] = 'L';
504 tmsg
->protocol
[3] = 'M';
505 tmsg
->protocol
[4] = 'S';
506 tmsg
->protocol
[5] = 'S';
507 tmsg
->protocol
[6] = 'P';
508 tmsg
->protocol
[7] = '\0';
509 tmsg
->type
= GUINT32_TO_LE(0x00000001);
510 tmsg
->flags
= GUINT32_TO_LE(0x0000b203);
511 tmsg
->dom_len1
= tmsg
->dom_len2
= GUINT16_TO_LE(domainlen
);
512 tmsg
->dom_off
= GUINT32_TO_LE(dom_off
);
513 tmsg
->host_len1
= tmsg
->host_len2
= GUINT16_TO_LE(hostnamelen
);
514 tmsg
->host_off
= GUINT32_TO_LE(host_off
);
515 memcpy(msg
+ host_off
, hostname
, hostnamelen
);
516 memcpy(msg
+ dom_off
, domain
, domainlen
);
518 tmp
= g_base64_encode(msg
, sizeof(struct _ntlm_type1_message
) + hostnamelen
+ domainlen
);
524 /*** HTTP Sockets *************************************************************/
527 purple_http_socket_hash(const gchar
*host
, int port
, gboolean is_ssl
)
529 return g_strdup_printf("%c:%s:%d", (is_ssl
? 'S' : 'R'), host
, port
);
533 purple_http_socket_connect_new_cb(GObject
*source
, GAsyncResult
*res
,
536 PurpleHttpSocket
*hs
= user_data
;
537 GSocketConnection
*conn
;
538 PurpleHttpSocketConnectCb cb
;
540 GError
*error
= NULL
;
542 conn
= g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source
),
545 cb
= g_object_steal_data(source
, "cb");
546 cb_data
= g_object_steal_data(source
, "cb_data");
549 if (!g_error_matches(error
,
550 G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
551 cb(hs
, error
->message
, cb_data
);
554 g_clear_error(&error
);
560 cb(hs
, NULL
, cb_data
);
563 static PurpleHttpSocket
*
564 purple_http_socket_connect_new(PurpleConnection
*gc
, const gchar
*host
,
565 int port
, gboolean is_ssl
,
566 PurpleHttpSocketConnectCb cb
, gpointer user_data
)
568 PurpleHttpSocket
*hs
;
569 GSocketClient
*client
;
570 GError
*error
= NULL
;
572 client
= purple_gio_socket_client_new(PURPLE_HTTP_GET_ACCOUNT(gc
), &error
);
574 if (client
== NULL
) {
575 purple_debug_error("http", "Error connecting to '%s:%d': %s",
576 host
, port
, error
->message
);
577 g_clear_error(&error
);
581 hs
= g_new0(PurpleHttpSocket
, 1);
582 hs
->cancellable
= g_cancellable_new();
584 g_socket_client_set_tls(client
, is_ssl
);
585 g_object_set_data(G_OBJECT(client
), "cb", cb
);
586 g_object_set_data(G_OBJECT(client
), "cb_data", user_data
);
588 g_socket_client_connect_to_host_async(client
,
589 host
, port
, hs
->cancellable
,
590 purple_http_socket_connect_new_cb
, hs
);
592 g_object_unref(client
);
594 if (purple_debug_is_verbose())
595 purple_debug_misc("http", "new socket created: %p\n", hs
);
601 purple_http_socket_close_free(PurpleHttpSocket
*hs
)
606 if (purple_debug_is_verbose())
607 purple_debug_misc("http", "destroying socket: %p\n", hs
);
609 if (hs
->input_source
> 0) {
610 g_source_remove(hs
->input_source
);
611 hs
->input_source
= 0;
614 if (hs
->output_source
> 0) {
615 g_source_remove(hs
->output_source
);
616 hs
->output_source
= 0;
619 if (hs
->cancellable
!= NULL
) {
620 g_cancellable_cancel(hs
->cancellable
);
621 g_clear_object(&hs
->cancellable
);
624 if (hs
->conn
!= NULL
) {
625 purple_gio_graceful_close(G_IO_STREAM(hs
->conn
), NULL
, NULL
);
626 g_clear_object(&hs
->conn
);
632 /*** Headers collection *******************************************************/
634 static PurpleHttpHeaders
* purple_http_headers_new(void);
635 static void purple_http_headers_free(PurpleHttpHeaders
*hdrs
);
636 static void purple_http_headers_add(PurpleHttpHeaders
*hdrs
, const gchar
*key
,
638 static const GList
* purple_http_headers_get_all(PurpleHttpHeaders
*hdrs
);
639 static GList
* purple_http_headers_get_all_by_name(
640 PurpleHttpHeaders
*hdrs
, const gchar
*key
);
641 static const gchar
* purple_http_headers_get(PurpleHttpHeaders
*hdrs
,
643 static gboolean
purple_http_headers_get_int(PurpleHttpHeaders
*hdrs
,
644 const gchar
*key
, int *dst
);
645 static gboolean
purple_http_headers_match(PurpleHttpHeaders
*hdrs
,
646 const gchar
*key
, const gchar
*value
);
647 static gchar
* purple_http_headers_dump(PurpleHttpHeaders
*hdrs
);
649 static PurpleHttpHeaders
* purple_http_headers_new(void)
651 PurpleHttpHeaders
*hdrs
= g_new0(PurpleHttpHeaders
, 1);
653 hdrs
->by_name
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
654 (GDestroyNotify
)g_list_free
);
659 static void purple_http_headers_free_kvp(PurpleKeyValuePair
*kvp
)
666 static void purple_http_headers_free(PurpleHttpHeaders
*hdrs
)
671 g_hash_table_destroy(hdrs
->by_name
);
672 g_list_free_full(hdrs
->list
,
673 (GDestroyNotify
)purple_http_headers_free_kvp
);
677 static void purple_http_headers_add(PurpleHttpHeaders
*hdrs
, const gchar
*key
,
680 PurpleKeyValuePair
*kvp
;
681 GList
*named_values
, *new_values
;
684 g_return_if_fail(hdrs
!= NULL
);
685 g_return_if_fail(key
!= NULL
);
686 g_return_if_fail(value
!= NULL
);
688 kvp
= g_new0(PurpleKeyValuePair
, 1);
689 kvp
->key
= g_strdup(key
);
690 kvp
->value
= g_strdup(value
);
691 hdrs
->list
= g_list_append(hdrs
->list
, kvp
);
693 key_low
= g_ascii_strdown(key
, -1);
694 named_values
= g_hash_table_lookup(hdrs
->by_name
, key_low
);
695 new_values
= g_list_append(named_values
, kvp
->value
);
699 g_hash_table_insert(hdrs
->by_name
, key_low
, new_values
);
702 static void purple_http_headers_remove(PurpleHttpHeaders
*hdrs
,
707 g_return_if_fail(hdrs
!= NULL
);
708 g_return_if_fail(key
!= NULL
);
710 if (!g_hash_table_remove(hdrs
->by_name
, key
))
713 /* Could be optimized to O(1). */
714 it
= g_list_first(hdrs
->list
);
716 PurpleKeyValuePair
*kvp
= it
->data
;
718 it
= g_list_next(it
);
719 if (g_ascii_strcasecmp(kvp
->key
, key
) != 0)
722 hdrs
->list
= g_list_delete_link(hdrs
->list
, curr
);
723 purple_http_headers_free_kvp(kvp
);
727 static const GList
* purple_http_headers_get_all(PurpleHttpHeaders
*hdrs
)
729 g_return_val_if_fail(hdrs
!= NULL
, NULL
);
735 static GList
* purple_http_headers_get_all_by_name(
736 PurpleHttpHeaders
*hdrs
, const gchar
*key
)
741 g_return_val_if_fail(hdrs
!= NULL
, NULL
);
742 g_return_val_if_fail(key
!= NULL
, NULL
);
744 key_low
= g_ascii_strdown(key
, -1);
745 values
= g_hash_table_lookup(hdrs
->by_name
, key_low
);
751 static const gchar
* purple_http_headers_get(PurpleHttpHeaders
*hdrs
,
754 const GList
*values
= purple_http_headers_get_all_by_name(hdrs
, key
);
762 static gboolean
purple_http_headers_get_int(PurpleHttpHeaders
*hdrs
,
763 const gchar
*key
, int *dst
)
768 str
= purple_http_headers_get(hdrs
, key
);
772 if (1 != sscanf(str
, "%d", &val
))
779 static gboolean
purple_http_headers_match(PurpleHttpHeaders
*hdrs
,
780 const gchar
*key
, const gchar
*value
)
784 str
= purple_http_headers_get(hdrs
, key
);
785 if (str
== NULL
|| value
== NULL
)
788 return (g_ascii_strcasecmp(str
, value
) == 0);
791 static gchar
* purple_http_headers_dump(PurpleHttpHeaders
*hdrs
)
795 GString
*s
= g_string_new("");
797 hdr
= purple_http_headers_get_all(hdrs
);
799 PurpleKeyValuePair
*kvp
= hdr
->data
;
800 hdr
= g_list_next(hdr
);
802 g_string_append_printf(s
, "%s: %s%s", kvp
->key
,
803 (gchar
*)kvp
->value
, hdr
? "\n" : "");
806 return g_string_free(s
, FALSE
);
809 /*** HTTP protocol backend ****************************************************/
811 static void _purple_http_disconnect(PurpleHttpConnection
*hc
,
812 gboolean is_graceful
);
814 static void _purple_http_gen_headers(PurpleHttpConnection
*hc
);
815 static gboolean
_purple_http_recv_loopbody(PurpleHttpConnection
*hc
);
816 static gboolean
_purple_http_recv(GObject
*source
, gpointer _hc
);
817 static gboolean
_purple_http_send(GObject
*source
, gpointer _hc
);
819 /* closes current connection (if exists), estabilishes one and proceeds with
821 static gboolean
_purple_http_reconnect(PurpleHttpConnection
*hc
);
823 static void _purple_http_error(PurpleHttpConnection
*hc
, const char *format
,
824 ...) G_GNUC_PRINTF(2, 3);
826 static void _purple_http_error(PurpleHttpConnection
*hc
, const char *format
,
831 va_start(args
, format
);
832 hc
->response
->error
= g_strdup_vprintf(format
, args
);
835 if (purple_debug_is_verbose())
836 purple_debug_warning("http", "error: %s\n", hc
->response
->error
);
838 purple_http_conn_cancel(hc
);
841 static void memset_zero(gpointer pnt
, gsize len
)
843 volatile unsigned char *volatile pnt_
=
844 (volatile unsigned char *volatile) pnt
;
845 size_t i
= (size_t) 0U;
852 static void _purple_http_gen_headers(PurpleHttpConnection
*hc
)
857 PurpleHttpRequest
*req
;
858 PurpleHttpHeaders
*hdrs
;
859 gchar
*request_url
, *tmp_url
= NULL
;
861 PurpleProxyInfo
*proxy
;
862 gboolean proxy_http
= FALSE
;
863 const gchar
*proxy_username
, *proxy_password
;
865 g_return_if_fail(hc
!= NULL
);
867 if (hc
->request_header
!= NULL
)
873 proxy
= purple_proxy_get_setup(PURPLE_HTTP_GET_ACCOUNT(hc
->gc
));
875 proxy_http
= (purple_proxy_info_get_proxy_type(proxy
) == PURPLE_PROXY_HTTP
||
876 purple_proxy_info_get_proxy_type(proxy
) == PURPLE_PROXY_USE_ENVVAR
);
877 /* this is HTTP proxy, but used with tunelling with CONNECT */
878 if (proxy_http
&& url
->port
!= 80)
881 hc
->request_header
= h
= g_string_new("");
882 hc
->request_header_written
= 0;
883 hc
->request_contents_written
= 0;
886 request_url
= tmp_url
= purple_http_url_print(url
);
888 request_url
= url
->path
;
890 g_string_append_printf(h
, "%s %s HTTP/%s\r\n",
891 req
->method
? req
->method
: "GET",
893 req
->http11
? "1.1" : "1.0");
897 if (!purple_http_headers_get(hdrs
, "host"))
898 g_string_append_printf(h
, "Host: %s\r\n", url
->host
);
899 if (!purple_http_headers_get(hdrs
, "connection")) {
900 g_string_append(h
, "Connection: ");
901 g_string_append(h
, hc
->is_keepalive
?
902 "Keep-Alive\r\n" : "close\r\n");
904 if (!purple_http_headers_get(hdrs
, "accept"))
905 g_string_append(h
, "Accept: */*\r\n");
906 if (!purple_http_headers_get(hdrs
, "accept-encoding"))
907 g_string_append(h
, "Accept-Encoding: gzip, deflate\r\n");
909 if (!purple_http_headers_get(hdrs
, "content-length") && (
910 req
->contents_length
> 0 ||
911 purple_http_request_is_method(req
, "post")))
913 g_string_append_printf(h
, "Content-Length: %u\r\n",
914 (guint
) req
->contents_length
);
918 g_string_append(h
, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */
920 proxy_username
= purple_proxy_info_get_username(proxy
);
921 if (proxy_http
&& proxy_username
!= NULL
&& proxy_username
[0] != '\0') {
922 gchar
*proxy_auth
, *ntlm_type1
, *tmp
;
925 proxy_password
= purple_proxy_info_get_password(proxy
);
926 if (proxy_password
== NULL
)
929 tmp
= g_strdup_printf("%s:%s", proxy_username
, proxy_password
);
931 proxy_auth
= g_base64_encode((const guchar
*)tmp
, len
);
932 memset_zero(tmp
, len
);
935 ntlm_type1
= purple_http_ntlm_gen_type1(purple_get_host_name(),
938 g_string_append_printf(h
, "Proxy-Authorization: Basic %s\r\n",
940 g_string_append_printf(h
, "Proxy-Authorization: NTLM %s\r\n",
942 g_string_append(h
, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */
944 memset_zero(proxy_auth
, strlen(proxy_auth
));
949 hdr
= purple_http_headers_get_all(hdrs
);
951 PurpleKeyValuePair
*kvp
= hdr
->data
;
952 hdr
= g_list_next(hdr
);
954 g_string_append_printf(h
, "%s: %s\r\n",
955 kvp
->key
, (gchar
*)kvp
->value
);
958 if (!purple_http_cookie_jar_is_empty(req
->cookie_jar
)) {
959 gchar
* cookies
= purple_http_cookie_jar_gen(req
->cookie_jar
);
960 g_string_append_printf(h
, "Cookie: %s\r\n", cookies
);
964 g_string_append_printf(h
, "\r\n");
966 if (purple_debug_is_unsafe() && purple_debug_is_verbose()) {
967 purple_debug_misc("http", "Generated request headers:\n%s",
972 static gboolean
_purple_http_recv_headers(PurpleHttpConnection
*hc
,
973 const gchar
*buf
, int len
)
977 if (hc
->headers_got
) {
978 purple_debug_error("http", "Headers already got\n");
979 _purple_http_error(hc
, _("Error parsing HTTP"));
983 g_string_append_len(hc
->response_buffer
, buf
, len
);
984 if (hc
->response_buffer
->len
> PURPLE_HTTP_MAX_RECV_BUFFER_LEN
) {
985 purple_debug_error("http",
986 "Buffer too big when parsing headers\n");
987 _purple_http_error(hc
, _("Error parsing HTTP"));
991 while ((eol
= strstr(hc
->response_buffer
->str
, "\r\n"))
994 gchar
*hdrline
= hc
->response_buffer
->str
;
995 int hdrline_len
= eol
- hdrline
;
997 hdrline
[hdrline_len
] = '\0';
999 if (hdrline
[0] == '\0') {
1000 if (!hc
->main_header_got
) {
1001 if (purple_debug_is_verbose() &&
1004 purple_debug_misc("http", "Got keep-"
1005 "alive terminator from previous"
1008 purple_debug_warning("http", "Got empty"
1009 " line at the beginning - this "
1010 "may be a HTTP server quirk\n");
1012 } else /* hc->main_header_got */ {
1013 hc
->headers_got
= TRUE
;
1014 if (purple_debug_is_verbose()) {
1015 purple_debug_misc("http", "Got headers "
1019 } else if (!hc
->main_header_got
) {
1020 hc
->main_header_got
= TRUE
;
1021 delim
= strchr(hdrline
, ' ');
1022 if (delim
== NULL
|| 1 != sscanf(delim
+ 1, "%d",
1023 &hc
->response
->code
))
1025 purple_debug_warning("http",
1026 "Invalid response code\n");
1027 _purple_http_error(hc
, _("Error parsing HTTP"));
1030 if (purple_debug_is_verbose())
1031 purple_debug_misc("http",
1032 "Got main header with code %d\n",
1033 hc
->response
->code
);
1035 if (purple_debug_is_verbose() &&
1036 purple_debug_is_unsafe())
1037 purple_debug_misc("http", "Got header: %s\n",
1039 delim
= strchr(hdrline
, ':');
1040 if (delim
== NULL
|| delim
== hdrline
) {
1041 purple_debug_warning("http",
1042 "Bad header delimiter\n");
1043 _purple_http_error(hc
, _("Error parsing HTTP"));
1047 while (*delim
== ' ')
1050 purple_http_headers_add(hc
->response
->headers
, hdrline
, delim
);
1053 g_string_erase(hc
->response_buffer
, 0, hdrline_len
+ 2);
1054 if (hc
->headers_got
)
1060 static gboolean
_purple_http_recv_body_data(PurpleHttpConnection
*hc
,
1061 const gchar
*buf
, int len
)
1063 GString
*decompressed
= NULL
;
1065 if (hc
->length_expected
>= 0 &&
1066 len
+ hc
->length_got
> (guint
)hc
->length_expected
)
1068 len
= hc
->length_expected
- hc
->length_got
;
1071 hc
->length_got
+= len
;
1073 if (hc
->gz_stream
!= NULL
) {
1074 decompressed
= purple_http_gz_put(hc
->gz_stream
, buf
, len
);
1075 if (decompressed
== NULL
) {
1076 _purple_http_error(hc
,
1077 _("Error while decompressing data"));
1080 buf
= decompressed
->str
;
1081 len
= decompressed
->len
;
1084 g_assert(hc
->request
->max_length
<=
1085 PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH
);
1086 if (hc
->length_got_decompressed
+ len
> hc
->request
->max_length
) {
1087 purple_debug_warning("http",
1088 "Maximum length exceeded, truncating\n");
1089 len
= hc
->request
->max_length
- hc
->length_got_decompressed
;
1090 hc
->length_expected
= hc
->length_got
;
1092 hc
->length_got_decompressed
+= len
;
1095 if (decompressed
!= NULL
)
1096 g_string_free(decompressed
, TRUE
);
1100 if (hc
->request
->response_writer
!= NULL
) {
1102 succ
= hc
->request
->response_writer(hc
, hc
->response
, buf
,
1103 hc
->length_got_decompressed
, len
,
1104 hc
->request
->response_writer_data
);
1106 if (decompressed
!= NULL
)
1107 g_string_free(decompressed
, TRUE
);
1108 purple_debug_error("http",
1109 "Cannot write using callback\n");
1110 _purple_http_error(hc
,
1111 _("Error handling retrieved data"));
1115 if (hc
->response
->contents
== NULL
)
1116 hc
->response
->contents
= g_string_new("");
1117 g_string_append_len(hc
->response
->contents
, buf
, len
);
1120 if (decompressed
!= NULL
)
1121 g_string_free(decompressed
, TRUE
);
1123 purple_http_conn_notify_progress_watcher(hc
);
1127 static gboolean
_purple_http_recv_body_chunked(PurpleHttpConnection
*hc
,
1128 const gchar
*buf
, int len
)
1133 if (hc
->chunks_done
)
1135 if (!hc
->response_buffer
)
1136 hc
->response_buffer
= g_string_new("");
1138 g_string_append_len(hc
->response_buffer
, buf
, len
);
1139 if (hc
->response_buffer
->len
> PURPLE_HTTP_MAX_RECV_BUFFER_LEN
) {
1140 purple_debug_error("http",
1141 "Buffer too big when searching for chunk\n");
1142 _purple_http_error(hc
, _("Error parsing HTTP"));
1146 while (hc
->response_buffer
->len
> 0) {
1148 int got_now
= hc
->response_buffer
->len
;
1149 if (hc
->chunk_got
+ got_now
> hc
->chunk_length
)
1150 got_now
= hc
->chunk_length
- hc
->chunk_got
;
1151 hc
->chunk_got
+= got_now
;
1153 if (!_purple_http_recv_body_data(hc
,
1154 hc
->response_buffer
->str
, got_now
))
1157 g_string_erase(hc
->response_buffer
, 0, got_now
);
1158 hc
->in_chunk
= (hc
->chunk_got
< hc
->chunk_length
);
1163 line
= hc
->response_buffer
->str
;
1164 eol
= strstr(line
, "\r\n");
1166 g_string_erase(hc
->response_buffer
, 0, 2);
1167 line
= hc
->response_buffer
->str
;
1168 eol
= strstr(line
, "\r\n");
1171 /* waiting for more data (unlikely, but possible) */
1172 if (hc
->response_buffer
->len
> 20) {
1173 purple_debug_warning("http", "Chunk length not "
1174 "found (buffer too large)\n");
1175 _purple_http_error(hc
, _("Error parsing HTTP"));
1180 line_len
= eol
- line
;
1182 if (1 != sscanf(line
, "%x", &hc
->chunk_length
)) {
1183 if (purple_debug_is_unsafe())
1184 purple_debug_warning("http",
1185 "Chunk length not found in [%s]\n",
1188 purple_debug_warning("http",
1189 "Chunk length not found\n");
1190 _purple_http_error(hc
, _("Error parsing HTTP"));
1194 hc
->in_chunk
= TRUE
;
1196 if (purple_debug_is_verbose())
1197 purple_debug_misc("http", "Found chunk of length %d\n", hc
->chunk_length
);
1199 g_string_erase(hc
->response_buffer
, 0, line_len
+ 2);
1201 if (hc
->chunk_length
== 0) {
1202 hc
->chunks_done
= TRUE
;
1203 hc
->in_chunk
= FALSE
;
1211 static gboolean
_purple_http_recv_body(PurpleHttpConnection
*hc
,
1212 const gchar
*buf
, int len
)
1215 return _purple_http_recv_body_chunked(hc
, buf
, len
);
1217 return _purple_http_recv_body_data(hc
, buf
, len
);
1220 static gboolean
_purple_http_recv_loopbody(PurpleHttpConnection
*hc
)
1224 gboolean got_anything
;
1225 GError
*error
= NULL
;
1227 len
= g_pollable_input_stream_read_nonblocking(
1228 G_POLLABLE_INPUT_STREAM(
1229 g_io_stream_get_input_stream(
1230 G_IO_STREAM(hc
->socket
->conn
))),
1231 buf
, sizeof(buf
), hc
->socket
->cancellable
,
1233 got_anything
= (len
> 0);
1235 if (len
< 0 && (g_error_matches(error
,
1236 G_IO_ERROR
, G_IO_ERROR_WOULD_BLOCK
) ||
1237 g_error_matches(error
,
1238 G_IO_ERROR
, G_IO_ERROR_CANCELLED
))) {
1239 g_clear_error(&error
);
1244 _purple_http_error(hc
, _("Error reading from %s: %s"),
1245 hc
->url
->host
, error
->message
);
1246 g_clear_error(&error
);
1252 if (hc
->request
->max_length
== 0) {
1253 /* It's definitely YHttpServer quirk. */
1254 purple_debug_warning("http", "Got EOF, but no data was "
1255 "expected (this may be a server quirk)\n");
1256 hc
->length_expected
= hc
->length_got
;
1258 if (hc
->length_expected
>= 0 &&
1259 hc
->length_got
< (guint
)hc
->length_expected
)
1261 purple_debug_warning("http", "No more data while reading"
1263 _purple_http_error(hc
, _("Error parsing HTTP"));
1266 if (hc
->headers_got
)
1267 hc
->length_expected
= hc
->length_got
;
1268 else if (hc
->length_got
== 0 && hc
->socket
->use_count
> 1) {
1269 purple_debug_info("http", "Keep-alive connection "
1270 "expired (when reading), retrying...\n");
1271 purple_http_conn_retry(hc
);
1274 const gchar
*server
= purple_http_headers_get(
1275 hc
->response
->headers
, "Server");
1277 g_ascii_strcasecmp(server
, "YHttpServer") == 0)
1279 purple_debug_warning("http", "No more data "
1280 "while parsing headers (YHttpServer "
1282 hc
->headers_got
= TRUE
;
1283 hc
->length_expected
= hc
->length_got
= 0;
1284 hc
->length_got_decompressed
= 0;
1286 purple_debug_warning("http", "No more data "
1287 "while parsing headers\n");
1288 _purple_http_error(hc
, _("Error parsing HTTP"));
1294 if (!hc
->headers_got
&& len
> 0) {
1295 if (!_purple_http_recv_headers(hc
, buf
, len
))
1298 if (hc
->headers_got
) {
1299 gboolean is_gzip
, is_deflate
;
1300 if (!purple_http_headers_get_int(hc
->response
->headers
,
1301 "Content-Length", &hc
->length_expected
))
1302 hc
->length_expected
= -1;
1303 hc
->is_chunked
= (purple_http_headers_match(
1304 hc
->response
->headers
,
1305 "Transfer-Encoding", "chunked"));
1306 is_gzip
= purple_http_headers_match(
1307 hc
->response
->headers
, "Content-Encoding",
1309 is_deflate
= purple_http_headers_match(
1310 hc
->response
->headers
, "Content-Encoding",
1312 if (is_gzip
|| is_deflate
) {
1313 hc
->gz_stream
= purple_http_gz_new(
1314 hc
->request
->max_length
+ 1,
1318 if (hc
->headers_got
&& hc
->response_buffer
&&
1319 hc
->response_buffer
->len
> 0)
1321 int buffer_len
= hc
->response_buffer
->len
;
1322 gchar
*buffer
= g_string_free(hc
->response_buffer
, FALSE
);
1323 hc
->response_buffer
= NULL
;
1324 if (!_purple_http_recv_body(hc
, buffer
, buffer_len
))
1331 if (!hc
->headers_got
)
1332 return got_anything
;
1336 if (!_purple_http_recv_body(hc
, buf
, len
))
1340 if (hc
->is_chunked
&& hc
->chunks_done
&& hc
->length_expected
< 0)
1341 hc
->length_expected
= hc
->length_got
;
1343 if (hc
->length_expected
>= 0 &&
1344 hc
->length_got
>= (guint
)hc
->length_expected
)
1346 const gchar
*redirect
;
1348 if (hc
->is_chunked
&& !hc
->chunks_done
) {
1350 _purple_http_error(hc
, _("Chunked connection terminated"));
1353 if (purple_debug_is_verbose()) {
1354 purple_debug_misc("http",
1355 "I need the terminating empty chunk\n");
1360 if (!hc
->headers_got
) {
1361 hc
->response
->code
= 0;
1362 purple_debug_warning("http", "No headers got\n");
1363 _purple_http_error(hc
, _("Error parsing HTTP"));
1367 if (purple_debug_is_unsafe() && purple_debug_is_verbose()) {
1368 gchar
*hdrs
= purple_http_headers_dump(
1369 hc
->response
->headers
);
1370 purple_debug_misc("http", "Got response headers: %s\n",
1375 purple_http_cookie_jar_parse(hc
->request
->cookie_jar
,
1376 purple_http_headers_get_all_by_name(
1377 hc
->response
->headers
, "Set-Cookie"));
1379 if (purple_debug_is_unsafe() && purple_debug_is_verbose() &&
1380 !purple_http_cookie_jar_is_empty(
1381 hc
->request
->cookie_jar
))
1383 gchar
*cookies
= purple_http_cookie_jar_dump(
1384 hc
->request
->cookie_jar
);
1385 purple_debug_misc("http", "Cookies: %s\n", cookies
);
1389 if (hc
->response
->code
== 407) {
1390 _purple_http_error(hc
, _("Invalid proxy credentials"));
1394 redirect
= purple_http_headers_get(hc
->response
->headers
,
1396 if (redirect
&& (hc
->request
->max_redirects
== -1 ||
1397 hc
->request
->max_redirects
> hc
->redirects_count
))
1399 PurpleHttpURL
*url
= purple_http_url_parse(redirect
);
1401 hc
->redirects_count
++;
1404 if (purple_debug_is_unsafe())
1405 purple_debug_warning("http",
1406 "Invalid redirect to %s\n",
1409 purple_debug_warning("http",
1410 "Invalid redirect\n");
1411 _purple_http_error(hc
, _("Error parsing HTTP"));
1414 purple_http_url_relative(hc
->url
, url
);
1415 purple_http_url_free(url
);
1417 _purple_http_reconnect(hc
);
1421 _purple_http_disconnect(hc
, TRUE
);
1422 purple_http_connection_terminate(hc
);
1426 return got_anything
;
1429 static gboolean
_purple_http_recv(GObject
*source
, gpointer _hc
)
1431 PurpleHttpConnection
*hc
= _hc
;
1433 while (_purple_http_recv_loopbody(hc
));
1435 return G_SOURCE_CONTINUE
;
1438 static void _purple_http_send_got_data(PurpleHttpConnection
*hc
,
1439 gboolean success
, gboolean eof
, size_t stored
)
1441 int estimated_length
;
1443 g_return_if_fail(hc
!= NULL
);
1446 _purple_http_error(hc
, _("Error requesting data to write"));
1450 hc
->contents_reader_requested
= FALSE
;
1451 g_string_set_size(hc
->contents_reader_buffer
, stored
);
1455 estimated_length
= hc
->request_contents_written
+ stored
;
1457 if (hc
->request
->contents_length
!= -1 &&
1458 hc
->request
->contents_length
!= estimated_length
)
1460 purple_debug_warning("http",
1461 "Invalid amount of data has been written\n");
1463 hc
->request
->contents_length
= estimated_length
;
1466 static gboolean
_purple_http_send(GObject
*source
, gpointer _hc
)
1468 PurpleHttpConnection
*hc
= _hc
;
1469 int written
, write_len
;
1470 const gchar
*write_from
;
1471 gboolean writing_headers
;
1472 GError
*error
= NULL
;
1475 /* Waiting for data. This could be written more efficiently, by removing
1476 * (and later, adding) hs->inpa. */
1477 if (hc
->contents_reader_requested
)
1478 return G_SOURCE_CONTINUE
;
1480 _purple_http_gen_headers(hc
);
1483 (hc
->request_header_written
< hc
->request_header
->len
);
1484 if (writing_headers
) {
1485 write_from
= hc
->request_header
->str
+
1486 hc
->request_header_written
;
1487 write_len
= hc
->request_header
->len
-
1488 hc
->request_header_written
;
1489 } else if (hc
->request
->contents_reader
) {
1490 if (hc
->contents_reader_requested
)
1491 return G_SOURCE_CONTINUE
; /* waiting for data */
1492 if (!hc
->contents_reader_buffer
)
1493 hc
->contents_reader_buffer
= g_string_new("");
1494 if (hc
->contents_reader_buffer
->len
== 0) {
1495 hc
->contents_reader_requested
= TRUE
;
1496 g_string_set_size(hc
->contents_reader_buffer
,
1497 PURPLE_HTTP_MAX_READ_BUFFER_LEN
);
1498 hc
->request
->contents_reader(hc
,
1499 hc
->contents_reader_buffer
->str
,
1500 hc
->request_contents_written
,
1501 PURPLE_HTTP_MAX_READ_BUFFER_LEN
,
1502 hc
->request
->contents_reader_data
,
1503 _purple_http_send_got_data
);
1504 return G_SOURCE_CONTINUE
;
1506 write_from
= hc
->contents_reader_buffer
->str
;
1507 write_len
= hc
->contents_reader_buffer
->len
;
1509 write_from
= hc
->request
->contents
+
1510 hc
->request_contents_written
;
1511 write_len
= hc
->request
->contents_length
-
1512 hc
->request_contents_written
;
1515 if (write_len
== 0) {
1516 purple_debug_warning("http", "Nothing to write\n");
1519 written
= g_pollable_output_stream_write_nonblocking(
1520 G_POLLABLE_OUTPUT_STREAM(
1521 g_io_stream_get_output_stream(
1522 G_IO_STREAM(hc
->socket
->conn
))),
1523 write_from
, write_len
, hc
->socket
->cancellable
,
1527 if (written
< 0 && (g_error_matches(error
,
1528 G_IO_ERROR
, G_IO_ERROR_WOULD_BLOCK
) ||
1529 g_error_matches(error
,
1530 G_IO_ERROR
, G_IO_ERROR_CANCELLED
))) {
1531 g_clear_error(&error
);
1532 return G_SOURCE_CONTINUE
;
1536 if (hc
->request_header_written
== 0 &&
1537 hc
->socket
->use_count
> 1)
1539 purple_debug_info("http", "Keep-alive connection "
1540 "expired (when writing), retrying...\n");
1541 purple_http_conn_retry(hc
);
1543 _purple_http_error(hc
, _("Error writing to %s: %s"),
1544 hc
->url
->host
, error
->message
);
1547 g_clear_error(&error
);
1548 return G_SOURCE_CONTINUE
;
1551 if (writing_headers
) {
1552 hc
->request_header_written
+= written
;
1553 purple_http_conn_notify_progress_watcher(hc
);
1554 if (hc
->request_header_written
< hc
->request_header
->len
)
1555 return G_SOURCE_CONTINUE
;
1556 if (hc
->request
->contents_length
> 0)
1557 return G_SOURCE_CONTINUE
;
1559 hc
->request_contents_written
+= written
;
1560 purple_http_conn_notify_progress_watcher(hc
);
1561 if (hc
->contents_reader_buffer
)
1562 g_string_erase(hc
->contents_reader_buffer
, 0, written
);
1563 if (hc
->request
->contents_length
> 0 &&
1564 hc
->request_contents_written
<
1565 (guint
)hc
->request
->contents_length
)
1567 return G_SOURCE_CONTINUE
;
1571 /* request is completely written, let's read the response */
1572 hc
->is_reading
= TRUE
;
1573 gsource
= g_pollable_input_stream_create_source(
1574 G_POLLABLE_INPUT_STREAM(
1575 g_io_stream_get_input_stream(
1576 G_IO_STREAM(hc
->socket
->conn
))),
1578 g_source_set_callback(gsource
,
1579 (GSourceFunc
)_purple_http_recv
, hc
, NULL
);
1580 hc
->socket
->input_source
= g_source_attach(gsource
, NULL
);
1581 g_source_unref(gsource
);
1583 hc
->socket
->output_source
= 0;
1584 return G_SOURCE_REMOVE
;
1587 static void _purple_http_disconnect(PurpleHttpConnection
*hc
,
1588 gboolean is_graceful
)
1590 g_return_if_fail(hc
!= NULL
);
1592 if (hc
->request_header
)
1593 g_string_free(hc
->request_header
, TRUE
);
1594 hc
->request_header
= NULL
;
1596 if (hc
->response_buffer
)
1597 g_string_free(hc
->response_buffer
, TRUE
);
1598 hc
->response_buffer
= NULL
;
1601 purple_http_gz_free(hc
->gz_stream
);
1602 hc
->gz_stream
= NULL
;
1604 if (hc
->socket_request
)
1605 purple_http_keepalive_pool_request_cancel(hc
->socket_request
);
1607 purple_http_keepalive_pool_release(hc
->socket
, !is_graceful
);
1613 _purple_http_connected(PurpleHttpSocket
*hs
, const gchar
*error
, gpointer _hc
)
1615 PurpleHttpConnection
*hc
= _hc
;
1618 hc
->socket_request
= NULL
;
1621 if (error
!= NULL
) {
1622 _purple_http_error(hc
, _("Unable to connect to %s: %s"),
1623 hc
->url
->host
, error
);
1627 source
= g_pollable_output_stream_create_source(
1628 G_POLLABLE_OUTPUT_STREAM(
1629 g_io_stream_get_output_stream(G_IO_STREAM(hs
->conn
))),
1631 g_source_set_callback(source
,
1632 (GSourceFunc
)_purple_http_send
, hc
, NULL
);
1633 hc
->socket
->output_source
= g_source_attach(source
, NULL
);
1634 g_source_unref(source
);
1637 static gboolean
_purple_http_reconnect(PurpleHttpConnection
*hc
)
1640 gboolean is_ssl
= FALSE
;
1642 g_return_val_if_fail(hc
!= NULL
, FALSE
);
1643 g_return_val_if_fail(hc
->url
!= NULL
, FALSE
);
1645 _purple_http_disconnect(hc
, TRUE
);
1647 if (purple_debug_is_verbose()) {
1648 if (purple_debug_is_unsafe()) {
1649 gchar
*urlp
= purple_http_url_print(hc
->url
);
1650 purple_debug_misc("http", "Connecting to %s...\n", urlp
);
1653 purple_debug_misc("http", "Connecting to %s...\n",
1658 if (g_strcmp0(url
->protocol
, "") == 0 ||
1659 g_ascii_strcasecmp(url
->protocol
, "http") == 0)
1662 } else if (g_ascii_strcasecmp(url
->protocol
, "https") == 0) {
1665 _purple_http_error(hc
, _("Unsupported protocol: %s"),
1670 if (hc
->request
->keepalive_pool
!= NULL
) {
1671 hc
->socket_request
= purple_http_keepalive_pool_request(
1672 hc
->request
->keepalive_pool
, hc
->gc
, url
->host
,
1673 url
->port
, is_ssl
, _purple_http_connected
, hc
);
1675 hc
->socket
= purple_http_socket_connect_new(hc
->gc
, url
->host
,
1676 url
->port
, is_ssl
, _purple_http_connected
, hc
);
1679 if (hc
->socket_request
== NULL
&& hc
->socket
== NULL
) {
1680 _purple_http_error(hc
, _("Unable to connect to %s"), url
->host
);
1684 purple_http_headers_free(hc
->response
->headers
);
1685 hc
->response
->headers
= purple_http_headers_new();
1686 hc
->response_buffer
= g_string_new("");
1687 hc
->main_header_got
= FALSE
;
1688 hc
->headers_got
= FALSE
;
1689 if (hc
->response
->contents
!= NULL
)
1690 g_string_free(hc
->response
->contents
, TRUE
);
1691 hc
->response
->contents
= NULL
;
1693 hc
->length_got_decompressed
= 0;
1694 hc
->length_expected
= -1;
1695 hc
->is_chunked
= FALSE
;
1696 hc
->in_chunk
= FALSE
;
1697 hc
->chunks_done
= FALSE
;
1699 purple_http_conn_notify_progress_watcher(hc
);
1704 /*** Performing HTTP requests *************************************************/
1706 static gboolean
purple_http_request_timeout(gpointer _hc
)
1708 PurpleHttpConnection
*hc
= _hc
;
1710 purple_debug_warning("http", "Timeout reached for request %p\n", hc
);
1712 purple_http_conn_cancel(hc
);
1717 PurpleHttpConnection
* purple_http_get(PurpleConnection
*gc
,
1718 PurpleHttpCallback callback
, gpointer user_data
, const gchar
*url
)
1720 PurpleHttpRequest
*request
;
1721 PurpleHttpConnection
*hc
;
1723 g_return_val_if_fail(url
!= NULL
, NULL
);
1725 request
= purple_http_request_new(url
);
1726 hc
= purple_http_request(gc
, request
, callback
, user_data
);
1727 purple_http_request_unref(request
);
1732 PurpleHttpConnection
* purple_http_get_printf(PurpleConnection
*gc
,
1733 PurpleHttpCallback callback
, gpointer user_data
,
1734 const gchar
*format
, ...)
1738 PurpleHttpConnection
*ret
;
1740 g_return_val_if_fail(format
!= NULL
, NULL
);
1742 va_start(args
, format
);
1743 value
= g_strdup_vprintf(format
, args
);
1746 ret
= purple_http_get(gc
, callback
, user_data
, value
);
1752 PurpleHttpConnection
* purple_http_request(PurpleConnection
*gc
,
1753 PurpleHttpRequest
*request
, PurpleHttpCallback callback
,
1756 PurpleHttpConnection
*hc
;
1758 g_return_val_if_fail(request
!= NULL
, NULL
);
1760 if (request
->url
== NULL
) {
1761 purple_debug_error("http", "Cannot perform new request - "
1762 "URL is not set\n");
1766 if (g_hash_table_lookup(purple_http_cancelling_gc
, gc
)) {
1767 purple_debug_warning("http", "Cannot perform another HTTP "
1768 "request while cancelling all related with this "
1769 "PurpleConnection\n");
1773 hc
= purple_http_connection_new(request
, gc
);
1774 hc
->callback
= callback
;
1775 hc
->user_data
= user_data
;
1777 hc
->url
= purple_http_url_parse(request
->url
);
1779 if (purple_debug_is_unsafe())
1780 purple_debug_misc("http", "Performing new request %p for %s.\n",
1783 purple_debug_misc("http", "Performing new request %p to %s.\n",
1784 hc
, hc
->url
? hc
->url
->host
: NULL
);
1786 if (!hc
->url
|| hc
->url
->host
== NULL
|| hc
->url
->host
[0] == '\0') {
1787 purple_debug_error("http", "Invalid URL requested.\n");
1788 purple_http_connection_terminate(hc
);
1792 _purple_http_reconnect(hc
);
1794 hc
->timeout_handle
= g_timeout_add_seconds(request
->timeout
,
1795 purple_http_request_timeout
, hc
);
1800 /*** HTTP connection API ******************************************************/
1802 static void purple_http_connection_free(PurpleHttpConnection
*hc
);
1803 static gboolean
purple_http_conn_notify_progress_watcher_timeout(gpointer _hc
);
1805 static PurpleHttpConnection
* purple_http_connection_new(
1806 PurpleHttpRequest
*request
, PurpleConnection
*gc
)
1808 PurpleHttpConnection
*hc
= g_new0(PurpleHttpConnection
, 1);
1810 g_assert(request
!= NULL
);
1812 hc
->request
= request
;
1813 purple_http_request_ref(request
);
1814 hc
->response
= purple_http_response_new();
1815 hc
->is_keepalive
= (request
->keepalive_pool
!= NULL
);
1817 hc
->link_global
= purple_http_hc_list
=
1818 g_list_prepend(purple_http_hc_list
, hc
);
1819 g_hash_table_insert(purple_http_hc_by_ptr
, hc
, hc
->link_global
);
1821 GList
*gc_list
= g_hash_table_lookup(purple_http_hc_by_gc
, gc
);
1822 g_hash_table_steal(purple_http_hc_by_gc
, gc
);
1823 hc
->link_gc
= gc_list
= g_list_prepend(gc_list
, hc
);
1824 g_hash_table_insert(purple_http_hc_by_gc
, gc
, gc_list
);
1831 static void purple_http_connection_free(PurpleHttpConnection
*hc
)
1833 if (hc
->timeout_handle
)
1834 g_source_remove(hc
->timeout_handle
);
1835 if (hc
->watcher_delayed_handle
)
1836 g_source_remove(hc
->watcher_delayed_handle
);
1838 if (hc
->connection_set
!= NULL
)
1839 purple_http_connection_set_remove(hc
->connection_set
, hc
);
1841 purple_http_url_free(hc
->url
);
1842 purple_http_request_unref(hc
->request
);
1843 purple_http_response_free(hc
->response
);
1845 if (hc
->contents_reader_buffer
)
1846 g_string_free(hc
->contents_reader_buffer
, TRUE
);
1847 purple_http_gz_free(hc
->gz_stream
);
1849 if (hc
->request_header
)
1850 g_string_free(hc
->request_header
, TRUE
);
1852 purple_http_hc_list
= g_list_delete_link(purple_http_hc_list
,
1854 g_hash_table_remove(purple_http_hc_by_ptr
, hc
);
1856 GList
*gc_list
, *gc_list_new
;
1857 gc_list
= g_hash_table_lookup(purple_http_hc_by_gc
, hc
->gc
);
1858 g_assert(gc_list
!= NULL
);
1860 gc_list_new
= g_list_delete_link(gc_list
, hc
->link_gc
);
1861 if (gc_list
!= gc_list_new
) {
1862 g_hash_table_steal(purple_http_hc_by_gc
, hc
->gc
);
1864 g_hash_table_insert(purple_http_hc_by_gc
,
1865 hc
->gc
, gc_list_new
);
1872 /* call callback and do the cleanup */
1873 static void purple_http_connection_terminate(PurpleHttpConnection
*hc
)
1875 g_return_if_fail(hc
!= NULL
);
1877 purple_debug_misc("http", "Request %p performed %s.\n", hc
,
1878 purple_http_response_is_successful(hc
->response
) ?
1879 "successfully" : "without success");
1882 hc
->callback(hc
, hc
->response
, hc
->user_data
);
1884 purple_http_connection_free(hc
);
1887 void purple_http_conn_cancel(PurpleHttpConnection
*http_conn
)
1889 if (http_conn
== NULL
)
1892 if (http_conn
->is_cancelling
)
1894 http_conn
->is_cancelling
= TRUE
;
1896 if (purple_debug_is_verbose()) {
1897 purple_debug_misc("http", "Cancelling connection %p...\n",
1901 if (http_conn
->response
!= NULL
) {
1902 http_conn
->response
->code
= 0;
1904 _purple_http_disconnect(http_conn
, FALSE
);
1905 purple_http_connection_terminate(http_conn
);
1909 purple_http_conn_retry(PurpleHttpConnection
*http_conn
)
1911 if (http_conn
== NULL
)
1914 purple_debug_info("http", "Retrying connection %p...\n", http_conn
);
1916 if (http_conn
->response
!= NULL
) {
1917 http_conn
->response
->code
= 0;
1919 _purple_http_disconnect(http_conn
, FALSE
);
1920 _purple_http_reconnect(http_conn
);
1923 void purple_http_conn_cancel_all(PurpleConnection
*gc
)
1927 if (purple_debug_is_verbose()) {
1928 purple_debug_misc("http", "Cancelling all running HTTP "
1932 gc_list
= g_hash_table_lookup(purple_http_hc_by_gc
, gc
);
1934 g_hash_table_insert(purple_http_cancelling_gc
, gc
, GINT_TO_POINTER(1));
1937 PurpleHttpConnection
*hc
= gc_list
->data
;
1938 gc_list
= g_list_next(gc_list
);
1939 purple_http_conn_cancel(hc
);
1942 g_hash_table_remove(purple_http_cancelling_gc
, gc
);
1944 if (NULL
!= g_hash_table_lookup(purple_http_hc_by_gc
, gc
))
1945 purple_debug_fatal("http", "Couldn't cancel all connections "
1946 "related to gc=%p (it shouldn't happen)\n", gc
);
1949 gboolean
purple_http_conn_is_running(PurpleHttpConnection
*http_conn
)
1951 if (http_conn
== NULL
)
1953 return (NULL
!= g_hash_table_lookup(purple_http_hc_by_ptr
, http_conn
));
1956 PurpleHttpRequest
* purple_http_conn_get_request(PurpleHttpConnection
*http_conn
)
1958 g_return_val_if_fail(http_conn
!= NULL
, NULL
);
1960 return http_conn
->request
;
1963 PurpleHttpCookieJar
* purple_http_conn_get_cookie_jar(
1964 PurpleHttpConnection
*http_conn
)
1966 return purple_http_request_get_cookie_jar(purple_http_conn_get_request(
1970 PurpleConnection
* purple_http_conn_get_purple_connection(
1971 PurpleHttpConnection
*http_conn
)
1973 g_return_val_if_fail(http_conn
!= NULL
, NULL
);
1975 return http_conn
->gc
;
1978 void purple_http_conn_set_progress_watcher(PurpleHttpConnection
*http_conn
,
1979 PurpleHttpProgressWatcher watcher
, gpointer user_data
,
1980 gint interval_threshold
)
1982 g_return_if_fail(http_conn
!= NULL
);
1984 if (interval_threshold
< 0) {
1985 interval_threshold
=
1986 PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL
;
1989 http_conn
->watcher
= watcher
;
1990 http_conn
->watcher_user_data
= user_data
;
1991 http_conn
->watcher_interval_threshold
= interval_threshold
;
1994 static void purple_http_conn_notify_progress_watcher(
1995 PurpleHttpConnection
*hc
)
1998 gboolean reading_state
;
1999 int processed
, total
;
2001 g_return_if_fail(hc
!= NULL
);
2003 if (hc
->watcher
== NULL
)
2006 reading_state
= hc
->is_reading
;
2007 if (reading_state
) {
2008 total
= hc
->length_expected
;
2009 processed
= hc
->length_got
;
2011 total
= hc
->request
->contents_length
;
2012 processed
= hc
->request_contents_written
;
2016 if (total
!= -1 && total
< processed
) {
2017 purple_debug_warning("http", "Processed too much\n");
2021 now
= g_get_monotonic_time();
2022 if (hc
->watcher_last_call
+ hc
->watcher_interval_threshold
2023 > now
&& processed
!= total
)
2025 if (hc
->watcher_delayed_handle
)
2027 hc
->watcher_delayed_handle
= g_timeout_add_seconds(
2028 1 + hc
->watcher_interval_threshold
/ 1000000,
2029 purple_http_conn_notify_progress_watcher_timeout
, hc
);
2033 if (hc
->watcher_delayed_handle
)
2034 g_source_remove(hc
->watcher_delayed_handle
);
2035 hc
->watcher_delayed_handle
= 0;
2037 hc
->watcher_last_call
= now
;
2038 hc
->watcher(hc
, reading_state
, processed
, total
, hc
->watcher_user_data
);
2041 static gboolean
purple_http_conn_notify_progress_watcher_timeout(gpointer _hc
)
2043 PurpleHttpConnection
*hc
= _hc
;
2045 purple_http_conn_notify_progress_watcher(hc
);
2050 /*** Cookie jar API ***********************************************************/
2052 static PurpleHttpCookie
* purple_http_cookie_new(const gchar
*value
);
2053 void purple_http_cookie_free(PurpleHttpCookie
*cookie
);
2055 static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar
*cookie_jar
,
2056 const gchar
*name
, const gchar
*value
, time_t expires
);
2058 static PurpleHttpCookie
* purple_http_cookie_new(const gchar
*value
)
2060 PurpleHttpCookie
*cookie
= g_new0(PurpleHttpCookie
, 1);
2062 cookie
->value
= g_strdup(value
);
2063 cookie
->expires
= -1;
2068 void purple_http_cookie_free(PurpleHttpCookie
*cookie
)
2070 g_free(cookie
->value
);
2074 void purple_http_cookie_jar_free(PurpleHttpCookieJar
*cookie_jar
);
2076 PurpleHttpCookieJar
* purple_http_cookie_jar_new(void)
2078 PurpleHttpCookieJar
*cjar
= g_new0(PurpleHttpCookieJar
, 1);
2080 cjar
->ref_count
= 1;
2081 cjar
->tab
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
2082 (GDestroyNotify
)purple_http_cookie_free
);
2087 void purple_http_cookie_jar_free(PurpleHttpCookieJar
*cookie_jar
)
2089 g_hash_table_destroy(cookie_jar
->tab
);
2093 void purple_http_cookie_jar_ref(PurpleHttpCookieJar
*cookie_jar
)
2095 g_return_if_fail(cookie_jar
!= NULL
);
2097 cookie_jar
->ref_count
++;
2100 PurpleHttpCookieJar
* purple_http_cookie_jar_unref(
2101 PurpleHttpCookieJar
*cookie_jar
)
2103 if (cookie_jar
== NULL
)
2106 g_return_val_if_fail(cookie_jar
->ref_count
> 0, NULL
);
2108 cookie_jar
->ref_count
--;
2109 if (cookie_jar
->ref_count
> 0)
2112 purple_http_cookie_jar_free(cookie_jar
);
2116 static void purple_http_cookie_jar_parse(PurpleHttpCookieJar
*cookie_jar
,
2119 values
= g_list_first(values
);
2121 const gchar
*cookie
= values
->data
;
2122 const gchar
*eqsign
, *semicolon
;
2123 gchar
*name
, *value
;
2124 time_t expires
= -1;
2125 values
= g_list_next(values
);
2127 eqsign
= strchr(cookie
, '=');
2128 semicolon
= strchr(cookie
, ';');
2130 if (eqsign
== NULL
|| eqsign
== cookie
||
2131 (semicolon
!= NULL
&& semicolon
< eqsign
))
2133 if (purple_debug_is_unsafe())
2134 purple_debug_warning("http",
2135 "Invalid cookie: [%s]\n", cookie
);
2137 purple_debug_warning("http", "Invalid cookie.");
2141 name
= g_strndup(cookie
, eqsign
- cookie
);
2143 if (semicolon
!= NULL
)
2144 value
= g_strndup(eqsign
, semicolon
- eqsign
);
2146 value
= g_strdup(eqsign
);
2148 if (semicolon
!= NULL
) {
2149 GMatchInfo
*match_info
;
2150 GRegex
*re_expires
= g_regex_new( /* XXX: make it static */
2151 "expires=([a-z0-9, :]+)",
2152 G_REGEX_OPTIMIZE
| G_REGEX_CASELESS
,
2153 G_REGEX_MATCH_NOTEMPTY
, NULL
);
2155 g_regex_match(re_expires
, semicolon
, 0, &match_info
);
2156 if (g_match_info_matches(match_info
)) {
2157 gchar
*expire_date
=
2158 g_match_info_fetch(match_info
, 1);
2159 expires
= purple_http_rfc1123_to_time(
2161 g_free(expire_date
);
2163 g_match_info_free(match_info
);
2165 g_regex_unref(re_expires
);
2168 purple_http_cookie_jar_set_ext(cookie_jar
, name
, value
, expires
);
2175 static gchar
* purple_http_cookie_jar_gen(PurpleHttpCookieJar
*cookie_jar
)
2179 PurpleHttpCookie
*cookie
;
2181 time_t now
= time(NULL
);
2183 g_return_val_if_fail(cookie_jar
!= NULL
, NULL
);
2185 str
= g_string_new("");
2187 g_hash_table_iter_init(&it
, cookie_jar
->tab
);
2188 while (g_hash_table_iter_next(&it
, (gpointer
*)&key
,
2189 (gpointer
*)&cookie
))
2191 if (cookie
->expires
!= -1 && cookie
->expires
!= 0 && cookie
->expires
<= now
)
2193 g_string_append_printf(str
, "%s=%s; ", key
, cookie
->value
);
2197 g_string_truncate(str
, str
->len
- 2);
2198 return g_string_free(str
, FALSE
);
2201 void purple_http_cookie_jar_set(PurpleHttpCookieJar
*cookie_jar
,
2202 const gchar
*name
, const gchar
*value
)
2204 gchar
*escaped_name
= g_strdup(purple_url_encode(name
));
2205 gchar
*escaped_value
= NULL
;
2208 escaped_value
= g_strdup(purple_url_encode(value
));
2211 purple_http_cookie_jar_set_ext(cookie_jar
, escaped_name
, escaped_value
, -1);
2213 g_free(escaped_name
);
2214 g_free(escaped_value
);
2217 static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar
*cookie_jar
,
2218 const gchar
*name
, const gchar
*value
, time_t expires
)
2220 g_return_if_fail(cookie_jar
!= NULL
);
2221 g_return_if_fail(name
!= NULL
);
2223 if (expires
!= -1 && expires
!= 0 && time(NULL
) >= expires
)
2226 if (value
!= NULL
) {
2227 PurpleHttpCookie
*cookie
= purple_http_cookie_new(value
);
2228 cookie
->expires
= expires
;
2229 g_hash_table_insert(cookie_jar
->tab
, g_strdup(name
), cookie
);
2231 g_hash_table_remove(cookie_jar
->tab
, name
);
2234 gchar
* purple_http_cookie_jar_get(PurpleHttpCookieJar
*cookie_jar
,
2237 PurpleHttpCookie
*cookie
;
2239 g_return_val_if_fail(cookie_jar
!= NULL
, NULL
);
2240 g_return_val_if_fail(name
!= NULL
, NULL
);
2242 cookie
= g_hash_table_lookup(cookie_jar
->tab
, name
);
2246 return g_strdup(purple_url_decode(cookie
->value
));
2249 gchar
* purple_http_cookie_jar_dump(PurpleHttpCookieJar
*cjar
)
2253 PurpleHttpCookie
*cookie
;
2254 GString
*str
= g_string_new("");
2256 g_hash_table_iter_init(&it
, cjar
->tab
);
2257 while (g_hash_table_iter_next(&it
, (gpointer
*)&key
, (gpointer
*)&cookie
))
2258 g_string_append_printf(str
, "%s: %s (expires: %" G_GINT64_FORMAT
2259 ")\n", key
, cookie
->value
, (gint64
)cookie
->expires
);
2262 g_string_truncate(str
, str
->len
- 1);
2263 return g_string_free(str
, FALSE
);
2266 gboolean
purple_http_cookie_jar_is_empty(PurpleHttpCookieJar
*cookie_jar
)
2268 g_return_val_if_fail(cookie_jar
!= NULL
, TRUE
);
2270 return g_hash_table_size(cookie_jar
->tab
) == 0;
2273 /*** HTTP Keep-Alive pool API *************************************************/
2276 purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost
*host
);
2279 purple_http_keepalive_host_free(gpointer _host
)
2281 PurpleHttpKeepaliveHost
*host
= _host
;
2285 g_slist_free_full(host
->queue
,
2286 (GDestroyNotify
)purple_http_keepalive_pool_request_cancel
);
2287 g_slist_free_full(host
->sockets
,
2288 (GDestroyNotify
)purple_http_socket_close_free
);
2290 if (host
->process_queue_timeout
> 0) {
2291 g_source_remove(host
->process_queue_timeout
);
2292 host
->process_queue_timeout
= 0;
2299 PurpleHttpKeepalivePool
*
2300 purple_http_keepalive_pool_new(void)
2302 PurpleHttpKeepalivePool
*pool
= g_new0(PurpleHttpKeepalivePool
, 1);
2304 pool
->ref_count
= 1;
2305 pool
->by_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
2306 purple_http_keepalive_host_free
);
2312 purple_http_keepalive_pool_free(PurpleHttpKeepalivePool
*pool
)
2314 g_return_if_fail(pool
!= NULL
);
2316 if (pool
->is_destroying
)
2318 pool
->is_destroying
= TRUE
;
2319 g_hash_table_destroy(pool
->by_hash
);
2324 purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool
*pool
)
2326 g_return_if_fail(pool
!= NULL
);
2331 PurpleHttpKeepalivePool
*
2332 purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool
*pool
)
2337 g_return_val_if_fail(pool
->ref_count
> 0, NULL
);
2340 if (pool
->ref_count
> 0)
2343 purple_http_keepalive_pool_free(pool
);
2347 static PurpleHttpKeepaliveRequest
*
2348 purple_http_keepalive_pool_request(PurpleHttpKeepalivePool
*pool
,
2349 PurpleConnection
*gc
, const gchar
*host
, int port
, gboolean is_ssl
,
2350 PurpleHttpSocketConnectCb cb
, gpointer user_data
)
2352 PurpleHttpKeepaliveRequest
*req
;
2353 PurpleHttpKeepaliveHost
*kahost
;
2356 g_return_val_if_fail(pool
!= NULL
, NULL
);
2357 g_return_val_if_fail(host
!= NULL
, NULL
);
2359 if (pool
->is_destroying
) {
2360 purple_debug_error("http", "pool is destroying\n");
2364 hash
= purple_http_socket_hash(host
, port
, is_ssl
);
2365 kahost
= g_hash_table_lookup(pool
->by_hash
, hash
);
2367 if (kahost
== NULL
) {
2368 kahost
= g_new0(PurpleHttpKeepaliveHost
, 1);
2369 kahost
->pool
= pool
;
2370 kahost
->host
= g_strdup(host
);
2371 kahost
->port
= port
;
2372 kahost
->is_ssl
= is_ssl
;
2374 g_hash_table_insert(pool
->by_hash
, g_strdup(hash
), kahost
);
2379 req
= g_new0(PurpleHttpKeepaliveRequest
, 1);
2382 req
->user_data
= user_data
;
2385 kahost
->queue
= g_slist_append(kahost
->queue
, req
);
2387 purple_http_keepalive_host_process_queue(kahost
);
2393 _purple_http_keepalive_socket_connected(PurpleHttpSocket
*hs
,
2394 const gchar
*error
, gpointer _req
)
2396 PurpleHttpKeepaliveRequest
*req
= _req
;
2401 req
->cb(hs
, error
, req
->user_data
);
2406 _purple_http_keepalive_host_process_queue_cb(gpointer _host
)
2408 PurpleHttpKeepaliveRequest
*req
;
2409 PurpleHttpKeepaliveHost
*host
= _host
;
2410 PurpleHttpSocket
*hs
= NULL
;
2412 guint sockets_count
;
2414 g_return_val_if_fail(host
!= NULL
, FALSE
);
2416 host
->process_queue_timeout
= 0;
2418 if (host
->queue
== NULL
)
2423 while (it
!= NULL
) {
2424 PurpleHttpSocket
*hs_current
= it
->data
;
2428 if (!hs_current
->is_busy
) {
2433 it
= g_slist_next(it
);
2436 /* There are no free sockets and we cannot create another one. */
2437 if (hs
== NULL
&& sockets_count
>= host
->pool
->limit_per_host
&&
2438 host
->pool
->limit_per_host
> 0)
2443 req
= host
->queue
->data
;
2444 host
->queue
= g_slist_remove(host
->queue
, req
);
2447 if (purple_debug_is_verbose()) {
2448 purple_debug_misc("http", "locking a (previously used) "
2449 "socket: %p\n", hs
);
2455 purple_http_keepalive_host_process_queue(host
);
2457 req
->cb(hs
, NULL
, req
->user_data
);
2463 hs
= purple_http_socket_connect_new(req
->gc
, req
->host
->host
,
2464 req
->host
->port
, req
->host
->is_ssl
,
2465 _purple_http_keepalive_socket_connected
, req
);
2467 purple_debug_error("http", "failed creating new socket");
2475 if (purple_debug_is_verbose())
2476 purple_debug_misc("http", "locking a (new) socket: %p\n", hs
);
2478 host
->sockets
= g_slist_append(host
->sockets
, hs
);
2484 purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost
*host
)
2486 g_return_if_fail(host
!= NULL
);
2488 if (host
->process_queue_timeout
> 0)
2491 host
->process_queue_timeout
= g_timeout_add(0,
2492 _purple_http_keepalive_host_process_queue_cb
, host
);
2496 purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest
*req
)
2501 if (req
->host
!= NULL
)
2502 req
->host
->queue
= g_slist_remove(req
->host
->queue
, req
);
2504 if (req
->hs
!= NULL
) {
2505 if (G_LIKELY(req
->host
)) {
2506 req
->host
->sockets
= g_slist_remove(req
->host
->sockets
,
2509 purple_http_socket_close_free(req
->hs
);
2510 /* req should already be free'd here */
2512 req
->cb(NULL
, _("Cancelled"), req
->user_data
);
2518 purple_http_keepalive_pool_release(PurpleHttpSocket
*hs
, gboolean invalidate
)
2520 PurpleHttpKeepaliveHost
*host
;
2525 if (purple_debug_is_verbose())
2526 purple_debug_misc("http", "releasing a socket: %p\n", hs
);
2528 if (hs
->input_source
> 0) {
2529 g_source_remove(hs
->input_source
);
2530 hs
->input_source
= 0;
2533 if (hs
->output_source
> 0) {
2534 g_source_remove(hs
->output_source
);
2535 hs
->output_source
= 0;
2538 hs
->is_busy
= FALSE
;
2542 purple_http_socket_close_free(hs
);
2547 host
->sockets
= g_slist_remove(host
->sockets
, hs
);
2548 purple_http_socket_close_free(hs
);
2551 purple_http_keepalive_host_process_queue(host
);
2555 purple_http_keepalive_pool_set_limit_per_host(PurpleHttpKeepalivePool
*pool
,
2558 g_return_if_fail(pool
!= NULL
);
2560 pool
->limit_per_host
= limit
;
2564 purple_http_keepalive_pool_get_limit_per_host(PurpleHttpKeepalivePool
*pool
)
2566 g_return_val_if_fail(pool
!= NULL
, 0);
2568 return pool
->limit_per_host
;
2571 /*** HTTP connection set API **************************************************/
2573 PurpleHttpConnectionSet
*
2574 purple_http_connection_set_new(void)
2576 PurpleHttpConnectionSet
*set
;
2578 set
= g_new0(PurpleHttpConnectionSet
, 1);
2579 set
->connections
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
2585 purple_http_connection_set_destroy(PurpleHttpConnectionSet
*set
)
2590 set
->is_destroying
= TRUE
;
2593 GHashTableIter iter
;
2594 PurpleHttpConnection
*http_conn
;
2596 g_hash_table_iter_init(&iter
, set
->connections
);
2597 if (!g_hash_table_iter_next(&iter
, (gpointer
*)&http_conn
, NULL
))
2600 purple_http_conn_cancel(http_conn
);
2603 g_hash_table_destroy(set
->connections
);
2608 purple_http_connection_set_add(PurpleHttpConnectionSet
*set
,
2609 PurpleHttpConnection
*http_conn
)
2611 if (set
->is_destroying
)
2613 if (http_conn
->connection_set
== set
)
2615 if (http_conn
->connection_set
!= NULL
) {
2616 purple_http_connection_set_remove(http_conn
->connection_set
,
2619 g_hash_table_insert(set
->connections
, http_conn
, GINT_TO_POINTER(1));
2620 http_conn
->connection_set
= set
;
2624 purple_http_connection_set_remove(PurpleHttpConnectionSet
*set
,
2625 PurpleHttpConnection
*http_conn
)
2627 g_hash_table_remove(set
->connections
, http_conn
);
2628 if (http_conn
->connection_set
== set
)
2629 http_conn
->connection_set
= NULL
;
2632 /*** Request API **************************************************************/
2634 static void purple_http_request_free(PurpleHttpRequest
*request
);
2636 PurpleHttpRequest
* purple_http_request_new(const gchar
*url
)
2638 PurpleHttpRequest
*request
;
2640 request
= g_new0(PurpleHttpRequest
, 1);
2642 request
->ref_count
= 1;
2643 request
->url
= g_strdup(url
);
2644 request
->headers
= purple_http_headers_new();
2645 request
->cookie_jar
= purple_http_cookie_jar_new();
2646 request
->keepalive_pool
= purple_http_keepalive_pool_new();
2648 request
->timeout
= PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT
;
2649 request
->max_redirects
= PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS
;
2650 request
->http11
= TRUE
;
2651 request
->max_length
= PURPLE_HTTP_REQUEST_DEFAULT_MAX_LENGTH
;
2656 static void purple_http_request_free(PurpleHttpRequest
*request
)
2658 purple_http_headers_free(request
->headers
);
2659 purple_http_cookie_jar_unref(request
->cookie_jar
);
2660 purple_http_keepalive_pool_unref(request
->keepalive_pool
);
2661 g_free(request
->method
);
2662 g_free(request
->contents
);
2663 g_free(request
->url
);
2667 void purple_http_request_ref(PurpleHttpRequest
*request
)
2669 g_return_if_fail(request
!= NULL
);
2671 request
->ref_count
++;
2674 PurpleHttpRequest
* purple_http_request_unref(PurpleHttpRequest
*request
)
2676 if (request
== NULL
)
2679 g_return_val_if_fail(request
->ref_count
> 0, NULL
);
2681 request
->ref_count
--;
2682 if (request
->ref_count
> 0)
2685 purple_http_request_free(request
);
2689 void purple_http_request_set_url(PurpleHttpRequest
*request
, const gchar
*url
)
2691 g_return_if_fail(request
!= NULL
);
2692 g_return_if_fail(url
!= NULL
);
2694 g_free(request
->url
);
2695 request
->url
= g_strdup(url
);
2698 void purple_http_request_set_url_printf(PurpleHttpRequest
*request
,
2699 const gchar
*format
, ...)
2704 g_return_if_fail(request
!= NULL
);
2705 g_return_if_fail(format
!= NULL
);
2707 va_start(args
, format
);
2708 value
= g_strdup_vprintf(format
, args
);
2711 purple_http_request_set_url(request
, value
);
2715 const gchar
* purple_http_request_get_url(PurpleHttpRequest
*request
)
2717 g_return_val_if_fail(request
!= NULL
, NULL
);
2719 return request
->url
;
2722 void purple_http_request_set_method(PurpleHttpRequest
*request
, const gchar
*method
)
2724 g_return_if_fail(request
!= NULL
);
2726 g_free(request
->method
);
2727 request
->method
= g_strdup(method
);
2730 const gchar
* purple_http_request_get_method(PurpleHttpRequest
*request
)
2732 g_return_val_if_fail(request
!= NULL
, NULL
);
2734 return request
->method
;
2737 static gboolean
purple_http_request_is_method(PurpleHttpRequest
*request
,
2738 const gchar
*method
)
2740 const gchar
*rmethod
;
2742 g_return_val_if_fail(request
!= NULL
, FALSE
);
2743 g_return_val_if_fail(method
!= NULL
, FALSE
);
2745 rmethod
= purple_http_request_get_method(request
);
2746 if (rmethod
== NULL
)
2747 return (g_ascii_strcasecmp(method
, "get") == 0);
2748 return (g_ascii_strcasecmp(method
, rmethod
) == 0);
2752 purple_http_request_set_keepalive_pool(PurpleHttpRequest
*request
,
2753 PurpleHttpKeepalivePool
*pool
)
2755 g_return_if_fail(request
!= NULL
);
2758 purple_http_keepalive_pool_ref(pool
);
2760 if (request
->keepalive_pool
!= NULL
) {
2761 purple_http_keepalive_pool_unref(request
->keepalive_pool
);
2762 request
->keepalive_pool
= NULL
;
2766 request
->keepalive_pool
= pool
;
2769 PurpleHttpKeepalivePool
*
2770 purple_http_request_get_keepalive_pool(PurpleHttpRequest
*request
)
2772 g_return_val_if_fail(request
!= NULL
, FALSE
);
2774 return request
->keepalive_pool
;
2777 void purple_http_request_set_contents(PurpleHttpRequest
*request
,
2778 const gchar
*contents
, int length
)
2780 g_return_if_fail(request
!= NULL
);
2781 g_return_if_fail(length
>= -1);
2783 request
->contents_reader
= NULL
;
2784 request
->contents_reader_data
= NULL
;
2786 g_free(request
->contents
);
2787 if (contents
== NULL
|| length
== 0) {
2788 request
->contents
= NULL
;
2789 request
->contents_length
= 0;
2794 length
= strlen(contents
);
2795 request
->contents
= g_memdup(contents
, length
);
2796 request
->contents_length
= length
;
2799 void purple_http_request_set_contents_reader(PurpleHttpRequest
*request
,
2800 PurpleHttpContentReader reader
, int contents_length
, gpointer user_data
)
2802 g_return_if_fail(request
!= NULL
);
2803 g_return_if_fail(reader
!= NULL
);
2804 g_return_if_fail(contents_length
>= -1);
2806 g_free(request
->contents
);
2807 request
->contents
= NULL
;
2808 request
->contents_length
= contents_length
;
2809 request
->contents_reader
= reader
;
2810 request
->contents_reader_data
= user_data
;
2813 void purple_http_request_set_response_writer(PurpleHttpRequest
*request
,
2814 PurpleHttpContentWriter writer
, gpointer user_data
)
2816 g_return_if_fail(request
!= NULL
);
2820 request
->response_writer
= writer
;
2821 request
->response_writer_data
= user_data
;
2824 void purple_http_request_set_timeout(PurpleHttpRequest
*request
, int timeout
)
2826 g_return_if_fail(request
!= NULL
);
2831 request
->timeout
= timeout
;
2834 int purple_http_request_get_timeout(PurpleHttpRequest
*request
)
2836 g_return_val_if_fail(request
!= NULL
, -1);
2838 return request
->timeout
;
2841 void purple_http_request_set_max_redirects(PurpleHttpRequest
*request
,
2844 g_return_if_fail(request
!= NULL
);
2846 if (max_redirects
< -1)
2849 request
->max_redirects
= max_redirects
;
2852 int purple_http_request_get_max_redirects(PurpleHttpRequest
*request
)
2854 g_return_val_if_fail(request
!= NULL
, -1);
2856 return request
->max_redirects
;
2859 void purple_http_request_set_cookie_jar(PurpleHttpRequest
*request
,
2860 PurpleHttpCookieJar
*cookie_jar
)
2862 g_return_if_fail(request
!= NULL
);
2863 g_return_if_fail(cookie_jar
!= NULL
);
2865 purple_http_cookie_jar_ref(cookie_jar
);
2866 purple_http_cookie_jar_unref(request
->cookie_jar
);
2867 request
->cookie_jar
= cookie_jar
;
2870 PurpleHttpCookieJar
* purple_http_request_get_cookie_jar(
2871 PurpleHttpRequest
*request
)
2873 g_return_val_if_fail(request
!= NULL
, NULL
);
2875 return request
->cookie_jar
;
2878 void purple_http_request_set_http11(PurpleHttpRequest
*request
, gboolean http11
)
2880 g_return_if_fail(request
!= NULL
);
2882 request
->http11
= http11
;
2885 gboolean
purple_http_request_is_http11(PurpleHttpRequest
*request
)
2887 g_return_val_if_fail(request
!= NULL
, FALSE
);
2889 return request
->http11
;
2892 void purple_http_request_set_max_len(PurpleHttpRequest
*request
, int max_len
)
2894 g_return_if_fail(request
!= NULL
);
2896 if (max_len
< 0 || max_len
> PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH
)
2897 max_len
= PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH
;
2899 request
->max_length
= max_len
;
2902 int purple_http_request_get_max_len(PurpleHttpRequest
*request
)
2904 g_return_val_if_fail(request
!= NULL
, -1);
2906 return request
->max_length
;
2909 void purple_http_request_header_set(PurpleHttpRequest
*request
,
2910 const gchar
*key
, const gchar
*value
)
2912 g_return_if_fail(request
!= NULL
);
2913 g_return_if_fail(key
!= NULL
);
2915 purple_http_headers_remove(request
->headers
, key
);
2917 purple_http_headers_add(request
->headers
, key
, value
);
2920 void purple_http_request_header_set_printf(PurpleHttpRequest
*request
,
2921 const gchar
*key
, const gchar
*format
, ...)
2926 g_return_if_fail(request
!= NULL
);
2927 g_return_if_fail(key
!= NULL
);
2928 g_return_if_fail(format
!= NULL
);
2930 va_start(args
, format
);
2931 value
= g_strdup_vprintf(format
, args
);
2934 purple_http_request_header_set(request
, key
, value
);
2938 void purple_http_request_header_add(PurpleHttpRequest
*request
,
2939 const gchar
*key
, const gchar
*value
)
2941 g_return_if_fail(request
!= NULL
);
2942 g_return_if_fail(key
!= NULL
);
2944 purple_http_headers_add(request
->headers
, key
, value
);
2947 /*** HTTP response API ********************************************************/
2949 static PurpleHttpResponse
* purple_http_response_new(void)
2951 PurpleHttpResponse
*response
= g_new0(PurpleHttpResponse
, 1);
2956 static void purple_http_response_free(PurpleHttpResponse
*response
)
2958 if (response
->contents
!= NULL
)
2959 g_string_free(response
->contents
, TRUE
);
2960 g_free(response
->error
);
2961 purple_http_headers_free(response
->headers
);
2965 gboolean
purple_http_response_is_successful(PurpleHttpResponse
*response
)
2969 g_return_val_if_fail(response
!= NULL
, FALSE
);
2971 code
= response
->code
;
2976 /* TODO: HTTP/1.1 100 Continue */
2978 if (code
/ 100 == 2)
2984 int purple_http_response_get_code(PurpleHttpResponse
*response
)
2986 g_return_val_if_fail(response
!= NULL
, 0);
2988 return response
->code
;
2991 const gchar
* purple_http_response_get_error(PurpleHttpResponse
*response
)
2993 g_return_val_if_fail(response
!= NULL
, NULL
);
2995 if (response
->error
!= NULL
)
2996 return response
->error
;
2998 if (!purple_http_response_is_successful(response
)) {
2999 static gchar errmsg
[200];
3000 if (response
->code
<= 0) {
3001 g_snprintf(errmsg
, sizeof(errmsg
),
3002 _("Unknown HTTP error"));
3004 g_snprintf(errmsg
, sizeof(errmsg
),
3005 _("Invalid HTTP response code (%d)"),
3014 gsize
purple_http_response_get_data_len(PurpleHttpResponse
*response
)
3016 g_return_val_if_fail(response
!= NULL
, 0);
3018 if (response
->contents
== NULL
)
3021 return response
->contents
->len
;
3024 const gchar
* purple_http_response_get_data(PurpleHttpResponse
*response
, size_t *len
)
3026 const gchar
*ret
= "";
3028 g_return_val_if_fail(response
!= NULL
, "");
3030 if (response
->contents
!= NULL
) {
3031 ret
= response
->contents
->str
;
3033 *len
= response
->contents
->len
;
3042 const GList
* purple_http_response_get_all_headers(PurpleHttpResponse
*response
)
3044 g_return_val_if_fail(response
!= NULL
, NULL
);
3046 return purple_http_headers_get_all(response
->headers
);
3049 const GList
* purple_http_response_get_headers_by_name(
3050 PurpleHttpResponse
*response
, const gchar
*name
)
3052 g_return_val_if_fail(response
!= NULL
, NULL
);
3053 g_return_val_if_fail(name
!= NULL
, NULL
);
3055 return purple_http_headers_get_all_by_name(response
->headers
, name
);
3058 const gchar
* purple_http_response_get_header(PurpleHttpResponse
*response
,
3061 g_return_val_if_fail(response
!= NULL
, NULL
);
3062 g_return_val_if_fail(name
!= NULL
, NULL
);
3064 return purple_http_headers_get(response
->headers
, name
);
3067 /*** URL functions ************************************************************/
3070 purple_http_url_parse(const char *raw_url
)
3073 GMatchInfo
*match_info
;
3075 gchar
*host_full
, *tmp
;
3077 g_return_val_if_fail(raw_url
!= NULL
, NULL
);
3079 if (!g_regex_match(purple_http_re_url
, raw_url
, 0, &match_info
)) {
3080 if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
3081 purple_debug_warning("http",
3082 "Invalid URL provided: %s\n",
3088 url
= g_new0(PurpleHttpURL
, 1);
3090 url
->protocol
= g_match_info_fetch(match_info
, 1);
3091 host_full
= g_match_info_fetch(match_info
, 2);
3092 url
->path
= g_match_info_fetch(match_info
, 3);
3093 url
->fragment
= g_match_info_fetch(match_info
, 4);
3094 g_match_info_free(match_info
);
3096 if (g_strcmp0(url
->protocol
, "") == 0) {
3097 g_free(url
->protocol
);
3098 url
->protocol
= NULL
;
3099 } else if (url
->protocol
!= NULL
) {
3100 tmp
= url
->protocol
;
3101 url
->protocol
= g_ascii_strdown(url
->protocol
, -1);
3104 if (host_full
[0] == '\0') {
3108 if (url
->path
[0] == '\0') {
3112 if ((url
->protocol
== NULL
) != (host_full
== NULL
))
3113 purple_debug_warning("http", "Protocol or host not present "
3114 "(unlikely case)\n");
3119 if (!g_regex_match(purple_http_re_url_host
, host_full
, 0,
3122 if (purple_debug_is_verbose() &&
3123 purple_debug_is_unsafe())
3125 purple_debug_warning("http",
3126 "Invalid host provided for URL: %s\n",
3131 purple_http_url_free(url
);
3135 url
->username
= g_match_info_fetch(match_info
, 1);
3136 url
->password
= g_match_info_fetch(match_info
, 2);
3137 url
->host
= g_match_info_fetch(match_info
, 3);
3138 port_str
= g_match_info_fetch(match_info
, 4);
3140 if (port_str
&& port_str
[0])
3141 url
->port
= atoi(port_str
);
3143 if (url
->username
[0] == '\0') {
3144 g_free(url
->username
);
3145 url
->username
= NULL
;
3147 if (url
->password
[0] == '\0') {
3148 g_free(url
->password
);
3149 url
->password
= NULL
;
3151 if (g_strcmp0(url
->host
, "") == 0) {
3154 } else if (url
->host
!= NULL
) {
3156 url
->host
= g_ascii_strdown(url
->host
, -1);
3161 g_match_info_free(match_info
);
3167 if (url
->host
!= NULL
) {
3168 if (url
->protocol
== NULL
)
3169 url
->protocol
= g_strdup("http");
3170 if (url
->port
== 0 && 0 == strcmp(url
->protocol
, "http"))
3172 if (url
->port
== 0 && 0 == strcmp(url
->protocol
, "https"))
3174 if (url
->path
== NULL
)
3175 url
->path
= g_strdup("/");
3176 if (url
->path
[0] != '/')
3177 purple_debug_warning("http",
3178 "URL path doesn't start with slash\n");
3185 purple_http_url_free(PurpleHttpURL
*parsed_url
)
3187 if (parsed_url
== NULL
)
3190 g_free(parsed_url
->protocol
);
3191 g_free(parsed_url
->username
);
3192 g_free(parsed_url
->password
);
3193 g_free(parsed_url
->host
);
3194 g_free(parsed_url
->path
);
3195 g_free(parsed_url
->fragment
);
3200 purple_http_url_relative(PurpleHttpURL
*base_url
, PurpleHttpURL
*relative_url
)
3202 g_return_if_fail(base_url
!= NULL
);
3203 g_return_if_fail(relative_url
!= NULL
);
3205 if (relative_url
->host
) {
3206 g_free(base_url
->protocol
);
3207 base_url
->protocol
= g_strdup(relative_url
->protocol
);
3208 g_free(base_url
->username
);
3209 base_url
->username
= g_strdup(relative_url
->username
);
3210 g_free(base_url
->password
);
3211 base_url
->password
= g_strdup(relative_url
->password
);
3212 g_free(base_url
->host
);
3213 base_url
->host
= g_strdup(relative_url
->host
);
3214 base_url
->port
= relative_url
->port
;
3216 g_free(base_url
->path
);
3217 base_url
->path
= NULL
;
3220 if (relative_url
->path
) {
3221 if (relative_url
->path
[0] == '/' ||
3222 base_url
->path
== NULL
)
3224 g_free(base_url
->path
);
3225 base_url
->path
= g_strdup(relative_url
->path
);
3227 gchar
*last_slash
= strrchr(base_url
->path
, '/');
3229 if (last_slash
== NULL
)
3230 base_url
->path
[0] = '\0';
3232 last_slash
[1] = '\0';
3233 tmp
= base_url
->path
;
3234 base_url
->path
= g_strconcat(base_url
->path
,
3235 relative_url
->path
, NULL
);
3240 g_free(base_url
->fragment
);
3241 base_url
->fragment
= g_strdup(relative_url
->fragment
);
3245 purple_http_url_print(PurpleHttpURL
*parsed_url
)
3247 GString
*url
= g_string_new("");
3248 gboolean before_host_printed
= FALSE
, host_printed
= FALSE
;
3249 gboolean port_is_default
= FALSE
;
3251 if (parsed_url
->protocol
) {
3252 g_string_append_printf(url
, "%s://", parsed_url
->protocol
);
3253 before_host_printed
= TRUE
;
3254 if (parsed_url
->port
== 80 && 0 == strcmp(parsed_url
->protocol
,
3256 port_is_default
= TRUE
;
3257 if (parsed_url
->port
== 443 && 0 == strcmp(parsed_url
->protocol
,
3259 port_is_default
= TRUE
;
3261 if (parsed_url
->username
|| parsed_url
->password
) {
3262 if (parsed_url
->username
)
3263 g_string_append(url
, parsed_url
->username
);
3264 g_string_append_c(url
, ':');
3265 if (parsed_url
->password
)
3266 g_string_append(url
, parsed_url
->password
);
3267 g_string_append(url
, "@");
3268 before_host_printed
= TRUE
;
3270 if (parsed_url
->host
|| parsed_url
->port
) {
3271 if (!parsed_url
->host
)
3272 g_string_append_printf(url
, "{???}:%d",
3275 g_string_append(url
, parsed_url
->host
);
3276 if (!port_is_default
)
3277 g_string_append_printf(url
, ":%d",
3280 host_printed
= TRUE
;
3282 if (parsed_url
->path
) {
3283 if (!host_printed
&& before_host_printed
)
3284 g_string_append(url
, "{???}");
3285 g_string_append(url
, parsed_url
->path
);
3287 if (parsed_url
->fragment
)
3288 g_string_append_printf(url
, "#%s", parsed_url
->fragment
);
3290 return g_string_free(url
, FALSE
);
3294 purple_http_url_get_protocol(const PurpleHttpURL
*parsed_url
)
3296 g_return_val_if_fail(parsed_url
!= NULL
, NULL
);
3298 return parsed_url
->protocol
;
3302 purple_http_url_get_username(const PurpleHttpURL
*parsed_url
)
3304 g_return_val_if_fail(parsed_url
!= NULL
, NULL
);
3306 return parsed_url
->username
;
3310 purple_http_url_get_password(const PurpleHttpURL
*parsed_url
)
3312 g_return_val_if_fail(parsed_url
!= NULL
, NULL
);
3314 return parsed_url
->password
;
3318 purple_http_url_get_host(const PurpleHttpURL
*parsed_url
)
3320 g_return_val_if_fail(parsed_url
!= NULL
, NULL
);
3322 return parsed_url
->host
;
3326 purple_http_url_get_port(const PurpleHttpURL
*parsed_url
)
3328 g_return_val_if_fail(parsed_url
!= NULL
, 0);
3330 return parsed_url
->port
;
3334 purple_http_url_get_path(const PurpleHttpURL
*parsed_url
)
3336 g_return_val_if_fail(parsed_url
!= NULL
, NULL
);
3338 return parsed_url
->path
;
3342 purple_http_url_get_fragment(const PurpleHttpURL
*parsed_url
)
3344 g_return_val_if_fail(parsed_url
!= NULL
, NULL
);
3346 return parsed_url
->fragment
;
3349 /*** HTTP Subsystem ***********************************************************/
3351 void purple_http_init(void)
3353 purple_http_re_url
= g_regex_new("^"
3355 "(?:" /* host part beginning */
3356 "([a-z]+)\\:/*" /* protocol */
3357 "([^/]+)" /* username, password, host, port */
3358 ")?" /* host part ending */
3360 "([^#]*)" /* path */
3362 "(?:#" "(.*)" ")?" /* fragment */
3364 "$", G_REGEX_OPTIMIZE
| G_REGEX_CASELESS
,
3365 G_REGEX_MATCH_NOTEMPTY
, NULL
);
3367 purple_http_re_url_host
= g_regex_new("^"
3369 "(?:" /* user credentials part beginning */
3370 "([" PURPLE_HTTP_URL_CREDENTIALS_CHARS
"]+)" /* username */
3371 "(?::([" PURPLE_HTTP_URL_CREDENTIALS_CHARS
"]+))" /* password */
3372 "@)?" /* user credentials part ending */
3374 "([a-z0-9.-]+)" /* host */
3375 "(?::([0-9]+))?" /* port*/
3377 "$", G_REGEX_OPTIMIZE
| G_REGEX_CASELESS
,
3378 G_REGEX_MATCH_NOTEMPTY
, NULL
);
3380 purple_http_re_rfc1123
= g_regex_new(
3381 "^[a-z]+, " /* weekday */
3382 "([0-9]+) " /* date */
3383 "([a-z]+) " /* month */
3384 "([0-9]+) " /* year */
3385 "([0-9]+:[0-9]+:[0-9]+) " /* time */
3387 G_REGEX_OPTIMIZE
| G_REGEX_CASELESS
,
3388 G_REGEX_MATCH_NOTEMPTY
, NULL
);
3390 purple_http_hc_list
= NULL
;
3391 purple_http_hc_by_ptr
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
3392 purple_http_hc_by_gc
= g_hash_table_new_full(g_direct_hash
,
3393 g_direct_equal
, NULL
, (GDestroyNotify
)g_list_free
);
3394 purple_http_cancelling_gc
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
3397 static void purple_http_foreach_conn_cancel(gpointer _hc
, gpointer user_data
)
3399 PurpleHttpConnection
*hc
= _hc
;
3400 purple_http_conn_cancel(hc
);
3403 void purple_http_uninit(void)
3405 g_regex_unref(purple_http_re_url
);
3406 purple_http_re_url
= NULL
;
3407 g_regex_unref(purple_http_re_url_host
);
3408 purple_http_re_url_host
= NULL
;
3409 g_regex_unref(purple_http_re_rfc1123
);
3410 purple_http_re_rfc1123
= NULL
;
3412 if (purple_http_hc_list
!= NULL
) {
3413 g_list_foreach(purple_http_hc_list
, purple_http_foreach_conn_cancel
, NULL
);
3416 if (purple_http_hc_list
!= NULL
||
3417 0 != g_hash_table_size(purple_http_hc_by_ptr
) ||
3418 0 != g_hash_table_size(purple_http_hc_by_gc
))
3419 purple_debug_warning("http",
3420 "Couldn't cleanup all connections.\n");
3422 g_list_free(purple_http_hc_list
);
3423 purple_http_hc_list
= NULL
;
3424 g_hash_table_destroy(purple_http_hc_by_gc
);
3425 purple_http_hc_by_gc
= NULL
;
3426 g_hash_table_destroy(purple_http_hc_by_ptr
);
3427 purple_http_hc_by_ptr
= NULL
;
3428 g_hash_table_destroy(purple_http_cancelling_gc
);
3429 purple_http_cancelling_gc
= NULL
;