diff options
author | Eion Robb <eion@robbmob.com> | 2017-04-11 12:23:57 +0300 |
---|---|---|
committer | Eion Robb <eion@robbmob.com> | 2017-04-11 12:23:57 +0300 |
commit | 1513ebdf47bd6b3e4e27dffbf2261aa0fab4082a (patch) | |
tree | 05e2ad1a5a2f67ad108dfc8663d7381586bf2382 | |
parent | 35624c3430ce37696eb760dd7c8503f184c8e9f2 (diff) |
libpurple 3.0 compatibility, see issue #538
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);
|