diff options
author | Gary Kramlich <grim@reaperworld.com> | 2019-10-18 04:19:09 +0000 |
---|---|---|
committer | Gary Kramlich <grim@reaperworld.com> | 2019-10-18 04:19:09 +0000 |
commit | f1ee3ee3aca53c41988dddb455d2346c8fce1ab1 (patch) | |
tree | 713e56270cdff6266f27b927115e0d41dd2a52f7 | |
parent | 369dcdfa46502b25fe348c37662eb7b3d4b2e218 (diff) | |
parent | a092bacccc88eb6cbe0378039f4c0221cafa982e (diff) | |
download | pidgin-f1ee3ee3aca53c41988dddb455d2346c8fce1ab1.tar.gz |
Merged in qulogic/pidgin (pull request #607)
Delete HTTP code
Approved-by: Gary Kramlich <grim@reaperworld.com>
26 files changed, 0 insertions, 4444 deletions
diff --git a/ChangeLog.API b/ChangeLog.API index 77b58a2f7c..87dfbbd9bc 100644 --- a/ChangeLog.API +++ b/ChangeLog.API @@ -417,8 +417,6 @@ version 3.0.0 (??/??/????): * PurpleEventLoopUiOps. Manually drive the GLib event loop yourself. See GLib Main Event Loop docs. * purple_get_tzoff_str(). Use g_date_time_format, instead. - * purple_http_digest_calculate_response - * purple_http_digest_calculate_session_key * purple_ip_address_is_valid, purple_ipv4_address_is_valid, and purple_ipv6_address_is_valid. Use g_hostname_is_ip_address() or #GInetAddress instead. diff --git a/doc/reference/libpurple/libpurple-docs.xml b/doc/reference/libpurple/libpurple-docs.xml index 62e7ad15f9..e0a6b22be0 100644 --- a/doc/reference/libpurple/libpurple-docs.xml +++ b/doc/reference/libpurple/libpurple-docs.xml @@ -48,7 +48,6 @@ <xi:include href="xml/eventloop.xml" /> <xi:include href="xml/group.xml" /> <xi:include href="xml/xfer.xml" /> - <xi:include href="xml/http.xml" /> <xi:include href="xml/idle.xml" /> <xi:include href="xml/keyring.xml" /> <xi:include href="xml/memorypool.xml" /> diff --git a/finch/plugins/gnttinyurl.c b/finch/plugins/gnttinyurl.c index 6c04b77ed1..d463cc0ea0 100644 --- a/finch/plugins/gnttinyurl.c +++ b/finch/plugins/gnttinyurl.c @@ -26,7 +26,6 @@ #define PREF_URL PREFS_BASE "/url" #include <conversation.h> -#include <http.h> #include <signals.h> #include <glib.h> diff --git a/libpurple/connection.c b/libpurple/connection.c index ce92243a55..aba689d9fb 100644 --- a/libpurple/connection.c +++ b/libpurple/connection.c @@ -27,7 +27,6 @@ #include "connection.h" #include "debug.h" #include "enums.h" -#include "http.h" #include "log.h" #include "notify.h" #include "prefs.h" @@ -869,7 +868,6 @@ purple_connection_finalize(GObject *object) buddies = g_slist_delete_link(buddies, buddies); } - purple_http_conn_cancel_all(gc); purple_proxy_connect_cancel_with_handle(gc); connections = g_list_remove(connections, gc); diff --git a/libpurple/core.c b/libpurple/core.c index ef5513b895..a59ac139dc 100644 --- a/libpurple/core.c +++ b/libpurple/core.c @@ -26,7 +26,6 @@ #include "debug.h" #include "xfer.h" #include "glibcompat.h" -#include "http.h" #include "idle.h" #include "image-store.h" #include "keyring.h" @@ -177,7 +176,6 @@ purple_core_init(const char *ui) purple_stun_init(); purple_xfers_init(); purple_idle_init(); - purple_http_init(); _purple_smiley_custom_init(); _purple_smiley_parser_init(); @@ -219,7 +217,6 @@ purple_core_quit(void) _purple_smiley_theme_uninit(); _purple_smiley_custom_uninit(); _purple_smiley_parser_uninit(); - purple_http_uninit(); purple_idle_uninit(); purple_pounces_uninit(); purple_conversations_uninit(); diff --git a/libpurple/http.c b/libpurple/http.c deleted file mode 100644 index 03dc2fc34c..0000000000 --- a/libpurple/http.c +++ /dev/null @@ -1,3430 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "http.h" - -#include "internal.h" -#include "glibcompat.h" - - -#include "debug.h" -#include "proxy.h" -#include "purple-gio.h" - -#define PURPLE_HTTP_URL_CREDENTIALS_CHARS "a-z0-9.,~_/*!&%?=+\\^-" -#define PURPLE_HTTP_MAX_RECV_BUFFER_LEN 102400 -#define PURPLE_HTTP_MAX_READ_BUFFER_LEN 102400 -#define PURPLE_HTTP_GZ_BUFF_LEN 1024 - -#define PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS 20 -#define PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT 30 -#define PURPLE_HTTP_REQUEST_DEFAULT_MAX_LENGTH 1048576 -#define PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH (G_MAXINT32 - 1) - -#define PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL 250000 - -#define PURPLE_HTTP_GET_ACCOUNT(gc) (gc ? purple_connection_get_account(gc) : NULL) - -typedef struct _PurpleHttpSocket PurpleHttpSocket; - -typedef struct _PurpleHttpHeaders PurpleHttpHeaders; - -typedef struct _PurpleHttpKeepaliveHost PurpleHttpKeepaliveHost; - -typedef struct _PurpleHttpKeepaliveRequest PurpleHttpKeepaliveRequest; - -typedef struct _PurpleHttpGzStream PurpleHttpGzStream; - -typedef void (*PurpleHttpSocketConnectCb)(PurpleHttpSocket *hs, - const gchar *error, gpointer _hc); - -struct _PurpleHttpSocket -{ - GSocketConnection *conn; - GCancellable *cancellable; - guint input_source; - guint output_source; - - gboolean is_busy; - guint use_count; - PurpleHttpKeepaliveHost *host; -}; - -struct _PurpleHttpRequest -{ - int ref_count; - - gchar *url; - gchar *method; - PurpleHttpHeaders *headers; - PurpleHttpCookieJar *cookie_jar; - PurpleHttpKeepalivePool *keepalive_pool; - - gchar *contents; - int contents_length; - PurpleHttpContentReader contents_reader; - gpointer contents_reader_data; - PurpleHttpContentWriter response_writer; - gpointer response_writer_data; - - int timeout; - int max_redirects; - gboolean http11; - guint max_length; -}; - -struct _PurpleHttpConnection -{ - PurpleConnection *gc; - PurpleHttpCallback callback; - gpointer user_data; - gboolean is_reading; - gboolean is_keepalive; - gboolean is_cancelling; - - PurpleHttpURL *url; - PurpleHttpRequest *request; - PurpleHttpResponse *response; - - PurpleHttpKeepaliveRequest *socket_request; - PurpleHttpConnectionSet *connection_set; - PurpleHttpSocket *socket; - GString *request_header; - guint request_header_written, request_contents_written; - gboolean main_header_got, headers_got; - GString *response_buffer; - PurpleHttpGzStream *gz_stream; - - GString *contents_reader_buffer; - gboolean contents_reader_requested; - - int redirects_count; - - int length_expected; - guint length_got, length_got_decompressed; - - gboolean is_chunked, in_chunk, chunks_done; - int chunk_length, chunk_got; - - GList *link_global, *link_gc; - - guint timeout_handle; - - PurpleHttpProgressWatcher watcher; - gpointer watcher_user_data; - guint watcher_interval_threshold; - gint64 watcher_last_call; - guint watcher_delayed_handle; -}; - -struct _PurpleHttpResponse -{ - int code; - gchar *error; - - GString *contents; - PurpleHttpHeaders *headers; -}; - -struct _PurpleHttpURL -{ - gchar *protocol; - gchar *username; - gchar *password; - gchar *host; - int port; - gchar *path; - gchar *fragment; -}; - -struct _PurpleHttpHeaders -{ - GList *list; - GHashTable *by_name; -}; - -typedef struct -{ - time_t expires; - gchar *value; -} PurpleHttpCookie; - -struct _PurpleHttpCookieJar -{ - int ref_count; - - GHashTable *tab; -}; - -struct _PurpleHttpKeepaliveRequest -{ - PurpleConnection *gc; - PurpleHttpSocketConnectCb cb; - gpointer user_data; - - PurpleHttpKeepaliveHost *host; - PurpleHttpSocket *hs; -}; - -struct _PurpleHttpKeepaliveHost -{ - PurpleHttpKeepalivePool *pool; - - gchar *host; - int port; - gboolean is_ssl; - - GSList *sockets; /* list of PurpleHttpSocket */ - - GSList *queue; /* list of PurpleHttpKeepaliveRequest */ - guint process_queue_timeout; -}; - -struct _PurpleHttpKeepalivePool -{ - gboolean is_destroying; - - int ref_count; - - guint limit_per_host; - - /* key: purple_http_socket_hash, value: PurpleHttpKeepaliveHost */ - GHashTable *by_hash; -}; - -struct _PurpleHttpConnectionSet -{ - gboolean is_destroying; - - GHashTable *connections; -}; - -struct _PurpleHttpGzStream -{ - gboolean failed; - GZlibDecompressor *decompressor; - gsize max_output; - gsize decompressed; - GString *pending; -}; - -struct _ntlm_type1_message { - guint8 protocol[8]; /* 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' */ - guint32 type; /* 0x00000001 */ - guint32 flags; /* 0x0000b203 */ - - guint16 dom_len1; /* domain string length */ - guint16 dom_len2; /* domain string length */ - guint32 dom_off; /* domain string offset */ - - guint16 host_len1; /* host string length */ - guint16 host_len2; /* host string length */ - guint32 host_off; /* host string offset (always 0x00000020) */ -}; - -static time_t purple_http_rfc1123_to_time(const gchar *str); - -static gboolean purple_http_request_is_method(PurpleHttpRequest *request, - const gchar *method); - -static PurpleHttpConnection * purple_http_connection_new( - PurpleHttpRequest *request, PurpleConnection *gc); -static void purple_http_connection_terminate(PurpleHttpConnection *hc); -static void purple_http_conn_notify_progress_watcher(PurpleHttpConnection *hc); -static void -purple_http_conn_retry(PurpleHttpConnection *http_conn); - -static PurpleHttpResponse * purple_http_response_new(void); -static void purple_http_response_free(PurpleHttpResponse *response); - -static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar, - GList *values); -static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar); -gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar); - -static PurpleHttpKeepaliveRequest * -purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool, - PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl, - PurpleHttpSocketConnectCb cb, gpointer user_data); -static void -purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req); -static void -purple_http_keepalive_pool_release(PurpleHttpSocket *hs, gboolean invalidate); - -static void -purple_http_connection_set_remove(PurpleHttpConnectionSet *set, - PurpleHttpConnection *http_conn); - -static GRegex *purple_http_re_url, *purple_http_re_url_host, - *purple_http_re_rfc1123; - -/* - * Values: pointers to running PurpleHttpConnection. - */ -static GList *purple_http_hc_list; - -/* - * Keys: pointers to PurpleConnection. - * Values: GList of pointers to running PurpleHttpConnection. - */ -static GHashTable *purple_http_hc_by_gc; - -/* - * Keys: pointers to PurpleConnection. - * Values: gboolean TRUE. - */ -static GHashTable *purple_http_cancelling_gc; - -/* - * Keys: pointers to PurpleHttpConnection. - * Values: pointers to links in purple_http_hc_list. - */ -static GHashTable *purple_http_hc_by_ptr; - -/*** Helper functions *********************************************************/ - -static time_t purple_http_rfc1123_to_time(const gchar *str) -{ - static const gchar *months[13] = { - "jan", "feb", "mar", "apr", "may", "jun", - "jul", "aug", "sep", "oct", "nov", "dec", NULL - }; - GMatchInfo *match_info; - gchar *d_date, *d_month, *d_year, *d_time; - int month; - gchar *iso_date; - time_t t; - - g_return_val_if_fail(str != NULL, 0); - - g_regex_match(purple_http_re_rfc1123, str, 0, &match_info); - if (!g_match_info_matches(match_info)) { - g_match_info_free(match_info); - return 0; - } - - d_date = g_match_info_fetch(match_info, 1); - d_month = g_match_info_fetch(match_info, 2); - d_year = g_match_info_fetch(match_info, 3); - d_time = g_match_info_fetch(match_info, 4); - - g_match_info_free(match_info); - - month = 0; - while (months[month] != NULL) { - if (0 == g_ascii_strcasecmp(d_month, months[month])) - break; - month++; - } - month++; - - iso_date = g_strdup_printf("%s-%02d-%sT%s+00:00", - d_year, month, d_date, d_time); - - g_free(d_date); - g_free(d_year); - g_free(d_time); - - if (month > 12) { - purple_debug_warning("http", "Invalid month: %s\n", d_month); - g_free(d_month); - g_free(iso_date); - return 0; - } - - g_free(d_month); - - t = purple_str_to_time(iso_date, TRUE, NULL, NULL, NULL); - - g_free(iso_date); - - return t; -} - -/*** GZip streams *************************************************************/ - -static PurpleHttpGzStream * -purple_http_gz_new(gsize max_output, gboolean is_deflate) -{ - PurpleHttpGzStream *gzs = g_new0(PurpleHttpGzStream, 1); - GZlibCompressorFormat format; - - if (is_deflate) - format = G_ZLIB_COMPRESSOR_FORMAT_RAW; - else /* is gzip */ - format = G_ZLIB_COMPRESSOR_FORMAT_GZIP; - - gzs->decompressor = g_zlib_decompressor_new(format); - gzs->max_output = max_output; - - return gzs; -} - -static GString * -purple_http_gz_put(PurpleHttpGzStream *gzs, const gchar *buf, gsize len) -{ - const gchar *compressed_buff; - gsize compressed_len; - GString *ret; - - g_return_val_if_fail(gzs != NULL, NULL); - g_return_val_if_fail(buf != NULL, NULL); - - if (gzs->failed) - return NULL; - - if (gzs->pending) { - g_string_append_len(gzs->pending, buf, len); - compressed_buff = gzs->pending->str; - compressed_len = gzs->pending->len; - } else { - compressed_buff = buf; - compressed_len = len; - } - - ret = g_string_new(NULL); - while (compressed_len > 0) { - GConverterResult gzres; - gchar decompressed_buff[PURPLE_HTTP_GZ_BUFF_LEN]; - gsize decompressed_len = 0; - gsize bytes_read = 0; - GError *error = NULL; - - gzres = g_converter_convert(G_CONVERTER(gzs->decompressor), - compressed_buff, compressed_len, - decompressed_buff, sizeof(decompressed_buff), - G_CONVERTER_NO_FLAGS, - &bytes_read, - &decompressed_len, - &error); - - compressed_buff += bytes_read; - compressed_len -= bytes_read; - - if (gzres == G_CONVERTER_CONVERTED || gzres == G_CONVERTER_FINISHED) { - if (decompressed_len == 0) - break; - if (gzs->decompressed + decompressed_len >= - gzs->max_output) - { - purple_debug_warning("http", "Maximum amount of" - " decompressed data is reached\n"); - decompressed_len = gzs->max_output - - gzs->decompressed; - gzres = G_CONVERTER_FINISHED; - } - gzs->decompressed += decompressed_len; - g_string_append_len(ret, decompressed_buff, - decompressed_len); - if (gzres == G_CONVERTER_FINISHED) - break; - } else { - purple_debug_error("http", - "Decompression failed (%d): %s\n", gzres, - error->message); - g_clear_error(&error); - gzs->failed = TRUE; - g_string_free(ret, TRUE); - return NULL; - } - } - - if (gzs->pending) { - g_string_free(gzs->pending, TRUE); - gzs->pending = NULL; - } - - if (compressed_len > 0) { - gzs->pending = g_string_new_len(compressed_buff, - compressed_len); - } - - return ret; -} - -static void -purple_http_gz_free(PurpleHttpGzStream *gzs) -{ - if (gzs == NULL) - return; - g_object_unref(gzs->decompressor); - if (gzs->pending) - g_string_free(gzs->pending, TRUE); - g_free(gzs); -} - -/*** NTLM *********************************************************************/ - -/** - * purple_ntlm_gen_type1: - * @hostname: Your hostname - * @domain: The domain to authenticate to - * - * Generates the base64 encoded type 1 message needed for NTLM authentication - * - * Returns: base64 encoded string to send to the server. This should - * be g_free'd by the caller. - */ -static gchar * -purple_http_ntlm_gen_type1(const gchar *hostname, const gchar *domain) -{ - int hostnamelen,host_off; - int domainlen,dom_off; - unsigned char *msg; - struct _ntlm_type1_message *tmsg; - gchar *tmp; - - hostnamelen = strlen(hostname); - domainlen = strlen(domain); - host_off = sizeof(struct _ntlm_type1_message); - dom_off = sizeof(struct _ntlm_type1_message) + hostnamelen; - msg = g_malloc0(sizeof(struct _ntlm_type1_message) + hostnamelen + domainlen); - tmsg = (struct _ntlm_type1_message*)(gpointer)msg; - tmsg->protocol[0] = 'N'; - tmsg->protocol[1] = 'T'; - tmsg->protocol[2] = 'L'; - tmsg->protocol[3] = 'M'; - tmsg->protocol[4] = 'S'; - tmsg->protocol[5] = 'S'; - tmsg->protocol[6] = 'P'; - tmsg->protocol[7] = '\0'; - tmsg->type = GUINT32_TO_LE(0x00000001); - tmsg->flags = GUINT32_TO_LE(0x0000b203); - tmsg->dom_len1 = tmsg->dom_len2 = GUINT16_TO_LE(domainlen); - tmsg->dom_off = GUINT32_TO_LE(dom_off); - tmsg->host_len1 = tmsg->host_len2 = GUINT16_TO_LE(hostnamelen); - tmsg->host_off = GUINT32_TO_LE(host_off); - memcpy(msg + host_off, hostname, hostnamelen); - memcpy(msg + dom_off, domain, domainlen); - - tmp = g_base64_encode(msg, sizeof(struct _ntlm_type1_message) + hostnamelen + domainlen); - g_free(msg); - - return tmp; -} - -/*** HTTP Sockets *************************************************************/ - -static gchar * -purple_http_socket_hash(const gchar *host, int port, gboolean is_ssl) -{ - return g_strdup_printf("%c:%s:%d", (is_ssl ? 'S' : 'R'), host, port); -} - -static void -purple_http_socket_connect_new_cb(GObject *source, GAsyncResult *res, - gpointer user_data) -{ - PurpleHttpSocket *hs = user_data; - GSocketConnection *conn; - PurpleHttpSocketConnectCb cb; - gpointer cb_data; - GError *error = NULL; - - conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source), - res, &error); - - cb = g_object_steal_data(source, "cb"); - cb_data = g_object_steal_data(source, "cb_data"); - - if (conn == NULL) { - if (!g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - cb(hs, error->message, cb_data); - } - - g_clear_error(&error); - return; - } - - hs->conn = conn; - - cb(hs, NULL, cb_data); -} - -static PurpleHttpSocket * -purple_http_socket_connect_new(PurpleConnection *gc, const gchar *host, - int port, gboolean is_ssl, - PurpleHttpSocketConnectCb cb, gpointer user_data) -{ - PurpleHttpSocket *hs; - GSocketClient *client; - GError *error = NULL; - - client = purple_gio_socket_client_new(PURPLE_HTTP_GET_ACCOUNT(gc), &error); - - if (client == NULL) { - purple_debug_error("http", "Error connecting to '%s:%d': %s", - host, port, error->message); - g_clear_error(&error); - return NULL; - } - - hs = g_new0(PurpleHttpSocket, 1); - hs->cancellable = g_cancellable_new(); - - g_socket_client_set_tls(client, is_ssl); - g_object_set_data(G_OBJECT(client), "cb", cb); - g_object_set_data(G_OBJECT(client), "cb_data", user_data); - - g_socket_client_connect_to_host_async(client, - host, port, hs->cancellable, - purple_http_socket_connect_new_cb, hs); - - g_object_unref(client); - - if (purple_debug_is_verbose()) - purple_debug_misc("http", "new socket created: %p\n", hs); - - return hs; -} - -static void -purple_http_socket_close_free(PurpleHttpSocket *hs) -{ - if (hs == NULL) - return; - - if (purple_debug_is_verbose()) - purple_debug_misc("http", "destroying socket: %p\n", hs); - - if (hs->input_source > 0) { - g_source_remove(hs->input_source); - hs->input_source = 0; - } - - if (hs->output_source > 0) { - g_source_remove(hs->output_source); - hs->output_source = 0; - } - - if (hs->cancellable != NULL) { - g_cancellable_cancel(hs->cancellable); - g_clear_object(&hs->cancellable); - } - - if (hs->conn != NULL) { - purple_gio_graceful_close(G_IO_STREAM(hs->conn), NULL, NULL); - g_clear_object(&hs->conn); - } - - g_free(hs); -} - -/*** Headers collection *******************************************************/ - -static PurpleHttpHeaders * purple_http_headers_new(void); -static void purple_http_headers_free(PurpleHttpHeaders *hdrs); -static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key, - const gchar *value); -static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs); -static GList * purple_http_headers_get_all_by_name( - PurpleHttpHeaders *hdrs, const gchar *key); -static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs, - const gchar *key); -static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs, - const gchar *key, int *dst); -static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs, - const gchar *key, const gchar *value); -static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs); - -static PurpleHttpHeaders * purple_http_headers_new(void) -{ - PurpleHttpHeaders *hdrs = g_new0(PurpleHttpHeaders, 1); - - hdrs->by_name = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - (GDestroyNotify)g_list_free); - - return hdrs; -} - -static void purple_http_headers_free_kvp(PurpleKeyValuePair *kvp) -{ - g_free(kvp->key); - g_free(kvp->value); - g_free(kvp); -} - -static void purple_http_headers_free(PurpleHttpHeaders *hdrs) -{ - if (hdrs == NULL) - return; - - g_hash_table_destroy(hdrs->by_name); - g_list_free_full(hdrs->list, - (GDestroyNotify)purple_http_headers_free_kvp); - g_free(hdrs); -} - -static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key, - const gchar *value) -{ - PurpleKeyValuePair *kvp; - GList *named_values, *new_values; - gchar *key_low; - - g_return_if_fail(hdrs != NULL); - g_return_if_fail(key != NULL); - g_return_if_fail(value != NULL); - - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(key); - kvp->value = g_strdup(value); - hdrs->list = g_list_append(hdrs->list, kvp); - - key_low = g_ascii_strdown(key, -1); - named_values = g_hash_table_lookup(hdrs->by_name, key_low); - new_values = g_list_append(named_values, kvp->value); - if (named_values) - g_free(key_low); - else - g_hash_table_insert(hdrs->by_name, key_low, new_values); -} - -static void purple_http_headers_remove(PurpleHttpHeaders *hdrs, - const gchar *key) -{ - GList *it, *curr; - - g_return_if_fail(hdrs != NULL); - g_return_if_fail(key != NULL); - - if (!g_hash_table_remove(hdrs->by_name, key)) - return; - - /* Could be optimized to O(1). */ - it = g_list_first(hdrs->list); - while (it) { - PurpleKeyValuePair *kvp = it->data; - curr = it; - it = g_list_next(it); - if (g_ascii_strcasecmp(kvp->key, key) != 0) - continue; - - hdrs->list = g_list_delete_link(hdrs->list, curr); - purple_http_headers_free_kvp(kvp); - } -} - -static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs) -{ - g_return_val_if_fail(hdrs != NULL, NULL); - - return hdrs->list; -} - -/* return const */ -static GList * purple_http_headers_get_all_by_name( - PurpleHttpHeaders *hdrs, const gchar *key) -{ - GList *values; - gchar *key_low; - - g_return_val_if_fail(hdrs != NULL, NULL); - g_return_val_if_fail(key != NULL, NULL); - - key_low = g_ascii_strdown(key, -1); - values = g_hash_table_lookup(hdrs->by_name, key_low); - g_free(key_low); - - return values; -} - -static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs, - const gchar *key) -{ - const GList *values = purple_http_headers_get_all_by_name(hdrs, key); - - if (!values) - return NULL; - - return values->data; -} - -static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs, - const gchar *key, int *dst) -{ - int val; - const gchar *str; - - str = purple_http_headers_get(hdrs, key); - if (!str) - return FALSE; - - if (1 != sscanf(str, "%d", &val)) - return FALSE; - - *dst = val; - return TRUE; -} - -static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs, - const gchar *key, const gchar *value) -{ - const gchar *str; - - str = purple_http_headers_get(hdrs, key); - if (str == NULL || value == NULL) - return str == value; - - return (g_ascii_strcasecmp(str, value) == 0); -} - -static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs) -{ - const GList *hdr; - - GString *s = g_string_new(""); - - hdr = purple_http_headers_get_all(hdrs); - while (hdr) { - PurpleKeyValuePair *kvp = hdr->data; - hdr = g_list_next(hdr); - - g_string_append_printf(s, "%s: %s%s", kvp->key, - (gchar*)kvp->value, hdr ? "\n" : ""); - } - - return g_string_free(s, FALSE); -} - -/*** HTTP protocol backend ****************************************************/ - -static void _purple_http_disconnect(PurpleHttpConnection *hc, - gboolean is_graceful); - -static void _purple_http_gen_headers(PurpleHttpConnection *hc); -static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc); -static gboolean _purple_http_recv(GObject *source, gpointer _hc); -static gboolean _purple_http_send(GObject *source, gpointer _hc); - -/* closes current connection (if exists), estabilishes one and proceeds with - * request */ -static gboolean _purple_http_reconnect(PurpleHttpConnection *hc); - -static void _purple_http_error(PurpleHttpConnection *hc, const char *format, - ...) G_GNUC_PRINTF(2, 3); - -static void _purple_http_error(PurpleHttpConnection *hc, const char *format, - ...) -{ - va_list args; - - va_start(args, format); - hc->response->error = g_strdup_vprintf(format, args); - va_end(args); - - if (purple_debug_is_verbose()) - purple_debug_warning("http", "error: %s\n", hc->response->error); - - purple_http_conn_cancel(hc); -} - -static void memset_zero(gpointer pnt, gsize len) -{ - volatile unsigned char *volatile pnt_ = - (volatile unsigned char *volatile) pnt; - size_t i = (size_t) 0U; - - while (i < len) { - pnt_[i++] = 0U; - } -} - -static void _purple_http_gen_headers(PurpleHttpConnection *hc) -{ - GString *h; - PurpleHttpURL *url; - const GList *hdr; - PurpleHttpRequest *req; - PurpleHttpHeaders *hdrs; - gchar *request_url, *tmp_url = NULL; - - PurpleProxyInfo *proxy; - gboolean proxy_http = FALSE; - const gchar *proxy_username, *proxy_password; - - g_return_if_fail(hc != NULL); - - if (hc->request_header != NULL) - return; - - req = hc->request; - url = hc->url; - hdrs = req->headers; - proxy = purple_proxy_get_setup(PURPLE_HTTP_GET_ACCOUNT(hc->gc)); - - proxy_http = (purple_proxy_info_get_proxy_type(proxy) == PURPLE_PROXY_HTTP || - purple_proxy_info_get_proxy_type(proxy) == PURPLE_PROXY_USE_ENVVAR); - /* this is HTTP proxy, but used with tunelling with CONNECT */ - if (proxy_http && url->port != 80) - proxy_http = FALSE; - - hc->request_header = h = g_string_new(""); - hc->request_header_written = 0; - hc->request_contents_written = 0; - - if (proxy_http) - request_url = tmp_url = purple_http_url_print(url); - else - request_url = url->path; - - g_string_append_printf(h, "%s %s HTTP/%s\r\n", - req->method ? req->method : "GET", - request_url, - req->http11 ? "1.1" : "1.0"); - - g_free(tmp_url); - - if (!purple_http_headers_get(hdrs, "host")) - g_string_append_printf(h, "Host: %s\r\n", url->host); - if (!purple_http_headers_get(hdrs, "connection")) { - g_string_append(h, "Connection: "); - g_string_append(h, hc->is_keepalive ? - "Keep-Alive\r\n" : "close\r\n"); - } - if (!purple_http_headers_get(hdrs, "accept")) - g_string_append(h, "Accept: */*\r\n"); - if (!purple_http_headers_get(hdrs, "accept-encoding")) - g_string_append(h, "Accept-Encoding: gzip, deflate\r\n"); - - if (!purple_http_headers_get(hdrs, "content-length") && ( - req->contents_length > 0 || - purple_http_request_is_method(req, "post"))) - { - g_string_append_printf(h, "Content-Length: %u\r\n", - (guint) req->contents_length); - } - - if (proxy_http) - g_string_append(h, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */ - - proxy_username = purple_proxy_info_get_username(proxy); - if (proxy_http && proxy_username != NULL && proxy_username[0] != '\0') { - gchar *proxy_auth, *ntlm_type1, *tmp; - int len; - - proxy_password = purple_proxy_info_get_password(proxy); - if (proxy_password == NULL) - proxy_password = ""; - - tmp = g_strdup_printf("%s:%s", proxy_username, proxy_password); - len = strlen(tmp); - proxy_auth = g_base64_encode((const guchar *)tmp, len); - memset_zero(tmp, len); - g_free(tmp); - - ntlm_type1 = purple_http_ntlm_gen_type1(purple_get_host_name(), - ""); - - g_string_append_printf(h, "Proxy-Authorization: Basic %s\r\n", - proxy_auth); - g_string_append_printf(h, "Proxy-Authorization: NTLM %s\r\n", - ntlm_type1); - g_string_append(h, "Proxy-Connection: close\r\n"); /* TEST: proxy+KeepAlive */ - - memset_zero(proxy_auth, strlen(proxy_auth)); - g_free(proxy_auth); - g_free(ntlm_type1); - } - - hdr = purple_http_headers_get_all(hdrs); - while (hdr) { - PurpleKeyValuePair *kvp = hdr->data; - hdr = g_list_next(hdr); - - g_string_append_printf(h, "%s: %s\r\n", - kvp->key, (gchar*)kvp->value); - } - - if (!purple_http_cookie_jar_is_empty(req->cookie_jar)) { - gchar * cookies = purple_http_cookie_jar_gen(req->cookie_jar); - g_string_append_printf(h, "Cookie: %s\r\n", cookies); - g_free(cookies); - } - - g_string_append_printf(h, "\r\n"); - - if (purple_debug_is_unsafe() && purple_debug_is_verbose()) { - purple_debug_misc("http", "Generated request headers:\n%s", - h->str); - } -} - -static gboolean _purple_http_recv_headers(PurpleHttpConnection *hc, - const gchar *buf, int len) -{ - gchar *eol, *delim; - - if (hc->headers_got) { - purple_debug_error("http", "Headers already got\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - - g_string_append_len(hc->response_buffer, buf, len); - if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) { - purple_debug_error("http", - "Buffer too big when parsing headers\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - - while ((eol = strstr(hc->response_buffer->str, "\r\n")) - != NULL) - { - gchar *hdrline = hc->response_buffer->str; - int hdrline_len = eol - hdrline; - - hdrline[hdrline_len] = '\0'; - - if (hdrline[0] == '\0') { - if (!hc->main_header_got) { - if (purple_debug_is_verbose() && - hc->is_keepalive) - { - purple_debug_misc("http", "Got keep-" - "alive terminator from previous" - " request\n"); - } else { - purple_debug_warning("http", "Got empty" - " line at the beginning - this " - "may be a HTTP server quirk\n"); - } - } else /* hc->main_header_got */ { - hc->headers_got = TRUE; - if (purple_debug_is_verbose()) { - purple_debug_misc("http", "Got headers " - "end\n"); - } - } - } else if (!hc->main_header_got) { - hc->main_header_got = TRUE; - delim = strchr(hdrline, ' '); - if (delim == NULL || 1 != sscanf(delim + 1, "%d", - &hc->response->code)) - { - purple_debug_warning("http", - "Invalid response code\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - if (purple_debug_is_verbose()) - purple_debug_misc("http", - "Got main header with code %d\n", - hc->response->code); - } else { - if (purple_debug_is_verbose() && - purple_debug_is_unsafe()) - purple_debug_misc("http", "Got header: %s\n", - hdrline); - delim = strchr(hdrline, ':'); - if (delim == NULL || delim == hdrline) { - purple_debug_warning("http", - "Bad header delimiter\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - *delim++ = '\0'; - while (*delim == ' ') - delim++; - - purple_http_headers_add(hc->response->headers, hdrline, delim); - } - - g_string_erase(hc->response_buffer, 0, hdrline_len + 2); - if (hc->headers_got) - break; - } - return TRUE; -} - -static gboolean _purple_http_recv_body_data(PurpleHttpConnection *hc, - const gchar *buf, int len) -{ - GString *decompressed = NULL; - - if (hc->length_expected >= 0 && - len + hc->length_got > (guint)hc->length_expected) - { - len = hc->length_expected - hc->length_got; - } - - hc->length_got += len; - - if (hc->gz_stream != NULL) { - decompressed = purple_http_gz_put(hc->gz_stream, buf, len); - if (decompressed == NULL) { - _purple_http_error(hc, - _("Error while decompressing data")); - return FALSE; - } - buf = decompressed->str; - len = decompressed->len; - } - - g_assert(hc->request->max_length <= - PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH); - if (hc->length_got_decompressed + len > hc->request->max_length) { - purple_debug_warning("http", - "Maximum length exceeded, truncating\n"); - len = hc->request->max_length - hc->length_got_decompressed; - hc->length_expected = hc->length_got; - } - hc->length_got_decompressed += len; - - if (len == 0) { - if (decompressed != NULL) - g_string_free(decompressed, TRUE); - return TRUE; - } - - if (hc->request->response_writer != NULL) { - gboolean succ; - succ = hc->request->response_writer(hc, hc->response, buf, - hc->length_got_decompressed, len, - hc->request->response_writer_data); - if (!succ) { - if (decompressed != NULL) - g_string_free(decompressed, TRUE); - purple_debug_error("http", - "Cannot write using callback\n"); - _purple_http_error(hc, - _("Error handling retrieved data")); - return FALSE; - } - } else { - if (hc->response->contents == NULL) - hc->response->contents = g_string_new(""); - g_string_append_len(hc->response->contents, buf, len); - } - - if (decompressed != NULL) - g_string_free(decompressed, TRUE); - - purple_http_conn_notify_progress_watcher(hc); - return TRUE; -} - -static gboolean _purple_http_recv_body_chunked(PurpleHttpConnection *hc, - const gchar *buf, int len) -{ - gchar *eol, *line; - int line_len; - - if (hc->chunks_done) - return FALSE; - if (!hc->response_buffer) - hc->response_buffer = g_string_new(""); - - g_string_append_len(hc->response_buffer, buf, len); - if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) { - purple_debug_error("http", - "Buffer too big when searching for chunk\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - - while (hc->response_buffer->len > 0) { - if (hc->in_chunk) { - int got_now = hc->response_buffer->len; - if (hc->chunk_got + got_now > hc->chunk_length) - got_now = hc->chunk_length - hc->chunk_got; - hc->chunk_got += got_now; - - if (!_purple_http_recv_body_data(hc, - hc->response_buffer->str, got_now)) - return FALSE; - - g_string_erase(hc->response_buffer, 0, got_now); - hc->in_chunk = (hc->chunk_got < hc->chunk_length); - - continue; - } - - line = hc->response_buffer->str; - eol = strstr(line, "\r\n"); - if (eol == line) { - g_string_erase(hc->response_buffer, 0, 2); - line = hc->response_buffer->str; - eol = strstr(line, "\r\n"); - } - if (eol == NULL) { - /* waiting for more data (unlikely, but possible) */ - if (hc->response_buffer->len > 20) { - purple_debug_warning("http", "Chunk length not " - "found (buffer too large)\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - return TRUE; - } - line_len = eol - line; - - if (1 != sscanf(line, "%x", &hc->chunk_length)) { - if (purple_debug_is_unsafe()) - purple_debug_warning("http", - "Chunk length not found in [%s]\n", - line); - else - purple_debug_warning("http", - "Chunk length not found\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - hc->chunk_got = 0; - hc->in_chunk = TRUE; - - if (purple_debug_is_verbose()) - purple_debug_misc("http", "Found chunk of length %d\n", hc->chunk_length); - - g_string_erase(hc->response_buffer, 0, line_len + 2); - - if (hc->chunk_length == 0) { - hc->chunks_done = TRUE; - hc->in_chunk = FALSE; - return TRUE; - } - } - - return TRUE; -} - -static gboolean _purple_http_recv_body(PurpleHttpConnection *hc, - const gchar *buf, int len) -{ - if (hc->is_chunked) - return _purple_http_recv_body_chunked(hc, buf, len); - - return _purple_http_recv_body_data(hc, buf, len); -} - -static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc) -{ - int len; - gchar buf[4096]; - gboolean got_anything; - GError *error = NULL; - - len = g_pollable_input_stream_read_nonblocking( - G_POLLABLE_INPUT_STREAM( - g_io_stream_get_input_stream( - G_IO_STREAM(hc->socket->conn))), - buf, sizeof(buf), hc->socket->cancellable, - &error); - got_anything = (len > 0); - - if (len < 0 && (g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) || - g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_CANCELLED))) { - g_clear_error(&error); - return FALSE; - } - - if (len < 0) { - _purple_http_error(hc, _("Error reading from %s: %s"), - hc->url->host, error->message); - g_clear_error(&error); - return FALSE; - } - - /* EOF */ - if (len == 0) { - if (hc->request->max_length == 0) { - /* It's definitely YHttpServer quirk. */ - purple_debug_warning("http", "Got EOF, but no data was " - "expected (this may be a server quirk)\n"); - hc->length_expected = hc->length_got; - } - if (hc->length_expected >= 0 && - hc->length_got < (guint)hc->length_expected) - { - purple_debug_warning("http", "No more data while reading" - " contents\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - if (hc->headers_got) - hc->length_expected = hc->length_got; - else if (hc->length_got == 0 && hc->socket->use_count > 1) { - purple_debug_info("http", "Keep-alive connection " - "expired (when reading), retrying...\n"); - purple_http_conn_retry(hc); - return FALSE; - } else { - const gchar *server = purple_http_headers_get( - hc->response->headers, "Server"); - if (server && - g_ascii_strcasecmp(server, "YHttpServer") == 0) - { - purple_debug_warning("http", "No more data " - "while parsing headers (YHttpServer " - "quirk)\n"); - hc->headers_got = TRUE; - hc->length_expected = hc->length_got = 0; - hc->length_got_decompressed = 0; - } else { - purple_debug_warning("http", "No more data " - "while parsing headers\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - } - } - - if (!hc->headers_got && len > 0) { - if (!_purple_http_recv_headers(hc, buf, len)) - return FALSE; - len = 0; - if (hc->headers_got) { - gboolean is_gzip, is_deflate; - if (!purple_http_headers_get_int(hc->response->headers, - "Content-Length", &hc->length_expected)) - hc->length_expected = -1; - hc->is_chunked = (purple_http_headers_match( - hc->response->headers, - "Transfer-Encoding", "chunked")); - is_gzip = purple_http_headers_match( - hc->response->headers, "Content-Encoding", - "gzip"); - is_deflate = purple_http_headers_match( - hc->response->headers, "Content-Encoding", - "deflate"); - if (is_gzip || is_deflate) { - hc->gz_stream = purple_http_gz_new( - hc->request->max_length + 1, - is_deflate); - } - } - if (hc->headers_got && hc->response_buffer && - hc->response_buffer->len > 0) - { - int buffer_len = hc->response_buffer->len; - gchar *buffer = g_string_free(hc->response_buffer, FALSE); - hc->response_buffer = NULL; - if (!_purple_http_recv_body(hc, buffer, buffer_len)) - { - g_free(buffer); - return FALSE; - } - g_free(buffer); - } - if (!hc->headers_got) - return got_anything; - } - - if (len > 0) { - if (!_purple_http_recv_body(hc, buf, len)) - return FALSE; - } - - if (hc->is_chunked && hc->chunks_done && hc->length_expected < 0) - hc->length_expected = hc->length_got; - - if (hc->length_expected >= 0 && - hc->length_got >= (guint)hc->length_expected) - { - const gchar *redirect; - - if (hc->is_chunked && !hc->chunks_done) { - if (len == 0) { - _purple_http_error(hc, _("Chunked connection terminated")); - return FALSE; - } - if (purple_debug_is_verbose()) { - purple_debug_misc("http", - "I need the terminating empty chunk\n"); - } - return TRUE; - } - - if (!hc->headers_got) { - hc->response->code = 0; - purple_debug_warning("http", "No headers got\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - return FALSE; - } - - if (purple_debug_is_unsafe() && purple_debug_is_verbose()) { - gchar *hdrs = purple_http_headers_dump( - hc->response->headers); - purple_debug_misc("http", "Got response headers: %s\n", - hdrs); - g_free(hdrs); - } - - purple_http_cookie_jar_parse(hc->request->cookie_jar, - purple_http_headers_get_all_by_name( - hc->response->headers, "Set-Cookie")); - - if (purple_debug_is_unsafe() && purple_debug_is_verbose() && - !purple_http_cookie_jar_is_empty( - hc->request->cookie_jar)) - { - gchar *cookies = purple_http_cookie_jar_dump( - hc->request->cookie_jar); - purple_debug_misc("http", "Cookies: %s\n", cookies); - g_free(cookies); - } - - if (hc->response->code == 407) { - _purple_http_error(hc, _("Invalid proxy credentials")); - return FALSE; - } - - redirect = purple_http_headers_get(hc->response->headers, - "location"); - if (redirect && (hc->request->max_redirects == -1 || - hc->request->max_redirects > hc->redirects_count)) - { - PurpleHttpURL *url = purple_http_url_parse(redirect); - - hc->redirects_count++; - - if (!url) { - if (purple_debug_is_unsafe()) - purple_debug_warning("http", - "Invalid redirect to %s\n", - redirect); - else - purple_debug_warning("http", - "Invalid redirect\n"); - _purple_http_error(hc, _("Error parsing HTTP")); - } - - purple_http_url_relative(hc->url, url); - purple_http_url_free(url); - - _purple_http_reconnect(hc); - return FALSE; - } - - _purple_http_disconnect(hc, TRUE); - purple_http_connection_terminate(hc); - return FALSE; - } - - return got_anything; -} - -static gboolean _purple_http_recv(GObject *source, gpointer _hc) -{ - PurpleHttpConnection *hc = _hc; - - while (_purple_http_recv_loopbody(hc)); - - return G_SOURCE_CONTINUE; -} - -static void _purple_http_send_got_data(PurpleHttpConnection *hc, - gboolean success, gboolean eof, size_t stored) -{ - int estimated_length; - - g_return_if_fail(hc != NULL); - - if (!success) { - _purple_http_error(hc, _("Error requesting data to write")); - return; - } - - hc->contents_reader_requested = FALSE; - g_string_set_size(hc->contents_reader_buffer, stored); - if (!eof) - return; - - estimated_length = hc->request_contents_written + stored; - - if (hc->request->contents_length != -1 && - hc->request->contents_length != estimated_length) - { - purple_debug_warning("http", - "Invalid amount of data has been written\n"); - } - hc->request->contents_length = estimated_length; -} - -static gboolean _purple_http_send(GObject *source, gpointer _hc) -{ - PurpleHttpConnection *hc = _hc; - int written, write_len; - const gchar *write_from; - gboolean writing_headers; - GError *error = NULL; - GSource *gsource; - - /* Waiting for data. This could be written more efficiently, by removing - * (and later, adding) hs->inpa. */ - if (hc->contents_reader_requested) - return G_SOURCE_CONTINUE; - - _purple_http_gen_headers(hc); - - writing_headers = - (hc->request_header_written < hc->request_header->len); - if (writing_headers) { - write_from = hc->request_header->str + - hc->request_header_written; - write_len = hc->request_header->len - - hc->request_header_written; - } else if (hc->request->contents_reader) { - if (hc->contents_reader_requested) - return G_SOURCE_CONTINUE; /* waiting for data */ - if (!hc->contents_reader_buffer) - hc->contents_reader_buffer = g_string_new(""); - if (hc->contents_reader_buffer->len == 0) { - hc->contents_reader_requested = TRUE; - g_string_set_size(hc->contents_reader_buffer, - PURPLE_HTTP_MAX_READ_BUFFER_LEN); - hc->request->contents_reader(hc, - hc->contents_reader_buffer->str, - hc->request_contents_written, - PURPLE_HTTP_MAX_READ_BUFFER_LEN, - hc->request->contents_reader_data, - _purple_http_send_got_data); - return G_SOURCE_CONTINUE; - } - write_from = hc->contents_reader_buffer->str; - write_len = hc->contents_reader_buffer->len; - } else { - write_from = hc->request->contents + - hc->request_contents_written; - write_len = hc->request->contents_length - - hc->request_contents_written; - } - - if (write_len == 0) { - purple_debug_warning("http", "Nothing to write\n"); - written = 0; - } else { - written = g_pollable_output_stream_write_nonblocking( - G_POLLABLE_OUTPUT_STREAM( - g_io_stream_get_output_stream( - G_IO_STREAM(hc->socket->conn))), - write_from, write_len, hc->socket->cancellable, - &error); - } - - if (written < 0 && (g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) || - g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_CANCELLED))) { - g_clear_error(&error); - return G_SOURCE_CONTINUE; - } - - if (written < 0) { - if (hc->request_header_written == 0 && - hc->socket->use_count > 1) - { - purple_debug_info("http", "Keep-alive connection " - "expired (when writing), retrying...\n"); - purple_http_conn_retry(hc); - } else { - _purple_http_error(hc, _("Error writing to %s: %s"), - hc->url->host, error->message); - } - - g_clear_error(&error); - return G_SOURCE_CONTINUE; - } - - if (writing_headers) { - hc->request_header_written += written; - purple_http_conn_notify_progress_watcher(hc); - if (hc->request_header_written < hc->request_header->len) - return G_SOURCE_CONTINUE; - if (hc->request->contents_length > 0) - return G_SOURCE_CONTINUE; - } else { - hc->request_contents_written += written; - purple_http_conn_notify_progress_watcher(hc); - if (hc->contents_reader_buffer) - g_string_erase(hc->contents_reader_buffer, 0, written); - if (hc->request->contents_length > 0 && - hc->request_contents_written < - (guint)hc->request->contents_length) - { - return G_SOURCE_CONTINUE; - } - } - - /* request is completely written, let's read the response */ - hc->is_reading = TRUE; - gsource = g_pollable_input_stream_create_source( - G_POLLABLE_INPUT_STREAM( - g_io_stream_get_input_stream( - G_IO_STREAM(hc->socket->conn))), - NULL); - g_source_set_callback(gsource, - (GSourceFunc)_purple_http_recv, hc, NULL); - hc->socket->input_source = g_source_attach(gsource, NULL); - g_source_unref(gsource); - - hc->socket->output_source = 0; - return G_SOURCE_REMOVE; -} - -static void _purple_http_disconnect(PurpleHttpConnection *hc, - gboolean is_graceful) -{ - g_return_if_fail(hc != NULL); - - if (hc->request_header) - g_string_free(hc->request_header, TRUE); - hc->request_header = NULL; - - if (hc->response_buffer) - g_string_free(hc->response_buffer, TRUE); - hc->response_buffer = NULL; - - if (hc->gz_stream) - purple_http_gz_free(hc->gz_stream); - hc->gz_stream = NULL; - - if (hc->socket_request) - purple_http_keepalive_pool_request_cancel(hc->socket_request); - else { - purple_http_keepalive_pool_release(hc->socket, !is_graceful); - hc->socket = NULL; - } -} - -static void -_purple_http_connected(PurpleHttpSocket *hs, const gchar *error, gpointer _hc) -{ - PurpleHttpConnection *hc = _hc; - GSource *source; - - hc->socket_request = NULL; - hc->socket = hs; - - if (error != NULL) { - _purple_http_error(hc, _("Unable to connect to %s: %s"), - hc->url->host, error); - return; - } - - source = g_pollable_output_stream_create_source( - G_POLLABLE_OUTPUT_STREAM( - g_io_stream_get_output_stream(G_IO_STREAM(hs->conn))), - NULL); - g_source_set_callback(source, - (GSourceFunc)_purple_http_send, hc, NULL); - hc->socket->output_source = g_source_attach(source, NULL); - g_source_unref(source); -} - -static gboolean _purple_http_reconnect(PurpleHttpConnection *hc) -{ - PurpleHttpURL *url; - gboolean is_ssl = FALSE; - - g_return_val_if_fail(hc != NULL, FALSE); - g_return_val_if_fail(hc->url != NULL, FALSE); - - _purple_http_disconnect(hc, TRUE); - - if (purple_debug_is_verbose()) { - if (purple_debug_is_unsafe()) { - gchar *urlp = purple_http_url_print(hc->url); - purple_debug_misc("http", "Connecting to %s...\n", urlp); - g_free(urlp); - } else - purple_debug_misc("http", "Connecting to %s...\n", - hc->url->host); - } - - url = hc->url; - if (g_strcmp0(url->protocol, "") == 0 || - g_ascii_strcasecmp(url->protocol, "http") == 0) - { - /* do nothing */ - } else if (g_ascii_strcasecmp(url->protocol, "https") == 0) { - is_ssl = TRUE; - } else { - _purple_http_error(hc, _("Unsupported protocol: %s"), - url->protocol); - return FALSE; - } - - if (hc->request->keepalive_pool != NULL) { - hc->socket_request = purple_http_keepalive_pool_request( - hc->request->keepalive_pool, hc->gc, url->host, - url->port, is_ssl, _purple_http_connected, hc); - } else { - hc->socket = purple_http_socket_connect_new(hc->gc, url->host, - url->port, is_ssl, _purple_http_connected, hc); - } - - if (hc->socket_request == NULL && hc->socket == NULL) { - _purple_http_error(hc, _("Unable to connect to %s"), url->host); - return FALSE; - } - - purple_http_headers_free(hc->response->headers); - hc->response->headers = purple_http_headers_new(); - hc->response_buffer = g_string_new(""); - hc->main_header_got = FALSE; - hc->headers_got = FALSE; - if (hc->response->contents != NULL) - g_string_free(hc->response->contents, TRUE); - hc->response->contents = NULL; - hc->length_got = 0; - hc->length_got_decompressed = 0; - hc->length_expected = -1; - hc->is_chunked = FALSE; - hc->in_chunk = FALSE; - hc->chunks_done = FALSE; - - purple_http_conn_notify_progress_watcher(hc); - - return TRUE; -} - -/*** Performing HTTP requests *************************************************/ - -static gboolean purple_http_request_timeout(gpointer _hc) -{ - PurpleHttpConnection *hc = _hc; - - purple_debug_warning("http", "Timeout reached for request %p\n", hc); - - purple_http_conn_cancel(hc); - - return FALSE; -} - -PurpleHttpConnection * purple_http_get(PurpleConnection *gc, - PurpleHttpCallback callback, gpointer user_data, const gchar *url) -{ - PurpleHttpRequest *request; - PurpleHttpConnection *hc; - - g_return_val_if_fail(url != NULL, NULL); - - request = purple_http_request_new(url); - hc = purple_http_request(gc, request, callback, user_data); - purple_http_request_unref(request); - - return hc; -} - -PurpleHttpConnection * purple_http_get_printf(PurpleConnection *gc, - PurpleHttpCallback callback, gpointer user_data, - const gchar *format, ...) -{ - va_list args; - gchar *value; - PurpleHttpConnection *ret; - - g_return_val_if_fail(format != NULL, NULL); - - va_start(args, format); - value = g_strdup_vprintf(format, args); - va_end(args); - - ret = purple_http_get(gc, callback, user_data, value); - g_free(value); - - return ret; -} - -PurpleHttpConnection * purple_http_request(PurpleConnection *gc, - PurpleHttpRequest *request, PurpleHttpCallback callback, - gpointer user_data) -{ - PurpleHttpConnection *hc; - - g_return_val_if_fail(request != NULL, NULL); - - if (request->url == NULL) { - purple_debug_error("http", "Cannot perform new request - " - "URL is not set\n"); - return NULL; - } - - if (g_hash_table_lookup(purple_http_cancelling_gc, gc)) { - purple_debug_warning("http", "Cannot perform another HTTP " - "request while cancelling all related with this " - "PurpleConnection\n"); - return NULL; - } - - hc = purple_http_connection_new(request, gc); - hc->callback = callback; - hc->user_data = user_data; - - hc->url = purple_http_url_parse(request->url); - - if (purple_debug_is_unsafe()) - purple_debug_misc("http", "Performing new request %p for %s.\n", - hc, request->url); - else - purple_debug_misc("http", "Performing new request %p to %s.\n", - hc, hc->url ? hc->url->host : NULL); - - if (!hc->url || hc->url->host == NULL || hc->url->host[0] == '\0') { - purple_debug_error("http", "Invalid URL requested.\n"); - purple_http_connection_terminate(hc); - return NULL; - } - - _purple_http_reconnect(hc); - - hc->timeout_handle = g_timeout_add_seconds(request->timeout, - purple_http_request_timeout, hc); - - return hc; -} - -/*** HTTP connection API ******************************************************/ - -static void purple_http_connection_free(PurpleHttpConnection *hc); -static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc); - -static PurpleHttpConnection * purple_http_connection_new( - PurpleHttpRequest *request, PurpleConnection *gc) -{ - PurpleHttpConnection *hc = g_new0(PurpleHttpConnection, 1); - - g_assert(request != NULL); - - hc->request = request; - purple_http_request_ref(request); - hc->response = purple_http_response_new(); - hc->is_keepalive = (request->keepalive_pool != NULL); - - hc->link_global = purple_http_hc_list = - g_list_prepend(purple_http_hc_list, hc); - g_hash_table_insert(purple_http_hc_by_ptr, hc, hc->link_global); - if (gc) { - GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc); - g_hash_table_steal(purple_http_hc_by_gc, gc); - hc->link_gc = gc_list = g_list_prepend(gc_list, hc); - g_hash_table_insert(purple_http_hc_by_gc, gc, gc_list); - hc->gc = gc; - } - - return hc; -} - -static void purple_http_connection_free(PurpleHttpConnection *hc) -{ - if (hc->timeout_handle) - g_source_remove(hc->timeout_handle); - if (hc->watcher_delayed_handle) - g_source_remove(hc->watcher_delayed_handle); - - if (hc->connection_set != NULL) - purple_http_connection_set_remove(hc->connection_set, hc); - - purple_http_url_free(hc->url); - purple_http_request_unref(hc->request); - purple_http_response_free(hc->response); - - if (hc->contents_reader_buffer) - g_string_free(hc->contents_reader_buffer, TRUE); - purple_http_gz_free(hc->gz_stream); - - if (hc->request_header) - g_string_free(hc->request_header, TRUE); - - purple_http_hc_list = g_list_delete_link(purple_http_hc_list, - hc->link_global); - g_hash_table_remove(purple_http_hc_by_ptr, hc); - if (hc->gc) { - GList *gc_list, *gc_list_new; - gc_list = g_hash_table_lookup(purple_http_hc_by_gc, hc->gc); - g_assert(gc_list != NULL); - - gc_list_new = g_list_delete_link(gc_list, hc->link_gc); - if (gc_list != gc_list_new) { - g_hash_table_steal(purple_http_hc_by_gc, hc->gc); - if (gc_list_new) - g_hash_table_insert(purple_http_hc_by_gc, - hc->gc, gc_list_new); - } - } - - g_free(hc); -} - -/* call callback and do the cleanup */ -static void purple_http_connection_terminate(PurpleHttpConnection *hc) -{ - g_return_if_fail(hc != NULL); - - purple_debug_misc("http", "Request %p performed %s.\n", hc, - purple_http_response_is_successful(hc->response) ? - "successfully" : "without success"); - - if (hc->callback) - hc->callback(hc, hc->response, hc->user_data); - - purple_http_connection_free(hc); -} - -void purple_http_conn_cancel(PurpleHttpConnection *http_conn) -{ - if (http_conn == NULL) - return; - - if (http_conn->is_cancelling) - return; - http_conn->is_cancelling = TRUE; - - if (purple_debug_is_verbose()) { - purple_debug_misc("http", "Cancelling connection %p...\n", - http_conn); - } - - if (http_conn->response != NULL) { - http_conn->response->code = 0; - } - _purple_http_disconnect(http_conn, FALSE); - purple_http_connection_terminate(http_conn); -} - -static void -purple_http_conn_retry(PurpleHttpConnection *http_conn) -{ - if (http_conn == NULL) - return; - - purple_debug_info("http", "Retrying connection %p...\n", http_conn); - - if (http_conn->response != NULL) { - http_conn->response->code = 0; - } - _purple_http_disconnect(http_conn, FALSE); - _purple_http_reconnect(http_conn); -} - -void purple_http_conn_cancel_all(PurpleConnection *gc) -{ - GList *gc_list; - - if (purple_debug_is_verbose()) { - purple_debug_misc("http", "Cancelling all running HTTP " - "connections\n"); - } - - gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc); - - g_hash_table_insert(purple_http_cancelling_gc, gc, GINT_TO_POINTER(1)); - - while (gc_list) { - PurpleHttpConnection *hc = gc_list->data; - gc_list = g_list_next(gc_list); - purple_http_conn_cancel(hc); - } - - g_hash_table_remove(purple_http_cancelling_gc, gc); - - if (NULL != g_hash_table_lookup(purple_http_hc_by_gc, gc)) - purple_debug_fatal("http", "Couldn't cancel all connections " - "related to gc=%p (it shouldn't happen)\n", gc); -} - -gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn) -{ - if (http_conn == NULL) - return FALSE; - return (NULL != g_hash_table_lookup(purple_http_hc_by_ptr, http_conn)); -} - -PurpleHttpRequest * purple_http_conn_get_request(PurpleHttpConnection *http_conn) -{ - g_return_val_if_fail(http_conn != NULL, NULL); - - return http_conn->request; -} - -PurpleHttpCookieJar * purple_http_conn_get_cookie_jar( - PurpleHttpConnection *http_conn) -{ - return purple_http_request_get_cookie_jar(purple_http_conn_get_request( - http_conn)); -} - -PurpleConnection * purple_http_conn_get_purple_connection( - PurpleHttpConnection *http_conn) -{ - g_return_val_if_fail(http_conn != NULL, NULL); - - return http_conn->gc; -} - -void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn, - PurpleHttpProgressWatcher watcher, gpointer user_data, - gint interval_threshold) -{ - g_return_if_fail(http_conn != NULL); - - if (interval_threshold < 0) { - interval_threshold = - PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL; - } - - http_conn->watcher = watcher; - http_conn->watcher_user_data = user_data; - http_conn->watcher_interval_threshold = interval_threshold; -} - -static void purple_http_conn_notify_progress_watcher( - PurpleHttpConnection *hc) -{ - gint64 now; - gboolean reading_state; - int processed, total; - - g_return_if_fail(hc != NULL); - - if (hc->watcher == NULL) - return; - - reading_state = hc->is_reading; - if (reading_state) { - total = hc->length_expected; - processed = hc->length_got; - } else { - total = hc->request->contents_length; - processed = hc->request_contents_written; - if (total == 0) - total = -1; - } - if (total != -1 && total < processed) { - purple_debug_warning("http", "Processed too much\n"); - total = processed; - } - - now = g_get_monotonic_time(); - if (hc->watcher_last_call + hc->watcher_interval_threshold - > now && processed != total) - { - if (hc->watcher_delayed_handle) - return; - hc->watcher_delayed_handle = g_timeout_add_seconds( - 1 + hc->watcher_interval_threshold / 1000000, - purple_http_conn_notify_progress_watcher_timeout, hc); - return; - } - - if (hc->watcher_delayed_handle) - g_source_remove(hc->watcher_delayed_handle); - hc->watcher_delayed_handle = 0; - - hc->watcher_last_call = now; - hc->watcher(hc, reading_state, processed, total, hc->watcher_user_data); -} - -static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc) -{ - PurpleHttpConnection *hc = _hc; - - purple_http_conn_notify_progress_watcher(hc); - - return FALSE; -} - -/*** Cookie jar API ***********************************************************/ - -static PurpleHttpCookie * purple_http_cookie_new(const gchar *value); -void purple_http_cookie_free(PurpleHttpCookie *cookie); - -static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar, - const gchar *name, const gchar *value, time_t expires); - -static PurpleHttpCookie * purple_http_cookie_new(const gchar *value) -{ - PurpleHttpCookie *cookie = g_new0(PurpleHttpCookie, 1); - - cookie->value = g_strdup(value); - cookie->expires = -1; - - return cookie; -} - -void purple_http_cookie_free(PurpleHttpCookie *cookie) -{ - g_free(cookie->value); - g_free(cookie); -} - -void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar); - -PurpleHttpCookieJar * purple_http_cookie_jar_new(void) -{ - PurpleHttpCookieJar *cjar = g_new0(PurpleHttpCookieJar, 1); - - cjar->ref_count = 1; - cjar->tab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - (GDestroyNotify)purple_http_cookie_free); - - return cjar; -} - -void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar) -{ - g_hash_table_destroy(cookie_jar->tab); - g_free(cookie_jar); -} - -void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar) -{ - g_return_if_fail(cookie_jar != NULL); - - cookie_jar->ref_count++; -} - -PurpleHttpCookieJar * purple_http_cookie_jar_unref( - PurpleHttpCookieJar *cookie_jar) -{ - if (cookie_jar == NULL) - return NULL; - - g_return_val_if_fail(cookie_jar->ref_count > 0, NULL); - - cookie_jar->ref_count--; - if (cookie_jar->ref_count > 0) - return cookie_jar; - - purple_http_cookie_jar_free(cookie_jar); - return NULL; -} - -static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar, - GList *values) -{ - values = g_list_first(values); - while (values) { - const gchar *cookie = values->data; - const gchar *eqsign, *semicolon; - gchar *name, *value; - time_t expires = -1; - values = g_list_next(values); - - eqsign = strchr(cookie, '='); - semicolon = strchr(cookie, ';'); - - if (eqsign == NULL || eqsign == cookie || - (semicolon != NULL && semicolon < eqsign)) - { - if (purple_debug_is_unsafe()) - purple_debug_warning("http", - "Invalid cookie: [%s]\n", cookie); - else - purple_debug_warning("http", "Invalid cookie."); - continue; - } - - name = g_strndup(cookie, eqsign - cookie); - eqsign++; - if (semicolon != NULL) - value = g_strndup(eqsign, semicolon - eqsign); - else - value = g_strdup(eqsign); - - if (semicolon != NULL) { - GMatchInfo *match_info; - GRegex *re_expires = g_regex_new( /* XXX: make it static */ - "expires=([a-z0-9, :]+)", - G_REGEX_OPTIMIZE | G_REGEX_CASELESS, - G_REGEX_MATCH_NOTEMPTY, NULL); - - g_regex_match(re_expires, semicolon, 0, &match_info); - if (g_match_info_matches(match_info)) { - gchar *expire_date = - g_match_info_fetch(match_info, 1); - expires = purple_http_rfc1123_to_time( - expire_date); - g_free(expire_date); - } - g_match_info_free(match_info); - - g_regex_unref(re_expires); - } - - purple_http_cookie_jar_set_ext(cookie_jar, name, value, expires); - - g_free(name); - g_free(value); - } -} - -static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar) -{ - GHashTableIter it; - gchar *key; - PurpleHttpCookie *cookie; - GString *str; - time_t now = time(NULL); - - g_return_val_if_fail(cookie_jar != NULL, NULL); - - str = g_string_new(""); - - g_hash_table_iter_init(&it, cookie_jar->tab); - while (g_hash_table_iter_next(&it, (gpointer*)&key, - (gpointer*)&cookie)) - { - if (cookie->expires != -1 && cookie->expires != 0 && cookie->expires <= now) - continue; - g_string_append_printf(str, "%s=%s; ", key, cookie->value); - } - - if (str->len > 0) - g_string_truncate(str, str->len - 2); - return g_string_free(str, FALSE); -} - -void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar, - const gchar *name, const gchar *value) -{ - gchar *escaped_name = g_strdup(purple_url_encode(name)); - gchar *escaped_value = NULL; - - if (value) { - escaped_value = g_strdup(purple_url_encode(value)); - } - - purple_http_cookie_jar_set_ext(cookie_jar, escaped_name, escaped_value, -1); - - g_free(escaped_name); - g_free(escaped_value); -} - -static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar, - const gchar *name, const gchar *value, time_t expires) -{ - g_return_if_fail(cookie_jar != NULL); - g_return_if_fail(name != NULL); - - if (expires != -1 && expires != 0 && time(NULL) >= expires) - value = NULL; - - if (value != NULL) { - PurpleHttpCookie *cookie = purple_http_cookie_new(value); - cookie->expires = expires; - g_hash_table_insert(cookie_jar->tab, g_strdup(name), cookie); - } else - g_hash_table_remove(cookie_jar->tab, name); -} - -gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar, - const gchar *name) -{ - PurpleHttpCookie *cookie; - - g_return_val_if_fail(cookie_jar != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - cookie = g_hash_table_lookup(cookie_jar->tab, name); - if (!cookie) - return NULL; - - return g_strdup(purple_url_decode(cookie->value)); -} - -gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar) -{ - GHashTableIter it; - gchar *key; - PurpleHttpCookie *cookie; - GString *str = g_string_new(""); - - g_hash_table_iter_init(&it, cjar->tab); - while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&cookie)) - g_string_append_printf(str, "%s: %s (expires: %" G_GINT64_FORMAT - ")\n", key, cookie->value, (gint64)cookie->expires); - - if (str->len > 0) - g_string_truncate(str, str->len - 1); - return g_string_free(str, FALSE); -} - -gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar) -{ - g_return_val_if_fail(cookie_jar != NULL, TRUE); - - return g_hash_table_size(cookie_jar->tab) == 0; -} - -/*** HTTP Keep-Alive pool API *************************************************/ - -static void -purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost *host); - -static void -purple_http_keepalive_host_free(gpointer _host) -{ - PurpleHttpKeepaliveHost *host = _host; - - g_free(host->host); - - g_slist_free_full(host->queue, - (GDestroyNotify)purple_http_keepalive_pool_request_cancel); - g_slist_free_full(host->sockets, - (GDestroyNotify)purple_http_socket_close_free); - - if (host->process_queue_timeout > 0) { - g_source_remove(host->process_queue_timeout); - host->process_queue_timeout = 0; - } - - - g_free(host); -} - -PurpleHttpKeepalivePool * -purple_http_keepalive_pool_new(void) -{ - PurpleHttpKeepalivePool *pool = g_new0(PurpleHttpKeepalivePool, 1); - - pool->ref_count = 1; - pool->by_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - purple_http_keepalive_host_free); - - return pool; -} - -static void -purple_http_keepalive_pool_free(PurpleHttpKeepalivePool *pool) -{ - g_return_if_fail(pool != NULL); - - if (pool->is_destroying) - return; - pool->is_destroying = TRUE; - g_hash_table_destroy(pool->by_hash); - g_free(pool); -} - -void -purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool *pool) -{ - g_return_if_fail(pool != NULL); - - pool->ref_count++; -} - -PurpleHttpKeepalivePool * -purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool *pool) -{ - if (pool == NULL) - return NULL; - - g_return_val_if_fail(pool->ref_count > 0, NULL); - - pool->ref_count--; - if (pool->ref_count > 0) - return pool; - - purple_http_keepalive_pool_free(pool); - return NULL; -} - -static PurpleHttpKeepaliveRequest * -purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool, - PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl, - PurpleHttpSocketConnectCb cb, gpointer user_data) -{ - PurpleHttpKeepaliveRequest *req; - PurpleHttpKeepaliveHost *kahost; - gchar *hash; - - g_return_val_if_fail(pool != NULL, NULL); - g_return_val_if_fail(host != NULL, NULL); - - if (pool->is_destroying) { - purple_debug_error("http", "pool is destroying\n"); - return NULL; - } - - hash = purple_http_socket_hash(host, port, is_ssl); - kahost = g_hash_table_lookup(pool->by_hash, hash); - - if (kahost == NULL) { - kahost = g_new0(PurpleHttpKeepaliveHost, 1); - kahost->pool = pool; - kahost->host = g_strdup(host); - kahost->port = port; - kahost->is_ssl = is_ssl; - - g_hash_table_insert(pool->by_hash, g_strdup(hash), kahost); - } - - g_free(hash); - - req = g_new0(PurpleHttpKeepaliveRequest, 1); - req->gc = gc; - req->cb = cb; - req->user_data = user_data; - req->host = kahost; - - kahost->queue = g_slist_append(kahost->queue, req); - - purple_http_keepalive_host_process_queue(kahost); - - return req; -} - -static void -_purple_http_keepalive_socket_connected(PurpleHttpSocket *hs, - const gchar *error, gpointer _req) -{ - PurpleHttpKeepaliveRequest *req = _req; - - if (hs != NULL) - hs->use_count++; - - req->cb(hs, error, req->user_data); - g_free(req); -} - -static gboolean -_purple_http_keepalive_host_process_queue_cb(gpointer _host) -{ - PurpleHttpKeepaliveRequest *req; - PurpleHttpKeepaliveHost *host = _host; - PurpleHttpSocket *hs = NULL; - GSList *it; - guint sockets_count; - - g_return_val_if_fail(host != NULL, FALSE); - - host->process_queue_timeout = 0; - - if (host->queue == NULL) - return FALSE; - - sockets_count = 0; - it = host->sockets; - while (it != NULL) { - PurpleHttpSocket *hs_current = it->data; - - sockets_count++; - - if (!hs_current->is_busy) { - hs = hs_current; - break; - } - - it = g_slist_next(it); - } - - /* There are no free sockets and we cannot create another one. */ - if (hs == NULL && sockets_count >= host->pool->limit_per_host && - host->pool->limit_per_host > 0) - { - return FALSE; - } - - req = host->queue->data; - host->queue = g_slist_remove(host->queue, req); - - if (hs != NULL) { - if (purple_debug_is_verbose()) { - purple_debug_misc("http", "locking a (previously used) " - "socket: %p\n", hs); - } - - hs->is_busy = TRUE; - hs->use_count++; - - purple_http_keepalive_host_process_queue(host); - - req->cb(hs, NULL, req->user_data); - g_free(req); - - return FALSE; - } - - hs = purple_http_socket_connect_new(req->gc, req->host->host, - req->host->port, req->host->is_ssl, - _purple_http_keepalive_socket_connected, req); - if (hs == NULL) { - purple_debug_error("http", "failed creating new socket"); - return FALSE; - } - - req->hs = hs; - hs->is_busy = TRUE; - hs->host = host; - - if (purple_debug_is_verbose()) - purple_debug_misc("http", "locking a (new) socket: %p\n", hs); - - host->sockets = g_slist_append(host->sockets, hs); - - return FALSE; -} - -static void -purple_http_keepalive_host_process_queue(PurpleHttpKeepaliveHost *host) -{ - g_return_if_fail(host != NULL); - - if (host->process_queue_timeout > 0) - return; - - host->process_queue_timeout = g_timeout_add(0, - _purple_http_keepalive_host_process_queue_cb, host); -} - -static void -purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req) -{ - if (req == NULL) - return; - - if (req->host != NULL) - req->host->queue = g_slist_remove(req->host->queue, req); - - if (req->hs != NULL) { - if (G_LIKELY(req->host)) { - req->host->sockets = g_slist_remove(req->host->sockets, - req->hs); - } - purple_http_socket_close_free(req->hs); - /* req should already be free'd here */ - } else { - req->cb(NULL, _("Cancelled"), req->user_data); - g_free(req); - } -} - -static void -purple_http_keepalive_pool_release(PurpleHttpSocket *hs, gboolean invalidate) -{ - PurpleHttpKeepaliveHost *host; - - if (hs == NULL) - return; - - if (purple_debug_is_verbose()) - purple_debug_misc("http", "releasing a socket: %p\n", hs); - - if (hs->input_source > 0) { - g_source_remove(hs->input_source); - hs->input_source = 0; - } - - if (hs->output_source > 0) { - g_source_remove(hs->output_source); - hs->output_source = 0; - } - - hs->is_busy = FALSE; - host = hs->host; - - if (host == NULL) { - purple_http_socket_close_free(hs); - return; - } - - if (invalidate) { - host->sockets = g_slist_remove(host->sockets, hs); - purple_http_socket_close_free(hs); - } - - purple_http_keepalive_host_process_queue(host); -} - -void -purple_http_keepalive_pool_set_limit_per_host(PurpleHttpKeepalivePool *pool, - guint limit) -{ - g_return_if_fail(pool != NULL); - - pool->limit_per_host = limit; -} - -guint -purple_http_keepalive_pool_get_limit_per_host(PurpleHttpKeepalivePool *pool) -{ - g_return_val_if_fail(pool != NULL, 0); - - return pool->limit_per_host; -} - -/*** HTTP connection set API **************************************************/ - -PurpleHttpConnectionSet * -purple_http_connection_set_new(void) -{ - PurpleHttpConnectionSet *set; - - set = g_new0(PurpleHttpConnectionSet, 1); - set->connections = g_hash_table_new(g_direct_hash, g_direct_equal); - - return set; -} - -void -purple_http_connection_set_destroy(PurpleHttpConnectionSet *set) -{ - if (set == NULL) - return; - - set->is_destroying = TRUE; - - while (TRUE) { - GHashTableIter iter; - PurpleHttpConnection *http_conn; - - g_hash_table_iter_init(&iter, set->connections); - if (!g_hash_table_iter_next(&iter, (gpointer*)&http_conn, NULL)) - break; - - purple_http_conn_cancel(http_conn); - } - - g_hash_table_destroy(set->connections); - g_free(set); -} - -void -purple_http_connection_set_add(PurpleHttpConnectionSet *set, - PurpleHttpConnection *http_conn) -{ - if (set->is_destroying) - return; - if (http_conn->connection_set == set) - return; - if (http_conn->connection_set != NULL) { - purple_http_connection_set_remove(http_conn->connection_set, - http_conn); - } - g_hash_table_insert(set->connections, http_conn, GINT_TO_POINTER(1)); - http_conn->connection_set = set; -} - -static void -purple_http_connection_set_remove(PurpleHttpConnectionSet *set, - PurpleHttpConnection *http_conn) -{ - g_hash_table_remove(set->connections, http_conn); - if (http_conn->connection_set == set) - http_conn->connection_set = NULL; -} - -/*** Request API **************************************************************/ - -static void purple_http_request_free(PurpleHttpRequest *request); - -PurpleHttpRequest * purple_http_request_new(const gchar *url) -{ - PurpleHttpRequest *request; - - request = g_new0(PurpleHttpRequest, 1); - - request->ref_count = 1; - request->url = g_strdup(url); - request->headers = purple_http_headers_new(); - request->cookie_jar = purple_http_cookie_jar_new(); - request->keepalive_pool = purple_http_keepalive_pool_new(); - - request->timeout = PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT; - request->max_redirects = PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS; - request->http11 = TRUE; - request->max_length = PURPLE_HTTP_REQUEST_DEFAULT_MAX_LENGTH; - - return request; -} - -static void purple_http_request_free(PurpleHttpRequest *request) -{ - purple_http_headers_free(request->headers); - purple_http_cookie_jar_unref(request->cookie_jar); - purple_http_keepalive_pool_unref(request->keepalive_pool); - g_free(request->method); - g_free(request->contents); - g_free(request->url); - g_free(request); -} - -void purple_http_request_ref(PurpleHttpRequest *request) -{ - g_return_if_fail(request != NULL); - - request->ref_count++; -} - -PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request) -{ - if (request == NULL) - return NULL; - - g_return_val_if_fail(request->ref_count > 0, NULL); - - request->ref_count--; - if (request->ref_count > 0) - return request; - - purple_http_request_free(request); - return NULL; -} - -void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url) -{ - g_return_if_fail(request != NULL); - g_return_if_fail(url != NULL); - - g_free(request->url); - request->url = g_strdup(url); -} - -void purple_http_request_set_url_printf(PurpleHttpRequest *request, - const gchar *format, ...) -{ - va_list args; - gchar *value; - - g_return_if_fail(request != NULL); - g_return_if_fail(format != NULL); - - va_start(args, format); - value = g_strdup_vprintf(format, args); - va_end(args); - - purple_http_request_set_url(request, value); - g_free(value); -} - -const gchar * purple_http_request_get_url(PurpleHttpRequest *request) -{ - g_return_val_if_fail(request != NULL, NULL); - - return request->url; -} - -void purple_http_request_set_method(PurpleHttpRequest *request, const gchar *method) -{ - g_return_if_fail(request != NULL); - - g_free(request->method); - request->method = g_strdup(method); -} - -const gchar * purple_http_request_get_method(PurpleHttpRequest *request) -{ - g_return_val_if_fail(request != NULL, NULL); - - return request->method; -} - -static gboolean purple_http_request_is_method(PurpleHttpRequest *request, - const gchar *method) -{ - const gchar *rmethod; - - g_return_val_if_fail(request != NULL, FALSE); - g_return_val_if_fail(method != NULL, FALSE); - - rmethod = purple_http_request_get_method(request); - if (rmethod == NULL) - return (g_ascii_strcasecmp(method, "get") == 0); - return (g_ascii_strcasecmp(method, rmethod) == 0); -} - -void -purple_http_request_set_keepalive_pool(PurpleHttpRequest *request, - PurpleHttpKeepalivePool *pool) -{ - g_return_if_fail(request != NULL); - - if (pool != NULL) - purple_http_keepalive_pool_ref(pool); - - if (request->keepalive_pool != NULL) { - purple_http_keepalive_pool_unref(request->keepalive_pool); - request->keepalive_pool = NULL; - } - - if (pool != NULL) - request->keepalive_pool = pool; -} - -PurpleHttpKeepalivePool * -purple_http_request_get_keepalive_pool(PurpleHttpRequest *request) -{ - g_return_val_if_fail(request != NULL, FALSE); - - return request->keepalive_pool; -} - -void purple_http_request_set_contents(PurpleHttpRequest *request, - const gchar *contents, int length) -{ - g_return_if_fail(request != NULL); - g_return_if_fail(length >= -1); - - request->contents_reader = NULL; - request->contents_reader_data = NULL; - - g_free(request->contents); - if (contents == NULL || length == 0) { - request->contents = NULL; - request->contents_length = 0; - return; - } - - if (length == -1) - length = strlen(contents); - request->contents = g_memdup(contents, length); - request->contents_length = length; -} - -void purple_http_request_set_contents_reader(PurpleHttpRequest *request, - PurpleHttpContentReader reader, int contents_length, gpointer user_data) -{ - g_return_if_fail(request != NULL); - g_return_if_fail(reader != NULL); - g_return_if_fail(contents_length >= -1); - - g_free(request->contents); - request->contents = NULL; - request->contents_length = contents_length; - request->contents_reader = reader; - request->contents_reader_data = user_data; -} - -void purple_http_request_set_response_writer(PurpleHttpRequest *request, - PurpleHttpContentWriter writer, gpointer user_data) -{ - g_return_if_fail(request != NULL); - - if (writer == NULL) - user_data = NULL; - request->response_writer = writer; - request->response_writer_data = user_data; -} - -void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout) -{ - g_return_if_fail(request != NULL); - - if (timeout < -1) - timeout = -1; - - request->timeout = timeout; -} - -int purple_http_request_get_timeout(PurpleHttpRequest *request) -{ - g_return_val_if_fail(request != NULL, -1); - - return request->timeout; -} - -void purple_http_request_set_max_redirects(PurpleHttpRequest *request, - int max_redirects) -{ - g_return_if_fail(request != NULL); - - if (max_redirects < -1) - max_redirects = -1; - - request->max_redirects = max_redirects; -} - -int purple_http_request_get_max_redirects(PurpleHttpRequest *request) -{ - g_return_val_if_fail(request != NULL, -1); - - return request->max_redirects; -} - -void purple_http_request_set_cookie_jar(PurpleHttpRequest *request, - PurpleHttpCookieJar *cookie_jar) -{ - g_return_if_fail(request != NULL); - g_return_if_fail(cookie_jar != NULL); - - purple_http_cookie_jar_ref(cookie_jar); - purple_http_cookie_jar_unref(request->cookie_jar); - request->cookie_jar = cookie_jar; -} - -PurpleHttpCookieJar * purple_http_request_get_cookie_jar( - PurpleHttpRequest *request) -{ - g_return_val_if_fail(request != NULL, NULL); - - return request->cookie_jar; -} - -void purple_http_request_set_http11(PurpleHttpRequest *request, gboolean http11) -{ - g_return_if_fail(request != NULL); - - request->http11 = http11; -} - -gboolean purple_http_request_is_http11(PurpleHttpRequest *request) -{ - g_return_val_if_fail(request != NULL, FALSE); - - return request->http11; -} - -void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len) -{ - g_return_if_fail(request != NULL); - - if (max_len < 0 || max_len > PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH) - max_len = PURPLE_HTTP_REQUEST_HARD_MAX_LENGTH; - - request->max_length = max_len; -} - -int purple_http_request_get_max_len(PurpleHttpRequest *request) -{ - g_return_val_if_fail(request != NULL, -1); - - return request->max_length; -} - -void purple_http_request_header_set(PurpleHttpRequest *request, - const gchar *key, const gchar *value) -{ - g_return_if_fail(request != NULL); - g_return_if_fail(key != NULL); - - purple_http_headers_remove(request->headers, key); - if (value) - purple_http_headers_add(request->headers, key, value); -} - -void purple_http_request_header_set_printf(PurpleHttpRequest *request, - const gchar *key, const gchar *format, ...) -{ - va_list args; - gchar *value; - - g_return_if_fail(request != NULL); - g_return_if_fail(key != NULL); - g_return_if_fail(format != NULL); - - va_start(args, format); - value = g_strdup_vprintf(format, args); - va_end(args); - - purple_http_request_header_set(request, key, value); - g_free(value); -} - -void purple_http_request_header_add(PurpleHttpRequest *request, - const gchar *key, const gchar *value) -{ - g_return_if_fail(request != NULL); - g_return_if_fail(key != NULL); - - purple_http_headers_add(request->headers, key, value); -} - -/*** HTTP response API ********************************************************/ - -static PurpleHttpResponse * purple_http_response_new(void) -{ - PurpleHttpResponse *response = g_new0(PurpleHttpResponse, 1); - - return response; -} - -static void purple_http_response_free(PurpleHttpResponse *response) -{ - if (response->contents != NULL) - g_string_free(response->contents, TRUE); - g_free(response->error); - purple_http_headers_free(response->headers); - g_free(response); -} - -gboolean purple_http_response_is_successful(PurpleHttpResponse *response) -{ - int code; - - g_return_val_if_fail(response != NULL, FALSE); - - code = response->code; - - if (code <= 0) - return FALSE; - - /* TODO: HTTP/1.1 100 Continue */ - - if (code / 100 == 2) - return TRUE; - - return FALSE; -} - -int purple_http_response_get_code(PurpleHttpResponse *response) -{ - g_return_val_if_fail(response != NULL, 0); - - return response->code; -} - -const gchar * purple_http_response_get_error(PurpleHttpResponse *response) -{ - g_return_val_if_fail(response != NULL, NULL); - - if (response->error != NULL) - return response->error; - - if (!purple_http_response_is_successful(response)) { - static gchar errmsg[200]; - if (response->code <= 0) { - g_snprintf(errmsg, sizeof(errmsg), - _("Unknown HTTP error")); - } else { - g_snprintf(errmsg, sizeof(errmsg), - _("Invalid HTTP response code (%d)"), - response->code); - } - return errmsg; - } - - return NULL; -} - -gsize purple_http_response_get_data_len(PurpleHttpResponse *response) -{ - g_return_val_if_fail(response != NULL, 0); - - if (response->contents == NULL) - return 0; - - return response->contents->len; -} - -const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len) -{ - const gchar *ret = ""; - - g_return_val_if_fail(response != NULL, ""); - - if (response->contents != NULL) { - ret = response->contents->str; - if (len) - *len = response->contents->len; - } else { - if (len) - *len = 0; - } - - return ret; -} - -const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response) -{ - g_return_val_if_fail(response != NULL, NULL); - - return purple_http_headers_get_all(response->headers); -} - -const GList * purple_http_response_get_headers_by_name( - PurpleHttpResponse *response, const gchar *name) -{ - g_return_val_if_fail(response != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - return purple_http_headers_get_all_by_name(response->headers, name); -} - -const gchar * purple_http_response_get_header(PurpleHttpResponse *response, - const gchar *name) -{ - g_return_val_if_fail(response != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - return purple_http_headers_get(response->headers, name); -} - -/*** URL functions ************************************************************/ - -PurpleHttpURL * -purple_http_url_parse(const char *raw_url) -{ - PurpleHttpURL *url; - GMatchInfo *match_info; - - gchar *host_full, *tmp; - - g_return_val_if_fail(raw_url != NULL, NULL); - - if (!g_regex_match(purple_http_re_url, raw_url, 0, &match_info)) { - if (purple_debug_is_verbose() && purple_debug_is_unsafe()) { - purple_debug_warning("http", - "Invalid URL provided: %s\n", - raw_url); - } - return NULL; - } - - url = g_new0(PurpleHttpURL, 1); - - url->protocol = g_match_info_fetch(match_info, 1); - host_full = g_match_info_fetch(match_info, 2); - url->path = g_match_info_fetch(match_info, 3); - url->fragment = g_match_info_fetch(match_info, 4); - g_match_info_free(match_info); - - if (g_strcmp0(url->protocol, "") == 0) { - g_free(url->protocol); - url->protocol = NULL; - } else if (url->protocol != NULL) { - tmp = url->protocol; - url->protocol = g_ascii_strdown(url->protocol, -1); - g_free(tmp); - } - if (host_full[0] == '\0') { - g_free(host_full); - host_full = NULL; - } - if (url->path[0] == '\0') { - g_free(url->path); - url->path = NULL; - } - if ((url->protocol == NULL) != (host_full == NULL)) - purple_debug_warning("http", "Protocol or host not present " - "(unlikely case)\n"); - - if (host_full) { - gchar *port_str; - - if (!g_regex_match(purple_http_re_url_host, host_full, 0, - &match_info)) - { - if (purple_debug_is_verbose() && - purple_debug_is_unsafe()) - { - purple_debug_warning("http", - "Invalid host provided for URL: %s\n", - raw_url); - } - - g_free(host_full); - purple_http_url_free(url); - return NULL; - } - - url->username = g_match_info_fetch(match_info, 1); - url->password = g_match_info_fetch(match_info, 2); - url->host = g_match_info_fetch(match_info, 3); - port_str = g_match_info_fetch(match_info, 4); - - if (port_str && port_str[0]) - url->port = atoi(port_str); - - if (url->username[0] == '\0') { - g_free(url->username); - url->username = NULL; - } - if (url->password[0] == '\0') { - g_free(url->password); - url->password = NULL; - } - if (g_strcmp0(url->host, "") == 0) { - g_free(url->host); - url->host = NULL; - } else if (url->host != NULL) { - tmp = url->host; - url->host = g_ascii_strdown(url->host, -1); - g_free(tmp); - } - - g_free(port_str); - g_match_info_free(match_info); - - g_free(host_full); - host_full = NULL; - } - - if (url->host != NULL) { - if (url->protocol == NULL) - url->protocol = g_strdup("http"); - if (url->port == 0 && 0 == strcmp(url->protocol, "http")) - url->port = 80; - if (url->port == 0 && 0 == strcmp(url->protocol, "https")) - url->port = 443; - if (url->path == NULL) - url->path = g_strdup("/"); - if (url->path[0] != '/') - purple_debug_warning("http", - "URL path doesn't start with slash\n"); - } - - return url; -} - -void -purple_http_url_free(PurpleHttpURL *parsed_url) -{ - if (parsed_url == NULL) - return; - - g_free(parsed_url->protocol); - g_free(parsed_url->username); - g_free(parsed_url->password); - g_free(parsed_url->host); - g_free(parsed_url->path); - g_free(parsed_url->fragment); - g_free(parsed_url); -} - -void -purple_http_url_relative(PurpleHttpURL *base_url, PurpleHttpURL *relative_url) -{ - g_return_if_fail(base_url != NULL); - g_return_if_fail(relative_url != NULL); - - if (relative_url->host) { - g_free(base_url->protocol); - base_url->protocol = g_strdup(relative_url->protocol); - g_free(base_url->username); - base_url->username = g_strdup(relative_url->username); - g_free(base_url->password); - base_url->password = g_strdup(relative_url->password); - g_free(base_url->host); - base_url->host = g_strdup(relative_url->host); - base_url->port = relative_url->port; - - g_free(base_url->path); - base_url->path = NULL; - } - - if (relative_url->path) { - if (relative_url->path[0] == '/' || - base_url->path == NULL) - { - g_free(base_url->path); - base_url->path = g_strdup(relative_url->path); - } else { - gchar *last_slash = strrchr(base_url->path, '/'); - gchar *tmp; - if (last_slash == NULL) - base_url->path[0] = '\0'; - else - last_slash[1] = '\0'; - tmp = base_url->path; - base_url->path = g_strconcat(base_url->path, - relative_url->path, NULL); - g_free(tmp); - } - } - - g_free(base_url->fragment); - base_url->fragment = g_strdup(relative_url->fragment); -} - -gchar * -purple_http_url_print(PurpleHttpURL *parsed_url) -{ - GString *url = g_string_new(""); - gboolean before_host_printed = FALSE, host_printed = FALSE; - gboolean port_is_default = FALSE; - - if (parsed_url->protocol) { - g_string_append_printf(url, "%s://", parsed_url->protocol); - before_host_printed = TRUE; - if (parsed_url->port == 80 && 0 == strcmp(parsed_url->protocol, - "http")) - port_is_default = TRUE; - if (parsed_url->port == 443 && 0 == strcmp(parsed_url->protocol, - "https")) - port_is_default = TRUE; - } - if (parsed_url->username || parsed_url->password) { - if (parsed_url->username) - g_string_append(url, parsed_url->username); - g_string_append_c(url, ':'); - if (parsed_url->password) - g_string_append(url, parsed_url->password); - g_string_append(url, "@"); - before_host_printed = TRUE; - } - if (parsed_url->host || parsed_url->port) { - if (!parsed_url->host) - g_string_append_printf(url, "{???}:%d", - parsed_url->port); - else { - g_string_append(url, parsed_url->host); - if (!port_is_default) - g_string_append_printf(url, ":%d", - parsed_url->port); - } - host_printed = TRUE; - } - if (parsed_url->path) { - if (!host_printed && before_host_printed) - g_string_append(url, "{???}"); - g_string_append(url, parsed_url->path); - } - if (parsed_url->fragment) - g_string_append_printf(url, "#%s", parsed_url->fragment); - - return g_string_free(url, FALSE); -} - -const gchar * -purple_http_url_get_protocol(const PurpleHttpURL *parsed_url) -{ - g_return_val_if_fail(parsed_url != NULL, NULL); - - return parsed_url->protocol; -} - -const gchar * -purple_http_url_get_username(const PurpleHttpURL *parsed_url) -{ - g_return_val_if_fail(parsed_url != NULL, NULL); - - return parsed_url->username; -} - -const gchar * -purple_http_url_get_password(const PurpleHttpURL *parsed_url) -{ - g_return_val_if_fail(parsed_url != NULL, NULL); - - return parsed_url->password; -} - -const gchar * -purple_http_url_get_host(const PurpleHttpURL *parsed_url) -{ - g_return_val_if_fail(parsed_url != NULL, NULL); - - return parsed_url->host; -} - -int -purple_http_url_get_port(const PurpleHttpURL *parsed_url) -{ - g_return_val_if_fail(parsed_url != NULL, 0); - - return parsed_url->port; -} - -const gchar * -purple_http_url_get_path(const PurpleHttpURL *parsed_url) -{ - g_return_val_if_fail(parsed_url != NULL, NULL); - - return parsed_url->path; -} - -const gchar * -purple_http_url_get_fragment(const PurpleHttpURL *parsed_url) -{ - g_return_val_if_fail(parsed_url != NULL, NULL); - - return parsed_url->fragment; -} - -/*** HTTP Subsystem ***********************************************************/ - -void purple_http_init(void) -{ - purple_http_re_url = g_regex_new("^" - - "(?:" /* host part beginning */ - "([a-z]+)\\:/*" /* protocol */ - "([^/]+)" /* username, password, host, port */ - ")?" /* host part ending */ - - "([^#]*)" /* path */ - - "(?:#" "(.*)" ")?" /* fragment */ - - "$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS, - G_REGEX_MATCH_NOTEMPTY, NULL); - - purple_http_re_url_host = g_regex_new("^" - - "(?:" /* user credentials part beginning */ - "([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+)" /* username */ - "(?::([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+))" /* password */ - "@)?" /* user credentials part ending */ - - "([a-z0-9.-]+)" /* host */ - "(?::([0-9]+))?" /* port*/ - - "$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS, - G_REGEX_MATCH_NOTEMPTY, NULL); - - purple_http_re_rfc1123 = g_regex_new( - "^[a-z]+, " /* weekday */ - "([0-9]+) " /* date */ - "([a-z]+) " /* month */ - "([0-9]+) " /* year */ - "([0-9]+:[0-9]+:[0-9]+) " /* time */ - "(?:GMT|UTC)$", - G_REGEX_OPTIMIZE | G_REGEX_CASELESS, - G_REGEX_MATCH_NOTEMPTY, NULL); - - purple_http_hc_list = NULL; - purple_http_hc_by_ptr = g_hash_table_new(g_direct_hash, g_direct_equal); - purple_http_hc_by_gc = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify)g_list_free); - purple_http_cancelling_gc = g_hash_table_new(g_direct_hash, g_direct_equal); -} - -static void purple_http_foreach_conn_cancel(gpointer _hc, gpointer user_data) -{ - PurpleHttpConnection *hc = _hc; - purple_http_conn_cancel(hc); -} - -void purple_http_uninit(void) -{ - g_regex_unref(purple_http_re_url); - purple_http_re_url = NULL; - g_regex_unref(purple_http_re_url_host); - purple_http_re_url_host = NULL; - g_regex_unref(purple_http_re_rfc1123); - purple_http_re_rfc1123 = NULL; - - if (purple_http_hc_list != NULL) { - g_list_foreach(purple_http_hc_list, purple_http_foreach_conn_cancel, NULL); - } - - if (purple_http_hc_list != NULL || - 0 != g_hash_table_size(purple_http_hc_by_ptr) || - 0 != g_hash_table_size(purple_http_hc_by_gc)) - purple_debug_warning("http", - "Couldn't cleanup all connections.\n"); - - g_list_free(purple_http_hc_list); - purple_http_hc_list = NULL; - g_hash_table_destroy(purple_http_hc_by_gc); - purple_http_hc_by_gc = NULL; - g_hash_table_destroy(purple_http_hc_by_ptr); - purple_http_hc_by_ptr = NULL; - g_hash_table_destroy(purple_http_cancelling_gc); - purple_http_cancelling_gc = NULL; -} diff --git a/libpurple/http.h b/libpurple/http.h deleted file mode 100644 index 7377769cf1..0000000000 --- a/libpurple/http.h +++ /dev/null @@ -1,984 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef PURPLE_HTTP_H -#define PURPLE_HTTP_H -/** - * SECTION:http - * @section_id: libpurple-http - * @short_description: <filename>http.h</filename> - * @title: HTTP API - */ - -#include <glib.h> - -#include "connection.h" - -/** - * PurpleHttpRequest: - * - * A structure containing all data required to generate a single HTTP request. - */ -typedef struct _PurpleHttpRequest PurpleHttpRequest; - -/** - * PurpleHttpConnection: - * - * A representation of actually running HTTP request. Can be used to cancel the - * request. - */ -typedef struct _PurpleHttpConnection PurpleHttpConnection; - -/** - * PurpleHttpResponse: - * - * All information got with response for HTTP request. - */ -typedef struct _PurpleHttpResponse PurpleHttpResponse; - -/** - * PurpleHttpURL: - * - * Parsed representation for the URL. - */ -typedef struct _PurpleHttpURL PurpleHttpURL; - -/** - * PurpleHttpCookieJar: - * - * An collection of cookies, got from HTTP response or provided for HTTP - * request. - */ -typedef struct _PurpleHttpCookieJar PurpleHttpCookieJar; - -/** - * PurpleHttpKeepalivePool: - * - * A pool of TCP connections for HTTP Keep-Alive session. - */ -typedef struct _PurpleHttpKeepalivePool PurpleHttpKeepalivePool; - -/** - * PurpleHttpConnectionSet: - * - * A set of running HTTP requests. Can be used to cancel all of them at once. - */ -typedef struct _PurpleHttpConnectionSet PurpleHttpConnectionSet; - -/** - * PurpleHttpCallback: - * - * An callback called after performing (successfully or not) HTTP request. - */ -typedef void (*PurpleHttpCallback)(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, gpointer user_data); - -/** - * PurpleHttpContentReaderCb: - * - * An callback called after storing data requested by PurpleHttpContentReader. - */ -typedef void (*PurpleHttpContentReaderCb)(PurpleHttpConnection *http_conn, - gboolean success, gboolean eof, size_t stored); - -/** - * PurpleHttpContentReader: - * @http_conn: Connection, which requests data. - * @buffer: Buffer to store data to (with offset ignored). - * @offset: Position, from where to read data. - * @length: Length of data to read. - * @user_data: The user data passed with callback function. - * @cb: The function to call after storing data to buffer. - * - * An callback for getting large request contents (ie. from file stored on - * disk). - */ -typedef void (*PurpleHttpContentReader)(PurpleHttpConnection *http_conn, - gchar *buffer, size_t offset, size_t length, gpointer user_data, - PurpleHttpContentReaderCb cb); - -/** - * PurpleHttpContentWriter: - * @http_conn: Connection, which requests data. - * @response: Response at point got so far (may change later). - * @buffer: Buffer to read data from (with offset ignored). - * @offset: Position of data got (its value is offset + length of - * previous call), can be safely ignored. - * @length: Length of data read. - * @user_data: The user data passed with callback function. - * - * An callback for writting large response contents. - * - * Returns: TRUE, if succeeded, FALSE otherwise. - */ -typedef gboolean (*PurpleHttpContentWriter)(PurpleHttpConnection *http_conn, - PurpleHttpResponse *response, const gchar *buffer, size_t offset, - size_t length, gpointer user_data); - -/** - * PurpleHttpProgressWatcher: - * @http_conn: The HTTP Connection. - * @reading_state: FALSE, is we are sending the request, TRUE, when reading - * the response. - * @processed: The amount of data already processed. - * @total: Total amount of data (in current state). - * @user_data: The user data passed with callback function. - * - * An callback for watching HTTP connection progress. - */ -typedef void (*PurpleHttpProgressWatcher)(PurpleHttpConnection *http_conn, - gboolean reading_state, int processed, int total, gpointer user_data); - -G_BEGIN_DECLS - -/**************************************************************************/ -/* Performing HTTP requests */ -/**************************************************************************/ - -/** - * purple_http_get: (skip) - * @gc: The connection for which the request is needed, or NULL. - * @callback: (scope call): The callback function. - * @user_data: The user data to pass to the callback function. - * @url: The URL. - * - * Fetches the data from a URL with GET request, and passes it to a callback - * function. - * - * Returns: The HTTP connection struct. - */ -PurpleHttpConnection * purple_http_get(PurpleConnection *gc, - PurpleHttpCallback callback, gpointer user_data, const gchar *url); - -/** - * purple_http_get_printf: (skip) - * @gc: The connection for which the request is needed, or NULL. - * @callback: (scope call): The callback function. - * @user_data: The user data to pass to the callback function. - * @format: The format string. - * @...: The parameters to insert into the format string. - * - * Constructs an URL and fetches the data from it with GET request, then passes - * it to a callback function. - * - * Returns: The HTTP connection struct. - */ -PurpleHttpConnection * purple_http_get_printf(PurpleConnection *gc, - PurpleHttpCallback callback, gpointer user_data, - const gchar *format, ...) G_GNUC_PRINTF(4, 5); - -/** - * purple_http_request: (skip) - * @gc: The connection for which the request is needed, or NULL. - * @request: The request. - * @callback: (scope call): The callback function. - * @user_data: The user data to pass to the callback function. - * - * Fetches a HTTP request and passes the response to a callback function. - * Provided request struct can be shared by multiple http requests but can not - * be modified when any of these is running. - * - * Returns: The HTTP connection struct. - */ -PurpleHttpConnection * purple_http_request(PurpleConnection *gc, - PurpleHttpRequest *request, PurpleHttpCallback callback, - gpointer user_data); - -/**************************************************************************/ -/* HTTP connection API */ -/**************************************************************************/ - -/** - * purple_http_conn_cancel: (skip) - * @http_conn: The data returned when you initiated the HTTP request. - * - * Cancel a pending HTTP request. - */ -void purple_http_conn_cancel(PurpleHttpConnection *http_conn); - -/** - * purple_http_conn_cancel_all: (skip) - * @gc: The handle. - * - * Cancels all HTTP connections associated with the specified handle. - */ -void purple_http_conn_cancel_all(PurpleConnection *gc); - -/** - * purple_http_conn_is_running: (skip) - * @http_conn: The HTTP connection (may be invalid pointer). - * - * Checks, if provided HTTP request is running. - * - * Returns: TRUE, if provided connection is currently running. - */ -gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn); - -/** - * purple_http_conn_get_request: (skip) - * @http_conn: The HTTP connection. - * - * Gets PurpleHttpRequest used for specified HTTP connection. - * - * Returns: The PurpleHttpRequest object. - */ -PurpleHttpRequest * purple_http_conn_get_request( - PurpleHttpConnection *http_conn); - -/** - * purple_http_conn_get_cookie_jar: (skip) - * @http_conn: The HTTP connection. - * - * Gets cookie jar used within connection. - * - * Returns: The cookie jar. - */ -PurpleHttpCookieJar * purple_http_conn_get_cookie_jar( - PurpleHttpConnection *http_conn); - -/** - * purple_http_conn_get_purple_connection: (skip) - * @http_conn: The HTTP connection. - * - * Gets PurpleConnection tied with specified HTTP connection. - * - * Returns: The PurpleConnection object. - */ -PurpleConnection * purple_http_conn_get_purple_connection( - PurpleHttpConnection *http_conn); - -/** - * purple_http_conn_set_progress_watcher: (skip) - * @http_conn: The HTTP connection. - * @watcher: (scope call): The watcher. - * @user_data: The user data to pass to the callback function. - * @interval_threshold: Minimum interval (in microseconds) of calls to - * watcher, or -1 for default. - * - * Sets the watcher, called after writing or reading data to/from HTTP stream. - * May be used for updating transfer progress gauge. - */ -void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn, - PurpleHttpProgressWatcher watcher, gpointer user_data, - gint interval_threshold); - - -/**************************************************************************/ -/* URL processing API */ -/**************************************************************************/ - -/** - * purple_http_url_parse: (skip) - * @url: The URL to parse. - * - * Parses a URL. - * - * The returned data must be freed with purple_http_url_free. - * - * Returns: The parsed url or NULL, if the URL is invalid. - */ -PurpleHttpURL * -purple_http_url_parse(const char *url); - -/** - * purple_http_url_free: (skip) - * @parsed_url: The parsed URL struct, or NULL. - * - * Frees the parsed URL struct. - */ -void -purple_http_url_free(PurpleHttpURL *parsed_url); - -/** - * purple_http_url_relative: (skip) - * @base_url: The base URL. The result is stored here. - * @relative_url: The relative URL. - * - * Converts the base URL to the absolute form of the provided relative URL. - * - * Example: "https://example.com/path/to/file.html" + "subdir/other-file.html" = - * "https://example.com/path/to/subdir/another-file.html" - */ -void -purple_http_url_relative(PurpleHttpURL *base_url, PurpleHttpURL *relative_url); - -/** - * purple_http_url_print: (skip) - * @parsed_url: The URL struct. - * - * Converts the URL struct to the printable form. The result may not be a valid - * URL (in cases, when the struct doesn't have all fields filled properly). - * - * The result must be g_free'd. - * - * Returns: The printable form of the URL. - */ -gchar * -purple_http_url_print(PurpleHttpURL *parsed_url); - -/** - * purple_http_url_get_protocol: (skip) - * @parsed_url: The URL struct. - * - * Gets the protocol part of URL. - * - * Returns: The protocol. - */ -const gchar * -purple_http_url_get_protocol(const PurpleHttpURL *parsed_url); - -/** - * purple_http_url_get_username: (skip) - * @parsed_url: The URL struct. - * - * Gets the username part of URL. - * - * Returns: The username. - */ -const gchar * -purple_http_url_get_username(const PurpleHttpURL *parsed_url); - -/** - * purple_http_url_get_password: (skip) - * @parsed_url: The URL struct. - * - * Gets the password part of URL. - * - * Returns: The password. - */ -const gchar * -purple_http_url_get_password(const PurpleHttpURL *parsed_url); - -/** - * purple_http_url_get_host: (skip) - * @parsed_url: The URL struct. - * - * Gets the hostname part of URL. - * - * Returns: The hostname. - */ -const gchar * -purple_http_url_get_host(const PurpleHttpURL *parsed_url); - -/** - * purple_http_url_get_port: (skip) - * @parsed_url: The URL struct. - * - * Gets the port part of URL. - * - * Returns: The port number. - */ -int -purple_http_url_get_port(const PurpleHttpURL *parsed_url); - -/** - * purple_http_url_get_path: (skip) - * @parsed_url: The URL struct. - * - * Gets the path part of URL. - * - * Returns: The path. - */ -const gchar * -purple_http_url_get_path(const PurpleHttpURL *parsed_url); - -/** - * purple_http_url_get_fragment: (skip) - * @parsed_url: The URL struct. - * - * Gets the fragment part of URL. - * - * Returns: The fragment. - */ -const gchar * -purple_http_url_get_fragment(const PurpleHttpURL *parsed_url); - - -/**************************************************************************/ -/* Cookie jar API */ -/**************************************************************************/ - -/** - * purple_http_cookie_jar_new: (skip) - * - * Creates new cookie jar, - * - * Returns: empty cookie jar. - */ -PurpleHttpCookieJar * purple_http_cookie_jar_new(void); - -/** - * purple_http_cookie_jar_ref: (skip) - * @cookie_jar: The cookie jar. - * - * Increment the reference count. - */ -void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar); - -/** - * purple_http_cookie_jar_unref: (skip) - * @cookie_jar: The cookie jar. - * - * Decrement the reference count. - * - * If the reference count reaches zero, the cookie jar will be freed. - * - * Returns: @cookie_jar or %NULL if the reference count reached zero. - */ -PurpleHttpCookieJar * purple_http_cookie_jar_unref( - PurpleHttpCookieJar *cookie_jar); - -/** - * purple_http_cookie_jar_set: (skip) - * @cookie_jar: The cookie jar. - * @name: Cookie name. - * @value: Cookie contents. - * - * Sets the cookie. - */ -void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar, - const gchar *name, const gchar *value); - -/** - * purple_http_cookie_jar_get: (skip) - * @cookie_jar: The cookie jar. - * @name: Cookie name. - * - * Gets the cookie. - * - * The result must be g_free'd. - * - * Returns: Cookie contents, or NULL, if cookie doesn't exists. - */ -gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar, - const gchar *name); - -/** - * purple_http_cookie_jar_is_empty: (skip) - * @cookie_jar: The cookie jar. - * - * Checks, if the cookie jar contains any cookies. - * - * Returns: TRUE, if cookie jar contains any cookie, FALSE otherwise. - */ -gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar); - - -/**************************************************************************/ -/* HTTP Request API */ -/**************************************************************************/ - -/** - * purple_http_request_new: (skip) - * @url: The URL to request for, or NULL to leave empty (to be set with - * purple_http_request_set_url). - * - * Creates the new instance of HTTP request configuration. - * - * Returns: The new instance of HTTP request struct. - */ -PurpleHttpRequest * purple_http_request_new(const gchar *url); - -/** - * purple_http_request_ref: (skip) - * @request: The request. - * - * Increment the reference count. - */ -void purple_http_request_ref(PurpleHttpRequest *request); - -/** - * purple_http_request_unref: (skip) - * @request: The request. - * - * Decrement the reference count. - * - * If the reference count reaches zero, the http request struct will be freed. - * - * Returns: @request or %NULL if the reference count reached zero. - */ -PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request); - -/** - * purple_http_request_set_url: (skip) - * @request: The request. - * @url: The url. - * - * Sets URL for HTTP request. - */ -void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url); - -/** - * purple_http_request_set_url_printf: (skip) - * @request: The request. - * @format: The format string. - * @...: The parameters to insert into the format string. - * - * Constructs and sets an URL for HTTP request. - */ -void purple_http_request_set_url_printf(PurpleHttpRequest *request, - const gchar *format, ...) G_GNUC_PRINTF(2, 3); - -/** - * purple_http_request_get_url: (skip) - * @request: The request. - * - * Gets URL set for the HTTP request. - * - * Returns: URL set for this request. - */ -const gchar * purple_http_request_get_url(PurpleHttpRequest *request); - -/** - * purple_http_request_set_method: (skip) - * @request: The request. - * @method: The method, or NULL for default. - * - * Sets custom HTTP method used for the request. - */ -void purple_http_request_set_method(PurpleHttpRequest *request, - const gchar *method); - -/** - * purple_http_request_get_method: (skip) - * @request: The request. - * - * Gets HTTP method set for the request. - * - * Returns: The method. - */ -const gchar * purple_http_request_get_method(PurpleHttpRequest *request); - -/** - * purple_http_request_set_keepalive_pool: (skip) - * @request: The request. - * @pool: The new KeepAlive pool, or NULL to reset. - * - * Sets HTTP KeepAlive connections pool for the request. - * - * It increases pool's reference count. - */ -void -purple_http_request_set_keepalive_pool(PurpleHttpRequest *request, - PurpleHttpKeepalivePool *pool); - -/** - * purple_http_request_get_keepalive_pool: (skip) - * @request: The request. - * - * Gets HTTP KeepAlive connections pool associated with the request. - * - * It doesn't affect pool's reference count. - * - * Returns: The KeepAlive pool, used for the request. - */ -PurpleHttpKeepalivePool * -purple_http_request_get_keepalive_pool(PurpleHttpRequest *request); - -/** - * purple_http_request_set_contents: (skip) - * @request: The request. - * @contents: The contents. - * @length: The length of contents (-1 if it's a NULL-terminated string) - * - * Sets contents of HTTP request (for example, POST data). - */ -void purple_http_request_set_contents(PurpleHttpRequest *request, - const gchar *contents, int length); - -/** - * purple_http_request_set_contents_reader: (skip) - * @request: The request. - * @reader: (scope call): The reader callback. - * @contents_length: The size of all contents. - * @user_data: The user data to pass to the callback function. - * - * Sets contents reader for HTTP request, used mainly for possible large - * uploads. - */ -void purple_http_request_set_contents_reader(PurpleHttpRequest *request, - PurpleHttpContentReader reader, int contents_length, gpointer user_data); - -/** - * purple_http_request_set_response_writer: (skip) - * @request: The request. - * @writer: (scope call): The writer callback, or %NULL to remove existing. - * @user_data: The user data to pass to the callback function. - * - * Set contents writer for HTTP response. - */ -void purple_http_request_set_response_writer(PurpleHttpRequest *request, - PurpleHttpContentWriter writer, gpointer user_data); - -/** - * purple_http_request_set_timeout: (skip) - * @request: The request. - * @timeout: Time (in seconds) after that timeout will be cancelled, - * -1 for infinite time. - * - * Set maximum amount of time, that request is allowed to run. - */ -void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout); - -/** - * purple_http_request_get_timeout: (skip) - * @request: The request. - * - * Get maximum amount of time, that request is allowed to run. - * - * Returns: Timeout currently set (-1 for infinite). - */ -int purple_http_request_get_timeout(PurpleHttpRequest *request); - -/** - * purple_http_request_set_max_redirects: (skip) - * @request: The request. - * @max_redirects: Maximum amount of redirects, or -1 for unlimited. - * - * Sets maximum amount of redirects. - */ -void purple_http_request_set_max_redirects(PurpleHttpRequest *request, - int max_redirects); - -/** - * purple_http_request_get_max_redirects: (skip) - * @request: The request. - * - * Gets maximum amount of redirects. - * - * Returns: Current maximum amount of redirects (-1 for unlimited). - */ -int purple_http_request_get_max_redirects(PurpleHttpRequest *request); - -/** - * purple_http_request_set_cookie_jar: (skip) - * @request: The request. - * @cookie_jar: The cookie jar. - * - * Sets cookie jar used for the request. - */ -void purple_http_request_set_cookie_jar(PurpleHttpRequest *request, - PurpleHttpCookieJar *cookie_jar); - -/** - * purple_http_request_get_cookie_jar: (skip) - * @request: The request. - * - * Gets cookie jar used for the request. - * - * Returns: The cookie jar. - */ -PurpleHttpCookieJar * purple_http_request_get_cookie_jar( - PurpleHttpRequest *request); - -/** - * purple_http_request_set_http11: (skip) - * @request: The request. - * @http11: TRUE for HTTP/1.1, FALSE for HTTP/1.0. - * - * Sets HTTP version to use. - */ -void purple_http_request_set_http11(PurpleHttpRequest *request, - gboolean http11); - -/** - * purple_http_request_is_http11: (skip) - * @request: The request. - * - * Gets used HTTP version. - * - * Returns: TRUE, if we use HTTP/1.1, FALSE for HTTP/1.0. - */ -gboolean purple_http_request_is_http11(PurpleHttpRequest *request); - -/** - * purple_http_request_set_max_len: (skip) - * @request: The request. - * @max_len: Maximum length of response to read (-1 for the maximum - * supported amount). - * - * Sets maximum length of response content to read. - * - * Headers length doesn't count here. - * - */ -void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len); - -/** - * purple_http_request_get_max_len: (skip) - * @request: The request. - * - * Gets maximum length of response content to read. - * - * Returns: Maximum length of response to read, or -1 if unlimited. - */ -int purple_http_request_get_max_len(PurpleHttpRequest *request); - -/** - * purple_http_request_header_set: (skip) - * @request: The request. - * @key: A header to be set. - * @value: A value to set, or NULL to remove specified header. - * - * Sets (replaces, if exists) specified HTTP request header with provided value. - * - * See purple_http_request_header_add(). - */ -void purple_http_request_header_set(PurpleHttpRequest *request, - const gchar *key, const gchar *value); - -/** - * purple_http_request_header_set_printf: (skip) - * @request: The request. - * @key: A header to be set. - * @format: The format string. - * - * Constructs and sets (replaces, if exists) specified HTTP request header. - */ -void purple_http_request_header_set_printf(PurpleHttpRequest *request, - const gchar *key, const gchar *format, ...) G_GNUC_PRINTF(3, 4); - -/** - * purple_http_request_header_add: (skip) - * @request: The request. - * @key: A header to be set. - * @value: A value to set. - * - * Adds (without replacing, if exists) an HTTP request header. - * - * See purple_http_request_header_set(). - */ -void purple_http_request_header_add(PurpleHttpRequest *request, - const gchar *key, const gchar *value); - - -/**************************************************************************/ -/* HTTP Keep-Alive pool API */ -/**************************************************************************/ - -/** - * purple_http_keepalive_pool_new: (skip) - * - * Creates a new HTTP Keep-Alive pool. - */ -PurpleHttpKeepalivePool * -purple_http_keepalive_pool_new(void); - -/** - * purple_http_keepalive_pool_ref: (skip) - * @pool: The HTTP Keep-Alive pool. - * - * Increment the reference count. - */ -void -purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool *pool); - -/** - * purple_http_keepalive_pool_unref: (skip) - * @pool: The HTTP Keep-Alive pool. - * - * Decrement the reference count. - * - * If the reference count reaches zero, the pool will be freed and all - * connections will be closed. - * - * Returns: @pool or %NULL if the reference count reached zero. - */ -PurpleHttpKeepalivePool * -purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool *pool); - -/** - * purple_http_keepalive_pool_set_limit_per_host: (skip) - * @pool: The HTTP Keep-Alive pool. - * @limit: The new limit, 0 for unlimited. - * - * Sets maximum allowed number of connections to specific host-triple (is_ssl + - * hostname + port). - */ -void -purple_http_keepalive_pool_set_limit_per_host(PurpleHttpKeepalivePool *pool, - guint limit); - -/** - * purple_http_keepalive_pool_get_limit_per_host: (skip) - * @pool: The HTTP Keep-Alive pool. - * - * Gets maximum allowed number of connections to specific host-triple (is_ssl + - * hostname + port). - * - * Returns: The limit. - */ -guint -purple_http_keepalive_pool_get_limit_per_host(PurpleHttpKeepalivePool *pool); - - -/**************************************************************************/ -/* HTTP connection set API */ -/**************************************************************************/ - -/** - * purple_http_connection_set_new: (skip) - * - * Creates a new connection set. - * - * Returns: A new connection set. - */ -PurpleHttpConnectionSet * -purple_http_connection_set_new(void); - -/** - * purple_http_connection_set_destroy: (skip) - * @set: The connection set to be destroyed - * - * Destroys connection set @set. - */ -void -purple_http_connection_set_destroy(PurpleHttpConnectionSet *set); - -/** - * purple_http_connection_set_add: (skip) - * @set: The connection set to add to - * @http_conn: The connection to add - * - * Adds @http_conn to @set. - */ -void -purple_http_connection_set_add(PurpleHttpConnectionSet *set, - PurpleHttpConnection *http_conn); - - -/**************************************************************************/ -/* HTTP response API */ -/**************************************************************************/ - -/** - * purple_http_response_is_successful: (skip) - * @response: The response. - * - * Checks, if HTTP request was performed successfully. - * - * Returns: TRUE, if request was performed successfully. - */ -gboolean purple_http_response_is_successful(PurpleHttpResponse *response); - -/** - * purple_http_response_get_code: (skip) - * @response: The response. - * - * Gets HTTP response code. - * - * Returns: HTTP response code. - */ -int purple_http_response_get_code(PurpleHttpResponse *response); - -/** - * purple_http_response_get_error: (skip) - * @response: The response. - * - * Gets error description. - * - * Returns: Localized error description or NULL, if there was no error. - */ -const gchar * purple_http_response_get_error(PurpleHttpResponse *response); - -/** - * purple_http_response_get_data_len: (skip) - * @response: The response. - * - * Gets HTTP response data length. - * - * Returns: Data length; - */ -gsize purple_http_response_get_data_len(PurpleHttpResponse *response); - -/** - * purple_http_response_get_data: (skip) - * @response: The response. - * @len: Return address for the size of the data. Can be NULL. - * - * Gets HTTP response data. - * - * Response data is not written, if writer callback was set for request. - * - * Returns: The data. - */ -const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len); - -/** - * purple_http_response_get_all_headers: (skip) - * @response: The response. - * - * Gets all headers got with response. - * - * Returns: (element-type PurpleKeyValuePair) (transfer none): Keys are header - * field names (gchar*) and values are its contents (gchar*). - */ -const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response); - -/** - * purple_http_response_get_headers_by_name: (skip) - * @response: The response. - * @name: The name of header field. - * - * Gets all headers with specified name got with response. - * - * Returns: (element-type gchar*) (transfer none): Header field record contents. - */ -const GList * purple_http_response_get_headers_by_name( - PurpleHttpResponse *response, const gchar *name); - -/** - * purple_http_response_get_header: (skip) - * @response: The response. - * @name: The name of header field. - * - * Gets one header contents with specified name got with response. - * - * To get all headers with the same name, use - * purple_http_response_get_headers_by_name instead. - * - * Returns: Header field contents or NULL, if there is no such one. - */ -const gchar * purple_http_response_get_header(PurpleHttpResponse *response, - const gchar *name); - - -/**************************************************************************/ -/* HTTP Subsystem */ -/**************************************************************************/ - -/** - * purple_http_init: - * - * Initializes the http subsystem. - */ -void purple_http_init(void); - -/** - * purple_http_uninit: - * - * Uninitializes the http subsystem. - */ -void purple_http_uninit(void); - -G_END_DECLS - -#endif /* PURPLE_HTTP_H */ diff --git a/libpurple/meson.build b/libpurple/meson.build index 6cca60510d..7c1cdfea9e 100644 --- a/libpurple/meson.build +++ b/libpurple/meson.build @@ -21,7 +21,6 @@ purple_coresources = [ 'e2ee.c', 'eventloop.c', 'group.c', - 'http.c', 'idle.c', 'image.c', 'image-store.c', @@ -104,7 +103,6 @@ purple_coreheaders = [ 'e2ee.h', 'eventloop.h', 'group.h', - 'http.h', 'idle.h', 'image.h', 'image-store.h', diff --git a/libpurple/protocols/facebook/http.h b/libpurple/protocols/facebook/http.h index ad987c6aeb..3396fccffe 100644 --- a/libpurple/protocols/facebook/http.h +++ b/libpurple/protocols/facebook/http.h @@ -34,8 +34,6 @@ #include <glib.h> #include <libsoup/soup.h> -#include <libpurple/http.h> - /** * FB_HTTP_ERROR: * diff --git a/libpurple/protocols/gg/avatar.c b/libpurple/protocols/gg/avatar.c index c6a8d2be3a..e7cd0bf7d6 100644 --- a/libpurple/protocols/gg/avatar.c +++ b/libpurple/protocols/gg/avatar.c @@ -31,7 +31,6 @@ #include <debug.h> #include <glibcompat.h> -#include <http.h> #include "gg.h" #include "utils.h" diff --git a/libpurple/protocols/gg/edisc.c b/libpurple/protocols/gg/edisc.c index 82633a1344..180b87be0a 100644 --- a/libpurple/protocols/gg/edisc.c +++ b/libpurple/protocols/gg/edisc.c @@ -33,7 +33,6 @@ #include "gg.h" #include "libgaduw.h" #include "utils.h" -#include <http.h> #include <json-glib/json-glib.h> diff --git a/libpurple/protocols/gg/oauth/oauth-purple.c b/libpurple/protocols/gg/oauth/oauth-purple.c index df4dba2bfc..4e12aa47d2 100644 --- a/libpurple/protocols/gg/oauth/oauth-purple.c +++ b/libpurple/protocols/gg/oauth/oauth-purple.c @@ -35,7 +35,6 @@ #include "../xml.h" #include <debug.h> -#include <http.h> #define GGP_OAUTH_RESPONSE_MAX 10240 diff --git a/libpurple/protocols/gg/pubdir-prpl.c b/libpurple/protocols/gg/pubdir-prpl.c index 76804e21b9..5243aaa39b 100644 --- a/libpurple/protocols/gg/pubdir-prpl.c +++ b/libpurple/protocols/gg/pubdir-prpl.c @@ -31,7 +31,6 @@ #include "gg.h" #include <debug.h> -#include <http.h> #include <request.h> #include "oauth/oauth-purple.h" diff --git a/libpurple/protocols/jabber/bosh.c b/libpurple/protocols/jabber/bosh.c index 080888ec6a..0537b5dd87 100644 --- a/libpurple/protocols/jabber/bosh.c +++ b/libpurple/protocols/jabber/bosh.c @@ -23,7 +23,6 @@ #include "internal.h" #include "core.h" #include "debug.h" -#include "http.h" #include <libsoup/soup.h> diff --git a/libpurple/protocols/jabber/google/relay.c b/libpurple/protocols/jabber/google/relay.c index 682758e021..01b8156c2f 100644 --- a/libpurple/protocols/jabber/google/relay.c +++ b/libpurple/protocols/jabber/google/relay.c @@ -20,7 +20,6 @@ #include "internal.h" #include "debug.h" -#include "http.h" #include "relay.h" diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 8fef16d504..98cdd952e9 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -29,7 +29,6 @@ #include "connection.h" #include "conversation.h" #include "debug.h" -#include "http.h" #include "message.h" #include "notify.h" #include "pluginpref.h" diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h index 348d210b41..a459143016 100644 --- a/libpurple/protocols/jabber/jabber.h +++ b/libpurple/protocols/jabber/jabber.h @@ -64,7 +64,6 @@ typedef struct _JabberStream JabberStream; #include "attention.h" #include "circularbuffer.h" #include "connection.h" -#include "http.h" #include "media.h" #include "mediamanager.h" #include "protocol.h" diff --git a/libpurple/protocols/jabber/oob.c b/libpurple/protocols/jabber/oob.c index 2698b5865a..ea945cae2f 100644 --- a/libpurple/protocols/jabber/oob.c +++ b/libpurple/protocols/jabber/oob.c @@ -23,7 +23,6 @@ #include "internal.h" #include "debug.h" #include "xfer.h" -#include "http.h" #include "util.h" #include "jabber.h" diff --git a/libpurple/protocols/jabber/useravatar.c b/libpurple/protocols/jabber/useravatar.c index e74e9bb85a..347383dfad 100644 --- a/libpurple/protocols/jabber/useravatar.c +++ b/libpurple/protocols/jabber/useravatar.c @@ -25,7 +25,6 @@ #include <libsoup/soup.h> -#include "http.h" #include "useravatar.h" #include "pep.h" #include "debug.h" diff --git a/libpurple/protocols/oscar/oscar.h b/libpurple/protocols/oscar/oscar.h index 5e439f176b..5fb3efaeb7 100644 --- a/libpurple/protocols/oscar/oscar.h +++ b/libpurple/protocols/oscar/oscar.h @@ -33,7 +33,6 @@ #include "circularbuffer.h" #include "debug.h" #include "eventloop.h" -#include "http.h" #include "proxy.h" #include "sslconn.h" diff --git a/libpurple/proxy.c b/libpurple/proxy.c index 1083896c05..7d597cc6e6 100644 --- a/libpurple/proxy.c +++ b/libpurple/proxy.c @@ -27,7 +27,6 @@ #include "internal.h" #include "debug.h" -#include "http.h" #include "notify.h" #include "prefs.h" #include "proxy.h" diff --git a/libpurple/upnp.c b/libpurple/upnp.c index 308bc72d72..bd4a0c5cf1 100644 --- a/libpurple/upnp.c +++ b/libpurple/upnp.c @@ -27,7 +27,6 @@ #include "debug.h" #include "eventloop.h" -#include "http.h" #include "network.h" #include "proxy.h" #include "signals.h" diff --git a/pidgin/gtkprefs.c b/pidgin/gtkprefs.c index b423916dad..b2d22e7e65 100644 --- a/pidgin/gtkprefs.c +++ b/pidgin/gtkprefs.c @@ -26,7 +26,6 @@ #include "pidgin.h" #include "debug.h" -#include "http.h" #include "nat-pmp.h" #include "notify.h" #include "prefs.h" diff --git a/pidgin/gtksmiley-manager.c b/pidgin/gtksmiley-manager.c index 213bbeb019..1b4b45a461 100644 --- a/pidgin/gtksmiley-manager.c +++ b/pidgin/gtksmiley-manager.c @@ -23,7 +23,6 @@ #include "internal.h" #include "debug.h" -#include "http.h" #include "notify.h" #include "smiley.h" #include "smiley-custom.h" diff --git a/pidgin/plugins/imgupload.c b/pidgin/plugins/imgupload.c index 6d066a46d3..0e681915a4 100644 --- a/pidgin/plugins/imgupload.c +++ b/pidgin/plugins/imgupload.c @@ -23,7 +23,6 @@ #include "debug.h" #include "glibcompat.h" -#include "http.h" #include "version.h" #include "gtk3compat.h" diff --git a/pidgin/plugins/relnot.c b/pidgin/plugins/relnot.c index 2d1e0b0c5a..a179a6dd32 100644 --- a/pidgin/plugins/relnot.c +++ b/pidgin/plugins/relnot.c @@ -34,7 +34,6 @@ #include "gtkblist.h" #include "gtkplugin.h" #include "gtkutils.h" -#include "http.h" #include "notify.h" #include "pidginicon.h" #include "prefs.h" |