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

github.com/EionRobb/skype4pidgin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--skypeweb/Makefile17
-rw-r--r--skypeweb/Makefile.eion54
-rw-r--r--skypeweb/libjson-glib-1.0.dllbin0 -> 278906 bytes
-rw-r--r--skypeweb/libskypeweb.c380
-rw-r--r--skypeweb/libskypeweb.h127
-rw-r--r--skypeweb/skypeweb_connection.c772
-rw-r--r--skypeweb/skypeweb_connection.h95
-rw-r--r--skypeweb/skypeweb_contacts.c462
-rw-r--r--skypeweb/skypeweb_contacts.h21
-rw-r--r--skypeweb/skypeweb_login.c108
-rw-r--r--skypeweb/skypeweb_login.h13
-rw-r--r--skypeweb/skypeweb_messages.c325
-rw-r--r--skypeweb/skypeweb_messages.h14
-rw-r--r--skypeweb/skypeweb_util.c170
-rw-r--r--skypeweb/skypeweb_util.h9
15 files changed, 2567 insertions, 0 deletions
diff --git a/skypeweb/Makefile b/skypeweb/Makefile
new file mode 100644
index 0000000..853f107
--- /dev/null
+++ b/skypeweb/Makefile
@@ -0,0 +1,17 @@
+
+COMPILER = gcc
+
+LIBPURPLE_CFLAGS += $(shell pkg-config --cflags glib-2.0 json-glib-1.0 purple nss gnome-keyring-1)
+LIBPURPLE_LIBS += $(shell pkg-config --libs glib-2.0 json-glib-1.0 purple nss)
+
+STEAM_SOURCES = \
+ steam_connection.c \
+ libsteam.c
+
+.PHONY: all clean install
+all: libsteam.so
+clean:
+ rm -f libsteam.so
+
+libsteam.so: ${STEAM_SOURCES}
+ ${COMPILER} -Wall -I. -g -O2 -fPIC -pipe ${STEAM_SOURCES} -o $@ ${LIBPURPLE_CFLAGS} ${LIBPURPLE_LIBS} -shared
diff --git a/skypeweb/Makefile.eion b/skypeweb/Makefile.eion
new file mode 100644
index 0000000..0113e75
--- /dev/null
+++ b/skypeweb/Makefile.eion
@@ -0,0 +1,54 @@
+#Customisable stuff here
+LINUX32_COMPILER = i686-pc-linux-gnu-gcc
+LINUX64_COMPILER = x86_64-pc-linux-gnu-gcc
+#WIN32_COMPILER = /usr/bin/i586-mingw32-gcc
+WIN32_COMPILER = gcc.exe
+WIN32_WINDRES = i586-mingw32-windres
+#WIN32_OBJCOPY = i586-mingw32-objcopy
+WIN32_OBJCOPY = objcopy.exe
+#LINUX_ARM_COMPILER = arm-pc-linux-gnu-gcc
+LINUX_ARM_COMPILER = arm-none-linux-gnueabi-gcc
+LINUX_PPC_COMPILER = powerpc-unknown-linux-gnu-gcc
+FREEBSD60_COMPILER = i686-pc-freebsd6.0-gcc
+MACPORT_COMPILER = i686-apple-darwin10-gcc-4.0.1
+
+LIBPURPLE_CFLAGS = -I/usr/include/libpurple -I/usr/local/include/libpurple -DPURPLE_PLUGINS -DENABLE_NLS -DHAVE_ZLIB
+GLIB_CFLAGS = -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/local/include -I/usr/include/json-glib-1.0 -ljson-glib-1.0
+WIN32_DEV_DIR = C:/cygwin/home/Eion/win32-dev
+WIN32_PIDGIN_DIR = C:/cygwin/home/Eion/pidgin-2.10.4
+WIN32_CFLAGS = -I${WIN32_DEV_DIR}/gtk_2_0-2.14/include/glib-2.0 -I${WIN32_PIDGIN_DIR}/libpurple/win32 -I${WIN32_DEV_DIR}/gtk_2_0-2.14/include -I${WIN32_DEV_DIR}/gtk_2_0-2.14/include/glib-2.0 -I${WIN32_DEV_DIR}/gtk_2_0-2.14/lib/glib-2.0/include -I${WIN32_DEV_DIR}/json-glib-0.8.0 -Wno-format -I${WIN32_PIDGIN_DIR}/libpurple -I${WIN32_DEV_DIR}/nss-3.12.5-nspr-4.8.2/include
+WIN32_LIBS = -L${WIN32_DEV_DIR}/gtk_2_0-2.14/lib -L${WIN32_PIDGIN_DIR}/libpurple -L${WIN32_DEV_DIR}/nss-3.12.5-nspr-4.8.2/lib -lglib-2.0 -lgobject-2.0 -lintl -lpurple -lws2_32 -L. -ljson-glib-1.0 -lz -lnss3
+MACPORT_CFLAGS = -I/opt/local/include/libpurple -DPURPLE_PLUGINS -DENABLE_NLS -DHAVE_ZLIB -I/opt/local/include/glib-2.0 -I/opt/local/lib/glib-2.0/include -I/opt/local/include -I/opt/local/include/json-glib-1.0 -arch i386 -arch ppc -dynamiclib -L/opt/local/lib -ljson-glib-1.0 -lpurple -lglib-2.0 -lgobject-2.0 -lintl -lz -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4
+
+DEB_PACKAGE_DIR = ./debdir
+
+STEAM_SOURCES = \
+ skypeweb_connection.c \
+ skypeweb_contacts.c \
+ skypeweb_login.c \
+ skypeweb_messages.c \
+ skypeweb_util.c \
+ libskypeweb.c
+
+
+#Standard stuff here
+.PHONY: all clean install sourcepackage
+
+all: libskypeweb.dll
+
+install:
+
+installers:
+
+clean:
+ rm -f libskypeweb.dll
+
+libskypeweb.dll: ${STEAM_SOURCES}
+ ${WIN32_COMPILER} ${LIBPURPLE_CFLAGS} -Wall -I. -g -O2 -pipe ${STEAM_SOURCES} -o $@ -shared -mno-cygwin ${WIN32_CFLAGS} ${WIN32_LIBS}
+ ${WIN32_OBJCOPY} --only-keep-debug $@ $@.dbg
+ ${WIN32_OBJCOPY} --strip-debug $@
+ ${WIN32_OBJCOPY} --add-gnu-debuglink=$@.dbg $@
+ upx libskypeweb.dll
+
+libskypeweb-debug.dll: ${STEAM_SOURCES}
+ ${WIN32_COMPILER} ${LIBPURPLE_CFLAGS} -Wall -I. -g -O2 -pipe ${STEAM_SOURCES} -o $@ -shared -mno-cygwin ${WIN32_CFLAGS} ${WIN32_LIBS}
diff --git a/skypeweb/libjson-glib-1.0.dll b/skypeweb/libjson-glib-1.0.dll
new file mode 100644
index 0000000..d36628f
--- /dev/null
+++ b/skypeweb/libjson-glib-1.0.dll
Binary files differ
diff --git a/skypeweb/libskypeweb.c b/skypeweb/libskypeweb.c
new file mode 100644
index 0000000..f7177b7
--- /dev/null
+++ b/skypeweb/libskypeweb.c
@@ -0,0 +1,380 @@
+#include "libskypeweb.h"
+#include "skypeweb_connection.h"
+#include "skypeweb_contacts.h"
+#include "skypeweb_login.h"
+#include "skypeweb_messages.h"
+#include "skypeweb_util.h"
+
+void
+skypeweb_do_all_the_things(SkypeWebAccount *sa)
+{
+ if (sa->registration_token) {
+ purple_connection_set_state(sa->pc, PURPLE_CONNECTED);
+
+ if (sa->authcheck_timeout)
+ purple_timeout_remove(sa->authcheck_timeout);
+ skypeweb_check_authrequests(sa);
+ sa->authcheck_timeout = purple_timeout_add_seconds(120, (GSourceFunc)skypeweb_check_authrequests, sa);
+
+ skypeweb_get_friend_list(sa);
+ skypeweb_poll(sa);
+ } else {
+ //Too soon!
+ skypeweb_get_registration_token(sa);
+ }
+}
+
+
+/******************************************************************************/
+/* PRPL functions */
+/******************************************************************************/
+
+static const char *skypeweb_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
+{
+ return "skype";
+}
+
+static gchar *skypeweb_status_text(PurpleBuddy *buddy)
+{
+ SkypeWebBuddy *sbuddy = buddy->proto_data;
+
+ if (sbuddy && (sbuddy->mood || sbuddy->rich_mood))
+ {
+ if (sbuddy->rich_mood)
+ {
+ return sbuddy->rich_mood;
+ } else {
+ return g_markup_printf_escaped("%s", sbuddy->mood);
+ }
+ }
+
+ return NULL;
+}
+
+void
+skypeweb_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
+{
+ SkypeWebBuddy *sbuddy = buddy->proto_data;
+
+ if (sbuddy)
+ {
+ PurplePresence *presence;
+ PurpleStatus *status;
+ SkypeWebBuddy *sbuddy = buddy->proto_data;
+
+ presence = purple_buddy_get_presence(buddy);
+ status = purple_presence_get_active_status(presence);
+ purple_notify_user_info_add_pair_html(user_info, _("Status"), purple_status_get_name(status));
+ if (sbuddy->mood && *sbuddy->mood)
+ purple_notify_user_info_add_pair_html(user_info, _("Message"), sbuddy->mood);
+
+ if (sbuddy->display_name && *sbuddy->display_name)
+ purple_notify_user_info_add_pair_html(user_info, "Alias", sbuddy->display_name);
+ if (sbuddy->fullname && *sbuddy->fullname)
+ purple_notify_user_info_add_pair_html(user_info, "Full Name", sbuddy->fullname);
+ }
+}
+
+const gchar *
+skypeweb_list_emblem(PurpleBuddy *buddy)
+{
+ SkypeWebBuddy *sbuddy = buddy->proto_data;
+
+ return NULL;
+}
+
+GList *
+skypeweb_status_types(PurpleAccount *account)
+{
+ GList *types = NULL;
+ PurpleStatusType *status;
+
+ purple_debug_info("skypeweb", "status_types\n");
+
+ status = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, "Online", NULL, TRUE, TRUE, FALSE, "message", "Mood", purple_value_new(PURPLE_TYPE_STRING), NULL);
+ types = g_list_append(types, status);
+ status = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE, "Offline", NULL, TRUE, TRUE, FALSE, "message", "Mood", purple_value_new(PURPLE_TYPE_STRING), NULL);
+ types = g_list_append(types, status);
+ status = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, "Idle", NULL, TRUE, TRUE, FALSE, "message", "Mood", purple_value_new(PURPLE_TYPE_STRING), NULL);
+ types = g_list_append(types, status);
+ status = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, "Away", NULL, TRUE, TRUE, FALSE, "message", "Mood", purple_value_new(PURPLE_TYPE_STRING), NULL);
+ types = g_list_append(types, status);
+ status = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY, "Hidden", NULL, TRUE, TRUE, FALSE, "message", "Mood", purple_value_new(PURPLE_TYPE_STRING), NULL);
+ types = g_list_append(types, status);
+
+ return types;
+}
+
+
+static void skypeweb_buddy_free(PurpleBuddy *buddy)
+{
+ SkypeWebBuddy *sbuddy = buddy->proto_data;
+ if (sbuddy != NULL)
+ {
+ buddy->proto_data = NULL;
+
+ g_free(sbuddy->skypename);
+ g_free(sbuddy->fullname);
+ g_free(sbuddy->display_name);
+ g_free(sbuddy->avatar_url);
+ g_free(sbuddy->mood);
+ g_free(sbuddy->rich_mood);
+ g_free(sbuddy->country);
+ g_free(sbuddy->city);
+
+ g_free(sbuddy);
+ }
+}
+
+void
+skypeweb_fake_group_buddy(PurpleConnection *pc, const char *who, const char *old_group, const char *new_group)
+{
+ // Do nothing to stop the remove+add behaviour
+}
+void
+skypeweb_fake_group_rename(PurpleConnection *pc, const char *old_name, PurpleGroup *group, GList *moved_buddies)
+{
+ // Do nothing to stop the remove+add behaviour
+}
+
+
+static void
+skypeweb_login(PurpleAccount *account)
+{
+ PurpleConnection *pc = purple_account_get_connection(account);
+ SkypeWebAccount *sa = g_new0(SkypeWebAccount, 1);
+
+ pc->proto_data = 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;
+ }
+
+ 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->sent_messages_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ sa->waiting_conns = g_queue_new();
+
+ skypeweb_begin_web_login(sa);
+}
+
+static void skypeweb_close(PurpleConnection *pc)
+{
+ SkypeWebAccount *sa;
+ GString *post;
+
+ g_return_if_fail(pc != NULL);
+ g_return_if_fail(pc->proto_data != NULL);
+
+ sa = pc->proto_data;
+
+ skypeweb_logout(sa);
+
+ purple_timeout_remove(sa->authcheck_timeout);
+ purple_timeout_remove(sa->poll_timeout);
+ purple_timeout_remove(sa->watchdog_timeout);
+
+ 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));
+
+ 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);
+ }
+
+ 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->skype_token);
+ g_free(sa->registration_token);
+ g_free(sa);
+}
+
+/******************************************************************************/
+/* Plugin functions */
+/******************************************************************************/
+
+static gboolean plugin_load(PurplePlugin *plugin)
+{
+ return TRUE;
+}
+
+static gboolean plugin_unload(PurplePlugin *plugin)
+{
+ return TRUE;
+}
+
+static GList *skypeweb_actions(PurplePlugin *plugin, gpointer context)
+{
+ GList *m = NULL;
+ PurplePluginAction *act;
+
+ act = purple_plugin_action_new(_("Search for friends..."),
+ skypeweb_search_users);
+ m = g_list_append(m, act);
+
+ return m;
+}
+
+static void plugin_init(PurplePlugin *plugin)
+{
+ PurpleAccountOption *option;
+ PurplePluginInfo *info = plugin->info;
+ PurplePluginProtocolInfo *prpl_info = info->extra_info;
+
+
+ /*option = purple_account_option_bool_new(
+ _("Always use HTTPS"),
+ "always_use_https", FALSE);
+ prpl_info->protocol_options = g_list_append(
+ prpl_info->protocol_options, option);*/
+
+}
+
+static PurplePluginProtocolInfo prpl_info = {
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+#endif
+
+ /* options */
+ OPT_PROTO_MAIL_CHECK,
+
+ NULL, /* user_splits */
+ NULL, /* protocol_options */
+ /* NO_BUDDY_ICONS */ /* icon_spec */
+ {"jpeg", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
+ skypeweb_list_icon, /* list_icon */
+ skypeweb_list_emblem, /* list_emblems */
+ skypeweb_status_text, /* status_text */
+ skypeweb_tooltip_text, /* tooltip_text */
+ skypeweb_status_types, /* status_types */
+ NULL,//skypeweb_node_menu, /* blist_node_menu */
+ NULL,//skypeweb_chat_info, /* chat_info */
+ NULL,//skypeweb_chat_info_defaults, /* chat_info_defaults */
+ skypeweb_login, /* login */
+ skypeweb_close, /* close */
+ skypeweb_send_im, /* send_im */
+ NULL, /* set_info */
+ skypeweb_send_typing, /* send_typing */
+ NULL,//skypeweb_get_info, /* get_info */
+ skypeweb_set_status, /* set_status */
+ skypeweb_set_idle, /* set_idle */
+ NULL, /* change_passwd */
+#if !PURPLE_VERSION_CHECK(3, 0, 0)
+ skypeweb_add_buddy, /* add_buddy */
+#else
+ skypeweb_add_buddy_with_invite,
+#endif
+ NULL, /* add_buddies */
+ skypeweb_buddy_remove, /* remove_buddy */
+ NULL, /* remove_buddies */
+ NULL, /* add_permit */
+ NULL, /* add_deny */
+ NULL, /* rem_permit */
+ NULL, /* rem_deny */
+ NULL, /* set_permit_deny */
+ NULL,//skypeweb_fake_join_chat, /* join_chat */
+ NULL, /* reject chat invite */
+ NULL,//skypeweb_get_chat_name, /* get_chat_name */
+ NULL, /* chat_invite */
+ NULL,//skypeweb_chat_fake_leave, /* chat_leave */
+ NULL, /* chat_whisper */
+ NULL,//skypeweb_chat_send, /* chat_send */
+ NULL, /* keepalive */
+ NULL, /* register_user */
+ NULL, /* get_cb_info */
+#if !PURPLE_VERSION_CHECK(3, 0, 0)
+ NULL, /* get_cb_away */
+#endif
+ NULL, /* alias_buddy */
+ skypeweb_fake_group_buddy, /* group_buddy */
+ skypeweb_fake_group_rename, /* rename_group */
+ skypeweb_buddy_free, /* buddy_free */
+ NULL, /* convo_closed */
+ purple_normalize_nocase, /* normalize */
+ NULL, /* set_buddy_icon */
+ NULL, /* remove_group */
+ NULL, /* get_cb_real_name */
+ NULL, /* set_chat_topic */
+ NULL, /* find_blist_chat */
+ NULL, /* roomlist_get_list */
+ NULL, /* roomlist_cancel */
+ NULL, /* roomlist_expand_category */
+ NULL, /* can_receive_file */
+ NULL, /* send_file */
+ NULL, /* new_xfer */
+ NULL, /* offline_message */
+ NULL, /* whiteboard_prpl_ops */
+ NULL, /* send_raw */
+ NULL, /* roomlist_room_serialize */
+ NULL, /* unregister_user */
+ NULL, /* send_attention */
+ NULL, /* attention_types */
+#if (PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION >= 5) || PURPLE_MAJOR_VERSION > 2
+#if PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION >= 5
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
+#endif
+ NULL, // skypeweb_get_account_text_table, /* get_account_text_table */
+ NULL, /* initiate_media */
+ NULL, /* can_do_media */
+ NULL, /* get_moods */
+ NULL, /* set_public_alias */
+ NULL /* get_public_alias */
+#if PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION >= 8
+, skypeweb_add_buddy_with_invite, /* add_buddy_with_invite */
+ NULL /* add_buddies_with_invite */
+#endif
+#endif
+};
+
+static PurplePluginInfo info = {
+ PURPLE_PLUGIN_MAGIC,
+ PURPLE_MAJOR_VERSION, /* major_version */
+ PURPLE_MINOR_VERSION, /* minor version */
+ PURPLE_PLUGIN_PROTOCOL, /* type */
+ NULL, /* ui_requirement */
+ 0, /* flags */
+ NULL, /* dependencies */
+ PURPLE_PRIORITY_DEFAULT, /* priority */
+ SKYPEWEB_PLUGIN_ID, /* id */
+ "Skype (HTTP)", /* name */
+ SKYPEWEB_PLUGIN_VERSION, /* version */
+ N_("Skype for Web Protocol Plugin"), /* summary */
+ N_("Skype for Web Protocol Plugin"), /* description */
+ "Eion Robb <eionrobb@gmail.com>", /* author */
+ "http://skype4pidgin.googlecode.com/", /* homepage */
+ plugin_load, /* load */
+ plugin_unload, /* unload */
+ NULL, /* destroy */
+ NULL, /* ui_info */
+ &prpl_info, /* extra_info */
+ NULL, /* prefs_info */
+ skypeweb_actions, /* actions */
+
+ /* padding */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PURPLE_INIT_PLUGIN(skypeweb, plugin_init, info);
diff --git a/skypeweb/libskypeweb.h b/skypeweb/libskypeweb.h
new file mode 100644
index 0000000..4676be8
--- /dev/null
+++ b/skypeweb/libskypeweb.h
@@ -0,0 +1,127 @@
+
+
+#ifndef LIBSKYPEWEB_H
+#define LIBSKYPEWEB_H
+
+/* Maximum number of simultaneous connections to a server */
+#define SKYPEWEB_MAX_CONNECTIONS 16
+
+#include <glib.h>
+
+#include <errno.h>
+#include <string.h>
+#include <glib/gi18n.h>
+#include <sys/types.h>
+#ifdef __GNUC__
+ #include <unistd.h>
+#endif
+
+#ifndef G_GNUC_NULL_TERMINATED
+# if __GNUC__ >= 4
+# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+# else
+# define G_GNUC_NULL_TERMINATED
+# endif /* __GNUC__ >= 4 */
+#endif /* G_GNUC_NULL_TERMINATED */
+
+#ifdef _WIN32
+# include "win32dep.h"
+# define dlopen(a,b) LoadLibrary(a)
+# define RTLD_LAZY
+# define dlsym(a,b) GetProcAddress(a,b)
+# define dlclose(a) FreeLibrary(a)
+#else
+# include <arpa/inet.h>
+# include <dlfcn.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+#endif
+
+#include <json-glib/json-glib.h>
+
+#ifndef PURPLE_PLUGINS
+# define PURPLE_PLUGINS
+#endif
+
+#include "accountopt.h"
+#include "blist.h"
+#include "core.h"
+#include "connection.h"
+#include "debug.h"
+#include "dnsquery.h"
+#include "proxy.h"
+#include "prpl.h"
+#include "request.h"
+#include "savedstatuses.h"
+#include "sslconn.h"
+#include "util.h"
+#include "version.h"
+
+#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
+#endif
+
+#define SKYPEWEB_MAX_MSG_RETRY 2
+
+#define SKYPEWEB_PLUGIN_ID "prpl-skypeweb"
+#define SKYPEWEB_PLUGIN_VERSION "0.1"
+
+#define SKYPEWEB_LOCKANDKEY_APPID "msmsgs@msnmsgr.com"
+#define SKYPEWEB_LOCKANDKEY_SECRET "Q1P7W2E4J9R8U3S5"
+
+#define SKYPEWEB_CONTACTS_HOST "api.skype.com"
+#define SKYPEWEB_MESSAGES_HOST "client-s.gateway.messenger.live.com"
+#define SKYPEWEB_LOGIN_HOST "login.skype.com"
+
+typedef struct _SkypeWebAccount SkypeWebAccount;
+typedef struct _SkypeWebBuddy SkypeWebBuddy;
+
+typedef void (*SkypeWebFunc)(SkypeWebAccount *swa);
+
+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;
+
+ GHashTable *sent_messages_hash;
+ guint poll_timeout;
+ guint watchdog_timeout;
+
+ guint authcheck_timeout;
+
+ gchar *skype_token;
+ gchar *registration_token;
+};
+
+struct _SkypeWebBuddy {
+ SkypeWebAccount *sa;
+ PurpleBuddy *buddy;
+
+ /** Contact info */
+ gchar *skypename;
+ gchar *fullname;
+ gchar *display_name;
+ gboolean authorized;
+ gboolean blocked;
+
+ /** Profile info */
+ gchar *avatar_url;
+ gchar *mood;
+ gchar *rich_mood;
+ gchar *country;
+ gchar *city;
+};
+
+
+void skypeweb_do_all_the_things(SkypeWebAccount *sa);
+
+#endif /* LIBSKYPEWEB_H */
diff --git a/skypeweb/skypeweb_connection.c b/skypeweb/skypeweb_connection.c
new file mode 100644
index 0000000..0a68adb
--- /dev/null
+++ b/skypeweb/skypeweb_connection.c
@@ -0,0 +1,772 @@
+
+
+#include "skypeweb_connection.h"
+
+#if !PURPLE_VERSION_CHECK(3, 0, 0)
+ #define purple_connection_error purple_connection_error_reason
+#endif
+
+#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)
+{
+ 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);
+ 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);
+}
+
+void
+skypeweb_connection_close(SkypeWebConnection *skypewebcon)
+{
+ skypewebcon->sa->conns = g_slist_remove(skypewebcon->sa->conns, skypewebcon);
+
+ 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);
+
+ g_free(skypewebcon->url);
+ g_free(skypewebcon->hostname);
+ g_free(skypewebcon);
+}
+
+static 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;
+ }
+ }
+
+ g_free(skypewebcon->rx_buf);
+ skypewebcon->rx_buf = NULL;
+
+ if (skypewebcon->callback != NULL) {
+ if (!len)
+ {
+ purple_debug_error("skypeweb", "No data in response\n");
+ //skypewebcon->callback(skypewebcon->sa, NULL, skypewebcon->user_data);
+ } else {
+ JsonParser *parser = json_parser_new();
+ if (!json_parser_load_from_data(parser, tmp, len, NULL))
+ {
+ if (skypewebcon->error_callback != NULL) {
+ skypewebcon->error_callback(skypewebcon->sa, tmp, len, skypewebcon->user_data);
+ } else {
+ purple_debug_error("skypeweb", "Error parsing response: %s\n", tmp);
+ }
+ } else {
+ JsonNode *root = json_parser_get_root(parser);
+
+ //TODO
+ purple_debug_info("skypeweb", "Got response: %s\n", tmp);
+ purple_debug_info("skypeweb", "executing callback for %s\n", skypewebcon->url);
+ skypewebcon->callback(skypewebcon->sa, root, skypewebcon->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);
+ 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);
+ 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);
+ }
+}
+
+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;
+ 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_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL)
+ proxy_info = purple_global_proxy_get_info();
+ if (purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_HTTP)
+ {
+ is_proxy = TRUE;
+ }
+ }
+ if (is_proxy == TRUE)
+ {
+ real_url = g_strdup_printf("http://%s%s", host, url);
+ } else {
+ real_url = g_strdup(url);
+ }
+
+ cookies = skypeweb_cookies_to_string(sa);
+ //user_agent = purple_account_get_string(sa->account, "user-agent", "SkypeWeb 1.2.0 / iPhone");
+
+ if (method & SKYPEWEB_METHOD_POST && !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) {
+
+ if (postdata && postdata[0] == '[' || postdata[0] == '{') {
+ g_string_append(request, "Content-Type: application/json\r\n"); // hax
+ } else {
+ g_string_append_printf(request, "Content-Type: application/x-www-form-urlencoded\r\n");
+ }
+ g_string_append_printf(request, "Content-length: %zu\r\n", strlen(postdata));
+ }
+ g_string_append_printf(request, "Accept: */*\r\n");
+
+ if (g_str_equal(host, SKYPEWEB_CONTACTS_HOST)) {
+ g_string_append_printf(request, "X-Skypetoken: %s\r\n", sa->skype_token);
+ g_string_append(request, "X-Stratus-Caller: swx-skype.com\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");
+ } else if (g_str_equal(host, SKYPEWEB_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=swx-skype.com; clientVer=908/1.0.0.20\r\n");
+ }
+
+ //g_string_append_printf(request, "Cookie: %s\r\n", cookies);
+ 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);
+ 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)
+ 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)
+ 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);
+ }
+
+ 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_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL)
+ proxy_info = purple_global_proxy_get_info();
+ if (purple_proxy_info_get_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
+
+ 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 && !sa->account->disconnecting) {
+ 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);
+ }
+
+ skypewebcon->timeout_watcher = purple_timeout_add_seconds(120, skypeweb_connection_timedout, skypewebcon);
+
+ return;
+}
+
diff --git a/skypeweb/skypeweb_connection.h b/skypeweb/skypeweb_connection.h
new file mode 100644
index 0000000..c49da65
--- /dev/null
+++ b/skypeweb/skypeweb_connection.h
@@ -0,0 +1,95 @@
+
+#ifndef SKYPEWEB_CONNECTION_H
+#define SKYPEWEB_CONNECTION_H
+
+#include "libskypeweb.h"
+
+typedef void (*SkypeWebProxyCallbackFunc)(SkypeWebAccount *sa, JsonNode *node, gpointer user_data);
+typedef void (*SkypeWebProxyCallbackErrorFunc)(SkypeWebAccount *sa, const gchar *data, gssize data_len, gpointer user_data);
+
+/*
+ * This is a bitmask.
+ */
+typedef enum
+{
+ SKYPEWEB_METHOD_GET = 0x0001,
+ SKYPEWEB_METHOD_POST = 0x0002,
+ SKYPEWEB_METHOD_PUT = 0x0004,
+ SKYPEWEB_METHOD_DELETE = 0x0008,
+ SKYPEWEB_METHOD_SSL = 0x1000,
+} SkypeWebMethod;
+
+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;
+ 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,
+ gboolean keepalive);
+gchar *skypeweb_cookies_to_string(SkypeWebAccount *sa);
+
+
+
+/* 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;
+};
+
+#endif /* SKYPEWEB_CONNECTION_H */
diff --git a/skypeweb/skypeweb_contacts.c b/skypeweb/skypeweb_contacts.c
new file mode 100644
index 0000000..e4be388
--- /dev/null
+++ b/skypeweb/skypeweb_contacts.c
@@ -0,0 +1,462 @@
+
+
+#include "skypeweb_contacts.h"
+#include "skypeweb_connection.h"
+#include "skypeweb_util.h"
+
+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)
+{
+ PurpleBuddy *buddy = user_data;
+ SkypeWebBuddy *sbuddy;
+
+ if (!buddy || !buddy->proto_data)
+ return;
+
+ sbuddy = buddy->proto_data;
+
+ purple_buddy_icons_set_for_user(buddy->account, buddy->name, g_memdup(url_text, len), len, sbuddy->avatar_url);
+
+ active_icon_downloads--;
+}
+
+static void
+skypeweb_get_icon_now(PurpleBuddy *buddy)
+{
+ SkypeWebBuddy *sbuddy;
+ gchar *url;
+
+ purple_debug_info("skypeweb", "getting new buddy icon for %s\n", buddy->name);
+
+ sbuddy = buddy->proto_data;
+ if (sbuddy->avatar_url) {
+ url = g_strdup(sbuddy->avatar_url);
+ } else {
+ url = g_strdup_printf("https://api.skype.com/users/%s/profile/avatar", purple_url_encode(buddy->name));
+ }
+
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ purple_util_fetch_url_request(buddy->account, url, TRUE, NULL, FALSE, NULL, FALSE, -1, skypeweb_get_icon_cb, buddy);
+#else
+ purple_util_fetch_url_request(url, TRUE, NULL, FALSE, NULL, FALSE, skypeweb_get_icon_cb, buddy);
+#endif
+
+ g_free(url);
+
+ active_icon_downloads++;
+}
+
+static gboolean
+skypeweb_get_icon_queuepop(gpointer data)
+{
+ PurpleBuddy *buddy = data;
+
+ // Only allow 4 simultaneous downloads
+ if (active_icon_downloads > 4)
+ return TRUE;
+
+ skypeweb_get_icon_now(buddy);
+ return FALSE;
+}
+
+void
+skypeweb_get_icon(PurpleBuddy *buddy)
+{
+ if (!buddy) return;
+
+ purple_timeout_add(100, skypeweb_get_icon_queuepop, (gpointer)buddy);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void
+skypeweb_search_results_add_buddy(PurpleConnection *pc, GList *row, void *user_data)
+{
+ PurpleAccount *account = purple_connection_get_account(pc);
+
+ if (!purple_find_buddy(account, g_list_nth_data(row, 0)))
+ purple_blist_request_add_buddy(account, g_list_nth_data(row, 0), "Skype", g_list_nth_data(row, 1));
+}
+
+void
+skypeweb_search_users_text_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user_data)
+{
+ JsonArray *resultsarray = NULL;
+ guint index, length;
+ GString *userids;
+ gchar *search_term = user_data;
+
+ PurpleNotifySearchResults *results;
+ PurpleNotifySearchColumn *column;
+
+
+ length = json_array_get_length(resultsarray);
+
+ 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, "");
+ g_free(primary_text);
+ g_free(search_term);
+ return;
+ }
+
+ userids = g_string_new("");
+
+ resultsarray = json_node_get_array(node);
+ for(index = 0; index < length; index++)
+ {
+ JsonObject *result = json_array_get_object_element(resultsarray, index);
+ g_string_append_printf(userids, "%s,", json_object_get_string_member(result, "skypewebid"));
+ }
+
+
+ results = purple_notify_searchresults_new();
+ if (results == NULL)
+ {
+ g_free(search_term);
+ return;
+ }
+
+ /* columns: Friend ID, Name, Network */
+ column = purple_notify_searchresults_column_new(_("Skype Name"));
+ purple_notify_searchresults_column_add(results, column);
+ column = purple_notify_searchresults_column_new(_("Display Name"));
+ purple_notify_searchresults_column_add(results, column);
+ column = purple_notify_searchresults_column_new(_("City"));
+ purple_notify_searchresults_column_add(results, column);
+ column = purple_notify_searchresults_column_new(_("Country"));
+ purple_notify_searchresults_column_add(results, column);
+
+ purple_notify_searchresults_button_add(results,
+ PURPLE_NOTIFY_BUTTON_ADD,
+ skypeweb_search_results_add_buddy);
+
+ for(index = 0; index < length; index++)
+ {
+ JsonObject *contact = json_array_get_object_element(resultsarray, index);
+ JsonObject *contactcards = json_object_get_object_member(contact, "ContactCards");
+ JsonObject *skypecontact = json_object_get_object_member(contactcards, "Skype");
+ JsonObject *currentlocation = json_object_get_object_member(contactcards, "CurrentLocation");
+
+ /* the row in the search results table */
+ /* prepend to it backwards then reverse to speed up adds */
+ GList *row = NULL;
+
+ row = g_list_prepend(row, g_strdup(json_object_get_string_member(skypecontact, "SkypeName")));
+ row = g_list_prepend(row, g_strdup(json_object_get_string_member(skypecontact, "DisplayName")));
+ row = g_list_prepend(row, g_strdup(json_object_get_string_member(currentlocation, "City")));
+ row = g_list_prepend(row, g_strdup(json_object_get_string_member(currentlocation, "Country")));
+
+ row = g_list_reverse(row);
+
+ purple_notify_searchresults_row_add(results, row);
+ }
+
+ purple_notify_searchresults(sa->pc, NULL, search_term, NULL,
+ results, NULL, NULL);
+}
+
+void
+skypeweb_search_users_text(gpointer user_data, const gchar *text)
+{
+ SkypeWebAccount *sa = user_data;
+ GString *url = g_string_new("/search/users/any?");
+
+ g_string_append_printf(url, "keyWord=%s&", purple_url_encode(text));
+ g_string_append(url, "contactTypes[]=skype&");
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_GET | SKYPEWEB_METHOD_SSL, SKYPEWEB_CONTACTS_HOST, url->str, NULL, skypeweb_search_users_text_cb, g_strdup(text), FALSE);
+
+ g_string_free(url, TRUE);
+}
+
+void
+skypeweb_search_users(PurplePluginAction *action)
+{
+ PurpleConnection *pc = (PurpleConnection *) action->context;
+ SkypeWebAccount *sa = pc->proto_data;
+
+ purple_request_input(pc, "Search for Skype Friends",
+ "Search for Skype Friends",
+ NULL,
+ NULL, FALSE, FALSE, NULL,
+ _("_Search"), G_CALLBACK(skypeweb_search_users_text),
+ _("_Cancel"), NULL,
+ purple_connection_get_account(pc), NULL, NULL,
+ sa);
+
+}
+
+
+
+
+static void
+skypeweb_got_friend_profiles(SkypeWebAccount *sa, JsonNode *node, gpointer user_data)
+{
+ JsonArray *contacts;
+ PurpleBuddy *buddy;
+ SkypeWebBuddy *sbuddy;
+ guint index;
+ guint length;
+
+ contacts = json_node_get_array(node);
+ length = json_array_get_length(contacts);
+
+ for(index = 0; index < length; index++)
+ {
+ JsonObject *contact = json_array_get_object_element(contacts, index);
+
+ const gchar *username = json_object_get_string_member(contact, "username");
+ const gchar *new_avatar;
+
+ buddy = purple_find_buddy(sa->account, username);
+ if (!buddy)
+ continue;
+ sbuddy = buddy->proto_data;
+ if (sbuddy == NULL)
+ {
+ sbuddy = g_new0(SkypeWebBuddy, 1);
+ buddy->proto_data = sbuddy;
+ sbuddy->skypename = g_strdup(username);
+ }
+
+ g_free(sbuddy->display_name); sbuddy->display_name = g_strdup(json_object_get_string_member(contact, "displayname"));
+ serv_got_alias(sa->pc, username, sbuddy->display_name);
+
+ new_avatar = json_object_get_string_member(contact, "avatarUrl");
+ if (new_avatar && (!sbuddy->avatar_url || !g_str_equal(sbuddy->avatar_url, new_avatar))) {
+ g_free(sbuddy->avatar_url);
+ sbuddy->avatar_url = g_strdup(new_avatar);
+ skypeweb_get_icon(buddy);
+ }
+
+ g_free(sbuddy->mood); sbuddy->mood = g_strdup(json_object_get_string_member(contact, "mood"));
+ g_free(sbuddy->rich_mood); sbuddy->rich_mood = g_strdup(json_object_get_string_member(contact, "richMood"));
+ g_free(sbuddy->country); sbuddy->country = g_strdup(json_object_get_string_member(contact, "country"));
+ g_free(sbuddy->city); sbuddy->city = g_strdup(json_object_get_string_member(contact, "city"));
+ }
+}
+
+void
+skypeweb_get_friend_profiles(SkypeWebAccount *sa, GSList *contacts)
+{
+ const gchar *profiles_url = "/users/SELF/contacts/profiles"; //TODO: Might need to be username instead of SELF
+ GString *postdata;
+ GSList *cur = contacts;
+
+ postdata = g_string_new("");
+
+ do {
+ g_string_append_printf(postdata, "&contacts[]=", purple_url_encode(cur->data));
+ } while((cur = g_slist_next(cur)));
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_GET | SKYPEWEB_METHOD_SSL, SKYPEWEB_CONTACTS_HOST, profiles_url, NULL, skypeweb_got_friend_profiles, NULL, TRUE);
+
+ g_string_free(postdata, TRUE);
+}
+
+void
+skypeweb_get_friend_profile(SkypeWebAccount *sa, const gchar *who)
+{
+ GSList *contacts = NULL;
+ gchar *whodup;
+
+ g_return_if_fail(sa && who && *who);
+
+ whodup = g_strdup(who);
+ contacts = g_slist_prepend(contacts, whodup);
+
+ skypeweb_get_friend_profiles(sa, contacts);
+
+ g_free(contacts);
+ g_free(whodup);
+}
+
+static void
+skypeweb_get_friend_list_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user_data)
+{
+ JsonArray *friends;
+ PurpleGroup *group = NULL;
+ GSList *users_to_fetch = NULL;
+ guint index, length;
+
+ /* {
+ "skypename": "eionrobb",
+ "fullname": "Eion Robb",
+ "authorized": true,
+ "blocked": false,
+ "display_name": "eionrobb",
+ "pstn_number": null,
+ "phone1": null,
+ "phone1_label": null,
+ "phone2": null,
+ "phone2_label": null,
+ "phone3": null,
+ "phone3_label": null
+} */
+
+ friends = json_node_get_array(node);
+ length = json_array_get_length(friends);
+
+ for(index = 0; index < length; index++)
+ {
+ JsonObject *friend = json_array_get_object_element(friends, index);
+ const gchar *skypename = json_object_get_string_member(friend, "skypename");
+ const gchar *fullname = json_object_get_string_member(friend, "fullname");
+ const gchar *display_name = json_object_get_string_member(friend, "display_name");
+ gboolean authorized = json_object_get_boolean_member(friend, "authorized");
+ gboolean blocked = json_object_get_boolean_member(friend, "blocked");
+
+ if (!purple_find_buddy(sa->account, skypename))
+ {
+ if (!group)
+ {
+ group = purple_find_group("Skype");
+ if (!group)
+ {
+ group = purple_group_new("Skype");
+ purple_blist_add_group(group, NULL);
+ }
+ }
+ purple_blist_add_buddy(purple_buddy_new(sa->account, skypename, NULL), NULL, group, NULL);
+ }
+
+ SkypeWebBuddy *sbuddy = g_new0(SkypeWebBuddy, 1);
+ sbuddy->skypename = g_strdup(skypename);
+ sbuddy->fullname = g_strdup(fullname);
+ sbuddy->display_name = g_strdup(display_name);
+ sbuddy->authorized = authorized;
+ sbuddy->blocked = blocked;
+
+ serv_got_alias(sa->pc, skypename, sbuddy->display_name); //purple_blist_server_alias_buddy(buddy, string_parts[3]);
+
+ users_to_fetch = g_slist_prepend(users_to_fetch, (gpointer) skypename);
+ }
+
+ if (users_to_fetch)
+ {
+ skypeweb_get_friend_profiles(sa, users_to_fetch);
+ }
+ g_free(users_to_fetch);
+}
+
+void
+skypeweb_get_friend_list(SkypeWebAccount *sa)
+{
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_GET | SKYPEWEB_METHOD_SSL, SKYPEWEB_CONTACTS_HOST, "/users/self/contacts", NULL, skypeweb_get_friend_list_cb, NULL, TRUE);
+}
+
+
+
+void
+skypeweb_auth_accept_cb(gpointer sender)
+{
+ PurpleBuddy *buddy = sender;
+ SkypeWebAccount *sa;
+ gchar *url = NULL;
+
+ sa = buddy->account->gc->proto_data;
+
+ url = g_strdup_printf("/users/self/contacts/auth-request/%s/accept", purple_url_encode(buddy->name));
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_PUT | SKYPEWEB_METHOD_SSL, SKYPEWEB_CONTACTS_HOST, url, NULL, NULL, NULL, TRUE);
+
+ g_free(url);
+}
+
+void
+skypeweb_auth_reject_cb(gpointer sender)
+{
+ PurpleBuddy *buddy = sender;
+ SkypeWebAccount *sa;
+ gchar *url = NULL;
+
+ sa = buddy->account->gc->proto_data;
+
+ url = g_strdup_printf("/users/self/contacts/auth-request/%s/reject", purple_url_encode(buddy->name));
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_PUT | SKYPEWEB_METHOD_SSL, SKYPEWEB_CONTACTS_HOST, url, NULL, NULL, NULL, TRUE);
+
+ g_free(url);
+}
+
+static void
+skypeweb_got_authrequests(SkypeWebAccount *sa, JsonNode *node, gpointer user_data)
+{
+ JsonArray *requests;
+ guint index, length;
+
+ requests = json_node_get_array(node);
+ /* [{
+ "event_time": "2014-10-21 18:53:53.763883",
+ "sender": "gretike1984",
+ "greeting": "Kedves Eion Robb! Szeretn\u00e9lek felvenni a partnerlist\u00e1mra."
+ }] */
+ length = json_array_get_length(requests);
+
+ for(index = 0; index < length; index++)
+ {
+ JsonObject *request = json_array_get_object_element(requests, index);
+ const gchar *event_time = json_object_get_string_member(request, "event_time");
+ const gchar *sender = json_object_get_string_member(request, "sender");
+ const gchar *greeting = json_object_get_string_member(request, "greeting");
+
+ purple_account_request_authorization(
+ sa->account, sender, NULL,
+ NULL, greeting, FALSE,
+ skypeweb_auth_accept_cb, skypeweb_auth_reject_cb, purple_buddy_new(sa->account, sender, NULL));
+
+ }
+}
+
+gboolean
+skypeweb_check_authrequests(SkypeWebAccount *sa)
+{
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_GET | SKYPEWEB_METHOD_SSL, SKYPEWEB_CONTACTS_HOST, "/users/self/contacts/auth-request", NULL, skypeweb_got_authrequests, NULL, TRUE);
+ return TRUE;
+}
+
+
+void
+skypeweb_add_buddy_with_invite(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char* message)
+{
+ SkypeWebAccount *sa = pc->proto_data;
+ gchar *url, *postdata;
+
+ url = g_strdup_printf("/users/self/contacts/auth-request/%s", purple_url_encode(buddy->name));
+ postdata = g_strdup_printf("greeting=%s", message ? purple_url_encode(message) : "");
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_PUT | SKYPEWEB_METHOD_SSL, SKYPEWEB_CONTACTS_HOST, url, postdata, NULL, NULL, TRUE);
+
+ g_free(postdata);
+ g_free(url);
+}
+
+void
+skypeweb_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+{
+ skypeweb_add_buddy_with_invite(gc, buddy, group, _("Please authorize me so I can add you to my buddy list."));
+}
+
+void
+skypeweb_buddy_remove(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group)
+{
+ SkypeWebAccount *sa = pc->proto_data;
+
+} \ No newline at end of file
diff --git a/skypeweb/skypeweb_contacts.h b/skypeweb/skypeweb_contacts.h
new file mode 100644
index 0000000..c2e8095
--- /dev/null
+++ b/skypeweb/skypeweb_contacts.h
@@ -0,0 +1,21 @@
+
+#ifndef SKYPEWEB_CONTACTS_H
+#define SKYPEWEB_CONTACTS_H
+
+#include "libskypeweb.h"
+
+void skypeweb_get_icon(PurpleBuddy *buddy);
+void skypeweb_search_users(PurplePluginAction *action);
+
+void skypeweb_get_friend_profiles(SkypeWebAccount *sa, GSList *contacts);
+void skypeweb_get_friend_profile(SkypeWebAccount *sa, const gchar *who);
+
+void skypeweb_get_friend_list(SkypeWebAccount *sa);
+
+void skypeweb_buddy_remove(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group);
+void skypeweb_add_buddy_with_invite(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char* message);
+void skypeweb_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group);
+
+gboolean skypeweb_check_authrequests(SkypeWebAccount *sa);
+
+#endif /* SKYPEWEB_CONTACTS_H */
diff --git a/skypeweb/skypeweb_login.c b/skypeweb/skypeweb_login.c
new file mode 100644
index 0000000..fe42d50
--- /dev/null
+++ b/skypeweb/skypeweb_login.c
@@ -0,0 +1,108 @@
+#include "skypeweb_login.h"
+#include "skypeweb_util.h"
+
+static void
+skypeweb_login_did_auth(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+{
+ gchar *refresh_token;
+ SkypeWebAccount *sa = user_data;
+
+ if (url_text == NULL) {
+ url_text = url_data->webdata;
+ len = url_data->data_len;
+ }
+
+ refresh_token = skypeweb_string_get_chunk(url_text, len, "Set-Cookie: refresh-token=", ";");
+ if (refresh_token == NULL) {
+ purple_debug_info("skypeweb", "login response was %s\r\n", url_text);
+ purple_connection_error (sa->pc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ _("Failed getting Skype Token"));
+ return;
+ }
+
+ sa->skype_token = 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)
+{
+ SkypeWebAccount *sa = user_data;
+ PurpleAccount *account = sa->account;
+ gchar *pie;
+ gchar *etm;
+ const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST "/login?message=signin_continue";
+ GString *postdata;
+ gchar *request;
+ struct timezone tz;
+ guint tzhours, tzminutes;
+ PurpleUtilFetchUrlData *requestdata;
+
+ gettimeofday(NULL, &tz);
+ tzminutes = tz.tz_minuteswest;
+ if (tzminutes < 0) tzminutes = -tzminutes;
+ tzhours = tzminutes / 60;
+ tzminutes -= tzhours * 60;
+
+ pie = skypeweb_string_get_chunk(url_text, len, "=\"pie\" value=\"", "\"");
+ if (!pie) {
+ return;
+ }
+
+ etm = skypeweb_string_get_chunk(url_text, len, "=\"etm\" value=\"", "\"");
+ if (!etm) {
+ return;
+ }
+
+
+ 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, "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));
+ g_string_append_printf(postdata, "js_time=%d&", time(NULL));
+ g_string_append(postdata, "return_url=https://web.skype.com/");
+
+ request = g_strdup_printf("POST /login?message=signin_continue 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: %" G_GSIZE_FORMAT "\r\n\r\n%s",
+ strlen(postdata->str), postdata->str);
+
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ requestdata = purple_util_fetch_url_request(sa->account, login_url, TRUE, NULL, FALSE, request, TRUE, -1, skypeweb_login_did_auth, sa);
+#else
+ requestdata = purple_util_fetch_url_request(login_url, TRUE, NULL, FALSE, request, TRUE, skypeweb_login_did_auth, sa);
+#endif
+ requestdata->num_times_redirected = 10; /* Prevent following redirects */
+
+ g_string_free(postdata, TRUE);
+ g_free(request);
+}
+
+void
+skypeweb_begin_web_login(SkypeWebAccount *sa)
+{
+ const gchar *login_url = "https://" SKYPEWEB_LOGIN_HOST "/login";
+
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ purple_util_fetch_url_request(sa->account, login_url, TRUE, NULL, FALSE, NULL, FALSE, -1, skypeweb_login_got_pie, sa);
+#else
+ purple_util_fetch_url_request(login_url, TRUE, NULL, FALSE, NULL, FALSE, skypeweb_login_got_pie, sa);
+#endif
+
+ purple_connection_set_state(sa->pc, PURPLE_CONNECTING);
+ purple_connection_update_progress(sa->pc, _("Connecting"), 1, 3);
+}
+
+void
+skypeweb_logout(SkypeWebAccount *sa)
+{
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_GET | SKYPEWEB_METHOD_SSL, SKYPEWEB_LOGIN_HOST, "/logout", NULL, NULL, NULL, TRUE);
+}
diff --git a/skypeweb/skypeweb_login.h b/skypeweb/skypeweb_login.h
new file mode 100644
index 0000000..f7812e5
--- /dev/null
+++ b/skypeweb/skypeweb_login.h
@@ -0,0 +1,13 @@
+
+#ifndef SKYPEWEB_LOGIN_H
+#define SKYPEWEB_LOGIN_H
+
+#include "libskypeweb.h"
+#include "skypeweb_connection.h"
+
+#include <util.h>
+
+void skypeweb_logout(SkypeWebAccount *sa);
+void skypeweb_begin_web_login(SkypeWebAccount *sa);
+
+#endif /* SKYPEWEB_LOGIN_H */
diff --git a/skypeweb/skypeweb_messages.c b/skypeweb/skypeweb_messages.c
new file mode 100644
index 0000000..2f1575b
--- /dev/null
+++ b/skypeweb/skypeweb_messages.c
@@ -0,0 +1,325 @@
+#include "skypeweb_messages.h"
+#include "skypeweb_util.h"
+#include "skypeweb_connection.h"
+
+static void
+process_userpresence_resource(SkypeWebAccount *sa, JsonObject *resource)
+{
+ const gchar *selfLink = json_object_get_string_member(resource, "selfLink");
+ const gchar *status = json_object_get_string_member(resource, "status");
+ const gchar *from;
+
+ from = skypeweb_contact_url_to_name(selfLink);
+
+ purple_prpl_got_user_status(sa->account, from, status, NULL);
+}
+
+static void
+process_message_resource(SkypeWebAccount *sa, JsonObject *resource)
+{
+ const gchar *messagetype = json_object_get_string_member(resource, "messagetype");
+ const gchar *from = json_object_get_string_member(resource, "from");
+ const gchar *content = json_object_get_string_member(resource, "content");
+ const gchar *composetime = json_object_get_string_member(resource, "composetime");
+ const gchar *conversationLink = json_object_get_string_member(resource, "conversationLink");
+ time_t composetimestamp = purple_str_to_time(composetime, TRUE, NULL, NULL, NULL);
+ gchar **messagetype_parts;
+
+ messagetype_parts = g_strsplit(messagetype, "/", -1);
+
+ from = skypeweb_contact_url_to_name(from);
+
+ if (json_object_has_member(resource, "threadtopic"))
+ return; //TODO multi user chat threads's
+
+ if (g_str_equal(messagetype_parts[0], "Control")) {
+ if (g_str_equal(messagetype_parts[1], "ClearTyping")) {
+ serv_got_typing(sa->pc, from, 20, PURPLE_NOT_TYPING);
+ } else if (g_str_equal(messagetype_parts[1], "Typing")) {
+ serv_got_typing(sa->pc, from, 20, PURPLE_TYPING);
+ }
+ } else if (g_str_equal(messagetype, "RichText") || g_str_equal(messagetype, "Text")) {
+ gchar *html;
+ if (g_str_equal(messagetype, "Text")) {
+ html = purple_markup_escape_text(content, -1);
+ } else {
+ html = g_strdup(content);
+ }
+ if (g_str_equal(sa->account->username, from)) {
+ from = skypeweb_contact_url_to_name(conversationLink);
+ PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, sa->account);
+ if (conv == NULL)
+ {
+ conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, sa->account, from);
+ }
+ purple_conversation_write(conv, from, html, PURPLE_MESSAGE_SEND, composetimestamp);
+ } else {
+ serv_got_im(sa->pc, from, html, PURPLE_MESSAGE_RECV, composetimestamp);
+ }
+ g_free(html);
+ } else {
+ purple_debug_warning("skypeweb", "Unknown message resource messagetype '%s'\n", messagetype);
+ }
+
+ g_strfreev(messagetype_parts);
+}
+
+gboolean
+skypeweb_timeout(gpointer userdata)
+{
+ SkypeWebAccount *sa = userdata;
+ skypeweb_poll(sa);
+
+ // If no response within 3 minutes, assume connection lost and try again
+ purple_timeout_remove(sa->watchdog_timeout);
+ sa->watchdog_timeout = purple_timeout_add_seconds(3 * 60, skypeweb_timeout, sa);
+
+ return FALSE;
+}
+
+static void
+skypeweb_poll_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user_data)
+{
+ JsonArray *messages = NULL;
+ guint index, length;
+ JsonObject *obj;
+
+ obj = json_node_get_object(node);
+
+ if (json_object_has_member(obj, "eventMessages"))
+ messages = json_object_get_array_member(obj, "eventMessages");
+
+ if (messages != NULL) {
+ length = json_array_get_length(messages);
+ for(index = 0; index < length; index++)
+ {
+ JsonObject *message = json_array_get_object_element(messages, index);
+ const gchar *resourceType = json_object_get_string_member(message, "resourceType");
+ JsonObject *resource = json_object_get_object_member(message, "resource");
+
+ if (g_str_equal(resourceType, "NewMessage"))
+ {
+ process_message_resource(sa, resource);
+ } else if (g_str_equal(resourceType, "UserPresence"))
+ {
+ process_userpresence_resource(sa, resource);
+ } else if (g_str_equal(resourceType, "EndpointPresence"))
+ {
+ //process_endpointpresence_resource(sa, resource);
+ } else if (g_str_equal(resourceType, "ConversationUpdate"))
+ {
+ //process_conversation_resource(sa, resource);
+ } else if (g_str_equal(resourceType, "ThreadUpdate"))
+ {
+ //process_thread_resource(sa, resource);
+ }
+ }
+ }
+
+ //TODO record id of highest recieved id to make sure we dont process the same id twice
+
+ sa->poll_timeout = purple_timeout_add_seconds(1, skypeweb_timeout, sa);
+
+}
+
+void
+skypeweb_poll(SkypeWebAccount *sa)
+{
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_POST | SKYPEWEB_METHOD_SSL, SKYPEWEB_MESSAGES_HOST, "/v1/users/ME/endpoints/SELF/subscriptions/0/poll", NULL, skypeweb_poll_cb, NULL, TRUE);
+}
+
+
+static void
+skypeweb_subscribe_cb(SkypeWebAccount *sa, JsonNode *node, gpointer user_data)
+{
+ //{"id":"messagingService","type":"EndpointPresenceDoc","selfLink":"uri","privateInfo":{"epname":"skype"},"publicInfo":{"capabilities":"video|audio","type":1,"skypeNameVersion":"908/1.0.20/swx-skype.com","nodeInfo":"xx","version":"908/1.0.20"}}
+ // /v1/users/ME/endpoints/%7B60da4fa8-79f0-445b-846f-1f1e0116ecb5%7D/presenceDocs/messagingService
+
+ skypeweb_do_all_the_things(sa);
+}
+
+static void
+skypeweb_subscribe(SkypeWebAccount *sa)
+{
+ JsonObject *obj;
+ JsonArray *interested;
+ gchar *post;
+
+ interested = json_array_new();
+ json_array_add_string_element(interested, "/v1/users/ME/conversations/ALL/properties");
+ json_array_add_string_element(interested, "/v1/users/ME/conversations/ALL/messages");
+ json_array_add_string_element(interested, "/v1/users/ME/contacts/ALL");
+ //json_array_add_string_element(interested, "/v1/users/ME/conversations/ALL/properties"); //TODO
+
+ obj = json_object_new();
+ json_object_set_array_member(obj, "interestedResources", interested);
+ json_object_set_string_member(obj, "template", "raw");
+ json_object_set_string_member(obj, "channelType", "httpLongPoll");
+
+ post = skypeweb_jsonobj_to_string(obj);
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_POST | SKYPEWEB_METHOD_SSL, SKYPEWEB_MESSAGES_HOST, "/v1/users/ME/endpoints/SELF/subscriptions", post, skypeweb_subscribe_cb, NULL, TRUE);
+
+ g_free(post);
+ json_object_unref(obj);
+ json_array_unref(interested);
+}
+
+static void
+skypeweb_got_registration_token(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
+{
+ gchar *registration_token;
+ //gchar *endpointId;
+ SkypeWebAccount *sa = user_data;
+
+ if (url_text == NULL) {
+ url_text = url_data->webdata;
+ len = url_data->data_len;
+ }
+
+ registration_token = skypeweb_string_get_chunk(url_text, len, "Set-RegistrationToken: ", ";");
+ //endpointId = skypeweb_string_get_chunk(url_text, len, "endpointId=", "\r\n");
+
+ if (registration_token == NULL) {
+ purple_connection_error (sa->pc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ _("Failed getting Registration Token"));
+ return;
+ }
+ purple_debug_info("skypeweb", "New RegistrationToken is %s\n", registration_token);
+
+ sa->registration_token = registration_token;
+
+ skypeweb_subscribe(sa);
+}
+
+void
+skypeweb_get_registration_token(SkypeWebAccount *sa)
+{
+ const gchar *messages_url = "https://" SKYPEWEB_MESSAGES_HOST "/v1/users/ME/endpoints";
+ gchar *request;
+ gchar *curtime;
+ gchar *response;
+ PurpleUtilFetchUrlData *requestdata;
+
+ g_free(sa->registration_token);
+ sa->registration_token = NULL;
+
+ curtime = g_strdup_printf("%d", (int) time(NULL));
+ response = skypeweb_hmac_sha256(curtime);
+
+ request = g_strdup_printf("POST /v1/users/ME/endpoints HTTP/1.0\r\n"
+ "Connection: close\r\n"
+ "Accept: */*\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=swx-skype.com; clientVer=908/1.0.0.20\r\n"
+ "Host: " SKYPEWEB_MESSAGES_HOST "\r\n"
+ "Content-Type: application/json\r\n"
+ "Authentication: skypetoken=%s\r\n"
+ "Content-Length: 2\r\n\r\n{}",
+ curtime, response, sa->skype_token);
+
+ purple_debug_info("skypeweb", "reg token request is %s\n", request);
+
+#if PURPLE_VERSION_CHECK(3, 0, 0)
+ requestdata = purple_util_fetch_url_request(sa->account, messages_url, TRUE, NULL, FALSE, request, TRUE, -1, skypeweb_got_registration_token, sa);
+#else
+ requestdata = purple_util_fetch_url_request(messages_url, TRUE, NULL, FALSE, request, TRUE, skypeweb_got_registration_token, sa);
+#endif
+ requestdata->num_times_redirected = 10; /* Prevent following redirects */
+
+ g_free(request);
+ g_free(curtime);
+ g_free(response);
+}
+
+
+
+
+
+guint
+skypeweb_send_typing(PurpleConnection *pc, const gchar *name, PurpleTypingState state)
+{
+ SkypeWebAccount *sa = pc->proto_data;
+ gchar *post, *url;
+ JsonObject *obj;
+
+ url = g_strdup_printf("/v1/users/ME/conversations/8:%s/messages", purple_url_encode(name));
+
+ obj = json_object_new();
+ json_object_set_int_member(obj, "clientmessageid", time(NULL));
+ json_object_set_string_member(obj, "content", "");
+ json_object_set_string_member(obj, "messagetype", state == PURPLE_TYPING ? "Control/Typing" : "Control/ClearTyping");
+ json_object_set_string_member(obj, "contenttype", "text");
+
+ post = skypeweb_jsonobj_to_string(obj);
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_POST | SKYPEWEB_METHOD_SSL, SKYPEWEB_MESSAGES_HOST, url, post, NULL, NULL, TRUE);
+
+ g_free(post);
+ json_object_unref(obj);
+ g_free(url);
+
+ return 20;
+}
+
+
+
+
+void
+skypeweb_set_status(PurpleAccount *account, PurpleStatus *status)
+{
+ PurpleConnection *pc = purple_account_get_connection(account);
+ SkypeWebAccount *sa = pc->proto_data;
+ gchar *post;
+
+ post = g_strdup_printf("{\"status\":\"%s\"}", purple_status_get_id(status));
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_PUT | SKYPEWEB_METHOD_SSL, SKYPEWEB_MESSAGES_HOST, "/v1/users/ME/presenceDocs/messagingService", post, NULL, NULL, TRUE);
+
+ g_free(post);
+}
+
+void
+skypeweb_set_idle(PurpleConnection *pc, int time)
+{
+ SkypeWebAccount *sa = pc->proto_data;
+
+ if (time < 30) {
+ //skypeweb_set_status(pc->account, active status);
+ } else {
+ //skypeweb_set_status(pc->account, idle status);
+ }
+}
+
+
+
+
+
+
+gint
+skypeweb_send_im(PurpleConnection *pc, const gchar *who, const gchar *msg,
+ PurpleMessageFlags flags)
+{
+ SkypeWebAccount *sa = pc->proto_data;
+ gchar *post, *url;
+ JsonObject *obj;
+
+ url = g_strdup_printf("/v1/users/ME/conversations/8:%s/messages", purple_url_encode(who));
+
+ obj = json_object_new();
+ json_object_set_int_member(obj, "clientmessageid", g_random_int_range(100000, 999999));
+ json_object_set_string_member(obj, "content", msg);
+ json_object_set_string_member(obj, "messagetype", "RichText");
+ json_object_set_string_member(obj, "contenttype", "text");
+
+ post = skypeweb_jsonobj_to_string(obj);
+
+ skypeweb_post_or_get(sa, SKYPEWEB_METHOD_POST | SKYPEWEB_METHOD_SSL, SKYPEWEB_MESSAGES_HOST, url, post, NULL, NULL, TRUE);
+
+ g_free(post);
+ json_object_unref(obj);
+ g_free(url);
+
+ return 1;
+} \ No newline at end of file
diff --git a/skypeweb/skypeweb_messages.h b/skypeweb/skypeweb_messages.h
new file mode 100644
index 0000000..2fd39e0
--- /dev/null
+++ b/skypeweb/skypeweb_messages.h
@@ -0,0 +1,14 @@
+
+#ifndef SKYPEWEB_MESSAGES_H
+#define SKYPEWEB_MESSAGES_H
+
+#include "libskypeweb.h"
+
+gint skypeweb_send_im(PurpleConnection *pc, const gchar *who, const gchar *msg, PurpleMessageFlags flags);
+void skypeweb_set_idle(PurpleConnection *pc, int time);
+void skypeweb_set_status(PurpleAccount *account, PurpleStatus *status);
+guint skypeweb_send_typing(PurpleConnection *pc, const gchar *name, PurpleTypingState state);
+void skypeweb_poll(SkypeWebAccount *sa);
+void skypeweb_get_registration_token(SkypeWebAccount *sa);
+
+#endif /* SKYPEWEB_MESSAGES_H */
diff --git a/skypeweb/skypeweb_util.c b/skypeweb/skypeweb_util.c
new file mode 100644
index 0000000..3c53502
--- /dev/null
+++ b/skypeweb/skypeweb_util.c
@@ -0,0 +1,170 @@
+#include "skypeweb_util.h"
+
+#include <cipher.h>
+
+gchar *
+skypeweb_string_get_chunk(const gchar *haystack, gsize len, const gchar *start, const gchar *end)
+{
+ const gchar *chunk_start, *chunk_end;
+ g_return_val_if_fail(haystack && start && end, NULL);
+
+ if (len > 0) {
+ chunk_start = g_strstr_len(haystack, len, start);
+ } else {
+ chunk_start = strstr(haystack, start);
+ }
+ g_return_val_if_fail(chunk_start, NULL);
+ chunk_start += strlen(start);
+
+ if (len > 0) {
+ chunk_end = g_strstr_len(chunk_start, len - (chunk_start - haystack), end);
+ } else {
+ chunk_end = strstr(chunk_start, end);
+ }
+ g_return_val_if_fail(chunk_end, NULL);
+
+ return g_strndup(chunk_start, chunk_end - chunk_start);
+}
+
+gchar *
+skypeweb_jsonobj_to_string(JsonObject *jsonobj)
+{
+ JsonGenerator *generator;
+ JsonNode *root;
+ gchar *string;
+
+ root = json_node_new(JSON_NODE_OBJECT);
+ json_node_set_object(root, jsonobj);
+
+ generator = json_generator_new();
+ json_generator_set_root(generator, root);
+
+ string = json_generator_to_data(generator, NULL);
+
+ g_object_unref(generator);
+ json_node_free(root);
+
+ return string;
+}
+
+/** turn https://bay-client-s.gateway.messenger.live.com/v1/users/ME/contacts/8:eionrobb
+ or https://bay-client-s.gateway.messenger.live.com/v1/users/8:eionrobb/presenceDocs/messagingService
+ into eionrobb
+*/
+const gchar *
+skypeweb_contact_url_to_name(const gchar *url)
+{
+ static gchar *tempname = NULL;
+ const gchar *start, *end;
+
+ start = g_strrstr(url, "/8:");
+ if (!start) return NULL;
+ start = start + 3;
+
+ if ((end = strchr(start, '/'))) {
+ g_free(tempname);
+ tempname = g_strndup(start, end - start);
+ return tempname;
+ }
+
+ return start;
+}
+
+/** Blatantly stolen from MSN prpl, with super-secret SHA256 change! */
+#define BUFSIZE 256
+char *
+skypeweb_hmac_sha256(char *input)
+{
+ PurpleCipher *cipher;
+ PurpleCipherContext *context;
+ const guchar productKey[] = SKYPEWEB_LOCKANDKEY_SECRET;
+ const guchar productID[] = SKYPEWEB_LOCKANDKEY_APPID;
+ const char hexChars[] = "0123456789abcdef";
+ char buf[BUFSIZE];
+ unsigned char sha256Hash[32];
+ unsigned char *newHash;
+ unsigned int *sha256Parts;
+ unsigned int *chlStringParts;
+ unsigned int newHashParts[5];
+ gchar *output;
+
+ long long nHigh = 0, nLow = 0;
+
+ int len;
+ int i;
+
+ /* Create the SHA256 hash by using Purple SHA256 algorithm */
+ cipher = purple_ciphers_find_cipher("sha256");
+ context = purple_cipher_context_new(cipher, NULL);
+
+ purple_cipher_context_append(context, (guchar *)input, strlen(input));
+ purple_cipher_context_append(context, productKey, sizeof(productKey) - 1);
+ purple_cipher_context_digest(context, sizeof(sha256Hash), sha256Hash, NULL);
+ purple_cipher_context_destroy(context);
+
+ /* Split it into four integers */
+ sha256Parts = (unsigned int *)sha256Hash;
+ for (i = 0; i < 4; i++) {
+ /* adjust endianess */
+ sha256Parts[i] = GUINT_TO_LE(sha256Parts[i]);
+
+ /* & each integer with 0x7FFFFFFF */
+ /* and save one unmodified array for later */
+ newHashParts[i] = sha256Parts[i];
+ sha256Parts[i] &= 0x7FFFFFFF;
+ }
+
+ /* make a new string and pad with '0' to length that's a multiple of 8 */
+ snprintf(buf, BUFSIZE - 5, "%s%s", input, productID);
+ len = strlen(buf);
+ if ((len % 8) != 0) {
+ int fix = 8 - (len % 8);
+ memset(&buf[len], '0', fix);
+ buf[len + fix] = '\0';
+ len += fix;
+ }
+
+ /* split into integers */
+ chlStringParts = (unsigned int *)buf;
+
+ /* this is magic */
+ for (i = 0; i < (len / 4); i += 2) {
+ long long temp;
+
+ chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]);
+ chlStringParts[i + 1] = GUINT_TO_LE(chlStringParts[i + 1]);
+
+ temp = (0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF;
+ temp = (sha256Parts[0] * (temp + nLow) + sha256Parts[1]) % 0x7FFFFFFF;
+ nHigh += temp;
+
+ temp = ((long long)chlStringParts[i + 1] + temp) % 0x7FFFFFFF;
+ nLow = (sha256Parts[2] * temp + sha256Parts[3]) % 0x7FFFFFFF;
+ nHigh += nLow;
+ }
+ nLow = (nLow + sha256Parts[1]) % 0x7FFFFFFF;
+ nHigh = (nHigh + sha256Parts[3]) % 0x7FFFFFFF;
+
+ newHashParts[0] ^= nLow;
+ newHashParts[1] ^= nHigh;
+ newHashParts[2] ^= nLow;
+ newHashParts[3] ^= nHigh;
+
+ /* adjust endianness */
+ for(i = 0; i < 4; i++)
+ newHashParts[i] = GUINT_TO_LE(newHashParts[i]);
+
+ /* make a string of the parts */
+ newHash = (unsigned char *)newHashParts;
+
+ /* convert to hexadecimal */
+ output = g_new0(gchar, 33);
+ for (i = 0; i < 16; i++)
+ {
+ output[i * 2] = hexChars[(newHash[i] >> 4) & 0xF];
+ output[(i * 2) + 1] = hexChars[newHash[i] & 0xF];
+ }
+ output[32] = '\0';
+
+ return output;
+}
diff --git a/skypeweb/skypeweb_util.h b/skypeweb/skypeweb_util.h
new file mode 100644
index 0000000..a9a4af5
--- /dev/null
+++ b/skypeweb/skypeweb_util.h
@@ -0,0 +1,9 @@
+#include "libskypeweb.h"
+
+gchar *skypeweb_string_get_chunk(const gchar *haystack, gsize len, const gchar *start, const gchar *end);
+
+gchar *skypeweb_jsonobj_to_string(JsonObject *jsonobj);
+
+const gchar *skypeweb_contact_url_to_name(const gchar *url);
+
+gchar *skypeweb_hmac_sha256(gchar *input); \ No newline at end of file