Tue, 14 Aug 2012 22:26:33 +0200
Merge hinted string account options from default
--- a/configure.ac Tue Aug 14 22:05:05 2012 +0200 +++ b/configure.ac Tue Aug 14 22:26:33 2012 +0200 @@ -816,6 +816,19 @@ AC_SUBST(LIBXML_LIBS) dnl ####################################################################### +dnl # Check for zlib (required) +dnl ####################################################################### + +PKG_CHECK_MODULES(ZLIB, [zlib >= 1.2.0], , [ + AC_MSG_RESULT(no) + AC_MSG_ERROR([ +You must have zlib >= 1.2.0 development headers installed to build. +])]) + +AC_SUBST(ZLIB_CFLAGS) +AC_SUBST(ZLIB_LIBS) + +dnl ####################################################################### dnl # GConf schemas dnl ####################################################################### AC_PATH_PROG(GCONFTOOL, gconftool-2, no) @@ -1069,13 +1082,14 @@ AC_ARG_WITH(gadu-libs, [AC_HELP_STRING([--with-gadu-libs=DIR], [compile the Gadu-Gadu plugin against the libs in DIR])], [ac_gadu_libs="$withval"], [ac_gadu_libs="no"]) GADU_CFLAGS="" GADU_LIBS="" +GADU_LIBGADU_VERSION=1.11.2 if test -n "$with_gadu_includes" || test -n "$with_gadu_libs"; then gadu_manual_check="yes" else gadu_manual_check="no" fi if test "x$gadu_manual_check" = "xno"; then - PKG_CHECK_MODULES(GADU, [libgadu >= 1.11.0], [ + PKG_CHECK_MODULES(GADU, [libgadu >= $GADU_LIBGADU_VERSION], [ gadu_includes="yes" gadu_libs="yes" ], [ @@ -1107,28 +1121,7 @@ #error "libgadu is not compatible with the GPL when compiled with OpenSSL support." #endif ]])], [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[ -#if GG_DEFAULT_PROTOCOL_VERSION < 0x2e -#error "Your libgadu version is too old. libpurple requires 1.11.0 or higher." -#endif - ]])], [ - AC_MSG_RESULT(yes) - AC_DEFINE([HAVE_LIBGADU], [1], - [Define to 1 if you have libgadu.]) - ], [ - AC_MSG_RESULT(no) - echo - echo - echo "Your supplied copy of libgadu is too old." - echo "Install version 1.11.0 or newer." - echo "Then rerun this ./configure" - echo - echo "Falling back to using our own copy of libgadu" - echo - GADU_LIBS="" - GADU_CFLAGS="" - gadu_libs=no - ]) + AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) echo @@ -1147,6 +1140,35 @@ CPPFLAGS="$CPPFLAGS_save" fi +if test "x$gadu_libs" = "xyes" -a "x$gadu_manual_check" = "xyes"; then + AC_MSG_CHECKING(for supplied libgadu compatibility) + CPPFLAGS_save="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $GADU_CFLAGS" + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[ +#if GG_DEFAULT_PROTOCOL_VERSION < 0x2e +#error "Your libgadu version is too old." +#endif + ]])], [ + AC_MSG_RESULT(yes) + ], [ + AC_MSG_RESULT(no) + echo + echo "Your supplied copy of libgadu is too old." + echo "Install version $GADU_LIBGADU_VERSION or newer." + echo "Then rerun this ./configure" + echo + echo "Falling back to using our own copy of libgadu" + echo + GADU_LIBS="" + GADU_CFLAGS="" + gadu_libs=no + ]) + + CPPFLAGS="$CPPFLAGS_save" +fi + +AM_CONDITIONAL(HAVE_LIBGADU, test "x$gadu_libs" = "xyes") AM_CONDITIONAL(USE_INTERNAL_LIBGADU, test "x$gadu_libs" != "xyes") if test "x$gadu_libs" = "x"; then @@ -1155,6 +1177,7 @@ AC_SUBST(GADU_LIBS) AC_SUBST(GADU_CFLAGS) +AC_SUBST(GADU_LIBGADU_VERSION) AC_ARG_ENABLE(distrib,,,enable_distrib=no) AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes")
--- a/libpurple/eventloop.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/eventloop.c Tue Aug 14 22:26:33 2012 +0200 @@ -91,6 +91,16 @@ } } +int +purple_input_pipe(int pipefd[2]) +{ +#ifdef _WIN32 + return wpurple_input_pipe(pipefd); +#else + return pipe(pipefd); +#endif +} + void purple_eventloop_set_ui_ops(PurpleEventLoopUiOps *ops) {
--- a/libpurple/eventloop.h Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/eventloop.h Tue Aug 14 22:26:33 2012 +0200 @@ -240,6 +240,24 @@ int purple_input_get_error(int fd, int *error); +/** + * Creates a pipe - an unidirectional data channel that can be used for + * interprocess communication. + * + * File descriptors for both ends of pipe will be written into provided array. + * The first one (pipefd[0]) can be used for reading, the second one (pipefd[1]) + * for writing. + * + * On Windows it's simulated by creating a pair of connected sockets, on other + * systems pipe() is used. + * + * @param pipefd Array used to return file descriptors for both ends of pipe. + * + * @return @c 0 on success, @c -1 on error. + */ +int +purple_input_pipe(int pipefd[2]); + /*@}*/
--- a/libpurple/protocols/gg/Makefile.am Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/Makefile.am Tue Aug 14 22:26:33 2012 +0200 @@ -1,13 +1,20 @@ +V=0 + +pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) + EXTRA_DIST = \ Makefile.mingw \ - win32-resolver.c \ - win32-resolver.h \ + lib/COPYING + +if USE_INTERNAL_LIBGADU +INTGGSOURCES = \ lib/common.c \ lib/compat.h \ - lib/COPYING \ + lib/config.h \ lib/dcc.c \ lib/dcc7.c \ lib/debug.c \ + lib/debug.h \ lib/deflate.c \ lib/deflate.h \ lib/encoding.c \ @@ -15,11 +22,9 @@ lib/events.c \ lib/handlers.c \ lib/http.c \ - lib/libgadu.h \ + lib/internal.h \ lib/libgadu.c \ - lib/libgadu-config.h \ - lib/libgadu-debug.h \ - lib/libgadu-internal.h \ + lib/libgadu.h \ lib/message.c \ lib/message.h \ lib/obsolete.c \ @@ -31,38 +36,11 @@ lib/session.h \ lib/sha1.c -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -if USE_INTERNAL_LIBGADU -INTGGSOURCES = \ - lib/common.c \ - lib/compat.h \ - lib/dcc.c \ - lib/dcc7.c \ - lib/debug.c \ - lib/deflate.c \ - lib/deflate.h \ - lib/encoding.c \ - lib/encoding.h \ - lib/events.c \ - lib/handlers.c \ - lib/http.c \ - lib/libgadu.h \ - lib/libgadu.c \ - lib/libgadu-config.h \ - lib/libgadu-internal.h \ - lib/message.c \ - lib/message.h \ - lib/obsolete.c \ - lib/protocol.h \ - lib/pubdir.c \ - lib/pubdir50.c \ - lib/resolver.c \ - lib/resolver.h \ - lib/session.h \ - lib/sha1.c - -INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED -DUSE_INTERNAL_LIBGADU +INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib \ + $(ZLIB_CFLAGS) \ + -DGG_IGNORE_DEPRECATED \ + -DGG_INTERNAL_LIBGADU_VERSION=$(GADU_LIBGADU_VERSION) +GADU_LIBS += $(ZLIB_LIBS) endif if USE_GNUTLS @@ -72,8 +50,8 @@ GGSOURCES = \ $(INTGGSOURCES) \ - gg-utils.h \ - gg-utils.c \ + utils.h \ + utils.c \ confer.h \ confer.c \ search.h \ @@ -81,7 +59,39 @@ buddylist.h \ buddylist.c \ gg.h \ - gg.c + gg.c \ + resolver-purple.h \ + resolver-purple.c \ + image.h \ + image.c \ + account.h \ + account.c \ + deprecated.h \ + deprecated.c \ + purplew.h \ + purplew.c \ + libgaduw.h \ + libgaduw.c \ + avatar.h \ + avatar.c \ + libgadu-events.h \ + libgadu-events.c \ + roster.c \ + roster.h \ + validator.c \ + validator.h \ + xml.c \ + xml.h \ + multilogon.c \ + multilogon.h \ + status.c \ + status.h \ + oauth/oauth.c \ + oauth/oauth.h \ + oauth/oauth-parameter.c \ + oauth/oauth-parameter.h \ + oauth/oauth-purple.c \ + oauth/oauth-purple.h AM_CFLAGS = $(st) @@ -105,9 +115,9 @@ endif AM_CPPFLAGS = \ + -Wall -Wextra -Werror \ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(INTGG_CFLAGS) \ $(GLIB_CFLAGS) \ $(DEBUG_CFLAGS) -
--- a/libpurple/protocols/gg/Makefile.mingw Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/Makefile.mingw Tue Aug 14 22:26:33 2012 +0200 @@ -24,14 +24,14 @@ ## ## INCLUDE PATHS ## -INCLUDE_PATHS += -I. \ +INCLUDE_PATHS +=\ + -I$(PIDGIN_TREE_TOP) \ + -I$(PURPLE_TOP) \ + -I$(PURPLE_TOP)/win32 \ -I./lib \ -I$(GTK_TOP)/include \ -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP) \ - -I$(PURPLE_TOP)/win32 \ - -I$(PIDGIN_TREE_TOP) + -I$(GTK_TOP)/lib/glib-2.0/include LIB_PATHS += -L$(GTK_TOP)/lib \ -L$(PURPLE_TOP) \ @@ -60,8 +60,8 @@ confer.c \ gg.c \ search.c \ - gg-utils.c \ - win32-resolver.c + utils.c \ + resolver-purple.c OBJECTS = $(C_SRC:%.c=%.o)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/account.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,620 @@ +#include "account.h" + +#include <libgadu.h> +#include <debug.h> + +#include "deprecated.h" +#include "purplew.h" +#include "utils.h" +#include "libgaduw.h" +#include "validator.h" + +/******************************************************************************* + * Token requesting. + ******************************************************************************/ + +typedef struct +{ + ggp_account_token_cb callback; + PurpleConnection *gc; + void *user_data; +} ggp_account_token_reqdata; + +static void ggp_account_token_response(struct gg_http *h, gboolean success, + gboolean cancelled, gpointer _reqdata); + +/******************************************************************************/ + +void ggp_account_token_free(ggp_account_token *token) +{ + if (!token) + return; + g_free(token->id); + g_free(token->data); + g_free(token); +} + +void ggp_account_token_request(PurpleConnection *gc, + ggp_account_token_cb callback, void *user_data) +{ + struct gg_http *h; + ggp_account_token_reqdata *reqdata; + + purple_debug_info("gg", "ggp_account_token_request: " + "requesting token...\n"); + + if (!ggp_deprecated_setup_proxy(gc)) + { + callback(gc, NULL, user_data); + return; + } + + h = gg_token(TRUE); + + if (!h) + { + callback(gc, NULL, user_data); + return; + } + + reqdata = g_new(ggp_account_token_reqdata, 1); + reqdata->callback = callback; + reqdata->gc = gc; + reqdata->user_data = user_data; + ggp_libgaduw_http_watch(gc, h, ggp_account_token_response, reqdata, + TRUE); +} + +static void ggp_account_token_response(struct gg_http *h, gboolean success, + gboolean cancelled, gpointer _reqdata) +{ + ggp_account_token_reqdata *reqdata = _reqdata; + struct gg_token *token_info; + ggp_account_token *token = NULL; + + g_assert(!(success && cancelled)); + + if (cancelled) + purple_debug_info("gg", "ggp_account_token_handler: " + "cancelled\n"); + else if (success) + { + purple_debug_info("gg", "ggp_account_token_handler: " + "got token\n"); + + token = g_new(ggp_account_token, 1); + + token_info = h->data; + token->id = g_strdup(token_info->tokenid); + token->size = h->body_size; + token->data = g_memdup(h->body, token->size); + token->length = token_info->length; + } + else + { + purple_debug_error("gg", "ggp_account_token_handler: error\n"); + purple_notify_error( + purple_connection_get_account(reqdata->gc), + _("Token Error"), + _("Unable to fetch the token."), NULL); + } + + reqdata->callback(reqdata->gc, token, reqdata->user_data); + g_free(reqdata); +} + +gboolean ggp_account_token_validate(ggp_account_token *token, + const gchar *value) +{ + if (strlen(value) != token->length) + return FALSE; + return g_regex_match_simple("^[a-zA-Z0-9]+$", value, 0, 0); +} + +/******************************************************************************* + * New account registration. + ******************************************************************************/ + +typedef struct +{ + ggp_account_token *token; + PurpleConnection *gc; + + gchar *email; + gchar *password; + gchar *token_value; + gboolean password_remember; +} ggp_account_register_data; + +static void ggp_account_register_dialog(PurpleConnection *gc, + ggp_account_token *token, gpointer _register_data); +static void ggp_account_register_dialog_ok( + ggp_account_register_data *register_data, PurpleRequestFields *fields); +#if 0 +static void ggp_account_register_dialog_invalid( + ggp_account_register_data *register_data, const gchar *message); +#endif +static void ggp_account_register_dialog_cancel( + ggp_account_register_data *register_data, PurpleRequestFields *fields); +static void ggp_account_register_response(struct gg_http *h, gboolean success, + gboolean cancelled, gpointer _reqdata); +static void ggp_account_register_completed( + ggp_account_register_data *register_data, gboolean success); + +#define GGP_ACCOUNT_REGISTER_TITLE _("Register New Gadu-Gadu Account") + +/******************************************************************************/ + +void ggp_account_register(PurpleAccount *account) +{ + PurpleConnection *gc = purple_account_get_connection(account); + ggp_account_register_data *register_data; + + purple_debug_info("gg", "ggp_account_register\n"); + + register_data = g_new0(ggp_account_register_data, 1); + register_data->gc = gc; + register_data->password_remember = TRUE; + + ggp_account_token_request(gc, ggp_account_register_dialog, + register_data); +} + +static void ggp_account_register_dialog(PurpleConnection *gc, + ggp_account_token *token, gpointer _register_data) +{ + PurpleRequestFields *fields; + PurpleRequestFieldGroup *main_group, *password_group, *token_group; + PurpleRequestField *field, *field_password; + ggp_account_register_data *register_data = _register_data; + + purple_debug_info("gg", "ggp_account_register_dialog(%x, %x, %x)\n", + (unsigned int)gc, (unsigned int)token, + (unsigned int)_register_data); + if (!token) + { + ggp_account_register_completed(register_data, FALSE); + return; + } + + fields = purple_request_fields_new(); + main_group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, main_group); + + field = purple_request_field_string_new("email", _("Email"), + register_data->email, FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_set_validator(field, + purple_request_field_email_validator, NULL); + purple_request_field_group_add_field(main_group, field); + + password_group = purple_request_field_group_new(_("Password")); + purple_request_fields_add_group(fields, password_group); + + field = purple_request_field_string_new("password1", _("Password"), + register_data->password, FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_string_set_masked(field, TRUE); + purple_request_field_set_validator(field, ggp_validator_password, NULL); + purple_request_field_group_add_field(password_group, field); + field_password = field; + + field = purple_request_field_string_new("password2", + _("Password (again)"), register_data->password, FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_string_set_masked(field, TRUE); + purple_request_field_set_validator(field, ggp_validator_password_equal, + field_password); + purple_request_field_group_add_field(password_group, field); + + field = purple_request_field_bool_new("password_remember", + _("Remember password"), register_data->password_remember); + purple_request_field_group_add_field(password_group, field); + + token_group = purple_request_field_group_new(_("Captcha")); + purple_request_fields_add_group(fields, token_group); + + field = purple_request_field_string_new("token_value", + _("Enter text from image below"), register_data->token_value, + FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_set_validator(field, ggp_validator_token, token); + purple_request_field_group_add_field(token_group, field); + purple_debug_info("gg", "token set %p\n", register_data->token); + + field = purple_request_field_image_new("token_image", _("Captcha"), + token->data, token->size); + purple_request_field_group_add_field(token_group, field); + + register_data->token = token; + + purple_request_fields(gc, + GGP_ACCOUNT_REGISTER_TITLE, + GGP_ACCOUNT_REGISTER_TITLE, + _("Please, fill in the following fields"), fields, + _("OK"), G_CALLBACK(ggp_account_register_dialog_ok), + _("Cancel"), G_CALLBACK(ggp_account_register_dialog_cancel), + purple_connection_get_account(gc), NULL, NULL, register_data); +} + +static void ggp_account_register_dialog_cancel( + ggp_account_register_data *register_data, PurpleRequestFields *fields) +{ + purple_debug_info("gg", "ggp_account_register_dialog_cancel(%x, %x)\n", + (unsigned int)register_data, (unsigned int)fields); + + ggp_account_register_completed(register_data, FALSE); +} + +static void ggp_account_register_dialog_ok( + ggp_account_register_data *register_data, PurpleRequestFields *fields) +{ + struct gg_http *h; + + purple_debug_misc("gg", "ggp_account_register_dialog_ok(%x, %x)\n", + (unsigned int)register_data, (unsigned int)fields); + + g_free(register_data->email); + g_free(register_data->password); + g_free(register_data->token_value); + + register_data->email = g_strdup( + purple_request_fields_get_string(fields, "email")); + register_data->password = g_strdup( + purple_request_fields_get_string(fields, "password1")); + register_data->password_remember = + purple_request_fields_get_bool(fields, "password_remember"); + register_data->token_value = g_strdup( + purple_request_fields_get_string(fields, "token_value")); + + g_assert(register_data->email != NULL); + g_assert(register_data->password != NULL); + g_assert(register_data->token_value != NULL); + + h = gg_register3(register_data->email, register_data->password, + register_data->token->id, register_data->token_value, TRUE); + + ggp_libgaduw_http_watch(register_data->gc, h, + ggp_account_register_response, register_data, TRUE); +} + +#if 0 +// libgadu 1.12.x: use it for invalid token +static void ggp_account_register_dialog_invalid( + ggp_account_register_data *register_data, const gchar *message) +{ + purple_debug_warning("gg", "ggp_account_register_dialog_invalid: %s\n", + message); + ggp_account_register_dialog(register_data->gc, register_data->token, + register_data); + purple_notify_error(purple_connection_get_account(register_data->gc), + GGP_ACCOUNT_REGISTER_TITLE, message, NULL); +} +#endif + +static void ggp_account_register_response(struct gg_http *h, gboolean success, + gboolean cancelled, gpointer _register_data) +{ + ggp_account_register_data *register_data = _register_data; + PurpleAccount *account = + purple_connection_get_account(register_data->gc); + struct gg_pubdir *register_result = h->data; + uin_t uin; + gchar *tmp; + + g_assert(!(success && cancelled)); + + if (cancelled) + { + purple_debug_info("gg", "ggp_account_register_response: " + "cancelled\n"); + ggp_account_register_completed(register_data, FALSE); + return; + } + if (!success || !register_result->success) + { + //TODO (libgadu 1.12.x): check register_result->error + purple_debug_error("gg", "ggp_account_register_response: " + "error\n"); + purple_notify_error(NULL, + GGP_ACCOUNT_REGISTER_TITLE, + _("Unable to register new account. " + "An unknown error occurred."), NULL); + ggp_account_register_completed(register_data, FALSE); + return; + } + + uin = register_result->uin; + purple_debug_info("gg", "ggp_account_register_response: " + "registered uin %u\n", uin); + + purple_account_set_username(account, ggp_uin_to_str(uin)); + purple_account_set_remember_password(account, + register_data->password_remember); + purple_account_set_password(account, register_data->password); + + tmp = g_strdup_printf(_("Your new GG number: %u."), uin); + purple_notify_info(account, GGP_ACCOUNT_REGISTER_TITLE, + _("Registration completed successfully!"), tmp); + g_free(tmp); + + ggp_account_register_completed(register_data, TRUE); +} + +static void ggp_account_register_completed( + ggp_account_register_data *register_data, gboolean success) +{ + PurpleAccount *account = + purple_connection_get_account(register_data->gc); + + purple_debug_misc("gg", "ggp_account_register_completed: %d\n", + success); + + g_free(register_data->email); + g_free(register_data->password); + g_free(register_data->token_value); + ggp_account_token_free(register_data->token); + g_free(register_data); + + purple_account_disconnect(account); + purple_account_register_completed(account, success); +} + +/******************************************************************************* + * Password change. + ******************************************************************************/ + +typedef struct +{ + ggp_account_token *token; + PurpleConnection *gc; + + gchar *email; + gchar *password_current; + gchar *password_new; + gchar *token_value; +} ggp_account_chpass_data; + +static void ggp_account_chpass_data_free(ggp_account_chpass_data *chpass_data); +static void ggp_account_chpass_dialog(PurpleConnection *gc, + ggp_account_token *token, gpointer _chpass_data); +static void ggp_account_chpass_dialog_ok( + ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields); +static void ggp_account_chpass_dialog_invalid( + ggp_account_chpass_data *chpass_data, const gchar *message); +static void ggp_account_chpass_dialog_cancel( + ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields); +static void ggp_account_chpass_response(struct gg_http *h, gboolean success, + gboolean cancelled, gpointer _chpass_data); + +#define GGP_ACCOUNT_CHPASS_TITLE _("Password change") + +/******************************************************************************/ + +static void ggp_account_chpass_data_free(ggp_account_chpass_data *chpass_data) +{ + g_free(chpass_data->email); + g_free(chpass_data->password_current); + g_free(chpass_data->password_new); + g_free(chpass_data->token_value); + ggp_account_token_free(chpass_data->token); + g_free(chpass_data); +} + +void ggp_account_chpass(PurpleConnection *gc) +{ + ggp_account_chpass_data *chpass_data; + void ggp_account_change_passwd(PurpleConnection *gc); + purple_debug_info("gg", "ggp_account_chpass\n"); + + chpass_data = g_new0(ggp_account_chpass_data, 1); + chpass_data->gc = gc; + + ggp_account_token_request(gc, ggp_account_chpass_dialog, chpass_data); +} + +static void ggp_account_chpass_dialog(PurpleConnection *gc, + ggp_account_token *token, gpointer _chpass_data) +{ + ggp_account_chpass_data *chpass_data = _chpass_data; + PurpleAccount *account = purple_connection_get_account(chpass_data->gc); + PurpleRequestFields *fields; + PurpleRequestFieldGroup *main_group, *password_group, *token_group; + PurpleRequestField *field, *field_password; + gchar *primary; + + purple_debug_info("gg", "ggp_account_chpass_dialog(%p, %p, %p)\n", + gc, token, _chpass_data); + if (!token) + { + ggp_account_chpass_data_free(chpass_data); + return; + } + + fields = purple_request_fields_new(); + main_group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, main_group); + + field = purple_request_field_string_new("email", + _("New email address"), chpass_data->email, FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_set_validator(field, + purple_request_field_email_validator, NULL); + purple_request_field_group_add_field(main_group, field); + + password_group = purple_request_field_group_new(_("Password")); + purple_request_fields_add_group(fields, password_group); + + field = purple_request_field_string_new("password_current", + _("Current password"), chpass_data->password_current, FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_string_set_masked(field, TRUE); + purple_request_field_group_add_field(password_group, field); + + field = purple_request_field_string_new("password_new1", + _("Password"), chpass_data->password_new, FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_string_set_masked(field, TRUE); + purple_request_field_set_validator(field, ggp_validator_password, NULL); + purple_request_field_group_add_field(password_group, field); + field_password = field; + + field = purple_request_field_string_new("password_new2", + _("Password (retype)"), chpass_data->password_new, FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_string_set_masked(field, TRUE); + purple_request_field_set_validator(field, ggp_validator_password_equal, + field_password); + purple_request_field_group_add_field(password_group, field); + + token_group = purple_request_field_group_new(_("Captcha")); + purple_request_fields_add_group(fields, token_group); + + field = purple_request_field_string_new("token_value", + _("Enter text from image below"), chpass_data->token_value, + FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_set_validator(field, ggp_validator_token, token); + purple_request_field_group_add_field(token_group, field); + + field = purple_request_field_image_new("token_image", _("Captcha"), + token->data, token->size); + purple_request_field_group_add_field(token_group, field); + + chpass_data->token = token; + + primary = g_strdup_printf(_("Change password for %s"), + purple_account_get_username(account)); + + purple_request_fields(gc, GGP_ACCOUNT_CHPASS_TITLE, primary, + _("Please enter your current password and your new password."), + fields, + _("OK"), G_CALLBACK(ggp_account_chpass_dialog_ok), + _("Cancel"), G_CALLBACK(ggp_account_chpass_dialog_cancel), + account, NULL, NULL, chpass_data); + + g_free(primary); +} + +static void ggp_account_chpass_dialog_ok( + ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields) +{ + PurpleAccount *account = purple_connection_get_account(chpass_data->gc); + struct gg_http *h; + uin_t uin; + + purple_debug_misc("gg", "ggp_account_chpass_dialog_ok(%p, %p)\n", + chpass_data, fields); + + g_free(chpass_data->email); + g_free(chpass_data->password_current); + g_free(chpass_data->password_new); + g_free(chpass_data->token_value); + + chpass_data->email = g_strdup( + purple_request_fields_get_string(fields, "email")); + chpass_data->password_current = g_strdup( + purple_request_fields_get_string(fields, "password_current")); + chpass_data->password_new = g_strdup( + purple_request_fields_get_string(fields, "password_new1")); + chpass_data->token_value = g_strdup( + purple_request_fields_get_string(fields, "token_value")); + + g_assert(chpass_data->email != NULL); + g_assert(chpass_data->password_current != NULL); + g_assert(chpass_data->password_new != NULL); + g_assert(chpass_data->token_value != NULL); + + if (g_utf8_collate(chpass_data->password_current, + purple_account_get_password(account)) != 0) + { + g_free(chpass_data->password_current); + chpass_data->password_current = NULL; + ggp_account_chpass_dialog_invalid(chpass_data, + _("Your current password is different from the one that" + " you specified.")); + return; + } + if (g_utf8_collate(chpass_data->password_current, + chpass_data->password_new) == 0) + { + g_free(chpass_data->password_new); + chpass_data->password_new = NULL; + ggp_account_chpass_dialog_invalid(chpass_data, + _("New password have to be different from the current " + "one.")); + return; + } + + uin = ggp_str_to_uin(purple_account_get_username(account)); + purple_debug_info("gg", "ggp_account_chpass_dialog_ok: validation ok " + "[token id=%s, value=%s]\n", + chpass_data->token->id, chpass_data->token_value); + h = gg_change_passwd4(uin, chpass_data->email, + chpass_data->password_current, chpass_data->password_new, + chpass_data->token->id, chpass_data->token_value, TRUE); + + ggp_libgaduw_http_watch(chpass_data->gc, h, + ggp_account_chpass_response, chpass_data, TRUE); +} + +static void ggp_account_chpass_dialog_invalid( + ggp_account_chpass_data *chpass_data, const gchar *message) +{ + purple_debug_warning("gg", "ggp_account_chpass_dialog_invalid: %s\n", + message); + ggp_account_chpass_dialog(chpass_data->gc, chpass_data->token, + chpass_data); + purple_notify_error(purple_connection_get_account(chpass_data->gc), + GGP_ACCOUNT_CHPASS_TITLE, message, NULL); +} + +static void ggp_account_chpass_dialog_cancel( + ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields) +{ + ggp_account_chpass_data_free(chpass_data); +} + +static void ggp_account_chpass_response(struct gg_http *h, gboolean success, + gboolean cancelled, gpointer _chpass_data) +{ + ggp_account_chpass_data *chpass_data = _chpass_data; + PurpleAccount *account = + purple_connection_get_account(chpass_data->gc); + struct gg_pubdir *chpass_result = h->data; + + g_assert(!(success && cancelled)); + + if (cancelled) + { + purple_debug_info("gg", "ggp_account_chpass_response: " + "cancelled\n"); + ggp_account_chpass_data_free(chpass_data); + return; + } + if (!success || !chpass_result->success) + { + //TODO (libgadu 1.12.x): check chpass_result->error + purple_debug_error("gg", "ggp_account_chpass_response: " + "error\n"); + purple_notify_error(NULL, + GGP_ACCOUNT_CHPASS_TITLE, + _("Unable to change password. " + "An unknown error occurred."), NULL); + ggp_account_chpass_data_free(chpass_data); + return; + } + + purple_debug_info("gg", "ggp_account_chpass_response: " + "password changed\n"); + + purple_account_set_password(account, chpass_data->password_new); + + purple_notify_info(account, GGP_ACCOUNT_CHPASS_TITLE, + _("Your password has been changed."), NULL); + + ggp_account_chpass_data_free(chpass_data); + + //TODO: reconnect / check how it is done in original client + purple_account_disconnect(account); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/account.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,31 @@ +#ifndef _GGP_ACCOUNT_H +#define _GGP_ACCOUNT_H + +#include <internal.h> + +typedef struct +{ + gchar *id; + gpointer data; + size_t size; + int length; +} ggp_account_token; + +/** + * token must be free'd with ggp_account_token_free + */ +typedef void (*ggp_account_token_cb)(PurpleConnection *gc, + ggp_account_token *token, gpointer user_data); + +void ggp_account_token_request(PurpleConnection *gc, + ggp_account_token_cb callback, void *user_data); +gboolean ggp_account_token_validate(ggp_account_token *token, + const gchar *value); +void ggp_account_token_free(ggp_account_token *token); + + +void ggp_account_register(PurpleAccount *account); + +void ggp_account_chpass(PurpleConnection *gc); + +#endif /* _GGP_ACCOUNT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/avatar.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,363 @@ +#include "avatar.h" + +#include <debug.h> + +#include "gg.h" +#include "utils.h" +#include "oauth/oauth-purple.h" + +// Common + +static inline ggp_avatar_session_data * +ggp_avatar_get_avdata(PurpleConnection *gc); + +static gboolean ggp_avatar_timer_cb(gpointer _gc); + +#define GGP_AVATAR_USERAGENT "GG Client build 11.0.0.7562" +#define GGP_AVATAR_SIZE_MAX 1048576 + +// Buddy avatars updating + +typedef struct +{ + uin_t uin; + time_t timestamp; + + PurpleConnection *gc; + PurpleUtilFetchUrlData *request; +} ggp_avatar_buddy_update_req; + +static gboolean ggp_avatar_buddy_update_next(PurpleConnection *gc); +static void ggp_avatar_buddy_update_received(PurpleUtilFetchUrlData *url_data, + gpointer _pending_update, const gchar *url_text, gsize len, + const gchar *error_message); + +#define GGP_AVATAR_BUDDY_URL "http://avatars.gg.pl/%u/s,big" + +// Own avatar setting + +typedef struct +{ + PurpleStoredImage *img; +} ggp_avatar_own_data; + +static void ggp_avatar_own_got_token(PurpleConnection *gc, const gchar *token, + gpointer img); +static void ggp_avatar_own_sent(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message); + +#define GGP_AVATAR_RESPONSE_MAX 10240 + +/******************************************************************************* + * Common. + ******************************************************************************/ + +void ggp_avatar_setup(PurpleConnection *gc) +{ + ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc); + + avdata->pending_updates = NULL; + avdata->current_update = NULL; + avdata->own_data = g_new0(ggp_avatar_own_data, 1); + + avdata->timer = purple_timeout_add_seconds(1, ggp_avatar_timer_cb, gc); +} + +void ggp_avatar_cleanup(PurpleConnection *gc) +{ + ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc); + + purple_timeout_remove(avdata->timer); + + if (avdata->current_update != NULL) + { + ggp_avatar_buddy_update_req *current_update = + avdata->current_update; + + purple_util_fetch_url_cancel(current_update->request); + g_free(current_update); + } + avdata->current_update = NULL; + + g_free(avdata->own_data); + + g_list_free_full(avdata->pending_updates, &g_free); + avdata->pending_updates = NULL; +} + +static inline ggp_avatar_session_data * +ggp_avatar_get_avdata(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + return &accdata->avatar_data; +} + +static gboolean ggp_avatar_timer_cb(gpointer _gc) +{ + PurpleConnection *gc = _gc; + ggp_avatar_session_data *avdata; + + g_return_val_if_fail(PURPLE_CONNECTION_IS_VALID(gc), FALSE); + + avdata = ggp_avatar_get_avdata(gc); + if (avdata->current_update != NULL) + { + //TODO: verbose mode + //purple_debug_misc("gg", "ggp_avatar_timer_cb(%p): there is " + // "already an update running\n", gc); + return TRUE; + } + + while (!ggp_avatar_buddy_update_next(gc)); + + return TRUE; +} + +/******************************************************************************* + * Buddy avatars updating. + ******************************************************************************/ + +void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp) +{ + ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc); + ggp_avatar_buddy_update_req *pending_update = + g_new(ggp_avatar_buddy_update_req, 1); + + purple_debug_misc("gg", "ggp_avatar_buddy_update(%p, %u, %lu)\n", gc, + uin, timestamp); + + pending_update->uin = uin; + pending_update->timestamp = timestamp; + + avdata->pending_updates = g_list_append(avdata->pending_updates, + pending_update); +} + +void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin) +{ + purple_debug_info("gg", "ggp_avatar_buddy_remove(%p, %u)\n", gc, uin); + + purple_buddy_icons_set_for_user(purple_connection_get_account(gc), + ggp_uin_to_str(uin), NULL, 0, NULL); +} + +/* return TRUE if avatar update was performed or there is no new requests, + FALSE if we can request another one immediately */ +static gboolean ggp_avatar_buddy_update_next(PurpleConnection *gc) +{ + ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc); + GList *pending_update_it; + ggp_avatar_buddy_update_req *pending_update; + PurpleBuddy *buddy; + PurpleAccount *account = purple_connection_get_account(gc); + time_t old_timestamp; + const char *old_timestamp_str; + gchar *avatar_url; + + pending_update_it = g_list_first(avdata->pending_updates); + if (pending_update_it == NULL) + return TRUE; + + pending_update = pending_update_it->data; + avdata->pending_updates = g_list_remove(avdata->pending_updates, + pending_update); + buddy = purple_find_buddy(account, ggp_uin_to_str(pending_update->uin)); + + if (!buddy) + { + if (ggp_str_to_uin(purple_account_get_username(account)) == + pending_update->uin) + { + purple_debug_misc("gg", + "ggp_avatar_buddy_update_next(%p): own " + "avatar update requested, but we don't have " + "ourselves on buddy list\n", gc); + } + else + { + purple_debug_warning("gg", + "ggp_avatar_buddy_update_next(%p): " + "%u update requested, but he's not on buddy " + "list\n", gc, pending_update->uin); + } + return FALSE; + } + + old_timestamp_str = purple_buddy_icons_get_checksum_for_user(buddy); + old_timestamp = old_timestamp_str ? g_ascii_strtoull( + old_timestamp_str, NULL, 10) : 0; + if (old_timestamp == pending_update->timestamp) + { + purple_debug_misc("gg", + "ggp_avatar_buddy_update_next(%p): " + "%u have up to date avatar with ts=%lu\n", gc, + pending_update->uin, pending_update->timestamp); + return FALSE; + } + if (old_timestamp > pending_update->timestamp) + { + purple_debug_warning("gg", + "ggp_avatar_buddy_update_next(%p): " + "saved timestamp for %u is newer than received " + "(%lu > %lu)\n", gc, pending_update->uin, old_timestamp, + pending_update->timestamp); + } + + purple_debug_info("gg", + "ggp_avatar_buddy_update_next(%p): " + "updating %u with ts=%lu...\n", gc, pending_update->uin, + pending_update->timestamp); + + pending_update->gc = gc; + avdata->current_update = pending_update; + avatar_url = g_strdup_printf(GGP_AVATAR_BUDDY_URL, pending_update->uin); + pending_update->request = purple_util_fetch_url_request(account, + avatar_url, FALSE, GGP_AVATAR_USERAGENT, TRUE, NULL, FALSE, + GGP_AVATAR_SIZE_MAX, ggp_avatar_buddy_update_received, + pending_update); + g_free(avatar_url); + + return TRUE; +} + +static void ggp_avatar_buddy_update_received(PurpleUtilFetchUrlData *url_data, + gpointer _pending_update, const gchar *url_text, gsize len, + const gchar *error_message) +{ + ggp_avatar_buddy_update_req *pending_update = _pending_update; + PurpleBuddy *buddy; + PurpleAccount *account; + PurpleConnection *gc = pending_update->gc; + ggp_avatar_session_data *avdata; + gchar timestamp_str[20]; + + if (!PURPLE_CONNECTION_IS_VALID(gc)) + { + g_free(pending_update); + return; + } + + avdata = ggp_avatar_get_avdata(gc); + g_assert(pending_update == avdata->current_update); + avdata->current_update = NULL; + + if (len == 0) + { + purple_debug_error("gg", "ggp_avatar_buddy_update_received: bad" + " response while getting avatar for %u: %s\n", + pending_update->uin, error_message); + g_free(pending_update); + return; + } + + account = purple_connection_get_account(gc); + buddy = purple_find_buddy(account, ggp_uin_to_str(pending_update->uin)); + + if (!buddy) + { + purple_debug_warning("gg", "ggp_avatar_buddy_update_received: " + "buddy %u disappeared\n", pending_update->uin); + g_free(pending_update); + return; + } + + g_snprintf(timestamp_str, sizeof(timestamp_str), "%lu", + pending_update->timestamp); + purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy), + g_memdup(url_text, len), len, timestamp_str); + + purple_debug_info("gg", "ggp_avatar_buddy_update_received: " + "got avatar for buddy %u [ts=%lu]\n", pending_update->uin, + pending_update->timestamp); + g_free(pending_update); +} + +/******************************************************************************* + * Own avatar setting. + ******************************************************************************/ + +/** + * TODO: use new, GG11 method, when IMToken will be provided by libgadu. + * + * POST https://avatars.mpa.gg.pl/avatars/user,<uin>/0 + * Authorization: IMToken 0123456789abcdef0123456789abcdef01234567 + * photo=<avatar content> + */ + +void ggp_avatar_own_set(PurpleConnection *gc, PurpleStoredImage *img) +{ + ggp_avatar_own_data *own_data = ggp_avatar_get_avdata(gc)->own_data; + + purple_debug_info("gg", "ggp_avatar_own_set(%p, %p)", gc, img); + + if (img == NULL) + { + purple_debug_warning("gg", "ggp_avatar_own_set: avatar removing" + " is probably not possible within old protocol"); + return; + } + + own_data->img = img; + + ggp_oauth_request(gc, ggp_avatar_own_got_token, img); +} + +static void ggp_avatar_own_got_token(PurpleConnection *gc, const gchar *token, + gpointer img) +{ + ggp_avatar_own_data *own_data = ggp_avatar_get_avdata(gc)->own_data; + gchar *img_data, *img_data_e, *request, *request_data; + PurpleAccount *account = purple_connection_get_account(gc); + uin_t uin = ggp_str_to_uin(purple_account_get_username(account)); + + if (img != own_data->img) + { + purple_debug_warning("gg", "ggp_avatar_own_got_token: " + "avatar was changed in meantime\n"); + return; + } + own_data->img = NULL; + + img_data = purple_base64_encode(purple_imgstore_get_data(img), + purple_imgstore_get_size(img)); + img_data_e = g_uri_escape_string(img_data, NULL, FALSE); + g_free(img_data); + request_data = g_strdup_printf("uin=%d&photo=%s", uin, img_data_e); + g_free(img_data_e); + + request = g_strdup_printf( + "POST /upload HTTP/1.1\r\n" + "Host: avatars.nowe.gg\r\n" + "Authorization: %s\r\n" + "From: avatars to avatars\r\n" + "Content-Length: %u\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n%s", + token, strlen(request_data), request_data); + g_free(request_data); + + purple_debug_misc("gg", "ggp_avatar_own_got_token: " + "uploading new avatar...\n"); + purple_util_fetch_url_request(account, "http://avatars.nowe.gg/upload", + FALSE, NULL, TRUE, request, FALSE, GGP_AVATAR_RESPONSE_MAX, + ggp_avatar_own_sent, gc); + + g_free(request); +} + +static void ggp_avatar_own_sent(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message) +{ + PurpleConnection *gc = user_data; + + if (!PURPLE_CONNECTION_IS_VALID(gc)) + return; + + if (len == 0) + purple_debug_error("gg", "ggp_avatar_own_sent: " + "avatar not sent. %s\n", error_message); + else + purple_debug_info("gg", "ggp_avatar_own_sent: %s\n", url_text); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/avatar.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,24 @@ +#ifndef _GGP_AVATAR_H +#define _GGP_AVATAR_H + +#include <internal.h> +#include <libgadu.h> + +typedef struct +{ + guint timer; + GList *pending_updates; + + gpointer current_update; + gpointer own_data; +} ggp_avatar_session_data; + +void ggp_avatar_setup(PurpleConnection *gc); +void ggp_avatar_cleanup(PurpleConnection *gc); + +void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp); +void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin); + +void ggp_avatar_own_set(PurpleConnection *gc, PurpleStoredImage *img); + +#endif /* _GGP_AVATAR_H */
--- a/libpurple/protocols/gg/buddylist.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/buddylist.c Tue Aug 14 22:26:33 2012 +0200 @@ -22,9 +22,10 @@ #include <libgadu.h> +#include <debug.h> #include "gg.h" -#include "gg-utils.h" +#include "utils.h" #include "buddylist.h" #define F_FIRSTNAME 0 @@ -36,6 +37,7 @@ #define F_UIN 6 /* void ggp_buddylist_send(PurpleConnection *gc) {{{ */ +// this is for for notify purposes, not synchronizing buddy list void ggp_buddylist_send(PurpleConnection *gc) { GGPInfo *info = purple_connection_get_protocol_data(gc); @@ -81,7 +83,7 @@ PurpleGroup *group; gchar **users_tbl; int i; - char *utf8buddylist = charset_convert(buddylist, "CP1250", "UTF-8"); + char *utf8buddylist = ggp_convert_from_cp1250(buddylist); /* Don't limit the number of records in a buddylist. */ users_tbl = g_strsplit(utf8buddylist, "\r\n", -1); @@ -94,7 +96,7 @@ continue; data_tbl = g_strsplit(users_tbl[i], ";", 8); - if (ggp_array_size(data_tbl) < 8) { + if (g_strv_length(data_tbl) < 8) { purple_debug_warning("gg", "Something is wrong on line %d of the buddylist. Skipping.\n", i + 1); @@ -127,7 +129,7 @@ /* XXX: Probably buddy should be added to all the groups. */ /* Hard limit to at most 50 groups */ gchar **group_tbl = g_strsplit(data_tbl[F_GROUP], ",", 50); - if (ggp_array_size(group_tbl) > 0) { + if (g_strv_length(group_tbl) > 0) { g_free(g); g = g_strdup(group_tbl[0]); } @@ -178,11 +180,22 @@ "", gname, bname, "", ""); } - ptr = charset_convert(buddylist->str, "UTF-8", "CP1250"); + ptr = ggp_convert_to_cp1250(buddylist->str); g_string_free(buddylist, TRUE); return ptr; } /* }}} */ +const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin) +{ + const char *uin_s = ggp_uin_to_str(uin); + PurpleBuddy *buddy = purple_find_buddy( + purple_connection_get_account(gc), uin_s); + + if (buddy != NULL) + return purple_buddy_get_alias(buddy); + else + return uin_s; +} /* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/buddylist.h Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/buddylist.h Tue Aug 14 22:26:33 2012 +0200 @@ -50,6 +50,16 @@ char * ggp_buddylist_dump(PurpleAccount *account); +/** + * Returns the best name of a buddy from the buddylist. + * + * @param gc PurpleConnection instance. + * @param uin UIN of the buddy. + * + * @return Name of the buddy, or UIN converted to string, if there is no such + * user on the list. + */ +const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin); #endif /* _PURPLE_GG_BUDDYLIST_H */
--- a/libpurple/protocols/gg/confer.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/confer.c Tue Aug 14 22:26:33 2012 +0200 @@ -23,7 +23,7 @@ #include <libgadu.h> #include "gg.h" -#include "gg-utils.h" +#include "utils.h" #include "confer.h" /* PurpleConversation *ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name) {{{ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/deprecated.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,33 @@ +#include "deprecated.h" + +#include <libgadu.h> + +gboolean ggp_deprecated_setup_proxy(PurpleConnection *gc) +{ + PurpleProxyInfo *gpi = purple_proxy_get_setup(purple_connection_get_account(gc)); + + if ((purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) && + (purple_proxy_info_get_host(gpi) == NULL || + purple_proxy_info_get_port(gpi) <= 0)) + { + gg_proxy_enabled = 0; + purple_notify_error(NULL, NULL, _("Invalid proxy settings"), + _("Either the host name or port number specified for your given proxy type is invalid.")); + return FALSE; + } + + if (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_NONE) + { + gg_proxy_enabled = 0; + return TRUE; + } + + gg_proxy_enabled = 1; + //TODO: memleak + gg_proxy_host = g_strdup(purple_proxy_info_get_host(gpi)); + gg_proxy_port = purple_proxy_info_get_port(gpi); + gg_proxy_username = g_strdup(purple_proxy_info_get_username(gpi)); + gg_proxy_password = g_strdup(purple_proxy_info_get_password(gpi)); + + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/deprecated.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,8 @@ +#ifndef _GGP_DEPRECATED_H +#define _GGP_DEPRECATED_H + +#include <internal.h> + +gboolean ggp_deprecated_setup_proxy(PurpleConnection *gc); + +#endif /* _GGP_DEPRECATED_H */
--- a/libpurple/protocols/gg/gg-utils.c Tue Aug 14 22:05:05 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,160 +0,0 @@ -/** - * @file gg-utils.c - * - * purple - * - * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us> - * - * 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 "gg-utils.h" - - -/* uin_t ggp_str_to_uin(const char *str) {{{ */ -uin_t ggp_str_to_uin(const char *str) -{ - char *tmp; - long num; - - if (!str) - return 0; - - errno = 0; - num = strtol(str, &tmp, 10); - - if (*str == '\0' || *tmp != '\0') - return 0; - - if ((errno == ERANGE || (num == LONG_MAX || num == LONG_MIN)) -#if (LONG_MAX > UINT_MAX) - || num > (long)UINT_MAX -#endif - || num < 0) - return 0; - - return (uin_t) num; -} -/* }}} */ - -/* unsigned int ggp_array_size(char **array) {{{ */ -unsigned int ggp_array_size(char **array) -{ - unsigned int i; - - for (i = 0; array[i] != NULL && i < UINT_MAX; i++) - {} - - return i; -} -/* }}} */ - -/* char *charset_convert(const gchar *locstr, const char *encsrc, const char *encdst) {{{ */ -char *charset_convert(const gchar *locstr, const char *encsrc, const char *encdst) -{ - gchar *msg; - GError *err = NULL; - - if (locstr == NULL) - return NULL; - - msg = g_convert_with_fallback(locstr, strlen(locstr), encdst, encsrc, - "?", NULL, NULL, &err); - if (err != NULL) { - purple_debug_error("gg", "Error converting from %s to %s: %s\n", - encsrc, encdst, err->message); - g_error_free(err); - } - - /* Just in case? */ - if (msg == NULL) - msg = g_strdup(locstr); - - return msg; -} -/* }}} */ - -/* ggp_get_uin(PurpleAccount *account) {{{ */ -uin_t ggp_get_uin(PurpleAccount *account) -{ - return ggp_str_to_uin(purple_account_get_username(account)); -} -/* }}} */ - -/* char *ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin) {{{ */ -char *ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin) -{ - PurpleBuddy *buddy; - gchar *str_uin; - - str_uin = g_strdup_printf("%lu", (unsigned long int)uin); - - buddy = purple_find_buddy(purple_connection_get_account(gc), str_uin); - if (buddy != NULL) { - g_free(str_uin); - return g_strdup(purple_buddy_get_alias(buddy)); - } else { - return str_uin; - } -} -/* }}} */ - -void ggp_status_fake_to_self(PurpleAccount *account) -{ - PurplePresence *presence; - PurpleStatus *status; - const char *status_id; - const char *msg; - - if (! purple_find_buddy(account, purple_account_get_username(account))) - return; - - presence = purple_account_get_presence(account); - status = purple_presence_get_active_status(presence); - msg = purple_status_get_attr_string(status, "message"); - if (msg && !*msg) - msg = NULL; - - status_id = purple_status_get_id(status); - if (strcmp(status_id, "invisible") == 0) { - status_id = "offline"; - } - - if (msg) { - if (strlen(msg) > GG_STATUS_DESCR_MAXSIZE) { - msg = purple_markup_slice(msg, 0, GG_STATUS_DESCR_MAXSIZE); - } - } - purple_prpl_got_user_status(account, purple_account_get_username(account), - status_id, - msg ? "message" : NULL, msg, NULL); -} - -guint ggp_http_input_add(struct gg_http *http_req, PurpleInputFunction func, - gpointer user_data) -{ - PurpleInputCondition cond = 0; - int check = http_req->check; - - if (check & GG_CHECK_READ) - cond |= PURPLE_INPUT_READ; - if (check & GG_CHECK_WRITE) - cond |= PURPLE_INPUT_WRITE; - - return purple_input_add(http_req->fd, cond, func, user_data); -} - -/* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/gg-utils.h Tue Aug 14 22:05:05 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +0,0 @@ -/** - * @file gg-utils.h - * - * purple - * - * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us> - * - * 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_GG_UTILS_H -#define _PURPLE_GG_UTILS_H - -#include "internal.h" - -#include "plugin.h" -#include "version.h" -#include "notify.h" -#include "status.h" -#include "blist.h" -#include "accountopt.h" -#include "debug.h" -#include "util.h" -#include "request.h" - -#include "gg.h" - - -/** - * Convert a base 10 string to a UIN. - * - * @param str The string to convert - * - * @return UIN or 0 if an error occurred. - */ -uin_t -ggp_str_to_uin(const char *str); - -/** - * Calculate size of a NULL-terminated array. - * - * @param array The array. - * - * @return Size of the array. - */ -unsigned int -ggp_array_size(char **array); - -/** - * Convert enconding of a given string. - * - * @param locstr Input string. - * @param encsrc Current encoding of the string. - * @param encdst Target encoding of the string. - * - * @return Converted string (it must be g_free()ed when not used. Or NULL if - * locstr is NULL. - */ -char * -charset_convert(const gchar *locstr, const char *encsrc, const char *encdst); - -/** - * Get UIN of a given account. - * - * @param account Current account. - * - * @return UIN of an account. - */ -uin_t -ggp_get_uin(PurpleAccount *account); - -/** - * Returns the best name of a buddy from the buddylist. - * - * @param gc PurpleConnection instance. - * @param uin UIN of the buddy. - * - * @return Name of the buddy, or UIN converted to string. - */ -char * -ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin); - -/** - * Manages the display of account's status in the buddylist. - * - * @param account Current account. - */ -void -ggp_status_fake_to_self(PurpleAccount *account); - - -/** - * Adds an input handler in purple event loop for http request. - * - * @see purple_input_add - * - * @param http_req Http connection to watch. - * @param func The callback function for data. - * @param user_data User-specified data. - * - * @return The resulting handle (will be greater than 0). - */ -guint -ggp_http_input_add(struct gg_http *http_req, PurpleInputFunction func, - gpointer user_data); - -#endif /* _PURPLE_GG_UTILS_H */ - -/* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/gg.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/gg.c Tue Aug 14 22:26:33 2012 +0200 @@ -26,12 +26,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include "internal.h" +#include <internal.h> #include "plugin.h" #include "version.h" #include "notify.h" -#include "status.h" #include "blist.h" #include "accountopt.h" #include "debug.h" @@ -43,206 +42,25 @@ #include "confer.h" #include "search.h" #include "buddylist.h" -#include "gg-utils.h" - -#ifdef _WIN32 -# include "win32-resolver.h" -#endif - -static PurplePlugin *my_protocol = NULL; +#include "utils.h" +#include "resolver-purple.h" +#include "account.h" +#include "deprecated.h" +#include "purplew.h" +#include "libgadu-events.h" +#include "multilogon.h" +#include "status.h" /* Prototypes */ -static void ggp_set_status(PurpleAccount *account, PurpleStatus *status); -static int ggp_to_gg_status(PurpleStatus *status, char **msg); -/* ---------------------------------------------------------------------- */ -/* ----- EXTERNAL CALLBACKS --------------------------------------------- */ -/* ---------------------------------------------------------------------- */ +typedef struct +{ + gboolean blocked; +} ggp_buddy_data; -/* ----- HELPERS -------------------------------------------------------- */ - -/** - * Set up libgadu's proxy. - * - * @param account Account for which to set up the proxy. - * - * @return Zero if proxy setup is valid, otherwise -1. - */ -static int ggp_setup_proxy(PurpleAccount *account) -{ - PurpleProxyInfo *gpi; - - gpi = purple_proxy_get_setup(account); - - if ((purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) && - (purple_proxy_info_get_host(gpi) == NULL || - purple_proxy_info_get_port(gpi) <= 0)) { - - gg_proxy_enabled = 0; - purple_notify_error(NULL, NULL, _("Invalid proxy settings"), - _("Either the host name or port number specified for your given proxy type is invalid.")); - return -1; - } else if (purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) { - gg_proxy_enabled = 1; - gg_proxy_host = g_strdup(purple_proxy_info_get_host(gpi)); - gg_proxy_port = purple_proxy_info_get_port(gpi); - gg_proxy_username = g_strdup(purple_proxy_info_get_username(gpi)); - gg_proxy_password = g_strdup(purple_proxy_info_get_password(gpi)); - } else { - gg_proxy_enabled = 0; - } - - return 0; -} - -static void ggp_async_token_handler(gpointer _gc, gint fd, PurpleInputCondition cond) -{ - PurpleConnection *gc = _gc; - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPToken *token = info->token; - GGPTokenCallback cb; - - struct gg_token *t = NULL; - - purple_debug_info("gg", "token_handler: token->req: check = %d; state = %d;\n", - token->req->check, token->req->state); - - if (gg_token_watch_fd(token->req) == -1 || token->req->state == GG_STATE_ERROR) { - purple_debug_error("gg", "token error (1): %d\n", token->req->error); - purple_input_remove(token->inpa); - gg_token_free(token->req); - token->req = NULL; - - purple_notify_error(purple_connection_get_account(gc), - _("Token Error"), - _("Unable to fetch the token.\n"), NULL); - return; - } - - if (token->req->state != GG_STATE_DONE) { - purple_input_remove(token->inpa); - token->inpa = purple_input_add(token->req->fd, - (token->req->check == 1) - ? PURPLE_INPUT_WRITE - : PURPLE_INPUT_READ, - ggp_async_token_handler, gc); - return; - } - - if (!(t = token->req->data) || !token->req->body) { - purple_debug_error("gg", "token error (2): %d\n", token->req->error); - purple_input_remove(token->inpa); - gg_token_free(token->req); - token->req = NULL; - - purple_notify_error(purple_connection_get_account(gc), - _("Token Error"), - _("Unable to fetch the token.\n"), NULL); - return; - } - - purple_input_remove(token->inpa); - - token->id = g_strdup(t->tokenid); - token->size = token->req->body_size; - token->data = g_new0(char, token->size); - memcpy(token->data, token->req->body, token->size); - - purple_debug_info("gg", "TOKEN! tokenid = %s; size = %d\n", - token->id, token->size); - - gg_token_free(token->req); - token->req = NULL; - token->inpa = 0; - - cb = token->cb; - token->cb = NULL; - cb(gc); -} - -static void ggp_token_request(PurpleConnection *gc, GGPTokenCallback cb) -{ - PurpleAccount *account; - struct gg_http *req; - GGPInfo *info; - - account = purple_connection_get_account(gc); - - if (ggp_setup_proxy(account) == -1) - return; - - info = purple_connection_get_protocol_data(gc); - - if ((req = gg_token(1)) == NULL) { - purple_notify_error(account, - _("Token Error"), - _("Unable to fetch the token.\n"), NULL); - return; - } - - info->token = g_new(GGPToken, 1); - info->token->cb = cb; - - info->token->req = req; - info->token->inpa = purple_input_add(req->fd, PURPLE_INPUT_READ, - ggp_async_token_handler, gc); -} -/* }}} */ - /* ---------------------------------------------------------------------- */ - -/** - * Request buddylist from the server. - * Buddylist is received in the ggp_callback_recv(). - * - * @param Current action handler. - */ -static void ggp_action_buddylist_get(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - GGPInfo *info = purple_connection_get_protocol_data(gc); - - purple_debug_info("gg", "Downloading...\n"); - - gg_userlist_request(info->session, GG_USERLIST_GET, NULL); -} - -/** - * Upload the buddylist to the server. - * - * @param action Current action handler. - */ -static void ggp_action_buddylist_put(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - GGPInfo *info = purple_connection_get_protocol_data(gc); - - char *buddylist = ggp_buddylist_dump(purple_connection_get_account(gc)); - - purple_debug_info("gg", "Uploading...\n"); - - if (buddylist == NULL) - return; - - gg_userlist_request(info->session, GG_USERLIST_PUT, buddylist); - g_free(buddylist); -} - -/** - * Delete buddylist from the server. - * - * @param action Current action handler. - */ -static void ggp_action_buddylist_delete(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - GGPInfo *info = purple_connection_get_protocol_data(gc); - - purple_debug_info("gg", "Deleting...\n"); - - gg_userlist_request(info->session, GG_USERLIST_PUT, NULL); -} +// buddy list import/export from/to file static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *filename) { @@ -332,154 +150,6 @@ gc); } -static void ggp_callback_register_account_ok(PurpleConnection *gc, - PurpleRequestFields *fields) -{ - PurpleAccount *account; - GGPInfo *info = purple_connection_get_protocol_data(gc); - struct gg_http *h = NULL; - struct gg_pubdir *s; - uin_t uin; - gchar *email, *p1, *p2, *t; - GGPToken *token = info->token; - - email = charset_convert(purple_request_fields_get_string(fields, "email"), - "UTF-8", "CP1250"); - p1 = charset_convert(purple_request_fields_get_string(fields, "password1"), - "UTF-8", "CP1250"); - p2 = charset_convert(purple_request_fields_get_string(fields, "password2"), - "UTF-8", "CP1250"); - t = charset_convert(purple_request_fields_get_string(fields, "token"), - "UTF-8", "CP1250"); - - account = purple_connection_get_account(gc); - - if (email == NULL || p1 == NULL || p2 == NULL || t == NULL || - *email == '\0' || *p1 == '\0' || *p2 == '\0' || *t == '\0') { - purple_connection_error (gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("You must fill in all registration fields")); - goto exit_err; - } - - if (g_utf8_collate(p1, p2) != 0) { - purple_connection_error (gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Passwords do not match")); - goto exit_err; - } - - purple_debug_info("gg", "register_account_ok: token_id = %s; t = %s\n", - token->id, t); - h = gg_register3(email, p1, token->id, t, 0); - if (h == NULL || !(s = h->data) || !s->success) { - purple_connection_error (gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Unable to register new account. An unknown error occurred.")); - goto exit_err; - } - - uin = s->uin; - purple_debug_info("gg", "registered uin: %d\n", uin); - - g_free(t); - t = g_strdup_printf("%u", uin); - purple_account_set_username(account, t); - /* Save the password if remembering passwords for the account */ - purple_account_set_password(account, p1); - - purple_notify_info(NULL, _("New Gadu-Gadu Account Registered"), - _("Registration completed successfully!"), NULL); - - purple_account_register_completed(account, TRUE); - - /* TODO: the currently open Accounts Window will not be updated withthe - * new username and etc, we need to somehow have it refresh at this - * point - */ - - /* Need to disconnect or actually log in. For now, we disconnect. */ - purple_account_disconnect(account); - -exit_err: - purple_account_register_completed(account, FALSE); - - gg_register_free(h); - g_free(email); - g_free(p1); - g_free(p2); - g_free(t); - g_free(token->id); - g_free(token); -} - -static void ggp_callback_register_account_cancel(PurpleConnection *gc, - PurpleRequestFields *fields) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPToken *token = info->token; - - purple_account_disconnect(purple_connection_get_account(gc)); - - g_free(token->id); - g_free(token->data); - g_free(token); - -} - -static void ggp_register_user_dialog(PurpleConnection *gc) -{ - PurpleAccount *account; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPToken *token = info->token; - - - account = purple_connection_get_account(gc); - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_string_new("email", - _("Email"), "", FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("password1", - _("Password"), "", FALSE); - purple_request_field_string_set_masked(field, TRUE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("password2", - _("Password (again)"), "", FALSE); - purple_request_field_string_set_masked(field, TRUE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("token", - _("Enter captcha text"), "", FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - /* original size: 60x24 */ - field = purple_request_field_image_new("token_img", - _("Captcha"), token->data, token->size); - purple_request_field_group_add_field(group, field); - - purple_request_fields(account, - _("Register New Gadu-Gadu Account"), - _("Register New Gadu-Gadu Account"), - _("Please, fill in the following fields"), - fields, - _("OK"), G_CALLBACK(ggp_callback_register_account_ok), - _("Cancel"), G_CALLBACK(ggp_callback_register_account_cancel), - purple_connection_get_account(gc), NULL, NULL, - gc); -} - /* ----- PUBLIC DIRECTORY SEARCH ---------------------------------------- */ static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data) @@ -617,265 +287,6 @@ gc); } -/* ----- CHANGE PASSWORD ---------------------------------------------------- */ - -typedef struct -{ - guint inpa; - struct gg_http *http_req; - gchar *new_password; - PurpleAccount *account; -} ggp_change_passwd_request; - -static void ggp_callback_change_passwd_handler(gpointer _req, gint fd, - PurpleInputCondition cond) -{ - ggp_change_passwd_request *req = _req; - const char *messagesTitle = - _("Change password for the Gadu-Gadu account"); - - purple_input_remove(req->inpa); - - if (gg_change_passwd_watch_fd(req->http_req) == -1 || - req->http_req->state == GG_STATE_ERROR) - goto exit_error; - - if (req->http_req->state != GG_STATE_DONE) - { - req->inpa = ggp_http_input_add(req->http_req, - ggp_callback_change_passwd_handler, req); - return; - } - - if (req->http_req->data != NULL && - ((struct gg_pubdir*)req->http_req->data)->success == 1) - { - purple_account_set_password(req->account, req->new_password); - purple_notify_info(req->account, messagesTitle, - _("Password was changed successfully!"), NULL); - goto exit_cleanup; - } - -exit_error: - purple_notify_error(req->account, messagesTitle, - _("Unable to change password. Error occurred.\n"), NULL); - -exit_cleanup: - gg_change_passwd_free(req->http_req); - g_free(req->new_password); - g_free(req); -} - -static void ggp_callback_change_passwd_ok(PurpleConnection *gc, - PurpleRequestFields *fields) -{ - PurpleAccount *account; - GGPInfo *info = purple_connection_get_protocol_data(gc); - struct gg_http *h; - gchar *cur, *p1, *p2, *t, *mail; - const char *messagesTitle = - _("Change password for the Gadu-Gadu account"); - - cur = g_strdup(purple_request_fields_get_string(fields, - "password_cur")); - p1 = g_strdup(purple_request_fields_get_string(fields, "password1")); - p2 = g_strdup(purple_request_fields_get_string(fields, "password2")); - t = g_strdup(purple_request_fields_get_string(fields, "token")); - mail = g_strdup(purple_request_fields_get_string(fields, "email")); - - account = purple_connection_get_account(gc); - - if (cur == NULL || p1 == NULL || p2 == NULL || t == NULL || - mail == NULL || *cur == '\0' || *p1 == '\0' || *p2 == '\0' || - *t == '\0' || *mail == '\0') { - purple_notify_error(account, messagesTitle, - _("Fill in the fields."), NULL); - goto exit_err; - } - - if (g_utf8_collate(p1, p2) != 0) { - purple_notify_error(account, messagesTitle, - _("New passwords do not match."), NULL); - goto exit_err; - } - - if (strlen(p1) > 15) { - purple_notify_error(account, messagesTitle, - _("New password should be at most 15 characters long."), - NULL); - goto exit_err; - } - - if (g_utf8_collate(cur, purple_account_get_password(account)) != 0) { - purple_notify_error(account, messagesTitle, - _("Your current password is different from the one that" - " you specified."), NULL); - goto exit_err; - } - - if (!purple_email_is_valid(mail)) { - purple_notify_error(account, messagesTitle, - _("Invalid email address"), NULL); - goto exit_err; - } - - purple_debug_info("gg", "Changing password with email \"%s\"...\n", - mail); - - h = gg_change_passwd4(ggp_get_uin(account), mail, - purple_account_get_password(account), p1, info->token->id, t, - 1); - - if (h == NULL) - purple_notify_error(account, messagesTitle, - _("Unable to change password. Error occurred.\n"), - NULL); - else - { - ggp_change_passwd_request *req = - g_new(ggp_change_passwd_request, 1); - req->http_req = h; - req->new_password = g_strdup(p1); - req->account = account; - - req->inpa = ggp_http_input_add(h, - ggp_callback_change_passwd_handler, req); - } - -exit_err: - g_free(cur); - g_free(p1); - g_free(p2); - g_free(t); - g_free(mail); - g_free(info->token->id); - g_free(info->token->data); - g_free(info->token); -} - -static void ggp_change_passwd_dialog(PurpleConnection *gc) -{ - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPToken *token = info->token; - - char *msg; - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_string_new("password_cur", - _("Current password"), "", FALSE); - purple_request_field_string_set_masked(field, TRUE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("password1", - _("Password"), "", FALSE); - purple_request_field_string_set_masked(field, TRUE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("password2", - _("Password (retype)"), "", FALSE); - purple_request_field_string_set_masked(field, TRUE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("email", - _("Email Address"), "", FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("token", - _("Enter current token"), "", FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - /* original size: 60x24 */ - field = purple_request_field_image_new("token_img", - _("Current token"), token->data, token->size); - purple_request_field_group_add_field(group, field); - - msg = g_strdup_printf("%s %d", - _("Please, enter your current password and your new password " - "for UIN: "), ggp_get_uin(purple_connection_get_account(gc))); - - purple_request_fields(gc, - _("Change Gadu-Gadu Password"), - _("Change Gadu-Gadu Password"), - msg, - fields, _("OK"), G_CALLBACK(ggp_callback_change_passwd_ok), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); - - g_free(msg); -} - -static void ggp_change_passwd(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - - ggp_token_request(gc, ggp_change_passwd_dialog); -} - -/* ----- CHANGE STATUS BROADCASTING ------------------------------------------------ */ - -static void ggp_action_change_status_broadcasting_ok(PurpleConnection *gc, PurpleRequestFields *fields) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - int selected_field; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleStatus *status; - - selected_field = purple_request_fields_get_choice(fields, "status_broadcasting"); - - if (selected_field == 0) - info->status_broadcasting = TRUE; - else - info->status_broadcasting = FALSE; - - status = purple_account_get_active_status(account); - - ggp_set_status(account, status); -} - -static void ggp_action_change_status_broadcasting(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - GGPInfo *info = purple_connection_get_protocol_data(gc); - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_choice_new("status_broadcasting", _("Show status to:"), 0); - purple_request_field_choice_add(field, _("All people")); - purple_request_field_choice_add(field, _("Only buddies")); - purple_request_field_group_add_field(group, field); - - if (info->status_broadcasting) - purple_request_field_choice_set_default_value(field, 0); - else - purple_request_field_choice_set_default_value(field, 1); - - purple_request_fields(gc, - _("Change status broadcasting"), - _("Change status broadcasting"), - _("Please, select who can see your status"), - fields, - _("OK"), G_CALLBACK(ggp_action_change_status_broadcasting_ok), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - /* ----- CONFERENCES ---------------------------------------------------- */ static void ggp_callback_add_to_chat_ok(PurpleBuddy *buddy, PurpleRequestFields *fields) @@ -970,164 +381,6 @@ /* ----- INTERNAL CALLBACKS --------------------------------------------- */ /* ---------------------------------------------------------------------- */ -struct gg_fetch_avatar_data -{ - PurpleConnection *gc; - gchar *uin; - gchar *avatar_url; -}; - - -static void gg_fetch_avatar_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *data, size_t len, const gchar *error_message) { - struct gg_fetch_avatar_data *d = user_data; - PurpleAccount *account; - PurpleBuddy *buddy; - gpointer buddy_icon_data; - - purple_debug_info("gg", "gg_fetch_avatar_cb: got avatar image for %s\n", - d->uin); - - /* FIXME: This shouldn't be necessary */ - if (!PURPLE_CONNECTION_IS_VALID(d->gc)) { - g_free(d->uin); - g_free(d->avatar_url); - g_free(d); - g_return_if_reached(); - } - - account = purple_connection_get_account(d->gc); - buddy = purple_find_buddy(account, d->uin); - - if (buddy == NULL) - goto out; - - buddy_icon_data = g_memdup(data, len); - - purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy), - buddy_icon_data, len, d->avatar_url); - purple_debug_info("gg", "gg_fetch_avatar_cb: UIN %s should have avatar " - "now\n", d->uin); - -out: - g_free(d->uin); - g_free(d->avatar_url); - g_free(d); -} - -static void gg_get_avatar_url_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *url_text, size_t len, const gchar *error_message) { - struct gg_fetch_avatar_data *data; - PurpleConnection *gc = user_data; - PurpleAccount *account; - PurpleBuddy *buddy; - const char *uin; - const char *is_blank; - const char *checksum; - - gchar *bigavatar = NULL; - xmlnode *xml = NULL; - xmlnode *xmlnode_users; - xmlnode *xmlnode_user; - xmlnode *xmlnode_avatars; - xmlnode *xmlnode_avatar; - xmlnode *xmlnode_bigavatar; - - g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); - account = purple_connection_get_account(gc); - - if (error_message != NULL) - purple_debug_error("gg", "gg_get_avatars_cb error: %s\n", error_message); - else if (len > 0 && url_text && *url_text) { - xml = xmlnode_from_str(url_text, -1); - if (xml == NULL) - goto out; - - xmlnode_users = xmlnode_get_child(xml, "users"); - if (xmlnode_users == NULL) - goto out; - - xmlnode_user = xmlnode_get_child(xmlnode_users, "user"); - if (xmlnode_user == NULL) - goto out; - - uin = xmlnode_get_attrib(xmlnode_user, "uin"); - - xmlnode_avatars = xmlnode_get_child(xmlnode_user, "avatars"); - if (xmlnode_avatars == NULL) - goto out; - - xmlnode_avatar = xmlnode_get_child(xmlnode_avatars, "avatar"); - if (xmlnode_avatar == NULL) - goto out; - - xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "originBigAvatar"); - if (xmlnode_bigavatar == NULL) - goto out; - - is_blank = xmlnode_get_attrib(xmlnode_avatar, "blank"); - bigavatar = xmlnode_get_data(xmlnode_bigavatar); - - purple_debug_info("gg", "gg_get_avatar_url_cb: UIN %s, IS_BLANK %s, " - "URL %s\n", - uin ? uin : "(null)", is_blank ? is_blank : "(null)", - bigavatar ? bigavatar : "(null)"); - - if (uin != NULL && bigavatar != NULL) { - buddy = purple_find_buddy(account, uin); - if (buddy == NULL) - goto out; - - checksum = purple_buddy_icons_get_checksum_for_user(buddy); - - if (purple_strequal(is_blank, "1")) { - purple_buddy_icons_set_for_user(account, - purple_buddy_get_name(buddy), NULL, 0, NULL); - } else if (!purple_strequal(checksum, bigavatar)) { - data = g_new0(struct gg_fetch_avatar_data, 1); - data->gc = gc; - data->uin = g_strdup(uin); - data->avatar_url = g_strdup(bigavatar); - - purple_debug_info("gg", "gg_get_avatar_url_cb: " - "requesting avatar for %s\n", uin); - /* FIXME: This should be cancelled somewhere if not needed. */ - url_data = purple_util_fetch_url_request(account, - bigavatar, TRUE, "Mozilla/4.0 (compatible; MSIE 5.0)", - FALSE, NULL, FALSE, -1, gg_fetch_avatar_cb, data); - } - } - } - -out: - if (xml) - xmlnode_free(xml); - g_free(bigavatar); -} - -/** - * Try to update avatar of the buddy. - * - * @param gc PurpleConnection - * @param uin UIN of the buddy. - */ -static void ggp_update_buddy_avatar(PurpleConnection *gc, uin_t uin) -{ - gchar *avatarurl; - PurpleUtilFetchUrlData *url_data; - - purple_debug_info("gg", "ggp_update_buddy_avatar(gc, %u)\n", uin); - - avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%u/0.xml", uin); - - /* FIXME: This should be cancelled somewhere if not needed. */ - url_data = purple_util_fetch_url_request( - purple_connection_get_account(gc), avatarurl, TRUE, - "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1, - gg_get_avatar_url_cb, gc); - - g_free(avatarurl); -} /** * Handle change of the status of the buddy. @@ -1143,14 +396,17 @@ gchar *from; const char *st; char *status_msg = NULL; - - ggp_update_buddy_avatar(gc, uin); + ggp_buddy_data *buddy_data; + PurpleBuddy *buddy; + PurpleAccount *account = purple_connection_get_account(gc); from = g_strdup_printf("%u", uin); + buddy = purple_find_buddy(purple_connection_get_account(gc), from); switch (status) { case GG_STATUS_NOT_AVAIL: case GG_STATUS_NOT_AVAIL_DESCR: + case GG_STATUS_BLOCKED: st = purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE); break; case GG_STATUS_FFC: @@ -1173,10 +429,6 @@ case GG_STATUS_DND_DESCR: st = purple_primitive_get_id_from_type(PURPLE_STATUS_UNAVAILABLE); break; - case GG_STATUS_BLOCKED: - /* user is blocking us.... */ - st = "blocked"; - break; default: st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); purple_debug_info("gg", @@ -1193,13 +445,27 @@ } } + buddy_data = purple_buddy_get_protocol_data(buddy); + if (!buddy_data) + { + buddy_data = g_new0(ggp_buddy_data, 1); + purple_buddy_set_protocol_data(buddy, buddy_data); + } + buddy_data->blocked = (status == GG_STATUS_BLOCKED); + + if (uin == ggp_str_to_uin(purple_account_get_username(account))) + { + purple_debug_info("gg", "own status changed to %s [%s]\n", st, + status_msg ? status_msg : ""); + } + purple_debug_info("gg", "status of %u is %s [%s]\n", uin, st, status_msg ? status_msg : ""); if (status_msg == NULL) { - purple_prpl_got_user_status(purple_connection_get_account(gc), + purple_prpl_got_user_status(account, from, st, NULL); } else { - purple_prpl_got_user_status(purple_connection_get_account(gc), + purple_prpl_got_user_status(account, from, st, "message", status_msg, NULL); g_free(status_msg); } @@ -1461,47 +727,6 @@ } } -static void ggp_recv_image_handler(PurpleConnection *gc, const struct gg_event *ev) -{ - gint imgid = 0; - GGPInfo *info = purple_connection_get_protocol_data(gc); - GList *entry = g_list_first(info->pending_richtext_messages); - gchar *handlerid = g_strdup_printf("IMGID_HANDLER-%i", ev->event.image_reply.crc32); - - imgid = purple_imgstore_add_with_id( - g_memdup(ev->event.image_reply.image, ev->event.image_reply.size), - ev->event.image_reply.size, - ev->event.image_reply.filename); - - purple_debug_info("gg", "ggp_recv_image_handler: got image with crc32: %u\n", ev->event.image_reply.crc32); - - while(entry) { - if (strstr((gchar *)entry->data, handlerid) != NULL) { - gchar **split = g_strsplit((gchar *)entry->data, handlerid, 3); - gchar *text = g_strdup_printf("%s%i%s", split[0], imgid, split[1]); - purple_debug_info("gg", "ggp_recv_image_handler: found message matching crc32: %s\n", (gchar *)entry->data); - g_strfreev(split); - info->pending_richtext_messages = g_list_remove(info->pending_richtext_messages, entry->data); - /* We don't have any more images to download */ - if (strstr(text, "<IMG ID=\"IMGID_HANDLER") == NULL) { - gchar *buf = g_strdup_printf("%lu", (unsigned long int)ev->event.image_reply.sender); - serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, time(NULL)); - g_free(buf); - purple_debug_info("gg", "ggp_recv_image_handler: richtext message: %s\n", text); - g_free(text); - break; - } - info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, text); - break; - } - entry = g_list_next(entry); - } - g_free(handlerid); - - return; -} - - /** * Dispatch a message received from a buddy. * @@ -1510,7 +735,7 @@ * * Image receiving, some code borrowed from Kadu http://www.kadu.net */ -static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev) +void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event_msg *ev, gboolean multilogon) { GGPInfo *info = purple_connection_get_protocol_data(gc); PurpleConversation *conv; @@ -1518,37 +743,38 @@ gchar *msg; gchar *tmp; time_t mtime; + uin_t sender = ev->sender; - if (ev->event.msg.message == NULL) + if (ev->message == NULL) { purple_debug_warning("gg", "ggp_recv_message_handler: NULL as message pointer\n"); return; } - from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender); + from = g_strdup_printf("%lu", (unsigned long int)ev->sender); - /* - tmp = charset_convert((const char *)ev->event.msg.message, - "CP1250", "UTF-8"); - */ - tmp = g_strdup_printf("%s", ev->event.msg.message); + tmp = g_strdup_printf("%s", ev->message); purple_str_strip_char(tmp, '\r'); msg = g_markup_escape_text(tmp, -1); g_free(tmp); + if (ev->msgclass & GG_CLASS_QUEUED) + mtime = ev->time; + else + mtime = time(NULL); + /* We got richtext message */ - if (ev->event.msg.formats_length) + if (ev->formats_length) { gboolean got_image = FALSE, bold = FALSE, italic = FALSE, under = FALSE; - char *cformats = (char *)ev->event.msg.formats; - char *cformats_end = cformats + ev->event.msg.formats_length; + char *cformats = (char *)ev->formats; + char *cformats_end = cformats + ev->formats_length; gint increased_len = 0; struct gg_msg_richtext_format *actformat; struct gg_msg_richtext_image *actimage; GString *message = g_string_new(msg); - gchar *handlerid; - purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->event.msg.formats_length); + purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->formats_length); while (cformats < cformats_end) { @@ -1569,7 +795,10 @@ (actformat->font & GG_FONT_UNDERLINE) != 0, increased_len); - if (actformat->font & GG_FONT_IMAGE) { + if (actformat->font & GG_FONT_IMAGE) + { + const char *placeholder; + got_image = TRUE; actimage = (struct gg_msg_richtext_image*)(cformats); cformats += sizeof(struct gg_msg_richtext_image); @@ -1582,13 +811,12 @@ continue; } - gg_image_request(info->session, ev->event.msg.sender, + gg_image_request(info->session, ev->sender, actimage->size, actimage->crc32); - handlerid = g_strdup_printf("<IMG ID=\"IMGID_HANDLER-%i\">", actimage->crc32); - g_string_insert(message, byteoffset, handlerid); - increased_len += strlen(handlerid); - g_free(handlerid); + placeholder = ggp_image_pending_placeholder(actimage->crc32); + g_string_insert(message, byteoffset, placeholder); + increased_len += strlen(placeholder); continue; } @@ -1636,80 +864,61 @@ msg = message->str; g_string_free(message, FALSE); - if (got_image) { - info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, msg); + if (got_image) + { + ggp_image_got_im(gc, sender, msg, mtime); return; } } - purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d)\n", - from, msg, ev->event.msg.msgclass, - ev->event.msg.recipients_count); + purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d; multilogon = %d)\n", + from, msg, ev->msgclass, + ev->recipients_count, + multilogon); - if (ev->event.msg.msgclass & GG_CLASS_QUEUED) - mtime = ev->event.msg.time; - else - mtime = time(NULL); - - if (ev->event.msg.recipients_count == 0) { + if (multilogon && ev->recipients_count != 0) { + purple_debug_warning("gg", "ggp_recv_message_handler: conference multilogon messages are not yet handled\n"); + } else if (multilogon) { + PurpleAccount *account = purple_connection_get_account(gc); + PurpleConversation *conv; + const gchar *who = ggp_uin_to_str(ev->sender); // not really sender + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, who, account); + if (conv == NULL) + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who); + purple_conversation_write(conv, purple_account_get_username(account), msg, PURPLE_MESSAGE_SEND, mtime); + } else if (ev->recipients_count == 0) { serv_got_im(gc, from, msg, 0, mtime); } else { const char *chat_name; int chat_id; - char *buddy_name; chat_name = ggp_confer_find_by_participants(gc, - ev->event.msg.recipients, - ev->event.msg.recipients_count); + ev->recipients, + ev->recipients_count); if (chat_name == NULL) { chat_name = ggp_confer_add_new(gc, NULL); serv_got_joined_chat(gc, info->chats_count, chat_name); ggp_confer_participants_add_uin(gc, chat_name, - ev->event.msg.sender); + ev->sender); ggp_confer_participants_add(gc, chat_name, - ev->event.msg.recipients, - ev->event.msg.recipients_count); + ev->recipients, + ev->recipients_count); } conv = ggp_confer_find_by_name(gc, chat_name); chat_id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)); - buddy_name = ggp_buddy_get_name(gc, ev->event.msg.sender); - serv_got_chat_in(gc, chat_id, buddy_name, - PURPLE_MESSAGE_RECV, msg, mtime); - g_free(buddy_name); + serv_got_chat_in(gc, chat_id, + ggp_buddylist_get_buddy_name(gc, ev->sender), + PURPLE_MESSAGE_RECV, msg, mtime); } g_free(msg); g_free(from); } -static void ggp_send_image_handler(PurpleConnection *gc, const struct gg_event *ev) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - PurpleStoredImage *image; - gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32))); - - purple_debug_info("gg", "ggp_send_image_handler: image request received, crc32: %u, imgid: %d\n", ev->event.image_request.crc32, imgid); - - if(imgid) - { - if((image = purple_imgstore_find_by_id(imgid))) { - gint image_size = purple_imgstore_get_size(image); - gconstpointer image_bin = purple_imgstore_get_data(image); - const char *image_filename = purple_imgstore_get_filename(image); - - purple_debug_info("gg", "ggp_send_image_handler: sending image imgid: %i, crc: %u\n", imgid, ev->event.image_request.crc32); - gg_image_reply(info->session, (unsigned long int)ev->event.image_request.sender, image_filename, image_bin, image_size); - purple_imgstore_unref(image); - } else { - purple_debug_error("gg", "ggp_send_image_handler: image imgid: %i, crc: %u in hash but not found in imgstore!\n", imgid, ev->event.image_request.crc32); - } - g_hash_table_remove(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32)); - } -} - static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int length) { gchar *from; @@ -1728,6 +937,7 @@ * @param data Raw XML contents. * * @see http://toxygen.net/libgadu/protocol/#ch1.13 + * @todo: this may not be necessary anymore */ static void ggp_xml_event_handler(PurpleConnection *gc, char *data) { @@ -1776,7 +986,6 @@ purple_debug_info("gg", "ggp_xml_event_handler: avatar updated (uid: %u)\n", event_sender); - ggp_update_buddy_avatar(gc, event_sender); break; default: purple_debug_error("gg", @@ -1811,7 +1020,7 @@ /* Nothing happened. */ break; case GG_EVENT_MSG: - ggp_recv_message_handler(gc, ev); + ggp_recv_message_handler(gc, &ev->event.msg, FALSE); break; case GG_EVENT_ACK: /* Changing %u to %i fixes compiler warning */ @@ -1821,10 +1030,10 @@ ev->event.ack.seq); break; case GG_EVENT_IMAGE_REPLY: - ggp_recv_image_handler(gc, ev); + ggp_image_recv(gc, &ev->event.image_reply); break; case GG_EVENT_IMAGE_REQUEST: - ggp_send_image_handler(gc, ev); + ggp_image_send(gc, &ev->event.image_request); break; case GG_EVENT_NOTIFY: case GG_EVENT_NOTIFY_DESCR: @@ -1884,22 +1093,6 @@ ggp_generic_status_handler(gc, ev->event.status60.uin, GG_S(ev->event.status60.status), ev->event.status60.descr); break; - case GG_EVENT_USERLIST: - if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) { - purple_debug_info("gg", "GG_USERLIST_GET_REPLY\n"); - purple_notify_info(gc, NULL, - _("Buddy list downloaded"), - _("Your buddy list was downloaded from the server.")); - if (ev->event.userlist.reply != NULL) { - ggp_buddylist_load(gc, ev->event.userlist.reply); - } - } else { - purple_debug_info("gg", "GG_USERLIST_PUT_REPLY\n"); - purple_notify_info(gc, NULL, - _("Buddy list uploaded"), - _("Your buddy list was stored on the server.")); - } - break; case GG_EVENT_PUBDIR50_SEARCH_REPLY: ggp_pubdir_reply_handler(gc, ev->event.pubdir50); break; @@ -1911,6 +1104,21 @@ purple_debug_info("gg", "GG_EVENT_XML_EVENT\n"); ggp_xml_event_handler(gc, ev->event.xml_event.data); break; + case GG_EVENT_USER_DATA: + ggp_events_user_data(gc, &ev->event.user_data); + break; + case GG_EVENT_USERLIST100_VERSION: + ggp_roster_version(gc, &ev->event.userlist100_version); + break; + case GG_EVENT_USERLIST100_REPLY: + ggp_roster_reply(gc, &ev->event.userlist100_reply); + break; + case GG_EVENT_MULTILOGON_MSG: + ggp_multilogon_msg(gc, &ev->event.multilogon_msg); + break; + case GG_EVENT_MULTILOGON_INFO: + ggp_multilogon_info(gc, &ev->event.multilogon_info); + break; default: purple_debug_error("gg", "unsupported event type=%d\n", ev->type); @@ -1998,9 +1206,11 @@ PURPLE_INPUT_READ, ggp_callback_recv, gc); - ggp_buddylist_send(gc); purple_connection_update_progress(gc, _("Connected"), 1, 2); purple_connection_set_state(gc, PURPLE_CONNECTED); + + ggp_buddylist_send(gc); + ggp_roster_request_update(gc); } break; case GG_EVENT_CONN_FAILED: @@ -2105,14 +1315,19 @@ static char *ggp_status_text(PurpleBuddy *b) { - PurpleStatus *status; const char *msg; char *text; char *tmp; + ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(b); - status = purple_presence_get_active_status( - purple_buddy_get_presence(b)); - msg = purple_status_get_attr_string(status, "message"); + if (buddy_data && buddy_data->blocked) + msg = _("Blocked"); + else + { + PurpleStatus *status = purple_presence_get_active_status( + purple_buddy_get_presence(b)); + msg = purple_status_get_attr_string(status, "message"); + } if (msg == NULL) return NULL; @@ -2199,15 +1414,6 @@ NULL); types = g_list_append(types, type); - /* - * This status is necessary to display guys who are blocking *us*. - */ - type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE, - "blocked", _("Blocked"), TRUE, FALSE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), @@ -2257,18 +1463,15 @@ static void ggp_login(PurpleAccount *account) { - PurpleConnection *gc; - PurplePresence *presence; - PurpleStatus *status; + PurpleConnection *gc = purple_account_get_connection(account); struct gg_login_params *glp; GGPInfo *info; const char *address; const gchar *encryption_type; - if (ggp_setup_proxy(account) == -1) + if (!ggp_deprecated_setup_proxy(gc)) return; - gc = purple_account_get_connection(account); glp = g_new0(struct gg_login_params, 1); info = g_new0(GGPInfo, 1); @@ -2276,17 +1479,19 @@ info->session = NULL; info->chats = NULL; info->chats_count = 0; - info->token = NULL; info->searches = ggp_search_new(); - info->pending_richtext_messages = NULL; - info->pending_images = g_hash_table_new(g_direct_hash, g_direct_equal); - info->status_broadcasting = purple_account_get_bool(account, "status_broadcasting", TRUE); purple_connection_set_protocol_data(gc, info); - glp->uin = ggp_get_uin(account); - glp->password = charset_convert(purple_account_get_password(account), - "UTF-8", "CP1250"); + + ggp_image_setup(gc); + ggp_avatar_setup(gc); + ggp_roster_setup(gc); + ggp_multilogon_setup(gc); + ggp_status_setup(gc); + + glp->uin = ggp_str_to_uin(purple_account_get_username(account)); + glp->password = ggp_convert_to_cp1250(purple_account_get_password(account)); if (glp->uin == 0) { purple_connection_error(gc, @@ -2302,15 +1507,12 @@ if (purple_account_get_bool(account, "show_links_from_strangers", 1)) glp->status_flags |= GG_STATUS_FLAG_SPAM; - presence = purple_account_get_presence(account); - status = purple_presence_get_active_status(presence); - glp->encoding = GG_ENCODING_UTF8; - glp->protocol_features = (GG_FEATURE_STATUS80|GG_FEATURE_DND_FFC - |GG_FEATURE_TYPING_NOTIFICATION); + glp->protocol_features = (GG_FEATURE_DND_FFC | + GG_FEATURE_TYPING_NOTIFICATION | GG_FEATURE_MULTILOGON | + GG_FEATURE_USER_DATA); glp->async = 1; - glp->status = ggp_to_gg_status(status, &glp->status_descr); encryption_type = purple_account_get_string(account, "encryption", "opportunistic_tls"); @@ -2333,28 +1535,22 @@ glp->tls = GG_SSL_DISABLED; purple_debug_info("gg", "TLS mode: %d\n", glp->tls); - if (!info->status_broadcasting) - glp->status = glp->status|GG_STATUS_FRIENDS_MASK; + ggp_status_set_initial(gc, glp); address = purple_account_get_string(account, "gg_server", ""); - if (address && *address) { - /* TODO: Make this non-blocking */ - struct in_addr *addr = gg_gethostbyname(address); - - purple_debug_info("gg", "Using gg server given by user (%s)\n", address); - - if (addr == NULL) { - gchar *tmp = g_strdup_printf(_("Unable to resolve hostname '%s': %s"), - address, g_strerror(errno)); + if (address && *address) + { + glp->server_addr = inet_addr(address); + glp->server_port = 8074; + + if (glp->server_addr == INADDR_NONE) + { purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, /* should this be a settings error? */ - tmp); - g_free(tmp); + PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, + _("Provided server IP address is not valid")); + g_free(glp); return; } - - glp->server_addr = inet_addr(inet_ntoa(*addr)); - glp->server_port = 8074; } else purple_debug_info("gg", "Trying to retrieve address from gg appmsg service\n"); @@ -2387,22 +1583,43 @@ if (info) { PurpleStatus *status = purple_account_get_active_status(account); - if (info->session != NULL) { - ggp_set_status(account, status); + if (info->session != NULL) + { + const gchar *status_msg = purple_status_get_attr_string(status, "message"); + + if (ggp_status_set(account, + status_msg ? GG_STATUS_NOT_AVAIL_DESCR : GG_STATUS_NOT_AVAIL, + status_msg)) + { + struct gg_event *ev; + guint64 wait_start = ggp_microtime(), now; + int sleep_time = 5000; + while ((ev = gg_watch_fd(info->session)) != NULL) + { + if (ev->type == GG_EVENT_DISCONNECT_ACK) + break; + now = ggp_microtime(); + if (now - wait_start + sleep_time >= 100000) + break; + usleep(sleep_time); + sleep_time *= 2; + } + } gg_logoff(info->session); gg_free_session(info->session); } - purple_account_set_bool(account, "status_broadcasting", info->status_broadcasting); - /* Immediately close any notifications on this handle since that process depends * upon the contents of info->searches, which we are about to destroy. */ purple_notify_close_with_handle(gc); ggp_search_destroy(info->searches); - g_list_free(info->pending_richtext_messages); - g_hash_table_destroy(info->pending_images); + ggp_image_cleanup(gc); + ggp_avatar_cleanup(gc); + ggp_roster_cleanup(gc); + ggp_multilogon_cleanup(gc); + ggp_status_cleanup(gc); if (info->inpa > 0) purple_input_remove(info->inpa); @@ -2425,11 +1642,16 @@ gint pos = 0; GData *attribs; const char *start, *end = NULL, *last; + ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data( + purple_find_buddy(purple_connection_get_account(gc), who)); if (msg == NULL || *msg == '\0') { return 0; } + if (buddy_data && buddy_data->blocked) + return -1; + last = msg; /* Check if the message is richtext */ @@ -2439,53 +1661,58 @@ GString *string_buffer = g_string_new(NULL); struct gg_msg_richtext fmt; - do { - PurpleStoredImage *image; - const char *id; + do + { + const char *id = g_datalist_get_data(&attribs, "id"); + struct gg_msg_richtext_format actformat; + struct gg_msg_richtext_image actimage; + ggp_image_prepare_result prepare_result; /* Add text before the image */ - if(start - last) { + if(start - last) + { pos = pos + g_utf8_strlen(last, start - last); - g_string_append_len(string_buffer, last, start - last); + g_string_append_len(string_buffer, last, + start - last); + } + last = end + 1; + + if (id == NULL) + { + g_datalist_clear(&attribs); + continue; } - if((id = g_datalist_get_data(&attribs, "id")) && (image = purple_imgstore_find_by_id(atoi(id)))) { - struct gg_msg_richtext_format actformat; - struct gg_msg_richtext_image actimage; - gint image_size = purple_imgstore_get_size(image); - gconstpointer image_bin = purple_imgstore_get_data(image); - const char *image_filename = purple_imgstore_get_filename(image); - uint32_t crc32 = gg_crc32(0, image_bin, image_size); - - g_hash_table_insert(info->pending_images, GINT_TO_POINTER(crc32), GINT_TO_POINTER(atoi(id))); - purple_imgstore_ref(image); - purple_debug_info("gg", "ggp_send_im_richtext: got crc: %u for imgid: %i\n", crc32, atoi(id)); - + /* add the image itself */ + prepare_result = ggp_image_prepare( + gc, atoi(id), who, &actimage); + if (prepare_result == GGP_IMAGE_PREPARE_OK) + { actformat.font = GG_FONT_IMAGE; actformat.position = pos; - actimage.unknown1 = 0x0109; - actimage.size = gg_fix32(image_size); - actimage.crc32 = gg_fix32(crc32); - - if (actimage.size > 255000) { - purple_debug_warning("gg", "ggp_send_im_richtext: image over 255kb!\n"); - } else { - purple_debug_info("gg", "ggp_send_im_richtext: adding images to richtext, size: %i, crc32: %u, name: %s\n", actimage.size, actimage.crc32, image_filename); - - memcpy(format + format_length, &actformat, sizeof(actformat)); - format_length += sizeof(actformat); - memcpy(format + format_length, &actimage, sizeof(actimage)); - format_length += sizeof(actimage); - } - } else { - purple_debug_error("gg", "ggp_send_im_richtext: image not found in the image store!"); + memcpy(format + format_length, &actformat, + sizeof(actformat)); + format_length += sizeof(actformat); + memcpy(format + format_length, &actimage, + sizeof(actimage)); + format_length += sizeof(actimage); } - - last = end + 1; + else if (prepare_result == GGP_IMAGE_PREPARE_TOO_BIG) + { + PurpleConversation *conv = + purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, who, + purple_connection_get_account(gc)); + purple_conversation_write(conv, "", + _("Image is too large, please try " + "smaller one."), PURPLE_MESSAGE_ERROR, + time(NULL)); + } + g_datalist_clear(&attribs); - - } while(purple_markup_find_tag("img", last, &start, &end, &attribs)); + } while (purple_markup_find_tag("img", last, &start, &end, + &attribs)); /* Add text after the images */ if(last && *last) { @@ -2505,10 +1732,7 @@ plain = purple_unescape_html(msg); } - /* - tmp = charset_convert(plain, "UTF-8", "CP1250"); - */ - tmp = g_strdup_printf("%s", plain); + tmp = g_strdup(plain); if (tmp && (format_length - sizeof(struct gg_msg_richtext))) { if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) { @@ -2570,101 +1794,19 @@ purple_debug_info("gg", "ggp_get_info(): Added seq %u", seq); } -static int ggp_to_gg_status(PurpleStatus *status, char **msg) -{ - const char *status_id = purple_status_get_id(status); - int new_status, new_status_descr; - const char *new_msg; - - g_return_val_if_fail(msg != NULL, 0); - - purple_debug_info("gg", "ggp_to_gg_status: Requested status = %s\n", - status_id); - - if (strcmp(status_id, "available") == 0) { - new_status = GG_STATUS_AVAIL; - new_status_descr = GG_STATUS_AVAIL_DESCR; - } else if (strcmp(status_id, "freeforchat") == 0) { - new_status = GG_STATUS_FFC; - new_status_descr = GG_STATUS_FFC_DESCR; - } else if (strcmp(status_id, "away") == 0) { - new_status = GG_STATUS_BUSY; - new_status_descr = GG_STATUS_BUSY_DESCR; - } else if (strcmp(status_id, "unavailable") == 0) { - new_status = GG_STATUS_DND; - new_status_descr = GG_STATUS_DND_DESCR; - } else if (strcmp(status_id, "invisible") == 0) { - new_status = GG_STATUS_INVISIBLE; - new_status_descr = GG_STATUS_INVISIBLE_DESCR; - } else if (strcmp(status_id, "offline") == 0) { - new_status = GG_STATUS_NOT_AVAIL; - new_status_descr = GG_STATUS_NOT_AVAIL_DESCR; - } else { - new_status = GG_STATUS_AVAIL; - new_status_descr = GG_STATUS_AVAIL_DESCR; - purple_debug_info("gg", - "ggp_set_status: unknown status requested (status_id=%s)\n", - status_id); - } - - new_msg = purple_status_get_attr_string(status, "message"); - - if(new_msg) { - /* - char *tmp = purple_markup_strip_html(new_msg); - *msg = charset_convert(tmp, "UTF-8", "CP1250"); - g_free(tmp); - */ - *msg = purple_markup_strip_html(new_msg); - - return new_status_descr; - } else { - *msg = NULL; - return new_status; - } -} - -static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc; - GGPInfo *info; - int new_status; - char *new_msg = NULL; - - if (!purple_status_is_active(status)) - return; - - gc = purple_account_get_connection(account); - info = purple_connection_get_protocol_data(gc); - - new_status = ggp_to_gg_status(status, &new_msg); - - if (!info->status_broadcasting) - new_status = new_status|GG_STATUS_FRIENDS_MASK; - - if (new_msg == NULL) { - gg_change_status(info->session, new_status); - } else { - gg_change_status_descr(info->session, new_status, new_msg); - g_free(new_msg); - } - - ggp_status_fake_to_self(account); - -} - static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) { - PurpleAccount *account; + PurpleAccount *account = purple_connection_get_account(gc); GGPInfo *info = purple_connection_get_protocol_data(gc); const gchar *name = purple_buddy_get_name(buddy); gg_add_notify(info->session, ggp_str_to_uin(name)); - account = purple_connection_get_account(gc); - if (strcmp(purple_account_get_username(account), name) == 0) { - ggp_status_fake_to_self(account); - } + // gg server won't tell us our status here + if (strcmp(purple_account_get_username(account), name) == 0) + ggp_status_fake_to_self(gc); + + ggp_roster_add_buddy(gc, buddy, group, message); } static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, @@ -2673,6 +1815,7 @@ GGPInfo *info = purple_connection_get_protocol_data(gc); gg_remove_notify(info->session, ggp_str_to_uin(purple_buddy_get_name(buddy))); + ggp_roster_remove_buddy(gc, buddy, group); } static void ggp_join_chat(PurpleConnection *gc, GHashTable *data) @@ -2750,11 +1893,6 @@ uins[count++] = uin; } - /* - plain = purple_unescape_html(message); - msg = charset_convert(plain, "UTF-8", "CP1250"); - g_free(plain); - */ msg = purple_unescape_html(message); gg_send_message_confer(info->session, GG_CLASS_CHAT, count, uins, (unsigned char *)msg); @@ -2783,11 +1921,14 @@ } } -static void ggp_register_user(PurpleAccount *account) +static void ggp_action_chpass(PurplePluginAction *action) { - PurpleConnection *gc = purple_account_get_connection(account); + ggp_account_chpass((PurpleConnection *)action->context); +} - ggp_token_request(gc, ggp_register_user_dialog); +static void ggp_action_status_broadcasting(PurplePluginAction *action) +{ + ggp_status_broadcasting_dialog((PurpleConnection *)action->context); } static GList *ggp_actions(PurplePlugin *plugin, gpointer context) @@ -2796,31 +1937,19 @@ PurplePluginAction *act; act = purple_plugin_action_new(_("Change password..."), - ggp_change_passwd); + ggp_action_chpass); m = g_list_append(m, act); act = purple_plugin_action_new(_("Find buddies..."), ggp_find_buddies); m = g_list_append(m, act); - act = purple_plugin_action_new(_("Change status broadcasting"), - ggp_action_change_status_broadcasting); + act = purple_plugin_action_new(_("Show status only for buddies"), + ggp_action_status_broadcasting); m = g_list_append(m, act); m = g_list_append(m, NULL); - act = purple_plugin_action_new(_("Upload buddylist to Server"), - ggp_action_buddylist_put); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Download buddylist from Server"), - ggp_action_buddylist_get); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Delete buddylist from Server"), - ggp_action_buddylist_delete); - m = g_list_append(m, act); - act = purple_plugin_action_new(_("Save buddylist to file..."), ggp_action_buddylist_save); m = g_list_append(m, act); @@ -2832,20 +1961,53 @@ return m; } +static const char* ggp_list_emblem(PurpleBuddy *buddy) +{ + ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(buddy); + + if (!buddy_data) + return NULL; + + if (buddy_data->blocked) + return "not-authorized"; + + return NULL; +} + +static void ggp_buddy_free(PurpleBuddy *buddy) +{ + ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(buddy); + + if (!buddy_data) + return; + + g_free(buddy_data); + + purple_buddy_set_protocol_data(buddy, NULL); +} + static gboolean ggp_offline_message(const PurpleBuddy *buddy) { return TRUE; } +static GHashTable * ggp_get_account_text_table(PurpleAccount *account) +{ + GHashTable *table; + table = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(table, "login_label", (gpointer)_("GG number...")); + return table; +} + static PurplePluginProtocolInfo prpl_info = { sizeof(PurplePluginProtocolInfo), /* struct_size */ OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_IM_IMAGE, NULL, /* user_splits */ NULL, /* protocol_options */ - {"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ + {"png", 1, 1, 200, 200, 0, PURPLE_ICON_SCALE_DISPLAY | PURPLE_ICON_SCALE_SEND}, /* icon_spec */ ggp_list_icon, /* list_icon */ - NULL, /* list_emblem */ + ggp_list_emblem, /* list_emblem */ ggp_status_text, /* status_text */ ggp_tooltip_text, /* tooltip_text */ ggp_status_types, /* status_types */ @@ -2858,7 +2020,7 @@ NULL, /* set_info */ ggp_send_typing, /* send_typing */ ggp_get_info, /* get_info */ - ggp_set_status, /* set_away */ + ggp_status_set_purplestatus, /* set_away */ NULL, /* set_idle */ NULL, /* change_passwd */ ggp_add_buddy, /* add_buddy */ @@ -2878,15 +2040,15 @@ NULL, /* chat_whisper */ ggp_chat_send, /* chat_send */ ggp_keepalive, /* keepalive */ - ggp_register_user, /* register_user */ + ggp_account_register, /* register_user */ NULL, /* get_cb_info */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ + ggp_roster_alias_buddy, /* alias_buddy */ + ggp_roster_group_buddy, /* group_buddy */ + ggp_roster_rename_group, /* rename_group */ + ggp_buddy_free, /* buddy_free */ NULL, /* convo_closed */ ggp_normalize, /* normalize */ - NULL, /* set_buddy_icon */ + ggp_avatar_own_set, /* set_buddy_icon */ NULL, /* remove_group */ NULL, /* get_cb_real_name */ NULL, /* set_chat_topic */ @@ -2904,7 +2066,7 @@ NULL, /* unregister_user */ NULL, /* send_attention */ NULL, /* get_attention_types */ - NULL, /* get_account_text_table */ + ggp_get_account_text_table, /* get_account_text_table */ NULL, /* initiate_media */ NULL, /* can_do_media */ NULL, /* get_moods */ @@ -2975,6 +2137,9 @@ PurpleAccountOption *option; GList *encryption_options = NULL; + purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with " + "libgadu %s...\n", gg_libgadu_version()); + option = purple_account_option_string_new(_("GG server"), "gg_server", ""); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, @@ -3002,14 +2167,9 @@ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - my_protocol = plugin; - gg_debug_handler = purple_gg_debug_handler; -#ifdef _WIN32 - gg_global_set_custom_resolver(ggp_resolver_win32thread_start, - ggp_resolver_win32thread_cleanup); -#endif + ggp_resolver_purple_setup(); } PURPLE_INIT_PLUGIN(gg, init_plugin, info);
--- a/libpurple/protocols/gg/gg.h Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/gg.h Tue Aug 14 22:26:33 2012 +0200 @@ -24,15 +24,22 @@ #ifndef _PURPLE_GG_H #define _PURPLE_GG_H -#undef printf #include <libgadu.h> #include "internal.h" #include "search.h" #include "connection.h" +#include "image.h" +#include "avatar.h" +#include "account.h" +#include "roster.h" +#include "multilogon.h" +#include "status.h" #define PUBDIR_RESULTS_MAX 20 +#define GGP_UIN_LEN_MAX 10 + typedef struct { @@ -41,34 +48,21 @@ } GGPChat; -typedef void (*GGPTokenCallback)(PurpleConnection *); - -typedef struct -{ - char *id; - char *data; - unsigned int size; - - struct gg_http *req; - guint inpa; - - GGPTokenCallback cb; - -} GGPToken; - typedef struct { struct gg_session *session; guint inpa; - GGPToken *token; GList *chats; GGPSearches *searches; int chats_count; - GList *pending_richtext_messages; - GHashTable *pending_images; - gboolean status_broadcasting; //When TRUE status is visible to all, when FALSE status is visible only to friends. + + ggp_image_connection_data image_data; + ggp_avatar_session_data avatar_data; + ggp_roster_session_data roster_data; + ggp_multilogon_session_data *multilogon_data; + ggp_status_session_data *status_data; } GGPInfo; -#endif /* _PURPLE_GG_H */ +void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event_msg *ev, gboolean multilogon); -/* vim: set ts=8 sts=0 sw=8 noet: */ +#endif /* _PURPLE_GG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/image.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,265 @@ +#include "image.h" + +#include <debug.h> + +#include "gg.h" +#include "utils.h" + +#define GGP_PENDING_IMAGE_ID_PREFIX "gg-pending-image-" + +typedef struct +{ + uin_t from; + gchar *text; + time_t mtime; +} ggp_image_pending_message; + +typedef struct +{ + int id; + gchar *conv_name; +} ggp_image_pending_image; + +static void ggp_image_pending_message_free(gpointer data) +{ + ggp_image_pending_message *pending_message = + (ggp_image_pending_message*)data; + g_free(pending_message->text); + g_free(pending_message); +} + +static void ggp_image_pending_image_free(gpointer data) +{ + ggp_image_pending_image *pending_image = + (ggp_image_pending_image*)data; + g_free(pending_image->conv_name); + g_free(pending_image); +} + +static inline ggp_image_connection_data * +ggp_image_get_imgdata(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + return &accdata->image_data; +} + +void ggp_image_setup(PurpleConnection *gc) +{ + ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + + imgdata->pending_messages = NULL; + imgdata->pending_images = g_hash_table_new_full(NULL, NULL, NULL, + ggp_image_pending_image_free); +} + +void ggp_image_cleanup(PurpleConnection *gc) +{ + ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + + g_list_free_full(imgdata->pending_messages, + &ggp_image_pending_message_free); + g_hash_table_destroy(imgdata->pending_images); +} + +const char * ggp_image_pending_placeholder(uint32_t id) +{ + static char buff[50]; + + g_snprintf(buff, 50, "<img id=\"" GGP_PENDING_IMAGE_ID_PREFIX + "%u\">", id); + + return buff; +} + +void ggp_image_got_im(PurpleConnection *gc, uin_t from, gchar *text, + time_t mtime) +{ + ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + ggp_image_pending_message *pending_message = + g_new(ggp_image_pending_message, 1); + + purple_debug_info("gg", "ggp_image_got_im: received message with " + "images from %u: %s\n", from, text); + + pending_message->from = from; + pending_message->text = text; + pending_message->mtime = mtime; + + imgdata->pending_messages = g_list_append(imgdata->pending_messages, + pending_message); +} + +ggp_image_prepare_result ggp_image_prepare(PurpleConnection *gc, const int id, + const char *conv_name, struct gg_msg_richtext_image *image_info) +{ + ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + PurpleStoredImage *image = purple_imgstore_find_by_id(id); + size_t image_size; + gconstpointer image_data; + const char *image_filename; + uint32_t image_crc; + ggp_image_pending_image *pending_image; + + if (!image) + { + purple_debug_error("gg", "ggp_image_prepare_to_send: image %d " + "not found in image store\n", id); + return GGP_IMAGE_PREPARE_FAILURE; + } + + image_size = purple_imgstore_get_size(image); + + if (image_size > GGP_IMAGE_SIZE_MAX) + { + purple_debug_warning("gg", "ggp_image_prepare_to_send: image " + "is too big (max bytes: %d)\n", GGP_IMAGE_SIZE_MAX); + return GGP_IMAGE_PREPARE_TOO_BIG; + } + + purple_imgstore_ref(image); + image_data = purple_imgstore_get_data(image); + image_filename = purple_imgstore_get_filename(image); + image_crc = gg_crc32(0, image_data, image_size); + + purple_debug_info("gg", "ggp_image_prepare_to_send: image prepared " + "[id=%d, crc=%u, size=%d, filename=%s]\n", + id, image_crc, image_size, image_filename); + + pending_image = g_new(ggp_image_pending_image, 1); + pending_image->id = id; + pending_image->conv_name = g_strdup(conv_name); + g_hash_table_insert(imgdata->pending_images, GINT_TO_POINTER(image_crc), + pending_image); + + image_info->unknown1 = 0x0109; + image_info->size = gg_fix32(image_size); + image_info->crc32 = gg_fix32(image_crc); + + return GGP_IMAGE_PREPARE_OK; +} + +void ggp_image_recv(PurpleConnection *gc, + const struct gg_event_image_reply *image_reply) +{ + ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + int stored_id; + const char *imgtag_search; + gchar *imgtag_replace; + GList *pending_messages_it; + + stored_id = purple_imgstore_add_with_id( + g_memdup(image_reply->image, image_reply->size), + image_reply->size, + image_reply->filename); + + purple_debug_info("gg", "ggp_image_recv: got image " + "[id=%d, crc=%u, size=%u, filename=\"%s\"]\n", + stored_id, + image_reply->crc32, + image_reply->size, + image_reply->filename); + + imgtag_search = ggp_image_pending_placeholder(image_reply->crc32); + imgtag_replace = g_strdup_printf("<img src=\"" + PURPLE_STORED_IMAGE_PROTOCOL "%u\">", stored_id); + + pending_messages_it = g_list_first(imgdata->pending_messages); + while (pending_messages_it) + { + ggp_image_pending_message *pending_message = + (ggp_image_pending_message*)pending_messages_it->data; + gchar *newText; + + if (strstr(pending_message->text, imgtag_search) == NULL) + { + pending_messages_it = g_list_next(pending_messages_it); + continue; + } + + purple_debug_misc("gg", "ggp_image_recv: found message " + "containing image: %s\n", pending_message->text); + + newText = purple_strreplace(pending_message->text, + imgtag_search, imgtag_replace); + g_free(pending_message->text); + pending_message->text = newText; + + if (strstr(pending_message->text, + "<img id=\"" GGP_PENDING_IMAGE_ID_PREFIX) == NULL) + { + purple_debug_info("gg", "ggp_image_recv: " + "message is ready to display\n"); + serv_got_im(gc, ggp_uin_to_str(pending_message->from), + pending_message->text, + PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_IMAGES, + pending_message->mtime); + + ggp_image_pending_message_free(pending_message); + imgdata->pending_messages = g_list_remove( + imgdata->pending_messages, pending_message); + } + + pending_messages_it = g_list_next(pending_messages_it); + } + g_free(imgtag_replace); +} + +void ggp_image_send(PurpleConnection *gc, + const struct gg_event_image_request *image_request) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + ggp_image_pending_image *pending_image; + PurpleStoredImage *image; + PurpleConversation *conv; + + purple_debug_info("gg", "ggp_image_send: got image request " + "[uin=%u, crc=%u, size=%u]\n", + image_request->sender, + image_request->crc32, + image_request->size); + + pending_image = g_hash_table_lookup(imgdata->pending_images, + GINT_TO_POINTER(image_request->crc32)); + + if (pending_image == NULL) + { + purple_debug_warning("gg", "ggp_image_send: requested image " + "not found\n"); + return; + } + + purple_debug_misc("gg", "ggp_image_send: requested image found " + "[id=%d, conv=%s]\n", + pending_image->id, + pending_image->conv_name); + + image = purple_imgstore_find_by_id(pending_image->id); + + if (!image) + { + purple_debug_error("gg", "ggp_image_send: requested image " + "found, but doesn't exists in image store\n"); + g_hash_table_remove(imgdata->pending_images, + GINT_TO_POINTER(image_request->crc32)); + return; + } + + //TODO: check allowed recipients + gg_image_reply(accdata->session, image_request->sender, + purple_imgstore_get_filename(image), + purple_imgstore_get_data(image), + purple_imgstore_get_size(image)); + purple_imgstore_unref(image); + + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, pending_image->conv_name, + purple_connection_get_account(gc)); + if (conv != NULL) + purple_conversation_write(conv, "", _("Image delivered."), + PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_NOTIFY, + time(NULL)); + + g_hash_table_remove(imgdata->pending_images, + GINT_TO_POINTER(image_request->crc32)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/image.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,37 @@ +#ifndef _GGP_IMAGE_H +#define _GGP_IMAGE_H + +#include <internal.h> +#include <libgadu.h> + +#define GGP_IMAGE_SIZE_MAX 255000 + +typedef struct +{ + GList *pending_messages; + GHashTable *pending_images; +} ggp_image_connection_data; + +typedef enum +{ + GGP_IMAGE_PREPARE_OK = 0, + GGP_IMAGE_PREPARE_FAILURE, + GGP_IMAGE_PREPARE_TOO_BIG +} ggp_image_prepare_result; + +void ggp_image_setup(PurpleConnection *gc); +void ggp_image_cleanup(PurpleConnection *gc); + +const char * ggp_image_pending_placeholder(uint32_t id); + +void ggp_image_got_im(PurpleConnection *gc, uin_t from, gchar *msg, + time_t mtime); +ggp_image_prepare_result ggp_image_prepare(PurpleConnection *gc, const int id, + const char *conv_name, struct gg_msg_richtext_image *image_info); + +void ggp_image_recv(PurpleConnection *gc, + const struct gg_event_image_reply *image_reply); +void ggp_image_send(PurpleConnection *gc, + const struct gg_event_image_request *image_request); + +#endif /* _GGP_IMAGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/config.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,79 @@ +/* Local libgadu configuration file. */ + +/* libpurple's config */ +#include <config.h> + +#define GGP_QUOTE(x) GGP_QUOTE2(x) +#define GGP_QUOTE2(x) #x +#define GG_LIBGADU_VERSION GGP_QUOTE(GG_INTERNAL_LIBGADU_VERSION) + +/* Defined if libgadu was compiled for bigendian machine. */ +#undef GG_CONFIG_BIGENDIAN +#ifdef WORDS_BIGENDIAN +# define GG_CONFIG_BIGENDIAN +#endif + +/* Defined if this machine has gethostbyname_r(). */ +#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R + +/* Define to 1 if you have the `_exit' function. */ +#define HAVE__EXIT 1 + +/* Defined if libgadu was compiled and linked with fork support. */ +#undef GG_CONFIG_HAVE_FORK +#ifndef _WIN32 +# define GG_CONFIG_HAVE_FORK +#endif + +/* Defined if libgadu was compiled and linked with pthread support. */ +/* We don't use pthreads - they may not be safe. */ +#undef GG_CONFIG_HAVE_PTHREAD + +/* Defined if this machine has C99-compiliant vsnprintf(). */ +#undef HAVE_C99_VSNPRINTF +#ifndef _WIN32 +# define HAVE_C99_VSNPRINTF +#endif + +/* Defined if this machine has va_copy(). */ +#define GG_CONFIG_HAVE_VA_COPY + +/* Defined if this machine has __va_copy(). */ +#define GG_CONFIG_HAVE___VA_COPY + +/* Defined if this machine supports long long. */ +#undef GG_CONFIG_HAVE_LONG_LONG +#ifdef HAVE_LONG_LONG +# define GG_CONFIG_HAVE_LONG_LONG +#endif + +/* Defined if libgadu was compiled and linked with GnuTLS support. */ +#undef GG_CONFIG_HAVE_GNUTLS +#ifdef HAVE_GNUTLS +# define GG_CONFIG_HAVE_GNUTLS +#endif + +/* Defined if libgadu was compiled and linked with OpenSSL support. */ +/* OpenSSL cannot be used with libpurple due to licence type. */ +#undef GG_CONFIG_HAVE_OPENSSL + +/* Defined if libgadu was compiled and linked with zlib support. */ +#define GG_CONFIG_HAVE_ZLIB + +/* Defined if uintX_t types are defined in <stdint.h>. */ +#undef GG_CONFIG_HAVE_STDINT_H +#ifdef HAVE_STDINT_H +# define GG_CONFIG_HAVE_STDINT_H +#endif + +/* Defined if uintX_t types are defined in <inttypes.h>. */ +#undef GG_CONFIG_HAVE_INTTYPES_H +#ifdef HAVE_INTTYPES_H +# define GG_CONFIG_HAVE_INTTYPES_H +#endif + +/* Defined if uintX_t types are defined in <sys/types.h>. */ +#undef GG_CONFIG_HAVE_SYS_TYPES_H +#ifdef HAVE_SYS_TYPES_H +# define GG_CONFIG_HAVE_SYS_TYPES_H +#endif
--- a/libpurple/protocols/gg/lib/dcc7.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/dcc7.c Tue Aug 14 22:26:33 2012 +0200 @@ -49,8 +49,8 @@ #include "libgadu.h" #include "protocol.h" #include "resolver.h" -#include "libgadu-internal.h" -#include "libgadu-debug.h" +#include "internal.h" +#include "debug.h" #define gg_debug_dcc(dcc, level, fmt...) \ gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt)
--- a/libpurple/protocols/gg/lib/debug.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/debug.c Tue Aug 14 22:26:33 2012 +0200 @@ -32,7 +32,7 @@ #include <string.h> #include "libgadu.h" -#include "libgadu-debug.h" +#include "debug.h" /** * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/debug.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,29 @@ +/* + * (C) Copyright 2009 Wojtek Kaniewski <wojtekka@irc.pl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_DEBUG_H +#define LIBGADU_DEBUG_H + +#include "libgadu.h" + +const char *gg_debug_state(enum gg_state_t state); +const char *gg_debug_event(enum gg_event_t event); +void gg_debug_dump(struct gg_session *sess, int level, const char *buf, size_t len); +void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap); + +#endif /* LIBGADU_DEBUG_H */
--- a/libpurple/protocols/gg/lib/deflate.h Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/deflate.h Tue Aug 14 22:26:33 2012 +0200 @@ -1,7 +1,7 @@ /* $Id$ */ /* - * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> + * (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version
--- a/libpurple/protocols/gg/lib/encoding.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/encoding.c Tue Aug 14 22:26:33 2012 +0200 @@ -35,22 +35,22 @@ */ static const uint16_t table_cp1250[] = { - 0x20ac, '?', 0x201a, '?', 0x201e, 0x2026, 0x2020, 0x2021, - '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, - '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, - '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, - 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, - 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, - 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, - 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, - 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, - 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, - 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, - 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, - 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, - 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, - 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, - 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, + 0x20ac, '?', 0x201a, '?', 0x201e, 0x2026, 0x2020, 0x2021, + '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, + '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, + '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, + 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, + 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, + 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, + 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, + 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, + 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, + 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, + 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, }; /** @@ -136,11 +136,8 @@ uint32_t uc = 0, uc_min = 0; for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) { - if ((src[i] & 0xc0) == 0xc0) { + if ((src[i] & 0xc0) != 0x80) len++; - } else if ((src[i] & 0x80) == 0x00) { - len++; - } } if ((dst_length != -1) && (len > dst_length))
--- a/libpurple/protocols/gg/lib/events.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/events.c Tue Aug 14 22:26:33 2012 +0200 @@ -1,4 +1,4 @@ -/* $Id: events.c 1105 2011-05-25 21:34:50Z wojtekka $ */ +/* $Id: events.c 1144 2011-07-09 15:43:00Z wojtekka $ */ /* * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> @@ -33,9 +33,9 @@ #include "compat.h" #include "libgadu.h" #include "protocol.h" -#include "libgadu-internal.h" +#include "internal.h" #include "encoding.h" -#include "libgadu-debug.h" +#include "debug.h" #include "session.h" #include <errno.h> @@ -806,14 +806,14 @@ const gnutls_datum_t *peers; gnutls_x509_crt_t cert; - if (gnutls_x509_crt_init(&cert) >= 0) { + if (gnutls_x509_crt_init(&cert) == 0) { peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count); if (peers != NULL) { char buf[256]; size_t size; - if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) >= 0) { + if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) == 0) { size = sizeof(buf); gnutls_x509_crt_get_dn(cert, buf, &size); gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf); @@ -822,6 +822,8 @@ gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf); } } + + gnutls_x509_crt_deinit(cert); } }
--- a/libpurple/protocols/gg/lib/handlers.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/handlers.c Tue Aug 14 22:26:33 2012 +0200 @@ -39,7 +39,7 @@ #include "protocol.h" #include "encoding.h" #include "message.h" -#include "libgadu-internal.h" +#include "internal.h" #include "deflate.h" #include <errno.h>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/internal.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,45 @@ +/* $Id$ */ + +/* + * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_INTERNAL_H +#define LIBGADU_INTERNAL_H + +#include "libgadu.h" +#include "config.h" + +struct gg_dcc7_relay { + uint32_t addr; + uint16_t port; + uint8_t family; +}; + +typedef struct gg_dcc7_relay gg_dcc7_relay_t; + +int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length); + +int gg_resolve(int *fd, int *pid, const char *hostname); +int gg_resolve_pthread(int *fd, void **resolver, const char *hostname); +void gg_resolve_pthread_cleanup(void *resolver, int kill); + +#ifdef HAVE_UINT64_T +uint64_t gg_fix64(uint64_t x); +#endif + +#endif /* LIBGADU_INTERNAL_H */
--- a/libpurple/protocols/gg/lib/libgadu-config.h Tue Aug 14 22:05:05 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* Local libgadu configuration. */ - -#include "config.h" - -#ifndef __GG_LIBGADU_CONFIG_H -#define __GG_LIBGADU_CONFIG_H - -/* Defined if libgadu was compiled for bigendian machine. */ -#undef __GG_LIBGADU_BIGENDIAN -#ifdef WORDS_BIGENDIAN -# define __GG_LIBGADU_BIGENDIAN -#endif - -/* Defined if this machine has gethostbyname_r(). */ -#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R - -/* Defined if this machine has _exit(). */ -#define GG_CONFIG_HAVE__EXIT - -/* Defined if libgadu was compiled and linked with fork support. */ -#undef GG_CONFIG_HAVE_FORK -#ifndef _WIN32 -# define GG_CONFIG_HAVE_FORK -#endif - -/* Defined if libgadu was compiled and linked with pthread support. */ -/* We don't like pthreads. */ -#undef __GG_LIBGADU_HAVE_PTHREAD - -/* Defined if this machine has C99-compiliant vsnprintf(). */ -#undef __GG_LIBGADU_HAVE_C99_VSNPRINTF -#ifndef _WIN32 -# define __GG_LIBGADU_HAVE_C99_VSNPRINTF -#endif - -/* Defined if this machine has va_copy(). */ -#define __GG_LIBGADU_HAVE_VA_COPY - -/* Defined if this machine has __va_copy(). */ -#define __GG_LIBGADU_HAVE___VA_COPY - -/* Defined if this machine supports long long. */ -#undef __GG_LIBGADU_HAVE_LONG_LONG -#ifdef HAVE_LONG_LONG -# define __GG_LIBGADU_HAVE_LONG_LONG -#endif - -/* Defined if libgadu was compiled and linked with GnuTLS support. */ -#undef GG_CONFIG_HAVE_GNUTLS -#ifdef HAVE_GNUTLS -# define GG_CONFIG_HAVE_GNUTLS -#endif - -/* Defined if libgadu was compiled and linked with OpenSSL support. */ -/* Always undefined in Purple. */ -#undef __GG_LIBGADU_HAVE_OPENSSL - -/* Defined if libgadu was compiled and linked with zlib support. */ -#undef GG_CONFIG_HAVE_ZLIB - -/* Defined if uintX_t types are defined in <stdint.h>. */ -#undef GG_CONFIG_HAVE_STDINT_H -#if HAVE_STDINT_H -# define GG_CONFIG_HAVE_STDINT_H -#endif - - -#define vnsprintf g_vnsprintf - -#endif
--- a/libpurple/protocols/gg/lib/libgadu-debug.h Tue Aug 14 22:05:05 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -/* - * (C) Copyright 2009 Wojtek Kaniewski <wojtekka@irc.pl> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#ifndef LIBGADU_DEBUG_H -#define LIBGADU_DEBUG_H - -#include "libgadu.h" - -const char *gg_debug_state(enum gg_state_t state); -const char *gg_debug_event(enum gg_event_t event); -void gg_debug_dump(struct gg_session *sess, int level, const char *buf, size_t len); -void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap); - -#endif /* LIBGADU_DEBUG_H */
--- a/libpurple/protocols/gg/lib/libgadu-internal.h Tue Aug 14 22:05:05 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#ifndef LIBGADU_INTERNAL_H -#define LIBGADU_INTERNAL_H - -#include "libgadu.h" - -struct gg_dcc7_relay { - uint32_t addr; - uint16_t port; - uint8_t family; -}; - -typedef struct gg_dcc7_relay gg_dcc7_relay_t; - -int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length); - -int gg_resolve(int *fd, int *pid, const char *hostname); -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname); -void gg_resolve_pthread_cleanup(void *resolver, int kill); - -#ifdef HAVE_UINT64_T -uint64_t gg_fix64(uint64_t x); -#endif - -#endif /* LIBGADU_INTERNAL_H */
--- a/libpurple/protocols/gg/lib/libgadu.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/libgadu.c Tue Aug 14 22:26:33 2012 +0200 @@ -1,4 +1,4 @@ -/* $Id: libgadu.c 1102 2011-05-05 21:17:57Z wojtekka $ */ +/* $Id: libgadu.c 1245 2012-01-10 22:48:31Z wojtekka $ */ /* * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl> @@ -37,9 +37,9 @@ #include "libgadu.h" #include "protocol.h" #include "resolver.h" -#include "libgadu-internal.h" +#include "internal.h" #include "encoding.h" -#include "libgadu-debug.h" +#include "debug.h" #include "session.h" #include "message.h" #include "deflate.h" @@ -60,8 +60,6 @@ # include <openssl/rand.h> #endif -#define GG_LIBGADU_VERSION "1.11.0" - /** * Port gniazda nasłuchującego dla połączeń bezpośrednich. * @@ -132,7 +130,7 @@ #ifdef __GNUC__ __attribute__ ((unused)) #endif -= "$Id: libgadu.c 1102 2011-05-05 21:17:57Z wojtekka $"; += "$Id: libgadu.c 1245 2012-01-10 22:48:31Z wojtekka $"; #endif #endif /* DOXYGEN */ @@ -149,7 +147,7 @@ return GG_LIBGADU_VERSION; } -#ifdef GG_CONFIG_HAVE_UINT64_T +#ifdef HAVE_UINT64_T /** * \internal Zamienia kolejność bajtów w 64-bitowym słowie. * @@ -178,7 +176,7 @@ ((x & (uint64_t) 0xff00000000000000ULL) >> 56)); #endif } -#endif /* GG_CONFIG_HAVE_UINT64_T */ +#endif /* HAVE_UINT64_T */ /** * \internal Zamienia kolejność bajtów w 32-bitowym słowie. @@ -443,11 +441,11 @@ res = written; } } else { - res = 0; - if (sess->send_buf == NULL) { res = gg_write_common(sess, buf, length); + if (res == -1 && errno == EAGAIN) + res = 0; if (res == -1) return -1; } @@ -1112,18 +1110,6 @@ sess->fd = -1; } -#ifdef GG_CONFIG_HAVE_GNUTLS - if (sess->ssl != NULL) { - gg_session_gnutls_t *tmp; - - tmp = (gg_session_gnutls_t*) sess->ssl; - gnutls_deinit(tmp->session); - gnutls_certificate_free_credentials(tmp->xcred); - gnutls_global_deinit(); - free(sess->ssl); - } -#endif - if (sess->send_buf) { free(sess->send_buf); sess->send_buf = NULL; @@ -1155,6 +1141,18 @@ free(sess->recv_buf); free(sess->header_buf); +#ifdef GG_CONFIG_HAVE_GNUTLS + if (sess->ssl != NULL) { + gg_session_gnutls_t *tmp; + + tmp = (gg_session_gnutls_t*) sess->ssl; + gnutls_deinit(tmp->session); + gnutls_certificate_free_credentials(tmp->xcred); + gnutls_global_deinit(); + free(sess->ssl); + } +#endif + #ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) SSL_free(sess->ssl);
--- a/libpurple/protocols/gg/lib/libgadu.h Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/libgadu.h Tue Aug 14 22:26:33 2012 +0200 @@ -101,7 +101,7 @@ /* Defined if uintX_t types are defined in <sys/types.h>. */ #undef GG_CONFIG_HAVE_SYS_TYPES_H -#include "libgadu-config.h" +#include "config.h" #ifdef GG_CONFIG_HAVE_OPENSSL #include <openssl/ssl.h>
--- a/libpurple/protocols/gg/lib/obsolete.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/obsolete.c Tue Aug 14 22:26:33 2012 +0200 @@ -34,7 +34,7 @@ #include <errno.h> #include "libgadu.h" -#include "libgadu-internal.h" +#include "internal.h" struct gg_http *gg_userlist_get(uin_t uin, const char *passwd, int async) {
--- a/libpurple/protocols/gg/lib/pubdir50.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/pubdir50.c Tue Aug 14 22:26:33 2012 +0200 @@ -31,7 +31,7 @@ #include <time.h> #include "libgadu.h" -#include "libgadu-internal.h" +#include "internal.h" #include "encoding.h" /**
--- a/libpurple/protocols/gg/lib/resolver.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/lib/resolver.c Tue Aug 14 22:26:33 2012 +0200 @@ -234,7 +234,7 @@ /* Kopiuj */ for (i = 0; he->h_addr_list[i] != NULL; i++) - memcpy(&((*result)[i]), he->h_addr_list[0], sizeof(struct in_addr)); + memcpy(&((*result)[i]), he->h_addr_list[i], sizeof(struct in_addr)); (*result)[i].s_addr = INADDR_NONE; @@ -249,6 +249,9 @@ /** * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora. * + * \note Użycie logowania w tej funkcji może mieć negatywny wpływ na + * aplikacje jednowątkowe korzystające. + * * \param fd Deskryptor * \param hostname Nazwa serwera * @@ -260,11 +263,10 @@ int addr_count; int res = 0; - gg_debug(GG_DEBUG_MISC, "// gg_resolver_run(%d, %s)\n", fd, hostname); - if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) { if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 1) == -1) { addr_list = addr_ip; + addr_count = 0; /* addr_ip[0] już zawiera INADDR_NONE */ } } else { @@ -273,8 +275,6 @@ addr_count = 1; } - gg_debug(GG_DEBUG_MISC, "// gg_resolver_run() count = %d\n", addr_count); - if (write(fd, addr_list, (addr_count + 1) * sizeof(struct in_addr)) != (addr_count + 1) * sizeof(struct in_addr)) res = -1; @@ -375,7 +375,7 @@ status = (gg_resolver_run(pipes[1], hostname) == -1) ? 1 : 0; -#ifdef GG_CONFIG_HAVE__EXIT +#ifdef HAVE__EXIT _exit(status); #else exit(status);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/libgadu-events.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,54 @@ +#include "libgadu-events.h" + +#include <debug.h> + +#include "avatar.h" + +void ggp_events_user_data(PurpleConnection *gc, struct gg_event_user_data *data) +{ + int user_idx; + gboolean is_update; + + purple_debug_info("gg", "GG_EVENT_USER_DATA [type=%d, user_count=%d]\n", + data->type, data->user_count); + + /* + type = + 1, 3: user information sent after connecting (divided by + 20 contacts; 3 - last one; 1 - rest of them) + 0: data update + */ + is_update = (data->type == 0); + + for (user_idx = 0; user_idx < data->user_count; user_idx++) + { + struct gg_event_user_data_user *data_user = + &data->users[user_idx]; + uin_t uin = data_user->uin; + int attr_idx; + gboolean got_avatar = FALSE; + for (attr_idx = 0; attr_idx < data_user->attr_count; attr_idx++) + { + struct gg_event_user_data_attr *data_attr = + &data_user->attrs[attr_idx]; + if (strcmp(data_attr->key, "avatar") == 0) + { + time_t timestamp; + if (data_attr->type == 0) + { + ggp_avatar_buddy_remove(gc, uin); + continue; + } + + timestamp = atoi(data_attr->value); + if (timestamp <= 0) + continue; + got_avatar = TRUE; + ggp_avatar_buddy_update(gc, uin, timestamp); + } + } + + if (!is_update && !got_avatar) + ggp_avatar_buddy_remove(gc, uin); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/libgadu-events.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,10 @@ +#ifndef _GGP_LIBGADU_EVENTS_H +#define _GGP_LIBGADU_EVENTS_H + +#include <internal.h> +#include <libgadu.h> + +void ggp_events_user_data(PurpleConnection *gc, + struct gg_event_user_data *data); + +#endif /* _GGP_LIBGADU_EVENTS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/libgaduw.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,112 @@ +#include "libgaduw.h" + +#include <debug.h> + +#include "purplew.h" + +/******************************************************************************* + * HTTP requests. + ******************************************************************************/ + +static void ggp_libgaduw_http_processing_cancel(PurpleConnection *gc, + void *_req); + +static void ggp_libgaduw_http_handler(gpointer _req, gint fd, + PurpleInputCondition cond); + +static void ggp_libgaduw_http_finish(ggp_libgaduw_http_req *req, + gboolean success); + +/******************************************************************************/ + +ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc, + struct gg_http *h, ggp_libgaduw_http_cb cb, + gpointer user_data, gboolean show_processing) +{ + ggp_libgaduw_http_req *req; + purple_debug_misc("gg", "ggp_libgaduw_http_watch(h=%x, " + "show_processing=%d)\n", (unsigned int)h, show_processing); + + req = g_new(ggp_libgaduw_http_req, 1); + req->user_data = user_data; + req->cb = cb; + req->cancelled = FALSE; + req->h = h; + req->processing = NULL; + if (show_processing) + req->processing = ggp_purplew_request_processing(gc, NULL, + req, ggp_libgaduw_http_processing_cancel); + req->inpa = ggp_purplew_http_input_add(h, ggp_libgaduw_http_handler, + req); + + return req; +} + +static void ggp_libgaduw_http_processing_cancel(PurpleConnection *gc, + void *_req) +{ + ggp_libgaduw_http_req *req = _req; + req->processing = NULL; + ggp_libgaduw_http_cancel(req); +} + +static void ggp_libgaduw_http_handler(gpointer _req, gint fd, + PurpleInputCondition cond) +{ + ggp_libgaduw_http_req *req = _req; + + if (req->h->callback(req->h) == -1 || req->h->state == GG_STATE_ERROR) + { + purple_debug_error("gg", "ggp_libgaduw_http_handler: failed to " + "make http request: %d\n", req->h->error); + ggp_libgaduw_http_finish(req, FALSE); + return; + } + + //TODO: verbose mode + //purple_debug_misc("gg", "ggp_libgaduw_http_handler: got fd update " + // "[check=%d, state=%d]\n", req->h->check, req->h->state); + + if (req->h->state != GG_STATE_DONE) + { + purple_input_remove(req->inpa); + req->inpa = ggp_purplew_http_input_add(req->h, + ggp_libgaduw_http_handler, req); + return; + } + + if (!req->h->data || !req->h->body) + { + purple_debug_error("gg", "ggp_libgaduw_http_handler: got empty " + "http response: %d\n", req->h->error); + ggp_libgaduw_http_finish(req, FALSE); + return; + } + + ggp_libgaduw_http_finish(req, TRUE); +} + +void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req) +{ + purple_debug_misc("gg", "ggp_libgaduw_http_cancel\n"); + req->cancelled = TRUE; + gg_http_stop(req->h); + ggp_libgaduw_http_finish(req, FALSE); +} + +static void ggp_libgaduw_http_finish(ggp_libgaduw_http_req *req, + gboolean success) +{ + purple_debug_misc("gg", "ggp_libgaduw_http_finish(h=%x, processing=%x):" + " success=%d\n", (unsigned int)req->h, + (unsigned int)req->processing, success); + if (req->processing) + { + ggp_purplew_request_processing_done(req->processing); + req->processing = NULL; + } + purple_input_remove(req->inpa); + req->cb(req->h, success, req->cancelled, req->user_data); + req->h->destroy(req->h); + g_free(req); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/libgaduw.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,29 @@ +#ifndef _GGP_LIBGADUW_H +#define _GGP_LIBGADUW_H + +#include <internal.h> +#include <libgadu.h> + +#include "purplew.h" + +typedef void (*ggp_libgaduw_http_cb)(struct gg_http *h, gboolean success, + gboolean cancelled, gpointer user_data); + +typedef struct +{ + gpointer user_data; + ggp_libgaduw_http_cb cb; + + gboolean cancelled; + struct gg_http *h; + ggp_purplew_request_processing_handle *processing; + guint inpa; +} ggp_libgaduw_http_req; + +ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc, + struct gg_http *h, ggp_libgaduw_http_cb cb, gpointer user_data, + gboolean show_processing); +void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req); + + +#endif /* _GGP_LIBGADUW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/multilogon.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,65 @@ +#include "multilogon.h" + +#include <debug.h> + +#include "gg.h" + +struct _ggp_multilogon_session_data +{ + int session_count; +}; + +static inline ggp_multilogon_session_data * +ggp_multilogon_get_mldata(PurpleConnection *gc); + +//////////// + +static inline ggp_multilogon_session_data * +ggp_multilogon_get_mldata(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + return accdata->multilogon_data; +} + +void ggp_multilogon_setup(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + + ggp_multilogon_session_data *mldata = g_new0(ggp_multilogon_session_data, 1); + accdata->multilogon_data = mldata; +} + +void ggp_multilogon_cleanup(PurpleConnection *gc) +{ + ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc); + g_free(mldata); +} + +void ggp_multilogon_msg(PurpleConnection *gc, struct gg_event_msg *msg) +{ + ggp_recv_message_handler(gc, msg, TRUE); +} + +void ggp_multilogon_info(PurpleConnection *gc, + struct gg_event_multilogon_info *info) +{ + ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc); + int i; + + purple_debug_info("gg", "ggp_multilogon_info: session list changed\n"); + for (i = 0; i < info->count; i++) + { + purple_debug_misc("gg", "ggp_multilogon_info: " + "session [%s] logged in at %lu\n", + info->sessions[i].name, + info->sessions[i].logon_time); + } + + mldata->session_count = info->count; +} + +int ggp_multilogon_get_session_count(PurpleConnection *gc) +{ + ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc); + return mldata->session_count; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/multilogon.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,18 @@ +#ifndef _GGP_MULTILOGON_H +#define _GGP_MULTILOGON_H + +#include <internal.h> +#include <libgadu.h> + +typedef struct _ggp_multilogon_session_data ggp_multilogon_session_data; + +void ggp_multilogon_setup(PurpleConnection *gc); +void ggp_multilogon_cleanup(PurpleConnection *gc); + +void ggp_multilogon_msg(PurpleConnection *gc, struct gg_event_msg *msg); +void ggp_multilogon_info(PurpleConnection *gc, + struct gg_event_multilogon_info *msg); + +int ggp_multilogon_get_session_count(PurpleConnection *gc); + +#endif /* _GGP_MULTILOGON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/oauth/oauth-parameter.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,154 @@ +/* + * (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// source: http://toxygen.net/libgadu/ + +#include "oauth-parameter.h" + +struct gg_oauth_parameter { + char *key; + char *value; + struct gg_oauth_parameter *next; +}; + +int gg_oauth_parameter_set(gg_oauth_parameter_t **list, const char *key, const char *value) +{ + gg_oauth_parameter_t *p, *new_p; + char *new_key; + char *new_value; + + if (value == NULL) + return 0; + + if (list == NULL) + return -1; + + new_key = strdup(key); + + if (new_key == NULL) + return -1; + + new_value = strdup(value); + + if (new_value == NULL) { + free(new_key); + return -1; + } + + new_p = malloc(sizeof(gg_oauth_parameter_t)); + + if (new_p == NULL) { + free(new_key); + free(new_value); + return -1; + } + + memset(new_p, 0, sizeof(gg_oauth_parameter_t)); + new_p->key = new_key; + new_p->value = new_value; + + if (*list != NULL) { + p = *list; + + while (p != NULL && p->next != NULL) + p = p->next; + + p->next = new_p; + } else { + *list = new_p; + } + + return 0; +} + +char *gg_oauth_parameter_join(gg_oauth_parameter_t *list, int header) +{ + gg_oauth_parameter_t *p; + int len = 0; + char *res, *out; + + if (header) + len += strlen("Authorization: OAuth "); + + for (p = list; p; p = p->next) { + gchar *escaped; + len += strlen(p->key); + + len += (header) ? 3 : 1; + + escaped = g_uri_escape_string(p->value, NULL, FALSE); + len += strlen(escaped); + g_free(escaped); + + if (p->next) + len += 1; + } + + res = malloc(len + 1); + + if (res == NULL) + return NULL; + + out = res; + + *out = 0; + + if (header) { + strcpy(out, "Authorization: OAuth "); + out += strlen(out); + } + + for (p = list; p; p = p->next) { + gchar *escaped; + strcpy(out, p->key); + out += strlen(p->key); + + strcpy(out++, "="); + + if (header) + strcpy(out++, "\""); + + escaped = g_uri_escape_string(p->value, NULL, FALSE); + strcpy(out, escaped); + out += strlen(escaped); + g_free(escaped); + + if (header) + strcpy(out++, "\""); + + if (p->next != NULL) + strcpy(out++, (header) ? "," : "&"); + } + + return res; +} + +void gg_oauth_parameter_free(gg_oauth_parameter_t *list) +{ + while (list != NULL) { + gg_oauth_parameter_t *next; + + next = list->next; + + free(list->key); + free(list->value); + free(list); + + list = next; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/oauth/oauth-parameter.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,14 @@ +// source: http://toxygen.net/libgadu/ + +#ifndef _GGP_OAUTH_PARAMETER_H +#define _GGP_OAUTH_PARAMETER_H + +#include <internal.h> + +typedef struct gg_oauth_parameter gg_oauth_parameter_t; + +int gg_oauth_parameter_set(gg_oauth_parameter_t **list, const char *key, const char *value); +char *gg_oauth_parameter_join(gg_oauth_parameter_t *list, int header); +void gg_oauth_parameter_free(gg_oauth_parameter_t *list); + +#endif /* _GGP_OAUTH_PARAMETER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/oauth/oauth-purple.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,225 @@ +#include "oauth-purple.h" + +#include "oauth.h" +#include "../utils.h" +#include "../xml.h" + +#include <debug.h> + +#define GGP_OAUTH_RESPONSE_MAX 10240 + +typedef struct +{ + PurpleConnection *gc; + ggp_oauth_request_cb callback; + gpointer user_data; + gchar *token; + gchar *token_secret; +} ggp_oauth_data; + +static void ggp_oauth_data_free(ggp_oauth_data *data); + +static void ggp_oauth_request_token_got(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message); + +static void ggp_oauth_authorization_done(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message); + +static void ggp_oauth_access_token_got(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message); + +static void ggp_oauth_data_free(ggp_oauth_data *data) +{ + g_free(data->token); + g_free(data->token_secret); + g_free(data); +} + +void ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback, + gpointer user_data) +{ + PurpleAccount *account = purple_connection_get_account(gc); + char *auth; + const char *method = "POST"; + const char *url = "http://api.gadu-gadu.pl/request_token"; + gchar *request; + ggp_oauth_data *data; + + purple_debug_misc("gg", "ggp_oauth_request: requesting token...\n"); + + auth = gg_oauth_generate_header(method, url, + purple_account_get_username(account), + purple_account_get_password(account), NULL, NULL); + request = g_strdup_printf( + "POST /request_token HTTP/1.1\r\n" + "Host: api.gadu-gadu.pl\r\n" + "%s\r\n" + "Content-Length: 0\r\n" + "\r\n", + auth); + free(auth); + + data = g_new0(ggp_oauth_data, 1); + data->gc = gc; + data->callback = callback; + data->user_data = user_data; + + purple_util_fetch_url_request(account, url, FALSE, NULL, TRUE, request, + FALSE, GGP_OAUTH_RESPONSE_MAX, ggp_oauth_request_token_got, + data); + + g_free(request); +} + +static void ggp_oauth_request_token_got(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message) +{ + ggp_oauth_data *data = user_data; + PurpleAccount *account; + xmlnode *xml; + gchar *request, *request_data; + gboolean succ = TRUE; + + if (!PURPLE_CONNECTION_IS_VALID(data->gc)) + { + ggp_oauth_data_free(data); + return; + } + account = purple_connection_get_account(data->gc); + + if (len == 0) + { + purple_debug_error("gg", "ggp_oauth_request_token_got: " + "requested token not received\n"); + ggp_oauth_data_free(data); + return; + } + + purple_debug_misc("gg", "ggp_oauth_request_token_got: " + "got request token, doing authorization...\n"); + + xml = xmlnode_from_str(url_text, -1); + if (xml == NULL) + { + purple_debug_error("gg", "ggp_oauth_request_token_got: " + "invalid xml\n"); + ggp_oauth_data_free(data); + return; + } + + succ &= ggp_xml_get_string(xml, "oauth_token", &data->token); + succ &= ggp_xml_get_string(xml, "oauth_token_secret", + &data->token_secret); + xmlnode_free(xml); + if (!succ) + { + purple_debug_error("gg", "ggp_oauth_request_token_got: " + "invalid xml - token is not present\n"); + ggp_oauth_data_free(data); + return; + } + + request_data = g_strdup_printf( + "callback_url=http://www.mojageneracja.pl&request_token=%s&" + "uin=%s&password=%s", data->token, + purple_account_get_username(account), + purple_account_get_password(account)); + request = g_strdup_printf( + "POST /authorize HTTP/1.1\r\n" + "Host: login.gadu-gadu.pl\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n%s", + strlen(request_data), request_data); + g_free(request_data); + + // we don't need any results, nor 302 redirection + purple_util_fetch_url_request(account, + "https://login.gadu-gadu.pl/authorize", FALSE, NULL, TRUE, + request, FALSE, 0, + ggp_oauth_authorization_done, data); + + g_free(request); +} + +static void ggp_oauth_authorization_done(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message) +{ + ggp_oauth_data *data = user_data; + PurpleAccount *account; + char *auth; + const char *url = "http://api.gadu-gadu.pl/access_token"; + gchar *request; + + if (!PURPLE_CONNECTION_IS_VALID(data->gc)) + { + ggp_oauth_data_free(data); + return; + } + account = purple_connection_get_account(data->gc); + + purple_debug_misc("gg", "ggp_oauth_authorization_done: " + "authorization done, requesting access token...\n"); + + auth = gg_oauth_generate_header("POST", url, + purple_account_get_username(account), + purple_account_get_password(account), + data->token, data->token_secret); + + request = g_strdup_printf( + "POST /access_token HTTP/1.1\r\n" + "Host: api.gadu-gadu.pl\r\n" + "%s\r\n" + "Content-Length: 0\r\n" + "\r\n", + auth); + free(auth); + + purple_util_fetch_url_request(account, url, FALSE, NULL, TRUE, request, + FALSE, GGP_OAUTH_RESPONSE_MAX, ggp_oauth_access_token_got, + data); + + g_free(request); +} + +static void ggp_oauth_access_token_got(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, gsize len, + const gchar *error_message) +{ + ggp_oauth_data *data = user_data; + gchar *token; + xmlnode *xml; + gboolean succ = TRUE; + + xml = xmlnode_from_str(url_text, -1); + if (xml == NULL) + { + purple_debug_error("gg", "ggp_oauth_access_token_got: " + "invalid xml\n"); + ggp_oauth_data_free(data); + return; + } + + succ &= ggp_xml_get_string(xml, "oauth_token", &token); + xmlnode_free(xml); + if (!succ || strlen(token) < 10) + { + purple_debug_error("gg", "ggp_oauth_access_token_got: " + "invalid xml - token is not present\n"); + ggp_oauth_data_free(data); + return; + } + + purple_debug_misc("gg", "ggp_oauth_access_token_got: " + "got access token\n"); + + data->callback(data->gc, token, data->user_data); + + g_free(token); + ggp_oauth_data_free(data); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/oauth/oauth-purple.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,13 @@ +#ifndef _GGP_OAUTH_PURPLE_H +#define _GGP_OAUTH_PURPLE_H + +#include <internal.h> +#include <libgadu.h> + +typedef void (*ggp_oauth_request_cb)(PurpleConnection *gc, const gchar *token, + gpointer user_data); + +void ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback, + gpointer user_data); + +#endif /* _GGP_OAUTH_PURPLE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/oauth/oauth.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,139 @@ +/* + * (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// source: http://toxygen.net/libgadu/ + +#include "oauth.h" + +#include "oauth-parameter.h" +#include <cipher.h> + +char *gg_oauth_static_nonce; /* dla unit testów */ +char *gg_oauth_static_timestamp; /* dla unit testów */ + +static void gg_oauth_generate_nonce(char *buf, int len) +{ + const char charset[] = "0123456789"; + + if (buf == NULL || len < 1) + return; + + while (len > 1) { + *buf++ = charset[(unsigned) (((float) sizeof(charset) - 1.0) * rand() / (RAND_MAX + 1.0))]; + len--; + } + + *buf = 0; +} + +static gchar *gg_hmac_sha1(const char *key, const char *message) +{ + PurpleCipherContext *context; + guchar digest[20]; + + context = purple_cipher_context_new_by_name("hmac", NULL); + purple_cipher_context_set_option(context, "hash", "sha1"); + purple_cipher_context_set_key(context, (guchar *)key); + purple_cipher_context_append(context, (guchar *)message, strlen(message)); + purple_cipher_context_digest(context, sizeof(digest), digest, NULL); + purple_cipher_context_destroy(context); + + return purple_base64_encode(digest, sizeof(digest)); +} + +static char *gg_oauth_generate_signature(const char *method, const char *url, const char *request, const char *consumer_secret, const char *token_secret) +{ + char *text, *key, *res; + gchar *url_e, *request_e, *consumer_secret_e, *token_secret_e; + + url_e = g_uri_escape_string(url, NULL, FALSE); + request_e = g_uri_escape_string(request, NULL, FALSE); + text = g_strdup_printf("%s&%s&%s", method, url_e, request_e); + g_free(url_e); + g_free(request_e); + + consumer_secret_e = g_uri_escape_string(consumer_secret, NULL, FALSE); + token_secret_e = g_uri_escape_string(token_secret, NULL, FALSE); + key = g_strdup_printf("%s&%s", consumer_secret, token_secret ? token_secret : ""); + g_free(consumer_secret_e); + g_free(token_secret_e); + + res = gg_hmac_sha1(key, text); + + free(key); + free(text); + + return res; +} + +char *gg_oauth_generate_header(const char *method, const char *url, const const char *consumer_key, const char *consumer_secret, const char *token, const char *token_secret) +{ + char *request, *signature, *res; + char nonce[80], timestamp[16]; + gg_oauth_parameter_t *params = NULL; + + if (gg_oauth_static_nonce == NULL) + gg_oauth_generate_nonce(nonce, sizeof(nonce)); + else { + strncpy(nonce, gg_oauth_static_nonce, sizeof(nonce) - 1); + nonce[sizeof(nonce) - 1] = 0; + } + + if (gg_oauth_static_timestamp == NULL) + snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL)); + else { + strncpy(timestamp, gg_oauth_static_timestamp, sizeof(timestamp) - 1); + timestamp[sizeof(timestamp) - 1] = 0; + } + + gg_oauth_parameter_set(¶ms, "oauth_consumer_key", consumer_key); + gg_oauth_parameter_set(¶ms, "oauth_nonce", nonce); + gg_oauth_parameter_set(¶ms, "oauth_signature_method", "HMAC-SHA1"); + gg_oauth_parameter_set(¶ms, "oauth_timestamp", timestamp); + gg_oauth_parameter_set(¶ms, "oauth_token", token); + gg_oauth_parameter_set(¶ms, "oauth_version", "1.0"); + + request = gg_oauth_parameter_join(params, 0); + + signature = gg_oauth_generate_signature(method, url, request, consumer_secret, token_secret); + + free(request); + + gg_oauth_parameter_free(params); + params = NULL; + + if (signature == NULL) + return NULL; + + gg_oauth_parameter_set(¶ms, "oauth_version", "1.0"); + gg_oauth_parameter_set(¶ms, "oauth_nonce", nonce); + gg_oauth_parameter_set(¶ms, "oauth_timestamp", timestamp); + gg_oauth_parameter_set(¶ms, "oauth_consumer_key", consumer_key); + gg_oauth_parameter_set(¶ms, "oauth_token", token); + gg_oauth_parameter_set(¶ms, "oauth_signature_method", "HMAC-SHA1"); + gg_oauth_parameter_set(¶ms, "oauth_signature", signature); + + free(signature); + + res = gg_oauth_parameter_join(params, 1); + + gg_oauth_parameter_free(params); + + return res; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/oauth/oauth.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,11 @@ +// source: http://toxygen.net/libgadu/ + +#ifndef _GGP_OAUTH_H +#define _GGP_OAUTH_H + +#include <internal.h> +#include <libgadu.h> + +char *gg_oauth_generate_header(const char *method, const char *url, const const char *consumer_key, const char *consumer_secret, const char *token, const char *token_secret); + +#endif /* _GGP_OAUTH_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/purplew.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,120 @@ +#include "purplew.h" + +#include <request.h> +#include <debug.h> + +guint ggp_purplew_http_input_add(struct gg_http *http_req, + PurpleInputFunction func, gpointer user_data) +{ + PurpleInputCondition cond = 0; + int check = http_req->check; + + if (check & GG_CHECK_READ) + cond |= PURPLE_INPUT_READ; + if (check & GG_CHECK_WRITE) + cond |= PURPLE_INPUT_WRITE; + + //TODO: verbose mode + //purple_debug_misc("gg", "ggp_purplew_http_input_add: " + // "[req=%x, fd=%d, cond=%d]\n", + // (unsigned int)http_req, http_req->fd, cond); + return purple_input_add(http_req->fd, cond, func, user_data); +} + +static void ggp_purplew_request_processing_cancel( + ggp_purplew_request_processing_handle *handle, gint id) +{ + handle->cancel_cb(handle->gc, handle->user_data); + g_free(handle); +} + +ggp_purplew_request_processing_handle * ggp_purplew_request_processing( + PurpleConnection *gc, const gchar *msg, void *user_data, + ggp_purplew_request_processing_cancel_cb cancel_cb) +{ + ggp_purplew_request_processing_handle *handle = + g_new(ggp_purplew_request_processing_handle, 1); + + handle->gc = gc; + handle->cancel_cb = cancel_cb; + handle->user_data = user_data; + handle->request_handle = purple_request_action(gc, _("Please wait..."), + (msg ? msg : _("Please wait...")), NULL, + PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), + NULL, NULL, handle, 1, + _("Cancel"), G_CALLBACK(ggp_purplew_request_processing_cancel)); + + return handle; +} + +void ggp_purplew_request_processing_done( + ggp_purplew_request_processing_handle *handle) +{ + purple_request_close(PURPLE_REQUEST_ACTION, handle->request_handle); + g_free(handle); +} + +PurpleGroup * ggp_purplew_buddy_get_group_only(PurpleBuddy *buddy) +{ + PurpleGroup *group = purple_buddy_get_group(buddy); + if (!group) + return NULL; + if (0 == strcmp(GGP_PURPLEW_GROUP_DEFAULT, purple_group_get_name(group))) + return NULL; + return group; +} + +GList * ggp_purplew_group_get_buddies(PurpleGroup *group, PurpleAccount *account) +{ + GList *buddies = NULL; + PurpleBlistNode *gnode, *cnode, *bnode; + + g_return_val_if_fail(group != NULL, NULL); + + gnode = PURPLE_BLIST_NODE(group); + for (cnode = gnode->child; cnode; cnode = cnode->next) + { + if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) + continue; + for (bnode = cnode->child; bnode; bnode = bnode->next) + { + PurpleBuddy *buddy; + if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) + continue; + + buddy = PURPLE_BUDDY(bnode); + if (account == NULL || buddy->account == account) + buddies = g_list_append(buddies, buddy); + } + } + + return buddies; +} + +GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive) +{ + PurpleBlistNode *bnode; + GList *groups = NULL; + for (bnode = purple_blist_get_root(); bnode; bnode = bnode->next) + { + PurpleGroup *group; + GSList *accounts; + gboolean have_specified = FALSE, have_others = FALSE; + + if (!PURPLE_BLIST_NODE_IS_GROUP(bnode)) + continue; + + group = PURPLE_GROUP(bnode); + for (accounts = purple_group_get_accounts(group); accounts; accounts = g_slist_delete_link(accounts, accounts)) + { + if (accounts->data == account) + have_specified = TRUE; + else + have_others = TRUE; + } + + if (have_specified && (!exclusive || !have_others)) + groups = g_list_append(groups, group); + } + return groups; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/purplew.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,49 @@ +#ifndef _GGP_PURPLEW_H +#define _GGP_PURPLEW_H + +#include <internal.h> +#include <libgadu.h> + +#define GGP_PURPLEW_GROUP_DEFAULT _("Buddies") + +/** + * Adds an input handler in purple event loop for http request. + * + * @see purple_input_add + * + * @param http_req Http connection to watch. + * @param func The callback function for data. + * @param user_data User-specified data. + * + * @return The resulting handle (will be greater than 0). + */ +guint ggp_purplew_http_input_add(struct gg_http *http_req, + PurpleInputFunction func, gpointer user_data); + +typedef void (*ggp_purplew_request_processing_cancel_cb)(PurpleConnection *gc, + void *user_data); + +typedef struct +{ + PurpleConnection *gc; + ggp_purplew_request_processing_cancel_cb cancel_cb; + void *request_handle; + void *user_data; +} ggp_purplew_request_processing_handle; + +ggp_purplew_request_processing_handle * ggp_purplew_request_processing( + PurpleConnection *gc, const gchar *msg, void *user_data, + ggp_purplew_request_processing_cancel_cb oncancel); + +void ggp_purplew_request_processing_done( + ggp_purplew_request_processing_handle *handle); + +// ignores default group +PurpleGroup * ggp_purplew_buddy_get_group_only(PurpleBuddy *buddy); + +GList * ggp_purplew_group_get_buddies(PurpleGroup *group, PurpleAccount *account); + +// you must g_free returned list +GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive); + +#endif /* _GGP_PURPLEW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/resolver-purple.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,163 @@ +#include <internal.h> +#include <debug.h> +#include <dnsquery.h> + +#include <libgadu.h> +#include "resolver-purple.h" + +static int ggp_resolver_purple_start(int *fd, void **private_data, + const char *hostname); + +static void ggp_resolver_purple_cleanup(void **private_data, int force); + +static void ggp_resolver_purple_cb(GSList *hosts, gpointer cbdata, + const char *error_message); + +typedef struct +{ + PurpleDnsQueryData *purpleQuery; + + /** + * File descriptors: + * pipes[0] - for reading + * pipes[1] - for writing + */ + int pipes[2]; +} ggp_resolver_purple_data; + + +extern void ggp_resolver_purple_setup(void) +{ + if (gg_global_set_custom_resolver(ggp_resolver_purple_start, + ggp_resolver_purple_cleanup) != 0) + { + purple_debug_error("gg", "failed to set custom resolver\n"); + } +} + +void ggp_resolver_purple_cb(GSList *hosts, gpointer cbdata, + const char *error_message) +{ + ggp_resolver_purple_data *data = (ggp_resolver_purple_data*)cbdata; + const int fd = data->pipes[1]; + int ipv4_count, all_count, write_size; + struct in_addr *addresses; + + purple_debug_misc("gg", "ggp_resolver_purple_cb(%x, %x, \"%s\")\n", + (unsigned int)hosts, (unsigned int)cbdata, error_message); + + if (error_message) + { + purple_debug_error("gg", "ggp_resolver_purple_cb failed: %s\n", + error_message); + } + + all_count = g_slist_length(hosts); + g_assert(all_count % 2 == 0); + all_count /= 2; + addresses = malloc((all_count + 1) * sizeof(struct in_addr)); + + ipv4_count = 0; + while (hosts && (hosts = g_slist_delete_link(hosts, hosts))) + { + const struct sockaddr *addr = hosts->data; + char dst[INET6_ADDRSTRLEN]; + + if (addr->sa_family == AF_INET6) + { + inet_ntop(addr->sa_family, + &((struct sockaddr_in6 *) addr)->sin6_addr, + dst, sizeof(dst)); + purple_debug_misc("gg", "ggp_resolver_purple_cb " + "ipv6 (ignore): %s\n", dst); + } + else if (addr->sa_family == AF_INET) + { + const struct in_addr addr_ipv4 = + ((struct sockaddr_in *) addr)->sin_addr; + inet_ntop(addr->sa_family, &addr_ipv4, + dst, sizeof(dst)); + purple_debug_misc("gg", "ggp_resolver_purple_cb " + "ipv4: %s\n", dst); + + g_assert(ipv4_count < all_count); + addresses[ipv4_count++] = addr_ipv4; + } + else + { + purple_debug_warning("gg", "ggp_resolver_purple_cb " + "unexpected sa_family: %d\n", addr->sa_family); + } + + g_free(hosts->data); + hosts = g_slist_delete_link(hosts, hosts); + } + + addresses[ipv4_count].s_addr = INADDR_NONE; + + write_size = (ipv4_count + 1) * sizeof(struct in_addr); + if (write(fd, addresses, write_size) != write_size) + { + purple_debug_error("gg", + "ggp_resolver_purple_cb write error\n"); + } + free(addresses); +} + +int ggp_resolver_purple_start(int *fd, void **private_data, + const char *hostname) +{ + ggp_resolver_purple_data *data; + purple_debug_misc("gg", "ggp_resolver_purple_start(%x, %x, \"%s\")\n", + (unsigned int)fd, (unsigned int)private_data, hostname); + + data = malloc(sizeof(ggp_resolver_purple_data)); + *private_data = (void*)data; + data->purpleQuery = NULL; + data->pipes[0] = 0; + data->pipes[1] = 0; + + if (purple_input_pipe(data->pipes) != 0) + { + purple_debug_error("gg", "ggp_resolver_purple_start: " + "unable to create pipe\n"); + ggp_resolver_purple_cleanup(private_data, 0); + return -1; + } + + *fd = data->pipes[0]; + + /* account and port is unknown in this context */ + data->purpleQuery = purple_dnsquery_a(NULL, hostname, 80, + ggp_resolver_purple_cb, (gpointer)data); + + if (!data->purpleQuery) + { + purple_debug_error("gg", "ggp_resolver_purple_start: " + "unable to call purple_dnsquery_a\n"); + ggp_resolver_purple_cleanup(private_data, 0); + return -1; + } + + return 0; +} + +void ggp_resolver_purple_cleanup(void **private_data, int force) +{ + ggp_resolver_purple_data *data = + (ggp_resolver_purple_data*)(*private_data); + + purple_debug_misc("gg", "ggp_resolver_purple_cleanup(%x, %d)\n", + (unsigned int)private_data, force); + + if (!data) + return; + *private_data = NULL; + + if (data->pipes[0]) + close(data->pipes[0]); + if (data->pipes[1]) + close(data->pipes[1]); + + free(data); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/resolver-purple.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,9 @@ +#ifndef _GGP_RESOLVER_PURPLE_H +#define _GGP_RESOLVER_PURPLE_H + +/** + * Registers custom resolver for libgadu, that uses libpurple for DNS queries. + */ +void ggp_resolver_purple_setup(void); + +#endif /* _GGP_RESOLVER_PURPLE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/roster.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,1090 @@ +#include "roster.h" + +#include "gg.h" +#include "xml.h" +#include "utils.h" +#include "purplew.h" + +#include <debug.h> + +#define GGP_ROSTER_SYNC_SETT "gg-synchronized" +#define GGP_ROSTER_DEBUG 0 +#define GGP_ROSTER_GROUPID_DEFAULT "00000000-0000-0000-0000-000000000000" +#define GGP_ROSTER_GROUPID_BOTS "0b345af6-0001-0000-0000-000000000004" + +// TODO: ignored contacts synchronization (?) + +typedef struct +{ + int version; + + xmlnode *xml; + + xmlnode *groups_node, *contacts_node; + + /** + * Key: (uin_t) user identifier + * Value: (xmlnode*) xml node for contact + */ + GHashTable *contact_nodes; + + /** + * Key: (gchar*) group id + * Value: (xmlnode*) xml node for group + */ + GHashTable *group_nodes; + + /** + * Key: (gchar*) group name + * Value: (gchar*) group id + */ + GHashTable *group_ids; + + /** + * Key: (gchar*) group id + * Value: (gchar*) group name + */ + GHashTable *group_names; + + gchar *bots_group_id; + + gboolean needs_update; +} ggp_roster_content; + +typedef struct +{ + enum + { + GGP_ROSTER_CHANGE_CONTACT_UPDATE, + GGP_ROSTER_CHANGE_CONTACT_REMOVE, + GGP_ROSTER_CHANGE_GROUP_RENAME, + } type; + union + { + uin_t uin; + struct + { + gchar *old_name; + gchar *new_name; + } group_rename; + } data; +} ggp_roster_change; + +static inline ggp_roster_session_data * +ggp_roster_get_rdata(PurpleConnection *gc); +static void ggp_roster_content_free(ggp_roster_content *content); +static void ggp_roster_change_free(gpointer change); +static int ggp_roster_get_version(PurpleConnection *gc); +static gboolean ggp_roster_timer_cb(gpointer _gc); +#if GGP_ROSTER_DEBUG +static void ggp_roster_dump(ggp_roster_content *content); +#endif + +// synchronization control +static gboolean ggp_roster_is_synchronized(PurpleBuddy *buddy); +static void ggp_roster_set_synchronized(PurpleConnection *gc, + PurpleBuddy *buddy, gboolean synchronized); + +// buddy list import +static gboolean ggp_roster_reply_list_read_group(xmlnode *node, + ggp_roster_content *content); +static gboolean ggp_roster_reply_list_read_buddy(PurpleConnection *gc, + xmlnode *node, ggp_roster_content *content, GHashTable *remove_buddies); +static void ggp_roster_reply_list(PurpleConnection *gc, uint32_t version, + const char *reply); + +// buddy list export +static const gchar * ggp_roster_send_update_group_add( + ggp_roster_content *content, PurpleGroup *group); +static gboolean ggp_roster_send_update_contact_update(PurpleConnection *gc, + ggp_roster_change *change); +static gboolean ggp_roster_send_update_contact_remove(PurpleConnection *gc, + ggp_roster_change *change); +static gboolean ggp_roster_send_update_group_rename(PurpleConnection *gc, + ggp_roster_change *change); +static void ggp_roster_send_update(PurpleConnection *gc); +static void ggp_roster_reply_ack(PurpleConnection *gc, uint32_t version); +static void ggp_roster_reply_reject(PurpleConnection *gc, uint32_t version); + +/******************************************************************************/ + +static inline ggp_roster_session_data * +ggp_roster_get_rdata(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + return &accdata->roster_data; +} + +static void ggp_roster_content_free(ggp_roster_content *content) +{ + if (content == NULL) + return; + if (content->xml) + xmlnode_free(content->xml); + if (content->contact_nodes) + g_hash_table_destroy(content->contact_nodes); + if (content->group_nodes) + g_hash_table_destroy(content->group_nodes); + if (content->group_ids) + g_hash_table_destroy(content->group_ids); + if (content->group_names) + g_hash_table_destroy(content->group_names); + if (content->bots_group_id) + g_free(content->bots_group_id); + g_free(content); +} + +static void ggp_roster_change_free(gpointer _change) +{ + ggp_roster_change *change = _change; + + if (change->type == GGP_ROSTER_CHANGE_GROUP_RENAME) + { + g_free(change->data.group_rename.old_name); + g_free(change->data.group_rename.new_name); + } + + g_free(change); +} + +static int ggp_roster_get_version(PurpleConnection *gc) +{ + ggp_roster_content *content = ggp_roster_get_rdata(gc)->content; + if (content == NULL) + return 0; + return content->version; +} + +static gboolean ggp_roster_timer_cb(gpointer _gc) +{ + PurpleConnection *gc = _gc; + + g_return_val_if_fail(PURPLE_CONNECTION_IS_VALID(gc), FALSE); + + ggp_roster_send_update(gc); + + return TRUE; +} + +#if GGP_ROSTER_DEBUG +static void ggp_roster_dump(ggp_roster_content *content) +{ + char *str; + int len; + + g_return_if_fail(content != NULL); + g_return_if_fail(content->xml != NULL); + + str = xmlnode_to_formatted_str(content->xml, &len); + purple_debug_misc("gg", "ggp_roster_dump: [%s]\n", str); + g_free(str); +} +#endif + +/******************************************************************************* + * Setup. + ******************************************************************************/ + +gboolean ggp_roster_enabled(void) +{ + static gboolean checked = FALSE; + static gboolean enabled; + + if (!checked) + { + enabled = gg_libgadu_check_feature( + GG_LIBGADU_FEATURE_USERLIST100); + checked = TRUE; + } + return enabled; +} + +void ggp_roster_setup(PurpleConnection *gc) +{ + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + + rdata->content = NULL; + rdata->sent_updates = NULL; + rdata->pending_updates = NULL; + rdata->timer = 0; + rdata->is_updating = FALSE; + + if (ggp_roster_enabled()) + rdata->timer = purple_timeout_add_seconds(2, + ggp_roster_timer_cb, gc); +} + +void ggp_roster_cleanup(PurpleConnection *gc) +{ + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + + if (rdata->timer) + purple_timeout_remove(rdata->timer); + ggp_roster_content_free(rdata->content); + g_list_free_full(rdata->sent_updates, ggp_roster_change_free); + g_list_free_full(rdata->pending_updates, ggp_roster_change_free); +} + +/******************************************************************************* + * Synchronization control. + ******************************************************************************/ + +static gboolean ggp_roster_is_synchronized(PurpleBuddy *buddy) +{ + gboolean ret = purple_blist_node_get_bool(PURPLE_BLIST_NODE(buddy), + GGP_ROSTER_SYNC_SETT); + return ret; +} + +static void ggp_roster_set_synchronized(PurpleConnection *gc, + PurpleBuddy *buddy, gboolean synchronized) +{ + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy)); + ggp_roster_change *change; + + purple_blist_node_set_bool(PURPLE_BLIST_NODE(buddy), + GGP_ROSTER_SYNC_SETT, synchronized); + if (!synchronized) + { + change = g_new0(ggp_roster_change, 1); + change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE; + change->data.uin = uin; + rdata->pending_updates = + g_list_append(rdata->pending_updates, change); + } +} + +void ggp_roster_request_update(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + int local_version = ggp_roster_get_version(gc); + + if (!ggp_roster_enabled()) + { + purple_debug_warning("gg", "ggp_roster_request_update: " + "feature disabled\n"); + return; + } + + purple_debug_info("gg", "ggp_roster_request_update: local=%u\n", + local_version); + + gg_userlist100_request(accdata->session, GG_USERLIST100_GET, + local_version, GG_USERLIST100_FORMAT_TYPE_GG100, NULL); +} + +/******************************************************************************* + * Libgadu callbacks. + ******************************************************************************/ + +void ggp_roster_reply(PurpleConnection *gc, + struct gg_event_userlist100_reply *reply) +{ + if (GG_USERLIST100_FORMAT_TYPE_GG100 != reply->format_type) + { + purple_debug_warning("gg", "ggp_roster_reply: " + "unsupported format type (%x)\n", reply->format_type); + return; + } + + if (reply->type == GG_USERLIST100_REPLY_LIST) + ggp_roster_reply_list(gc, reply->version, reply->reply); + else if (reply->type == 0x01) // list up to date (TODO: push to libgadu) + purple_debug_info("gg", "ggp_roster_reply: list up to date\n"); + else if (reply->type == GG_USERLIST100_REPLY_ACK) + ggp_roster_reply_ack(gc, reply->version); + else if (reply->type == GG_USERLIST100_REPLY_REJECT) + ggp_roster_reply_reject(gc, reply->version); + else + purple_debug_error("gg", "ggp_roster_reply: " + "unsupported reply (%x)\n", reply->type); +} + +void ggp_roster_version(PurpleConnection *gc, + struct gg_event_userlist100_version *version) +{ + int local_version = ggp_roster_get_version(gc); + int remote_version = version->version; + + purple_debug_info("gg", "ggp_roster_version: local=%u, remote=%u\n", + local_version, remote_version); + + if (local_version < remote_version) + ggp_roster_request_update(gc); +} + +/******************************************************************************* + * Libpurple callbacks. + ******************************************************************************/ + +void ggp_roster_alias_buddy(PurpleConnection *gc, const char *who, + const char *alias) +{ + PurpleBuddy *buddy; + + g_return_if_fail(who != NULL); + + if (!ggp_roster_enabled()) + return; + + purple_debug_misc("gg", "ggp_roster_alias_buddy(\"%s\", \"%s\")\n", + who, alias); + + buddy = purple_find_buddy(purple_connection_get_account(gc), who); + g_return_if_fail(buddy != NULL); + + ggp_roster_set_synchronized(gc, buddy, FALSE); +} + +void ggp_roster_group_buddy(PurpleConnection *gc, const char *who, + const char *old_group, const char *new_group) +{ + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + ggp_roster_change *change = g_new0(ggp_roster_change, 1); + + if (!ggp_roster_enabled()) + return; + if (rdata->is_updating) + return; + + purple_debug_misc("gg", "ggp_roster_group_buddy: " + "who=\"%s\", group=\"%s\" -> \"%s\")\n", + who, old_group, new_group); + + // purple_find_buddy(..., who) is not accessible at this moment + change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE; + change->data.uin = ggp_str_to_uin(who); + rdata->pending_updates = g_list_append(rdata->pending_updates, change); +} + +void ggp_roster_rename_group(PurpleConnection *gc, const char *old_name, + PurpleGroup *group, GList *moved_buddies) +{ + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + ggp_roster_change *change = g_new0(ggp_roster_change, 1); + + if (!ggp_roster_enabled()) + return; + + change->type = GGP_ROSTER_CHANGE_GROUP_RENAME; + change->data.group_rename.old_name = g_strdup(old_name); + change->data.group_rename.new_name = + g_strdup(purple_group_get_name(group)); + rdata->pending_updates = g_list_append(rdata->pending_updates, change); +} + +void ggp_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, + PurpleGroup *group, const char *message) +{ + g_return_if_fail(gc != NULL); + g_return_if_fail(buddy != NULL); + + if (!ggp_roster_enabled()) + return; + + ggp_roster_set_synchronized(gc, buddy, FALSE); +} + +void ggp_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, + PurpleGroup *group) +{ + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + ggp_roster_change *change = g_new0(ggp_roster_change, 1); + + if (!ggp_roster_enabled()) + return; + + change->type = GGP_ROSTER_CHANGE_CONTACT_REMOVE; + change->data.uin = ggp_str_to_uin(purple_buddy_get_name(buddy)); + rdata->pending_updates = g_list_append(rdata->pending_updates, change); +} + +/******************************************************************************* + * Buddy list import. + ******************************************************************************/ + +static gboolean ggp_roster_reply_list_read_group(xmlnode *node, + ggp_roster_content *content) +{ + char *name, *id; + gboolean removable; + gboolean succ = TRUE, is_bot, is_default; + + succ &= ggp_xml_get_string(node, "Id", &id); + succ &= ggp_xml_get_string(node, "Name", &name); + succ &= ggp_xml_get_bool(node, "IsRemovable", &removable); + + if (!succ) + { + g_free(id); + g_free(name); + g_return_val_if_reached(FALSE); + } + + is_bot = (strcmp(id, GGP_ROSTER_GROUPID_BOTS) == 0 || + g_strcmp0(name, "Pomocnicy") == 0); + is_default = (strcmp(id, GGP_ROSTER_GROUPID_DEFAULT) == 0 || + g_strcmp0(name, GGP_PURPLEW_GROUP_DEFAULT) == 0 || + g_strcmp0(name, "[default]") == 0); + + if (!content->bots_group_id && is_bot) + content->bots_group_id = g_strdup(id); + + if (!removable || is_bot || is_default) + { + g_free(id); + g_free(name); + return TRUE; + } + + g_hash_table_insert(content->group_nodes, g_strdup(id), node); + g_hash_table_insert(content->group_ids, g_strdup(name), g_strdup(id)); + g_hash_table_insert(content->group_names, id, name); + + return TRUE; +} + +static gboolean ggp_roster_reply_list_read_buddy(PurpleConnection *gc, + xmlnode *node, ggp_roster_content *content, GHashTable *remove_buddies) +{ + gchar *alias, *group_name; + uin_t uin; + gboolean succ = TRUE; + xmlnode *group_list, *group_elem; + PurpleBuddy *buddy = NULL; + PurpleGroup *group = NULL; + PurpleGroup *currentGroup; + gboolean alias_changed; + PurpleAccount *account = purple_connection_get_account(gc); + + succ &= ggp_xml_get_string(node, "ShowName", &alias); + succ &= ggp_xml_get_uint(node, "GGNumber", &uin); + + group_list = xmlnode_get_child(node, "Groups"); + succ &= (group_list != NULL); + + if (!succ) + { + g_free(alias); + g_return_val_if_reached(FALSE); + } + + g_hash_table_insert(content->contact_nodes, GINT_TO_POINTER(uin), node); + + // check, if alias is set + if (strlen(alias) == 0 || + strcmp(alias, ggp_uin_to_str(uin)) == 0) + { + g_free(alias); + alias = NULL; + } + + // getting (eventually creating) group + group_elem = xmlnode_get_child(group_list, "GroupId"); + while (group_elem != NULL) + { + gchar *id; + gboolean isbot; + + if (!ggp_xml_get_string(group_elem, NULL, &id)) + continue; + isbot = (0 == g_strcmp0(id, content->bots_group_id)); + group_name = g_hash_table_lookup(content->group_names, id); + g_free(id); + + // we don't want to import bots; + // they are inserted to roster by default + if (isbot) + { + g_free(alias); + return TRUE; + } + + if (group_name != NULL) + break; + + group_elem = xmlnode_get_next_twin(group_elem); + } + if (group_name) + { + group = purple_find_group(group_name); + if (!group) + { + group = purple_group_new(group_name); + purple_blist_add_group(group, NULL); + } + } + + // add buddy, if doesn't exists + buddy = purple_find_buddy(account, ggp_uin_to_str(uin)); + g_hash_table_remove(remove_buddies, GINT_TO_POINTER(uin)); + if (!buddy) + { + purple_debug_info("gg", "ggp_roster_reply_list_read_buddy: " + "adding %u (%s) to buddy list\n", uin, alias); + buddy = purple_buddy_new(account, ggp_uin_to_str(uin), alias); + purple_blist_add_buddy(buddy, NULL, group, NULL); + ggp_roster_set_synchronized(gc, buddy, TRUE); + + g_free(alias); + return TRUE; + } + + // buddy exists, but is not synchronized - local list has priority + if (!ggp_roster_is_synchronized(buddy)) + { + purple_debug_misc("gg", "ggp_roster_reply_list_read_buddy: " + "ignoring not synchronized %u (%s)\n", + uin, purple_buddy_get_name(buddy)); + g_free(alias); + return TRUE; + } + + currentGroup = ggp_purplew_buddy_get_group_only(buddy); + alias_changed = + (0 != g_strcmp0(alias, purple_buddy_get_alias_only(buddy))); + + if (currentGroup == group && !alias_changed) + { + g_free(alias); + return TRUE; + } + + purple_debug_misc("gg", "ggp_roster_reply_list_read_buddy: " + "updating %u (%s) - alias=\"%s\"->\"%s\", group=%p->%p (%s)\n", + uin, purple_buddy_get_name(buddy), + purple_buddy_get_alias(buddy), alias, + currentGroup, group, group_name); + if (alias_changed) + purple_blist_alias_buddy(buddy, alias); + if (currentGroup != group) + purple_blist_add_buddy(buddy, NULL, group, NULL); + + g_free(alias); + return TRUE; +} + +static void ggp_roster_reply_list(PurpleConnection *gc, uint32_t version, + const char *data) +{ + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + xmlnode *xml, *xml_it; + PurpleAccount *account; + GSList *local_buddies; + GHashTable *remove_buddies; + GList *update_buddies = NULL, *local_groups, *it, *table_values; + ggp_roster_content *content; + + g_return_if_fail(gc != NULL); + g_return_if_fail(data != NULL); + + account = purple_connection_get_account(gc); + + purple_debug_info("gg", "ggp_roster_reply_list: got list, version=%u\n", + version); + + xml = xmlnode_from_str(data, -1); + if (xml == NULL) + { + purple_debug_warning("gg", "ggp_roster_reply_list: " + "invalid xml\n"); + return; + } + + ggp_roster_content_free(rdata->content); + rdata->content = NULL; + rdata->is_updating = TRUE; + content = g_new0(ggp_roster_content, 1); + content->version = version; + content->xml = xml; + content->contact_nodes = g_hash_table_new(NULL, NULL); + content->group_nodes = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, NULL); + content->group_ids = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, g_free); + content->group_names = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, g_free); + +#if GGP_ROSTER_DEBUG + ggp_roster_dump(content); +#endif + + // reading groups + content->groups_node = xmlnode_get_child(xml, "Groups"); + if (content->groups_node == NULL) + { + ggp_roster_content_free(content); + g_return_if_reached(); + } + xml_it = xmlnode_get_child(content->groups_node, "Group"); + while (xml_it != NULL) + { + if (!ggp_roster_reply_list_read_group(xml_it, content)) + { + ggp_roster_content_free(content); + g_return_if_reached(); + } + + xml_it = xmlnode_get_next_twin(xml_it); + } + + // dumping current group list + local_groups = ggp_purplew_account_get_groups(account, TRUE); + + // dumping current buddy list + // we will: + // - remove synchronized ones, if not found in list at server + // - upload not synchronized ones + local_buddies = purple_find_buddies(account, NULL); + remove_buddies = g_hash_table_new(g_direct_hash, g_direct_equal); + while (local_buddies) + { + PurpleBuddy *buddy = local_buddies->data; + uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy)); + local_buddies = + g_slist_delete_link(local_buddies, local_buddies); + + if (!uin) + continue; + + if (ggp_roster_is_synchronized(buddy)) + g_hash_table_insert(remove_buddies, + GINT_TO_POINTER(uin), buddy); + else + update_buddies = g_list_append(update_buddies, buddy); + } + + // reading buddies + content->contacts_node = xmlnode_get_child(xml, "Contacts"); + if (content->contacts_node == NULL) + { + g_hash_table_destroy(remove_buddies); + g_list_free(update_buddies); + ggp_roster_content_free(content); + g_return_if_reached(); + } + xml_it = xmlnode_get_child(content->contacts_node, "Contact"); + while (xml_it != NULL) + { + if (!ggp_roster_reply_list_read_buddy(gc, xml_it, content, + remove_buddies)) + { + g_hash_table_destroy(remove_buddies); + g_list_free(update_buddies); + ggp_roster_content_free(content); + g_return_if_reached(); + } + + xml_it = xmlnode_get_next_twin(xml_it); + } + + // removing buddies, which are not present in roster + table_values = g_hash_table_get_values(remove_buddies); + it = g_list_first(table_values); + while (it) + { + PurpleBuddy *buddy = it->data; + it = g_list_next(it); + if (!ggp_roster_is_synchronized(buddy)) + continue; + purple_debug_info("gg", "ggp_roster_reply_list: " + "removing %s from buddy list\n", + purple_buddy_get_name(buddy)); + purple_blist_remove_buddy(buddy); + } + g_list_free(table_values); + g_hash_table_destroy(remove_buddies); + + // remove groups, which are empty, but had contacts before + // synchronization + it = g_list_first(local_groups); + while (it) + { + PurpleGroup *group = it->data; + it = g_list_next(it); + if (purple_blist_get_group_size(group, TRUE) != 0) + continue; + purple_debug_info("gg", "ggp_roster_reply_list: " + "removing group %s\n", purple_group_get_name(group)); + purple_blist_remove_group(group); + } + g_list_free(local_groups); + + // adding not synchronized buddies + it = g_list_first(update_buddies); + while (it) + { + PurpleBuddy *buddy = it->data; + uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy)); + ggp_roster_change *change; + + it = g_list_next(it); + g_assert(uin > 0); + + purple_debug_misc("gg", "ggp_roster_reply_list: " + "adding change of %u for roster\n", uin); + change = g_new0(ggp_roster_change, 1); + change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE; + change->data.uin = uin; + rdata->pending_updates = + g_list_append(rdata->pending_updates, change); + } + g_list_free(update_buddies); + + rdata->content = content; + rdata->is_updating = FALSE; + purple_debug_info("gg", "ggp_roster_reply_list: " + "import done, version=%u\n", version); +} + +/******************************************************************************* + * Buddy list export. + ******************************************************************************/ + +static const gchar * ggp_roster_send_update_group_add( + ggp_roster_content *content, PurpleGroup *group) +{ + gchar *id_dyn; + const char *id_existing, *group_name; + static gchar id[40]; + xmlnode *group_node; + gboolean succ = TRUE; + + if (group) + { + group_name = purple_group_get_name(group); + id_existing = + g_hash_table_lookup(content->group_ids, group_name); + } + else + id_existing = GGP_ROSTER_GROUPID_DEFAULT; + if (id_existing) + return id_existing; + + purple_debug_info("gg", "ggp_roster_send_update_group_add: adding %s\n", + purple_group_get_name(group)); + + id_dyn = purple_uuid_random(); + g_snprintf(id, sizeof(id), "%s", id_dyn); + g_free(id_dyn); + + group_node = xmlnode_new_child(content->groups_node, "Group"); + succ &= ggp_xml_set_string(group_node, "Id", id); + succ &= ggp_xml_set_string(group_node, "Name", group_name); + succ &= ggp_xml_set_string(group_node, "IsExpanded", "true"); + succ &= ggp_xml_set_string(group_node, "IsRemovable", "true"); + content->needs_update = TRUE; + + g_hash_table_insert(content->group_ids, g_strdup(group_name), + g_strdup(id)); + g_hash_table_insert(content->group_nodes, g_strdup(id), group_node); + + g_return_val_if_fail(succ, NULL); + + return id; +} + +static gboolean ggp_roster_send_update_contact_update(PurpleConnection *gc, + ggp_roster_change *change) +{ + PurpleAccount *account = purple_connection_get_account(gc); + ggp_roster_content *content = ggp_roster_get_rdata(gc)->content; + uin_t uin = change->data.uin; + PurpleBuddy *buddy; + xmlnode *buddy_node, *contact_groups; + gboolean succ = TRUE; + const char *group_id; + + g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_CONTACT_UPDATE, + FALSE); + + buddy = purple_find_buddy(account, ggp_uin_to_str(uin)); + if (!buddy) + return TRUE; + buddy_node = g_hash_table_lookup(content->contact_nodes, + GINT_TO_POINTER(uin)); + + group_id = ggp_roster_send_update_group_add(content, + ggp_purplew_buddy_get_group_only(buddy)); + + if (buddy_node) + { // update existing + purple_debug_misc("gg", "ggp_roster_send_update_contact_update:" + " updating %u...\n", uin); + + succ &= ggp_xml_set_string(buddy_node, "ShowName", + purple_buddy_get_alias(buddy)); + + contact_groups = xmlnode_get_child(buddy_node, "Groups"); + g_assert(contact_groups); + ggp_xmlnode_remove_children(contact_groups); + succ &= ggp_xml_set_string(contact_groups, "GroupId", group_id); + + g_return_val_if_fail(succ, FALSE); + + return TRUE; + } + + // add new + purple_debug_misc("gg", "ggp_roster_send_update_contact_update: " + "adding %u...\n", uin); + buddy_node = xmlnode_new_child(content->contacts_node, "Contact"); + succ &= ggp_xml_set_string(buddy_node, "Guid", purple_uuid_random()); + succ &= ggp_xml_set_uint(buddy_node, "GGNumber", uin); + succ &= ggp_xml_set_string(buddy_node, "ShowName", + purple_buddy_get_alias(buddy)); + + contact_groups = xmlnode_new_child(buddy_node, "Groups"); + g_assert(contact_groups); + succ &= ggp_xml_set_string(contact_groups, "GroupId", group_id); + + xmlnode_new_child(buddy_node, "Avatars"); + succ &= ggp_xml_set_bool(buddy_node, "FlagBuddy", TRUE); + succ &= ggp_xml_set_bool(buddy_node, "FlagNormal", TRUE); + succ &= ggp_xml_set_bool(buddy_node, "FlagFriend", TRUE); + + // we don't use Guid, so update is not needed + //content->needs_update = TRUE; + + g_hash_table_insert(content->contact_nodes, GINT_TO_POINTER(uin), + buddy_node); + + g_return_val_if_fail(succ, FALSE); + + return TRUE; +} + +static gboolean ggp_roster_send_update_contact_remove(PurpleConnection *gc, + ggp_roster_change *change) +{ + PurpleAccount *account = purple_connection_get_account(gc); + ggp_roster_content *content = ggp_roster_get_rdata(gc)->content; + uin_t uin = change->data.uin; + PurpleBuddy *buddy; + xmlnode *buddy_node; + + g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_CONTACT_REMOVE, + FALSE); + + buddy = purple_find_buddy(account, ggp_uin_to_str(uin)); + if (buddy) + { + purple_debug_info("gg", "ggp_roster_send_update_contact_remove:" + " contact %u re-added\n", uin); + return TRUE; + } + + buddy_node = g_hash_table_lookup(content->contact_nodes, + GINT_TO_POINTER(uin)); + if (!buddy_node) // already removed + return TRUE; + + purple_debug_info("gg", "ggp_roster_send_update_contact_remove: " + "removing %u\n", uin); + xmlnode_free(buddy_node); + g_hash_table_remove(content->contact_nodes, GINT_TO_POINTER(uin)); + + return TRUE; +} + +static gboolean ggp_roster_send_update_group_rename(PurpleConnection *gc, + ggp_roster_change *change) +{ + PurpleAccount *account = purple_connection_get_account(gc); + ggp_roster_content *content = ggp_roster_get_rdata(gc)->content; + const char *old_name = change->data.group_rename.old_name; + const char *new_name = change->data.group_rename.new_name; + xmlnode *group_node; + const char *group_id; + + g_return_val_if_fail(change->type == GGP_ROSTER_CHANGE_GROUP_RENAME, + FALSE); + + purple_debug_misc("gg", "ggp_roster_send_update_group_rename: " + "\"%s\"->\"%s\"\n", old_name, new_name); + + // moving to or from default group instead of renaming it + if (0 == g_strcmp0(old_name, GGP_PURPLEW_GROUP_DEFAULT) || + 0 == g_strcmp0(new_name, GGP_PURPLEW_GROUP_DEFAULT)) + { + PurpleGroup *group; + GList *group_buddies; + group = purple_find_group(new_name); + if (!group) + return TRUE; + purple_debug_info("gg", "ggp_roster_send_update_group_rename: " + "invalidating buddies in default group\n"); + group_buddies = ggp_purplew_group_get_buddies(group, account); + while (group_buddies) + { + ggp_roster_set_synchronized(gc, group_buddies->data, + FALSE); + group_buddies = g_list_remove_link(group_buddies, + group_buddies); + } + return TRUE; + } + group_id = g_hash_table_lookup(content->group_ids, old_name); + if (!group_id) + { + purple_debug_info("gg", "ggp_roster_send_update_group_rename: " + "%s is not present at roster\n", old_name); + return TRUE; + } + + group_node = g_hash_table_lookup(content->group_nodes, group_id); + if (!group_node) + { + purple_debug_error("gg", "ggp_roster_send_update_group_rename: " + "node for %s not found, id=%s\n", old_name, group_id); + g_hash_table_remove(content->group_ids, old_name); + return TRUE; + } + + g_hash_table_remove(content->group_ids, old_name); + g_hash_table_insert(content->group_ids, g_strdup(new_name), + g_strdup(group_id)); + g_hash_table_insert(content->group_nodes, g_strdup(group_id), + group_node); + return ggp_xml_set_string(group_node, "Name", new_name); +} + +static void ggp_roster_send_update(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + ggp_roster_content *content = rdata->content; + GList *updates_it; + gchar *str; + int len; + + // an update is running now + if (rdata->sent_updates) + return; + + // no pending updates found + if (!rdata->pending_updates) + return; + + // not initialized + if (!content) + return; + + purple_debug_info("gg", "ggp_roster_send_update: " + "pending updates found\n"); + + rdata->sent_updates = rdata->pending_updates; + rdata->pending_updates = NULL; + + updates_it = g_list_first(rdata->sent_updates); + while (updates_it) + { + ggp_roster_change *change = updates_it->data; + gboolean succ = FALSE; + updates_it = g_list_next(updates_it); + + if (change->type == GGP_ROSTER_CHANGE_CONTACT_UPDATE) + succ = ggp_roster_send_update_contact_update(gc, + change); + else if (change->type == GGP_ROSTER_CHANGE_CONTACT_REMOVE) + succ = ggp_roster_send_update_contact_remove(gc, + change); + else if (change->type == GGP_ROSTER_CHANGE_GROUP_RENAME) + succ = ggp_roster_send_update_group_rename(gc, change); + else + purple_debug_fatal("gg", "ggp_roster_send_update: " + "not handled\n"); + g_return_if_fail(succ); + } + +#if GGP_ROSTER_DEBUG + ggp_roster_dump(content); +#endif + + str = xmlnode_to_str(content->xml, &len); + gg_userlist100_request(accdata->session, GG_USERLIST100_PUT, + content->version, GG_USERLIST100_FORMAT_TYPE_GG100, str); + g_free(str); +} + +static void ggp_roster_reply_ack(PurpleConnection *gc, uint32_t version) +{ + PurpleAccount *account = purple_connection_get_account(gc); + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + ggp_roster_content *content = rdata->content; + GList *updates_it; + + purple_debug_info("gg", "ggp_roster_reply_ack: version=%u\n", version); + + // set synchronization flag for all buddies, that were updated at roster + updates_it = g_list_first(rdata->sent_updates); + while (updates_it) + { + ggp_roster_change *change = updates_it->data; + PurpleBuddy *buddy; + updates_it = g_list_next(updates_it); + + if (change->type != GGP_ROSTER_CHANGE_CONTACT_UPDATE) + continue; + + buddy = purple_find_buddy(account, + ggp_uin_to_str(change->data.uin)); + if (buddy) + ggp_roster_set_synchronized(gc, buddy, TRUE); + } + + // we need to remove "synchronized" flag for all contacts, that have + // beed modified between roster update start and now + updates_it = g_list_first(rdata->pending_updates); + while (updates_it) + { + ggp_roster_change *change = updates_it->data; + PurpleBuddy *buddy; + updates_it = g_list_next(updates_it); + + if (change->type != GGP_ROSTER_CHANGE_CONTACT_UPDATE) + continue; + + buddy = purple_find_buddy(account, + ggp_uin_to_str(change->data.uin)); + if (buddy && ggp_roster_is_synchronized(buddy)) + ggp_roster_set_synchronized(gc, buddy, FALSE); + } + + g_list_free_full(rdata->sent_updates, ggp_roster_change_free); + rdata->sent_updates = NULL; + + // bump roster version or update it, if needed + g_return_if_fail(content != NULL); + if (content->needs_update) + { + ggp_roster_content_free(rdata->content); + rdata->content = NULL; + // we have to wait for gg_event_userlist100_version + //ggp_roster_request_update(gc); + } + else + content->version = version; +} + +static void ggp_roster_reply_reject(PurpleConnection *gc, uint32_t version) +{ + ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc); + + purple_debug_info("gg", "ggp_roster_reply_reject: version=%u\n", + version); + + g_return_if_fail(rdata->sent_updates); + + rdata->pending_updates = g_list_concat(rdata->pending_updates, + rdata->sent_updates); + rdata->sent_updates = NULL; + + ggp_roster_content_free(rdata->content); + rdata->content = NULL; + ggp_roster_request_update(gc); +} + +/******************************************************************************/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/roster.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,44 @@ +#ifndef _GGP_ROSTER_H +#define _GGP_ROSTER_H + +#include <internal.h> +#include <libgadu.h> + +typedef struct +{ + gpointer content; + gboolean is_updating; + + GList *sent_updates; + GList *pending_updates; + + guint timer; +} ggp_roster_session_data; + +// setup +gboolean ggp_roster_enabled(void); +void ggp_roster_setup(PurpleConnection *gc); +void ggp_roster_cleanup(PurpleConnection *gc); + +// synchronization control +void ggp_roster_request_update(PurpleConnection *gc); + +// libgadu callbacks +void ggp_roster_reply(PurpleConnection *gc, + struct gg_event_userlist100_reply *reply); +void ggp_roster_version(PurpleConnection *gc, + struct gg_event_userlist100_version *version); + +// libpurple callbacks +void ggp_roster_alias_buddy(PurpleConnection *gc, const char *who, + const char *alias); +void ggp_roster_group_buddy(PurpleConnection *gc, const char *who, + const char *old_group, const char *new_group); +void ggp_roster_rename_group(PurpleConnection *, const char *old_name, + PurpleGroup *group, GList *moved_buddies); +void ggp_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, + PurpleGroup *group, const char *message); +void ggp_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, + PurpleGroup *group); + +#endif /* _GGP_ROSTER_H */
--- a/libpurple/protocols/gg/search.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/search.c Tue Aug 14 22:26:33 2012 +0200 @@ -23,9 +23,12 @@ #include <libgadu.h> -#include "gg-utils.h" +#include "gg.h" +#include "utils.h" #include "search.h" +#include <debug.h> + /* GGPSearchForm *ggp_search_form_new() {{{ */ GGPSearchForm *ggp_search_form_new(GGPSearchType st)
--- a/libpurple/protocols/gg/search.h Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/protocols/gg/search.h Tue Aug 14 22:26:33 2012 +0200 @@ -27,7 +27,6 @@ #include "connection.h" #include <libgadu.h> -#include "gg.h" typedef enum {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/status.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,228 @@ +#include "status.h" + +#include <libgadu.h> +#include <debug.h> +#include <request.h> + +#include "gg.h" +#include "utils.h" + +struct _ggp_status_session_data +{ + gboolean status_broadcasting; + gchar *current_description; +}; + +static inline ggp_status_session_data * +ggp_status_get_ssdata(PurpleConnection *gc); + +gchar * ggp_status_validate_description(const gchar* msg); +static int ggp_status_from_purplestatus(PurpleStatus *status, gchar **message); + +//// + +static inline ggp_status_session_data * +ggp_status_get_ssdata(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + return accdata->status_data; +} + +void ggp_status_setup(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + PurpleAccount *account = purple_connection_get_account(gc); + + ggp_status_session_data *ssdata = g_new0(ggp_status_session_data, 1); + accdata->status_data = ssdata; + + ssdata->status_broadcasting = + purple_account_get_bool(account, "status_broadcasting", TRUE); +} + +void ggp_status_cleanup(PurpleConnection *gc) +{ + ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc); + g_free(ssdata->current_description); + g_free(ssdata); +} + +gchar * ggp_status_validate_description(const gchar* msg) +{ + if (msg == NULL || msg[0] == '\0') + return NULL; + + return ggp_utf8_strndup(msg, GG_STATUS_DESCR_MAXSIZE); +} + +static int ggp_status_from_purplestatus(PurpleStatus *status, gchar **message) +{ + const char *status_id = purple_status_get_id(status); + const char *status_message = + purple_status_get_attr_string(status, "message"); + + g_return_val_if_fail(message != NULL, 0); + + *message = NULL; + if (status_message) + { + gchar *stripped = purple_markup_strip_html(status_message); + *message = ggp_status_validate_description(stripped); + g_free(stripped); + } + + if (0 == strcmp(status_id, "available")) + return status_message ? GG_STATUS_AVAIL_DESCR : GG_STATUS_AVAIL; + if (0 == strcmp(status_id, "freeforchat")) + return status_message ? GG_STATUS_FFC_DESCR : GG_STATUS_FFC; + if (0 == strcmp(status_id, "away")) + return status_message ? GG_STATUS_BUSY_DESCR : GG_STATUS_BUSY; + if (0 == strcmp(status_id, "unavailable")) + return status_message ? GG_STATUS_DND_DESCR : GG_STATUS_DND; + if (0 == strcmp(status_id, "invisible")) + return status_message ? + GG_STATUS_INVISIBLE_DESCR : GG_STATUS_INVISIBLE; + if (0 == strcmp(status_id, "offline")) + return status_message ? + GG_STATUS_NOT_AVAIL_DESCR : GG_STATUS_NOT_AVAIL; + + purple_debug_error("gg", "ggp_status_from_purplestatus: " + "unknown status requested (%s)\n", status_id); + return status_message ? GG_STATUS_AVAIL_DESCR : GG_STATUS_AVAIL; +} + +/******************************************************************************* + * Own status. + ******************************************************************************/ + +static void ggp_status_broadcasting_dialog_ok(PurpleConnection *gc, + PurpleRequestFields *fields); + +/******************************************************************************/ + +void ggp_status_set_initial(PurpleConnection *gc, struct gg_login_params *glp) +{ + ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc); + PurpleAccount *account = purple_connection_get_account(gc); + + glp->status = ggp_status_from_purplestatus( + purple_account_get_active_status(account), &glp->status_descr); + if (!ggp_status_get_status_broadcasting(gc)) + glp->status |= GG_STATUS_FRIENDS_MASK; + ssdata->current_description = g_strdup(glp->status_descr); +} + +gboolean ggp_status_set(PurpleAccount *account, int status, const gchar* msg) +{ + PurpleConnection *gc = purple_account_get_connection(account); + ggp_status_session_data *ssdata = ggp_status_get_ssdata(gc); + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + gchar *new_description = ggp_status_validate_description(msg); + + if (!ssdata->status_broadcasting) + status |= GG_STATUS_FRIENDS_MASK; + + if ((status == GG_STATUS_NOT_AVAIL || + status == GG_STATUS_NOT_AVAIL_DESCR) && + 0 == g_strcmp0(ssdata->current_description, new_description)) + { + purple_debug_info("gg", "ggp_status_set: new status doesn't " + "differ when closing connection - ignore\n"); + g_free(new_description); + return FALSE; + } + g_free(ssdata->current_description); + ssdata->current_description = new_description; + + if (msg == NULL) + gg_change_status(accdata->session, status); + else + gg_change_status_descr(accdata->session, status, new_description); + + return TRUE; +} + +void ggp_status_set_purplestatus(PurpleAccount *account, PurpleStatus *status) +{ + int status_gg; + gchar *msg = NULL; + + if (!purple_status_is_active(status)) + return; + + status_gg = ggp_status_from_purplestatus(status, &msg); + ggp_status_set(account, status_gg, msg); + g_free(msg); +} + +void ggp_status_fake_to_self(PurpleConnection *gc) +{ + PurpleAccount *account = purple_connection_get_account(gc); + PurpleStatus *status = purple_presence_get_active_status( + purple_account_get_presence(account)); + const char *status_msg = purple_status_get_attr_string(status, + "message"); + gchar *status_msg_gg = NULL; + + if (status_msg != NULL && status_msg[0] != '\0') + { + status_msg_gg = g_new0(gchar, GG_STATUS_DESCR_MAXSIZE + 1); + g_utf8_strncpy(status_msg_gg, status_msg, + GG_STATUS_DESCR_MAXSIZE); + } + + purple_prpl_got_user_status(account, + purple_account_get_username(account), + purple_status_get_id(status), + status_msg_gg ? "message" : NULL, status_msg_gg, NULL); + + g_free(status_msg_gg); +} + +gboolean ggp_status_get_status_broadcasting(PurpleConnection *gc) +{ + return ggp_status_get_ssdata(gc)->status_broadcasting; +} + +void ggp_status_set_status_broadcasting(PurpleConnection *gc, + gboolean broadcasting) +{ + PurpleAccount *account = purple_connection_get_account(gc); + + ggp_status_get_ssdata(gc)->status_broadcasting = broadcasting; + purple_account_set_bool(account, "status_broadcasting", broadcasting); + ggp_status_set_purplestatus(account, + purple_account_get_active_status(account)); +} + +void ggp_status_broadcasting_dialog(PurpleConnection *gc) +{ + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; + + fields = purple_request_fields_new(); + group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, group); + + field = purple_request_field_bool_new("buddies_only", + _("Show status only for buddies"), + !ggp_status_get_status_broadcasting(gc)); + purple_request_field_group_add_field(group, field); + + purple_request_fields(gc, + _("Change status broadcasting"), + _("Please, select who can see your status"), + NULL, + fields, + _("OK"), G_CALLBACK(ggp_status_broadcasting_dialog_ok), + _("Cancel"), NULL, + purple_connection_get_account(gc), NULL, NULL, gc); +} + +static void ggp_status_broadcasting_dialog_ok(PurpleConnection *gc, + PurpleRequestFields *fields) +{ + ggp_status_set_status_broadcasting(gc, + !purple_request_fields_get_bool(fields, "buddies_only")); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/status.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,25 @@ +#ifndef _GGP_STATUS_H +#define _GGP_STATUS_H + +#include <internal.h> +#include <libgadu.h> + +typedef struct _ggp_status_session_data ggp_status_session_data; + +void ggp_status_setup(PurpleConnection *gc); +void ggp_status_cleanup(PurpleConnection *gc); + +// own status + +void ggp_status_set_initial(PurpleConnection *gc, struct gg_login_params *glp); + +gboolean ggp_status_set(PurpleAccount *account, int status, const gchar* msg); +void ggp_status_set_purplestatus(PurpleAccount *account, PurpleStatus *status); +void ggp_status_fake_to_self(PurpleConnection *gc); + +gboolean ggp_status_get_status_broadcasting(PurpleConnection *gc); +void ggp_status_set_status_broadcasting(PurpleConnection *gc, + gboolean broadcasting); +void ggp_status_broadcasting_dialog(PurpleConnection *gc); + +#endif /* _GGP_STATUS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/utils.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,129 @@ +/** + * @file utils.c + * + * purple + * + * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us> + * + * 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 "utils.h" + +#include "gg.h" + +#include <debug.h> + +uin_t ggp_str_to_uin(const char *str) +{ + char *endptr; + uin_t uin; + + if (!str || str[0] < '0' || str[0] > '9') + return 0; + + errno = 0; + uin = strtoul(str, &endptr, 10); + + if (errno == ERANGE || endptr[0] != '\0') + return 0; + + return uin; +} + +const char * ggp_uin_to_str(uin_t uin) +{ + static char buff[GGP_UIN_LEN_MAX + 1]; + + g_snprintf(buff, GGP_UIN_LEN_MAX + 1, "%u", uin); + + return buff; +} + +static gchar * ggp_convert(const gchar *src, const char *srcenc, + const char *dstenc) +{ + gchar *dst; + GError *err = NULL; + + if (src == NULL) + return NULL; + + dst = g_convert_with_fallback(src, strlen(src), dstenc, srcenc, "?", + NULL, NULL, &err); + if (err != NULL) + { + purple_debug_error("gg", "error converting from %s to %s: %s\n", + srcenc, dstenc, err->message); + g_error_free(err); + } + + if (dst == NULL) + dst = g_strdup(src); + + return dst; +} + +gchar * ggp_convert_to_cp1250(const gchar *src) +{ + return ggp_convert(src, "UTF-8", "CP1250"); +} + +gchar * ggp_convert_from_cp1250(const gchar *src) +{ + return ggp_convert(src, "CP1250", "UTF-8"); +} + +gboolean ggp_password_validate(const gchar *password) +{ + const int len = strlen(password); + if (len < 6 || len > 15) + return FALSE; + return g_regex_match_simple("^[ a-zA-Z0-9~`!@#$%^&*()_+=[\\]{};':\",./?" + "<>\\\\|-]+$", password, 0, 0); +} + +guint64 ggp_microtime(void) +{ + // replace with g_get_monotonic_time, when gtk 2.28 will be available + GTimeVal time_s; + + g_get_current_time(&time_s); + + return ((guint64)time_s.tv_sec << 32) | time_s.tv_usec; +} + +gchar * ggp_utf8_strndup(const gchar *str, gsize n) +{ + int raw_len = strlen(str); + gchar *end_ptr; + if (str == NULL) + return NULL; + if (raw_len <= n) + return g_strdup(str); + + end_ptr = g_utf8_offset_to_pointer(str, g_utf8_pointer_to_offset(str, &str[n])); + raw_len = end_ptr - str; + + if (raw_len > n) + { + end_ptr = g_utf8_prev_char(end_ptr); + raw_len = end_ptr - str; + } + + g_assert(raw_len <= n); + + return g_strndup(str, raw_len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/utils.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,73 @@ +/** + * @file utils.h + * + * purple + * + * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us> + * + * 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 _GGP_UTILS_H +#define _GGP_UTILS_H + +#include <internal.h> +#include <libgadu.h> + +/** + * Converts stringified UIN to uin_t. + * + * @param str The string to convert. + * + * @return Converted UIN or 0 if an error occurred. + */ +uin_t ggp_str_to_uin(const char *str); + +/** + * Stringifies UIN. + * + * @param uin UIN to stringify. + * + * @return Stringified UIN. + */ +const char * ggp_uin_to_str(uin_t uin); + +/** + * Converts encoding of a given string from UTF-8 to CP1250. + * + * @param src Input string. + * + * @return Converted string (must be freed with g_free). If src is NULL, + * then NULL is returned. + */ +gchar * ggp_convert_to_cp1250(const gchar *src); + +/** + * Converts encoding of a given string from CP1250 to UTF-8. + * + * @param src Input string. + * + * @return Converted string (must be freed with g_free). If src is NULL, + * then NULL is returned. + */ +gchar * ggp_convert_from_cp1250(const gchar *src); + +gboolean ggp_password_validate(const gchar *password); + +guint64 ggp_microtime(void); + +gchar * ggp_utf8_strndup(const gchar *str, gsize n); + +#endif /* _GGP_UTILS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/validator.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,66 @@ +#include "validator.h" + +#include "account.h" +#include "utils.h" + +gboolean ggp_validator_token(PurpleRequestField *field, gchar **errmsg, + void *token) +{ + const char *value; + + g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(purple_request_field_get_type(field) == + PURPLE_REQUEST_FIELD_STRING, FALSE); + + value = purple_request_field_string_get_value(field); + + if (value != NULL && ggp_account_token_validate(token, value)) + return TRUE; + + if (errmsg) + *errmsg = g_strdup(_("Captcha validation failed")); + return FALSE; +} + +gboolean ggp_validator_password(PurpleRequestField *field, gchar **errmsg, + void *user_data) +{ + const char *value; + + g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(purple_request_field_get_type(field) == + PURPLE_REQUEST_FIELD_STRING, FALSE); + + value = purple_request_field_string_get_value(field); + + if (value != NULL && ggp_password_validate(value)) + return TRUE; + + if (errmsg) + *errmsg = g_strdup(_("Password can contain 6-15 alphanumeric characters")); + return FALSE; +} + +gboolean ggp_validator_password_equal(PurpleRequestField *field, gchar **errmsg, + void *field2_p) +{ + const char *value1, *value2; + PurpleRequestField *field2 = field2_p; + + g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(field2 != NULL, FALSE); + g_return_val_if_fail(purple_request_field_get_type(field) == + PURPLE_REQUEST_FIELD_STRING, FALSE); + g_return_val_if_fail(purple_request_field_get_type(field2) == + PURPLE_REQUEST_FIELD_STRING, FALSE); + + value1 = purple_request_field_string_get_value(field); + value2 = purple_request_field_string_get_value(field2); + + if (g_strcmp0(value1, value2) == 0) + return TRUE; + + if (errmsg) + *errmsg = g_strdup(_("Passwords do not match")); + return FALSE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/validator.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,16 @@ +#ifndef _GGP_VALIDATOR_H +#define _GGP_VALIDATOR_H + +#include <internal.h> +#include <request.h> + +gboolean ggp_validator_token(PurpleRequestField *field, gchar **errmsg, + void *token); + +gboolean ggp_validator_password(PurpleRequestField *field, gchar **errmsg, + void *user_data); + +gboolean ggp_validator_password_equal(PurpleRequestField *field, gchar **errmsg, + void *field2); + +#endif /* _GGP_VALIDATOR_H */
--- a/libpurple/protocols/gg/win32-resolver.c Tue Aug 14 22:05:05 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,322 +0,0 @@ -/** - * @file win32-resolver.c - * - * purple - * - * 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 "win32-resolver.h" - -#include <errno.h> -#include <resolver.h> -#include "debug.h" - -#ifndef _WIN32 -#error "win32thread resolver is not supported on current platform" -#endif - -/** - * Deal with the fact that you can't select() on a win32 file fd. - * This makes it practically impossible to tie into purple's event loop. - * - * -This is thanks to Tor Lillqvist. - */ -static int ggp_resolver_win32thread_socket_pipe(int *fds) -{ - SOCKET temp, socket1 = -1, socket2 = -1; - struct sockaddr_in saddr; - int len; - u_long arg; - fd_set read_set, write_set; - struct timeval tv; - - purple_debug_misc("gg", "ggp_resolver_win32thread_socket_pipe(&%d)\n", - *fds); - - temp = socket(AF_INET, SOCK_STREAM, 0); - - if (temp == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { - goto out0; - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { - goto out0; - } - - if (listen(temp, 1) == SOCKET_ERROR) { - goto out0; - } - - len = sizeof(saddr); - if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { - goto out0; - } - - socket1 = socket(AF_INET, SOCK_STREAM, 0); - - if (socket1 == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out1; - } - - if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || - WSAGetLastError() != WSAEWOULDBLOCK) { - goto out1; - } - - FD_ZERO(&read_set); - FD_SET(temp, &read_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { - goto out1; - } - - if (!FD_ISSET(temp, &read_set)) { - goto out1; - } - - socket2 = accept(temp, (struct sockaddr *) &saddr, &len); - if (socket2 == INVALID_SOCKET) { - goto out1; - } - - FD_ZERO(&write_set); - FD_SET(socket1, &write_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { - goto out2; - } - - if (!FD_ISSET(socket1, &write_set)) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - fds[0] = socket1; - fds[1] = socket2; - - closesocket (temp); - - return 0; - -out2: - closesocket (socket2); -out1: - closesocket (socket1); -out0: - closesocket (temp); - errno = EIO; /* XXX */ - - return -1; -} - -struct ggp_resolver_win32thread_data { - char *hostname; - int fd; -}; - -/** - * Copy-paste from gg_resolver_run(). - */ -static DWORD WINAPI ggp_resolver_win32thread_thread(LPVOID arg) -{ - struct ggp_resolver_win32thread_data *data = arg; - struct in_addr addr_ip[2], *addr_list; - int addr_count; - - purple_debug_info("gg", "ggp_resolver_win32thread_thread() host: %s, " - "fd: %i called\n", data->hostname, data->fd); - - if ((addr_ip[0].s_addr = inet_addr(data->hostname)) == INADDR_NONE) { - if (gg_gethostbyname_real(data->hostname, &addr_list, - &addr_count, 0) == -1) { - addr_list = addr_ip; - /* addr_ip[0] już zawiera INADDR_NONE */ - } - } else { - addr_list = addr_ip; - addr_ip[1].s_addr = INADDR_NONE; - addr_count = 1; - } - - purple_debug_misc("gg", "ggp_resolver_win32thread_thread() " - "count = %d\n", addr_count); - - write(data->fd, addr_list, (addr_count + 1) * sizeof(struct in_addr)); - close(data->fd); - - free(data->hostname); - data->hostname = NULL; - - free(data); - - if (addr_list != addr_ip) - free(addr_list); - - purple_debug_misc("gg", "ggp_resolver_win32thread_thread() done\n"); - - return 0; -} - - -int ggp_resolver_win32thread_start(int *fd, void **private_data, - const char *hostname) -{ - struct ggp_resolver_win32thread_data *data = NULL; - HANDLE h; - DWORD dwTId; - int pipes[2], new_errno; - - purple_debug_info("gg", "ggp_resolver_win32thread_start(%p, %p, " - "\"%s\");\n", fd, private_data, hostname); - - if (!private_data || !fd || !hostname) { - purple_debug_error("gg", "ggp_resolver_win32thread_start() " - "invalid arguments\n"); - errno = EFAULT; - return -1; - } - - purple_debug_misc("gg", "ggp_resolver_win32thread_start() creating " - "pipes...\n"); - - if (ggp_resolver_win32thread_socket_pipe(pipes) == -1) { - purple_debug_error("gg", "ggp_resolver_win32thread_start() " - "unable to create pipes (errno=%d, %s)\n", - errno, strerror(errno)); - return -1; - } - - if (!(data = malloc(sizeof(*data)))) { - purple_debug_error("gg", "ggp_resolver_win32thread_start() out " - "of memory\n"); - new_errno = errno; - goto cleanup; - } - - data->hostname = NULL; - - if (!(data->hostname = strdup(hostname))) { - purple_debug_error("gg", "ggp_resolver_win32thread_start() out " - "of memory\n"); - new_errno = errno; - goto cleanup; - } - - data->fd = pipes[1]; - - purple_debug_misc("gg", "ggp_resolver_win32thread_start() creating " - "thread...\n"); - - h = CreateThread(NULL, 0, ggp_resolver_win32thread_thread, data, 0, - &dwTId); - - if (h == NULL) { - purple_debug_error("gg", "ggp_resolver_win32thread_start() " - "unable to create thread\n"); - new_errno = errno; - goto cleanup; - } - - *private_data = h; - *fd = pipes[0]; - - purple_debug_misc("gg", "ggp_resolver_win32thread_start() done\n"); - - return 0; - -cleanup: - if (data) { - free(data->hostname); - free(data); - } - - close(pipes[0]); - close(pipes[1]); - - errno = new_errno; - - return -1; - -} - -void ggp_resolver_win32thread_cleanup(void **private_data, int force) -{ - struct ggp_resolver_win32thread_data *data; - - purple_debug_info("gg", "ggp_resolver_win32thread_cleanup() force: %i " - "called\n", force); - - if (private_data == NULL || *private_data == NULL) { - purple_debug_error("gg", "ggp_resolver_win32thread_cleanup() " - "private_data: NULL\n"); - return; - } - return; /* XXX */ - - data = (struct ggp_resolver_win32thread_data*) *private_data; - purple_debug_misc("gg", "ggp_resolver_win32thread_cleanup() data: " - "%s called\n", data->hostname); - *private_data = NULL; - - if (force) { - purple_debug_misc("gg", "ggp_resolver_win32thread_cleanup() " - "force called\n"); - //pthread_cancel(data->thread); - //pthread_join(data->thread, NULL); - } - - free(data->hostname); - data->hostname = NULL; - - if (data->fd != -1) { - close(data->fd); - data->fd = -1; - } - purple_debug_info("gg", "ggp_resolver_win32thread_cleanup() done\n"); - free(data); -} - -/* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/win32-resolver.h Tue Aug 14 22:05:05 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/** - * @file win32-resolver.h - * - * purple - * - * 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_GG_WIN32_RESOLVER -#define _PURPLE_GG_WIN32_RESOLVER - -/** - * Starts hostname resolving in new win32 thread. - * - * @param fd Pointer to variable, where pipe descriptor will be saved. - * @param private_data Pointer to variable, where pointer to private data will - * be saved. - * @param hostname Hostname to resolve. - */ -int ggp_resolver_win32thread_start(int *fd, void **private_data, - const char *hostname); - -/** - * Cleans up resources after hostname resolving. - * - * @param private_data Pointer to variable storing pointer to private data. - * @param force TRUE, if resources should be cleaned up even, if - * resolving process didn't finished. - */ -void ggp_resolver_win32thread_cleanup(void **private_data, int force); - -#endif /* _PURPLE_GG_WIN32_RESOLVER */ - -/* vim: set ts=8 sts=0 sw=8 noet: */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/xml.c Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,103 @@ +#include "xml.h" + +#include "debug.h" + +gboolean ggp_xml_get_string(const xmlnode *xml, gchar *childName, gchar **var) +{ + char *str; + + g_return_val_if_fail(xml != NULL, FALSE); + g_return_val_if_fail(var != NULL, FALSE); + + if (childName != NULL) + { + xml = xmlnode_get_child(xml, childName); + g_return_val_if_fail(xml != NULL, FALSE); + } + + str = xmlnode_get_data(xml); + g_return_val_if_fail(str != NULL, FALSE); + + *var = str; + return TRUE; +} + +gboolean ggp_xml_get_bool(const xmlnode *xml, gchar *childName, gboolean *var) +{ + char *str; + gboolean succ; + + succ = ggp_xml_get_string(xml, childName, &str); + g_return_val_if_fail(succ, FALSE); + + *var = (strcmp(str, "true") == 0 || + strcmp(str, "True") == 0 || + strcmp(str, "TRUE") == 0 || + strcmp(str, "1") == 0); + g_free(str); + + return TRUE; +} + +gboolean ggp_xml_get_uint(const xmlnode *xml, gchar *childName, unsigned int *var) +{ + char *str, *endptr; + gboolean succ; + unsigned int val; + + succ = ggp_xml_get_string(xml, childName, &str); + g_return_val_if_fail(succ, FALSE); + + errno = 0; + val = strtoul(str, &endptr, 10); + + succ = (errno != ERANGE && endptr[0] == '\0'); + g_free(str); + + if (succ) + *var = val; + return succ; +} + +gboolean ggp_xml_set_string(xmlnode *xml, gchar *childName, const gchar *val) +{ + g_return_val_if_fail(xml != NULL, FALSE); + g_return_val_if_fail(val != NULL, FALSE); + + if (childName != NULL) + { + xmlnode *child = xmlnode_get_child(xml, childName); + if (child == NULL) + child = xmlnode_new_child(xml, childName); + xml = child; + } + + ggp_xmlnode_remove_children(xml); + xmlnode_insert_data(xml, val, -1); + + return TRUE; +} + +gboolean ggp_xml_set_bool(xmlnode *xml, gchar *childName, gboolean val) +{ + return ggp_xml_set_string(xml, childName, val ? "true" : "false"); +} + +gboolean ggp_xml_set_uint(xmlnode *xml, gchar *childName, unsigned int val) +{ + gchar buff[20]; + g_snprintf(buff, sizeof(buff), "%u", val); + return ggp_xml_set_string(xml, childName, buff); +} + +void ggp_xmlnode_remove_children(xmlnode *xml) +{ + xmlnode *child = xml->child; + while (child) + { + xmlnode *next = child->next; + if (child->type != XMLNODE_TYPE_ATTRIB) + xmlnode_free(child); + child = next; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/xml.h Tue Aug 14 22:26:33 2012 +0200 @@ -0,0 +1,17 @@ +#ifndef _GGP_XML_H +#define _GGP_XML_H + +#include <internal.h> +#include <xmlnode.h> + +gboolean ggp_xml_get_string(const xmlnode *xml, gchar *childName, gchar **var); +gboolean ggp_xml_get_bool(const xmlnode *xml, gchar *childName, gboolean *var); +gboolean ggp_xml_get_uint(const xmlnode *xml, gchar *childName, unsigned int *var); + +gboolean ggp_xml_set_string(xmlnode *xml, gchar *childName, const gchar *val); +gboolean ggp_xml_set_bool(xmlnode *xml, gchar *childName, gboolean val); +gboolean ggp_xml_set_uint(xmlnode *xml, gchar *childName, unsigned int val); + +void ggp_xmlnode_remove_children(xmlnode *xml); + +#endif /* _GGP_XML_H */
--- a/libpurple/win32/win32dep.c Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/win32/win32dep.c Tue Aug 14 22:26:33 2012 +0200 @@ -328,6 +328,111 @@ return result; } +int wpurple_input_pipe(int pipefd[2]) +{ + SOCKET sock_server, sock_client, sock_server_established; + struct sockaddr_in saddr_in; + struct sockaddr * const saddr_p = (struct sockaddr *)&saddr_in; + int saddr_len = sizeof(struct sockaddr_in); + u_long arg; + fd_set select_set; + char succ = 1; + + sock_server = sock_client = sock_server_established = INVALID_SOCKET; + + purple_debug_misc("wpurple", "wpurple_input_pipe(0x%x[%d,%d])\n", + (unsigned int)pipefd, pipefd[0], pipefd[1]); + + /* create client and passive server sockets */ + sock_server = socket(AF_INET, SOCK_STREAM, 0); + sock_client = socket(AF_INET, SOCK_STREAM, 0); + succ = (sock_server != INVALID_SOCKET || sock_client != INVALID_SOCKET); + + /* set created sockets into nonblocking mode */ + arg = 1; + succ = (succ && + ioctlsocket(sock_server, FIONBIO, &arg) != SOCKET_ERROR); + arg = 1; + succ = (succ && + ioctlsocket(sock_client, FIONBIO, &arg) != SOCKET_ERROR); + + /* listen on server socket */ + memset(&saddr_in, 0, saddr_len); + saddr_in.sin_family = AF_INET; + saddr_in.sin_port = 0; + saddr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + succ = (succ && + bind(sock_server, saddr_p, saddr_len) != SOCKET_ERROR && + listen(sock_server, 1) != SOCKET_ERROR && + getsockname(sock_server, saddr_p, &saddr_len) != SOCKET_ERROR); + + /* request a connection from client to server socket */ + succ = (succ && + connect(sock_client, saddr_p, saddr_len) == SOCKET_ERROR && + WSAGetLastError() == WSAEWOULDBLOCK); + + /* ensure, that server socket is readable */ + if (succ) + { + FD_ZERO(&select_set); + FD_SET(sock_server, &select_set); + } + succ = (succ && + select(0, &select_set, NULL, NULL, NULL) != SOCKET_ERROR && + FD_ISSET(sock_server, &select_set)); + + /* accept (establish) connection from client socket */ + if (succ) + { + sock_server_established = + accept(sock_server, saddr_p, &saddr_len); + succ = (sock_server_established != INVALID_SOCKET); + } + + /* ensure, that client socket is writable */ + if (succ) + { + FD_ZERO(&select_set); + FD_SET(sock_client, &select_set); + } + succ = (succ && + select(0, NULL, &select_set, NULL, NULL) != SOCKET_ERROR && + FD_ISSET(sock_client, &select_set)); + + /* set sockets into blocking mode */ + arg = 0; + succ = (succ && + ioctlsocket(sock_client, FIONBIO, &arg) != SOCKET_ERROR); + arg = 0; + succ = (succ && + ioctlsocket(sock_server_established, FIONBIO, &arg) + != SOCKET_ERROR); + + /* we don't need (passive) server socket anymore */ + if (sock_server != INVALID_SOCKET) + closesocket(sock_server); + + if (succ) + { + purple_debug_misc("wpurple", + "wpurple_input_pipe created pipe [%d,%d]\n", + sock_client, sock_server_established); + pipefd[0] = sock_client; /* for reading */ + pipefd[1] = sock_server_established; /* for writing */ + return 0; + } + else + { + purple_debug_error("wpurple", "wpurple_input_pipe failed\n"); + if (sock_client != INVALID_SOCKET) + closesocket(sock_client); + if (sock_server_established != INVALID_SOCKET) + closesocket(sock_server_established); + errno = EMFILE; + return -1; + } +} + void wpurple_init(void) { WORD wVersionRequested; WSADATA wsaData;
--- a/libpurple/win32/win32dep.h Tue Aug 14 22:05:05 2012 +0200 +++ b/libpurple/win32/win32dep.h Tue Aug 14 22:26:33 2012 +0200 @@ -60,6 +60,9 @@ char *wpurple_escape_dirsep(const char *filename); /* needs to be g_free'd */ GIOChannel *wpurple_g_io_channel_win32_new_socket(int socket); /* Until we get the post-2.8 glib win32 giochannel implementation working, use the thread-based one */ +/* Simulate unix pipes by creating a pair of connected sockets */ +int wpurple_input_pipe(int pipefd[2]); + /* Determine Purple paths */ gchar *wpurple_get_special_folder(int folder_type); /* needs to be g_free'd */ const char *wpurple_install_dir(void);