Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/EionRobb/skype4pidgin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEion Robb <eion@robbmob.com>2017-04-11 12:23:57 +0300
committerEion Robb <eion@robbmob.com>2017-04-11 12:23:57 +0300
commit1513ebdf47bd6b3e4e27dffbf2261aa0fab4082a (patch)
tree05e2ad1a5a2f67ad108dfc8663d7381586bf2382
parent35624c3430ce37696eb760dd7c8503f184c8e9f2 (diff)
libpurple 3.0 compatibility, see issue #538
-rw-r--r--skypeweb/Makefile164
-rw-r--r--skypeweb/glibcompat.h21
-rw-r--r--skypeweb/libskypeweb.c72
-rw-r--r--skypeweb/libskypeweb.h175
-rw-r--r--skypeweb/purple2compat/ciphers/sha256hash.h18
-rw-r--r--skypeweb/purple2compat/circularbuffer.h15
-rw-r--r--skypeweb/purple2compat/glibcompat.h1
-rw-r--r--skypeweb/purple2compat/http.c3239
-rw-r--r--skypeweb/purple2compat/http.h961
-rw-r--r--skypeweb/purple2compat/image-store.h14
-rw-r--r--skypeweb/purple2compat/image.h14
-rw-r--r--skypeweb/purple2compat/internal.h17
-rw-r--r--skypeweb/purple2compat/plugins.h1
-rw-r--r--skypeweb/purple2compat/purple-socket.c415
-rw-r--r--skypeweb/purple2compat/purple-socket.h217
-rw-r--r--skypeweb/purple2compat/xfer.h1
-rw-r--r--skypeweb/purplecompat.h252
-rw-r--r--skypeweb/skypeweb_connection.c781
-rw-r--r--skypeweb/skypeweb_connection.h17
-rw-r--r--skypeweb/skypeweb_contacts.c393
-rw-r--r--skypeweb/skypeweb_contacts.h2
-rw-r--r--skypeweb/skypeweb_login.c183
-rw-r--r--skypeweb/skypeweb_messages.c156
-rw-r--r--skypeweb/skypeweb_messages.h18
-rw-r--r--skypeweb/skypeweb_util.c142
-rw-r--r--skypeweb/skypeweb_util.h10
26 files changed, 5778 insertions, 1521 deletions
diff --git a/skypeweb/Makefile b/skypeweb/Makefile
index fbb91cc..605eee9 100644
--- a/skypeweb/Makefile
+++ b/skypeweb/Makefile
@@ -1,45 +1,119 @@
-CC ?= cc
-CFLAGS ?= -O2 -g -pipe
-LDFLAGS ?= -Wl,-z,relro
-PKG_CONFIG ?= pkg-config
-
-DIR_PERM = 0755
-FILE_PERM = 0644
-
-LIBPURPLE_CFLAGS += $(shell $(PKG_CONFIG) --cflags glib-2.0 json-glib-1.0 purple zlib)
-LIBPURPLE_LIBS += $(shell $(PKG_CONFIG) --libs glib-2.0 json-glib-1.0 purple zlib)
-PLUGIN_DIR_PURPLE = $(shell $(PKG_CONFIG) --variable=plugindir purple)
-DATA_ROOT_DIR_PURPLE = $(shell $(PKG_CONFIG) --variable=datarootdir purple)
-
-PRPL_NAME = libskypeweb.so
-PRPL_LIBNAME = ${PRPL_NAME}
-
-SKYPEWEB_SOURCES = \
- skypeweb_connection.c \
- skypeweb_contacts.c \
- skypeweb_login.c \
- skypeweb_messages.c \
- skypeweb_util.c \
- libskypeweb.c
-
-.PHONY: all clean install
-all: $(PRPL_NAME)
-install:
- mkdir -m $(DIR_PERM) -p $(DESTDIR)$(PLUGIN_DIR_PURPLE)
- install -m $(FILE_PERM) $(PRPL_LIBNAME) $(DESTDIR)$(PLUGIN_DIR_PURPLE)/$(PRPL_NAME)
- mkdir -m $(DIR_PERM) -p $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/16
- install -m $(FILE_PERM) icons/16/skype.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/16/skype.png
- install -m $(FILE_PERM) icons/16/skypeout.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/16/skypeout.png
- mkdir -m $(DIR_PERM) -p $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/22
- install -m $(FILE_PERM) icons/22/skype.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/22/skype.png
- install -m $(FILE_PERM) icons/22/skypeout.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/22/skypeout.png
- mkdir -m $(DIR_PERM) -p $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/48
- install -m $(FILE_PERM) icons/48/skype.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/48/skype.png
- install -m $(FILE_PERM) icons/48/skypeout.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/48/skypeout.png
- mkdir -m $(DIR_PERM) -p $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/emotes/skype
- install -m $(FILE_PERM) theme $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/emotes/skype/theme
-clean:
- rm -f libskypeweb.so
-
-$(PRPL_NAME): $(SKYPEWEB_SOURCES)
- $(CC) -Wall -I. -fPIC $(CFLAGS) $(SKYPEWEB_SOURCES) -o $@ $(LIBPURPLE_CFLAGS) $(LDFLAGS) $(LIBPURPLE_LIBS) -shared
+
+PIDGIN_TREE_TOP ?= ../../pidgin-2.10.11
+PIDGIN3_TREE_TOP ?= ../../pidgin-main
+LIBPURPLE_DIR ?= $(PIDGIN_TREE_TOP)/libpurple
+WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev
+
+WIN32_CC ?= $(WIN32_DEV_TOP)/mingw-4.7.2/bin/gcc
+
+PKG_CONFIG ?= pkg-config
+
+CFLAGS ?= -O2 -g -pipe
+LDFLAGS ?=
+
+# Do some nasty OS and purple version detection
+ifeq ($(OS),Windows_NT)
+ SKYPEWEB_TARGET = libskypeweb.dll
+ SKYPEWEB_DEST = "$(PROGRAMFILES)/Pidgin/plugins"
+ SKYPEWEB_ICONS_DEST = "$(PROGRAMFILES)/Pidgin/pixmaps/pidgin/protocols"
+ SKYPEWEB_THEME_DEST = "$(PROGRAMFILES)/Pidgin/pixmaps/pidgin/emotes/skype"
+else
+
+ UNAME_S := $(shell uname -s)
+
+ #.. There are special flags we need for OSX
+ ifeq ($(UNAME_S), Darwin)
+ #
+ #.. /opt/local/include and subdirs are included here to ensure this compiles
+ # for folks using Macports. I believe Homebrew uses /usr/local/include
+ # so things should "just work". You *must* make sure your packages are
+ # all up to date or you will most likely get compilation errors.
+ #
+ INCLUDES = -I/opt/local/include -lz $(OS)
+
+ CC = gcc
+ else
+ INCLUDES =
+ CC ?= gcc
+ endif
+
+ ifeq ($(shell $(PKG_CONFIG) --exists purple-3 2>/dev/null && echo "true"),)
+ ifeq ($(shell $(PKG_CONFIG) --exists purple 2>/dev/null && echo "true"),)
+ SKYPEWEB_TARGET = FAILNOPURPLE
+ SKYPEWEB_DEST =
+ SKYPEWEB_ICONS_DEST =
+ SKYPEWEB_THEME_DEST =
+ else
+ SKYPEWEB_TARGET = libskypeweb.so
+ SKYPEWEB_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=plugindir purple`
+ SKYPEWEB_ICONS_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=datadir purple`/pixmaps/pidgin/protocols
+ SKYPEWEB_THEME_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=datadir purple`/pixmaps/pidgin/emotes/skype
+ endif
+ else
+ SKYPEWEB_TARGET = libskypeweb3.so
+ SKYPEWEB_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=plugindir purple-3`
+ SKYPEWEB_ICONS_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=datadir purple-3`/pixmaps/pidgin/protocols
+ SKYPEWEB_THEME_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=datadir purple-3`/pixmaps/pidgin/emotes/skype
+ endif
+endif
+
+WIN32_CFLAGS = -I$(WIN32_DEV_TOP)/glib-2.28.8/include -I$(WIN32_DEV_TOP)/glib-2.28.8/include/glib-2.0 -I$(WIN32_DEV_TOP)/glib-2.28.8/lib/glib-2.0/include -I$(WIN32_DEV_TOP)/json-glib-0.14/include/json-glib-1.0 -DENABLE_NLS -DPACKAGE_VERSION='"$(PLUGIN_VERSION)"' -Wall -Wextra -Werror -Wno-deprecated-declarations -Wno-unused-parameter -fno-strict-aliasing -Wformat -Wno-sign-compare
+WIN32_LDFLAGS = -L$(WIN32_DEV_TOP)/glib-2.28.8/lib -L$(PROTOBUF_C_DIR)/bin -L$(WIN32_DEV_TOP)/json-glib-0.14/lib -lpurple -lintl -lglib-2.0 -lgobject-2.0 -ljson-glib-1.0 -g -ggdb -static-libgcc -lz
+WIN32_PIDGIN2_CFLAGS = -I$(PIDGIN_TREE_TOP)/libpurple -I$(PIDGIN_TREE_TOP) $(WIN32_CFLAGS)
+WIN32_PIDGIN3_CFLAGS = -I$(PIDGIN3_TREE_TOP)/libpurple -I$(PIDGIN3_TREE_TOP) -I$(WIN32_DEV_TOP)/gplugin-dev/gplugin $(WIN32_CFLAGS)
+WIN32_PIDGIN2_LDFLAGS = -L$(PIDGIN_TREE_TOP)/libpurple $(WIN32_LDFLAGS)
+WIN32_PIDGIN3_LDFLAGS = -L$(PIDGIN3_TREE_TOP)/libpurple -L$(WIN32_DEV_TOP)/gplugin-dev/gplugin $(WIN32_LDFLAGS) -lgplugin
+
+C_FILES = \
+ skypeweb_connection.c \
+ skypeweb_contacts.c \
+ skypeweb_login.c \
+ skypeweb_messages.c \
+ skypeweb_util.c \
+ libskypeweb.c
+PURPLE_COMPAT_FILES := purple2compat/http.c purple2compat/purple-socket.c
+PURPLE_C_FILES := libskypeweb.c $(C_FILES)
+
+
+
+.PHONY: all install FAILNOPURPLE clean
+
+all: $(SKYPEWEB_TARGET)
+
+libskypeweb.so: $(PURPLE_C_FILES) $(PURPLE_COMPAT_FILES)
+ $(CC) -fPIC $(CFLAGS) -shared -o $@ $^ $(LDFLAGS) $(PROTOBUF_OPTS) `$(PKG_CONFIG) purple glib-2.0 json-glib-1.0 zlib --libs --cflags` $(INCLUDES) -Ipurple2compat -g -ggdb
+
+libskypeweb3.so: $(PURPLE_C_FILES)
+ $(CC) -fPIC $(CFLAGS) -shared -o $@ $^ $(LDFLAGS) $(PROTOBUF_OPTS) `$(PKG_CONFIG) purple-3 glib-2.0 json-glib-1.0 zlib --libs --cflags` $(INCLUDES) -g -ggdb
+
+libskypeweb.dll: $(PURPLE_C_FILES) $(PURPLE_COMPAT_FILES)
+ $(WIN32_CC) -shared -o $@ $^ $(WIN32_PIDGIN2_CFLAGS) $(WIN32_PIDGIN2_LDFLAGS) -Ipurple2compat
+
+libskypeweb3.dll: $(PURPLE_C_FILES)
+ $(WIN32_CC) -shared -o $@ $^ $(WIN32_PIDGIN3_CFLAGS) $(WIN32_PIDGIN3_LDFLAGS)
+
+install: $(SKYPEWEB_TARGET) install-icons install-theme
+ mkdir -p $(SKYPEWEB_DEST)
+ install -p $(SKYPEWEB_TARGET) $(SKYPEWEB_DEST)
+
+install-icons: icons/16/skype.png icons/22/skype.png icons/48/skype.png icons/16/skypeout.png icons/22/skypeout.png icons/48/skypeout.png
+ mkdir -p $(SKYPEWEB_ICONS_DEST)/16
+ mkdir -p $(SKYPEWEB_ICONS_DEST)/22
+ mkdir -p $(SKYPEWEB_ICONS_DEST)/48
+ install icons/16/skype.png $(SKYPEWEB_ICONS_DEST)/16/skype.png
+ install icons/22/skype.png $(SKYPEWEB_ICONS_DEST)/22/skype.png
+ install icons/48/skype.png $(SKYPEWEB_ICONS_DEST)/48/skype.png
+ install icons/16/skypeout.png $(SKYPEWEB_ICONS_DEST)/16/skypeout.png
+ install icons/22/skypeout.png $(SKYPEWEB_ICONS_DEST)/22/skypeout.png
+ install icons/48/skypeout.png $(SKYPEWEB_ICONS_DEST)/48/skypeout.png
+
+install-theme: theme
+ mkdir -p $(SKYPEWEB_THEME_DEST)
+ install theme $(SKYPEWEB_THEME_DEST)/theme
+
+FAILNOPURPLE:
+ echo "You need libpurple development headers installed to be able to compile this plugin"
+
+clean:
+ rm -f $(SKYPEWEB_TARGET)
+
diff --git a/skypeweb/glibcompat.h b/skypeweb/glibcompat.h
new file mode 100644
index 0000000..35cb10a
--- /dev/null
+++ b/skypeweb/glibcompat.h
@@ -0,0 +1,21 @@
+#ifndef _GLIBCOMPAT_H_
+#define _GLIBCOMPAT_H_
+
+#if !GLIB_CHECK_VERSION(2, 32, 0)
+#define g_hash_table_contains(hash_table, key) g_hash_table_lookup_extended(hash_table, key, NULL, NULL)
+#endif /* 2.32.0 */
+
+
+#if !GLIB_CHECK_VERSION(2, 28, 0)
+gint64
+g_get_real_time()
+{
+ GTimeVal val;
+
+ g_get_current_time (&val);
+
+ return (((gint64) val.tv_sec) * 1000000) + val.tv_usec;
+}
+#endif /* 2.28.0 */
+
+#endif /*_GLIBCOMPAT_H_*/ \ No newline at end of file
diff --git a/skypeweb/libskypeweb.c b/skypeweb/libskypeweb.c
index 14f3708..e15654c 100644
--- a/skypeweb/libskypeweb.c
+++ b/skypeweb/libskypeweb.c
@@ -329,13 +329,6 @@ skypeweb_login(PurpleAccount *account)
PurpleConnectionFlags flags;
purple_connection_set_protocol_data(pc, sa);
-
- if (!purple_ssl_is_supported()) {
- purple_connection_error (pc,
- PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
- _("Server requires TLS/SSL for login. No TLS/SSL support found."));
- return;
- }
flags = purple_connection_get_flags(pc);
flags |= PURPLE_CONNECTION_FLAG_HTML | PURPLE_CONNECTION_FLAG_NO_BGCOLOR | PURPLE_CONNECTION_FLAG_NO_FONTSIZE;
@@ -346,12 +339,12 @@ skypeweb_login(PurpleAccount *account)
}
sa->account = account;
sa->pc = pc;
- sa->cookie_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
- sa->hostname_ip_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ sa->cookie_jar = purple_http_cookie_jar_new();
sa->sent_messages_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- sa->waiting_conns = g_queue_new();
sa->messages_host = g_strdup(SKYPEWEB_DEFAULT_MESSAGES_HOST);
- sa->url_datas = NULL;
+ sa->keepalive_pool = purple_http_keepalive_pool_new();
+ purple_http_keepalive_pool_set_limit_per_host(sa->keepalive_pool, SKYPEWEB_MAX_CONNECTIONS);
+ sa->conns = purple_http_connection_set_new();
if (purple_account_get_string(account, "refresh-token", NULL) && purple_account_get_remember_password(account)) {
skypeweb_refresh_token_login(sa);
@@ -383,33 +376,15 @@ skypeweb_close(PurpleConnection *pc)
purple_timeout_remove(sa->watchdog_timeout);
skypeweb_logout(sa);
- purple_debug_info("skypeweb", "destroying %d waiting connections\n",
- g_queue_get_length(sa->waiting_conns));
-
- while (!g_queue_is_empty(sa->waiting_conns))
- skypeweb_connection_destroy(g_queue_pop_tail(sa->waiting_conns));
- g_queue_free(sa->waiting_conns);
- purple_debug_info("skypeweb", "destroying %d incomplete connections\n",
- g_slist_length(sa->conns));
+ purple_debug_info("skypeweb", "destroying incomplete connections\n");
- while (sa->conns != NULL)
- skypeweb_connection_destroy(sa->conns->data);
-
- while (sa->dns_queries != NULL) {
- PurpleDnsQueryData *dns_query = sa->dns_queries->data;
- purple_debug_info("skypeweb", "canceling dns query for %s\n",
- purple_dnsquery_get_host(dns_query));
- sa->dns_queries = g_slist_remove(sa->dns_queries, dns_query);
- purple_dnsquery_destroy(dns_query);
- }
+ purple_http_conn_cancel_all(pc);
+ purple_http_connection_set_destroy(sa->conns);
+ purple_http_keepalive_pool_unref(sa->keepalive_pool);
+ purple_http_cookie_jar_unref(sa->cookie_jar);
- while (sa->url_datas) {
- purple_util_fetch_url_cancel(sa->url_datas->data);
- sa->url_datas = g_slist_delete_link(sa->url_datas, sa->url_datas);
- }
-
- buddies = purple_find_buddies(sa->account, NULL);
+ buddies = purple_blist_find_buddies(sa->account, NULL);
while (buddies != NULL) {
PurpleBuddy *buddy = buddies->data;
skypeweb_buddy_free(buddy);
@@ -418,8 +393,6 @@ skypeweb_close(PurpleConnection *pc)
}
g_hash_table_destroy(sa->sent_messages_hash);
- g_hash_table_destroy(sa->cookie_table);
- g_hash_table_destroy(sa->hostname_ip_cache);
g_free(sa->messages_host);
g_free(sa->skype_token);
@@ -529,7 +502,7 @@ skypeweb_cmd_topic(PurpleConversation *conv, const gchar *cmd, gchar **args, gch
buf = g_strdup(_("No topic is set"));
}
- purple_conv_chat_write(chat, NULL, buf, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time(NULL));
+ purple_conversation_write_system_message(conv, buf, PURPLE_MESSAGE_NO_LOG);
g_free(buf);
return PURPLE_CMD_RET_OK;
@@ -622,6 +595,24 @@ skypeweb_uri_handler(const char *proto, const char *cmd, GHashTable *params)
}
#if PURPLE_VERSION_CHECK(3, 0, 0)
+ typedef struct _SkypeWebProtocol
+ {
+ PurpleProtocol parent;
+ } SkypeWebProtocol;
+
+ typedef struct _SkypeWebProtocolClass
+ {
+ PurpleProtocolClass parent_class;
+ } SkypeWebProtocolClass;
+
+ G_MODULE_EXPORT GType skypeweb_protocol_get_type(void);
+ #define SKYPEWEB_TYPE_PROTOCOL (skypeweb_protocol_get_type())
+ #define SKYPEWEB_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SKYPEWEB_TYPE_PROTOCOL, SkypeWebProtocol))
+ #define SKYPEWEB_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SKYPEWEB_TYPE_PROTOCOL, SkypeWebProtocolClass))
+ #define SKYPEWEB_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SKYPEWEB_TYPE_PROTOCOL))
+ #define SKYPEWEB_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SKYPEWEB_TYPE_PROTOCOL))
+ #define SKYPEWEB_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SKYPEWEB_TYPE_PROTOCOL, SkypeWebProtocolClass))
+
static PurpleProtocol *skypeweb_protocol;
#endif
@@ -717,10 +708,9 @@ PurpleConnection *pc
)
{
GList *m = NULL;
- PurplePluginAction *act;
+ PurpleProtocolAction *act;
- act = purple_plugin_action_new(_("Search for friends..."),
- skypeweb_search_users);
+ act = purple_protocol_action_new(_("Search for friends..."), skypeweb_search_users);
m = g_list_append(m, act);
return m;
diff --git a/skypeweb/libskypeweb.h b/skypeweb/libskypeweb.h
index c3f17cb..02155fd 100644
--- a/skypeweb/libskypeweb.h
+++ b/skypeweb/libskypeweb.h
@@ -41,8 +41,9 @@
# endif /* __GNUC__ >= 4 */
#endif /* G_GNUC_NULL_TERMINATED */
+#include "purplecompat.h"
+
#ifdef _WIN32
-# include "win32dep.h"
# define dlopen(a,b) LoadLibrary(a)
# define RTLD_LAZY
# define dlsym(a,b) GetProcAddress(a,b)
@@ -80,7 +81,8 @@
#include "cmds.h"
#include "connection.h"
#include "debug.h"
-#include "dnsquery.h"
+#include "http.h"
+#include "plugins.h"
#include "proxy.h"
#include "request.h"
#include "roomlist.h"
@@ -89,170 +91,11 @@
#include "util.h"
#include "version.h"
-#if !PURPLE_VERSION_CHECK(3, 0, 0)
- #include "blist.h"
- #include "prpl.h"
-#else
- #include "buddylist.h"
- #include "plugins.h"
- #include "http.h"
-#endif
#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 12
# define atoll(a) g_ascii_strtoll(a, NULL, 0)
#endif
-#if !PURPLE_VERSION_CHECK(3, 0, 0)
- #define purple_connection_error purple_connection_error_reason
- #define purple_notify_user_info_add_pair_html purple_notify_user_info_add_pair
- #define purple_util_fetch_url_request purple_util_fetch_url_request_len_with_account
-
- #define PurpleConversationUpdateType PurpleConvUpdateType
- #define PurpleChatConversation PurpleConvChat
- #define PurpleIMConversation PurpleConvIm
- #define PurpleProtocolAction PurplePluginAction
- #define PURPLE_IS_BUDDY PURPLE_BLIST_NODE_IS_BUDDY
- #define PurpleProtocolChatEntry struct proto_chat_entry
- #define PURPLE_CONNECTION_CONNECTED PURPLE_CONNECTED
- #define PURPLE_CONNECTION_CONNECTING PURPLE_CONNECTING
- #define PURPLE_CONNECTION_FLAG_HTML PURPLE_CONNECTION_HTML
- #define PURPLE_CONNECTION_FLAG_NO_BGCOLOR PURPLE_CONNECTION_NO_BGCOLOR
- #define PURPLE_CONNECTION_FLAG_NO_FONTSIZE PURPLE_CONNECTION_NO_FONTSIZE
- #define purple_connection_get_protocol_data(pc) ((pc)->proto_data)
- #define purple_connection_set_protocol_data(pc, data) ((pc)->proto_data = (data))
- #define purple_connection_set_flags(pc, flags) (pc->flags = flags)
- #define purple_connection_get_flags(pc) (pc->flags)
- #define purple_connection_get_protocol purple_connection_get_prpl
- #define purple_conversation_present_error purple_conv_present_error
- #define purple_account_privacy_check purple_privacy_check
- #define purple_account_get_private_alias purple_account_get_alias
- #define purple_account_set_private_alias purple_account_set_alias
- #define purple_serv_got_im serv_got_im
- #define purple_serv_got_typing serv_got_typing
- #define purple_serv_got_alias serv_got_alias
- #define PurpleIMTypingState PurpleTypingState
- #define PURPLE_IM_NOT_TYPING PURPLE_NOT_TYPING
- #define PURPLE_IM_TYPING PURPLE_TYPING
- #define PURPLE_IM_TYPED PURPLE_TYPED
- #define purple_blist_find_buddy purple_find_buddy
- #define purple_blist_find_group purple_find_group
- #define PurpleChatUser PurpleConvChatBuddy
- #define PurpleChatUserFlags PurpleConvChatBuddyFlags
- #define PURPLE_CHAT_USER_NONE PURPLE_CBFLAGS_NONE
- #define PURPLE_CHAT_USER_OP PURPLE_CBFLAGS_OP
- #define PURPLE_CHAT_USER_FOUNDER PURPLE_CBFLAGS_FOUNDER
- #define PURPLE_CHAT_USER_TYPING PURPLE_CBFLAGS_TYPING
- #define PURPLE_CHAT_USER_AWAY PURPLE_CBFLAGS_AWAY
- #define PURPLE_CHAT_USER_HALFOP PURPLE_CBFLAGS_HALFOP
- #define PURPLE_CHAT_USER_VOICE PURPLE_CBFLAGS_VOICE
- #define PURPLE_CHAT_USER_TYPING PURPLE_CBFLAGS_TYPING
- #define purple_chat_user_get_flags(cb) purple_conv_chat_user_get_flags(
- #define purple_serv_got_joined_chat(pc, id, name) PURPLE_CONV_CHAT(serv_got_joined_chat(pc, id, name))
- #define purple_serv_got_chat_invite serv_got_chat_invite
- #define purple_serv_got_chat_in serv_got_chat_in
- #define purple_chat_conversation_get_topic purple_conv_chat_get_topic
- #define purple_chat_conversation_set_topic purple_conv_chat_set_topic
- #define purple_chat_conversation_find_user(chat, name) purple_conv_chat_cb_find(chat, name)
- #define purple_chat_conversation_add_user purple_conv_chat_add_user
- #define purple_chat_conversation_leave purple_conv_chat_left
- #define purple_chat_conversation_has_left purple_conv_chat_has_left
- #define purple_chat_conversation_add_user purple_conv_chat_add_user
- #define purple_chat_conversation_remove_user purple_conv_chat_remove_user
- #define purple_chat_conversation_clear_users purple_conv_chat_clear_users
- #define purple_protocol_get_id purple_plugin_get_id
- #define purple_chat_conversation_get_id purple_conv_chat_get_id
- #define purple_protocol_got_user_status purple_prpl_got_user_status
- #define purple_protocol_got_user_idle purple_prpl_got_user_idle
- #define PURPLE_CONVERSATION_UPDATE_TOPIC PURPLE_CONV_UPDATE_TOPIC
- #define PURPLE_CONVERSATION_UPDATE_UNSEEN PURPLE_CONV_UPDATE_UNSEEN
- #define purple_conversations_find_chat(pc, id) PURPLE_CONV_CHAT(purple_find_chat(pc, id))
- #define purple_conversations_find_chat_with_account(name, account) PURPLE_CONV_CHAT(purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, account))
- #define purple_conversations_find_im_with_account(name, account) PURPLE_CONV_IM(purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account))
- #define purple_chat_conversation_new(account, from) PURPLE_CONV_CHAT(purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, from))
- #define purple_im_conversation_new(account, from) PURPLE_CONV_IM(purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from))
- #define PURPLE_CONVERSATION(chatorim) (chatorim == NULL ? NULL : chatorim->conv)
- #define PURPLE_IM_CONVERSATION(conv) PURPLE_CONV_IM(conv)
- #define PURPLE_CHAT_CONVERSATION(conv) PURPLE_CONV_CHAT(conv)
- #define PURPLE_IS_IM_CONVERSATION(conv) (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
- #define PURPLE_IS_CHAT_CONVERSATION(conv) (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
- #define purple_conversation_get_connection purple_conversation_get_gc
- #define PurpleMessage PurpleConvMessage
- #define purple_conversation_write_message(conv, msg) purple_conversation_write(conv, msg->who, msg->what, msg->flags, msg->when)
- #define PurpleXmlNode xmlnode
- #define purple_xmlnode_from_str xmlnode_from_str
- #define purple_xmlnode_get_child xmlnode_get_child
- #define purple_xmlnode_get_next_twin xmlnode_get_next_twin
- #define purple_xmlnode_get_data xmlnode_get_data
- #define purple_xmlnode_get_data_unescaped xmlnode_get_data_unescaped
- #define purple_xmlnode_get_attrib xmlnode_get_attrib
- #define purple_xmlnode_free xmlnode_free
- #define PurpleHash PurpleCipherContext
- #define purple_sha256_hash_new() purple_cipher_context_new(purple_ciphers_find_cipher("sha256"), NULL)
- #define purple_hash_append purple_cipher_context_append
- #define purple_hash_digest(hash, data, len) purple_cipher_context_digest(hash, len, data, NULL)
- #define purple_hash_destroy purple_cipher_context_destroy
- #define purple_xfer_set_protocol_data(xfer, proto_data) ((xfer)->data = (proto_data))
- #define purple_xfer_get_protocol_data(xfer) ((xfer)->data)
-#if !PURPLE_VERSION_CHECK(2, 10, 12) && !FEDORA
-static inline gboolean
-purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size) {
- PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
- purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) +
- (ui_ops && ui_ops->ui_write ? ui_ops->ui_write(xfer, buffer, size) : fwrite(buffer, 1, size, xfer->dest_fp)));
- return TRUE;
-}
-#endif
- #define PURPLE_CMD_FLAG_PROTOCOL_ONLY PURPLE_CMD_FLAG_PRPL_ONLY
- #define PURPLE_CMD_P_PLUGIN PURPLE_CMD_P_PRPL
-
-/*typedef struct {
- PurpleConvChatBuddy cb;
- PurpleConvChat *chat;
-} PurpleChatUser;*/
-
-typedef struct {
- gchar *host;
- gint port;
- gchar *path;
- gchar *user;
- gchar *passwd;
-} PurpleHttpURL;
-
-static inline PurpleHttpURL *purple_http_url_parse(const gchar *url) {
- PurpleHttpURL *ret = g_new0(PurpleHttpURL, 1);
- purple_url_parse(url, &(ret->host), &(ret->port), &(ret->path), &(ret->user), &(ret->passwd));
- return ret;
-}
- #define purple_http_url_get_host(httpurl) (httpurl->host)
- #define purple_http_url_get_path(httpurl) (httpurl->path)
-static inline void purple_http_url_free(PurpleHttpURL *phl) { g_free(phl->host); g_free(phl->path); g_free(phl->user); g_free(phl->passwd); g_free(phl); }
-
-#else
- #define purple_conversation_set_data(conv, key, value) g_object_set_data(G_OBJECT(conv), key, value)
- #define purple_conversation_get_data(conv, key) g_object_get_data(G_OBJECT(conv), key)
- #define purple_hash_destroy g_object_unref
- #define PURPLE_TYPE_STRING G_TYPE_STRING
-
-
-typedef struct _SkypeWebProtocol
-{
- PurpleProtocol parent;
-} SkypeWebProtocol;
-
-typedef struct _SkypeWebProtocolClass
-{
- PurpleProtocolClass parent_class;
-} SkypeWebProtocolClass;
-
-G_MODULE_EXPORT GType skypeweb_protocol_get_type(void);
-#define SKYPEWEB_TYPE_PROTOCOL (skypeweb_protocol_get_type())
-#define SKYPEWEB_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SKYPEWEB_TYPE_PROTOCOL, SkypeWebProtocol))
-#define SKYPEWEB_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SKYPEWEB_TYPE_PROTOCOL, SkypeWebProtocolClass))
-#define SKYPEWEB_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SKYPEWEB_TYPE_PROTOCOL))
-#define SKYPEWEB_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SKYPEWEB_TYPE_PROTOCOL))
-#define SKYPEWEB_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SKYPEWEB_TYPE_PROTOCOL, SkypeWebProtocolClass))
-
-#endif
#define SKYPEWEB_MAX_MSG_RETRY 2
@@ -301,11 +144,9 @@ struct _SkypeWebAccount {
PurpleAccount *account;
PurpleConnection *pc;
- GSList *conns; /**< A list of all active SkypeWebConnections */
- GQueue *waiting_conns; /**< A list of all SkypeWebConnections waiting to process */
- GSList *dns_queries;
- GHashTable *cookie_table;
- GHashTable *hostname_ip_cache;
+ PurpleHttpKeepalivePool *keepalive_pool;
+ PurpleHttpConnectionSet *conns;
+ PurpleHttpCookieJar *cookie_jar;
gchar *messages_host;
GHashTable *sent_messages_hash;
@@ -321,8 +162,6 @@ struct _SkypeWebAccount {
gchar *endpoint;
gint registration_expiry;
gint vdms_expiry;
-
- GSList *url_datas; /**< PurpleUtilFetchUrlData to be cancelled on logout */
};
struct _SkypeWebBuddy {
diff --git a/skypeweb/purple2compat/ciphers/sha256hash.h b/skypeweb/purple2compat/ciphers/sha256hash.h
new file mode 100644
index 0000000..85d65b0
--- /dev/null
+++ b/skypeweb/purple2compat/ciphers/sha256hash.h
@@ -0,0 +1,18 @@
+#ifndef _CIPHERS_SHA256HASH_H_
+#define _CIPHERS_SHA256HASH_H_
+
+#include "cipher.h"
+
+#define purple_sha256_hash_new() purple_cipher_context_new(purple_ciphers_find_cipher("sha256"), NULL)
+
+#ifndef PurpleHash
+# define PurpleHash PurpleCipherContext
+# define purple_hash_append purple_cipher_context_append
+# define purple_hash_digest_to_str(ctx, data, size) \
+ purple_cipher_context_digest_to_str(ctx, size, data, NULL)
+# define purple_hash_digest(ctx, data, size) \
+ purple_cipher_context_digest(ctx, size, data, NULL)
+# define purple_hash_destroy purple_cipher_context_destroy
+#endif /*PurpleHash*/
+
+#endif /*_CIPHERS_SHA256HASH_H_*/
diff --git a/skypeweb/purple2compat/circularbuffer.h b/skypeweb/purple2compat/circularbuffer.h
new file mode 100644
index 0000000..50e9660
--- /dev/null
+++ b/skypeweb/purple2compat/circularbuffer.h
@@ -0,0 +1,15 @@
+#ifndef _CIRCULARBUFFER_H_
+#define _CIRCULARBUFFER_H_
+
+#include "circbuffer.h"
+
+#define PurpleCircularBuffer PurpleCircBuffer
+#define purple_circular_buffer_new purple_circ_buffer_new
+#define purple_circular_buffer_destroy purple_circ_buffer_destroy
+#define purple_circular_buffer_append purple_circ_buffer_append
+#define purple_circular_buffer_get_max_read purple_circ_buffer_get_max_read
+#define purple_circular_buffer_mark_read purple_circ_buffer_mark_read
+#define purple_circular_buffer_get_output(buf) ((const gchar *) (buf)->outptr)
+#define purple_circular_buffer_get_used(buf) ((buf)->bufused)
+
+#endif /*_CIRCULARBUFFER_H_*/
diff --git a/skypeweb/purple2compat/glibcompat.h b/skypeweb/purple2compat/glibcompat.h
new file mode 100644
index 0000000..9730a09
--- /dev/null
+++ b/skypeweb/purple2compat/glibcompat.h
@@ -0,0 +1 @@
+#include "../glibcompat.h"
diff --git a/skypeweb/purple2compat/http.c b/skypeweb/purple2compat/http.c
new file mode 100644
index 0000000..dc44655
--- /dev/null
+++ b/skypeweb/purple2compat/http.c
@@ -0,0 +1,3239 @@
+/* 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"
+
+#ifndef _WIN32
+#include <errno.h>
+#include <unistd.h>
+#endif
+
+#include "internal.h"
+#include "glibcompat.h"
+
+
+#include "debug.h"
+#include "ntlm.h"
+#include "proxy.h"
+#include "purple-socket.h"
+
+#include <zlib.h>
+#ifndef z_const
+#define z_const
+#endif
+
+#define PURPLE_HTTP_URL_CREDENTIALS_CHARS "a-z0-9.,~_/*!&%?=+\\^-"
+#define PURPLE_HTTP_MAX_RECV_BUFFER_LEN 10240
+#define PURPLE_HTTP_MAX_READ_BUFFER_LEN 10240
+#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
+
+typedef struct _PurpleHttpSocket PurpleHttpSocket;
+
+typedef struct _PurpleHttpHeaders PurpleHttpHeaders;
+
+typedef struct _PurpleHttpKeepaliveHost PurpleHttpKeepaliveHost;
+
+typedef struct _PurpleHttpKeepaliveRequest PurpleHttpKeepaliveRequest;
+
+typedef struct _PurpleHttpGzStream PurpleHttpGzStream;
+
+struct _PurpleHttpSocket
+{
+ PurpleSocket *ps;
+
+ 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;
+ PurpleSocketConnectCb 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;
+ z_stream zs;
+ gsize max_output;
+ gsize decompressed;
+ GString *pending;
+};
+
+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,
+ PurpleSocketConnectCb 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_month);
+ g_free(d_year);
+ g_free(d_time);
+
+ if (month > 12) {
+ purple_debug_warning("http", "Invalid month: %s\n", d_month);
+ g_free(iso_date);
+ return 0;
+ }
+
+ 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);
+ int windowBits;
+
+ if (is_deflate)
+ windowBits = -MAX_WBITS;
+ else /* is gzip */
+ windowBits = MAX_WBITS + 32;
+
+ if (inflateInit2(&gzs->zs, windowBits) != Z_OK) {
+ purple_debug_error("http", "Cannot initialize zlib stream\n");
+ g_free(gzs);
+ return NULL;
+ }
+
+ 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;
+ z_stream *zs;
+
+ g_return_val_if_fail(gzs != NULL, NULL);
+ g_return_val_if_fail(buf != NULL, NULL);
+
+ if (gzs->failed)
+ return NULL;
+
+ zs = &gzs->zs;
+
+ 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;
+ }
+
+ zs->next_in = (z_const Bytef*)compressed_buff;
+ zs->avail_in = compressed_len;
+
+ ret = g_string_new(NULL);
+ while (zs->avail_in > 0) {
+ int gzres;
+ gchar decompressed_buff[PURPLE_HTTP_GZ_BUFF_LEN];
+ gsize decompressed_len;
+
+ zs->next_out = (Bytef*)decompressed_buff;
+ zs->avail_out = sizeof(decompressed_buff);
+ decompressed_len = zs->avail_out = sizeof(decompressed_buff);
+ gzres = inflate(zs, Z_FULL_FLUSH);
+ decompressed_len -= zs->avail_out;
+
+ if (gzres == Z_OK || gzres == Z_STREAM_END) {
+ 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 = Z_STREAM_END;
+ }
+ gzs->decompressed += decompressed_len;
+ g_string_append_len(ret, decompressed_buff,
+ decompressed_len);
+ if (gzres == Z_STREAM_END)
+ break;
+ } else {
+ purple_debug_error("http",
+ "Decompression failed (%d): %s\n", gzres,
+ zs->msg);
+ gzs->failed = TRUE;
+ return NULL;
+ }
+ }
+
+ if (gzs->pending) {
+ g_string_free(gzs->pending, TRUE);
+ gzs->pending = NULL;
+ }
+
+ if (zs->avail_in > 0) {
+ gzs->pending = g_string_new_len((gchar*)zs->next_in,
+ zs->avail_in);
+ }
+
+ return ret;
+}
+
+static void
+purple_http_gz_free(PurpleHttpGzStream *gzs)
+{
+ if (gzs == NULL)
+ return;
+ inflateEnd(&gzs->zs);
+ if (gzs->pending)
+ g_string_free(gzs->pending, TRUE);
+ g_free(gzs);
+}
+
+/*** 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 PurpleHttpSocket *
+purple_http_socket_connect_new(PurpleConnection *gc, const gchar *host,
+ int port, gboolean is_ssl, PurpleSocketConnectCb cb, gpointer user_data)
+{
+ PurpleHttpSocket *hs = g_new0(PurpleHttpSocket, 1);
+
+ hs->ps = purple_socket_new(gc);
+ purple_socket_set_data(hs->ps, "hs", hs);
+ purple_socket_set_tls(hs->ps, is_ssl);
+ purple_socket_set_host(hs->ps, host);
+ purple_socket_set_port(hs->ps, port);
+ if (!purple_socket_connect(hs->ps, cb, user_data)) {
+ purple_socket_destroy(hs->ps);
+ g_free(hs);
+ return NULL;
+ }
+
+ 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);
+
+ purple_socket_destroy(hs->ps);
+ 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, gint fd);
+static void _purple_http_recv(gpointer _hc, gint fd,
+ PurpleInputCondition cond);
+static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond);
+
+/* 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 _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(hc->gc ?
+ purple_connection_get_account(hc->gc) : NULL);
+
+ 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",
+ 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 = purple_base64_encode((const guchar *)tmp, len);
+ memset(tmp, 0, len);
+ g_free(tmp);
+
+ ntlm_type1 = purple_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(proxy_auth, 0, 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, gint fd)
+{
+ int len;
+ gchar buf[4096];
+ gboolean got_anything;
+
+ len = purple_socket_read(hc->socket->ps, (guchar*)buf, sizeof(buf));
+ got_anything = (len > 0);
+
+ if (len < 0 && errno == EAGAIN)
+ return FALSE;
+
+ if (len < 0) {
+ _purple_http_error(hc, _("Error reading from %s: %s"),
+ hc->url->host, g_strerror(errno));
+ 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 void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond)
+{
+ PurpleHttpConnection *hc = _hc;
+
+ while (_purple_http_recv_loopbody(hc, fd));
+}
+
+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 void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond)
+{
+ PurpleHttpConnection *hc = _hc;
+ int written, write_len;
+ const gchar *write_from;
+ gboolean writing_headers;
+
+ /* Waiting for data. This could be written more efficiently, by removing
+ * (and later, adding) hs->inpa. */
+ if (hc->contents_reader_requested)
+ return;
+
+ _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; /* 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;
+ }
+ 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 = purple_socket_write(hc->socket->ps,
+ (const guchar*)write_from, write_len);
+ }
+
+ if (written < 0 && errno == EAGAIN)
+ return;
+
+ 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);
+ return;
+ }
+
+ _purple_http_error(hc, _("Error writing to %s: %s"),
+ hc->url->host, g_strerror(errno));
+ return;
+ }
+
+ 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;
+ if (hc->request->contents_length > 0)
+ return;
+ } 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;
+ }
+ }
+
+ /* request is completely written, let's read the response */
+ hc->is_reading = TRUE;
+ purple_socket_watch(hc->socket->ps, PURPLE_INPUT_READ,
+ _purple_http_recv, hc);
+}
+
+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->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(PurpleSocket *ps, const gchar *error, gpointer _hc)
+{
+ PurpleHttpSocket *hs = NULL;
+ PurpleHttpConnection *hc = _hc;
+
+ if (ps != NULL)
+ hs = purple_socket_get_data(ps, "hs");
+
+ 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;
+ }
+
+ purple_socket_watch(ps, PURPLE_INPUT_WRITE, _purple_http_send, hc);
+}
+
+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 = purple_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)
+ purple_timeout_remove(hc->timeout_handle);
+ if (hc->watcher_delayed_handle)
+ purple_timeout_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);
+ }
+
+ 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);
+
+ 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 = purple_timeout_add_seconds(
+ 1 + hc->watcher_interval_threshold / 1000000,
+ purple_http_conn_notify_progress_watcher_timeout, hc);
+ return;
+ }
+
+ if (hc->watcher_delayed_handle)
+ purple_timeout_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 (escaped_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) {
+ purple_timeout_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,
+ PurpleSocketConnectCb 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(PurpleSocket *ps,
+ const gchar *error, gpointer _req)
+{
+ PurpleHttpSocket *hs = NULL;
+ PurpleHttpKeepaliveRequest *req = _req;
+
+ if (ps != NULL)
+ hs = purple_socket_get_data(ps, "hs");
+
+ if (hs != NULL)
+ hs->use_count++;
+
+ req->cb(ps, 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->ps, 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 = purple_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);
+
+ purple_socket_watch(hs->ps, 0, NULL, NULL);
+ 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_printf(url, ":%s", 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;
+
+ 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/skypeweb/purple2compat/http.h b/skypeweb/purple2compat/http.h
new file mode 100644
index 0000000..2e0ae52
--- /dev/null
+++ b/skypeweb/purple2compat/http.h
@@ -0,0 +1,961 @@
+/* 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:
+ * @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:
+ * @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.
+ *
+ * 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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ *
+ * Creates new cookie jar,
+ *
+ * Returns: empty cookie jar.
+ */
+PurpleHttpCookieJar * purple_http_cookie_jar_new(void);
+
+/**
+ * purple_http_cookie_jar_ref:
+ * @cookie_jar: The cookie jar.
+ *
+ * Increment the reference count.
+ */
+void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar);
+
+/**
+ * purple_http_cookie_jar_unref:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @request: The request.
+ *
+ * Increment the reference count.
+ */
+void purple_http_request_ref(PurpleHttpRequest *request);
+
+/**
+ * purple_http_request_unref:
+ * @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:
+ * @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:
+ * @request: The request.
+ * @format: 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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ * @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:
+ *
+ * Creates a new HTTP Keep-Alive pool.
+ */
+PurpleHttpKeepalivePool *
+purple_http_keepalive_pool_new(void);
+
+/**
+ * purple_http_keepalive_pool_ref:
+ * @pool: The HTTP Keep-Alive pool.
+ *
+ * Increment the reference count.
+ */
+void
+purple_http_keepalive_pool_ref(PurpleHttpKeepalivePool *pool);
+
+/**
+ * purple_http_keepalive_pool_unref:
+ * @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:
+ * @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:
+ * @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 */
+/**************************************************************************/
+
+PurpleHttpConnectionSet *
+purple_http_connection_set_new(void);
+
+void
+purple_http_connection_set_destroy(PurpleHttpConnectionSet *set);
+
+void
+purple_http_connection_set_add(PurpleHttpConnectionSet *set,
+ PurpleHttpConnection *http_conn);
+
+
+/**************************************************************************/
+/* HTTP response API */
+/**************************************************************************/
+
+/**
+ * purple_http_response_is_successful:
+ * @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:
+ * @response: The response.
+ *
+ * Gets HTTP response code.
+ *
+ * Returns: HTTP response code.
+ */
+int purple_http_response_get_code(PurpleHttpResponse *response);
+
+/**
+ * purple_http_response_get_error:
+ * @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:
+ * @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:
+ * @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:
+ * @response: The response.
+ *
+ * Gets all headers got with response.
+ *
+ * Returns: GList of PurpleKeyValuePair, which 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:
+ * @response: The response.
+ * @name: The name of header field.
+ *
+ * Gets all headers with specified name got with response.
+ *
+ * Returns: GList of header field records contents (gchar*).
+ */
+const GList * purple_http_response_get_headers_by_name(
+ PurpleHttpResponse *response, const gchar *name);
+
+/**
+ * purple_http_response_get_header:
+ * @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/skypeweb/purple2compat/image-store.h b/skypeweb/purple2compat/image-store.h
new file mode 100644
index 0000000..7c62711
--- /dev/null
+++ b/skypeweb/purple2compat/image-store.h
@@ -0,0 +1,14 @@
+#ifndef _IMAGE_STORE_H_
+#define _IMAGE_STORE_H_
+
+#include "imgstore.h"
+#include "image.h"
+
+
+#define purple_image_store_add(img) purple_imgstore_add_with_id( \
+ g_memdup(purple_imgstore_get_data(img), purple_imgstore_get_size(img)), \
+ purple_imgstore_get_size(img), purple_imgstore_get_filename(img))
+#define purple_image_store_get purple_imgstore_find_by_id
+
+
+#endif /*_IMAGE_STORE_H_*/
diff --git a/skypeweb/purple2compat/image.h b/skypeweb/purple2compat/image.h
new file mode 100644
index 0000000..725ff84
--- /dev/null
+++ b/skypeweb/purple2compat/image.h
@@ -0,0 +1,14 @@
+#ifndef _IMAGE_H_
+#define _IMAGE_H_
+
+#include "imgstore.h"
+
+#define PurpleImage PurpleStoredImage
+#define purple_image_new_from_file(p, e) purple_imgstore_new_from_file(p)
+#define purple_image_new_from_data(d, l) purple_imgstore_add(d, l, NULL)
+#define purple_image_get_path purple_imgstore_get_filename
+#define purple_image_get_size purple_imgstore_get_size
+#define purple_image_get_data purple_imgstore_get_data
+#define purple_image_get_extension purple_imgstore_get_extension
+
+#endif /* _IMAGE_H_ */
diff --git a/skypeweb/purple2compat/internal.h b/skypeweb/purple2compat/internal.h
new file mode 100644
index 0000000..b59053e
--- /dev/null
+++ b/skypeweb/purple2compat/internal.h
@@ -0,0 +1,17 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include "win32/win32dep.h"
+#endif
+
+#include "../purplecompat.h"
+
+#ifndef N_
+# define N_(a) (a)
+#endif
+
+#ifndef _
+# define _(a) (a)
+#endif
diff --git a/skypeweb/purple2compat/plugins.h b/skypeweb/purple2compat/plugins.h
new file mode 100644
index 0000000..5985562
--- /dev/null
+++ b/skypeweb/purple2compat/plugins.h
@@ -0,0 +1 @@
+#include "plugin.h"
diff --git a/skypeweb/purple2compat/purple-socket.c b/skypeweb/purple2compat/purple-socket.c
new file mode 100644
index 0000000..bf429ed
--- /dev/null
+++ b/skypeweb/purple2compat/purple-socket.c
@@ -0,0 +1,415 @@
+/* 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 "purple-socket.h"
+
+#ifndef _WIN32
+#include <errno.h>
+#include <unistd.h>
+#endif
+
+#include "internal.h"
+
+#include "debug.h"
+#include "proxy.h"
+#include "sslconn.h"
+
+typedef enum {
+ PURPLE_SOCKET_STATE_DISCONNECTED = 0,
+ PURPLE_SOCKET_STATE_CONNECTING,
+ PURPLE_SOCKET_STATE_CONNECTED,
+ PURPLE_SOCKET_STATE_ERROR
+} PurpleSocketState;
+
+struct _PurpleSocket
+{
+ PurpleConnection *gc;
+ gchar *host;
+ int port;
+ gboolean is_tls;
+ GHashTable *data;
+
+ PurpleSocketState state;
+
+ PurpleSslConnection *tls_connection;
+ PurpleProxyConnectData *raw_connection;
+ int fd;
+ guint inpa;
+
+ PurpleSocketConnectCb cb;
+ gpointer cb_data;
+};
+
+static GHashTable *handles = NULL;
+
+static void
+handle_add(PurpleSocket *ps)
+{
+ PurpleConnection *gc = ps->gc;
+ GSList *l;
+
+ l = g_hash_table_lookup(handles, gc);
+ l = g_slist_prepend(l, ps);
+ g_hash_table_insert(handles, gc, l);
+}
+
+static void
+handle_remove(PurpleSocket *ps)
+{
+ PurpleConnection *gc = ps->gc;
+ GSList *l;
+
+ l = g_hash_table_lookup(handles, gc);
+ l = g_slist_remove(l, ps);
+ g_hash_table_insert(handles, gc, l);
+}
+
+void
+_purple_socket_init(void)
+{
+ handles = g_hash_table_new(g_direct_hash, g_direct_equal);
+}
+
+void
+_purple_socket_uninit(void)
+{
+ g_hash_table_destroy(handles);
+ handles = NULL;
+}
+
+PurpleSocket *
+purple_socket_new(PurpleConnection *gc)
+{
+ PurpleSocket *ps = g_new0(PurpleSocket, 1);
+
+ ps->gc = gc;
+ ps->fd = -1;
+ ps->port = -1;
+ ps->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ handle_add(ps);
+
+ return ps;
+}
+
+PurpleConnection *
+purple_socket_get_connection(PurpleSocket *ps)
+{
+ g_return_val_if_fail(ps != NULL, NULL);
+
+ return ps->gc;
+}
+
+static gboolean
+purple_socket_check_state(PurpleSocket *ps, PurpleSocketState wanted_state)
+{
+ g_return_val_if_fail(ps != NULL, FALSE);
+
+ if (ps->state == wanted_state)
+ return TRUE;
+
+ purple_debug_error("socket", "invalid state: %d (should be: %d)",
+ ps->state, wanted_state);
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+}
+
+void
+purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls)
+{
+ g_return_if_fail(ps != NULL);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+ return;
+
+ ps->is_tls = is_tls;
+}
+
+void
+purple_socket_set_host(PurpleSocket *ps, const gchar *host)
+{
+ g_return_if_fail(ps != NULL);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+ return;
+
+ g_free(ps->host);
+ ps->host = g_strdup(host);
+}
+
+void
+purple_socket_set_port(PurpleSocket *ps, int port)
+{
+ g_return_if_fail(ps != NULL);
+ g_return_if_fail(port >= 0);
+ g_return_if_fail(port <= 65535);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+ return;
+
+ ps->port = port;
+}
+
+static void
+_purple_socket_connected_raw(gpointer _ps, gint fd, const gchar *error_message)
+{
+ PurpleSocket *ps = _ps;
+
+ ps->raw_connection = NULL;
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
+ if (fd > 0)
+ close(fd);
+ ps->cb(ps, _("Invalid socket state"), ps->cb_data);
+ return;
+ }
+
+ if (fd <= 0 || error_message != NULL) {
+ if (error_message == NULL)
+ error_message = _("Unknown error");
+ ps->fd = -1;
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ ps->cb(ps, error_message, ps->cb_data);
+ return;
+ }
+
+ ps->state = PURPLE_SOCKET_STATE_CONNECTED;
+ ps->fd = fd;
+ ps->cb(ps, NULL, ps->cb_data);
+}
+
+static void
+_purple_socket_connected_tls(gpointer _ps, PurpleSslConnection *tls_connection,
+ PurpleInputCondition cond)
+{
+ PurpleSocket *ps = _ps;
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
+ purple_ssl_close(tls_connection);
+ ps->tls_connection = NULL;
+ ps->cb(ps, _("Invalid socket state"), ps->cb_data);
+ return;
+ }
+
+ if (ps->tls_connection->fd <= 0) {
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ purple_ssl_close(tls_connection);
+ ps->tls_connection = NULL;
+ ps->cb(ps, _("Invalid file descriptor"), ps->cb_data);
+ return;
+ }
+
+ ps->state = PURPLE_SOCKET_STATE_CONNECTED;
+ ps->fd = ps->tls_connection->fd;
+ ps->cb(ps, NULL, ps->cb_data);
+}
+
+static void
+_purple_socket_connected_tls_error(PurpleSslConnection *ssl_connection,
+ PurpleSslErrorType error, gpointer _ps)
+{
+ PurpleSocket *ps = _ps;
+
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ ps->tls_connection = NULL;
+ ps->cb(ps, purple_ssl_strerror(error), ps->cb_data);
+}
+
+gboolean
+purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
+ gpointer user_data)
+{
+ PurpleAccount *account = NULL;
+
+ g_return_val_if_fail(ps != NULL, FALSE);
+
+ if (ps->gc && purple_connection_is_disconnecting(ps->gc)) {
+ purple_debug_error("socket", "connection is being destroyed");
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+ }
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+ return FALSE;
+ ps->state = PURPLE_SOCKET_STATE_CONNECTING;
+
+ if (ps->host == NULL || ps->port < 0) {
+ purple_debug_error("socket", "Host or port is not specified");
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+ }
+
+ if (ps->gc != NULL)
+ account = purple_connection_get_account(ps->gc);
+
+ ps->cb = cb;
+ ps->cb_data = user_data;
+
+ if (ps->is_tls) {
+ ps->tls_connection = purple_ssl_connect(account, ps->host,
+ ps->port, _purple_socket_connected_tls,
+ _purple_socket_connected_tls_error, ps);
+ } else {
+ ps->raw_connection = purple_proxy_connect(ps->gc, account,
+ ps->host, ps->port, _purple_socket_connected_raw, ps);
+ }
+
+ if (ps->tls_connection == NULL &&
+ ps->raw_connection == NULL)
+ {
+ ps->state = PURPLE_SOCKET_STATE_ERROR;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gssize
+purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len)
+{
+ g_return_val_if_fail(ps != NULL, -1);
+ g_return_val_if_fail(buf != NULL, -1);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+ return -1;
+
+ if (ps->is_tls)
+ return purple_ssl_read(ps->tls_connection, buf, len);
+ else
+ return read(ps->fd, buf, len);
+}
+
+gssize
+purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len)
+{
+ g_return_val_if_fail(ps != NULL, -1);
+ g_return_val_if_fail(buf != NULL, -1);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+ return -1;
+
+ if (ps->is_tls)
+ return purple_ssl_write(ps->tls_connection, buf, len);
+ else
+ return write(ps->fd, buf, len);
+}
+
+void
+purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
+ PurpleInputFunction func, gpointer user_data)
+{
+ g_return_if_fail(ps != NULL);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+ return;
+
+ if (ps->inpa > 0)
+ purple_input_remove(ps->inpa);
+ ps->inpa = 0;
+
+ g_return_if_fail(ps->fd > 0);
+
+ if (func != NULL)
+ ps->inpa = purple_input_add(ps->fd, cond, func, user_data);
+}
+
+int
+purple_socket_get_fd(PurpleSocket *ps)
+{
+ g_return_val_if_fail(ps != NULL, -1);
+
+ if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+ return -1;
+
+ g_return_val_if_fail(ps->fd > 0, -1);
+
+ return ps->fd;
+}
+
+void
+purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data)
+{
+ g_return_if_fail(ps != NULL);
+ g_return_if_fail(key != NULL);
+
+ if (data == NULL)
+ g_hash_table_remove(ps->data, key);
+ else
+ g_hash_table_insert(ps->data, g_strdup(key), data);
+}
+
+gpointer
+purple_socket_get_data(PurpleSocket *ps, const gchar *key)
+{
+ g_return_val_if_fail(ps != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+
+ return g_hash_table_lookup(ps->data, key);
+}
+
+static void
+purple_socket_cancel(PurpleSocket *ps)
+{
+ if (ps->inpa > 0)
+ purple_input_remove(ps->inpa);
+ ps->inpa = 0;
+
+ if (ps->tls_connection != NULL) {
+ purple_ssl_close(ps->tls_connection);
+ ps->fd = -1;
+ }
+ ps->tls_connection = NULL;
+
+ if (ps->raw_connection != NULL)
+ purple_proxy_connect_cancel(ps->raw_connection);
+ ps->raw_connection = NULL;
+
+ if (ps->fd > 0)
+ close(ps->fd);
+ ps->fd = 0;
+}
+
+void
+purple_socket_destroy(PurpleSocket *ps)
+{
+ if (ps == NULL)
+ return;
+
+ handle_remove(ps);
+
+ purple_socket_cancel(ps);
+
+ g_free(ps->host);
+ g_hash_table_destroy(ps->data);
+ g_free(ps);
+}
+
+void
+_purple_socket_cancel_with_connection(PurpleConnection *gc)
+{
+ GSList *it;
+
+ it = g_hash_table_lookup(handles, gc);
+ for (; it; it = g_slist_next(it)) {
+ PurpleSocket *ps = it->data;
+ purple_socket_cancel(ps);
+ }
+}
diff --git a/skypeweb/purple2compat/purple-socket.h b/skypeweb/purple2compat/purple-socket.h
new file mode 100644
index 0000000..b43e512
--- /dev/null
+++ b/skypeweb/purple2compat/purple-socket.h
@@ -0,0 +1,217 @@
+/* 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_SOCKET_H_
+#define _PURPLE_SOCKET_H_
+/**
+ * SECTION:purple-socket
+ * @section_id: libpurple-purple-socket
+ * @short_description: <filename>purple-socket.h</filename>
+ * @title: Generic Sockets
+ */
+
+#include "connection.h"
+
+/**
+ * PurpleSocket:
+ *
+ * A structure holding all resources needed for the TCP connection.
+ */
+typedef struct _PurpleSocket PurpleSocket;
+
+/**
+ * PurpleSocketConnectCb:
+ * @ps: The socket.
+ * @error: Error message, or NULL if connection was successful.
+ * @user_data: The user data passed with callback function.
+ *
+ * A callback fired after (successfully or not) establishing a connection.
+ */
+typedef void (*PurpleSocketConnectCb)(PurpleSocket *ps, const gchar *error,
+ gpointer user_data);
+
+/**
+ * purple_socket_new:
+ * @gc: The connection for which the socket is needed, or NULL.
+ *
+ * Creates new, disconnected socket.
+ *
+ * Passing a PurpleConnection allows for proper proxy handling.
+ *
+ * Returns: The new socket struct.
+ */
+PurpleSocket *
+purple_socket_new(PurpleConnection *gc);
+
+/**
+ * purple_socket_get_connection:
+ * @ps: The socket.
+ *
+ * Gets PurpleConnection tied with specified socket.
+ *
+ * Returns: The PurpleConnection object.
+ */
+PurpleConnection *
+purple_socket_get_connection(PurpleSocket *ps);
+
+/**
+ * purple_socket_set_tls:
+ * @ps: The socket.
+ * @is_tls: TRUE, if TLS should be handled transparently, FALSE otherwise.
+ *
+ * Determines, if socket should handle TLS.
+ */
+void
+purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls);
+
+/**
+ * purple_socket_set_host:
+ * @ps: The socket.
+ * @host: The connection host.
+ *
+ * Sets connection host.
+ */
+void
+purple_socket_set_host(PurpleSocket *ps, const gchar *host);
+
+/**
+ * purple_socket_set_port:
+ * @ps: The socket.
+ * @port: The connection port.
+ *
+ * Sets connection port.
+ */
+void
+purple_socket_set_port(PurpleSocket *ps, int port);
+
+/**
+ * purple_socket_connect:
+ * @ps: The socket.
+ * @cb: The function to call after establishing a connection, or on
+ * error.
+ * @user_data: The user data to be passed to callback function.
+ *
+ * Establishes a connection.
+ *
+ * Returns: TRUE on success (this doesn't mean it's connected yet), FALSE
+ * otherwise.
+ */
+gboolean
+purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
+ gpointer user_data);
+
+/**
+ * purple_socket_read:
+ * @ps: The socket.
+ * @buf: The buffer to write data to.
+ * @len: The buffer size.
+ *
+ * Reads incoming data from socket.
+ *
+ * This function deals with TLS, if the socket is configured to do it.
+ *
+ * Returns: Amount of data written, or -1 on error (errno will be also be set).
+ */
+gssize
+purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len);
+
+/**
+ * purple_socket_write:
+ * @ps: The socket.
+ * @buf: The buffer to read data from.
+ * @len: The amount of data to read and send.
+ *
+ * Sends data through socket.
+ *
+ * This function deals with TLS, if the socket is configured to do it.
+ *
+ * Returns: Amount of data sent, or -1 on error (errno will albo be set).
+ */
+gssize
+purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len);
+
+/**
+ * purple_socket_watch:
+ * @ps: The socket.
+ * @cond: The condition type.
+ * @func: The callback function for data, or NULL to remove any
+ * existing callbacks.
+ * @user_data: The user data to be passed to callback function.
+ *
+ * Adds an input handler for the socket.
+ *
+ * If the specified socket had input handler already registered, it will be
+ * removed. To remove any input handlers, pass an NULL handler function.
+ */
+void
+purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
+ PurpleInputFunction func, gpointer user_data);
+
+/**
+ * purple_socket_get_fd:
+ * @ps: The socket
+ *
+ * Gets underlying file descriptor for socket.
+ *
+ * It's not meant to read/write data (use purple_socket_read/
+ * purple_socket_write), rather for watching for changes with select().
+ *
+ * Returns: The file descriptor, or -1 on error.
+ */
+int
+purple_socket_get_fd(PurpleSocket *ps);
+
+/**
+ * purple_socket_set_data:
+ * @ps: The socket.
+ * @key: The unique key.
+ * @data: The data to assign, or NULL to remove.
+ *
+ * Sets extra data for a socket.
+ */
+void
+purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data);
+
+/**
+ * purple_socket_get_data:
+ * @ps: The socket.
+ * @key: The unqiue key.
+ *
+ * Returns extra data in a socket.
+ *
+ * Returns: The data associated with the key.
+ */
+gpointer
+purple_socket_get_data(PurpleSocket *ps, const gchar *key);
+
+/**
+ * purple_socket_destroy:
+ * @ps: The socket.
+ *
+ * Destroys the socket, closes connection and frees all resources.
+ *
+ * If file descriptor for the socket was extracted with purple_socket_get_fd and
+ * added to event loop, it have to be removed prior this.
+ */
+void
+purple_socket_destroy(PurpleSocket *ps);
+
+#endif /* _PURPLE_SOCKET_H_ */
diff --git a/skypeweb/purple2compat/xfer.h b/skypeweb/purple2compat/xfer.h
new file mode 100644
index 0000000..682968d
--- /dev/null
+++ b/skypeweb/purple2compat/xfer.h
@@ -0,0 +1 @@
+#include "ft.h"
diff --git a/skypeweb/purplecompat.h b/skypeweb/purplecompat.h
new file mode 100644
index 0000000..7c74733
--- /dev/null
+++ b/skypeweb/purplecompat.h
@@ -0,0 +1,252 @@
+#ifndef _PURPLECOMPAT_H_
+#define _PURPLECOMPAT_H_
+
+#include <glib.h>
+#include "version.h"
+
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+#include <glib-object.h>
+
+#define purple_conversation_set_data(conv, key, value) g_object_set_data(G_OBJECT(conv), key, value)
+#define purple_conversation_get_data(conv, key) g_object_get_data(G_OBJECT(conv), key)
+
+#define purple_xfer_ref g_object_ref
+
+#define purple_xfer_unref g_object_unref
+#define purple_circular_buffer_destroy g_object_unref
+#define purple_hash_destroy g_object_unref
+#define purple_message_destroy g_object_unref
+#define purple_buddy_destroy g_object_unref
+
+#define PURPLE_TYPE_STRING G_TYPE_STRING
+
+#define purple_protocol_action_get_connection(action) ((action)->connection)
+
+#define purple_chat_user_set_alias(cb, alias) g_object_set((cb), "alias", (alias), NULL)
+#define purple_chat_get_alias(chat) g_object_get_data(G_OBJECT(chat), "alias")
+
+//TODO remove this when dx adds this to the PurpleMessageFlags enum
+#define PURPLE_MESSAGE_REMOTE_SEND 0x10000
+
+#else /*!PURPLE_VERSION_CHECK(3, 0, 0)*/
+
+#include "connection.h"
+
+#define purple_blist_find_buddy purple_find_buddy
+#define purple_blist_find_group purple_find_group
+#define purple_blist_find_buddies purple_find_buddies
+#define PURPLE_IS_BUDDY PURPLE_BLIST_NODE_IS_BUDDY
+#define PURPLE_IS_CHAT PURPLE_BLIST_NODE_IS_CHAT
+#define purple_chat_get_name_only purple_chat_get_name
+#define purple_chat_set_alias purple_blist_alias_chat
+#define purple_chat_get_alias(chat) ((chat)->alias)
+#define purple_buddy_set_server_alias purple_blist_server_alias_buddy
+#define purple_buddy_get_local_alias purple_buddy_get_local_buddy_alias
+static inline void
+purple_blist_node_set_transient(PurpleBlistNode *node, gboolean transient)
+{
+ PurpleBlistNodeFlags old_flags = purple_blist_node_get_flags(node);
+ PurpleBlistNodeFlags new_flags;
+
+ if (transient)
+ new_flags = old_flags | PURPLE_BLIST_NODE_FLAG_NO_SAVE;
+ else
+ new_flags = old_flags & ~PURPLE_BLIST_NODE_FLAG_NO_SAVE;
+
+ purple_blist_node_set_flags(node, new_flags);
+}
+
+#define PURPLE_CMD_FLAG_PROTOCOL_ONLY PURPLE_CMD_FLAG_PRPL_ONLY
+
+#define PURPLE_TYPE_CONNECTION purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION)
+#define PURPLE_IS_CONNECTION PURPLE_CONNECTION_IS_VALID
+
+#define PURPLE_CONNECTION_DISCONNECTED PURPLE_DISCONNECTED
+#define PURPLE_CONNECTION_CONNECTING PURPLE_CONNECTING
+#define PURPLE_CONNECTION_CONNECTED PURPLE_CONNECTED
+#define PURPLE_CONNECTION_FLAG_HTML PURPLE_CONNECTION_HTML
+#define PURPLE_CONNECTION_FLAG_NO_BGCOLOR PURPLE_CONNECTION_NO_BGCOLOR
+#define PURPLE_CONNECTION_FLAG_NO_FONTSIZE PURPLE_CONNECTION_NO_FONTSIZE
+#define PURPLE_CONNECTION_FLAG_NO_IMAGES PURPLE_CONNECTION_NO_IMAGES
+
+#define purple_request_cpar_from_connection(a) purple_connection_get_account(a), NULL, NULL
+#define purple_connection_get_protocol purple_connection_get_prpl
+#define purple_connection_error purple_connection_error_reason
+#define purple_connection_is_disconnecting(c) FALSE
+#define purple_connection_set_flags(pc, f) ((pc)->flags = (f))
+#define purple_connection_get_flags(pc) ((pc)->flags)
+
+#define PurpleConversationUpdateType PurpleConvUpdateType
+#define PURPLE_CONVERSATION_UPDATE_TOPIC PURPLE_CONV_UPDATE_TOPIC
+#define PURPLE_CONVERSATION_UPDATE_UNSEEN PURPLE_CONV_UPDATE_UNSEEN
+#define PurpleChatConversation PurpleConvChat
+#define PurpleIMConversation PurpleConvIm
+#define purple_conversations_find_chat_with_account(id, account) \
+ PURPLE_CONV_CHAT(purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, id, account))
+#define purple_conversations_find_chat(pc, id) PURPLE_CONV_CHAT(purple_find_chat(pc, id))
+#define purple_conversations_get_all purple_get_conversations
+#define purple_conversation_get_connection purple_conversation_get_gc
+#define purple_conversation_present_error purple_conv_present_error
+#define purple_chat_conversation_get_id purple_conv_chat_get_id
+#define purple_chat_conversation_get_topic purple_conv_chat_get_topic
+#define purple_chat_conversation_set_topic purple_conv_chat_set_topic
+#define purple_conversations_find_im_with_account(name, account) \
+ PURPLE_CONV_IM(purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account))
+#define purple_im_conversation_new(account, from) PURPLE_CONV_IM(purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from))
+#define PURPLE_CONVERSATION(chatorim) (chatorim == NULL ? NULL : chatorim->conv)
+#define PURPLE_IM_CONVERSATION(conv) PURPLE_CONV_IM(conv)
+#define PURPLE_CHAT_CONVERSATION(conv) PURPLE_CONV_CHAT(conv)
+#define PURPLE_IS_IM_CONVERSATION(conv) (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+#define PURPLE_IS_CHAT_CONVERSATION(conv) (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+#define purple_chat_conversation_add_user purple_conv_chat_add_user
+#define purple_chat_conversation_has_left purple_conv_chat_has_left
+#define purple_chat_conversation_leave purple_conv_chat_left
+#define purple_chat_conversation_remove_user purple_conv_chat_remove_user
+#define purple_chat_conversation_clear_users purple_conv_chat_clear_users
+
+#define PurpleMessage PurpleConvMessage
+#define purple_message_get_contents(msg) ((msg)->what)
+#define purple_message_set_time(msg, time) ((msg)->when = (time))
+#define purple_conversation_write_message(conv, msg) purple_conversation_write((conv), (msg)->who, (msg)->what, (msg)->flags, (msg)->when)
+static inline PurpleMessage *
+purple_message_new_outgoing(const gchar *who, const gchar *contents, PurpleMessageFlags flags)
+{
+ PurpleMessage *message = g_new0(PurpleMessage, 1);
+
+ message->who = g_strdup(who);
+ message->what = g_strdup(contents);
+ message->flags = flags | PURPLE_MESSAGE_SEND;
+ message->when = time(NULL);
+
+ return message;
+}
+static inline PurpleMessage *
+purple_message_new_system(const gchar *contents, PurpleMessageFlags flags)
+{
+ PurpleMessage *message = g_new0(PurpleMessage, 1);
+
+ message->what = g_strdup(contents);
+ message->flags = flags | PURPLE_MESSAGE_SYSTEM;
+ message->when = time(NULL);
+
+ return message;
+}
+static inline void
+purple_message_destroy(PurpleMessage *message)
+{
+ g_free(message->who);
+ g_free(message->what);
+ g_free(message);
+}
+#if !PURPLE_VERSION_CHECK(2, 12, 0)
+# define PURPLE_MESSAGE_REMOTE_SEND 0x10000
+#endif
+
+#define purple_conversation_write_system_message(conv, msg, flags) purple_conversation_write((conv), NULL, (msg), (flags) | PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+#define PurpleProtocolChatEntry struct proto_chat_entry
+#define PurpleChatUserFlags PurpleConvChatBuddyFlags
+#define PURPLE_CHAT_USER_NONE PURPLE_CBFLAGS_NONE
+#define PURPLE_CHAT_USER_OP PURPLE_CBFLAGS_OP
+#define PURPLE_CHAT_USER_FOUNDER PURPLE_CBFLAGS_FOUNDER
+#define PURPLE_CHAT_USER_TYPING PURPLE_CBFLAGS_TYPING
+#define PURPLE_CHAT_USER_AWAY PURPLE_CBFLAGS_AWAY
+#define PURPLE_CHAT_USER_HALFOP PURPLE_CBFLAGS_HALFOP
+#define PURPLE_CHAT_USER_VOICE PURPLE_CBFLAGS_VOICE
+#define PURPLE_CHAT_USER_TYPING PURPLE_CBFLAGS_TYPING
+#define PurpleChatUser PurpleConvChatBuddy
+
+static inline PurpleChatUser *
+purple_chat_conversation_find_user(PurpleChatConversation *chat, const char *name)
+{
+ PurpleChatUser *cb = purple_conv_chat_cb_find(chat, name);
+
+ if (cb != NULL) {
+ g_dataset_set_data(cb, "chat", chat);
+ }
+
+ return cb;
+}
+#define purple_chat_user_get_flags(cb) purple_conv_chat_user_get_flags(g_dataset_get_data((cb), "chat"), (cb)->name)
+#define purple_chat_user_set_flags(cb, f) purple_conv_chat_user_set_flags(g_dataset_get_data((cb), "chat"), (cb)->name, (f))
+#define purple_chat_user_set_alias(cb, a) ((cb)->alias = (a))
+
+#define PurpleIMTypingState PurpleTypingState
+#define PURPLE_IM_NOT_TYPING PURPLE_NOT_TYPING
+#define PURPLE_IM_TYPING PURPLE_TYPING
+#define PURPLE_IM_TYPED PURPLE_TYPED
+
+#define purple_media_set_protocol_data purple_media_set_prpl_data
+#if PURPLE_VERSION_CHECK(2, 10, 12)
+// Handle ABI breakage
+# define PURPLE_MEDIA_NETWORK_PROTOCOL_TCP PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
+#endif
+
+#undef purple_notify_error
+#define purple_notify_error(handle, title, primary, secondary, cpar) \
+ purple_notify_message((handle), PURPLE_NOTIFY_MSG_ERROR, (title), \
+ (primary), (secondary), NULL, NULL)
+#undef purple_notify_warning
+#define purple_notify_warning(handle, title, primary, secondary, cpar) \
+ purple_notify_message((handle), PURPLE_NOTIFY_MSG_WARNING, (title), \
+ (primary), (secondary), NULL, NULL)
+#define purple_notify_user_info_add_pair_html purple_notify_user_info_add_pair
+
+#define PurpleProtocolAction PurplePluginAction
+#define purple_protocol_action_get_connection(action) ((PurpleConnection *) (action)->context)
+#define purple_protocol_action_new purple_plugin_action_new
+#define purple_protocol_get_id purple_plugin_get_id
+
+#define purple_protocol_got_user_status purple_prpl_got_user_status
+#define purple_protocol_got_user_idle purple_prpl_got_user_idle
+
+#define purple_account_privacy_deny_add purple_privacy_deny_add
+#define purple_account_privacy_deny_remove purple_privacy_deny_remove
+#define purple_account_set_password(account, password, dummy1, dummy2) \
+ purple_account_set_password(account, password);
+#define purple_account_set_private_alias purple_account_set_alias
+#define purple_account_get_private_alias purple_account_get_alias
+
+#define purple_proxy_info_get_proxy_type purple_proxy_info_get_type
+
+#define purple_serv_got_im serv_got_im
+#define purple_serv_got_typing serv_got_typing
+#define purple_serv_got_alias serv_got_alias
+#define purple_serv_got_chat_in serv_got_chat_in
+#define purple_serv_got_chat_left serv_got_chat_left
+#define purple_serv_got_joined_chat(pc, id, name) PURPLE_CONV_CHAT(serv_got_joined_chat(pc, id, name))
+
+#define purple_status_get_status_type purple_status_get_type
+
+#define PurpleXmlNode xmlnode
+#define purple_xmlnode_new xmlnode_new
+#define purple_xmlnode_new_child xmlnode_new_child
+#define purple_xmlnode_from_str xmlnode_from_str
+#define purple_xmlnode_to_str xmlnode_to_str
+#define purple_xmlnode_get_child xmlnode_get_child
+#define purple_xmlnode_get_next_twin xmlnode_get_next_twin
+#define purple_xmlnode_get_data xmlnode_get_data
+#define purple_xmlnode_get_attrib xmlnode_get_attrib
+#define purple_xmlnode_set_attrib xmlnode_set_attrib
+#define purple_xmlnode_insert_data xmlnode_insert_data
+#define purple_xmlnode_free xmlnode_free
+
+#define purple_xfer_set_protocol_data(xfer, proto_data) ((xfer)->data = (proto_data))
+#define purple_xfer_get_protocol_data(xfer) ((xfer)->data)
+#define purple_xfer_get_xfer_type purple_xfer_get_type
+#define purple_xfer_protocol_ready purple_xfer_prpl_ready
+#if !PURPLE_VERSION_CHECK(2, 10, 12) && !FEDORA
+static inline gboolean
+purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size) {
+ PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
+ purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) +
+ (ui_ops && ui_ops->ui_write ? ui_ops->ui_write(xfer, buffer, size) : fwrite(buffer, 1, size, xfer->dest_fp)));
+ return TRUE;
+}
+#define PURPLE_XFER_TYPE_RECEIVE PURPLE_XFER_RECEIVE
+#define PURPLE_XFER_TYPE_SEND PURPLE_XFER_SEND
+#endif
+
+#endif
+
+#endif /*_PURPLECOMPAT_H_*/
diff --git a/skypeweb/skypeweb_connection.c b/skypeweb/skypeweb_connection.c
index 44cf4ae..f2fc9ac 100644
--- a/skypeweb/skypeweb_connection.c
+++ b/skypeweb/skypeweb_connection.c
@@ -18,778 +18,121 @@
#include "skypeweb_connection.h"
-#if !PURPLE_VERSION_CHECK(3, 0, 0)
- #define purple_connection_error purple_connection_error_reason
- #define purple_proxy_info_get_proxy_type purple_proxy_info_get_type
- #define purple_account_is_disconnecting(account) (account->disconnecting)
-#endif
+#include "http.h"
-#if !GLIB_CHECK_VERSION (2, 22, 0)
-#define g_hostname_is_ip_address(hostname) (g_ascii_isdigit(hostname[0]) && g_strstr_len(hostname, 4, "."))
-#endif
-
-static void skypeweb_attempt_connection(SkypeWebConnection *);
-static void skypeweb_next_connection(SkypeWebAccount *sa);
-
-#include <zlib.h>
-
-static gchar *skypeweb_gunzip(const guchar *gzip_data, gssize *len_ptr)
+static void
+skypeweb_destroy_connection(SkypeWebConnection *conn)
{
- gsize gzip_data_len = *len_ptr;
- z_stream zstr;
- int gzip_err = 0;
- gchar *data_buffer;
- gulong gzip_len = G_MAXUINT16;
- GString *output_string = NULL;
-
- data_buffer = g_new0(gchar, gzip_len);
-
- zstr.next_in = NULL;
- zstr.avail_in = 0;
- zstr.zalloc = Z_NULL;
- zstr.zfree = Z_NULL;
- zstr.opaque = 0;
- gzip_err = inflateInit2(&zstr, MAX_WBITS+32);
- if (gzip_err != Z_OK)
- {
- g_free(data_buffer);
- purple_debug_error("skypeweb", "no built-in gzip support in zlib\n");
- return NULL;
- }
-
- zstr.next_in = (Bytef *)gzip_data;
- zstr.avail_in = gzip_data_len;
-
- zstr.next_out = (Bytef *)data_buffer;
- zstr.avail_out = gzip_len;
-
- gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
-
- if (gzip_err == Z_DATA_ERROR)
- {
- inflateEnd(&zstr);
- gzip_err = inflateInit2(&zstr, -MAX_WBITS);
- if (gzip_err != Z_OK)
- {
- g_free(data_buffer);
- purple_debug_error("skypeweb", "Cannot decode gzip header\n");
- return NULL;
- }
- zstr.next_in = (Bytef *)gzip_data;
- zstr.avail_in = gzip_data_len;
- zstr.next_out = (Bytef *)data_buffer;
- zstr.avail_out = gzip_len;
- gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
- }
- output_string = g_string_new("");
- while (gzip_err == Z_OK)
- {
- //append data to buffer
- output_string = g_string_append_len(output_string, data_buffer, gzip_len - zstr.avail_out);
- //reset buffer pointer
- zstr.next_out = (Bytef *)data_buffer;
- zstr.avail_out = gzip_len;
- gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
- }
- if (gzip_err == Z_STREAM_END)
- {
- output_string = g_string_append_len(output_string, data_buffer, gzip_len - zstr.avail_out);
- } else {
- purple_debug_error("skypeweb", "gzip inflate error\n");
- }
- inflateEnd(&zstr);
-
- g_free(data_buffer);
-
- if (len_ptr)
- *len_ptr = output_string->len;
-
- return g_string_free(output_string, FALSE);
+ g_free(conn->url);
+ g_free(conn);
}
-void
-skypeweb_connection_close(SkypeWebConnection *skypewebcon)
+static void
+skypeweb_post_or_get_cb(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
- skypewebcon->sa->conns = g_slist_remove(skypewebcon->sa->conns, skypewebcon);
+ SkypeWebConnection *conn = user_data;
+ const gchar *data;
+ gsize len;
- if (skypewebcon->connect_data != NULL) {
- purple_proxy_connect_cancel(skypewebcon->connect_data);
- skypewebcon->connect_data = NULL;
- }
-
- if (skypewebcon->ssl_conn != NULL) {
- purple_ssl_close(skypewebcon->ssl_conn);
- skypewebcon->ssl_conn = NULL;
- }
-
- if (skypewebcon->fd >= 0) {
- close(skypewebcon->fd);
- skypewebcon->fd = -1;
- }
-
- if (skypewebcon->input_watcher > 0) {
- purple_input_remove(skypewebcon->input_watcher);
- skypewebcon->input_watcher = 0;
- }
-
- purple_timeout_remove(skypewebcon->timeout_watcher);
-
- g_free(skypewebcon->rx_buf);
- skypewebcon->rx_buf = NULL;
- skypewebcon->rx_len = 0;
-}
-
-void skypeweb_connection_destroy(SkypeWebConnection *skypewebcon)
-{
- skypeweb_connection_close(skypewebcon);
-
- if (skypewebcon->request != NULL)
- g_string_free(skypewebcon->request, TRUE);
+ data = purple_http_response_get_data(response, &len);
- g_free(skypewebcon->url);
- g_free(skypewebcon->hostname);
- g_free(skypewebcon);
-}
-
-void
-skypeweb_update_cookies(SkypeWebAccount *sa, const gchar *headers)
-{
- const gchar *cookie_start;
- const gchar *cookie_end;
- gchar *cookie_name;
- gchar *cookie_value;
- int header_len;
-
- g_return_if_fail(headers != NULL);
-
- header_len = strlen(headers);
-
- /* look for the next "Set-Cookie: " */
- /* grab the data up until ';' */
- cookie_start = headers;
- while ((cookie_start = strstr(cookie_start, "\r\nSet-Cookie: ")) &&
- (cookie_start - headers) < header_len)
- {
- cookie_start += 14;
- cookie_end = strchr(cookie_start, '=');
- cookie_name = g_strndup(cookie_start, cookie_end-cookie_start);
- cookie_start = cookie_end + 1;
- cookie_end = strchr(cookie_start, ';');
- cookie_value= g_strndup(cookie_start, cookie_end-cookie_start);
- cookie_start = cookie_end;
-
- g_hash_table_replace(sa->cookie_table, cookie_name,
- cookie_value);
- }
-}
-
-static void skypeweb_connection_process_data(SkypeWebConnection *skypewebcon)
-{
- gssize len;
- gchar *tmp;
-
- len = skypewebcon->rx_len;
- tmp = g_strstr_len(skypewebcon->rx_buf, len, "\r\n\r\n");
- if (tmp == NULL) {
- /* This is a corner case that occurs when the connection is
- * prematurely closed either on the client or the server.
- * This can either be no data at all or a partial set of
- * headers. We pass along the data to be good, but don't
- * do any fancy massaging. In all likelihood the result will
- * be tossed by the connection callback func anyways
- */
- tmp = g_strndup(skypewebcon->rx_buf, len);
- } else {
- tmp += 4;
- len -= g_strstr_len(skypewebcon->rx_buf, len, "\r\n\r\n") -
- skypewebcon->rx_buf + 4;
- tmp = g_memdup(tmp, len + 1);
- tmp[len] = '\0';
- skypewebcon->rx_buf[skypewebcon->rx_len - len] = '\0';
- skypeweb_update_cookies(skypewebcon->sa, skypewebcon->rx_buf);
-
- if (strstr(skypewebcon->rx_buf, "Content-Encoding: gzip"))
- {
- /* we've received compressed gzip data, decompress */
- gchar *gunzipped;
- gunzipped = skypeweb_gunzip((const guchar *)tmp, &len);
- g_free(tmp);
- tmp = gunzipped;
- }
- }
-
- purple_debug_misc("skypeweb", "Got response: %s\n", skypewebcon->rx_buf);
- g_free(skypewebcon->rx_buf);
- skypewebcon->rx_buf = NULL;
-
- if (skypewebcon->callback != NULL) {
+ if (conn->callback != NULL) {
if (!len)
{
purple_debug_info("skypeweb", "No data in response\n");
- skypewebcon->callback(skypewebcon->sa, NULL, skypewebcon->user_data);
+ conn->callback(conn->sa, NULL, conn->user_data);
} else {
JsonParser *parser = json_parser_new();
- if (!json_parser_load_from_data(parser, tmp, len, NULL))
+ if (!json_parser_load_from_data(parser, data, len, NULL))
{
- if (skypewebcon->error_callback != NULL) {
- skypewebcon->error_callback(skypewebcon->sa, tmp, len, skypewebcon->user_data);
+ if (conn->error_callback != NULL) {
+ conn->error_callback(conn->sa, data, len, conn->user_data);
} else {
- purple_debug_error("skypeweb", "Error parsing response: %s\n", tmp);
+ purple_debug_error("skypeweb", "Error parsing response: %s\n", data);
}
} else {
JsonNode *root = json_parser_get_root(parser);
- purple_debug_info("skypeweb", "executing callback for %s\n", skypewebcon->url);
- skypewebcon->callback(skypewebcon->sa, root, skypewebcon->user_data);
+ purple_debug_info("skypeweb", "executing callback for %s\n", conn->url);
+ conn->callback(conn->sa, root, conn->user_data);
}
g_object_unref(parser);
}
}
-
- g_free(tmp);
-}
-
-static void skypeweb_fatal_connection_cb(SkypeWebConnection *skypewebcon)
-{
- PurpleConnection *pc = skypewebcon->sa->pc;
-
- purple_debug_error("skypeweb", "fatal connection error\n");
-
- skypeweb_connection_destroy(skypewebcon);
-
- /* We died. Do not pass Go. Do not collect $200 */
- /* In all seriousness, don't attempt to call the normal callback here.
- * That may lead to the wrong error message being displayed */
- purple_connection_error(pc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Server closed the connection."));
-
-}
-
-static void skypeweb_post_or_get_readdata_cb(gpointer data, gint source,
- PurpleInputCondition cond)
-{
- SkypeWebConnection *skypewebcon;
- SkypeWebAccount *sa;
- gchar buf[4096];
- gssize len;
-
- skypewebcon = data;
- sa = skypewebcon->sa;
-
- if (skypewebcon->method & SKYPEWEB_METHOD_SSL) {
- len = purple_ssl_read(skypewebcon->ssl_conn,
- buf, sizeof(buf) - 1);
- } else {
- len = recv(skypewebcon->fd, buf, sizeof(buf) - 1, 0);
- }
-
- if (len < 0)
- {
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
- /* Try again later */
- return;
- }
-
- if (skypewebcon->method & SKYPEWEB_METHOD_SSL && skypewebcon->rx_len > 0) {
- /*
- * This is a slightly hacky workaround for a bug in either
- * GNU TLS or in the SSL implementation on skypeweb's web
- * servers. The sequence of events is:
- * 1. We attempt to read the first time and successfully read
- * the server's response.
- * 2. We attempt to read a second time and libpurple's call
- * to gnutls_record_recv() returns the error
- * GNUTLS_E_UNEXPECTED_PACKET_LENGTH, or
- * "A TLS packet with unexpected length was received."
- *
- * Normally the server would have closed the connection
- * cleanly and this second read() request would have returned
- * 0. Or maybe it's normal for SSL connections to be severed
- * in this manner? In any case, this differs from the behavior
- * of the standard recv() system call.
- */
- purple_debug_warning("skypeweb",
- "ssl error, but data received. attempting to continue\n");
- } else {
- /* Try resend the request */
- skypewebcon->retry_count++;
- if (skypewebcon->retry_count < 3) {
- skypeweb_connection_close(skypewebcon);
- skypewebcon->request_time = time(NULL);
-
- g_queue_push_head(sa->waiting_conns, skypewebcon);
- skypeweb_next_connection(sa);
- } else {
- skypeweb_fatal_connection_cb(skypewebcon);
- }
- return;
- }
- }
-
- if (len > 0)
- {
- buf[len] = '\0';
-
- skypewebcon->rx_buf = g_realloc(skypewebcon->rx_buf,
- skypewebcon->rx_len + len + 1);
- memcpy(skypewebcon->rx_buf + skypewebcon->rx_len, buf, len + 1);
- skypewebcon->rx_len += len;
-
- /* Wait for more data before processing */
- return;
- }
-
- /* The server closed the connection, let's parse the data */
- skypeweb_connection_process_data(skypewebcon);
-
- skypeweb_connection_destroy(skypewebcon);
-
- skypeweb_next_connection(sa);
-}
-
-static void skypeweb_post_or_get_ssl_readdata_cb (gpointer data,
- PurpleSslConnection *ssl, PurpleInputCondition cond)
-{
- skypeweb_post_or_get_readdata_cb(data, -1, cond);
-}
-
-static void skypeweb_post_or_get_connect_cb(gpointer data, gint source,
- const gchar *error_message)
-{
- SkypeWebConnection *skypewebcon;
- gssize len;
-
- skypewebcon = data;
- skypewebcon->connect_data = NULL;
-
- if (error_message)
- {
- purple_debug_error("skypeweb", "post_or_get_connect failure to %s\n", skypewebcon->url);
- purple_debug_error("skypeweb", "post_or_get_connect_cb %s\n",
- error_message);
- skypeweb_fatal_connection_cb(skypewebcon);
- return;
- }
-
- skypewebcon->fd = source;
-
- /* TODO: Check the return value of write() */
- len = write(skypewebcon->fd, skypewebcon->request->str,
- skypewebcon->request->len);
- (void) len;
- skypewebcon->input_watcher = purple_input_add(skypewebcon->fd,
- PURPLE_INPUT_READ,
- skypeweb_post_or_get_readdata_cb, skypewebcon);
-}
-
-static void skypeweb_post_or_get_ssl_connect_cb(gpointer data,
- PurpleSslConnection *ssl, PurpleInputCondition cond)
-{
- SkypeWebConnection *skypewebcon;
- gssize len;
-
- skypewebcon = data;
-
- purple_debug_info("skypeweb", "post_or_get_ssl_connect_cb\n");
-
- /* TODO: Check the return value of write() */
- len = purple_ssl_write(skypewebcon->ssl_conn,
- skypewebcon->request->str, skypewebcon->request->len);
- (void) len;
- purple_ssl_input_add(skypewebcon->ssl_conn,
- skypeweb_post_or_get_ssl_readdata_cb, skypewebcon);
-}
-
-static void skypeweb_host_lookup_cb(GSList *hosts, gpointer data,
- const char *error_message)
-{
- GSList *host_lookup_list;
- struct sockaddr_in *addr;
- gchar *hostname;
- gchar *ip_address;
- SkypeWebAccount *sa;
- PurpleDnsQueryData *query;
-
- /* Extract variables */
- host_lookup_list = data;
-
- sa = host_lookup_list->data;
- host_lookup_list =
- g_slist_delete_link(host_lookup_list, host_lookup_list);
- hostname = host_lookup_list->data;
- host_lookup_list =
- g_slist_delete_link(host_lookup_list, host_lookup_list);
- query = host_lookup_list->data;
- host_lookup_list =
- g_slist_delete_link(host_lookup_list, host_lookup_list);
-
- /* The callback has executed, so we no longer need to keep track of
- * the original query. This always needs to run when the cb is
- * executed. */
- sa->dns_queries = g_slist_remove(sa->dns_queries, query);
-
- /* Any problems, capt'n? */
- if (error_message != NULL)
- {
- purple_debug_warning("skypeweb",
- "Error doing host lookup: %s\n", error_message);
- return;
- }
-
- if (hosts == NULL)
- {
- purple_debug_warning("skypeweb",
- "Could not resolve host name\n");
- return;
- }
-
- /* Discard the length... */
- hosts = g_slist_delete_link(hosts, hosts);
- /* Copy the address then free it... */
- addr = hosts->data;
- ip_address = g_strdup(inet_ntoa(addr->sin_addr));
- g_free(addr);
- hosts = g_slist_delete_link(hosts, hosts);
-
- /*
- * DNS lookups can return a list of IP addresses, but we only cache
- * the first one. So free the rest.
- */
- while (hosts != NULL)
- {
- /* Discard the length... */
- hosts = g_slist_delete_link(hosts, hosts);
- /* Free the address... */
- g_free(hosts->data);
- hosts = g_slist_delete_link(hosts, hosts);
- }
-
- g_hash_table_insert(sa->hostname_ip_cache, hostname, ip_address);
-}
-
-static void skypeweb_cookie_foreach_cb(gchar *cookie_name,
- gchar *cookie_value, GString *str)
-{
- /* TODO: Need to escape name and value? */
- g_string_append_printf(str, "%s=%s;", cookie_name, cookie_value);
-}
-
-/**
- * Serialize the sa->cookie_table hash table to a string.
- */
-gchar *skypeweb_cookies_to_string(SkypeWebAccount *sa)
-{
- GString *str;
-
- str = g_string_new(NULL);
-
- g_hash_table_foreach(sa->cookie_table,
- (GHFunc)skypeweb_cookie_foreach_cb, str);
-
- return g_string_free(str, FALSE);
-}
-
-static void skypeweb_ssl_connection_error(PurpleSslConnection *ssl,
- PurpleSslErrorType errortype, gpointer data)
-{
- SkypeWebConnection *skypewebcon = data;
- SkypeWebAccount *sa = skypewebcon->sa;
- PurpleConnection *pc = sa->pc;
- skypewebcon->ssl_conn = NULL;
-
- /* Try resend the request */
- skypewebcon->retry_count++;
- if (skypewebcon->retry_count < 3) {
- skypeweb_connection_close(skypewebcon);
- skypewebcon->request_time = time(NULL);
-
- g_queue_push_head(sa->waiting_conns, skypewebcon);
- skypeweb_next_connection(sa);
- } else {
- skypeweb_connection_destroy(skypewebcon);
- purple_connection_ssl_error(pc, errortype);
- }
+ skypeweb_destroy_connection(conn);
}
-SkypeWebConnection *
-skypeweb_post_or_get(SkypeWebAccount *sa, SkypeWebMethod method,
+SkypeWebConnection *skypeweb_post_or_get(SkypeWebAccount *sa, SkypeWebMethod method,
const gchar *host, const gchar *url, const gchar *postdata,
SkypeWebProxyCallbackFunc callback_func, gpointer user_data,
gboolean keepalive)
{
- GString *request;
- gchar *cookies;
- SkypeWebConnection *skypewebcon;
- gchar *real_url;
- gboolean is_proxy = FALSE;
- //const gchar *user_agent;
+ SkypeWebConnection *conn;
+ PurpleHttpRequest *request;
const gchar* const *languages;
gchar *language_names;
- PurpleProxyInfo *proxy_info = NULL;
- gchar *proxy_auth;
- gchar *proxy_auth_base64;
-
- /* TODO: Fix keepalive and use it as much as possible */
- keepalive = FALSE;
-
- if (host == NULL)
- host = "api.skype.com";
-
- if (sa && sa->account)
- {
- if (purple_account_get_bool(sa->account, "use-https", TRUE))
- method |= SKYPEWEB_METHOD_SSL;
- }
-
- if (sa && sa->account && !(method & SKYPEWEB_METHOD_SSL))
- {
- proxy_info = purple_proxy_get_setup(sa->account);
- if (purple_proxy_info_get_proxy_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL)
- proxy_info = purple_global_proxy_get_info();
- if (purple_proxy_info_get_proxy_type(proxy_info) == PURPLE_PROXY_HTTP)
- {
- is_proxy = TRUE;
- }
+
+ request = purple_http_request_new(url);
+ switch(method) {
+ case SKYPEWEB_METHOD_POST:
+ purple_http_request_set_method(request, "POST");
+ break;
+ case SKYPEWEB_METHOD_PUT:
+ purple_http_request_set_method(request, "PUT");
+ break;
+ default:
+ break;
}
- if (is_proxy == TRUE)
- {
- real_url = g_strdup_printf("http://%s%s", host, url);
- } else {
- real_url = g_strdup(url);
+ if (keepalive) {
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
}
-
- cookies = skypeweb_cookies_to_string(sa);
- //user_agent = purple_account_get_string(sa->account, "user-agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36");
- if ((method & (SKYPEWEB_METHOD_POST | SKYPEWEB_METHOD_PUT)) && !postdata)
- postdata = "";
-
- /* Build the request */
- request = g_string_new(NULL);
- g_string_append_printf(request, "%s %s HTTP/1.0\r\n",
- ((method & SKYPEWEB_METHOD_POST) ? "POST" : ((method & SKYPEWEB_METHOD_PUT) ? "PUT" : ((method & SKYPEWEB_METHOD_DELETE) ? "DELETE" : "GET"))),
- real_url);
- if (is_proxy == FALSE)
- g_string_append_printf(request, "Host: %s\r\n", host);
- g_string_append_printf(request, "Connection: %s\r\n",
- (keepalive ? "Keep-Alive" : "close"));
- //g_string_append_printf(request, "User-Agent: %s\r\n", user_agent);
if (method & (SKYPEWEB_METHOD_POST | SKYPEWEB_METHOD_PUT)) {
if (postdata && (postdata[0] == '[' || postdata[0] == '{')) {
- g_string_append(request, "Content-Type: application/json\r\n"); // hax
+ purple_http_request_header_set(request, "Content-Type", "application/json"); // hax
} else {
- g_string_append_printf(request, "Content-Type: application/x-www-form-urlencoded\r\n");
+ purple_http_request_header_set(request, "Content-Type", "application/x-www-form-urlencoded");
}
- g_string_append_printf(request, "Content-length: %" G_GSIZE_FORMAT "\r\n", strlen(postdata));
+ purple_http_request_set_contents(request, postdata, -1);
}
if (g_str_equal(host, SKYPEWEB_CONTACTS_HOST) || g_str_equal(host, SKYPEWEB_VIDEOMAIL_HOST) || g_str_equal(host, SKYPEWEB_NEW_CONTACTS_HOST)) {
- g_string_append_printf(request, "X-Skypetoken: %s\r\n", sa->skype_token);
- g_string_append(request, "X-Stratus-Caller: " SKYPEWEB_CLIENTINFO_NAME "\r\n");
- g_string_append(request, "X-Stratus-Request: abcd1234\r\n");
- g_string_append(request, "Origin: https://web.skype.com\r\n");
- g_string_append(request, "Referer: https://web.skype.com/main\r\n");
- g_string_append(request, "Accept: application/json; ver=1.0;\r\n");
+ purple_http_request_header_set(request, "X-Skypetoken", sa->skype_token);
+ purple_http_request_header_set(request, "X-Stratus-Caller", SKYPEWEB_CLIENTINFO_NAME);
+ purple_http_request_header_set(request, "X-Stratus-Request", "abcd1234");
+ purple_http_request_header_set(request, "Origin", "https://web.skype.com");
+ purple_http_request_header_set(request, "Referer", "https://web.skype.com/main");
+ purple_http_request_header_set(request, "Accept", "application/json; ver=1.0;");
} else if (g_str_equal(host, SKYPEWEB_GRAPH_HOST)) {
- g_string_append_printf(request, "X-Skypetoken: %s\r\n", sa->skype_token);
- g_string_append(request, "Accept: application/json\r\n");
+ purple_http_request_header_set(request, "X-Skypetoken", sa->skype_token);
+ purple_http_request_header_set(request, "Accept", "application/json");
} else if (g_str_equal(host, sa->messages_host)) {
- g_string_append_printf(request, "RegistrationToken: %s\r\n", sa->registration_token);
- g_string_append(request, "Referer: https://web.skype.com/main\r\n");
- g_string_append(request, "Accept: application/json; ver=1.0;\r\n");
- g_string_append(request, "ClientInfo: os=Windows; osVer=8.1; proc=Win32; lcid=en-us; deviceType=1; country=n/a; clientName=" SKYPEWEB_CLIENTINFO_NAME "; clientVer=" SKYPEWEB_CLIENTINFO_VERSION "\r\n");
+ purple_http_request_header_set(request, "RegistrationToken", sa->registration_token);
+ purple_http_request_header_set(request, "Referer", "https://web.skype.com/main");
+ purple_http_request_header_set(request, "Accept", "application/json; ver=1.0");
+ purple_http_request_header_set(request, "ClientInfo", "os=Windows; osVer=8.1; proc=Win32; lcid=en-us; deviceType=1; country=n/a; clientName=" SKYPEWEB_CLIENTINFO_NAME "; clientVer=" SKYPEWEB_CLIENTINFO_VERSION);
} else {
- g_string_append_printf(request, "Accept: */*\r\n");
- g_string_append_printf(request, "Cookie: %s\r\n", cookies);
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_set_cookie_jar(request, sa->cookie_jar);
}
- g_string_append_printf(request, "Accept-Encoding: gzip\r\n");
- if (is_proxy == TRUE)
- {
- if (purple_proxy_info_get_username(proxy_info) &&
- purple_proxy_info_get_password(proxy_info))
- {
- proxy_auth = g_strdup_printf("%s:%s", purple_proxy_info_get_username(proxy_info), purple_proxy_info_get_password(proxy_info));
- proxy_auth_base64 = purple_base64_encode((guchar *)proxy_auth, strlen(proxy_auth));
- g_string_append_printf(request, "Proxy-Authorization: Basic %s\r\n", proxy_auth_base64);
- g_free(proxy_auth_base64);
- g_free(proxy_auth);
- }
- }
-
/* Tell the server what language we accept, so that we get error messages in our language (rather than our IP's) */
languages = g_get_language_names();
language_names = g_strjoinv(", ", (gchar **)languages);
purple_util_chrreplace(language_names, '_', '-');
- g_string_append_printf(request, "Accept-Language: %s\r\n", language_names);
+ purple_http_request_header_set(request, "Accept-Language", language_names);
g_free(language_names);
-
- purple_debug_info("skypeweb", "getting url %s\n", url);
-
- g_string_append_printf(request, "\r\n");
- if (method & (SKYPEWEB_METHOD_POST | SKYPEWEB_METHOD_PUT))
- g_string_append_printf(request, "%s", postdata);
-
- /* If it needs to go over a SSL connection, we probably shouldn't print
- * it in the debug log. Without this condition a user's password is
- * printed in the debug log */
- if (method == SKYPEWEB_METHOD_POST || method == SKYPEWEB_METHOD_PUT)
- purple_debug_info("skypeweb", "sending request data:\n%s\n", postdata);
-
- purple_debug_misc("skypeweb", "sending headers:\n%s\n", request->str);
-
- g_free(cookies);
-
- skypewebcon = g_new0(SkypeWebConnection, 1);
- skypewebcon->sa = sa;
- skypewebcon->url = real_url;
- skypewebcon->method = method;
- skypewebcon->hostname = g_strdup(host);
- skypewebcon->request = request;
- skypewebcon->callback = callback_func;
- skypewebcon->user_data = user_data;
- skypewebcon->fd = -1;
- skypewebcon->connection_keepalive = keepalive;
- skypewebcon->request_time = time(NULL);
-
- g_queue_push_head(sa->waiting_conns, skypewebcon);
- skypeweb_next_connection(sa);
-
- return skypewebcon;
-}
-
-static void skypeweb_next_connection(SkypeWebAccount *sa)
-{
- SkypeWebConnection *skypewebcon;
-
- g_return_if_fail(sa != NULL);
-
- if (!g_queue_is_empty(sa->waiting_conns))
- {
- if(g_slist_length(sa->conns) < SKYPEWEB_MAX_CONNECTIONS)
- {
- skypewebcon = g_queue_pop_tail(sa->waiting_conns);
- skypeweb_attempt_connection(skypewebcon);
- }
- }
-}
-
-
-static gboolean
-skypeweb_connection_timedout(gpointer userdata)
-{
- SkypeWebConnection *skypewebcon = userdata;
- SkypeWebAccount *sa = skypewebcon->sa;
- /* Try resend the request */
- skypewebcon->retry_count++;
- if (skypewebcon->retry_count < 3) {
- skypeweb_connection_close(skypewebcon);
- skypewebcon->request_time = time(NULL);
-
- g_queue_push_head(sa->waiting_conns, skypewebcon);
- skypeweb_next_connection(sa);
- } else {
- skypeweb_fatal_connection_cb(skypewebcon);
- }
+ conn = g_new0(SkypeWebConnection, 1);
+ conn->sa = sa;
+ conn->user_data = user_data;
+ conn->url = g_strdup(url);
- return FALSE;
-}
-
-static void skypeweb_attempt_connection(SkypeWebConnection *skypewebcon)
-{
- gboolean is_proxy = FALSE;
- SkypeWebAccount *sa = skypewebcon->sa;
- PurpleProxyInfo *proxy_info = NULL;
-
- if (sa && sa->account && !(skypewebcon->method & SKYPEWEB_METHOD_SSL))
- {
- proxy_info = purple_proxy_get_setup(sa->account);
- if (purple_proxy_info_get_proxy_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL)
- proxy_info = purple_global_proxy_get_info();
- if (purple_proxy_info_get_proxy_type(proxy_info) == PURPLE_PROXY_HTTP)
- {
- is_proxy = TRUE;
- }
- }
-
-#if 0
- /* Connection to attempt retries. This code doesn't work perfectly, but
- * remains here for future reference if needed */
- if (time(NULL) - skypewebcon->request_time > 5) {
- /* We've continuously tried to remake this connection for a
- * bit now. It isn't happening, sadly. Time to die. */
- purple_debug_error("skypeweb", "could not connect after retries\n");
- skypeweb_fatal_connection_cb(skypewebcon);
- return;
- }
-
- purple_debug_info("skypeweb", "making connection attempt\n");
-
- /* TODO: If we're retrying the connection, consider clearing the cached
- * DNS value. This will require some juggling with the hostname param */
- /* TODO/FIXME: This retries almost instantenously, which in some cases
- * runs at blinding speed. Slow it down. */
- /* TODO/FIXME: this doesn't retry properly on non-ssl connections */
-#endif
+ conn->http_conn = purple_http_request(sa->pc, request, skypeweb_post_or_get_cb, conn);
+ purple_http_connection_set_add(sa->conns, conn->http_conn);
- sa->conns = g_slist_prepend(sa->conns, skypewebcon);
-
- /*
- * Do a separate DNS lookup for the given host name and cache it
- * for next time.
- *
- * TODO: It would be better if we did this before we call
- * purple_proxy_connect(), so we could re-use the result.
- * Or even better: Use persistent HTTP connections for servers
- * that we access continually.
- *
- * TODO: This cache of the hostname<-->IP address does not respect
- * the TTL returned by the DNS server. We should expire things
- * from the cache after some amount of time.
- */
- if (!is_proxy && !(skypewebcon->method & SKYPEWEB_METHOD_SSL) && !g_hostname_is_ip_address(skypewebcon->hostname))
- {
- /* Don't do this for proxy connections, since proxies do the DNS lookup */
- gchar *host_ip;
-
- host_ip = g_hash_table_lookup(sa->hostname_ip_cache, skypewebcon->hostname);
- if (host_ip != NULL) {
- g_free(skypewebcon->hostname);
- skypewebcon->hostname = g_strdup(host_ip);
- } else if (sa->account && !purple_account_is_disconnecting(sa->account)) {
- GSList *host_lookup_list = NULL;
- PurpleDnsQueryData *query;
-
- host_lookup_list = g_slist_prepend(
- host_lookup_list, g_strdup(skypewebcon->hostname));
- host_lookup_list = g_slist_prepend(
- host_lookup_list, sa);
-
- query = purple_dnsquery_a(
-#if PURPLE_VERSION_CHECK(3, 0, 0)
- skypewebcon->sa->account,
-#endif
- skypewebcon->hostname, 80,
- skypeweb_host_lookup_cb, host_lookup_list);
- sa->dns_queries = g_slist_prepend(sa->dns_queries, query);
- host_lookup_list = g_slist_append(host_lookup_list, query);
- }
- }
-
- if (skypewebcon->method & SKYPEWEB_METHOD_SSL) {
- skypewebcon->ssl_conn = purple_ssl_connect(sa->account, skypewebcon->hostname,
- 443, skypeweb_post_or_get_ssl_connect_cb,
- skypeweb_ssl_connection_error, skypewebcon);
- } else {
- skypewebcon->connect_data = purple_proxy_connect(NULL, sa->account,
- skypewebcon->hostname, 80, skypeweb_post_or_get_connect_cb, skypewebcon);
- }
+ purple_http_request_unref(request);
- skypewebcon->timeout_watcher = purple_timeout_add_seconds(120, skypeweb_connection_timedout, skypewebcon);
-
- return;
+ return conn;
}
-
diff --git a/skypeweb/skypeweb_connection.h b/skypeweb/skypeweb_connection.h
index f24c4f2..d793d0b 100644
--- a/skypeweb/skypeweb_connection.h
+++ b/skypeweb/skypeweb_connection.h
@@ -39,27 +39,13 @@ typedef enum
typedef struct _SkypeWebConnection SkypeWebConnection;
struct _SkypeWebConnection {
SkypeWebAccount *sa;
- SkypeWebMethod method;
- gchar *hostname;
gchar *url;
- GString *request;
SkypeWebProxyCallbackFunc callback;
gpointer user_data;
- char *rx_buf;
- size_t rx_len;
- PurpleProxyConnectData *connect_data;
- PurpleSslConnection *ssl_conn;
- int fd;
- guint input_watcher;
- gboolean connection_keepalive;
- time_t request_time;
- guint retry_count;
- guint timeout_watcher;
+ PurpleHttpConnection *http_conn;
SkypeWebProxyCallbackErrorFunc error_callback;
};
-void skypeweb_connection_destroy(SkypeWebConnection *skypewebcon);
-void skypeweb_connection_close(SkypeWebConnection *skypewebcon);
SkypeWebConnection *skypeweb_post_or_get(SkypeWebAccount *sa, SkypeWebMethod method,
const gchar *host, const gchar *url, const gchar *postdata,
SkypeWebProxyCallbackFunc callback_func, gpointer user_data,
@@ -68,5 +54,6 @@ SkypeWebConnection *skypeweb_post_or_get(SkypeWebAccount *sa, SkypeWebMethod met
void skypeweb_update_cookies(SkypeWebAccount *sa, const gchar *headers);
gchar *skypeweb_cookies_to_string(SkypeWebAccount *sa);
+//SkypeWebConnection *skypeweb_fetch_url_request();
#endif /* SKYPEWEB_CONNECTION_H */
diff --git a/skypeweb/skypeweb_contacts.c b/skypeweb/skypeweb_contacts.c
index ce8df22..79261eb 100644
--- a/skypeweb/skypeweb_contacts.c
+++ b/skypeweb/skypeweb_contacts.c
@@ -22,17 +22,15 @@
#include "skypeweb_messages.h"
#include "skypeweb_util.h"
-#if !PURPLE_VERSION_CHECK(3, 0, 0)
-# include "ft.h"
-#else
-# include "xfer.h"
-#endif
+#include "http.h"
+#include "xfer.h"
+#include "image-store.h"
// Check that the conversation hasn't been closed
static gboolean
purple_conversation_is_valid(PurpleConversation *conv)
{
- GList *convs = purple_get_conversations();
+ GList *convs = purple_conversations_get_all();
return (g_list_find(convs, conv) != NULL);
}
@@ -51,33 +49,36 @@ typedef struct {
static guint active_icon_downloads = 0;
static void
-skypeweb_get_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_get_icon_cb(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
+ PurpleHttpRequest *request = purple_http_conn_get_request(http_conn);
PurpleBuddy *buddy = user_data;
- SkypeWebBuddy *sbuddy = purple_buddy_get_protocol_data(buddy);
- SkypeWebAccount *sa = sbuddy->sa;
- gchar *url = g_dataset_get_data(url_data, "url");
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ const gchar *url = purple_http_request_get_url(request);
+ const gchar *data;
+ gsize len;
active_icon_downloads--;
- if (!buddy) {
- g_dataset_destroy(url_data);
+ if (!buddy || !purple_http_response_is_successful(response)) {
return;
}
- purple_buddy_icons_set_for_user(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), g_memdup(url_text, len), len, url);
+ data = purple_http_response_get_data(response, &len);
+
+ if (!len || !*data) {
+ return;
+ }
+
+ purple_buddy_icons_set_for_user(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy), g_memdup(data, len), len, url);
- g_dataset_destroy(url_data);
}
static void
skypeweb_get_icon_now(PurpleBuddy *buddy)
{
SkypeWebBuddy *sbuddy;
+ SkypeWebAccount *sa;
gchar *url;
- gpointer url_data;
purple_debug_info("skypeweb", "getting new buddy icon for %s\n", purple_buddy_get_name(buddy));
@@ -87,9 +88,10 @@ skypeweb_get_icon_now(PurpleBuddy *buddy)
} else {
url = g_strdup_printf("https://avatar.skype.com/v1/avatars/%s/public?returnDefaultImage=false", purple_url_encode(purple_buddy_get_name(buddy)));
}
+ sa = sbuddy->sa;
- url_data = skypeweb_fetch_url_request(sbuddy->sa, url, TRUE, NULL, FALSE, NULL, FALSE, 524288, skypeweb_get_icon_cb, buddy);
- g_dataset_set_data_full(url_data, "url", url, g_free);
+ purple_http_get(sa->pc, skypeweb_get_icon_cb, buddy, url);
+ g_free(url);
active_icon_downloads++;
}
@@ -117,131 +119,108 @@ skypeweb_get_icon(PurpleBuddy *buddy)
static void
-skypeweb_got_imagemessage(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_got_imagemessage(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
PurpleConversation *conv = user_data;
- PurpleConnection *pc;
- SkypeWebAccount *sa;
gint icon_id;
gchar *msg_tmp;
- gchar *location;
+ const gchar *url_text;
+ gsize len;
+ PurpleImage *image;
// Conversation could have been closed before we retrieved the image
if (!purple_conversation_is_valid(conv)) {
return;
}
- pc = purple_conversation_get_connection(conv);
- sa = purple_connection_get_protocol_data(pc);
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
-
- location = skypeweb_string_get_chunk(url_text, len, "Location: https://", "/");
- if (location && *location) {
- skypeweb_download_uri_to_conv(sa, location, conv);
- g_free(location);
- return;
- }
+ url_text = purple_http_response_get_data(response, &len);
if (!url_text || !len || url_text[0] == '{' || url_text[0] == '<')
return;
- if (error_message && *error_message)
+ if (!purple_http_response_is_successful(response))
return;
- icon_id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
-
+ image = purple_image_new_from_data(g_memdup(url_text, len), len);
+ icon_id = purple_image_store_add(image);
msg_tmp = g_strdup_printf("<img id='%d'>", icon_id);
- purple_conversation_write(conv, conv->name, msg_tmp, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_IMAGES, time(NULL));
+ purple_conversation_write_system_message(conv, msg_tmp, PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_IMAGES);
g_free(msg_tmp);
-
- purple_imgstore_unref_by_id(icon_id);
}
void
skypeweb_download_uri_to_conv(SkypeWebAccount *sa, const gchar *uri, PurpleConversation *conv)
{
- gchar *headers, *url, *text;
- gpointer requestdata;
- PurpleHttpURL *httpurl;
+ gchar *url, *text;
+ PurpleHttpRequest *request;
- httpurl = purple_http_url_parse(uri);
- headers = g_strdup_printf("GET /%s HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: image/*\r\n"
- "Cookie: skypetoken_asm=%s\r\n"
- "Host: %s\r\n"
- "\r\n",
- purple_http_url_get_path(httpurl), sa->skype_token,
- purple_http_url_get_host(httpurl));
-
- requestdata = skypeweb_fetch_url_request(sa, uri, TRUE, NULL, FALSE, headers, FALSE, -1, skypeweb_got_imagemessage, conv);
-
- skypeweb_url_prevent_follow_redirects(requestdata);
-
- g_free(headers);
- purple_http_url_free(httpurl);
+ request = purple_http_request_new(uri);
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_header_set_printf(request, "Cookie", "skypetoken_asm=%s", sa->skype_token);
+ purple_http_request_header_set(request, "Accept", "image/*");
+ purple_http_request(sa->pc, request, skypeweb_got_imagemessage, conv);
+ purple_http_request_unref(request);
url = purple_strreplace(uri, "imgt1", "imgpsh_fullsize");
text = g_strdup_printf("<a href=\"%s\">Click here to view full version</a>", url);
- purple_conversation_write(conv, conv->name, text, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ purple_conversation_write_system_message(conv, text, PURPLE_MESSAGE_SYSTEM);
}
void
skypeweb_download_moji_to_conv(SkypeWebAccount *sa, const gchar *text, const gchar *url_thumbnail, PurpleConversation *conv)
{
- gchar *headers, *cdn_url_thumbnail;
- gpointer requestdata;
+ gchar *cdn_url_thumbnail;
PurpleHttpURL *httpurl;
+ PurpleHttpRequest *request;
httpurl = purple_http_url_parse(url_thumbnail);
cdn_url_thumbnail = g_strdup_printf("https://%s/%s", SKYPEWEB_STATIC_CDN_HOST, purple_http_url_get_path(httpurl));
-
- headers = g_strdup_printf("GET /%s HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: image/*\r\n"
- "Cookie: vdms-skype-token=%s\r\n"
- "Host: %s\r\n"
- "\r\n",
- purple_http_url_get_path(httpurl), sa->vdms_token,
- SKYPEWEB_STATIC_CDN_HOST);
-
- requestdata = skypeweb_fetch_url_request(sa, cdn_url_thumbnail, TRUE, NULL, FALSE, headers, FALSE, -1, skypeweb_got_imagemessage, conv);
-
- skypeweb_url_prevent_follow_redirects(requestdata);
- purple_conversation_write(conv, conv->name, text, PURPLE_MESSAGE_SYSTEM, time(NULL));
+ request = purple_http_request_new(cdn_url_thumbnail);
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_header_set_printf(request, "Cookie", "vdms-skype-token=%s", sa->vdms_token);
+ purple_http_request_header_set(request, "Accept", "image/*");
+ purple_http_request(sa->pc, request, skypeweb_got_imagemessage, conv);
+ purple_http_request_unref(request);
+
+ purple_conversation_write_system_message(conv, text, PURPLE_MESSAGE_SYSTEM);
- g_free(headers);
g_free(cdn_url_thumbnail);
purple_http_url_free(httpurl);
}
static void
-skypeweb_got_vm_file(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_got_vm_file(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
PurpleXfer *xfer = user_data;
- SkypeWebAccount *sa = purple_connection_get_protocol_data(purple_account_get_connection(xfer->account));
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ const gchar *data;
+ gsize len;
- purple_xfer_write(xfer, (guchar *)url_text, len);
+ data = purple_http_response_get_data(response, &len);
+ purple_xfer_write(xfer, (guchar *)data, len);
}
static void
skypeweb_init_vm_download(PurpleXfer *xfer)
{
SkypeWebAccount *sa;
- JsonObject *file = xfer->data;
+ JsonObject *file = purple_xfer_get_protocol_data(xfer);
gint64 fileSize;
const gchar *url;
+ PurpleHttpRequest *request;
fileSize = json_object_get_int_member(file, "fileSize");
url = json_object_get_string_member(file, "url");
purple_xfer_set_completed(xfer, FALSE);
- sa = purple_connection_get_protocol_data(purple_account_get_connection(xfer->account));
- skypeweb_fetch_url_request(sa, url, TRUE, NULL, FALSE, NULL, FALSE, fileSize, skypeweb_got_vm_file, xfer);
+ sa = purple_connection_get_protocol_data(purple_account_get_connection(purple_xfer_get_account(xfer)));
+
+ request = purple_http_request_new(url);
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_set_max_len(request, fileSize);
+ purple_http_request(sa->pc, request, skypeweb_got_vm_file, xfer);
+ purple_http_request_unref(request);
json_object_unref(file);
}
@@ -249,7 +228,7 @@ skypeweb_init_vm_download(PurpleXfer *xfer)
static void
skypeweb_cancel_vm_download(PurpleXfer *xfer)
{
- JsonObject *file = xfer->data;
+ JsonObject *file = purple_xfer_get_protocol_data(xfer);
json_object_unref(file);
}
@@ -280,7 +259,7 @@ skypeweb_got_vm_download_info(SkypeWebAccount *sa, JsonNode *node, gpointer user
filename = g_strconcat(assetId, ".mp4", NULL);
- xfer = purple_xfer_new(sa->account, PURPLE_XFER_RECEIVE, conv->name);
+ xfer = purple_xfer_new(sa->account, PURPLE_XFER_TYPE_RECEIVE, purple_conversation_get_name(conv));
purple_xfer_set_size(xfer, fileSize);
purple_xfer_set_filename(xfer, filename);
json_object_ref(file);
@@ -357,19 +336,21 @@ skypeweb_free_xfer(PurpleXfer *xfer)
}
static void
-skypeweb_got_file(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_got_file(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
SkypeWebFileTransfer *swft = user_data;
PurpleXfer *xfer = swft->xfer;
SkypeWebAccount *sa = swft->sa;
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ const gchar *data;
+ gsize len;
+
- if (error_message) {
- purple_xfer_error(purple_xfer_get_type(xfer), sa->account, swft->from, error_message);
+ if (!purple_http_response_is_successful(response)) {
+ purple_xfer_error(purple_xfer_get_xfer_type(xfer), sa->account, swft->from, purple_http_response_get_error(response));
purple_xfer_cancel_local(xfer);
} else {
- purple_xfer_write_file(xfer, (guchar *)url_text, len);
+ data = purple_http_response_get_data(response, &len);
+ purple_xfer_write_file(xfer, (guchar *)data, len);
purple_xfer_set_completed(xfer, TRUE);
}
@@ -381,37 +362,31 @@ skypeweb_got_file(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gc
static void
skypeweb_init_file_download(PurpleXfer *xfer)
{
+ SkypeWebAccount *sa;
SkypeWebFileTransfer *swft;
const gchar *view_location;
gint64 content_full_length;
- PurpleHttpURL *httpurl;
- gchar *headers;
+ PurpleHttpRequest *request;
swft = purple_xfer_get_protocol_data(xfer);
+ sa = swft->sa;
view_location = json_object_get_string_member(swft->info, "view_location");
content_full_length = json_object_get_int_member(swft->info, "content_full_length");
purple_xfer_start(xfer, -1, NULL, 0);
- httpurl = purple_http_url_parse(view_location);
- headers = g_strdup_printf("GET /%s HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Cookie: skypetoken_asm=%s\r\n"
- "Host: %s\r\n"
- "\r\n",
- purple_http_url_get_path(httpurl),
- swft->sa->skype_token,
- purple_http_url_get_host(httpurl));
-
- skypeweb_fetch_url_request(swft->sa, view_location, TRUE, NULL, FALSE, headers, FALSE, content_full_length, skypeweb_got_file, swft);
-
- g_free(headers);
- purple_http_url_free(httpurl);
+ request = purple_http_request_new(view_location);
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_header_set_printf(request, "Cookie", "skypetoken_asm=%s", sa->skype_token);
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_set_max_len(request, content_full_length);
+ purple_http_request(sa->pc, request, skypeweb_got_file, swft);
+ purple_http_request_unref(request);
}
static void
-skypeweb_got_file_info(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_got_file_info(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
JsonObject *obj;
PurpleXfer *xfer;
@@ -419,11 +394,13 @@ skypeweb_got_file_info(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
SkypeWebAccount *sa = swft->sa;
JsonParser *parser;
JsonNode *node;
+ const gchar *data;
+ gsize len;
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ data = purple_http_response_get_data(response, &len);
parser = json_parser_new();
- if (!json_parser_load_from_data(parser, url_text, len, NULL)) {
+ if (!json_parser_load_from_data(parser, data, len, NULL)) {
g_free(swft->url);
g_free(swft->from);
g_free(swft);
@@ -455,7 +432,7 @@ skypeweb_got_file_info(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
},
"original_filename": "filename"
} */
- purple_debug_info("skypeweb", "File info: %s\n", url_text);
+ purple_debug_info("skypeweb", "File info: %s\n", data);
if (!json_object_has_member(obj, "content_state") || !g_str_equal(json_object_get_string_member(obj, "content_state"), "ready")) {
skypeweb_present_uri_as_filetransfer(sa, json_object_get_string_member(obj, "status_location"), swft->from);
@@ -469,7 +446,7 @@ skypeweb_got_file_info(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
json_object_ref(obj);
swft->info = obj;
- xfer = purple_xfer_new(sa->account, PURPLE_XFER_RECEIVE, swft->from);
+ xfer = purple_xfer_new(sa->account, PURPLE_XFER_TYPE_RECEIVE, swft->from);
purple_xfer_set_size(xfer, json_object_get_int_member(obj, "content_full_length"));
purple_xfer_set_filename(xfer, json_object_get_string_member(obj, "original_filename"));
purple_xfer_set_init_fnc(xfer, skypeweb_init_file_download);
@@ -486,41 +463,23 @@ skypeweb_got_file_info(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
void
skypeweb_present_uri_as_filetransfer(SkypeWebAccount *sa, const gchar *uri, const gchar *from)
{
- PurpleHttpURL *httpurl;
- const gchar *host;
- gchar *path;
SkypeWebFileTransfer *swft;
- gchar *headers;
-
-
- httpurl = purple_http_url_parse(uri);
- host = purple_http_url_get_host(httpurl);
-
- if (g_str_has_suffix(uri, "/views/original/status")) {
- path = g_strconcat(purple_http_url_get_path(httpurl), NULL);
- } else {
- path = g_strconcat(purple_http_url_get_path(httpurl), "/views/original/status", NULL);
- }
-
- headers = g_strdup_printf("GET /%s HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Cookie: skypetoken_asm=%s\r\n"
- "Host: %s\r\n"
- "\r\n",
- path,
- sa->skype_token,
- host);
+ PurpleHttpRequest *request;
swft = g_new0(SkypeWebFileTransfer, 1);
swft->sa = sa;
swft->url = g_strdup(uri);
swft->from = g_strdup(from);
- skypeweb_fetch_url_request(sa, uri, TRUE, NULL, FALSE, headers, FALSE, -1, skypeweb_got_file_info, swft);
-
- g_free(path);
- g_free(headers);
- purple_http_url_free(httpurl);
+ request = purple_http_request_new(uri);
+ if (!g_str_has_suffix(uri, "/views/original/status")) {
+ purple_http_request_set_url_printf(request, "%s%s", uri, "/views/original/status");
+ }
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_header_set_printf(request, "Cookie", "skypetoken_asm=%s", sa->skype_token);
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request(sa->pc, request, skypeweb_got_file_info, swft);
+ purple_http_request_unref(request);
}
static gssize
@@ -558,7 +517,7 @@ skypeweb_xfer_send_connect_cb(gpointer user_data, PurpleSslConnection *ssl_conne
"\r\n",
purple_url_encode(swft->id),
sa->skype_token,
- purple_xfer_get_size(xfer));
+ (gsize) purple_xfer_get_size(xfer));
purple_ssl_write(ssl_connection, headers, strlen(headers));
@@ -568,7 +527,7 @@ skypeweb_xfer_send_connect_cb(gpointer user_data, PurpleSslConnection *ssl_conne
purple_xfer_ref(xfer);
purple_xfer_start(xfer, ssl_connection->fd, NULL, 0);
- purple_xfer_prpl_ready(xfer);
+ purple_xfer_protocol_ready(xfer);
g_free(headers);
@@ -577,7 +536,7 @@ skypeweb_xfer_send_connect_cb(gpointer user_data, PurpleSslConnection *ssl_conne
static gboolean poll_file_send_progress(gpointer user_data);
static void
-got_file_send_progress(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+got_file_send_progress(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
SkypeWebFileTransfer *swft = user_data;
PurpleXfer *xfer = swft->xfer;
@@ -585,12 +544,14 @@ got_file_send_progress(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
JsonParser *parser;
JsonNode *node;
JsonObject *obj;
+ const gchar *data;
+ gsize len;
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ data = purple_http_response_get_data(response, &len);
//{"content_length":0,"content_full_length":0,"view_length":0,"content_state":"no content","view_state":"none","view_location":"https://nus1-api.asm.skype.com/v1/objects/0-cus-d1-61121cfae8cf601944627a66afdb77ad/views/original","status_location":"https://nus1-api.asm.skype.com/v1/objects/0-cus-d1-61121cfae8cf601944627a66afdb77ad/views/original/status"}
parser = json_parser_new();
- if (!json_parser_load_from_data(parser, url_text, len, NULL)) {
+ if (!json_parser_load_from_data(parser, data, len, NULL)) {
//probably bad
poll_file_send_progress(swft);
return;
@@ -610,12 +571,12 @@ got_file_send_progress(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
}
if (json_object_has_member(obj, "content_state") && g_str_equal(json_object_get_string_member(obj, "content_state"), "ready")) {
- xmlnode *uriobject = xmlnode_new("URIObject");
- xmlnode *title = xmlnode_new_child(uriobject, "Title");
- xmlnode *description = xmlnode_new_child(uriobject, "Description");
- xmlnode *anchor = xmlnode_new_child(uriobject, "a");
- xmlnode *originalname = xmlnode_new_child(uriobject, "OriginalName");
- xmlnode *filesize = xmlnode_new_child(uriobject, "FileSize");
+ PurpleXmlNode *uriobject = purple_xmlnode_new("URIObject");
+ PurpleXmlNode *title = purple_xmlnode_new_child(uriobject, "Title");
+ PurpleXmlNode *description = purple_xmlnode_new_child(uriobject, "Description");
+ PurpleXmlNode *anchor = purple_xmlnode_new_child(uriobject, "a");
+ PurpleXmlNode *originalname = purple_xmlnode_new_child(uriobject, "OriginalName");
+ PurpleXmlNode *filesize = purple_xmlnode_new_child(uriobject, "FileSize");
gchar *message, *temp;
//We finally did it!
// May the pesants rejoyce
@@ -623,32 +584,38 @@ got_file_send_progress(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
// Don't forget to let the other end know about it
- xmlnode_set_attrib(uriobject, "type", "File.1");
+ purple_xmlnode_set_attrib(uriobject, "type", "File.1");
temp = g_strconcat("https://" SKYPEWEB_XFER_HOST "/v1/objects/", purple_url_encode(swft->id), NULL);
- xmlnode_set_attrib(uriobject, "uri", temp);
+ purple_xmlnode_set_attrib(uriobject, "uri", temp);
g_free(temp);
temp = g_strconcat("https://" SKYPEWEB_XFER_HOST "/v1/objects/", purple_url_encode(swft->id), "/views/thumbnail", NULL);
- xmlnode_set_attrib(uriobject, "url_thumbnail", temp);
+ purple_xmlnode_set_attrib(uriobject, "url_thumbnail", temp);
g_free(temp);
- xmlnode_insert_data(title, purple_xfer_get_filename(xfer), -1);
- xmlnode_insert_data(description, "Description: ", -1);
+ purple_xmlnode_insert_data(title, purple_xfer_get_filename(xfer), -1);
+ purple_xmlnode_insert_data(description, "Description: ", -1);
temp = g_strconcat("https://login.skype.com/login/sso?go=webclient.xmm&docid=", purple_url_encode(swft->id), NULL);
- xmlnode_set_attrib(anchor, "href", temp);
- xmlnode_insert_data(anchor, temp, -1);
+ purple_xmlnode_set_attrib(anchor, "href", temp);
+ purple_xmlnode_insert_data(anchor, temp, -1);
g_free(temp);
- xmlnode_set_attrib(originalname, "v", purple_xfer_get_filename(xfer));
- temp = g_strdup_printf("%" G_GSIZE_FORMAT, purple_xfer_get_size(xfer));
- xmlnode_set_attrib(filesize, "v", temp);
+ purple_xmlnode_set_attrib(originalname, "v", purple_xfer_get_filename(xfer));
+ temp = g_strdup_printf("%" G_GSIZE_FORMAT, (gsize) purple_xfer_get_size(xfer));
+ purple_xmlnode_set_attrib(filesize, "v", temp);
g_free(temp);
- message = xmlnode_to_str(uriobject, NULL);
+ message = purple_xmlnode_to_str(uriobject, NULL);
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ PurpleMessage *msg = purple_message_new_outgoing(swft->from, message, PURPLE_MESSAGE_SEND);
+ skypeweb_send_im(sa->pc, msg);
+ purple_message_destroy(msg);
+#else
skypeweb_send_im(sa->pc, swft->from, message, PURPLE_MESSAGE_SEND);
+#endif
g_free(message);
skypeweb_free_xfer(xfer);
purple_xfer_unref(xfer);
- xmlnode_free(uriobject);
+ purple_xmlnode_free(uriobject);
g_object_unref(parser);
return;
}
@@ -665,27 +632,14 @@ poll_file_send_progress(gpointer user_data)
{
SkypeWebFileTransfer *swft = user_data;
SkypeWebAccount *sa = swft->sa;
- PurpleHttpURL *httpurl;
- const gchar *host, *path;
- gchar *headers;
-
- httpurl = purple_http_url_parse(swft->url);
- host = purple_http_url_get_host(httpurl);
- path = purple_http_url_get_path(httpurl);
-
- headers = g_strdup_printf("GET /%s HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Cookie: skypetoken_asm=%s\r\n"
- "Host: %s\r\n"
- "\r\n",
- path,
- sa->skype_token,
- host);
+ PurpleHttpRequest *request;
- skypeweb_fetch_url_request(sa, swft->url, TRUE, NULL, FALSE, headers, FALSE, -1, got_file_send_progress, swft);
-
- g_free(headers);
- purple_http_url_free(httpurl);
+ request = purple_http_request_new(swft->url);
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_header_set_printf(request, "Cookie", "skypetoken_asm=%s", sa->skype_token);
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request(sa->pc, request, got_file_send_progress, swft);
+ purple_http_request_unref(request);
return FALSE;
}
@@ -708,7 +662,7 @@ skypeweb_xfer_send_end(PurpleXfer *xfer)
}
static void
-skypeweb_got_object_for_file(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_got_object_for_file(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
SkypeWebFileTransfer *swft = user_data;
SkypeWebAccount *sa = swft->sa;
@@ -716,12 +670,14 @@ skypeweb_got_object_for_file(PurpleUtilFetchUrlData *url_data, gpointer user_dat
JsonParser *parser;
JsonNode *node;
JsonObject *obj;
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ const gchar *data;
+ gsize len;
+
+ data = purple_http_response_get_data(response, &len);
//Get back {"id": "0-cus-d3-deadbeefdeadbeef012345678"}
parser = json_parser_new();
- if (!json_parser_load_from_data(parser, url_text, len, NULL)) {
+ if (!json_parser_load_from_data(parser, data, len, NULL)) {
g_free(swft->from);
g_free(swft);
g_object_unref(parser);
@@ -754,6 +710,7 @@ skypeweb_got_object_for_file(PurpleUtilFetchUrlData *url_data, gpointer user_dat
//can't use fetch_url_request because it doesn't handle binary data
//TODO make an error handler callback func
+ //TODO switch to purple_http_request_set_contents_reader()
purple_ssl_connect(sa->account, SKYPEWEB_XFER_HOST, 443, skypeweb_xfer_send_connect_cb, NULL, swft);
}
@@ -764,11 +721,12 @@ skypeweb_xfer_send_init(PurpleXfer *xfer)
PurpleConnection *pc = purple_account_get_connection(purple_xfer_get_account(xfer));
SkypeWebAccount *sa = purple_connection_get_protocol_data(pc);
gchar *basename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
- gchar *id, *post, *headers;
+ gchar *id, *post;
SkypeWebFileTransfer *swft = purple_xfer_get_protocol_data(xfer);
JsonObject *obj = json_object_new();
JsonObject *permissions = json_object_new();
JsonArray *userpermissions = json_array_new();
+ PurpleHttpRequest *request;
purple_xfer_set_filename(xfer, basename);
purple_xfer_ref(xfer);
@@ -785,17 +743,14 @@ skypeweb_xfer_send_init(PurpleXfer *xfer)
//POST to api.asm.skype.com /v1/objects
//{"type":"sharing/file","permissions":{"8:eionrobb":["read"]},"filename":"GiantLobsterMoose.txt"}
- headers = g_strdup_printf("POST /v1/objects HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Authorization: skype_token %s\r\n" //slightly different to normal!
- "Host: " SKYPEWEB_XFER_HOST "\r\n"
- "Content-Length: %" G_GSIZE_FORMAT "\r\n"
- "Content-Type: application/json\r\n"
- "\r\n%s",
- sa->skype_token,
- strlen(post), post);
-
- skypeweb_fetch_url_request(sa, "https://" SKYPEWEB_XFER_HOST, TRUE, NULL, FALSE, headers, FALSE, -1, skypeweb_got_object_for_file, swft);
+ request = purple_http_request_new("https://" SKYPEWEB_XFER_HOST "/v1/objects");
+ purple_http_request_set_method(request, "POST");
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_header_set_printf(request, "Authorization", "skype_token %s", sa->skype_token); //slightly different to normal!
+ purple_http_request_header_set(request, "Content-Type", "application/json");
+ purple_http_request_set_contents(request, post, -1);
+ purple_http_request(sa->pc, request, skypeweb_got_object_for_file, swft);
+ purple_http_request_unref(request);
g_free(post);
json_object_unref(obj);
@@ -810,7 +765,7 @@ skypeweb_new_xfer(PurpleConnection *pc, const char *who)
PurpleXfer *xfer;
SkypeWebFileTransfer *swft;
- xfer = purple_xfer_new(sa->account, PURPLE_XFER_SEND, who);
+ xfer = purple_xfer_new(sa->account, PURPLE_XFER_TYPE_SEND, who);
swft = g_new0(SkypeWebFileTransfer, 1);
swft->sa = sa;
@@ -965,7 +920,7 @@ skypeweb_search_users_text_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user
if (length == 0)
{
gchar *primary_text = g_strdup_printf("Your search for the user \"%s\" returned no results", search_term);
- purple_notify_warning(sa->pc, "No users found", primary_text, "");
+ purple_notify_warning(sa->pc, "No users found", primary_text, "", purple_request_cpar_from_connection(sa->pc));
g_free(primary_text);
g_free(search_term);
return;
@@ -1035,9 +990,9 @@ skypeweb_search_users_text(gpointer user_data, const gchar *text)
}
void
-skypeweb_search_users(PurplePluginAction *action)
+skypeweb_search_users(PurpleProtocolAction *action)
{
- PurpleConnection *pc = (PurpleConnection *) action->context;
+ PurpleConnection *pc = purple_protocol_action_get_connection(action);
SkypeWebAccount *sa = purple_connection_get_protocol_data(pc);
purple_request_input(pc, "Search for Skype Friends",
@@ -1046,7 +1001,7 @@ skypeweb_search_users(PurplePluginAction *action)
NULL, FALSE, FALSE, NULL,
_("_Search"), G_CALLBACK(skypeweb_search_users_text),
_("_Cancel"), NULL,
- purple_connection_get_account(pc), NULL, NULL,
+ purple_request_cpar_from_connection(pc),
sa);
}
@@ -1074,7 +1029,7 @@ skypeweb_got_friend_profiles(SkypeWebAccount *sa, JsonNode *node, gpointer user_
const gchar *username = json_object_get_string_member(contact, "username");
const gchar *new_avatar;
- buddy = purple_find_buddy(sa->account, username);
+ buddy = purple_blist_find_buddy(sa->account, username);
if (!buddy)
continue;
sbuddy = purple_buddy_get_protocol_data(buddy);
@@ -1090,11 +1045,11 @@ skypeweb_got_friend_profiles(SkypeWebAccount *sa, JsonNode *node, gpointer user_
if (json_object_has_member(contact, "lastname")) {
gchar *fullname = g_strconcat(json_object_get_string_member(contact, "firstname"), " ", json_object_get_string_member(contact, "lastname"), NULL);
- purple_blist_server_alias_buddy(buddy, fullname);
+ purple_buddy_set_server_alias(buddy, fullname);
g_free(fullname);
} else {
- purple_blist_server_alias_buddy(buddy, json_object_get_string_member(contact, "firstname"));
+ purple_buddy_set_server_alias(buddy, json_object_get_string_member(contact, "firstname"));
}
new_avatar = json_object_get_string_member(contact, "avatarUrl");
@@ -1192,7 +1147,7 @@ skypeweb_got_info(SkypeWebAccount *sa, JsonNode *node, gpointer user_data)
//_SKYPE_USER_INFO("richMood", "Mood");
//_SKYPE_USER_INFO("avatarUrl", "Avatar");
- buddy = purple_find_buddy(sa->account, username);
+ buddy = purple_blist_find_buddy(sa->account, username);
if (buddy) {
sbuddy = purple_buddy_get_protocol_data(buddy);
if (sbuddy == NULL) {
@@ -1287,7 +1242,7 @@ skypeweb_get_friend_list_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user_d
id = skypeweb_strip_user_prefix(mri);
- buddy = purple_find_buddy(sa->account, id);
+ buddy = purple_blist_find_buddy(sa->account, id);
if (!buddy)
{
if (!group)
@@ -1322,11 +1277,11 @@ skypeweb_get_friend_list_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user_d
sbuddy->buddy = buddy;
purple_buddy_set_protocol_data(buddy, sbuddy);
- if (!purple_strequal(purple_buddy_get_local_buddy_alias(buddy), sbuddy->display_name)) {
+ if (!purple_strequal(purple_buddy_get_local_alias(buddy), sbuddy->display_name)) {
purple_serv_got_alias(sa->pc, id, sbuddy->display_name);
}
if (!purple_strequal(purple_buddy_get_server_alias(buddy), sbuddy->fullname)) {
- purple_blist_server_alias_buddy(buddy, sbuddy->fullname);
+ purple_buddy_set_server_alias(buddy, sbuddy->fullname);
}
if (json_object_has_member(profile, "avatar_url")) {
@@ -1339,7 +1294,7 @@ skypeweb_get_friend_list_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user_d
}
if (blocked == TRUE) {
- purple_privacy_deny_add(sa->account, id, TRUE);
+ purple_account_privacy_deny_add(sa->account, id, TRUE);
} else {
users_to_fetch = g_slist_prepend(users_to_fetch, sbuddy->skypename);
}
@@ -1364,7 +1319,11 @@ skypeweb_get_friend_list(SkypeWebAccount *sa)
void
-skypeweb_auth_accept_cb(gpointer sender)
+skypeweb_auth_accept_cb(
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ const gchar *who,
+#endif
+ gpointer sender)
{
PurpleBuddy *buddy = sender;
SkypeWebAccount *sa;
@@ -1387,7 +1346,11 @@ skypeweb_auth_accept_cb(gpointer sender)
}
void
-skypeweb_auth_reject_cb(gpointer sender)
+skypeweb_auth_reject_cb(
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ const gchar *who,
+#endif
+ gpointer sender)
{
PurpleBuddy *buddy = sender;
SkypeWebAccount *sa;
diff --git a/skypeweb/skypeweb_contacts.h b/skypeweb/skypeweb_contacts.h
index 505a908..1f2eab1 100644
--- a/skypeweb/skypeweb_contacts.h
+++ b/skypeweb/skypeweb_contacts.h
@@ -31,7 +31,7 @@ PurpleXfer *skypeweb_new_xfer(PurpleConnection *pc, const char *who);
void skypeweb_send_file(PurpleConnection *pc, const gchar *who, const gchar *filename);
gboolean skypeweb_can_receive_file(PurpleConnection *pc, const gchar *who);
-void skypeweb_search_users(PurplePluginAction *action);
+void skypeweb_search_users(PurpleProtocolAction *action);
void skypeweb_received_contacts(SkypeWebAccount *sa, PurpleXmlNode *contacts);
diff --git a/skypeweb/skypeweb_login.c b/skypeweb/skypeweb_login.c
index ff39567..c7e0414 100644
--- a/skypeweb/skypeweb_login.c
+++ b/skypeweb/skypeweb_login.c
@@ -21,15 +21,17 @@
static void
-skypeweb_login_did_auth(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_login_did_auth(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
gchar *refresh_token = NULL;
SkypeWebAccount *sa = user_data;
+ const gchar *data;
+ gsize len;
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ data = purple_http_response_get_data(response, &len);
- if (url_text != NULL) {
- refresh_token = skypeweb_string_get_chunk(url_text, len, "=\"skypetoken\" value=\"", "\"");
+ if (data != NULL) {
+ refresh_token = skypeweb_string_get_chunk(data, len, "=\"skypetoken\" value=\"", "\"");
} else {
purple_connection_error(sa->pc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -38,13 +40,13 @@ skypeweb_login_did_auth(PurpleUtilFetchUrlData *url_data, gpointer user_data, co
if (refresh_token == NULL) {
purple_account_set_string(sa->account, "refresh-token", NULL);
- if (g_strstr_len(url_text, len, "recaptcha_response_field")) {
+ if (g_strstr_len(data, len, "recaptcha_response_field")) {
purple_connection_error(sa->pc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("Captcha required.\nTry logging into web.skype.com and try again."));
return;
} else {
- purple_debug_info("skypeweb", "login response was %s\r\n", url_text);
+ purple_debug_info("skypeweb", "login response was %s\r\n", data);
purple_connection_error(sa->pc,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
_("Failed getting Skype Token"));
@@ -54,36 +56,37 @@ skypeweb_login_did_auth(PurpleUtilFetchUrlData *url_data, gpointer user_data, co
sa->skype_token = refresh_token;
- skypeweb_update_cookies(sa, url_text);
if (purple_account_get_remember_password(sa->account)) {
- purple_account_set_string(sa->account, "refresh-token", g_hash_table_lookup(sa->cookie_table, "refresh-token"));
+ purple_account_set_string(sa->account, "refresh-token", purple_http_cookie_jar_get(sa->cookie_jar, "refresh-token"));
}
skypeweb_do_all_the_things(sa);
}
static void
-skypeweb_login_got_pie(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_login_got_pie(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
SkypeWebAccount *sa = user_data;
PurpleAccount *account = sa->account;
gchar *pie;
gchar *etm;
- const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST;// "/login?client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com";
+ const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST "/login?client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com";
GString *postdata;
- gchar *request;
struct timeval tv;
struct timezone tz;
gint tzhours, tzminutes;
int tmplen;
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
-
- if (error_message && *error_message) {
- purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
+ PurpleHttpRequest *request;
+ const gchar *data;
+ gsize len;
+
+ if (!purple_http_response_is_successful(response)) {
+ purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, purple_http_response_get_error(response));
return;
}
+ data = purple_http_response_get_data(response, &len);
+
gettimeofday(&tv, &tz);
(void) tv;
tzminutes = tz.tz_minuteswest;
@@ -91,13 +94,13 @@ skypeweb_login_got_pie(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
tzhours = tzminutes / 60;
tzminutes -= tzhours * 60;
- pie = skypeweb_string_get_chunk(url_text, len, "=\"pie\" value=\"", "\"");
+ pie = skypeweb_string_get_chunk(data, len, "=\"pie\" value=\"", "\"");
if (!pie) {
purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Failed getting PIE value"));
return;
}
- etm = skypeweb_string_get_chunk(url_text, len, "=\"etm\" value=\"", "\"");
+ etm = skypeweb_string_get_chunk(data, len, "=\"etm\" value=\"", "\"");
if (!etm) {
purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Failed getting ETM value"));
return;
@@ -106,7 +109,7 @@ skypeweb_login_got_pie(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
postdata = g_string_new("");
g_string_append_printf(postdata, "username=%s&", purple_url_encode(purple_account_get_username(account)));
- g_string_append_printf(postdata, "password=%s&", purple_url_encode(purple_account_get_password(account)));
+ g_string_append_printf(postdata, "password=%s&", purple_url_encode(purple_connection_get_password(sa->pc)));
g_string_append_printf(postdata, "timezone_field=%c|%d|%d&", (tz.tz_minuteswest < 0 ? '+' : '-'), tzhours, tzminutes);
g_string_append_printf(postdata, "pie=%s&", purple_url_encode(pie));
g_string_append_printf(postdata, "etm=%s&", purple_url_encode(etm));
@@ -116,21 +119,19 @@ skypeweb_login_got_pie(PurpleUtilFetchUrlData *url_data, gpointer user_data, con
tmplen = postdata->len;
if (postdata->len > INT_MAX) tmplen = INT_MAX;
-
- request = g_strdup_printf("POST /login?client_id=578134&redirect_uri=https%%3A%%2F%%2Fweb.skype.com HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n"
- "BehaviorOverride: redirectAs404\r\n"
- "Host: " SKYPEWEB_LOGIN_HOST "\r\n"
- "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"
- "Content-Length: %d\r\n\r\n%.*s",
- tmplen, tmplen, postdata->str);
-
- skypeweb_fetch_url_request(sa, login_url, TRUE, NULL, FALSE, request, TRUE, 524288, skypeweb_login_did_auth, sa);
-
- g_string_free(postdata, TRUE);
- g_free(request);
+ request = purple_http_request_new(login_url);
+ purple_http_request_set_method(request, "POST");
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_set_cookie_jar(request, sa->cookie_jar);
+ purple_http_request_header_set(request, "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_header_set(request, "BehaviorOverride", "redirectAs404");
+ purple_http_request_set_contents(request, postdata->str, tmplen);
+ purple_http_request(sa->pc, request, skypeweb_login_did_auth, sa);
+ purple_http_request_unref(request);
+
+ g_string_free(postdata, TRUE);
g_free(pie);
g_free(etm);
@@ -142,41 +143,43 @@ skypeweb_begin_web_login(SkypeWebAccount *sa)
{
const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST "/login?method=skype&client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com";
- skypeweb_fetch_url_request(sa, login_url, TRUE, NULL, FALSE, NULL, FALSE, 524288, skypeweb_login_got_pie, sa);
+ purple_http_get(sa->pc, skypeweb_login_got_pie, sa, login_url);
purple_connection_set_state(sa->pc, PURPLE_CONNECTION_CONNECTING);
purple_connection_update_progress(sa->pc, _("Connecting"), 1, 4);
}
static void
-skypeweb_login_got_t(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_login_got_t(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
SkypeWebAccount *sa = user_data;
- const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST;// "/login/oauth?client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com";
- gchar *request;
+ const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST "/login/oauth?client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com";
+ PurpleHttpRequest *request;
GString *postdata;
gchar *magic_t_value; // T is for tasty
int tmplen;
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ const gchar *data;
+ gsize len;
+
+ data = purple_http_response_get_data(response, &len);
// <input type="hidden" name="t" id="t" value="...">
- magic_t_value = skypeweb_string_get_chunk(url_text, len, "=\"t\" value=\"", "\"");
+ magic_t_value = skypeweb_string_get_chunk(data, len, "=\"t\" value=\"", "\"");
if (!magic_t_value) {
//No Magic T???? Maybe it be the mighty 2fa-beast
if (FALSE)
- /*if (g_strnstr(url_text, len, "Set-Cookie: LOpt=0;"))*/ {
+ /*if (g_strnstr(data, len, "Set-Cookie: LOpt=0;"))*/ {
//XX - Would this be better retrieved with JSON decoding the "var ServerData = {...}" code?
// <script type="text/javascript">var ServerData = {...};</script>
- gchar *session_state = skypeweb_string_get_chunk(url_text, len, ":'https://login.live.com/GetSessionState.srf?", "',");
+ gchar *session_state = skypeweb_string_get_chunk(data, len, ":'https://login.live.com/GetSessionState.srf?", "',");
if (session_state) {
//These two appear to have different object keys each request :(
/*
- gchar *PPFT = skypeweb_string_get_chunk(url_text, len, ",sFT:'", "',");
- gchar *SLK = skypeweb_string_get_chunk(url_text, len, ",aB:'", "',");
- gchar *ppauth_cookie = skypeweb_string_get_chunk(url_text, len, "Set-Cookie: PPAuth=", ";");
- gchar *mspok_cookie = skypeweb_string_get_chunk(url_text, len, "Set-Cookie: MSPOK=", "; domain=");
+ gchar *PPFT = skypeweb_string_get_chunk(data, len, ",sFT:'", "',");
+ gchar *SLK = skypeweb_string_get_chunk(data, len, ",aB:'", "',");
+ gchar *ppauth_cookie = skypeweb_string_get_chunk(data, len, "Set-Cookie: PPAuth=", ";");
+ gchar *mspok_cookie = skypeweb_string_get_chunk(data, len, "Set-Cookie: MSPOK=", "; domain=");
*/
//Poll https://login.live.com/GetSessionState.srv?{session_state} to retrieve GIF(!!) of 2fa status
@@ -202,52 +205,54 @@ skypeweb_login_got_t(PurpleUtilFetchUrlData *url_data, gpointer user_data, const
if (postdata->len > INT_MAX) tmplen = INT_MAX;
// post the t to https://login.skype.com/login/oauth?client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com
- request = g_strdup_printf("POST /login/microsoft?client_id=578134&redirect_uri=https%%3A%%2F%%2Fweb.skype.com HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n"
- "BehaviorOverride: redirectAs404\r\n"
- "Host: " SKYPEWEB_LOGIN_HOST "\r\n"
- "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"
- "Content-Length: %d\r\n\r\n%.*s",
- tmplen, tmplen, postdata->str);
- skypeweb_fetch_url_request(sa, login_url, TRUE, NULL, FALSE, request, TRUE, 524288, skypeweb_login_did_auth, sa);
+ request = purple_http_request_new(login_url);
+ purple_http_request_set_method(request, "POST");
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_set_cookie_jar(request, sa->cookie_jar);
+ purple_http_request_header_set(request, "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_header_set(request, "BehaviorOverride", "redirectAs404");
+ purple_http_request_set_contents(request, postdata->str, tmplen);
+ purple_http_request(sa->pc, request, skypeweb_login_did_auth, sa);
+ purple_http_request_unref(request);
g_string_free(postdata, TRUE);
- g_free(request);
g_free(magic_t_value);
purple_connection_update_progress(sa->pc, _("Verifying"), 3, 4);
}
static void
-skypeweb_login_got_ppft(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_login_got_ppft(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
SkypeWebAccount *sa = user_data;
- const gchar *live_login_url = "https://login.live.com";// "/ppsecure/post.srf?wa=wsignin1.0&wreply=https%3A%2F%2Fsecure.skype.com%2Flogin%2Foauth%2Fproxy%3Fclient_id%3D578134%26redirect_uri%3Dhttps%253A%252F%252Fweb.skype.com";
+ const gchar *live_login_url = "https://login.live.com" "/ppsecure/post.srf?wa=wsignin1.0&wreply=https%3A%2F%2Fsecure.skype.com%2Flogin%2Foauth%2Fproxy%3Fclient_id%3D578134%26redirect_uri%3Dhttps%253A%252F%252Fweb.skype.com";
gchar *msprequ_cookie;
gchar *mspok_cookie;
gchar *cktst_cookie;
gchar *ppft;
GString *postdata;
- gchar *request;
+ PurpleHttpRequest *request;
int tmplen;
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ const gchar *data;
+ gsize len;
+
+ data = purple_http_response_get_data(response, &len);
// grab PPFT and cookies (MSPRequ, MSPOK)
- msprequ_cookie = skypeweb_string_get_chunk(url_text, len, "Set-Cookie: MSPRequ=", ";");
+ msprequ_cookie = skypeweb_string_get_chunk(data, len, "Set-Cookie: MSPRequ=", ";");
if (!msprequ_cookie) {
purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Failed getting MSPRequ cookie"));
return;
}
- mspok_cookie = skypeweb_string_get_chunk(url_text, len, "Set-Cookie: MSPOK=", ";");
+ mspok_cookie = skypeweb_string_get_chunk(data, len, "Set-Cookie: MSPOK=", ";");
if (!mspok_cookie) {
purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Failed getting MSPOK cookie"));
return;
}
// <input type="hidden" name="PPFT" id="i0327" value="..."/>
- ppft = skypeweb_string_get_chunk(url_text, len, "name=\"PPFT\" id=\"i0327\" value=\"", "\"");
+ ppft = skypeweb_string_get_chunk(data, len, "name=\"PPFT\" id=\"i0327\" value=\"", "\"");
if (!ppft) {
purple_connection_error(sa->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Failed getting PPFT value"));
return;
@@ -258,27 +263,26 @@ skypeweb_login_got_ppft(PurpleUtilFetchUrlData *url_data, gpointer user_data, co
// postdata: login={username}&passwd={password}&PPFT={ppft value}
postdata = g_string_new("");
g_string_append_printf(postdata, "login=%s&", purple_url_encode(purple_account_get_username(sa->account)));
- g_string_append_printf(postdata, "passwd=%s&", purple_url_encode(purple_account_get_password(sa->account)));
+ g_string_append_printf(postdata, "passwd=%s&", purple_url_encode(purple_connection_get_password(sa->pc)));
g_string_append_printf(postdata, "PPFT=%s&", purple_url_encode(ppft));
tmplen = postdata->len;
if (postdata->len > INT_MAX) tmplen = INT_MAX;
// POST to https://login.live.com/ppsecure/post.srf?wa=wsignin1.0&wreply=https%3A%2F%2Fsecure.skype.com%2Flogin%2Foauth%2Fproxy%3Fclient_id%3D578134%26redirect_uri%3Dhttps%253A%252F%252Fweb.skype.com
-
- request = g_strdup_printf("POST /ppsecure/post.srf?wa=wsignin1.0&wp=MBI_SSL&wreply=https%%3A%%2F%%2Flw.skype.com%%2Flogin%%2Foauth%%2Fproxy%%3Fclient_id%%3D578134%%26redirect_uri%%3Dhttps%%253A%%252F%%252Fweb.skype.com%%252F%%26site_name%%3Dlw.skype.com HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n"
- "Host: login.live.com\r\n"
- "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"
- "Cookie: MSPRequ=%s;MSPOK=%s;CkTst=%s;\r\n"
- "Content-Length: %d\r\n\r\n%.*s",
- msprequ_cookie, mspok_cookie, cktst_cookie, tmplen, tmplen, postdata->str);
- skypeweb_fetch_url_request(sa, live_login_url, TRUE, NULL, FALSE, request, FALSE, 524288, skypeweb_login_got_t, sa);
+ request = purple_http_request_new(live_login_url);
+ purple_http_request_set_method(request, "POST");
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_set_cookie_jar(request, sa->cookie_jar);
+ purple_http_request_header_set(request, "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_header_set_printf(request, "Cookie", "MSPRequ=%s;MSPOK=%s;CkTst=%s;", msprequ_cookie, mspok_cookie, cktst_cookie);
+ purple_http_request_set_contents(request, postdata->str, tmplen);
+ purple_http_request(sa->pc, request, skypeweb_login_got_t, sa);
+ purple_http_request_unref(request);
g_string_free(postdata, TRUE);
- g_free(request);
g_free(msprequ_cookie);
g_free(mspok_cookie);
@@ -293,7 +297,7 @@ skypeweb_begin_oauth_login(SkypeWebAccount *sa)
{
const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST "/login/oauth/microsoft?client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com";
- skypeweb_fetch_url_request(sa, login_url, TRUE, NULL, FALSE, NULL, TRUE, 524288, skypeweb_login_got_ppft, sa);
+ purple_http_get(sa->pc, skypeweb_login_got_ppft, sa, login_url);
purple_connection_set_state(sa->pc, PURPLE_CONNECTION_CONNECTING);
purple_connection_update_progress(sa->pc, _("Connecting"), 1, 4);
@@ -311,20 +315,17 @@ void
skypeweb_refresh_token_login(SkypeWebAccount *sa)
{
PurpleAccount *account = sa->account;
- const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST;// "/login?client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com";
- gchar *request;
-
- request = g_strdup_printf("GET /login?client_id=578134&redirect_uri=https%%3A%%2F%%2Fweb.skype.com HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n"
- "BehaviorOverride: redirectAs404\r\n"
- "Host: " SKYPEWEB_LOGIN_HOST "\r\n"
- "Cookie: refresh-token=%s\r\n\r\n",
- purple_account_get_string(account, "refresh-token", ""));
-
- skypeweb_fetch_url_request(sa, login_url, TRUE, NULL, FALSE, request, TRUE, 524288, skypeweb_login_did_auth, sa);
-
- g_free(request);
+ const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST "/login?client_id=578134&redirect_uri=https%3A%2F%2Fweb.skype.com";
+ PurpleHttpRequest *request;
+
+ request = purple_http_request_new(login_url);
+ purple_http_request_set_method(request, "GET");
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_header_set(request, "BehaviorOverride", "redirectAs404");
+ purple_http_request_header_set_printf(request, "Cookie", "refresh-token=%s", purple_account_get_string(account, "refresh-token", ""));
+ purple_http_request(sa->pc, request, skypeweb_login_did_auth, sa);
+ purple_http_request_unref(request);
purple_connection_update_progress(sa->pc, _("Authenticating"), 2, 4);
}
diff --git a/skypeweb/skypeweb_messages.c b/skypeweb/skypeweb_messages.c
index 2a7970c..59156d3 100644
--- a/skypeweb/skypeweb_messages.c
+++ b/skypeweb/skypeweb_messages.c
@@ -176,24 +176,22 @@ process_message_resource(SkypeWebAccount *sa, JsonObject *resource)
const gchar *last_message = NULL;
// get last message (first in GList)
- if (conv && g_list_length(conv->message_history)) {
- PurpleMessage *last = g_list_nth_data(g_list_first(conv->message_history),0);
- last_message = g_strdup(last->what);
+ if (conv && g_list_length(purple_conversation_get_message_history(conv))) {
+ PurpleMessage *last = g_list_nth_data(g_list_first(purple_conversation_get_message_history(conv)),0);
+ last_message = g_strdup(purple_message_get_contents(last));
}
// add typing notification to chat
if (last_message && !g_str_equal(last_message, message)) {
- purple_conversation_write(conv, NULL, message, PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_ACTIVE_ONLY , composetimestamp);
+ PurpleMessage *msg = purple_message_new_system(message, PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_ACTIVE_ONLY);
+ purple_message_set_time(msg, composetimestamp);
+ purple_conversation_write_message(conv, msg);
+ purple_message_destroy(msg);
}
}
- #if !PURPLE_VERSION_CHECK(3, 0, 0)
- cbflags = purple_conv_chat_user_get_flags(chatconv, from);
- (void) cb;
- #else
- cb = purple_chat_conversation_find_user(chatconv, from);
- cbflags = purple_chat_user_get_flags(cb);
- #endif
+ cb = purple_chat_conversation_find_user(chatconv, from);
+ cbflags = purple_chat_user_get_flags(cb);
cbflags |= PURPLE_CHAT_USER_TYPING;
@@ -202,11 +200,7 @@ process_message_resource(SkypeWebAccount *sa, JsonObject *resource)
cbflags |= PURPLE_CHAT_USER_VOICE;
}
- #if !PURPLE_VERSION_CHECK(3, 0, 0)
- purple_conv_chat_user_set_flags(chatconv, from, cbflags);
- #else
- purple_chat_user_set_flags(cb, cbflags);
- #endif
+ purple_chat_user_set_flags(cb, cbflags);
//purple_timeout_add_seconds(7, skypeweb_clear_typing_hack, cb);
@@ -382,13 +376,18 @@ process_message_resource(SkypeWebAccount *sa, JsonObject *resource)
if (skypeweb_is_user_self(sa, from)) {
if (!g_str_has_prefix(html, "?OTR")) {
+ PurpleMessage *msg;
imconv = purple_conversations_find_im_with_account(convbuddyname, sa->account);
if (imconv == NULL)
{
imconv = purple_im_conversation_new(sa->account, convbuddyname);
}
conv = PURPLE_CONVERSATION(imconv);
- purple_conversation_write(conv, convbuddyname, html, PURPLE_MESSAGE_SEND, composetimestamp);
+
+ msg = purple_message_new_outgoing(convbuddyname, html, PURPLE_MESSAGE_SEND);
+ purple_message_set_time(msg, composetimestamp);
+ purple_conversation_write_message(conv, msg);
+ purple_message_destroy(msg);
}
} else {
purple_serv_got_im(sa->pc, from, html, PURPLE_MESSAGE_RECV, composetimestamp);
@@ -1110,17 +1109,19 @@ skypeweb_subscribe(SkypeWebAccount *sa)
}
static void
-skypeweb_got_registration_token(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_got_registration_token(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
gchar *registration_token = NULL;
gchar *endpointId = NULL;
gchar *expires = NULL;
SkypeWebAccount *sa = user_data;
gchar *new_messages_host = NULL;
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
+ const gchar *data;
+ gsize len;
- if (url_text == NULL) {
+ data = purple_http_response_get_data(response, &len);
+
+ if (data == NULL) {
if (purple_major_version == 2 && (
purple_minor_version < 10 ||
(purple_minor_version == 10 && purple_micro_version < 11))
@@ -1132,7 +1133,7 @@ skypeweb_got_registration_token(PurpleUtilFetchUrlData *url_data, gpointer user_
}
}
- new_messages_host = skypeweb_string_get_chunk(url_text, len, "Location: https://", "/");
+ new_messages_host = skypeweb_string_get_chunk(data, len, "Location: https://", "/");
if (new_messages_host != NULL && !g_str_equal(sa->messages_host, new_messages_host)) {
g_free(sa->messages_host);
sa->messages_host = new_messages_host;
@@ -1145,9 +1146,9 @@ skypeweb_got_registration_token(PurpleUtilFetchUrlData *url_data, gpointer user_
}
g_free(new_messages_host);
- registration_token = skypeweb_string_get_chunk(url_text, len, "Set-RegistrationToken: ", ";");
- endpointId = skypeweb_string_get_chunk(url_text, len, "endpointId=", "\r\n");
- expires = skypeweb_string_get_chunk(url_text, len, "expires=", ";");
+ registration_token = skypeweb_string_get_chunk(data, len, "Set-RegistrationToken: ", ";");
+ endpointId = skypeweb_string_get_chunk(data, len, "endpointId=", "\r\n");
+ expires = skypeweb_string_get_chunk(data, len, "expires=", ";");
if (registration_token == NULL) {
if (purple_account_get_string(sa->account, "refresh-token", NULL)) {
@@ -1173,17 +1174,17 @@ skypeweb_got_registration_token(PurpleUtilFetchUrlData *url_data, gpointer user_
}
static void
-skypeweb_got_vdms_token(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+skypeweb_got_vdms_token(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer user_data)
{
const gchar *token;
-
SkypeWebAccount *sa = user_data;
-
- sa->url_datas = g_slist_remove(sa->url_datas, url_data);
-
JsonParser *parser = json_parser_new();
+ const gchar *data;
+ gsize len;
+
+ data = purple_http_response_get_data(response, &len);
- if (json_parser_load_from_data(parser, url_text, -1, NULL)) {
+ if (json_parser_load_from_data(parser, data, len, NULL)) {
JsonNode *root = json_parser_get_root(parser);
JsonObject *obj = json_node_get_object(root);
@@ -1200,10 +1201,9 @@ void
skypeweb_get_registration_token(SkypeWebAccount *sa)
{
gchar *messages_url;
- gchar *request;
+ PurpleHttpRequest *request;
gchar *curtime;
gchar *response;
- gpointer requestdata;
g_free(sa->registration_token); sa->registration_token = NULL;
g_free(sa->endpoint); sa->endpoint = NULL;
@@ -1213,25 +1213,20 @@ skypeweb_get_registration_token(SkypeWebAccount *sa)
messages_url = g_strdup_printf("https://%s/v1/users/ME/endpoints", sa->messages_host);
- request = g_strdup_printf("POST /v1/users/ME/endpoints HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n"
- "BehaviorOverride: redirectAs404\r\n"
- "LockAndKey: appId=" SKYPEWEB_LOCKANDKEY_APPID "; time=%s; lockAndKeyResponse=%s\r\n"
- "ClientInfo: os=Windows; osVer=8.1; proc=Win32; lcid=en-us; deviceType=1; country=n/a; clientName=" SKYPEWEB_CLIENTINFO_NAME "; clientVer=" SKYPEWEB_CLIENTINFO_VERSION "\r\n"
- "Host: %s\r\n"
- "Content-Type: application/json\r\n"
- "Authentication: skypetoken=%s\r\n"
- "Content-Length: 28\r\n\r\n{\"endpointFeatures\":\"Agent\"}",
- curtime, response, sa->messages_host, sa->skype_token);
-
- //purple_debug_info("skypeweb", "reg token request is %s\n", request);
+ request = purple_http_request_new(messages_url);
+ purple_http_request_set_method(request, "POST");
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_set_max_redirects(request, 0);
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_header_set(request, "BehaviorOverride", "redirectAs404");
+ purple_http_request_header_set_printf(request, "LockAndKey", "appId=" SKYPEWEB_LOCKANDKEY_APPID "; time=%s; lockAndKeyResponse=%s", curtime, response);
+ purple_http_request_header_set(request, "ClientInfo", "os=Windows; osVer=8.1; proc=Win32; lcid=en-us; deviceType=1; country=n/a; clientName=" SKYPEWEB_CLIENTINFO_NAME "; clientVer=" SKYPEWEB_CLIENTINFO_VERSION);
+ purple_http_request_header_set(request, "Content-Type", "application/json");
+ purple_http_request_header_set_printf(request, "Authentication", "skypetoken=%s", sa->skype_token);
+ purple_http_request_set_contents(request, "{\"endpointFeatures\":\"Agent\"}", -1);
+ purple_http_request(sa->pc, request, skypeweb_got_registration_token, sa);
+ purple_http_request_unref(request);
- requestdata = skypeweb_fetch_url_request(sa, messages_url, TRUE, NULL, FALSE, request, TRUE, 524288, skypeweb_got_registration_token, sa);
-
- skypeweb_url_prevent_follow_redirects(requestdata);
-
- g_free(request);
g_free(curtime);
g_free(response);
g_free(messages_url);
@@ -1240,24 +1235,18 @@ skypeweb_get_registration_token(SkypeWebAccount *sa)
void
skypeweb_get_vdms_token(SkypeWebAccount *sa)
{
- gchar *request;
-
const gchar *messages_url = "https://" SKYPEWEB_STATIC_HOST "/pes/v1/petoken";
-
- request = g_strdup_printf("GET /pes/v1/petoken HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Accept: */*\r\n"
- "Host: %s\r\n"
- "Origin: https://web.skype.com\r\n"
- "Authorization: skype_token %s\r\n"
- "Content-Type: application/x-www-form-urlencoded\r\n"
- "Content-Length: 2\r\n\r\n{}",
- SKYPEWEB_STATIC_HOST, sa->skype_token);
-
- skypeweb_fetch_url_request(sa, messages_url, TRUE, NULL, FALSE, request, FALSE, 524288, skypeweb_got_vdms_token, sa);
-
- g_free(request);
-
+ PurpleHttpRequest *request;
+
+ request = purple_http_request_new(messages_url);
+ purple_http_request_set_keepalive_pool(request, sa->keepalive_pool);
+ purple_http_request_header_set(request, "Accept", "*/*");
+ purple_http_request_header_set(request, "Origin", "https://web.skype.com");
+ purple_http_request_header_set_printf(request, "Authorization", "skype_token %s", sa->skype_token);
+ purple_http_request_header_set(request, "Content-Type", "application/x-www-form-urlencoded");
+ purple_http_request_set_contents(request, "{}", -1);
+ purple_http_request(sa->pc, request, skypeweb_got_vdms_token, sa);
+ purple_http_request_unref(request);
}
@@ -1383,11 +1372,13 @@ skypeweb_sent_message_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user_data
if (obj != NULL) {
if (json_object_has_member(obj, "errorCode")) {
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, convname, sa->account);
- if (conv == NULL) {
- purple_conv_present_error(skypeweb_strip_user_prefix(convname), sa->account, json_object_get_string_member(obj, "message"));
+ PurpleChatConversation *chatconv = purple_conversations_find_chat_with_account(convname, sa->account);
+ if (chatconv == NULL) {
+ purple_conversation_present_error(skypeweb_strip_user_prefix(convname), sa->account, json_object_get_string_member(obj, "message"));
} else {
- purple_conversation_write(conv, NULL, json_object_get_string_member(obj, "message"), PURPLE_MESSAGE_ERROR, time(NULL));
+ PurpleMessage *msg = purple_message_new_system(json_object_get_string_member(obj, "message"), PURPLE_MESSAGE_ERROR);
+ purple_conversation_write_message(PURPLE_CONVERSATION(chatconv), msg);
+ purple_message_destroy(msg);
}
}
}
@@ -1453,8 +1444,16 @@ skypeweb_send_message(SkypeWebAccount *sa, const gchar *convname, const gchar *m
gint
-skypeweb_chat_send(PurpleConnection *pc, gint id, const gchar *message, PurpleMessageFlags flags)
+skypeweb_chat_send(PurpleConnection *pc, gint id,
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+PurpleMessage *msg)
+{
+ const gchar *message = purple_message_get_contents(msg);
+#else
+const gchar *message, PurpleMessageFlags flags)
{
+#endif
+
SkypeWebAccount *sa = purple_connection_get_protocol_data(pc);
PurpleChatConversation *chatconv;
@@ -1477,8 +1476,17 @@ skypeweb_chat_send(PurpleConnection *pc, gint id, const gchar *message, PurpleMe
}
gint
-skypeweb_send_im(PurpleConnection *pc, const gchar *who, const gchar *message, PurpleMessageFlags flags)
+skypeweb_send_im(PurpleConnection *pc,
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+PurpleMessage *msg)
+{
+ const gchar *who = purple_message_get_recipient(msg);
+ const gchar *message = purple_message_get_contents(msg);
+#else
+const gchar *who, const gchar *message, PurpleMessageFlags flags)
{
+#endif
+
SkypeWebAccount *sa = purple_connection_get_protocol_data(pc);
gchar *convname;
diff --git a/skypeweb/skypeweb_messages.h b/skypeweb/skypeweb_messages.h
index 2042b3c..db5bd43 100644
--- a/skypeweb/skypeweb_messages.h
+++ b/skypeweb/skypeweb_messages.h
@@ -21,8 +21,22 @@
#include "libskypeweb.h"
-gint skypeweb_send_im(PurpleConnection *pc, const gchar *who, const gchar *msg, PurpleMessageFlags flags);
-gint skypeweb_chat_send(PurpleConnection *pc, gint id, const gchar *message, PurpleMessageFlags flags);
+gint skypeweb_send_im(PurpleConnection *pc,
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ PurpleMessage *msg
+#else
+ const gchar *who, const gchar *message, PurpleMessageFlags flags
+#endif
+);
+
+gint skypeweb_chat_send(PurpleConnection *pc, gint id,
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+PurpleMessage *msg
+#else
+const gchar *message, PurpleMessageFlags flags
+#endif
+);
+
void skypeweb_set_idle(PurpleConnection *pc, int time);
void skypeweb_set_status(PurpleAccount *account, PurpleStatus *status);
guint skypeweb_conv_send_typing(PurpleConversation *conv, PurpleIMTypingState state);
diff --git a/skypeweb/skypeweb_util.c b/skypeweb/skypeweb_util.c
index 8b81597..4f0d4a9 100644
--- a/skypeweb/skypeweb_util.c
+++ b/skypeweb/skypeweb_util.c
@@ -18,11 +18,7 @@
#include "skypeweb_util.h"
-#if !PURPLE_VERSION_CHECK(3, 0, 0)
-# include "cipher.h"
-#else
-# include "ciphers/sha256hash.h"
-#endif
+#include "ciphers/sha256hash.h"
gchar *
skypeweb_string_get_chunk(const gchar *haystack, gsize len, const gchar *start, const gchar *end)
@@ -256,142 +252,6 @@ find_acct(const char *prpl, const char *acct_id)
return acct;
}
-
-/* Hack needed to stop redirect */
-struct _PurpleUtilFetchUrlData
-{
- PurpleUtilFetchUrlCallback callback;
- void *user_data;
-
- struct
- {
- char *user;
- char *passwd;
- char *address;
- int port;
- char *page;
-
- } website;
-
- char *url;
- int num_times_redirected;
- gboolean full;
- char *user_agent;
- gboolean http11;
- char *request;
- gsize request_written;
- gboolean include_headers;
-
- gboolean is_ssl;
- PurpleSslConnection *ssl_connection;
- PurpleProxyConnectData *connect_data;
- int fd;
- guint inpa;
-
- gboolean got_headers;
- gboolean has_explicit_data_len;
- char *webdata;
- gsize len;
- unsigned long data_len;
- gssize max_len;
- gboolean chunked;
- PurpleAccount *account;
-};
-
-/* Hack needed to stop redirect */
-struct _PurpleUtilFetchUrlDataTwoEleven
-{
- PurpleUtilFetchUrlCallback callback;
- void *user_data;
-
- struct
- {
- char *user;
- char *passwd;
- char *address;
- int port;
- char *page;
-
- } website;
-
- char *url;
- int num_times_redirected;
- gboolean full;
- char *user_agent;
- gboolean http11;
- char *request;
- gsize request_len;
- gsize request_written;
- gboolean include_headers;
-
- gboolean is_ssl;
- PurpleSslConnection *ssl_connection;
- PurpleProxyConnectData *connect_data;
- int fd;
- guint inpa;
-
- gboolean got_headers;
- gboolean has_explicit_data_len;
- char *webdata;
- gsize len;
- unsigned long data_len;
- gssize max_len;
- gboolean chunked;
- PurpleAccount *account;
-};
-
-static void
-skypeweb_fetch_url_request_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) {
-
- PurpleUtilFetchUrlCallback callback;
-
- if (url_text == NULL) {
- if (purple_major_version == 2 && purple_minor_version >= 11) {
- struct _PurpleUtilFetchUrlDataTwoEleven *two_eleven_url_data = (struct _PurpleUtilFetchUrlDataTwoEleven *) url_data;
-
- url_text = two_eleven_url_data->webdata;
- len = two_eleven_url_data->data_len;
- } else {
- url_text = url_data->webdata;
- len = url_data->data_len;
- }
- }
-
- callback = g_dataset_get_data(url_data, "real_callback");
- callback(url_data, user_data, url_text, len, error_message);
-
- g_dataset_destroy(url_data);
-}
-
-/* Wrapper of purple_util_fetch_url_request_len_with_account()
- * that takes a SkypeWebAccount instead of a PurpleAccount,
- * and keeps track of requests in sa->url_datas to cancel them on logout. */
-
-PurpleUtilFetchUrlData *
-skypeweb_fetch_url_request(SkypeWebAccount *sa,
- const char *url, gboolean full, const char *user_agent, gboolean http11,
- const char *request, gboolean include_headers, gssize max_len,
- PurpleUtilFetchUrlCallback callback, void *user_data)
-{
- PurpleUtilFetchUrlData *url_data;
-
- url_data = purple_util_fetch_url_request(sa->account, url, full, user_agent, http11, request, include_headers, max_len, skypeweb_fetch_url_request_cb, user_data);
- g_dataset_set_data(url_data, "real_callback", callback);
-
- if (url_data != NULL)
- sa->url_datas = g_slist_prepend(sa->url_datas, url_data);
-
- return url_data;
-}
-
-void
-skypeweb_url_prevent_follow_redirects(PurpleUtilFetchUrlData *requestdata)
-{
- if (requestdata != NULL) {
- requestdata->num_times_redirected = 10;
- }
-}
-
const gchar *
skypeweb_user_url_prefix(const gchar *who)
{
diff --git a/skypeweb/skypeweb_util.h b/skypeweb/skypeweb_util.h
index dc46a5a..520d36a 100644
--- a/skypeweb/skypeweb_util.h
+++ b/skypeweb/skypeweb_util.h
@@ -31,13 +31,5 @@ gint64 skypeweb_get_js_time();
PurpleAccount *find_acct(const char *prpl, const char *acct_id);
-PurpleUtilFetchUrlData *
-skypeweb_fetch_url_request(SkypeWebAccount *sa,
- const char *url, gboolean full, const char *user_agent, gboolean http11,
- const char *request, gboolean include_headers, gssize max_len,
- PurpleUtilFetchUrlCallback callback, void *user_data);
-
-void skypeweb_url_prevent_follow_redirects(PurpleUtilFetchUrlData *requestdata);
-
const gchar *skypeweb_user_url_prefix(const gchar *who);
-const gchar *skypeweb_strip_user_prefix(const gchar *who); \ No newline at end of file
+const gchar *skypeweb_strip_user_prefix(const gchar *who);