Merge GSoC 2012 Gadu-Gadu branch
authorTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>
Tue, 21 Aug 2012 19:16:39 +0200
changeset65e581a04a06 pushlog
parent e3155594ea98
parent d4a017428f4d
child bd15903d0d89
Merge GSoC 2012 Gadu-Gadu branch
libpurple/protocols/gg/gg-utils.c
libpurple/protocols/gg/gg-utils.h
libpurple/protocols/gg/lib/libgadu-config.h
libpurple/protocols/gg/lib/libgadu-debug.h
libpurple/protocols/gg/lib/libgadu-internal.h
libpurple/protocols/gg/search.c
libpurple/protocols/gg/search.h
libpurple/protocols/gg/win32-resolver.c
libpurple/protocols/gg/win32-resolver.h
     1.1 --- a/configure.ac
     1.2 +++ b/configure.ac
     1.3 @@ -816,6 +816,19 @@
     1.4  AC_SUBST(LIBXML_LIBS)
     1.5  
     1.6  dnl #######################################################################
     1.7 +dnl # Check for zlib (required)
     1.8 +dnl #######################################################################
     1.9 +
    1.10 +PKG_CHECK_MODULES(ZLIB, [zlib >= 1.2.0], , [
    1.11 +	AC_MSG_RESULT(no)
    1.12 +	AC_MSG_ERROR([
    1.13 +You must have zlib >= 1.2.0 development headers installed to build.
    1.14 +])])
    1.15 +
    1.16 +AC_SUBST(ZLIB_CFLAGS)
    1.17 +AC_SUBST(ZLIB_LIBS)
    1.18 +
    1.19 +dnl #######################################################################
    1.20  dnl # GConf schemas
    1.21  dnl #######################################################################
    1.22  AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
    1.23 @@ -1069,13 +1082,14 @@
    1.24  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"])
    1.25  GADU_CFLAGS=""
    1.26  GADU_LIBS=""
    1.27 +GADU_LIBGADU_VERSION=1.11.2
    1.28  if test -n "$with_gadu_includes" || test -n "$with_gadu_libs"; then
    1.29  	gadu_manual_check="yes"
    1.30  else
    1.31  	gadu_manual_check="no"
    1.32  fi
    1.33  if test "x$gadu_manual_check" = "xno"; then
    1.34 -	PKG_CHECK_MODULES(GADU, [libgadu >= 1.11.0], [
    1.35 +	PKG_CHECK_MODULES(GADU, [libgadu >= $GADU_LIBGADU_VERSION], [
    1.36  		gadu_includes="yes"
    1.37  		gadu_libs="yes"
    1.38  	], [
    1.39 @@ -1107,28 +1121,7 @@
    1.40  #error "libgadu is not compatible with the GPL when compiled with OpenSSL support."
    1.41  #endif
    1.42  	]])], [
    1.43 -		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[
    1.44 -#if GG_DEFAULT_PROTOCOL_VERSION < 0x2e
    1.45 -#error "Your libgadu version is too old. libpurple requires 1.11.0 or higher."
    1.46 -#endif
    1.47 -		]])], [
    1.48 -			AC_MSG_RESULT(yes)
    1.49 -			AC_DEFINE([HAVE_LIBGADU], [1],
    1.50 -				[Define to 1 if you have libgadu.])
    1.51 -		], [
    1.52 -			AC_MSG_RESULT(no)
    1.53 -			echo
    1.54 -			echo
    1.55 -			echo "Your supplied copy of libgadu is too old."
    1.56 -			echo "Install version 1.11.0 or newer."
    1.57 -			echo "Then rerun this ./configure"
    1.58 -			echo
    1.59 -			echo "Falling back to using our own copy of libgadu"
    1.60 -			echo
    1.61 -			GADU_LIBS=""
    1.62 -			GADU_CFLAGS=""
    1.63 -			gadu_libs=no
    1.64 -		])
    1.65 +		AC_MSG_RESULT(yes)
    1.66  	], [
    1.67  		AC_MSG_RESULT(no)
    1.68  		echo
    1.69 @@ -1147,6 +1140,35 @@
    1.70  	CPPFLAGS="$CPPFLAGS_save"
    1.71  fi
    1.72  
    1.73 +if test "x$gadu_libs" = "xyes" -a "x$gadu_manual_check" = "xyes"; then
    1.74 +	AC_MSG_CHECKING(for supplied libgadu compatibility)
    1.75 +	CPPFLAGS_save="$CPPFLAGS"
    1.76 +	CPPFLAGS="$CPPFLAGS $GADU_CFLAGS"
    1.77 +
    1.78 +	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[
    1.79 +#if GG_DEFAULT_PROTOCOL_VERSION < 0x2e
    1.80 +#error "Your libgadu version is too old."
    1.81 +#endif
    1.82 +	]])], [
    1.83 +		AC_MSG_RESULT(yes)
    1.84 +	], [
    1.85 +		AC_MSG_RESULT(no)
    1.86 +		echo
    1.87 +		echo "Your supplied copy of libgadu is too old."
    1.88 +		echo "Install version $GADU_LIBGADU_VERSION or newer."
    1.89 +		echo "Then rerun this ./configure"
    1.90 +		echo
    1.91 +		echo "Falling back to using our own copy of libgadu"
    1.92 +		echo
    1.93 +		GADU_LIBS=""
    1.94 +		GADU_CFLAGS=""
    1.95 +		gadu_libs=no
    1.96 +	])
    1.97 +
    1.98 +	CPPFLAGS="$CPPFLAGS_save"
    1.99 +fi
   1.100 +
   1.101 +AM_CONDITIONAL(HAVE_LIBGADU, test "x$gadu_libs" = "xyes")
   1.102  AM_CONDITIONAL(USE_INTERNAL_LIBGADU, test "x$gadu_libs" != "xyes")
   1.103  
   1.104  if test "x$gadu_libs" = "x"; then
   1.105 @@ -1155,6 +1177,7 @@
   1.106  
   1.107  AC_SUBST(GADU_LIBS)
   1.108  AC_SUBST(GADU_CFLAGS)
   1.109 +AC_SUBST(GADU_LIBGADU_VERSION)
   1.110  
   1.111  AC_ARG_ENABLE(distrib,,,enable_distrib=no)
   1.112  AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes")
     2.1 --- a/libpurple/eventloop.c
     2.2 +++ b/libpurple/eventloop.c
     2.3 @@ -91,6 +91,16 @@
     2.4  	}
     2.5  }
     2.6  
     2.7 +int
     2.8 +purple_input_pipe(int pipefd[2])
     2.9 +{
    2.10 +#ifdef _WIN32
    2.11 +	return wpurple_input_pipe(pipefd);
    2.12 +#else
    2.13 +	return pipe(pipefd);
    2.14 +#endif
    2.15 +}
    2.16 +
    2.17  void
    2.18  purple_eventloop_set_ui_ops(PurpleEventLoopUiOps *ops)
    2.19  {
     3.1 --- a/libpurple/eventloop.h
     3.2 +++ b/libpurple/eventloop.h
     3.3 @@ -240,6 +240,24 @@
     3.4  int
     3.5  purple_input_get_error(int fd, int *error);
     3.6  
     3.7 +/**
     3.8 + * Creates a pipe - an unidirectional data channel that can be used for
     3.9 + * interprocess communication.
    3.10 + *
    3.11 + * File descriptors for both ends of pipe will be written into provided array.
    3.12 + * The first one (pipefd[0]) can be used for reading, the second one (pipefd[1])
    3.13 + * for writing.
    3.14 + *
    3.15 + * On Windows it's simulated by creating a pair of connected sockets, on other
    3.16 + * systems pipe() is used.
    3.17 + *
    3.18 + * @param pipefd Array used to return file descriptors for both ends of pipe.
    3.19 + *
    3.20 + * @return @c 0 on success, @c -1 on error.
    3.21 + */
    3.22 +int
    3.23 +purple_input_pipe(int pipefd[2]);
    3.24 +
    3.25  
    3.26  /*@}*/
    3.27  
     4.1 --- a/libpurple/protocols/gg/Makefile.am
     4.2 +++ b/libpurple/protocols/gg/Makefile.am
     4.3 @@ -1,13 +1,20 @@
     4.4 +V=0
     4.5 +
     4.6 +pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
     4.7 +
     4.8  EXTRA_DIST = \
     4.9  	Makefile.mingw \
    4.10 -	win32-resolver.c \
    4.11 -	win32-resolver.h \
    4.12 +	lib/COPYING
    4.13 +
    4.14 +if USE_INTERNAL_LIBGADU
    4.15 +INTGGSOURCES = \
    4.16  	lib/common.c \
    4.17  	lib/compat.h \
    4.18 -	lib/COPYING \
    4.19 +	lib/config.h \
    4.20  	lib/dcc.c \
    4.21  	lib/dcc7.c \
    4.22  	lib/debug.c \
    4.23 +	lib/debug.h \
    4.24  	lib/deflate.c \
    4.25  	lib/deflate.h \
    4.26  	lib/encoding.c \
    4.27 @@ -15,11 +22,9 @@
    4.28  	lib/events.c \
    4.29  	lib/handlers.c \
    4.30  	lib/http.c \
    4.31 +	lib/internal.h \
    4.32 +	lib/libgadu.c \
    4.33  	lib/libgadu.h \
    4.34 -	lib/libgadu.c \
    4.35 -	lib/libgadu-config.h \
    4.36 -	lib/libgadu-debug.h \
    4.37 -	lib/libgadu-internal.h \
    4.38  	lib/message.c \
    4.39  	lib/message.h \
    4.40  	lib/obsolete.c \
    4.41 @@ -31,38 +36,11 @@
    4.42  	lib/session.h \
    4.43  	lib/sha1.c
    4.44  
    4.45 -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
    4.46 -
    4.47 -if USE_INTERNAL_LIBGADU
    4.48 -INTGGSOURCES = \
    4.49 -	lib/common.c \
    4.50 -	lib/compat.h \
    4.51 -	lib/dcc.c \
    4.52 -	lib/dcc7.c \
    4.53 -	lib/debug.c \
    4.54 -	lib/deflate.c \
    4.55 -	lib/deflate.h \
    4.56 -	lib/encoding.c \
    4.57 -	lib/encoding.h \
    4.58 -	lib/events.c \
    4.59 -	lib/handlers.c \
    4.60 -	lib/http.c \
    4.61 -	lib/libgadu.h \
    4.62 -	lib/libgadu.c \
    4.63 -	lib/libgadu-config.h \
    4.64 -	lib/libgadu-internal.h \
    4.65 -	lib/message.c \
    4.66 -	lib/message.h \
    4.67 -	lib/obsolete.c \
    4.68 -	lib/protocol.h \
    4.69 -	lib/pubdir.c \
    4.70 -	lib/pubdir50.c \
    4.71 -	lib/resolver.c \
    4.72 -	lib/resolver.h \
    4.73 -	lib/session.h \
    4.74 -	lib/sha1.c
    4.75 -
    4.76 -INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED -DUSE_INTERNAL_LIBGADU
    4.77 +INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib \
    4.78 +	$(ZLIB_CFLAGS) \
    4.79 +	-DGG_IGNORE_DEPRECATED \
    4.80 +	-DGG_INTERNAL_LIBGADU_VERSION=$(GADU_LIBGADU_VERSION)
    4.81 +GADU_LIBS += $(ZLIB_LIBS)
    4.82  endif
    4.83  
    4.84  if USE_GNUTLS
    4.85 @@ -72,16 +50,50 @@
    4.86  
    4.87  GGSOURCES = \
    4.88  	$(INTGGSOURCES) \
    4.89 -	gg-utils.h \
    4.90 -	gg-utils.c \
    4.91 +	utils.h \
    4.92 +	utils.c \
    4.93  	confer.h \
    4.94  	confer.c \
    4.95 -	search.h \
    4.96 -	search.c \
    4.97  	buddylist.h \
    4.98  	buddylist.c \
    4.99  	gg.h \
   4.100 -	gg.c
   4.101 +	gg.c \
   4.102 +	resolver-purple.h \
   4.103 +	resolver-purple.c \
   4.104 +	image.h \
   4.105 +	image.c \
   4.106 +	account.h \
   4.107 +	account.c \
   4.108 +	deprecated.h \
   4.109 +	deprecated.c \
   4.110 +	purplew.h \
   4.111 +	purplew.c \
   4.112 +	libgaduw.h \
   4.113 +	libgaduw.c \
   4.114 +	avatar.h \
   4.115 +	avatar.c \
   4.116 +	libgadu-events.h \
   4.117 +	libgadu-events.c \
   4.118 +	roster.c \
   4.119 +	roster.h \
   4.120 +	validator.c \
   4.121 +	validator.h \
   4.122 +	xml.c \
   4.123 +	xml.h \
   4.124 +	multilogon.c \
   4.125 +	multilogon.h \
   4.126 +	status.c \
   4.127 +	status.h \
   4.128 +	servconn.c \
   4.129 +	servconn.h \
   4.130 +	pubdir-prpl.c \
   4.131 +	pubdir-prpl.h \
   4.132 +	oauth/oauth.c \
   4.133 +	oauth/oauth.h \
   4.134 +	oauth/oauth-parameter.c \
   4.135 +	oauth/oauth-parameter.h \
   4.136 +	oauth/oauth-purple.c \
   4.137 +	oauth/oauth-purple.h
   4.138  
   4.139  AM_CFLAGS = $(st)
   4.140  
   4.141 @@ -105,9 +117,9 @@
   4.142  endif
   4.143  
   4.144  AM_CPPFLAGS = \
   4.145 +	-Wall -Wextra -Werror \
   4.146  	-I$(top_srcdir)/libpurple \
   4.147  	-I$(top_builddir)/libpurple \
   4.148  	$(INTGG_CFLAGS) \
   4.149  	$(GLIB_CFLAGS) \
   4.150  	$(DEBUG_CFLAGS)
   4.151 -
     5.1 --- a/libpurple/protocols/gg/Makefile.mingw
     5.2 +++ b/libpurple/protocols/gg/Makefile.mingw
     5.3 @@ -24,14 +24,14 @@
     5.4  ##
     5.5  ## INCLUDE PATHS
     5.6  ##
     5.7 -INCLUDE_PATHS +=	-I. \
     5.8 +INCLUDE_PATHS +=\
     5.9 +			-I$(PIDGIN_TREE_TOP) \
    5.10 +			-I$(PURPLE_TOP) \
    5.11 +			-I$(PURPLE_TOP)/win32 \
    5.12  			-I./lib \
    5.13  			-I$(GTK_TOP)/include \
    5.14  			-I$(GTK_TOP)/include/glib-2.0 \
    5.15 -			-I$(GTK_TOP)/lib/glib-2.0/include \
    5.16 -			-I$(PURPLE_TOP) \
    5.17 -			-I$(PURPLE_TOP)/win32 \
    5.18 -			-I$(PIDGIN_TREE_TOP)
    5.19 +			-I$(GTK_TOP)/lib/glib-2.0/include
    5.20  
    5.21  LIB_PATHS +=		-L$(GTK_TOP)/lib \
    5.22  			-L$(PURPLE_TOP) \
    5.23 @@ -60,8 +60,8 @@
    5.24  	confer.c \
    5.25  	gg.c \
    5.26  	search.c \
    5.27 -	gg-utils.c \
    5.28 -	win32-resolver.c
    5.29 +	utils.c \
    5.30 +	resolver-purple.c
    5.31  
    5.32  OBJECTS = $(C_SRC:%.c=%.o)
    5.33  
     6.1 new file mode 100644
     6.2 --- /dev/null
     6.3 +++ b/libpurple/protocols/gg/account.c
     6.4 @@ -0,0 +1,649 @@
     6.5 +/* purple
     6.6 + *
     6.7 + * Purple is the legal property of its developers, whose names are too numerous
     6.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
     6.9 + * source distribution.
    6.10 + *
    6.11 + * Rewritten from scratch during Google Summer of Code 2012
    6.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
    6.13 + *
    6.14 + * Previously implemented by:
    6.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
    6.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
    6.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
    6.18 + *
    6.19 + * This program is free software; you can redistribute it and/or modify
    6.20 + * it under the terms of the GNU General Public License as published by
    6.21 + * the Free Software Foundation; either version 2 of the License, or
    6.22 + * (at your option) any later version.
    6.23 + *
    6.24 + * This program is distributed in the hope that it will be useful,
    6.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.27 + * GNU General Public License for more details.
    6.28 + *
    6.29 + * You should have received a copy of the GNU General Public License
    6.30 + * along with this program; if not, write to the Free Software
    6.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
    6.32 + */
    6.33 +
    6.34 +#include "account.h"
    6.35 +
    6.36 +#include <libgadu.h>
    6.37 +#include <debug.h>
    6.38 +
    6.39 +#include "deprecated.h"
    6.40 +#include "purplew.h"
    6.41 +#include "utils.h"
    6.42 +#include "libgaduw.h"
    6.43 +#include "validator.h"
    6.44 +
    6.45 +/*******************************************************************************
    6.46 + * Token requesting.
    6.47 + ******************************************************************************/
    6.48 +
    6.49 +typedef struct
    6.50 +{
    6.51 +	ggp_account_token_cb callback;
    6.52 +	PurpleConnection *gc;
    6.53 +	void *user_data;
    6.54 +} ggp_account_token_reqdata;
    6.55 +
    6.56 +static void ggp_account_token_response(struct gg_http *h, gboolean success,
    6.57 +	gboolean cancelled, gpointer _reqdata);
    6.58 +
    6.59 +/******************************************************************************/
    6.60 +
    6.61 +void ggp_account_token_free(ggp_account_token *token)
    6.62 +{
    6.63 +	if (!token)
    6.64 +		return;
    6.65 +	g_free(token->id);
    6.66 +	g_free(token->data);
    6.67 +	g_free(token);
    6.68 +}
    6.69 +
    6.70 +void ggp_account_token_request(PurpleConnection *gc,
    6.71 +	ggp_account_token_cb callback, void *user_data)
    6.72 +{
    6.73 +	struct gg_http *h;
    6.74 +	ggp_account_token_reqdata *reqdata;
    6.75 +
    6.76 +	purple_debug_info("gg", "ggp_account_token_request: "
    6.77 +		"requesting token...\n");
    6.78 +
    6.79 +	if (!ggp_deprecated_setup_proxy(gc))
    6.80 +	{
    6.81 +		callback(gc, NULL, user_data);
    6.82 +		return;
    6.83 +	}
    6.84 +	
    6.85 +	h = gg_token(TRUE);
    6.86 +	
    6.87 +	if (!h)
    6.88 +	{
    6.89 +		callback(gc, NULL, user_data);
    6.90 +		return;
    6.91 +	}
    6.92 +	
    6.93 +	reqdata = g_new(ggp_account_token_reqdata, 1);
    6.94 +	reqdata->callback = callback;
    6.95 +	reqdata->gc = gc;
    6.96 +	reqdata->user_data = user_data;
    6.97 +	ggp_libgaduw_http_watch(gc, h, ggp_account_token_response, reqdata,
    6.98 +		TRUE);
    6.99 +}
   6.100 +
   6.101 +static void ggp_account_token_response(struct gg_http *h, gboolean success,
   6.102 +	gboolean cancelled, gpointer _reqdata)
   6.103 +{
   6.104 +	ggp_account_token_reqdata *reqdata = _reqdata;
   6.105 +	struct gg_token *token_info;
   6.106 +	ggp_account_token *token = NULL;
   6.107 +	
   6.108 +	g_assert(!(success && cancelled));
   6.109 +	
   6.110 +	if (cancelled)
   6.111 +		purple_debug_info("gg", "ggp_account_token_handler: "
   6.112 +			"cancelled\n");
   6.113 +	else if (success)
   6.114 +	{
   6.115 +		purple_debug_info("gg", "ggp_account_token_handler: "
   6.116 +			"got token\n");
   6.117 +	
   6.118 +		token = g_new(ggp_account_token, 1);
   6.119 +	
   6.120 +		token_info = h->data;
   6.121 +		token->id = g_strdup(token_info->tokenid);
   6.122 +		token->size = h->body_size;
   6.123 +		token->data = g_memdup(h->body, token->size);
   6.124 +		token->length = token_info->length;
   6.125 +	}
   6.126 +	else
   6.127 +	{
   6.128 +		purple_debug_error("gg", "ggp_account_token_handler: error\n");
   6.129 +		purple_notify_error(
   6.130 +			purple_connection_get_account(reqdata->gc),
   6.131 +			_("Token Error"),
   6.132 +			_("Unable to fetch the token."), NULL);
   6.133 +	}
   6.134 +	
   6.135 +	reqdata->callback(reqdata->gc, token, reqdata->user_data);
   6.136 +	g_free(reqdata);
   6.137 +}
   6.138 +
   6.139 +gboolean ggp_account_token_validate(ggp_account_token *token,
   6.140 +	const gchar *value)
   6.141 +{
   6.142 +	if (strlen(value) != token->length)
   6.143 +		return FALSE;
   6.144 +	return g_regex_match_simple("^[a-zA-Z0-9]+$", value, 0, 0);
   6.145 +}
   6.146 +
   6.147 +/*******************************************************************************
   6.148 + * New account registration.
   6.149 + ******************************************************************************/
   6.150 +
   6.151 +typedef struct
   6.152 +{
   6.153 +	ggp_account_token *token;
   6.154 +	PurpleConnection *gc;
   6.155 +	
   6.156 +	gchar *email;
   6.157 +	gchar *password;
   6.158 +	gchar *token_value;
   6.159 +	gboolean password_remember;
   6.160 +} ggp_account_register_data;
   6.161 +
   6.162 +static void ggp_account_register_dialog(PurpleConnection *gc,
   6.163 +	ggp_account_token *token, gpointer _register_data);
   6.164 +static void ggp_account_register_dialog_ok(
   6.165 +	ggp_account_register_data *register_data, PurpleRequestFields *fields);
   6.166 +#if 0
   6.167 +static void ggp_account_register_dialog_invalid(
   6.168 +	ggp_account_register_data *register_data, const gchar *message);
   6.169 +#endif
   6.170 +static void ggp_account_register_dialog_cancel(
   6.171 +	ggp_account_register_data *register_data, PurpleRequestFields *fields);
   6.172 +static void ggp_account_register_response(struct gg_http *h, gboolean success,
   6.173 +	gboolean cancelled, gpointer _reqdata);
   6.174 +static void ggp_account_register_completed(
   6.175 +	ggp_account_register_data *register_data, gboolean success);
   6.176 +
   6.177 +#define GGP_ACCOUNT_REGISTER_TITLE _("Register New Gadu-Gadu Account")
   6.178 +
   6.179 +/******************************************************************************/
   6.180 +
   6.181 +void ggp_account_register(PurpleAccount *account)
   6.182 +{
   6.183 +	PurpleConnection *gc = purple_account_get_connection(account);
   6.184 +	ggp_account_register_data *register_data;
   6.185 +	
   6.186 +	purple_debug_info("gg", "ggp_account_register\n");
   6.187 +	
   6.188 +	register_data = g_new0(ggp_account_register_data, 1);
   6.189 +	register_data->gc = gc;
   6.190 +	register_data->password_remember = TRUE;
   6.191 +	
   6.192 +	ggp_account_token_request(gc, ggp_account_register_dialog,
   6.193 +		register_data);
   6.194 +}
   6.195 +
   6.196 +static void ggp_account_register_dialog(PurpleConnection *gc,
   6.197 +	ggp_account_token *token, gpointer _register_data)
   6.198 +{
   6.199 +	PurpleRequestFields *fields;
   6.200 +	PurpleRequestFieldGroup *main_group, *password_group, *token_group;
   6.201 +	PurpleRequestField *field, *field_password;
   6.202 +	ggp_account_register_data *register_data = _register_data;
   6.203 +	
   6.204 +	purple_debug_info("gg", "ggp_account_register_dialog(%x, %x, %x)\n",
   6.205 +		(unsigned int)gc, (unsigned int)token,
   6.206 +		(unsigned int)_register_data);
   6.207 +	if (!token)
   6.208 +	{
   6.209 +		ggp_account_register_completed(register_data, FALSE);
   6.210 +		return;
   6.211 +	}
   6.212 +	
   6.213 +	fields = purple_request_fields_new();
   6.214 +	main_group = purple_request_field_group_new(NULL);
   6.215 +	purple_request_fields_add_group(fields, main_group);
   6.216 +	
   6.217 +	field = purple_request_field_string_new("email", _("Email"),
   6.218 +		register_data->email, FALSE);
   6.219 +	purple_request_field_set_required(field, TRUE);
   6.220 +	purple_request_field_set_validator(field,
   6.221 +		purple_request_field_email_validator, NULL);
   6.222 +	purple_request_field_group_add_field(main_group, field);
   6.223 +
   6.224 +	password_group = purple_request_field_group_new(_("Password"));
   6.225 +	purple_request_fields_add_group(fields, password_group);
   6.226 +
   6.227 +	field = purple_request_field_string_new("password1", _("Password"),
   6.228 +		register_data->password, FALSE);
   6.229 +	purple_request_field_set_required(field, TRUE);
   6.230 +	purple_request_field_string_set_masked(field, TRUE);
   6.231 +	purple_request_field_set_validator(field, ggp_validator_password, NULL);
   6.232 +	purple_request_field_group_add_field(password_group, field);
   6.233 +	field_password = field;
   6.234 +	
   6.235 +	field = purple_request_field_string_new("password2",
   6.236 +		_("Password (again)"), register_data->password, FALSE);
   6.237 +	purple_request_field_set_required(field, TRUE);
   6.238 +	purple_request_field_string_set_masked(field, TRUE);
   6.239 +	purple_request_field_set_validator(field, ggp_validator_password_equal,
   6.240 +		field_password);
   6.241 +	purple_request_field_group_add_field(password_group, field);
   6.242 +	
   6.243 +	field = purple_request_field_bool_new("password_remember",
   6.244 +		_("Remember password"), register_data->password_remember);
   6.245 +	purple_request_field_group_add_field(password_group, field);
   6.246 +	
   6.247 +	token_group = purple_request_field_group_new(_("Captcha"));
   6.248 +	purple_request_fields_add_group(fields, token_group);
   6.249 +	
   6.250 +	field = purple_request_field_string_new("token_value",
   6.251 +		_("Enter text from image below"), register_data->token_value,
   6.252 +		FALSE);
   6.253 +	purple_request_field_set_required(field, TRUE);
   6.254 +	purple_request_field_set_validator(field, ggp_validator_token, token);
   6.255 +	purple_request_field_group_add_field(token_group, field);
   6.256 +	purple_debug_info("gg", "token set %p\n", register_data->token);
   6.257 +	
   6.258 +	field = purple_request_field_image_new("token_image", _("Captcha"),
   6.259 +		token->data, token->size);
   6.260 +	purple_request_field_group_add_field(token_group, field);
   6.261 +
   6.262 +	register_data->token = token;
   6.263 +	
   6.264 +	purple_request_fields(gc,
   6.265 +		GGP_ACCOUNT_REGISTER_TITLE,
   6.266 +		GGP_ACCOUNT_REGISTER_TITLE,
   6.267 +		_("Please, fill in the following fields"), fields,
   6.268 +		_("OK"), G_CALLBACK(ggp_account_register_dialog_ok),
   6.269 +		_("Cancel"), G_CALLBACK(ggp_account_register_dialog_cancel),
   6.270 +		purple_connection_get_account(gc), NULL, NULL, register_data);
   6.271 +}
   6.272 +
   6.273 +static void ggp_account_register_dialog_cancel(
   6.274 +	ggp_account_register_data *register_data, PurpleRequestFields *fields)
   6.275 +{
   6.276 +	purple_debug_info("gg", "ggp_account_register_dialog_cancel(%x, %x)\n",
   6.277 +		(unsigned int)register_data, (unsigned int)fields);
   6.278 +
   6.279 +	ggp_account_register_completed(register_data, FALSE);
   6.280 +}
   6.281 +
   6.282 +static void ggp_account_register_dialog_ok(
   6.283 +	ggp_account_register_data *register_data, PurpleRequestFields *fields)
   6.284 +{
   6.285 +	struct gg_http *h;
   6.286 +
   6.287 +	purple_debug_misc("gg", "ggp_account_register_dialog_ok(%x, %x)\n",
   6.288 +		(unsigned int)register_data, (unsigned int)fields);
   6.289 +
   6.290 +	g_free(register_data->email);
   6.291 +	g_free(register_data->password);
   6.292 +	g_free(register_data->token_value);
   6.293 +	
   6.294 +	register_data->email = g_strdup(
   6.295 +		purple_request_fields_get_string(fields, "email"));
   6.296 +	register_data->password = g_strdup(
   6.297 +		purple_request_fields_get_string(fields, "password1"));
   6.298 +	register_data->password_remember =
   6.299 +		purple_request_fields_get_bool(fields, "password_remember");
   6.300 +	register_data->token_value = g_strdup(
   6.301 +		purple_request_fields_get_string(fields, "token_value"));
   6.302 +
   6.303 +	g_assert(register_data->email != NULL);
   6.304 +	g_assert(register_data->password != NULL);
   6.305 +	g_assert(register_data->token_value != NULL);
   6.306 +
   6.307 +	h = gg_register3(register_data->email, register_data->password,
   6.308 +		register_data->token->id, register_data->token_value, TRUE);
   6.309 +	
   6.310 +	ggp_libgaduw_http_watch(register_data->gc, h,
   6.311 +		ggp_account_register_response, register_data, TRUE);
   6.312 +}
   6.313 +
   6.314 +#if 0
   6.315 +// libgadu 1.12.x: use it for invalid token
   6.316 +static void ggp_account_register_dialog_invalid(
   6.317 +	ggp_account_register_data *register_data, const gchar *message)
   6.318 +{
   6.319 +	purple_debug_warning("gg", "ggp_account_register_dialog_invalid: %s\n",
   6.320 +		message);
   6.321 +	ggp_account_register_dialog(register_data->gc, register_data->token,
   6.322 +		register_data);
   6.323 +	purple_notify_error(purple_connection_get_account(register_data->gc),
   6.324 +		GGP_ACCOUNT_REGISTER_TITLE, message, NULL);
   6.325 +}
   6.326 +#endif
   6.327 +
   6.328 +static void ggp_account_register_response(struct gg_http *h, gboolean success,
   6.329 +	gboolean cancelled, gpointer _register_data)
   6.330 +{
   6.331 +	ggp_account_register_data *register_data = _register_data;
   6.332 +	PurpleAccount *account =
   6.333 +		purple_connection_get_account(register_data->gc);
   6.334 +	struct gg_pubdir *register_result = h->data;
   6.335 +	uin_t uin;
   6.336 +	gchar *tmp;
   6.337 +	
   6.338 +	g_assert(!(success && cancelled));
   6.339 +	
   6.340 +	if (cancelled)
   6.341 +	{
   6.342 +		purple_debug_info("gg", "ggp_account_register_response: "
   6.343 +			"cancelled\n");
   6.344 +		ggp_account_register_completed(register_data, FALSE);
   6.345 +		return;
   6.346 +	}
   6.347 +	if (!success || !register_result->success)
   6.348 +	{
   6.349 +		//TODO (libgadu 1.12.x): check register_result->error
   6.350 +		purple_debug_error("gg", "ggp_account_register_response: "
   6.351 +			"error\n");
   6.352 +		purple_notify_error(NULL,
   6.353 +			GGP_ACCOUNT_REGISTER_TITLE,
   6.354 +			_("Unable to register new account. "
   6.355 +			"An unknown error occurred."), NULL);
   6.356 +		ggp_account_register_completed(register_data, FALSE);
   6.357 +		return;
   6.358 +	}
   6.359 +
   6.360 +	uin = register_result->uin;
   6.361 +	purple_debug_info("gg", "ggp_account_register_response: "
   6.362 +		"registered uin %u\n", uin);
   6.363 +	
   6.364 +	purple_account_set_username(account, ggp_uin_to_str(uin));
   6.365 +	purple_account_set_remember_password(account,
   6.366 +		register_data->password_remember);
   6.367 +	purple_account_set_password(account, register_data->password);
   6.368 +	
   6.369 +	tmp = g_strdup_printf(_("Your new GG number: %u."), uin);
   6.370 +	purple_notify_info(account, GGP_ACCOUNT_REGISTER_TITLE,
   6.371 +		_("Registration completed successfully!"), tmp);
   6.372 +	g_free(tmp);
   6.373 +	
   6.374 +	ggp_account_register_completed(register_data, TRUE);
   6.375 +}
   6.376 +
   6.377 +static void ggp_account_register_completed(
   6.378 +	ggp_account_register_data *register_data, gboolean success)
   6.379 +{
   6.380 +	PurpleAccount *account =
   6.381 +		purple_connection_get_account(register_data->gc);
   6.382 +
   6.383 +	purple_debug_misc("gg", "ggp_account_register_completed: %d\n",
   6.384 +		success);
   6.385 +	
   6.386 +	g_free(register_data->email);
   6.387 +	g_free(register_data->password);
   6.388 +	g_free(register_data->token_value);
   6.389 +	ggp_account_token_free(register_data->token);
   6.390 +	g_free(register_data);
   6.391 +	
   6.392 +	purple_account_disconnect(account);
   6.393 +	purple_account_register_completed(account, success);
   6.394 +}
   6.395 +
   6.396 +/*******************************************************************************
   6.397 + * Password change.
   6.398 + ******************************************************************************/
   6.399 +
   6.400 +typedef struct
   6.401 +{
   6.402 +	ggp_account_token *token;
   6.403 +	PurpleConnection *gc;
   6.404 +	
   6.405 +	gchar *email;
   6.406 +	gchar *password_current;
   6.407 +	gchar *password_new;
   6.408 +	gchar *token_value;
   6.409 +} ggp_account_chpass_data;
   6.410 +
   6.411 +static void ggp_account_chpass_data_free(ggp_account_chpass_data *chpass_data);
   6.412 +static void ggp_account_chpass_dialog(PurpleConnection *gc,
   6.413 +	ggp_account_token *token, gpointer _chpass_data);
   6.414 +static void ggp_account_chpass_dialog_ok(
   6.415 +	ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields);
   6.416 +static void ggp_account_chpass_dialog_invalid(
   6.417 +	ggp_account_chpass_data *chpass_data, const gchar *message);
   6.418 +static void ggp_account_chpass_dialog_cancel(
   6.419 +	ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields);
   6.420 +static void ggp_account_chpass_response(struct gg_http *h, gboolean success,
   6.421 +	gboolean cancelled, gpointer _chpass_data);
   6.422 +
   6.423 +#define GGP_ACCOUNT_CHPASS_TITLE _("Password change")
   6.424 +
   6.425 +/******************************************************************************/
   6.426 +
   6.427 +static void ggp_account_chpass_data_free(ggp_account_chpass_data *chpass_data)
   6.428 +{
   6.429 +	g_free(chpass_data->email);
   6.430 +	g_free(chpass_data->password_current);
   6.431 +	g_free(chpass_data->password_new);
   6.432 +	g_free(chpass_data->token_value);
   6.433 +	ggp_account_token_free(chpass_data->token);
   6.434 +	g_free(chpass_data);
   6.435 +}
   6.436 +
   6.437 +void ggp_account_chpass(PurpleConnection *gc)
   6.438 +{
   6.439 +	ggp_account_chpass_data *chpass_data;
   6.440 +	void ggp_account_change_passwd(PurpleConnection *gc);
   6.441 +	purple_debug_info("gg", "ggp_account_chpass\n");
   6.442 +	
   6.443 +	chpass_data = g_new0(ggp_account_chpass_data, 1);
   6.444 +	chpass_data->gc = gc;
   6.445 +	
   6.446 +	ggp_account_token_request(gc, ggp_account_chpass_dialog, chpass_data);
   6.447 +}
   6.448 +
   6.449 +static void ggp_account_chpass_dialog(PurpleConnection *gc,
   6.450 +	ggp_account_token *token, gpointer _chpass_data)
   6.451 +{
   6.452 +	ggp_account_chpass_data *chpass_data = _chpass_data;
   6.453 +	PurpleAccount *account = purple_connection_get_account(chpass_data->gc);
   6.454 +	PurpleRequestFields *fields;
   6.455 +	PurpleRequestFieldGroup *main_group, *password_group, *token_group;
   6.456 +	PurpleRequestField *field, *field_password;
   6.457 +	gchar *primary;
   6.458 +	
   6.459 +	purple_debug_info("gg", "ggp_account_chpass_dialog(%p, %p, %p)\n",
   6.460 +		gc, token, _chpass_data);
   6.461 +	if (!token)
   6.462 +	{
   6.463 +		ggp_account_chpass_data_free(chpass_data);
   6.464 +		return;
   6.465 +	}
   6.466 +	
   6.467 +	fields = purple_request_fields_new();
   6.468 +	main_group = purple_request_field_group_new(NULL);
   6.469 +	purple_request_fields_add_group(fields, main_group);
   6.470 +	
   6.471 +	field = purple_request_field_string_new("email",
   6.472 +		_("New email address"), chpass_data->email, FALSE);
   6.473 +	purple_request_field_set_required(field, TRUE);
   6.474 +	purple_request_field_set_validator(field,
   6.475 +		purple_request_field_email_validator, NULL);
   6.476 +	purple_request_field_group_add_field(main_group, field);
   6.477 +
   6.478 +	password_group = purple_request_field_group_new(_("Password"));
   6.479 +	purple_request_fields_add_group(fields, password_group);
   6.480 +
   6.481 +	field = purple_request_field_string_new("password_current",
   6.482 +		_("Current password"), chpass_data->password_current, FALSE);
   6.483 +	purple_request_field_set_required(field, TRUE);
   6.484 +	purple_request_field_string_set_masked(field, TRUE);
   6.485 +	purple_request_field_group_add_field(password_group, field);
   6.486 +	
   6.487 +	field = purple_request_field_string_new("password_new1",
   6.488 +		_("Password"), chpass_data->password_new, FALSE);
   6.489 +	purple_request_field_set_required(field, TRUE);
   6.490 +	purple_request_field_string_set_masked(field, TRUE);
   6.491 +	purple_request_field_set_validator(field, ggp_validator_password, NULL);
   6.492 +	purple_request_field_group_add_field(password_group, field);
   6.493 +	field_password = field;
   6.494 +	
   6.495 +	field = purple_request_field_string_new("password_new2",
   6.496 +		_("Password (retype)"), chpass_data->password_new, FALSE);
   6.497 +	purple_request_field_set_required(field, TRUE);
   6.498 +	purple_request_field_string_set_masked(field, TRUE);
   6.499 +	purple_request_field_set_validator(field, ggp_validator_password_equal,
   6.500 +		field_password);
   6.501 +	purple_request_field_group_add_field(password_group, field);
   6.502 +	
   6.503 +	token_group = purple_request_field_group_new(_("Captcha"));
   6.504 +	purple_request_fields_add_group(fields, token_group);
   6.505 +	
   6.506 +	field = purple_request_field_string_new("token_value",
   6.507 +		_("Enter text from image below"), chpass_data->token_value,
   6.508 +		FALSE);
   6.509 +	purple_request_field_set_required(field, TRUE);
   6.510 +	purple_request_field_set_validator(field, ggp_validator_token, token);
   6.511 +	purple_request_field_group_add_field(token_group, field);
   6.512 +	
   6.513 +	field = purple_request_field_image_new("token_image", _("Captcha"),
   6.514 +		token->data, token->size);
   6.515 +	purple_request_field_group_add_field(token_group, field);
   6.516 +	
   6.517 +	chpass_data->token = token;
   6.518 +	
   6.519 +	primary = g_strdup_printf(_("Change password for %s"),
   6.520 +		purple_account_get_username(account));
   6.521 +	
   6.522 +	purple_request_fields(gc, GGP_ACCOUNT_CHPASS_TITLE, primary,
   6.523 +		_("Please enter your current password and your new password."),
   6.524 +		fields,
   6.525 +		_("OK"), G_CALLBACK(ggp_account_chpass_dialog_ok),
   6.526 +		_("Cancel"), G_CALLBACK(ggp_account_chpass_dialog_cancel),
   6.527 +		account, NULL, NULL, chpass_data);
   6.528 +	
   6.529 +	g_free(primary);
   6.530 +}
   6.531 +
   6.532 +static void ggp_account_chpass_dialog_ok(
   6.533 +	ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields)
   6.534 +{
   6.535 +	PurpleAccount *account = purple_connection_get_account(chpass_data->gc);
   6.536 +	struct gg_http *h;
   6.537 +	uin_t uin;
   6.538 +
   6.539 +	purple_debug_misc("gg", "ggp_account_chpass_dialog_ok(%p, %p)\n",
   6.540 +		chpass_data, fields);
   6.541 +
   6.542 +	g_free(chpass_data->email);
   6.543 +	g_free(chpass_data->password_current);
   6.544 +	g_free(chpass_data->password_new);
   6.545 +	g_free(chpass_data->token_value);
   6.546 +	
   6.547 +	chpass_data->email = g_strdup(
   6.548 +		purple_request_fields_get_string(fields, "email"));
   6.549 +	chpass_data->password_current = g_strdup(
   6.550 +		purple_request_fields_get_string(fields, "password_current"));
   6.551 +	chpass_data->password_new = g_strdup(
   6.552 +		purple_request_fields_get_string(fields, "password_new1"));
   6.553 +	chpass_data->token_value = g_strdup(
   6.554 +		purple_request_fields_get_string(fields, "token_value"));
   6.555 +
   6.556 +	g_assert(chpass_data->email != NULL);
   6.557 +	g_assert(chpass_data->password_current != NULL);
   6.558 +	g_assert(chpass_data->password_new != NULL);
   6.559 +	g_assert(chpass_data->token_value != NULL);
   6.560 +
   6.561 +	if (g_utf8_collate(chpass_data->password_current,
   6.562 +		purple_account_get_password(account)) != 0)
   6.563 +	{
   6.564 +		g_free(chpass_data->password_current);
   6.565 +		chpass_data->password_current = NULL;
   6.566 +		ggp_account_chpass_dialog_invalid(chpass_data,
   6.567 +			_("Your current password is different from the one that"
   6.568 +			" you specified."));
   6.569 +		return;
   6.570 +	}
   6.571 +	if (g_utf8_collate(chpass_data->password_current,
   6.572 +		chpass_data->password_new) == 0)
   6.573 +	{
   6.574 +		g_free(chpass_data->password_new);
   6.575 +		chpass_data->password_new = NULL;
   6.576 +		ggp_account_chpass_dialog_invalid(chpass_data,
   6.577 +			_("New password have to be different from the current "
   6.578 +			"one."));
   6.579 +		return;
   6.580 +	}
   6.581 +
   6.582 +	uin = ggp_str_to_uin(purple_account_get_username(account));
   6.583 +	purple_debug_info("gg", "ggp_account_chpass_dialog_ok: validation ok "
   6.584 +		"[token id=%s, value=%s]\n",
   6.585 +		chpass_data->token->id, chpass_data->token_value);
   6.586 +	h = gg_change_passwd4(uin, chpass_data->email,
   6.587 +		chpass_data->password_current, chpass_data->password_new,
   6.588 +		chpass_data->token->id, chpass_data->token_value, TRUE);
   6.589 +	
   6.590 +	ggp_libgaduw_http_watch(chpass_data->gc, h,
   6.591 +		ggp_account_chpass_response, chpass_data, TRUE);
   6.592 +}
   6.593 +
   6.594 +static void ggp_account_chpass_dialog_invalid(
   6.595 +	ggp_account_chpass_data *chpass_data, const gchar *message)
   6.596 +{
   6.597 +	purple_debug_warning("gg", "ggp_account_chpass_dialog_invalid: %s\n",
   6.598 +		message);
   6.599 +	ggp_account_chpass_dialog(chpass_data->gc, chpass_data->token,
   6.600 +		chpass_data);
   6.601 +	purple_notify_error(purple_connection_get_account(chpass_data->gc),
   6.602 +		GGP_ACCOUNT_CHPASS_TITLE, message, NULL);
   6.603 +}
   6.604 +
   6.605 +static void ggp_account_chpass_dialog_cancel(
   6.606 +	ggp_account_chpass_data *chpass_data, PurpleRequestFields *fields)
   6.607 +{
   6.608 +	ggp_account_chpass_data_free(chpass_data);
   6.609 +}
   6.610 +
   6.611 +static void ggp_account_chpass_response(struct gg_http *h, gboolean success,
   6.612 +	gboolean cancelled, gpointer _chpass_data)
   6.613 +{
   6.614 +	ggp_account_chpass_data *chpass_data = _chpass_data;
   6.615 +	PurpleAccount *account =
   6.616 +		purple_connection_get_account(chpass_data->gc);
   6.617 +	struct gg_pubdir *chpass_result = h->data;
   6.618 +	
   6.619 +	g_assert(!(success && cancelled));
   6.620 +	
   6.621 +	if (cancelled)
   6.622 +	{
   6.623 +		purple_debug_info("gg", "ggp_account_chpass_response: "
   6.624 +			"cancelled\n");
   6.625 +		ggp_account_chpass_data_free(chpass_data);
   6.626 +		return;
   6.627 +	}
   6.628 +	if (!success || !chpass_result->success)
   6.629 +	{
   6.630 +		//TODO (libgadu 1.12.x): check chpass_result->error
   6.631 +		purple_debug_error("gg", "ggp_account_chpass_response: "
   6.632 +			"error\n");
   6.633 +		purple_notify_error(NULL,
   6.634 +			GGP_ACCOUNT_CHPASS_TITLE,
   6.635 +			_("Unable to change password. "
   6.636 +			"An unknown error occurred."), NULL);
   6.637 +		ggp_account_chpass_data_free(chpass_data);
   6.638 +		return;
   6.639 +	}
   6.640 +
   6.641 +	purple_debug_info("gg", "ggp_account_chpass_response: "
   6.642 +		"password changed\n");
   6.643 +	
   6.644 +	purple_account_set_password(account, chpass_data->password_new);
   6.645 +
   6.646 +	purple_notify_info(account, GGP_ACCOUNT_CHPASS_TITLE,
   6.647 +		_("Your password has been changed."), NULL);
   6.648 +	
   6.649 +	ggp_account_chpass_data_free(chpass_data);
   6.650 +	
   6.651 +	//TODO: reconnect / check how it is done in original client
   6.652 +	purple_account_disconnect(account);
   6.653 +}
     7.1 new file mode 100644
     7.2 --- /dev/null
     7.3 +++ b/libpurple/protocols/gg/account.h
     7.4 @@ -0,0 +1,60 @@
     7.5 +/* purple
     7.6 + *
     7.7 + * Purple is the legal property of its developers, whose names are too numerous
     7.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
     7.9 + * source distribution.
    7.10 + *
    7.11 + * Rewritten from scratch during Google Summer of Code 2012
    7.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
    7.13 + *
    7.14 + * Previously implemented by:
    7.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
    7.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
    7.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
    7.18 + *
    7.19 + * This program is free software; you can redistribute it and/or modify
    7.20 + * it under the terms of the GNU General Public License as published by
    7.21 + * the Free Software Foundation; either version 2 of the License, or
    7.22 + * (at your option) any later version.
    7.23 + *
    7.24 + * This program is distributed in the hope that it will be useful,
    7.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.27 + * GNU General Public License for more details.
    7.28 + *
    7.29 + * You should have received a copy of the GNU General Public License
    7.30 + * along with this program; if not, write to the Free Software
    7.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
    7.32 + */
    7.33 +
    7.34 +#ifndef _GGP_ACCOUNT_H
    7.35 +#define _GGP_ACCOUNT_H
    7.36 +
    7.37 +#include <internal.h>
    7.38 +
    7.39 +typedef struct
    7.40 +{
    7.41 +	gchar *id;
    7.42 +	gpointer data;
    7.43 +	size_t size;
    7.44 +	int length;
    7.45 +} ggp_account_token;
    7.46 +
    7.47 +/**
    7.48 + * token must be free'd with ggp_account_token_free
    7.49 + */
    7.50 +typedef void (*ggp_account_token_cb)(PurpleConnection *gc,
    7.51 +	ggp_account_token *token, gpointer user_data);
    7.52 +
    7.53 +void ggp_account_token_request(PurpleConnection *gc,
    7.54 +	ggp_account_token_cb callback, void *user_data);
    7.55 +gboolean ggp_account_token_validate(ggp_account_token *token,
    7.56 +	const gchar *value);
    7.57 +void ggp_account_token_free(ggp_account_token *token);
    7.58 +
    7.59 +
    7.60 +void ggp_account_register(PurpleAccount *account);
    7.61 +
    7.62 +void ggp_account_chpass(PurpleConnection *gc);
    7.63 +
    7.64 +#endif /* _GGP_ACCOUNT_H */
     8.1 new file mode 100644
     8.2 --- /dev/null
     8.3 +++ b/libpurple/protocols/gg/avatar.c
     8.4 @@ -0,0 +1,392 @@
     8.5 +/* purple
     8.6 + *
     8.7 + * Purple is the legal property of its developers, whose names are too numerous
     8.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
     8.9 + * source distribution.
    8.10 + *
    8.11 + * Rewritten from scratch during Google Summer of Code 2012
    8.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
    8.13 + *
    8.14 + * Previously implemented by:
    8.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
    8.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
    8.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
    8.18 + *
    8.19 + * This program is free software; you can redistribute it and/or modify
    8.20 + * it under the terms of the GNU General Public License as published by
    8.21 + * the Free Software Foundation; either version 2 of the License, or
    8.22 + * (at your option) any later version.
    8.23 + *
    8.24 + * This program is distributed in the hope that it will be useful,
    8.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.27 + * GNU General Public License for more details.
    8.28 + *
    8.29 + * You should have received a copy of the GNU General Public License
    8.30 + * along with this program; if not, write to the Free Software
    8.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
    8.32 + */
    8.33 +
    8.34 +#include "avatar.h"
    8.35 +
    8.36 +#include <debug.h>
    8.37 +
    8.38 +#include "gg.h"
    8.39 +#include "utils.h"
    8.40 +#include "oauth/oauth-purple.h"
    8.41 +
    8.42 +// Common
    8.43 +
    8.44 +static inline ggp_avatar_session_data *
    8.45 +ggp_avatar_get_avdata(PurpleConnection *gc);
    8.46 +
    8.47 +static gboolean ggp_avatar_timer_cb(gpointer _gc);
    8.48 +
    8.49 +#define GGP_AVATAR_USERAGENT "GG Client build 11.0.0.7562"
    8.50 +#define GGP_AVATAR_SIZE_MAX 1048576
    8.51 +
    8.52 +// Buddy avatars updating
    8.53 +
    8.54 +typedef struct
    8.55 +{
    8.56 +	uin_t uin;
    8.57 +	time_t timestamp;
    8.58 +	
    8.59 +	PurpleConnection *gc;
    8.60 +	PurpleUtilFetchUrlData *request;
    8.61 +} ggp_avatar_buddy_update_req;
    8.62 +
    8.63 +static gboolean ggp_avatar_buddy_update_next(PurpleConnection *gc);
    8.64 +static void ggp_avatar_buddy_update_received(PurpleUtilFetchUrlData *url_data,
    8.65 +	gpointer _pending_update, const gchar *url_text, gsize len,
    8.66 +	const gchar *error_message);
    8.67 +
    8.68 +#define GGP_AVATAR_BUDDY_URL "http://avatars.gg.pl/%u/s,big"
    8.69 +
    8.70 +// Own avatar setting
    8.71 +
    8.72 +typedef struct
    8.73 +{
    8.74 +	PurpleStoredImage *img;
    8.75 +} ggp_avatar_own_data;
    8.76 +
    8.77 +static void ggp_avatar_own_got_token(PurpleConnection *gc, const gchar *token,
    8.78 +	gpointer img);
    8.79 +static void ggp_avatar_own_sent(PurpleUtilFetchUrlData *url_data,
    8.80 +	gpointer user_data, const gchar *url_text, gsize len,
    8.81 +	const gchar *error_message);
    8.82 +
    8.83 +#define GGP_AVATAR_RESPONSE_MAX 10240
    8.84 +
    8.85 +/*******************************************************************************
    8.86 + * Common.
    8.87 + ******************************************************************************/
    8.88 +
    8.89 +void ggp_avatar_setup(PurpleConnection *gc)
    8.90 +{
    8.91 +	ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
    8.92 +
    8.93 +	avdata->pending_updates = NULL;
    8.94 +	avdata->current_update = NULL;
    8.95 +	avdata->own_data = g_new0(ggp_avatar_own_data, 1);
    8.96 +	
    8.97 +	avdata->timer = purple_timeout_add_seconds(1, ggp_avatar_timer_cb, gc);
    8.98 +}
    8.99 +
   8.100 +void ggp_avatar_cleanup(PurpleConnection *gc)
   8.101 +{
   8.102 +	ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
   8.103 +
   8.104 +	purple_timeout_remove(avdata->timer);
   8.105 +
   8.106 +	if (avdata->current_update != NULL)
   8.107 +	{
   8.108 +		ggp_avatar_buddy_update_req *current_update =
   8.109 +			avdata->current_update;
   8.110 +		
   8.111 +		purple_util_fetch_url_cancel(current_update->request);
   8.112 +		g_free(current_update);
   8.113 +	}
   8.114 +	avdata->current_update = NULL;
   8.115 +	
   8.116 +	g_free(avdata->own_data);
   8.117 +
   8.118 +	g_list_free_full(avdata->pending_updates, &g_free);
   8.119 +	avdata->pending_updates = NULL;
   8.120 +}
   8.121 +
   8.122 +static inline ggp_avatar_session_data *
   8.123 +ggp_avatar_get_avdata(PurpleConnection *gc)
   8.124 +{
   8.125 +	GGPInfo *accdata = purple_connection_get_protocol_data(gc);
   8.126 +	return &accdata->avatar_data;
   8.127 +}
   8.128 +
   8.129 +static gboolean ggp_avatar_timer_cb(gpointer _gc)
   8.130 +{
   8.131 +	PurpleConnection *gc = _gc;
   8.132 +	ggp_avatar_session_data *avdata;
   8.133 +	
   8.134 +	g_return_val_if_fail(PURPLE_CONNECTION_IS_VALID(gc), FALSE);
   8.135 +	
   8.136 +	avdata = ggp_avatar_get_avdata(gc);
   8.137 +	if (avdata->current_update != NULL)
   8.138 +	{
   8.139 +		//TODO: verbose mode
   8.140 +		//purple_debug_misc("gg", "ggp_avatar_timer_cb(%p): there is "
   8.141 +		//	"already an update running\n", gc);
   8.142 +		return TRUE;
   8.143 +	}
   8.144 +	
   8.145 +	while (!ggp_avatar_buddy_update_next(gc));
   8.146 +	
   8.147 +	return TRUE;
   8.148 +}
   8.149 +
   8.150 +/*******************************************************************************
   8.151 + * Buddy avatars updating.
   8.152 + ******************************************************************************/
   8.153 +
   8.154 +void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp)
   8.155 +{
   8.156 +	ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
   8.157 +	ggp_avatar_buddy_update_req *pending_update =
   8.158 +		g_new(ggp_avatar_buddy_update_req, 1);
   8.159 +
   8.160 +	purple_debug_misc("gg", "ggp_avatar_buddy_update(%p, %u, %lu)\n", gc,
   8.161 +		uin, timestamp);
   8.162 +
   8.163 +	pending_update->uin = uin;
   8.164 +	pending_update->timestamp = timestamp;
   8.165 +
   8.166 +	avdata->pending_updates = g_list_append(avdata->pending_updates,
   8.167 +		pending_update);
   8.168 +}
   8.169 +
   8.170 +void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin)
   8.171 +{
   8.172 +	purple_debug_info("gg", "ggp_avatar_buddy_remove(%p, %u)\n", gc, uin);
   8.173 +
   8.174 +	purple_buddy_icons_set_for_user(purple_connection_get_account(gc),
   8.175 +		ggp_uin_to_str(uin), NULL, 0, NULL);
   8.176 +}
   8.177 +
   8.178 +/* return TRUE if avatar update was performed or there is no new requests,
   8.179 +   FALSE if we can request another one immediately */
   8.180 +static gboolean ggp_avatar_buddy_update_next(PurpleConnection *gc)
   8.181 +{
   8.182 +	ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc);
   8.183 +	GList *pending_update_it;
   8.184 +	ggp_avatar_buddy_update_req *pending_update;
   8.185 +	PurpleBuddy *buddy;
   8.186 +	PurpleAccount *account = purple_connection_get_account(gc);
   8.187 +	time_t old_timestamp;
   8.188 +	const char *old_timestamp_str;
   8.189 +	gchar *avatar_url;
   8.190 +	
   8.191 +	pending_update_it = g_list_first(avdata->pending_updates);
   8.192 +	if (pending_update_it == NULL)
   8.193 +		return TRUE;
   8.194 +	
   8.195 +	pending_update = pending_update_it->data;
   8.196 +	avdata->pending_updates = g_list_remove(avdata->pending_updates,
   8.197 +		pending_update);
   8.198 +	buddy = purple_find_buddy(account, ggp_uin_to_str(pending_update->uin));
   8.199 +	
   8.200 +	if (!buddy)
   8.201 +	{
   8.202 +		if (ggp_str_to_uin(purple_account_get_username(account)) ==
   8.203 +			pending_update->uin)
   8.204 +		{
   8.205 +			purple_debug_misc("gg",
   8.206 +				"ggp_avatar_buddy_update_next(%p): own "
   8.207 +				"avatar update requested, but we don't have "
   8.208 +				"ourselves on buddy list\n", gc);
   8.209 +		}
   8.210 +		else
   8.211 +		{
   8.212 +			purple_debug_warning("gg",
   8.213 +				"ggp_avatar_buddy_update_next(%p): "
   8.214 +				"%u update requested, but he's not on buddy "
   8.215 +				"list\n", gc, pending_update->uin);
   8.216 +		}
   8.217 +		return FALSE;
   8.218 +	}
   8.219 +
   8.220 +	old_timestamp_str = purple_buddy_icons_get_checksum_for_user(buddy);
   8.221 +	old_timestamp = old_timestamp_str ? g_ascii_strtoull(
   8.222 +		old_timestamp_str, NULL, 10) : 0;
   8.223 +	if (old_timestamp == pending_update->timestamp)
   8.224 +	{
   8.225 +		purple_debug_misc("gg",
   8.226 +			"ggp_avatar_buddy_update_next(%p): "
   8.227 +			"%u have up to date avatar with ts=%lu\n", gc,
   8.228 +			pending_update->uin, pending_update->timestamp);
   8.229 +		return FALSE;
   8.230 +	}
   8.231 +	if (old_timestamp > pending_update->timestamp)
   8.232 +	{
   8.233 +		purple_debug_warning("gg",
   8.234 +			"ggp_avatar_buddy_update_next(%p): "
   8.235 +			"saved timestamp for %u is newer than received "
   8.236 +			"(%lu > %lu)\n", gc, pending_update->uin, old_timestamp,
   8.237 +			pending_update->timestamp);
   8.238 +	}
   8.239 +	
   8.240 +	purple_debug_info("gg",
   8.241 +		"ggp_avatar_buddy_update_next(%p): "
   8.242 +		"updating %u with ts=%lu...\n", gc, pending_update->uin,
   8.243 +		pending_update->timestamp);
   8.244 +
   8.245 +	pending_update->gc = gc;
   8.246 +	avdata->current_update = pending_update;
   8.247 +	avatar_url = g_strdup_printf(GGP_AVATAR_BUDDY_URL, pending_update->uin);
   8.248 +	pending_update->request = purple_util_fetch_url_request(account,
   8.249 +		avatar_url, FALSE, GGP_AVATAR_USERAGENT, TRUE, NULL, FALSE,
   8.250 +		GGP_AVATAR_SIZE_MAX, ggp_avatar_buddy_update_received,
   8.251 +		pending_update);
   8.252 +	g_free(avatar_url);
   8.253 +	
   8.254 +	return TRUE;
   8.255 +}
   8.256 +
   8.257 +static void ggp_avatar_buddy_update_received(PurpleUtilFetchUrlData *url_data,
   8.258 +	gpointer _pending_update, const gchar *url_text, gsize len,
   8.259 +	const gchar *error_message)
   8.260 +{
   8.261 +	ggp_avatar_buddy_update_req *pending_update = _pending_update;
   8.262 +	PurpleBuddy *buddy;
   8.263 +	PurpleAccount *account;
   8.264 +	PurpleConnection *gc = pending_update->gc;
   8.265 +	ggp_avatar_session_data *avdata;
   8.266 +	gchar timestamp_str[20];
   8.267 +
   8.268 +	if (!PURPLE_CONNECTION_IS_VALID(gc))
   8.269 +	{
   8.270 +		g_free(pending_update);
   8.271 +		return;
   8.272 +	}
   8.273 +
   8.274 +	avdata = ggp_avatar_get_avdata(gc);
   8.275 +	g_assert(pending_update == avdata->current_update);
   8.276 +	avdata->current_update = NULL;
   8.277 +
   8.278 +	if (len == 0)
   8.279 +	{
   8.280 +		purple_debug_error("gg", "ggp_avatar_buddy_update_received: bad"
   8.281 +			" response while getting avatar for %u: %s\n",
   8.282 +			pending_update->uin, error_message);
   8.283 +		g_free(pending_update);
   8.284 +		return;
   8.285 +	}
   8.286 +
   8.287 +	account = purple_connection_get_account(gc);
   8.288 +	buddy = purple_find_buddy(account, ggp_uin_to_str(pending_update->uin));
   8.289 +
   8.290 +	if (!buddy)
   8.291 +	{
   8.292 +		purple_debug_warning("gg", "ggp_avatar_buddy_update_received: "
   8.293 +			"buddy %u disappeared\n", pending_update->uin);
   8.294 +		g_free(pending_update);
   8.295 +		return;
   8.296 +	}
   8.297 +
   8.298 +	g_snprintf(timestamp_str, sizeof(timestamp_str), "%lu",
   8.299 +		pending_update->timestamp);
   8.300 +	purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy),
   8.301 +		g_memdup(url_text, len), len, timestamp_str);
   8.302 +
   8.303 +	purple_debug_info("gg", "ggp_avatar_buddy_update_received: "
   8.304 +		"got avatar for buddy %u [ts=%lu]\n", pending_update->uin,
   8.305 +		pending_update->timestamp);
   8.306 +	g_free(pending_update);
   8.307 +}
   8.308 +
   8.309 +/*******************************************************************************
   8.310 + * Own avatar setting.
   8.311 + ******************************************************************************/
   8.312 +
   8.313 +/**
   8.314 + * TODO: use new, GG11 method, when IMToken will be provided by libgadu.
   8.315 + *
   8.316 + * POST https://avatars.mpa.gg.pl/avatars/user,<uin>/0
   8.317 + * Authorization: IMToken 0123456789abcdef0123456789abcdef01234567
   8.318 + * photo=<avatar content>
   8.319 + */
   8.320 +
   8.321 +void ggp_avatar_own_set(PurpleConnection *gc, PurpleStoredImage *img)
   8.322 +{
   8.323 +	ggp_avatar_own_data *own_data = ggp_avatar_get_avdata(gc)->own_data;
   8.324 +	
   8.325 +	purple_debug_info("gg", "ggp_avatar_own_set(%p, %p)", gc, img);
   8.326 +	
   8.327 +	if (img == NULL)
   8.328 +	{
   8.329 +		purple_debug_warning("gg", "ggp_avatar_own_set: avatar removing"
   8.330 +			" is probably not possible within old protocol");
   8.331 +		return;
   8.332 +	}
   8.333 +	
   8.334 +	own_data->img = img;
   8.335 +	
   8.336 +	ggp_oauth_request(gc, ggp_avatar_own_got_token, img, NULL, NULL);
   8.337 +}
   8.338 +
   8.339 +static void ggp_avatar_own_got_token(PurpleConnection *gc, const gchar *token,
   8.340 +	gpointer img)
   8.341 +{
   8.342 +	ggp_avatar_own_data *own_data = ggp_avatar_get_avdata(gc)->own_data;
   8.343 +	gchar *img_data, *img_data_e, *request, *request_data;
   8.344 +	PurpleAccount *account = purple_connection_get_account(gc);
   8.345 +	uin_t uin = ggp_str_to_uin(purple_account_get_username(account));
   8.346 +	
   8.347 +	if (img != own_data->img)
   8.348 +	{
   8.349 +		purple_debug_warning("gg", "ggp_avatar_own_got_token: "
   8.350 +			"avatar was changed in meantime\n");
   8.351 +		return;
   8.352 +	}
   8.353 +	own_data->img = NULL;
   8.354 +	
   8.355 +	img_data = purple_base64_encode(purple_imgstore_get_data(img),
   8.356 +		purple_imgstore_get_size(img));
   8.357 +	img_data_e = g_uri_escape_string(img_data, NULL, FALSE);
   8.358 +	g_free(img_data);
   8.359 +	request_data = g_strdup_printf("uin=%d&photo=%s", uin, img_data_e);
   8.360 +	g_free(img_data_e);
   8.361 +	
   8.362 +	request = g_strdup_printf(
   8.363 +		"POST /upload HTTP/1.1\r\n"
   8.364 +		"Host: avatars.nowe.gg\r\n"
   8.365 +		"Authorization: %s\r\n"
   8.366 +		"From: avatars to avatars\r\n"
   8.367 +		"Content-Length: %u\r\n"
   8.368 +		"Content-Type: application/x-www-form-urlencoded\r\n"
   8.369 +		"\r\n%s",
   8.370 +		token, strlen(request_data), request_data);
   8.371 +	g_free(request_data);
   8.372 +	
   8.373 +	purple_debug_misc("gg", "ggp_avatar_own_got_token: "
   8.374 +		"uploading new avatar...\n");
   8.375 +	purple_util_fetch_url_request(account, "http://avatars.nowe.gg/upload",
   8.376 +		FALSE, NULL, TRUE, request, FALSE, GGP_AVATAR_RESPONSE_MAX,
   8.377 +		ggp_avatar_own_sent, gc);
   8.378 +	
   8.379 +	g_free(request);
   8.380 +}
   8.381 +
   8.382 +static void ggp_avatar_own_sent(PurpleUtilFetchUrlData *url_data,
   8.383 +	gpointer user_data, const gchar *url_text, gsize len,
   8.384 +	const gchar *error_message)
   8.385 +{
   8.386 +	PurpleConnection *gc = user_data;
   8.387 +	
   8.388 +	if (!PURPLE_CONNECTION_IS_VALID(gc))
   8.389 +		return;
   8.390 +	
   8.391 +	if (len == 0)
   8.392 +		purple_debug_error("gg", "ggp_avatar_own_sent: "
   8.393 +			"avatar not sent. %s\n", error_message);
   8.394 +	else
   8.395 +		purple_debug_info("gg", "ggp_avatar_own_sent: %s\n", url_text);
   8.396 +}
     9.1 new file mode 100644
     9.2 --- /dev/null
     9.3 +++ b/libpurple/protocols/gg/avatar.h
     9.4 @@ -0,0 +1,53 @@
     9.5 +/* purple
     9.6 + *
     9.7 + * Purple is the legal property of its developers, whose names are too numerous
     9.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
     9.9 + * source distribution.
    9.10 + *
    9.11 + * Rewritten from scratch during Google Summer of Code 2012
    9.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
    9.13 + *
    9.14 + * Previously implemented by:
    9.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
    9.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
    9.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
    9.18 + *
    9.19 + * This program is free software; you can redistribute it and/or modify
    9.20 + * it under the terms of the GNU General Public License as published by
    9.21 + * the Free Software Foundation; either version 2 of the License, or
    9.22 + * (at your option) any later version.
    9.23 + *
    9.24 + * This program is distributed in the hope that it will be useful,
    9.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    9.27 + * GNU General Public License for more details.
    9.28 + *
    9.29 + * You should have received a copy of the GNU General Public License
    9.30 + * along with this program; if not, write to the Free Software
    9.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
    9.32 + */
    9.33 +
    9.34 +#ifndef _GGP_AVATAR_H
    9.35 +#define _GGP_AVATAR_H
    9.36 +
    9.37 +#include <internal.h>
    9.38 +#include <libgadu.h>
    9.39 +
    9.40 +typedef struct
    9.41 +{
    9.42 +	guint timer;
    9.43 +	GList *pending_updates;
    9.44 +	
    9.45 +	gpointer current_update;
    9.46 +	gpointer own_data;
    9.47 +} ggp_avatar_session_data;
    9.48 +
    9.49 +void ggp_avatar_setup(PurpleConnection *gc);
    9.50 +void ggp_avatar_cleanup(PurpleConnection *gc);
    9.51 +
    9.52 +void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp);
    9.53 +void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin);
    9.54 +
    9.55 +void ggp_avatar_own_set(PurpleConnection *gc, PurpleStoredImage *img);
    9.56 +
    9.57 +#endif /* _GGP_AVATAR_H */
    10.1 --- a/libpurple/protocols/gg/buddylist.c
    10.2 +++ b/libpurple/protocols/gg/buddylist.c
    10.3 @@ -22,9 +22,10 @@
    10.4  
    10.5  
    10.6  #include <libgadu.h>
    10.7 +#include <debug.h>
    10.8  
    10.9  #include "gg.h"
   10.10 -#include "gg-utils.h"
   10.11 +#include "utils.h"
   10.12  #include "buddylist.h"
   10.13  
   10.14  #define F_FIRSTNAME 0
   10.15 @@ -36,6 +37,7 @@
   10.16  #define F_UIN 6
   10.17  
   10.18  /* void ggp_buddylist_send(PurpleConnection *gc) {{{ */
   10.19 +// this is for for notify purposes, not synchronizing buddy list
   10.20  void ggp_buddylist_send(PurpleConnection *gc)
   10.21  {
   10.22  	GGPInfo *info = purple_connection_get_protocol_data(gc);
   10.23 @@ -81,7 +83,7 @@
   10.24  	PurpleGroup *group;
   10.25  	gchar **users_tbl;
   10.26  	int i;
   10.27 -	char *utf8buddylist = charset_convert(buddylist, "CP1250", "UTF-8");
   10.28 +	char *utf8buddylist = ggp_convert_from_cp1250(buddylist);
   10.29  
   10.30  	/* Don't limit the number of records in a buddylist. */
   10.31  	users_tbl = g_strsplit(utf8buddylist, "\r\n", -1);
   10.32 @@ -94,7 +96,7 @@
   10.33  			continue;
   10.34  
   10.35  		data_tbl = g_strsplit(users_tbl[i], ";", 8);
   10.36 -		if (ggp_array_size(data_tbl) < 8) {
   10.37 +		if (g_strv_length(data_tbl) < 8) {
   10.38  			purple_debug_warning("gg",
   10.39  				"Something is wrong on line %d of the buddylist. Skipping.\n",
   10.40  				i + 1);
   10.41 @@ -127,7 +129,7 @@
   10.42  			/* XXX: Probably buddy should be added to all the groups. */
   10.43  			/* Hard limit to at most 50 groups */
   10.44  			gchar **group_tbl = g_strsplit(data_tbl[F_GROUP], ",", 50);
   10.45 -			if (ggp_array_size(group_tbl) > 0) {
   10.46 +			if (g_strv_length(group_tbl) > 0) {
   10.47  				g_free(g);
   10.48  				g = g_strdup(group_tbl[0]);
   10.49  			}
   10.50 @@ -178,11 +180,22 @@
   10.51  				"", gname, bname, "", "");
   10.52  	}
   10.53  
   10.54 -	ptr = charset_convert(buddylist->str, "UTF-8", "CP1250");
   10.55 +	ptr = ggp_convert_to_cp1250(buddylist->str);
   10.56  	g_string_free(buddylist, TRUE);
   10.57  	return ptr;
   10.58  }
   10.59  /* }}} */
   10.60  
   10.61 +const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin)
   10.62 +{
   10.63 +	const char *uin_s = ggp_uin_to_str(uin);
   10.64 +	PurpleBuddy *buddy = purple_find_buddy(
   10.65 +		purple_connection_get_account(gc), uin_s);
   10.66 +	
   10.67 +	if (buddy != NULL)
   10.68 +		return purple_buddy_get_alias(buddy);
   10.69 +	else
   10.70 +		return uin_s;
   10.71 +}
   10.72  
   10.73  /* vim: set ts=8 sts=0 sw=8 noet: */
    11.1 --- a/libpurple/protocols/gg/buddylist.h
    11.2 +++ b/libpurple/protocols/gg/buddylist.h
    11.3 @@ -50,6 +50,16 @@
    11.4  char *
    11.5  ggp_buddylist_dump(PurpleAccount *account);
    11.6  
    11.7 +/**
    11.8 + * Returns the best name of a buddy from the buddylist.
    11.9 + *
   11.10 + * @param gc  PurpleConnection instance.
   11.11 + * @param uin UIN of the buddy.
   11.12 + *
   11.13 + * @return Name of the buddy, or UIN converted to string, if there is no such
   11.14 + * user on the list.
   11.15 + */
   11.16 +const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin);
   11.17  
   11.18  #endif /* _PURPLE_GG_BUDDYLIST_H */
   11.19  
    12.1 --- a/libpurple/protocols/gg/confer.c
    12.2 +++ b/libpurple/protocols/gg/confer.c
    12.3 @@ -23,7 +23,7 @@
    12.4  
    12.5  #include <libgadu.h>
    12.6  #include "gg.h"
    12.7 -#include "gg-utils.h"
    12.8 +#include "utils.h"
    12.9  #include "confer.h"
   12.10  
   12.11  /* PurpleConversation *ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name) {{{ */
    13.1 new file mode 100644
    13.2 --- /dev/null
    13.3 +++ b/libpurple/protocols/gg/deprecated.c
    13.4 @@ -0,0 +1,62 @@
    13.5 +/* purple
    13.6 + *
    13.7 + * Purple is the legal property of its developers, whose names are too numerous
    13.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    13.9 + * source distribution.
   13.10 + *
   13.11 + * Rewritten from scratch during Google Summer of Code 2012
   13.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   13.13 + *
   13.14 + * Previously implemented by:
   13.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   13.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   13.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   13.18 + *
   13.19 + * This program is free software; you can redistribute it and/or modify
   13.20 + * it under the terms of the GNU General Public License as published by
   13.21 + * the Free Software Foundation; either version 2 of the License, or
   13.22 + * (at your option) any later version.
   13.23 + *
   13.24 + * This program is distributed in the hope that it will be useful,
   13.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13.27 + * GNU General Public License for more details.
   13.28 + *
   13.29 + * You should have received a copy of the GNU General Public License
   13.30 + * along with this program; if not, write to the Free Software
   13.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   13.32 + */
   13.33 +
   13.34 +#include "deprecated.h"
   13.35 +
   13.36 +#include <libgadu.h>
   13.37 +
   13.38 +gboolean ggp_deprecated_setup_proxy(PurpleConnection *gc)
   13.39 +{
   13.40 +	PurpleProxyInfo *gpi = purple_proxy_get_setup(purple_connection_get_account(gc));
   13.41 +	
   13.42 +	if ((purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) &&
   13.43 +		(purple_proxy_info_get_host(gpi) == NULL ||
   13.44 +		purple_proxy_info_get_port(gpi) <= 0))
   13.45 +	{
   13.46 +		gg_proxy_enabled = 0;
   13.47 +		purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
   13.48 +			_("Either the host name or port number specified for your given proxy type is invalid."));
   13.49 +		return FALSE;
   13.50 +	}
   13.51 +	
   13.52 +	if (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_NONE)
   13.53 +	{
   13.54 +		gg_proxy_enabled = 0;
   13.55 +		return TRUE;
   13.56 +	}
   13.57 +
   13.58 +	gg_proxy_enabled = 1;
   13.59 +	//TODO: memleak
   13.60 +	gg_proxy_host = g_strdup(purple_proxy_info_get_host(gpi));
   13.61 +	gg_proxy_port = purple_proxy_info_get_port(gpi);
   13.62 +	gg_proxy_username = g_strdup(purple_proxy_info_get_username(gpi));
   13.63 +	gg_proxy_password = g_strdup(purple_proxy_info_get_password(gpi));
   13.64 +	
   13.65 +	return TRUE;
   13.66 +}
    14.1 new file mode 100644
    14.2 --- /dev/null
    14.3 +++ b/libpurple/protocols/gg/deprecated.h
    14.4 @@ -0,0 +1,37 @@
    14.5 +/* purple
    14.6 + *
    14.7 + * Purple is the legal property of its developers, whose names are too numerous
    14.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    14.9 + * source distribution.
   14.10 + *
   14.11 + * Rewritten from scratch during Google Summer of Code 2012
   14.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   14.13 + *
   14.14 + * Previously implemented by:
   14.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   14.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   14.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   14.18 + *
   14.19 + * This program is free software; you can redistribute it and/or modify
   14.20 + * it under the terms of the GNU General Public License as published by
   14.21 + * the Free Software Foundation; either version 2 of the License, or
   14.22 + * (at your option) any later version.
   14.23 + *
   14.24 + * This program is distributed in the hope that it will be useful,
   14.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14.27 + * GNU General Public License for more details.
   14.28 + *
   14.29 + * You should have received a copy of the GNU General Public License
   14.30 + * along with this program; if not, write to the Free Software
   14.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   14.32 + */
   14.33 +
   14.34 +#ifndef _GGP_DEPRECATED_H
   14.35 +#define _GGP_DEPRECATED_H
   14.36 +
   14.37 +#include <internal.h>
   14.38 +
   14.39 +gboolean ggp_deprecated_setup_proxy(PurpleConnection *gc);
   14.40 +
   14.41 +#endif /* _GGP_DEPRECATED_H */
    15.1 --- a/libpurple/protocols/gg/gg.c
    15.2 +++ b/libpurple/protocols/gg/gg.c
    15.3 @@ -26,12 +26,11 @@
    15.4   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
    15.5   */
    15.6  
    15.7 -#include "internal.h"
    15.8 +#include <internal.h>
    15.9  
   15.10  #include "plugin.h"
   15.11  #include "version.h"
   15.12  #include "notify.h"
   15.13 -#include "status.h"
   15.14  #include "blist.h"
   15.15  #include "accountopt.h"
   15.16  #include "debug.h"
   15.17 @@ -43,206 +42,43 @@
   15.18  #include "confer.h"
   15.19  #include "search.h"
   15.20  #include "buddylist.h"
   15.21 -#include "gg-utils.h"
   15.22 -
   15.23 -#ifdef _WIN32
   15.24 -#  include "win32-resolver.h"
   15.25 -#endif
   15.26 -
   15.27 -static PurplePlugin *my_protocol = NULL;
   15.28 -
   15.29 -/* Prototypes */
   15.30 -static void ggp_set_status(PurpleAccount *account, PurpleStatus *status);
   15.31 -static int ggp_to_gg_status(PurpleStatus *status, char **msg);
   15.32 -
   15.33 -/* ---------------------------------------------------------------------- */
   15.34 -/* ----- EXTERNAL CALLBACKS --------------------------------------------- */
   15.35 -/* ---------------------------------------------------------------------- */
   15.36 -
   15.37 -
   15.38 -/* ----- HELPERS -------------------------------------------------------- */
   15.39 -
   15.40 -/**
   15.41 - * Set up libgadu's proxy.
   15.42 - *
   15.43 - * @param account Account for which to set up the proxy.
   15.44 - *
   15.45 - * @return Zero if proxy setup is valid, otherwise -1.
   15.46 - */
   15.47 -static int ggp_setup_proxy(PurpleAccount *account)
   15.48 -{
   15.49 -	PurpleProxyInfo *gpi;
   15.50 -
   15.51 -	gpi = purple_proxy_get_setup(account);
   15.52 -
   15.53 -	if ((purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) &&
   15.54 -	    (purple_proxy_info_get_host(gpi) == NULL ||
   15.55 -	     purple_proxy_info_get_port(gpi) <= 0)) {
   15.56 -
   15.57 -		gg_proxy_enabled = 0;
   15.58 -		purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
   15.59 -				  _("Either the host name or port number specified for your given proxy type is invalid."));
   15.60 -		return -1;
   15.61 -	} else if (purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) {
   15.62 -		gg_proxy_enabled = 1;
   15.63 -		gg_proxy_host = g_strdup(purple_proxy_info_get_host(gpi));
   15.64 -		gg_proxy_port = purple_proxy_info_get_port(gpi);
   15.65 -		gg_proxy_username = g_strdup(purple_proxy_info_get_username(gpi));
   15.66 -		gg_proxy_password = g_strdup(purple_proxy_info_get_password(gpi));
   15.67 -	} else {
   15.68 -		gg_proxy_enabled = 0;
   15.69 -	}
   15.70 -
   15.71 -	return 0;
   15.72 -}
   15.73 -
   15.74 -static void ggp_async_token_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
   15.75 -{
   15.76 -	PurpleConnection *gc = _gc;
   15.77 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
   15.78 -	GGPToken *token = info->token;
   15.79 -	GGPTokenCallback cb;
   15.80 -
   15.81 -	struct gg_token *t = NULL;
   15.82 -
   15.83 -	purple_debug_info("gg", "token_handler: token->req: check = %d; state = %d;\n",
   15.84 -			token->req->check, token->req->state);
   15.85 -
   15.86 -	if (gg_token_watch_fd(token->req) == -1 || token->req->state == GG_STATE_ERROR) {
   15.87 -		purple_debug_error("gg", "token error (1): %d\n", token->req->error);
   15.88 -		purple_input_remove(token->inpa);
   15.89 -		gg_token_free(token->req);
   15.90 -		token->req = NULL;
   15.91 -
   15.92 -		purple_notify_error(purple_connection_get_account(gc),
   15.93 -				  _("Token Error"),
   15.94 -				  _("Unable to fetch the token.\n"), NULL);
   15.95 -		return;
   15.96 -	}
   15.97 -
   15.98 -	if (token->req->state != GG_STATE_DONE) {
   15.99 -		purple_input_remove(token->inpa);
  15.100 -		token->inpa = purple_input_add(token->req->fd,
  15.101 -						   (token->req->check == 1)
  15.102 -						   	? PURPLE_INPUT_WRITE
  15.103 -							: PURPLE_INPUT_READ,
  15.104 -						   ggp_async_token_handler, gc);
  15.105 -		return;
  15.106 -	}
  15.107 -
  15.108 -	if (!(t = token->req->data) || !token->req->body) {
  15.109 -		purple_debug_error("gg", "token error (2): %d\n", token->req->error);
  15.110 -		purple_input_remove(token->inpa);
  15.111 -		gg_token_free(token->req);
  15.112 -		token->req = NULL;
  15.113 -
  15.114 -		purple_notify_error(purple_connection_get_account(gc),
  15.115 -				  _("Token Error"),
  15.116 -				  _("Unable to fetch the token.\n"), NULL);
  15.117 -		return;
  15.118 -	}
  15.119 -
  15.120 -	purple_input_remove(token->inpa);
  15.121 -
  15.122 -	token->id = g_strdup(t->tokenid);
  15.123 -	token->size = token->req->body_size;
  15.124 -	token->data = g_new0(char, token->size);
  15.125 -	memcpy(token->data, token->req->body, token->size);
  15.126 -
  15.127 -	purple_debug_info("gg", "TOKEN! tokenid = %s; size = %d\n",
  15.128 -			token->id, token->size);
  15.129 -
  15.130 -	gg_token_free(token->req);
  15.131 -	token->req = NULL;
  15.132 -	token->inpa = 0;
  15.133 -
  15.134 -	cb = token->cb;
  15.135 -	token->cb = NULL;
  15.136 -	cb(gc);
  15.137 -}
  15.138 -
  15.139 -static void ggp_token_request(PurpleConnection *gc, GGPTokenCallback cb)
  15.140 -{
  15.141 -	PurpleAccount *account;
  15.142 -	struct gg_http *req;
  15.143 -	GGPInfo *info;
  15.144 -
  15.145 -	account = purple_connection_get_account(gc);
  15.146 -
  15.147 -	if (ggp_setup_proxy(account) == -1)
  15.148 -		return;
  15.149 -
  15.150 -	info = purple_connection_get_protocol_data(gc);
  15.151 -
  15.152 -	if ((req = gg_token(1)) == NULL) {
  15.153 -		purple_notify_error(account,
  15.154 -				  _("Token Error"),
  15.155 -				  _("Unable to fetch the token.\n"), NULL);
  15.156 -		return;
  15.157 -	}
  15.158 -
  15.159 -	info->token = g_new(GGPToken, 1);
  15.160 -	info->token->cb = cb;
  15.161 -
  15.162 -	info->token->req = req;
  15.163 -	info->token->inpa = purple_input_add(req->fd, PURPLE_INPUT_READ,
  15.164 -					   ggp_async_token_handler, gc);
  15.165 -}
  15.166 -/* }}} */
  15.167 +#include "utils.h"
  15.168 +#include "resolver-purple.h"
  15.169 +#include "account.h"
  15.170 +#include "deprecated.h"
  15.171 +#include "purplew.h"
  15.172 +#include "libgadu-events.h"
  15.173 +#include "multilogon.h"
  15.174 +#include "status.h"
  15.175 +#include "servconn.h"
  15.176 +#include "pubdir-prpl.h"
  15.177  
  15.178  /* ---------------------------------------------------------------------- */
  15.179  
  15.180 -/**
  15.181 - * Request buddylist from the server.
  15.182 - * Buddylist is received in the ggp_callback_recv().
  15.183 - *
  15.184 - * @param Current action handler.
  15.185 - */
  15.186 -static void ggp_action_buddylist_get(PurplePluginAction *action)
  15.187 +ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy)
  15.188  {
  15.189 -	PurpleConnection *gc = (PurpleConnection *)action->context;
  15.190 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.191 -
  15.192 -	purple_debug_info("gg", "Downloading...\n");
  15.193 -
  15.194 -	gg_userlist_request(info->session, GG_USERLIST_GET, NULL);
  15.195 +	ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(buddy);
  15.196 +	if (buddy_data)
  15.197 +		return buddy_data;
  15.198 +	
  15.199 +	buddy_data = g_new0(ggp_buddy_data, 1);
  15.200 +	purple_buddy_set_protocol_data(buddy, buddy_data);
  15.201 +	return buddy_data;
  15.202  }
  15.203  
  15.204 -/**
  15.205 - * Upload the buddylist to the server.
  15.206 - *
  15.207 - * @param action Current action handler.
  15.208 - */
  15.209 -static void ggp_action_buddylist_put(PurplePluginAction *action)
  15.210 +static void ggp_buddy_free(PurpleBuddy *buddy)
  15.211  {
  15.212 -	PurpleConnection *gc = (PurpleConnection *)action->context;
  15.213 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.214 +	ggp_buddy_data *buddy_data = purple_buddy_get_protocol_data(buddy);
  15.215  
  15.216 -	char *buddylist = ggp_buddylist_dump(purple_connection_get_account(gc));
  15.217 -
  15.218 -	purple_debug_info("gg", "Uploading...\n");
  15.219 -
  15.220 -	if (buddylist == NULL)
  15.221 +	if (!buddy_data)
  15.222  		return;
  15.223  
  15.224 -	gg_userlist_request(info->session, GG_USERLIST_PUT, buddylist);
  15.225 -	g_free(buddylist);
  15.226 +	g_free(buddy_data);
  15.227 +	purple_buddy_set_protocol_data(buddy, NULL);
  15.228  }
  15.229  
  15.230 -/**
  15.231 - * Delete buddylist from the server.
  15.232 - *
  15.233 - * @param action Current action handler.
  15.234 - */
  15.235 -static void ggp_action_buddylist_delete(PurplePluginAction *action)
  15.236 -{
  15.237 -	PurpleConnection *gc = (PurpleConnection *)action->context;
  15.238 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.239 -
  15.240 -	purple_debug_info("gg", "Deleting...\n");
  15.241 -
  15.242 -	gg_userlist_request(info->session, GG_USERLIST_PUT, NULL);
  15.243 -}
  15.244 +/* ---------------------------------------------------------------------- */
  15.245 +// buddy list import/export from/to file
  15.246  
  15.247  static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *filename)
  15.248  {
  15.249 @@ -332,550 +168,6 @@
  15.250  			gc);
  15.251  }
  15.252  
  15.253 -static void ggp_callback_register_account_ok(PurpleConnection *gc,
  15.254 -					     PurpleRequestFields *fields)
  15.255 -{
  15.256 -	PurpleAccount *account;
  15.257 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.258 -	struct gg_http *h = NULL;
  15.259 -	struct gg_pubdir *s;
  15.260 -	uin_t uin;
  15.261 -	gchar *email, *p1, *p2, *t;
  15.262 -	GGPToken *token = info->token;
  15.263 -
  15.264 -	email = charset_convert(purple_request_fields_get_string(fields, "email"),
  15.265 -			     "UTF-8", "CP1250");
  15.266 -	p1  = charset_convert(purple_request_fields_get_string(fields, "password1"),
  15.267 -			     "UTF-8", "CP1250");
  15.268 -	p2  = charset_convert(purple_request_fields_get_string(fields, "password2"),
  15.269 -			     "UTF-8", "CP1250");
  15.270 -	t   = charset_convert(purple_request_fields_get_string(fields, "token"),
  15.271 -			     "UTF-8", "CP1250");
  15.272 -
  15.273 -	account = purple_connection_get_account(gc);
  15.274 -
  15.275 -	if (email == NULL || p1 == NULL || p2 == NULL || t == NULL ||
  15.276 -	    *email == '\0' || *p1 == '\0' || *p2 == '\0' || *t == '\0') {
  15.277 -		purple_connection_error (gc,
  15.278 -			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
  15.279 -			_("You must fill in all registration fields"));
  15.280 -		goto exit_err;
  15.281 -	}
  15.282 -
  15.283 -	if (g_utf8_collate(p1, p2) != 0) {
  15.284 -		purple_connection_error (gc,
  15.285 -			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
  15.286 -			_("Passwords do not match"));
  15.287 -		goto exit_err;
  15.288 -	}
  15.289 -
  15.290 -	purple_debug_info("gg", "register_account_ok: token_id = %s; t = %s\n",
  15.291 -			token->id, t);
  15.292 -	h = gg_register3(email, p1, token->id, t, 0);
  15.293 -	if (h == NULL || !(s = h->data) || !s->success) {
  15.294 -		purple_connection_error (gc,
  15.295 -			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
  15.296 -			_("Unable to register new account.  An unknown error occurred."));
  15.297 -		goto exit_err;
  15.298 -	}
  15.299 -
  15.300 -	uin = s->uin;
  15.301 -	purple_debug_info("gg", "registered uin: %d\n", uin);
  15.302 -
  15.303 -	g_free(t);
  15.304 -	t = g_strdup_printf("%u", uin);
  15.305 -	purple_account_set_username(account, t);
  15.306 -	/* Save the password if remembering passwords for the account */
  15.307 -	purple_account_set_password(account, p1);
  15.308 -
  15.309 -	purple_notify_info(NULL, _("New Gadu-Gadu Account Registered"),
  15.310 -			 _("Registration completed successfully!"), NULL);
  15.311 -
  15.312 -	purple_account_register_completed(account, TRUE);
  15.313 -
  15.314 -	/* TODO: the currently open Accounts Window will not be updated withthe
  15.315 -	 * new username and etc, we need to somehow have it refresh at this
  15.316 -	 * point
  15.317 -	 */
  15.318 -
  15.319 -	/* Need to disconnect or actually log in. For now, we disconnect. */
  15.320 -	purple_account_disconnect(account);
  15.321 -
  15.322 -exit_err:
  15.323 -	purple_account_register_completed(account, FALSE);
  15.324 -
  15.325 -	gg_register_free(h);
  15.326 -	g_free(email);
  15.327 -	g_free(p1);
  15.328 -	g_free(p2);
  15.329 -	g_free(t);
  15.330 -	g_free(token->id);
  15.331 -	g_free(token);
  15.332 -}
  15.333 -
  15.334 -static void ggp_callback_register_account_cancel(PurpleConnection *gc,
  15.335 -						 PurpleRequestFields *fields)
  15.336 -{
  15.337 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.338 -	GGPToken *token = info->token;
  15.339 -
  15.340 -	purple_account_disconnect(purple_connection_get_account(gc));
  15.341 -
  15.342 -	g_free(token->id);
  15.343 -	g_free(token->data);
  15.344 -	g_free(token);
  15.345 -
  15.346 -}
  15.347 -
  15.348 -static void ggp_register_user_dialog(PurpleConnection *gc)
  15.349 -{
  15.350 -	PurpleAccount *account;
  15.351 -	PurpleRequestFields *fields;
  15.352 -	PurpleRequestFieldGroup *group;
  15.353 -	PurpleRequestField *field;
  15.354 -
  15.355 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.356 -	GGPToken *token = info->token;
  15.357 -
  15.358 -
  15.359 -	account = purple_connection_get_account(gc);
  15.360 -
  15.361 -	fields = purple_request_fields_new();
  15.362 -	group = purple_request_field_group_new(NULL);
  15.363 -	purple_request_fields_add_group(fields, group);
  15.364 -
  15.365 -	field = purple_request_field_string_new("email",
  15.366 -			_("Email"), "", FALSE);
  15.367 -	purple_request_field_string_set_masked(field, FALSE);
  15.368 -	purple_request_field_group_add_field(group, field);
  15.369 -
  15.370 -	field = purple_request_field_string_new("password1",
  15.371 -			_("Password"), "", FALSE);
  15.372 -	purple_request_field_string_set_masked(field, TRUE);
  15.373 -	purple_request_field_group_add_field(group, field);
  15.374 -
  15.375 -	field = purple_request_field_string_new("password2",
  15.376 -			_("Password (again)"), "", FALSE);
  15.377 -	purple_request_field_string_set_masked(field, TRUE);
  15.378 -	purple_request_field_group_add_field(group, field);
  15.379 -
  15.380 -	field = purple_request_field_string_new("token",
  15.381 -			_("Enter captcha text"), "", FALSE);
  15.382 -	purple_request_field_string_set_masked(field, FALSE);
  15.383 -	purple_request_field_group_add_field(group, field);
  15.384 -
  15.385 -	/* original size: 60x24 */
  15.386 -	field = purple_request_field_image_new("token_img",
  15.387 -			_("Captcha"), token->data, token->size);
  15.388 -	purple_request_field_group_add_field(group, field);
  15.389 -
  15.390 -	purple_request_fields(account,
  15.391 -		_("Register New Gadu-Gadu Account"),
  15.392 -		_("Register New Gadu-Gadu Account"),
  15.393 -		_("Please, fill in the following fields"),
  15.394 -		fields,
  15.395 -		_("OK"), G_CALLBACK(ggp_callback_register_account_ok),
  15.396 -		_("Cancel"), G_CALLBACK(ggp_callback_register_account_cancel),
  15.397 -		purple_connection_get_account(gc), NULL, NULL,
  15.398 -		gc);
  15.399 -}
  15.400 -
  15.401 -/* ----- PUBLIC DIRECTORY SEARCH ---------------------------------------- */
  15.402 -
  15.403 -static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data)
  15.404 -{
  15.405 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.406 -	GGPSearchForm *form = user_data;
  15.407 -	guint32 seq;
  15.408 -
  15.409 -	form->page_number++;
  15.410 -
  15.411 -	ggp_search_remove(info->searches, form->seq);
  15.412 -	purple_debug_info("gg", "ggp_callback_show_next(): Removed seq %u\n",
  15.413 -		form->seq);
  15.414 -
  15.415 -	seq = ggp_search_start(gc, form);
  15.416 -	ggp_search_add(info->searches, seq, form);
  15.417 -	purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u\n",
  15.418 -		seq);
  15.419 -}
  15.420 -
  15.421 -static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data)
  15.422 -{
  15.423 -	purple_blist_request_add_buddy(purple_connection_get_account(gc),
  15.424 -				     g_list_nth_data(row, 0), NULL, NULL);
  15.425 -}
  15.426 -
  15.427 -static void ggp_callback_im(PurpleConnection *gc, GList *row, gpointer user_data)
  15.428 -{
  15.429 -	PurpleAccount *account;
  15.430 -	PurpleConversation *conv;
  15.431 -	char *name;
  15.432 -
  15.433 -	account = purple_connection_get_account(gc);
  15.434 -
  15.435 -	name = g_list_nth_data(row, 0);
  15.436 -	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
  15.437 -	purple_conversation_present(conv);
  15.438 -}
  15.439 -
  15.440 -static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields)
  15.441 -{
  15.442 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.443 -	GGPSearchForm *form;
  15.444 -	guint32 seq;
  15.445 -
  15.446 -	form = ggp_search_form_new(GGP_SEARCH_TYPE_FULL);
  15.447 -
  15.448 -	form->user_data = info;
  15.449 -	form->lastname = g_strdup(
  15.450 -		purple_request_fields_get_string(fields, "lastname"));
  15.451 -	form->firstname = g_strdup(
  15.452 -		purple_request_fields_get_string(fields, "firstname"));
  15.453 -	form->nickname = g_strdup(
  15.454 -		purple_request_fields_get_string(fields, "nickname"));
  15.455 -	form->city = g_strdup(
  15.456 -		purple_request_fields_get_string(fields, "city"));
  15.457 -	form->birthyear = g_strdup(
  15.458 -		purple_request_fields_get_string(fields, "year"));
  15.459 -
  15.460 -	switch (purple_request_fields_get_choice(fields, "gender")) {
  15.461 -		case 1:
  15.462 -			form->gender = g_strdup(GG_PUBDIR50_GENDER_MALE);
  15.463 -			break;
  15.464 -		case 2:
  15.465 -			form->gender = g_strdup(GG_PUBDIR50_GENDER_FEMALE);
  15.466 -			break;
  15.467 -		default:
  15.468 -			form->gender = NULL;
  15.469 -			break;
  15.470 -	}
  15.471 -
  15.472 -	form->active = purple_request_fields_get_bool(fields, "active")
  15.473 -				   ? g_strdup(GG_PUBDIR50_ACTIVE_TRUE) : NULL;
  15.474 -
  15.475 -	seq = ggp_search_start(gc, form);
  15.476 -	ggp_search_add(info->searches, seq, form);
  15.477 -	purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u\n",
  15.478 -		seq);
  15.479 -}
  15.480 -
  15.481 -static void ggp_find_buddies(PurplePluginAction *action)
  15.482 -{
  15.483 -	PurpleConnection *gc = (PurpleConnection *)action->context;
  15.484 -
  15.485 -	PurpleRequestFields *fields;
  15.486 -	PurpleRequestFieldGroup *group;
  15.487 -	PurpleRequestField *field;
  15.488 -
  15.489 -	fields = purple_request_fields_new();
  15.490 -	group = purple_request_field_group_new(NULL);
  15.491 -	purple_request_fields_add_group(fields, group);
  15.492 -
  15.493 -	field = purple_request_field_string_new("lastname",
  15.494 -			_("Last name"), NULL, FALSE);
  15.495 -	purple_request_field_string_set_masked(field, FALSE);
  15.496 -	purple_request_field_group_add_field(group, field);
  15.497 -
  15.498 -	field = purple_request_field_string_new("firstname",
  15.499 -			_("First name"), NULL, FALSE);
  15.500 -	purple_request_field_string_set_masked(field, FALSE);
  15.501 -	purple_request_field_group_add_field(group, field);
  15.502 -
  15.503 -	field = purple_request_field_string_new("nickname",
  15.504 -			_("Nickname"), NULL, FALSE);
  15.505 -	purple_request_field_string_set_masked(field, FALSE);
  15.506 -	purple_request_field_group_add_field(group, field);
  15.507 -
  15.508 -	field = purple_request_field_string_new("city",
  15.509 -			_("City"), NULL, FALSE);
  15.510 -	purple_request_field_string_set_masked(field, FALSE);
  15.511 -	purple_request_field_group_add_field(group, field);
  15.512 -
  15.513 -	field = purple_request_field_string_new("year",
  15.514 -			_("Year of birth"), NULL, FALSE);
  15.515 -	purple_request_field_group_add_field(group, field);
  15.516 -
  15.517 -	field = purple_request_field_choice_new("gender", _("Gender"), 0);
  15.518 -	purple_request_field_choice_add(field, _("Male or female"));
  15.519 -	purple_request_field_choice_add(field, _("Male"));
  15.520 -	purple_request_field_choice_add(field, _("Female"));
  15.521 -	purple_request_field_group_add_field(group, field);
  15.522 -
  15.523 -	field = purple_request_field_bool_new("active",
  15.524 -			_("Only online"), FALSE);
  15.525 -	purple_request_field_group_add_field(group, field);
  15.526 -
  15.527 -	purple_request_fields(gc,
  15.528 -		_("Find buddies"),
  15.529 -		_("Find buddies"),
  15.530 -		_("Please, enter your search criteria below"),
  15.531 -		fields,
  15.532 -		_("OK"), G_CALLBACK(ggp_callback_find_buddies),
  15.533 -		_("Cancel"), NULL,
  15.534 -		purple_connection_get_account(gc), NULL, NULL,
  15.535 -		gc);
  15.536 -}
  15.537 -
  15.538 -/* ----- CHANGE PASSWORD ---------------------------------------------------- */
  15.539 -
  15.540 -typedef struct
  15.541 -{
  15.542 -	guint inpa;
  15.543 -	struct gg_http *http_req;
  15.544 -	gchar *new_password;
  15.545 -	PurpleAccount *account;
  15.546 -} ggp_change_passwd_request;
  15.547 -
  15.548 -static void ggp_callback_change_passwd_handler(gpointer _req, gint fd,
  15.549 -	PurpleInputCondition cond)
  15.550 -{
  15.551 -	ggp_change_passwd_request *req = _req;
  15.552 -	const char *messagesTitle =
  15.553 -		_("Change password for the Gadu-Gadu account");
  15.554 -
  15.555 -	purple_input_remove(req->inpa);
  15.556 -
  15.557 -	if (gg_change_passwd_watch_fd(req->http_req) == -1 ||
  15.558 -		req->http_req->state == GG_STATE_ERROR)
  15.559 -		goto exit_error;
  15.560 -
  15.561 -	if (req->http_req->state != GG_STATE_DONE)
  15.562 -	{
  15.563 -		req->inpa = ggp_http_input_add(req->http_req,
  15.564 -			ggp_callback_change_passwd_handler, req);
  15.565 -		return;
  15.566 -	}
  15.567 -
  15.568 -	if (req->http_req->data != NULL &&
  15.569 -		((struct gg_pubdir*)req->http_req->data)->success == 1)
  15.570 -	{
  15.571 -		purple_account_set_password(req->account, req->new_password);
  15.572 -		purple_notify_info(req->account, messagesTitle,
  15.573 -			_("Password was changed successfully!"), NULL);
  15.574 -		goto exit_cleanup;
  15.575 -	}
  15.576 -
  15.577 -exit_error:
  15.578 -	purple_notify_error(req->account, messagesTitle,
  15.579 -		_("Unable to change password. Error occurred.\n"), NULL);
  15.580 -
  15.581 -exit_cleanup:
  15.582 -	gg_change_passwd_free(req->http_req);
  15.583 -	g_free(req->new_password);
  15.584 -	g_free(req);
  15.585 -}
  15.586 -
  15.587 -static void ggp_callback_change_passwd_ok(PurpleConnection *gc,
  15.588 -	PurpleRequestFields *fields)
  15.589 -{
  15.590 -	PurpleAccount *account;
  15.591 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.592 -	struct gg_http *h;
  15.593 -	gchar *cur, *p1, *p2, *t, *mail;
  15.594 -	const char *messagesTitle =
  15.595 -		_("Change password for the Gadu-Gadu account");
  15.596 -
  15.597 -	cur = g_strdup(purple_request_fields_get_string(fields,
  15.598 -		"password_cur"));
  15.599 -	p1 = g_strdup(purple_request_fields_get_string(fields, "password1"));
  15.600 -	p2 = g_strdup(purple_request_fields_get_string(fields, "password2"));
  15.601 -	t = g_strdup(purple_request_fields_get_string(fields, "token"));
  15.602 -	mail = g_strdup(purple_request_fields_get_string(fields, "email"));
  15.603 -
  15.604 -	account = purple_connection_get_account(gc);
  15.605 -
  15.606 -	if (cur == NULL || p1 == NULL || p2 == NULL || t == NULL ||
  15.607 -		mail == NULL || *cur == '\0' || *p1 == '\0' || *p2 == '\0' ||
  15.608 -		*t == '\0' || *mail == '\0') {
  15.609 -		purple_notify_error(account, messagesTitle,
  15.610 -			_("Fill in the fields."), NULL);
  15.611 -		goto exit_err;
  15.612 -	}
  15.613 -
  15.614 -	if (g_utf8_collate(p1, p2) != 0) {
  15.615 -		purple_notify_error(account, messagesTitle,
  15.616 -			_("New passwords do not match."), NULL);
  15.617 -		goto exit_err;
  15.618 -	}
  15.619 -
  15.620 -	if (strlen(p1) > 15) {
  15.621 -		purple_notify_error(account, messagesTitle,
  15.622 -			_("New password should be at most 15 characters long."),
  15.623 -			NULL);
  15.624 -		goto exit_err;
  15.625 -	}
  15.626 -
  15.627 -	if (g_utf8_collate(cur, purple_account_get_password(account)) != 0) {
  15.628 -		purple_notify_error(account, messagesTitle,
  15.629 -			_("Your current password is different from the one that"
  15.630 -			" you specified."), NULL);
  15.631 -		goto exit_err;
  15.632 -	}
  15.633 -
  15.634 -	if (!purple_email_is_valid(mail)) {
  15.635 -		purple_notify_error(account, messagesTitle,
  15.636 -			_("Invalid email address"), NULL);
  15.637 -		goto exit_err;
  15.638 -	}
  15.639 -
  15.640 -	purple_debug_info("gg", "Changing password with email \"%s\"...\n",
  15.641 -		mail);
  15.642 -
  15.643 -	h = gg_change_passwd4(ggp_get_uin(account), mail,
  15.644 -		purple_account_get_password(account), p1, info->token->id, t,
  15.645 -		1);
  15.646 -
  15.647 -	if (h == NULL)
  15.648 -		purple_notify_error(account, messagesTitle,
  15.649 -			_("Unable to change password. Error occurred.\n"),
  15.650 -			NULL);
  15.651 -	else
  15.652 -	{
  15.653 -		ggp_change_passwd_request *req =
  15.654 -			g_new(ggp_change_passwd_request, 1);
  15.655 -		req->http_req = h;
  15.656 -		req->new_password = g_strdup(p1);
  15.657 -		req->account = account;
  15.658 -		
  15.659 -		req->inpa = ggp_http_input_add(h,
  15.660 -			ggp_callback_change_passwd_handler, req);
  15.661 -	}
  15.662 -	
  15.663 -exit_err:
  15.664 -	g_free(cur);
  15.665 -	g_free(p1);
  15.666 -	g_free(p2);
  15.667 -	g_free(t);
  15.668 -	g_free(mail);
  15.669 -	g_free(info->token->id);
  15.670 -	g_free(info->token->data);
  15.671 -	g_free(info->token);
  15.672 -}
  15.673 -
  15.674 -static void ggp_change_passwd_dialog(PurpleConnection *gc)
  15.675 -{
  15.676 -	PurpleRequestFields *fields;
  15.677 -	PurpleRequestFieldGroup *group;
  15.678 -	PurpleRequestField *field;
  15.679 -
  15.680 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.681 -	GGPToken *token = info->token;
  15.682 -
  15.683 -	char *msg;
  15.684 -
  15.685 -	fields = purple_request_fields_new();
  15.686 -	group = purple_request_field_group_new(NULL);
  15.687 -	purple_request_fields_add_group(fields, group);
  15.688 -
  15.689 -	field = purple_request_field_string_new("password_cur",
  15.690 -			_("Current password"), "", FALSE);
  15.691 -	purple_request_field_string_set_masked(field, TRUE);
  15.692 -	purple_request_field_group_add_field(group, field);
  15.693 -
  15.694 -	field = purple_request_field_string_new("password1",
  15.695 -			_("Password"), "", FALSE);
  15.696 -	purple_request_field_string_set_masked(field, TRUE);
  15.697 -	purple_request_field_group_add_field(group, field);
  15.698 -
  15.699 -	field = purple_request_field_string_new("password2",
  15.700 -			_("Password (retype)"), "", FALSE);
  15.701 -	purple_request_field_string_set_masked(field, TRUE);
  15.702 -	purple_request_field_group_add_field(group, field);
  15.703 -
  15.704 -	field = purple_request_field_string_new("email",
  15.705 -			_("Email Address"), "", FALSE);
  15.706 -	purple_request_field_string_set_masked(field, FALSE);
  15.707 -	purple_request_field_group_add_field(group, field);
  15.708 -
  15.709 -	field = purple_request_field_string_new("token",
  15.710 -			_("Enter current token"), "", FALSE);
  15.711 -	purple_request_field_string_set_masked(field, FALSE);
  15.712 -	purple_request_field_group_add_field(group, field);
  15.713 -
  15.714 -	/* original size: 60x24 */
  15.715 -	field = purple_request_field_image_new("token_img",
  15.716 -			_("Current token"), token->data, token->size);
  15.717 -	purple_request_field_group_add_field(group, field);
  15.718 -
  15.719 -	msg = g_strdup_printf("%s %d",
  15.720 -		_("Please, enter your current password and your new password "
  15.721 -		"for UIN: "), ggp_get_uin(purple_connection_get_account(gc)));
  15.722 -
  15.723 -	purple_request_fields(gc,
  15.724 -		_("Change Gadu-Gadu Password"),
  15.725 -		_("Change Gadu-Gadu Password"),
  15.726 -		msg,
  15.727 -		fields, _("OK"), G_CALLBACK(ggp_callback_change_passwd_ok),
  15.728 -		_("Cancel"), NULL,
  15.729 -		purple_connection_get_account(gc), NULL, NULL,
  15.730 -		gc);
  15.731 -
  15.732 -	g_free(msg);
  15.733 -}
  15.734 -
  15.735 -static void ggp_change_passwd(PurplePluginAction *action)
  15.736 -{
  15.737 -	PurpleConnection *gc = (PurpleConnection *)action->context;
  15.738 -
  15.739 -	ggp_token_request(gc, ggp_change_passwd_dialog);
  15.740 -}
  15.741 -
  15.742 -/* ----- CHANGE STATUS BROADCASTING ------------------------------------------------ */
  15.743 -
  15.744 -static void ggp_action_change_status_broadcasting_ok(PurpleConnection *gc, PurpleRequestFields *fields)
  15.745 -{
  15.746 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.747 -	int selected_field;
  15.748 -	PurpleAccount *account = purple_connection_get_account(gc);
  15.749 -	PurpleStatus *status;
  15.750 -
  15.751 -	selected_field = purple_request_fields_get_choice(fields, "status_broadcasting");
  15.752 -	
  15.753 -	if (selected_field == 0)
  15.754 -		info->status_broadcasting = TRUE;
  15.755 -	else
  15.756 -		info->status_broadcasting = FALSE;
  15.757 -	
  15.758 -	status = purple_account_get_active_status(account);
  15.759 -	
  15.760 -	ggp_set_status(account, status);
  15.761 -}
  15.762 -
  15.763 -static void ggp_action_change_status_broadcasting(PurplePluginAction *action)
  15.764 -{
  15.765 -	PurpleConnection *gc = (PurpleConnection *)action->context;
  15.766 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
  15.767 -	
  15.768 -	PurpleRequestFields *fields;
  15.769 -	PurpleRequestFieldGroup *group;
  15.770 -	PurpleRequestField *field;
  15.771 -
  15.772 -	fields = purple_request_fields_new();
  15.773 -	group = purple_request_field_group_new(NULL);
  15.774 -	purple_request_fields_add_group(fields, group);
  15.775 -	
  15.776 -	field = purple_request_field_choice_new("status_broadcasting", _("Show status to:"), 0);
  15.777 -	purple_request_field_choice_add(field, _("All people"));
  15.778 -	purple_request_field_choice_add(field, _("Only buddies"));
  15.779 -	purple_request_field_group_add_field(group, field);
  15.780 -
  15.781 -	if (info->status_broadcasting)
  15.782 -		purple_request_field_choice_set_default_value(field, 0);
  15.783 -	else
  15.784 -		purple_request_field_choice_set_default_value(field, 1);
  15.785 -
  15.786 -	purple_request_fields(gc,
  15.787 -		_("Change status broadcasting"),
  15.788 -		_("Change status broadcasting"),
  15.789 -		_("Please, select who can see your status"),
  15.790 -		fields,
  15.791 -		_("OK"), G_CALLBACK(ggp_action_change_status_broadcasting_ok),
  15.792 -		_("Cancel"), NULL,
  15.793 -		purple_connection_get_account(gc), NULL, NULL,
  15.794 -		gc);
  15.795 -}
  15.796 -
  15.797  /* ----- CONFERENCES ---------------------------------------------------- */
  15.798  
  15.799  static void ggp_callback_add_to_chat_ok(PurpleBuddy *buddy, PurpleRequestFields *fields)
  15.800 @@ -970,538 +262,6 @@
  15.801  /* ----- INTERNAL CALLBACKS --------------------------------------------- */
  15.802  /* ---------------------------------------------------------------------- */
  15.803  
  15.804 -struct gg_fetch_avatar_data
  15.805 -{
  15.806 -	PurpleConnection *gc;
  15.807 -	gchar *uin;
  15.808 -	gchar *avatar_url;
  15.809 -};
  15.810 -
  15.811 -
  15.812 -static void gg_fetch_avatar_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
  15.813 -               const gchar *data, size_t len, const gchar *error_message) {
  15.814 -	struct gg_fetch_avatar_data *d = user_data;
  15.815 -	PurpleAccount *account;
  15.816 -	PurpleBuddy *buddy;
  15.817 -	gpointer buddy_icon_data;
  15.818 -
  15.819 -	purple_debug_info("gg", "gg_fetch_avatar_cb: got avatar image for %s\n",
  15.820 -		d->uin);
  15.821 -
  15.822 -	/* FIXME: This shouldn't be necessary */
  15.823 -	if (!PURPLE_CONNECTION_IS_VALID(d->gc)) {
  15.824 -		g_free(d->uin);
  15.825 -		g_free(d->avatar_url);
  15.826 -		g_free(d);
  15.827 -		g_return_if_reached();
  15.828 -	}
  15.829 -
  15.830 -	account = purple_connection_get_account(d->gc);
  15.831 -	buddy = purple_find_buddy(account, d->uin);
  15.832 -
  15.833 -	if (buddy == NULL)
  15.834 -		goto out;
  15.835 -
  15.836 -	buddy_icon_data = g_memdup(data, len);
  15.837 -
  15.838 -	purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy),
  15.839 -			buddy_icon_data, len, d->avatar_url);
  15.840 -	purple_debug_info("gg", "gg_fetch_avatar_cb: UIN %s should have avatar "
  15.841 -		"now\n", d->uin);
  15.842 -
  15.843 -out:
  15.844 -	g_free(d->uin);
  15.845 -	g_free(d->avatar_url);
  15.846 -	g_free(d);
  15.847 -}
  15.848 -
  15.849 -static void gg_get_avatar_url_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
  15.850 -               const gchar *url_text, size_t len, const gchar *error_message) {
  15.851 -	struct gg_fetch_avatar_data *data;
  15.852 -	PurpleConnection *gc = user_data;
  15.853 -	PurpleAccount *account;
  15.854 -	PurpleBuddy *buddy;
  15.855 -	const char *uin;
  15.856 -	const char *is_blank;
  15.857 -	const char *checksum;
  15.858 -
  15.859 -	gchar *bigavatar = NULL;
  15.860 -	xmlnode *xml = NULL;
  15.861 -	xmlnode *xmlnode_users;
  15.862 -	xmlnode *xmlnode_user;
  15.863 -	xmlnode *xmlnode_avatars;
  15.864 -	xmlnode *xmlnode_avatar;
  15.865 -	xmlnode *xmlnode_bigavatar;
  15.866 -
  15.867 -	g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
  15.868 -	account = purple_connection_get_account(gc);
  15.869 -
  15.870 -	if (error_message != NULL)
  15.871 -		purple_debug_error("gg", "gg_get_avatars_cb error: %s\n", error_message);
  15.872 -	else if (len > 0 && url_text && *url_text) {
  15.873 -		xml = xmlnode_from_str(url_text, -1);
  15.874 -		if (xml == NULL)
  15.875 -			goto out;
  15.876 -
  15.877 -		xmlnode_users = xmlnode_get_child(xml, "users");
  15.878 -		if (xmlnode_users == NULL)
  15.879 -			goto out;
  15.880 -
  15.881 -		xmlnode_user = xmlnode_get_child(xmlnode_users, "user");
  15.882 -		if (xmlnode_user == NULL)
  15.883 -			goto out;
  15.884 -
  15.885 -		uin = xmlnode_get_attrib(xmlnode_user, "uin");
  15.886 -
  15.887 -		xmlnode_avatars = xmlnode_get_child(xmlnode_user, "avatars");
  15.888 -		if (xmlnode_avatars == NULL)
  15.889 -			goto out;
  15.890 -
  15.891 -		xmlnode_avatar = xmlnode_get_child(xmlnode_avatars, "avatar");
  15.892 -		if (xmlnode_avatar == NULL)
  15.893 -			goto out;
  15.894 -
  15.895 -		xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "originBigAvatar");
  15.896 -		if (xmlnode_bigavatar == NULL)
  15.897 -			goto out;
  15.898 -
  15.899 -		is_blank = xmlnode_get_attrib(xmlnode_avatar, "blank");
  15.900 -		bigavatar = xmlnode_get_data(xmlnode_bigavatar);
  15.901 -
  15.902 -		purple_debug_info("gg", "gg_get_avatar_url_cb: UIN %s, IS_BLANK %s, "
  15.903 -		                        "URL %s\n",
  15.904 -		                  uin ? uin : "(null)", is_blank ? is_blank : "(null)",
  15.905 -		                  bigavatar ? bigavatar : "(null)");
  15.906 -
  15.907 -		if (uin != NULL && bigavatar != NULL) {
  15.908 -			buddy = purple_find_buddy(account, uin);
  15.909 -			if (buddy == NULL)
  15.910 -				goto out;
  15.911 -
  15.912 -			checksum = purple_buddy_icons_get_checksum_for_user(buddy);
  15.913 -
  15.914 -			if (purple_strequal(is_blank, "1")) {
  15.915 -				purple_buddy_icons_set_for_user(account,
  15.916 -						purple_buddy_get_name(buddy), NULL, 0, NULL);
  15.917 -			} else if (!purple_strequal(checksum, bigavatar)) {
  15.918 -				data = g_new0(struct gg_fetch_avatar_data, 1);
  15.919 -				data->gc = gc;
  15.920 -				data->uin = g_strdup(uin);
  15.921 -				data->avatar_url = g_strdup(bigavatar);
  15.922 -
  15.923 -				purple_debug_info("gg", "gg_get_avatar_url_cb: "
  15.924 -					"requesting avatar for %s\n", uin);
  15.925 -				/* FIXME: This should be cancelled somewhere if not needed. */
  15.926 -				url_data = purple_util_fetch_url_request(account,
  15.927 -						bigavatar, TRUE, "Mozilla/4.0 (compatible; MSIE 5.0)",
  15.928 -						FALSE, NULL, FALSE, -1, gg_fetch_avatar_cb, data);
  15.929 -			}
  15.930 -		}
  15.931 -	}
  15.932 -
  15.933 -out:
  15.934 -	if (xml)
  15.935 -		xmlnode_free(xml);
  15.936 -	g_free(bigavatar);
  15.937 -}
  15.938 -
  15.939 -/**
  15.940 - * Try to update avatar of the buddy.
  15.941 - *
  15.942 - * @param gc     PurpleConnection
  15.943 - * @param uin    UIN of the buddy.
  15.944 - */
  15.945 -static void ggp_update_buddy_avatar(PurpleConnection *gc, uin_t uin)
  15.946 -{
  15.947 -	gchar *avatarurl;
  15.948 -	PurpleUtilFetchUrlData *url_data;
  15.949 -
  15.950 -	purple_debug_info("gg", "ggp_update_buddy_avatar(gc, %u)\n", uin);
  15.951 -
  15.952 -	avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%u/0.xml", uin);
  15.953 -
  15.954 -	/* FIXME: This should be cancelled somewhere if not needed. */
  15.955 -	url_data = purple_util_fetch_url_request(
  15.956 -			purple_connection_get_account(gc), avatarurl, TRUE,
  15.957 -			"Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1,
  15.958 -			gg_get_avatar_url_cb, gc);
  15.959 -
  15.960 -	g_free(avatarurl);
  15.961 -}
  15.962 -
  15.963 -/**
  15.964 - * Handle change of the status of the buddy.
  15.965 - *
  15.966 - * @param gc     PurpleConnection
  15.967 - * @param uin    UIN of the buddy.
  15.968 - * @param status ID of the status.
  15.969 - * @param descr  Description.
  15.970 - */
  15.971 -static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin,
  15.972 -				       int status, const char *descr)
  15.973 -{
  15.974 -	gchar *from;
  15.975 -	const char *st;
  15.976 -	char *status_msg = NULL;
  15.977 -
  15.978 -	ggp_update_buddy_avatar(gc, uin);
  15.979 -
  15.980 -	from = g_strdup_printf("%u", uin);
  15.981 -
  15.982 -	switch (status) {
  15.983 -		case GG_STATUS_NOT_AVAIL:
  15.984 -		case GG_STATUS_NOT_AVAIL_DESCR:
  15.985 -			st = purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE);
  15.986 -			break;
  15.987 -		case GG_STATUS_FFC:
  15.988 -		case GG_STATUS_FFC_DESCR:
  15.989 -			st = "freeforchat";
  15.990 -			break;
  15.991 -		case GG_STATUS_AVAIL:
  15.992 -		case GG_STATUS_AVAIL_DESCR:
  15.993 -			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
  15.994 -			break;
  15.995 -		case GG_STATUS_BUSY:
  15.996 -		case GG_STATUS_BUSY_DESCR:
  15.997 -			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY);
  15.998 -			break;
  15.999 -		case GG_STATUS_INVISIBLE:
 15.1000 -		case GG_STATUS_INVISIBLE_DESCR:
 15.1001 -			st = purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE);
 15.1002 -			break;
 15.1003 -		case GG_STATUS_DND:
 15.1004 -		case GG_STATUS_DND_DESCR:
 15.1005 -			st = purple_primitive_get_id_from_type(PURPLE_STATUS_UNAVAILABLE);
 15.1006 -			break;
 15.1007 -		case GG_STATUS_BLOCKED:
 15.1008 -			/* user is blocking us.... */
 15.1009 -			st = "blocked";
 15.1010 -			break;
 15.1011 -		default:
 15.1012 -			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 15.1013 -			purple_debug_info("gg",
 15.1014 -				"GG_EVENT_NOTIFY: Unknown status: %d\n", status);
 15.1015 -			break;
 15.1016 -	}
 15.1017 -
 15.1018 -	if (descr != NULL) {
 15.1019 -		status_msg = g_strdup(descr);
 15.1020 -		g_strstrip(status_msg);
 15.1021 -		if (status_msg[0] == '\0') {
 15.1022 -			g_free(status_msg);
 15.1023 -			status_msg = NULL;
 15.1024 -		}
 15.1025 -	}
 15.1026 -
 15.1027 -	purple_debug_info("gg", "status of %u is %s [%s]\n", uin, st,
 15.1028 -		status_msg ? status_msg : "");
 15.1029 -	if (status_msg == NULL) {
 15.1030 -		purple_prpl_got_user_status(purple_connection_get_account(gc),
 15.1031 -			from, st, NULL);
 15.1032 -	} else {
 15.1033 -		purple_prpl_got_user_status(purple_connection_get_account(gc),
 15.1034 -			from, st, "message", status_msg, NULL);
 15.1035 -		g_free(status_msg);
 15.1036 -	}
 15.1037 -	g_free(from);
 15.1038 -}
 15.1039 -
 15.1040 -static void ggp_sr_close_cb(gpointer user_data)
 15.1041 -{
 15.1042 -	GGPSearchForm *form = user_data;
 15.1043 -	GGPInfo *info = form->user_data;
 15.1044 -
 15.1045 -	ggp_search_remove(info->searches, form->seq);
 15.1046 -	purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u\n",
 15.1047 -		form->seq);
 15.1048 -	ggp_search_form_destroy(form);
 15.1049 -}
 15.1050 -
 15.1051 -/**
 15.1052 - * Translate a status' ID to a more user-friendly name.
 15.1053 - *
 15.1054 - * @param id The ID of the status.
 15.1055 - *
 15.1056 - * @return The user-friendly name of the status.
 15.1057 - */
 15.1058 -static const char *ggp_status_by_id(unsigned int id)
 15.1059 -{
 15.1060 -	const char *st;
 15.1061 -
 15.1062 -	purple_debug_info("gg", "ggp_status_by_id: %d\n", id);
 15.1063 -	switch (id) {
 15.1064 -		case GG_STATUS_NOT_AVAIL:
 15.1065 -		case GG_STATUS_NOT_AVAIL_DESCR:
 15.1066 -			st = _("Offline");
 15.1067 -			break;
 15.1068 -		case GG_STATUS_AVAIL:
 15.1069 -		case GG_STATUS_AVAIL_DESCR:
 15.1070 -			st = _("Available");
 15.1071 -			break;
 15.1072 -		case GG_STATUS_FFC:
 15.1073 -		case GG_STATUS_FFC_DESCR:
 15.1074 -			return _("Chatty");
 15.1075 -		case GG_STATUS_DND:
 15.1076 -		case GG_STATUS_DND_DESCR:
 15.1077 -			return _("Do Not Disturb");
 15.1078 -		case GG_STATUS_BUSY:
 15.1079 -		case GG_STATUS_BUSY_DESCR:
 15.1080 -			st = _("Away");
 15.1081 -			break;
 15.1082 -		default:
 15.1083 -			st = _("Unknown");
 15.1084 -			break;
 15.1085 -	}
 15.1086 -
 15.1087 -	return st;
 15.1088 -}
 15.1089 -
 15.1090 -static void ggp_pubdir_handle_info(PurpleConnection *gc, gg_pubdir50_t req,
 15.1091 -				   GGPSearchForm *form)
 15.1092 -{
 15.1093 -	PurpleNotifyUserInfo *user_info;
 15.1094 -	PurpleBuddy *buddy;
 15.1095 -	char *val, *who;
 15.1096 -
 15.1097 -	user_info = purple_notify_user_info_new();
 15.1098 -
 15.1099 -	val = ggp_search_get_result(req, 0, GG_PUBDIR50_STATUS);
 15.1100 -	/* XXX: Use of ggp_str_to_uin() is an ugly hack! */
 15.1101 -	purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), ggp_status_by_id(ggp_str_to_uin(val)));
 15.1102 -	g_free(val);
 15.1103 -
 15.1104 -	who = ggp_search_get_result(req, 0, GG_PUBDIR50_UIN);
 15.1105 -	/* TODO: Check whether it's correct to call add_pair_html,
 15.1106 -	         or if we should be using add_pair_plaintext */
 15.1107 -	purple_notify_user_info_add_pair_html(user_info, _("UIN"), who);
 15.1108 -
 15.1109 -	val = ggp_search_get_result(req, 0, GG_PUBDIR50_FIRSTNAME);
 15.1110 -	/* TODO: Check whether it's correct to call add_pair_html,
 15.1111 -	         or if we should be using add_pair_plaintext */
 15.1112 -	purple_notify_user_info_add_pair_html(user_info, _("First Name"), val);
 15.1113 -	g_free(val);
 15.1114 -
 15.1115 -	val = ggp_search_get_result(req, 0, GG_PUBDIR50_NICKNAME);
 15.1116 -	/* TODO: Check whether it's correct to call add_pair_html,
 15.1117 -	         or if we should be using add_pair_plaintext */
 15.1118 -	purple_notify_user_info_add_pair_html(user_info, _("Nickname"), val);
 15.1119 -	g_free(val);
 15.1120 -
 15.1121 -	val = ggp_search_get_result(req, 0, GG_PUBDIR50_CITY);
 15.1122 -	/* TODO: Check whether it's correct to call add_pair_html,
 15.1123 -	         or if we should be using add_pair_plaintext */
 15.1124 -	purple_notify_user_info_add_pair_html(user_info, _("City"), val);
 15.1125 -	g_free(val);
 15.1126 -
 15.1127 -	val = ggp_search_get_result(req, 0, GG_PUBDIR50_BIRTHYEAR);
 15.1128 -	if (strncmp(val, "0", 1)) {
 15.1129 -		/* TODO: Check whether it's correct to call add_pair_html,
 15.1130 -		         or if we should be using add_pair_plaintext */
 15.1131 -		purple_notify_user_info_add_pair_html(user_info, _("Birth Year"), val);
 15.1132 -	}
 15.1133 -	g_free(val);
 15.1134 -
 15.1135 -	/*
 15.1136 -	 * Include a status message, if exists and buddy is in the blist.
 15.1137 -	 */
 15.1138 -	buddy = purple_find_buddy(purple_connection_get_account(gc), who);
 15.1139 -	if (NULL != buddy) {
 15.1140 -		PurpleStatus *status;
 15.1141 -		const char *msg;
 15.1142 -
 15.1143 -		status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
 15.1144 -		msg = purple_status_get_attr_string(status, "message");
 15.1145 -
 15.1146 -		if (msg != NULL) {
 15.1147 -			purple_notify_user_info_add_pair_plaintext(user_info, _("Message"), msg);
 15.1148 -		}
 15.1149 -	}
 15.1150 -
 15.1151 -	purple_notify_userinfo(gc, who, user_info, ggp_sr_close_cb, form);
 15.1152 -	g_free(who);
 15.1153 -	purple_notify_user_info_destroy(user_info);
 15.1154 -}
 15.1155 -
 15.1156 -static void ggp_pubdir_handle_full(PurpleConnection *gc, gg_pubdir50_t req,
 15.1157 -				   GGPSearchForm *form)
 15.1158 -{
 15.1159 -	PurpleNotifySearchResults *results;
 15.1160 -	PurpleNotifySearchColumn *column;
 15.1161 -	int res_count;
 15.1162 -	int start;
 15.1163 -	int i;
 15.1164 -
 15.1165 -	g_return_if_fail(form != NULL);
 15.1166 -
 15.1167 -	res_count = gg_pubdir50_count(req);
 15.1168 -	res_count = (res_count > PUBDIR_RESULTS_MAX) ? PUBDIR_RESULTS_MAX : res_count;
 15.1169 -	if (form->page_size == 0)
 15.1170 -		form->page_size = res_count;
 15.1171 -
 15.1172 -	results = purple_notify_searchresults_new();
 15.1173 -
 15.1174 -	if (results == NULL) {
 15.1175 -		purple_debug_error("gg", "ggp_pubdir_reply_handler: "
 15.1176 -				 "Unable to display the search results.\n");
 15.1177 -		purple_notify_error(gc, NULL,
 15.1178 -				  _("Unable to display the search results."),
 15.1179 -				  NULL);
 15.1180 -		if (form->window == NULL)
 15.1181 -			ggp_sr_close_cb(form);
 15.1182 -		return;
 15.1183 -	}
 15.1184 -
 15.1185 -	column = purple_notify_searchresults_column_new(_("UIN"));
 15.1186 -	purple_notify_searchresults_column_add(results, column);
 15.1187 -
 15.1188 -	column = purple_notify_searchresults_column_new(_("First Name"));
 15.1189 -	purple_notify_searchresults_column_add(results, column);
 15.1190 -
 15.1191 -	column = purple_notify_searchresults_column_new(_("Nickname"));
 15.1192 -	purple_notify_searchresults_column_add(results, column);
 15.1193 -
 15.1194 -	column = purple_notify_searchresults_column_new(_("City"));
 15.1195 -	purple_notify_searchresults_column_add(results, column);
 15.1196 -
 15.1197 -	column = purple_notify_searchresults_column_new(_("Birth Year"));
 15.1198 -	purple_notify_searchresults_column_add(results, column);
 15.1199 -
 15.1200 -	purple_debug_info("gg", "Going with %d entries\n", res_count);
 15.1201 -
 15.1202 -	start = (int)ggp_str_to_uin(gg_pubdir50_get(req, 0, GG_PUBDIR50_START));
 15.1203 -	purple_debug_info("gg", "start = %d\n", start);
 15.1204 -
 15.1205 -	for (i = 0; i < res_count; i++) {
 15.1206 -		GList *row = NULL;
 15.1207 -		char *birth = ggp_search_get_result(req, i, GG_PUBDIR50_BIRTHYEAR);
 15.1208 -
 15.1209 -		/* TODO: Status will be displayed as an icon. */
 15.1210 -		/* row = g_list_append(row, ggp_search_get_result(req, i, GG_PUBDIR50_STATUS)); */
 15.1211 -		row = g_list_append(row, ggp_search_get_result(req, i,
 15.1212 -							GG_PUBDIR50_UIN));
 15.1213 -		row = g_list_append(row, ggp_search_get_result(req, i,
 15.1214 -							GG_PUBDIR50_FIRSTNAME));
 15.1215 -		row = g_list_append(row, ggp_search_get_result(req, i,
 15.1216 -							GG_PUBDIR50_NICKNAME));
 15.1217 -		row = g_list_append(row, ggp_search_get_result(req, i,
 15.1218 -							GG_PUBDIR50_CITY));
 15.1219 -		row = g_list_append(row,
 15.1220 -			(birth && strncmp(birth, "0", 1)) ? birth : g_strdup("-"));
 15.1221 -
 15.1222 -		purple_notify_searchresults_row_add(results, row);
 15.1223 -	}
 15.1224 -
 15.1225 -	purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_CONTINUE,
 15.1226 -					     ggp_callback_show_next);
 15.1227 -	purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD,
 15.1228 -					     ggp_callback_add_buddy);
 15.1229 -	purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_IM,
 15.1230 -					     ggp_callback_im);
 15.1231 -
 15.1232 -	if (form->window == NULL) {
 15.1233 -		void *h = purple_notify_searchresults(gc,
 15.1234 -				_("Gadu-Gadu Public Directory"),
 15.1235 -				_("Search results"), NULL, results,
 15.1236 -				(PurpleNotifyCloseCallback)ggp_sr_close_cb,
 15.1237 -				form);
 15.1238 -
 15.1239 -		if (h == NULL) {
 15.1240 -			purple_debug_error("gg", "ggp_pubdir_reply_handler: "
 15.1241 -					 "Unable to display the search results.\n");
 15.1242 -			purple_notify_error(gc, NULL,
 15.1243 -					  _("Unable to display the search results."),
 15.1244 -					  NULL);
 15.1245 -			return;
 15.1246 -		}
 15.1247 -
 15.1248 -		form->window = h;
 15.1249 -	} else {
 15.1250 -		purple_notify_searchresults_new_rows(gc, results, form->window);
 15.1251 -	}
 15.1252 -}
 15.1253 -
 15.1254 -static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req)
 15.1255 -{
 15.1256 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
 15.1257 -	GGPSearchForm *form;
 15.1258 -	int res_count;
 15.1259 -	guint32 seq;
 15.1260 -
 15.1261 -	seq = gg_pubdir50_seq(req);
 15.1262 -	form = ggp_search_get(info->searches, seq);
 15.1263 -	purple_debug_info("gg",
 15.1264 -		"ggp_pubdir_reply_handler(): seq %u --> form %p\n", seq, form);
 15.1265 -	/*
 15.1266 -	 * this can happen when user will request more results
 15.1267 -	 * and close the results window before they arrive.
 15.1268 -	 */
 15.1269 -	g_return_if_fail(form != NULL);
 15.1270 -
 15.1271 -	res_count = gg_pubdir50_count(req);
 15.1272 -	if (res_count < 1) {
 15.1273 -		purple_debug_info("gg", "GG_EVENT_PUBDIR50_SEARCH_REPLY: Nothing found\n");
 15.1274 -		purple_notify_error(gc, NULL,
 15.1275 -			_("No matching users found"),
 15.1276 -			_("There are no users matching your search criteria."));
 15.1277 -		if (form->window == NULL)
 15.1278 -			ggp_sr_close_cb(form);
 15.1279 -		return;
 15.1280 -	}
 15.1281 -
 15.1282 -	switch (form->search_type) {
 15.1283 -		case GGP_SEARCH_TYPE_INFO:
 15.1284 -			ggp_pubdir_handle_info(gc, req, form);
 15.1285 -			break;
 15.1286 -		case GGP_SEARCH_TYPE_FULL:
 15.1287 -			ggp_pubdir_handle_full(gc, req, form);
 15.1288 -			break;
 15.1289 -		default:
 15.1290 -			purple_debug_warning("gg", "Unknown search_type!\n");
 15.1291 -			break;
 15.1292 -	}
 15.1293 -}
 15.1294 -
 15.1295 -static void ggp_recv_image_handler(PurpleConnection *gc, const struct gg_event *ev)
 15.1296 -{
 15.1297 -	gint imgid = 0;
 15.1298 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
 15.1299 -	GList *entry = g_list_first(info->pending_richtext_messages);
 15.1300 -	gchar *handlerid = g_strdup_printf("IMGID_HANDLER-%i", ev->event.image_reply.crc32);
 15.1301 -
 15.1302 -	imgid = purple_imgstore_add_with_id(
 15.1303 -		g_memdup(ev->event.image_reply.image, ev->event.image_reply.size),
 15.1304 -		ev->event.image_reply.size,
 15.1305 -		ev->event.image_reply.filename);
 15.1306 -
 15.1307 -	purple_debug_info("gg", "ggp_recv_image_handler: got image with crc32: %u\n", ev->event.image_reply.crc32);
 15.1308 -
 15.1309 -	while(entry) {
 15.1310 -		if (strstr((gchar *)entry->data, handlerid) != NULL) {
 15.1311 -			gchar **split = g_strsplit((gchar *)entry->data, handlerid, 3);
 15.1312 -			gchar *text = g_strdup_printf("%s%i%s", split[0], imgid, split[1]);
 15.1313 -			purple_debug_info("gg", "ggp_recv_image_handler: found message matching crc32: %s\n", (gchar *)entry->data);
 15.1314 -			g_strfreev(split);
 15.1315 -			info->pending_richtext_messages = g_list_remove(info->pending_richtext_messages, entry->data);
 15.1316 -			/* We don't have any more images to download */
 15.1317 -			if (strstr(text, "<IMG ID=\"IMGID_HANDLER") == NULL) {
 15.1318 -				gchar *buf = g_strdup_printf("%lu", (unsigned long int)ev->event.image_reply.sender);
 15.1319 -				serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, time(NULL));
 15.1320 -				g_free(buf);
 15.1321 -				purple_debug_info("gg", "ggp_recv_image_handler: richtext message: %s\n", text);
 15.1322 -				g_free(text);
 15.1323 -				break;
 15.1324 -			}
 15.1325 -			info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, text);
 15.1326 -			break;
 15.1327 -		}
 15.1328 -		entry = g_list_next(entry);
 15.1329 -	}
 15.1330 -	g_free(handlerid);
 15.1331 -
 15.1332 -	return;
 15.1333 -}
 15.1334 -
 15.1335 -
 15.1336  /**
 15.1337   * Dispatch a message received from a buddy.
 15.1338   *
 15.1339 @@ -1510,7 +270,7 @@
 15.1340   *
 15.1341   * Image receiving, some code borrowed from Kadu http://www.kadu.net
 15.1342   */
 15.1343 -static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev)
 15.1344 +void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event_msg *ev, gboolean multilogon)
 15.1345  {
 15.1346  	GGPInfo *info = purple_connection_get_protocol_data(gc);
 15.1347  	PurpleConversation *conv;
 15.1348 @@ -1518,37 +278,38 @@
 15.1349  	gchar *msg;
 15.1350  	gchar *tmp;
 15.1351  	time_t mtime;
 15.1352 +	uin_t sender = ev->sender;
 15.1353  
 15.1354 -	if (ev->event.msg.message == NULL)
 15.1355 +	if (ev->message == NULL)
 15.1356  	{
 15.1357  		purple_debug_warning("gg", "ggp_recv_message_handler: NULL as message pointer\n");
 15.1358  		return;
 15.1359  	}
 15.1360  
 15.1361 -	from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
 15.1362 +	from = g_strdup_printf("%lu", (unsigned long int)ev->sender);
 15.1363  
 15.1364 -	/*
 15.1365 -	tmp = charset_convert((const char *)ev->event.msg.message,
 15.1366 -			      "CP1250", "UTF-8");
 15.1367 -	*/
 15.1368 -	tmp = g_strdup_printf("%s", ev->event.msg.message);
 15.1369 +	tmp = g_strdup_printf("%s", ev->message);
 15.1370  	purple_str_strip_char(tmp, '\r');
 15.1371  	msg = g_markup_escape_text(tmp, -1);
 15.1372  	g_free(tmp);
 15.1373  
 15.1374 +	if (ev->msgclass & GG_CLASS_QUEUED)
 15.1375 +		mtime = ev->time;
 15.1376 +	else
 15.1377 +		mtime = time(NULL);
 15.1378 +
 15.1379  	/* We got richtext message */
 15.1380 -	if (ev->event.msg.formats_length)
 15.1381 +	if (ev->formats_length)
 15.1382  	{
 15.1383  		gboolean got_image = FALSE, bold = FALSE, italic = FALSE, under = FALSE;
 15.1384 -		char *cformats = (char *)ev->event.msg.formats;
 15.1385 -		char *cformats_end = cformats + ev->event.msg.formats_length;
 15.1386 +		char *cformats = (char *)ev->formats;
 15.1387 +		char *cformats_end = cformats + ev->formats_length;
 15.1388  		gint increased_len = 0;
 15.1389  		struct gg_msg_richtext_format *actformat;
 15.1390  		struct gg_msg_richtext_image *actimage;
 15.1391  		GString *message = g_string_new(msg);
 15.1392 -		gchar *handlerid;
 15.1393  
 15.1394 -		purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->event.msg.formats_length);
 15.1395 +		purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->formats_length);
 15.1396  
 15.1397  		while (cformats < cformats_end)
 15.1398  		{
 15.1399 @@ -1569,7 +330,10 @@
 15.1400  				(actformat->font & GG_FONT_UNDERLINE) != 0,
 15.1401  				increased_len);
 15.1402  
 15.1403 -			if (actformat->font & GG_FONT_IMAGE) {
 15.1404 +			if (actformat->font & GG_FONT_IMAGE)
 15.1405 +			{
 15.1406 +				const char *placeholder;
 15.1407 +			
 15.1408  				got_image = TRUE;
 15.1409  				actimage = (struct gg_msg_richtext_image*)(cformats);
 15.1410  				cformats += sizeof(struct gg_msg_richtext_image);
 15.1411 @@ -1582,13 +346,12 @@
 15.1412  					continue;
 15.1413  				}
 15.1414  
 15.1415 -				gg_image_request(info->session, ev->event.msg.sender,
 15.1416 +				gg_image_request(info->session, ev->sender,
 15.1417  					actimage->size, actimage->crc32);
 15.1418  
 15.1419 -				handlerid = g_strdup_printf("<IMG ID=\"IMGID_HANDLER-%i\">", actimage->crc32);
 15.1420 -				g_string_insert(message, byteoffset, handlerid);
 15.1421 -				increased_len += strlen(handlerid);
 15.1422 -				g_free(handlerid);
 15.1423 +				placeholder = ggp_image_pending_placeholder(actimage->crc32);
 15.1424 +				g_string_insert(message, byteoffset, placeholder);
 15.1425 +				increased_len += strlen(placeholder);
 15.1426  				continue;
 15.1427  			}
 15.1428  
 15.1429 @@ -1636,80 +399,61 @@
 15.1430  		msg = message->str;
 15.1431  		g_string_free(message, FALSE);
 15.1432  
 15.1433 -		if (got_image) {
 15.1434 -			info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, msg);
 15.1435 +		if (got_image)
 15.1436 +		{
 15.1437 +			ggp_image_got_im(gc, sender, msg, mtime);
 15.1438  			return;
 15.1439  		}
 15.1440  	}
 15.1441  
 15.1442 -	purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d)\n",
 15.1443 -			from, msg, ev->event.msg.msgclass,
 15.1444 -			ev->event.msg.recipients_count);
 15.1445 +	purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d; multilogon = %d)\n",
 15.1446 +			from, msg, ev->msgclass,
 15.1447 +			ev->recipients_count,
 15.1448 +			multilogon);
 15.1449  
 15.1450 -	if (ev->event.msg.msgclass & GG_CLASS_QUEUED)
 15.1451 -		mtime = ev->event.msg.time;
 15.1452 -	else
 15.1453 -		mtime = time(NULL);
 15.1454 -
 15.1455 -	if (ev->event.msg.recipients_count == 0) {
 15.1456 +	if (multilogon && ev->recipients_count != 0) {
 15.1457 +		purple_debug_warning("gg", "ggp_recv_message_handler: conference multilogon messages are not yet handled\n");
 15.1458 +	} else if (multilogon) {
 15.1459 +		PurpleAccount *account = purple_connection_get_account(gc);
 15.1460 +		PurpleConversation *conv;
 15.1461 +		const gchar *who = ggp_uin_to_str(ev->sender); // not really sender
 15.1462 +		conv = purple_find_conversation_with_account(
 15.1463 +			PURPLE_CONV_TYPE_IM, who, account);
 15.1464 +		if (conv == NULL)
 15.1465 +			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who);
 15.1466 +		purple_conversation_write(conv, purple_account_get_username(account), msg, PURPLE_MESSAGE_SEND, mtime);
 15.1467 +	} else if (ev->recipients_count == 0) {
 15.1468  		serv_got_im(gc, from, msg, 0, mtime);
 15.1469  	} else {
 15.1470  		const char *chat_name;
 15.1471  		int chat_id;
 15.1472 -		char *buddy_name;
 15.1473  
 15.1474  		chat_name = ggp_confer_find_by_participants(gc,
 15.1475 -				ev->event.msg.recipients,
 15.1476 -				ev->event.msg.recipients_count);
 15.1477 +				ev->recipients,
 15.1478 +				ev->recipients_count);
 15.1479  
 15.1480  		if (chat_name == NULL) {
 15.1481  			chat_name = ggp_confer_add_new(gc, NULL);
 15.1482  			serv_got_joined_chat(gc, info->chats_count, chat_name);
 15.1483  
 15.1484  			ggp_confer_participants_add_uin(gc, chat_name,
 15.1485 -							ev->event.msg.sender);
 15.1486 +							ev->sender);
 15.1487  
 15.1488  			ggp_confer_participants_add(gc, chat_name,
 15.1489 -						    ev->event.msg.recipients,
 15.1490 -						    ev->event.msg.recipients_count);
 15.1491 +						    ev->recipients,
 15.1492 +						    ev->recipients_count);
 15.1493  		}
 15.1494  		conv = ggp_confer_find_by_name(gc, chat_name);
 15.1495  		chat_id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
 15.1496  
 15.1497 -		buddy_name = ggp_buddy_get_name(gc, ev->event.msg.sender);
 15.1498 -		serv_got_chat_in(gc, chat_id, buddy_name,
 15.1499 -				 PURPLE_MESSAGE_RECV, msg, mtime);
 15.1500 -		g_free(buddy_name);
 15.1501 +		serv_got_chat_in(gc, chat_id,
 15.1502 +			ggp_buddylist_get_buddy_name(gc, ev->sender),
 15.1503 +			PURPLE_MESSAGE_RECV, msg, mtime);
 15.1504  	}
 15.1505  	g_free(msg);
 15.1506  	g_free(from);
 15.1507  }
 15.1508  
 15.1509 -static void ggp_send_image_handler(PurpleConnection *gc, const struct gg_event *ev)
 15.1510 -{
 15.1511 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
 15.1512 -	PurpleStoredImage *image;
 15.1513 -	gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32)));
 15.1514 -
 15.1515 -	purple_debug_info("gg", "ggp_send_image_handler: image request received, crc32: %u, imgid: %d\n", ev->event.image_request.crc32, imgid);
 15.1516 -
 15.1517 -	if(imgid)
 15.1518 -	{
 15.1519 -		if((image = purple_imgstore_find_by_id(imgid))) {
 15.1520 -			gint image_size = purple_imgstore_get_size(image);
 15.1521 -			gconstpointer image_bin = purple_imgstore_get_data(image);
 15.1522 -			const char *image_filename = purple_imgstore_get_filename(image);
 15.1523 -
 15.1524 -			purple_debug_info("gg", "ggp_send_image_handler: sending image imgid: %i, crc: %u\n", imgid, ev->event.image_request.crc32);
 15.1525 -			gg_image_reply(info->session, (unsigned long int)ev->event.image_request.sender, image_filename, image_bin, image_size);
 15.1526 -			purple_imgstore_unref(image);
 15.1527 -		} else {
 15.1528 -			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);
 15.1529 -		}
 15.1530 -		g_hash_table_remove(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32));
 15.1531 -	}
 15.1532 -}
 15.1533 -
 15.1534  static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int length) {
 15.1535  	gchar *from;
 15.1536  
 15.1537 @@ -1728,6 +472,7 @@
 15.1538   * @param data Raw XML contents.
 15.1539   *
 15.1540   * @see http://toxygen.net/libgadu/protocol/#ch1.13
 15.1541 + * @todo: this may not be necessary anymore
 15.1542   */
 15.1543  static void ggp_xml_event_handler(PurpleConnection *gc, char *data)
 15.1544  {
 15.1545 @@ -1776,7 +521,6 @@
 15.1546  				purple_debug_info("gg",
 15.1547  					"ggp_xml_event_handler: avatar updated (uid: %u)\n",
 15.1548  					event_sender);
 15.1549 -				ggp_update_buddy_avatar(gc, event_sender);
 15.1550  				break;
 15.1551  			default:
 15.1552  				purple_debug_error("gg",
 15.1553 @@ -1795,7 +539,6 @@
 15.1554  	PurpleConnection *gc = _gc;
 15.1555  	GGPInfo *info = purple_connection_get_protocol_data(gc);
 15.1556  	struct gg_event *ev;
 15.1557 -	int i;
 15.1558  
 15.1559  	if (!(ev = gg_watch_fd(info->session))) {
 15.1560  		purple_debug_error("gg",
 15.1561 @@ -1811,7 +554,7 @@
 15.1562  			/* Nothing happened. */
 15.1563  			break;
 15.1564  		case GG_EVENT_MSG:
 15.1565 -			ggp_recv_message_handler(gc, ev);
 15.1566 +			ggp_recv_message_handler(gc, &ev->event.msg, FALSE);
 15.1567  			break;
 15.1568  		case GG_EVENT_ACK:
 15.1569  			/* Changing %u to %i fixes compiler warning */
 15.1570 @@ -1821,87 +564,14 @@
 15.1571  				ev->event.ack.seq);
 15.1572  			break;
 15.1573  		case GG_EVENT_IMAGE_REPLY:
 15.1574 -			ggp_recv_image_handler(gc, ev);
 15.1575 +			ggp_image_recv(gc, &ev->event.image_reply);
 15.1576  			break;
 15.1577  		case GG_EVENT_IMAGE_REQUEST:
 15.1578 -			ggp_send_image_handler(gc, ev);
 15.1579 -			break;
 15.1580 -		case GG_EVENT_NOTIFY:
 15.1581 -		case GG_EVENT_NOTIFY_DESCR:
 15.1582 -			{
 15.1583 -				struct gg_notify_reply *n;
 15.1584 -				char *descr;
 15.1585 -
 15.1586 -				purple_debug_info("gg", "notify_pre: (%d) status: %d\n",
 15.1587 -						ev->event.notify->uin,
 15.1588 -						GG_S(ev->event.notify->status));
 15.1589 -
 15.1590 -				n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify
 15.1591 -								  : ev->event.notify_descr.notify;
 15.1592 -
 15.1593 -				for (; n->uin; n++) {
 15.1594 -					descr = (ev->type == GG_EVENT_NOTIFY) ? NULL
 15.1595 -							: ev->event.notify_descr.descr;
 15.1596 -
 15.1597 -					purple_debug_info("gg",
 15.1598 -						"notify: (%d) status: %d; descr: %s\n",
 15.1599 -						n->uin, GG_S(n->status), descr ? descr : "(null)");
 15.1600 -
 15.1601 -					ggp_generic_status_handler(gc,
 15.1602 -						n->uin, GG_S(n->status), descr);
 15.1603 -				}
 15.1604 -			}
 15.1605 +			ggp_image_send(gc, &ev->event.image_request);
 15.1606  			break;
 15.1607  		case GG_EVENT_NOTIFY60:
 15.1608 -			for (i = 0; ev->event.notify60[i].uin; i++) {
 15.1609 -				purple_debug_info("gg",
 15.1610 -					"notify60: (%d) status=%d; version=%d; descr=%s\n",
 15.1611 -					ev->event.notify60[i].uin,
 15.1612 -					GG_S(ev->event.notify60[i].status),
 15.1613 -					ev->event.notify60[i].version,
 15.1614 -					ev->event.notify60[i].descr ? ev->event.notify60[i].descr : "(null)");
 15.1615 -
 15.1616 -				ggp_generic_status_handler(gc, ev->event.notify60[i].uin,
 15.1617 -					GG_S(ev->event.notify60[i].status),
 15.1618 -					ev->event.notify60[i].descr);
 15.1619 -			}
 15.1620 -			break;
 15.1621 -		case GG_EVENT_STATUS:
 15.1622 -			purple_debug_info("gg", "status: (%d) status=%d; descr=%s\n",
 15.1623 -					ev->event.status.uin, GG_S(ev->event.status.status),
 15.1624 -					ev->event.status.descr ? ev->event.status.descr : "(null)");
 15.1625 -
 15.1626 -			ggp_generic_status_handler(gc, ev->event.status.uin,
 15.1627 -				GG_S(ev->event.status.status), ev->event.status.descr);
 15.1628 -			break;
 15.1629  		case GG_EVENT_STATUS60:
 15.1630 -			purple_debug_info("gg",
 15.1631 -				"status60: (%d) status=%d; version=%d; descr=%s\n",
 15.1632 -				ev->event.status60.uin, GG_S(ev->event.status60.status),
 15.1633 -				ev->event.status60.version,
 15.1634 -				ev->event.status60.descr ? ev->event.status60.descr : "(null)");
 15.1635 -
 15.1636 -			ggp_generic_status_handler(gc, ev->event.status60.uin,
 15.1637 -				GG_S(ev->event.status60.status), ev->event.status60.descr);
 15.1638 -			break;
 15.1639 -		case GG_EVENT_USERLIST:
 15.1640 -			if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) {
 15.1641 -				purple_debug_info("gg", "GG_USERLIST_GET_REPLY\n");
 15.1642 -				purple_notify_info(gc, NULL,
 15.1643 -					_("Buddy list downloaded"),
 15.1644 -					_("Your buddy list was downloaded from the server."));
 15.1645 -				if (ev->event.userlist.reply != NULL) {
 15.1646 -					ggp_buddylist_load(gc, ev->event.userlist.reply);
 15.1647 -				}
 15.1648 -			} else {
 15.1649 -				purple_debug_info("gg", "GG_USERLIST_PUT_REPLY\n");
 15.1650 -				purple_notify_info(gc, NULL,
 15.1651 -					_("Buddy list uploaded"),
 15.1652 -					_("Your buddy list was stored on the server."));
 15.1653 -			}
 15.1654 -			break;
 15.1655 -		case GG_EVENT_PUBDIR50_SEARCH_REPLY:
 15.1656 -			ggp_pubdir_reply_handler(gc, ev->event.pubdir50);
 15.1657 +			ggp_status_got_others(gc, ev);
 15.1658  			break;
 15.1659  		case GG_EVENT_TYPING_NOTIFICATION:
 15.1660  			ggp_typing_notification_handler(gc, ev->event.typing_notification.uin,
 15.1661 @@ -1911,6 +581,21 @@
 15.1662  			purple_debug_info("gg", "GG_EVENT_XML_EVENT\n");
 15.1663  			ggp_xml_event_handler(gc, ev->event.xml_event.data);
 15.1664  			break;
 15.1665 +		case GG_EVENT_USER_DATA:
 15.1666 +			ggp_events_user_data(gc, &ev->event.user_data);
 15.1667 +			break;
 15.1668 +		case GG_EVENT_USERLIST100_VERSION:
 15.1669 +			ggp_roster_version(gc, &ev->event.userlist100_version);
 15.1670 +			break;
 15.1671 +		case GG_EVENT_USERLIST100_REPLY:
 15.1672 +			ggp_roster_reply(gc, &ev->event.userlist100_reply);
 15.1673 +			break;
 15.1674 +		case GG_EVENT_MULTILOGON_MSG:
 15.1675 +			ggp_multilogon_msg(gc, &ev->event.multilogon_msg);
 15.1676 +			break;
 15.1677 +		case GG_EVENT_MULTILOGON_INFO:
 15.1678 +			ggp_multilogon_info(gc, &ev->event.multilogon_info);
 15.1679 +			break;
 15.1680  		default:
 15.1681  			purple_debug_error("gg",
 15.1682  				"unsupported event type=%d\n", ev->type);
 15.1683 @@ -1992,15 +677,22 @@
 15.1684  			break;
 15.1685  		case GG_EVENT_CONN_SUCCESS:
 15.1686  			{
 15.1687 -				purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS\n");
 15.1688 +				const gchar * server_ip = ggp_ipv4_to_str(
 15.1689 +					info->session->server_addr);
 15.1690 +				purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS:"
 15.1691 +					" successfully connected to %s\n",
 15.1692 +					server_ip);
 15.1693 +				ggp_servconn_add_server(server_ip);
 15.1694  				purple_input_remove(info->inpa);
 15.1695  				info->inpa = purple_input_add(info->session->fd,
 15.1696  							  PURPLE_INPUT_READ,
 15.1697  							  ggp_callback_recv, gc);
 15.1698  
 15.1699 -				ggp_buddylist_send(gc);
 15.1700  				purple_connection_update_progress(gc, _("Connected"), 1, 2);
 15.1701  				purple_connection_set_state(gc, PURPLE_CONNECTED);
 15.1702 +				
 15.1703 +				ggp_buddylist_send(gc);
 15.1704 +				ggp_roster_request_update(gc);
 15.1705  			}
 15.1706  			break;
 15.1707  		case GG_EVENT_CONN_FAILED:
 15.1708 @@ -2103,39 +795,19 @@
 15.1709  	return normalized;
 15.1710  }
 15.1711  
 15.1712 -static char *ggp_status_text(PurpleBuddy *b)
 15.1713 -{
 15.1714 -	PurpleStatus *status;
 15.1715 -	const char *msg;
 15.1716 -	char *text;
 15.1717 -	char *tmp;
 15.1718 -
 15.1719 -	status = purple_presence_get_active_status(
 15.1720 -		purple_buddy_get_presence(b));
 15.1721 -	msg = purple_status_get_attr_string(status, "message");
 15.1722 -
 15.1723 -	if (msg == NULL)
 15.1724 -		return NULL;
 15.1725 -
 15.1726 -	tmp = purple_markup_strip_html(msg);
 15.1727 -	text = g_markup_escape_text(tmp, -1);
 15.1728 -	g_free(tmp);
 15.1729 -
 15.1730 -	return text;
 15.1731 -}
 15.1732 -
 15.1733  static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 15.1734  {
 15.1735  	PurpleStatus *status;
 15.1736  	char *tmp;
 15.1737 -	const char *msg, *name, *alias;
 15.1738 +	const char *name, *alias;
 15.1739 +	gchar *msg;
 15.1740  
 15.1741  	g_return_if_fail(b != NULL);
 15.1742  
 15.1743  	status = purple_presence_get_active_status(purple_buddy_get_presence(b));
 15.1744 -	msg = purple_status_get_attr_string(status, "message");
 15.1745  	name = purple_status_get_name(status);
 15.1746  	alias = purple_buddy_get_alias(b);
 15.1747 +	ggp_status_from_purplestatus(status, &msg);
 15.1748  
 15.1749  	purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), alias);
 15.1750  
 15.1751 @@ -2147,76 +819,13 @@
 15.1752  		} else {
 15.1753  			purple_notify_user_info_add_pair_plaintext(user_info, _("Message"), msg);
 15.1754  		}
 15.1755 +		g_free(msg);
 15.1756  	/* We don't want to duplicate 'Status: Offline'. */
 15.1757  	} else if (PURPLE_BUDDY_IS_ONLINE(b)) {
 15.1758  		purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), name);
 15.1759  	}
 15.1760  }
 15.1761  
 15.1762 -static GList *ggp_status_types(PurpleAccount *account)
 15.1763 -{
 15.1764 -	PurpleStatusType *type;
 15.1765 -	GList *types = NULL;
 15.1766 -
 15.1767 -	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
 15.1768 -		NULL, NULL, TRUE, TRUE, FALSE,
 15.1769 -		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 15.1770 -		NULL);
 15.1771 -	types = g_list_append(types, type);
 15.1772 -
 15.1773 -	/*
 15.1774 -	 * New status for GG 8.0: PoGGadaj ze mna (chatty).
 15.1775 -	 * NOTE: at this time, this is used only to set our own status.
 15.1776 -	 */
 15.1777 -	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
 15.1778 -		"freeforchat", _("Chatty"), TRUE, TRUE, FALSE,
 15.1779 -		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 15.1780 -		NULL);
 15.1781 -	types = g_list_append(types, type);
 15.1782 -
 15.1783 -	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
 15.1784 -		NULL, NULL, TRUE, TRUE, FALSE,
 15.1785 -		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 15.1786 -		NULL);
 15.1787 -	types = g_list_append(types, type);
 15.1788 -
 15.1789 -	/*
 15.1790 -	 * New status for GG 8.0: Nie przeszkadzac (do not disturb).
 15.1791 -	 */
 15.1792 -	type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
 15.1793 -		NULL, NULL, TRUE, TRUE, FALSE,
 15.1794 -		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 15.1795 -		NULL);
 15.1796 -	types = g_list_append(types, type);
 15.1797 -
 15.1798 -	/*
 15.1799 -	 * It's used on buddy list if and only if it's showing our own
 15.1800 -	 * (invisible) status.
 15.1801 -	 */
 15.1802 -	type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
 15.1803 -		NULL, NULL, TRUE, TRUE, FALSE,
 15.1804 -		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 15.1805 -		NULL);
 15.1806 -	types = g_list_append(types, type);
 15.1807 -
 15.1808 -	/*
 15.1809 -	 * This status is necessary to display guys who are blocking *us*.
 15.1810 -	 */
 15.1811 -	type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
 15.1812 -		"blocked", _("Blocked"), TRUE, FALSE, FALSE,
 15.1813 -		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 15.1814 -		NULL);
 15.1815 -	types = g_list_append(types, type);
 15.1816 -
 15.1817 -	type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
 15.1818 -		NULL, NULL, TRUE, TRUE, FALSE,
 15.1819 -		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 15.1820 -		NULL);
 15.1821 -	types = g_list_append(types, type);
 15.1822 -
 15.1823 -	return types;
 15.1824 -}
 15.1825 -
 15.1826  static GList *ggp_blist_node_menu(PurpleBlistNode *node)
 15.1827  {
 15.1828  	PurpleMenuAction *act;
 15.1829 @@ -2257,18 +866,15 @@
 15.1830  
 15.1831  static void ggp_login(PurpleAccount *account)
 15.1832  {
 15.1833 -	PurpleConnection *gc;
 15.1834 -	PurplePresence *presence;
 15.1835 -	PurpleStatus *status;
 15.1836 +	PurpleConnection *gc = purple_account_get_connection(account);
 15.1837  	struct gg_login_params *glp;
 15.1838  	GGPInfo *info;
 15.1839  	const char *address;
 15.1840  	const gchar *encryption_type;
 15.1841  
 15.1842 -	if (ggp_setup_proxy(account) == -1)
 15.1843 +	if (!ggp_deprecated_setup_proxy(gc))
 15.1844  		return;
 15.1845  
 15.1846 -	gc = purple_account_get_connection(account);
 15.1847  	glp = g_new0(struct gg_login_params, 1);
 15.1848  	info = g_new0(GGPInfo, 1);
 15.1849  
 15.1850 @@ -2276,17 +882,18 @@
 15.1851  	info->session = NULL;
 15.1852  	info->chats = NULL;
 15.1853  	info->chats_count = 0;
 15.1854 -	info->token = NULL;
 15.1855 -	info->searches = ggp_search_new();
 15.1856 -	info->pending_richtext_messages = NULL;
 15.1857 -	info->pending_images = g_hash_table_new(g_direct_hash, g_direct_equal);
 15.1858 -	info->status_broadcasting = purple_account_get_bool(account, "status_broadcasting", TRUE);
 15.1859  	
 15.1860  	purple_connection_set_protocol_data(gc, info);
 15.1861  
 15.1862 -	glp->uin = ggp_get_uin(account);
 15.1863 -	glp->password = charset_convert(purple_account_get_password(account),
 15.1864 -		"UTF-8", "CP1250");
 15.1865 +
 15.1866 +	ggp_image_setup(gc);
 15.1867 +	ggp_avatar_setup(gc);
 15.1868 +	ggp_roster_setup(gc);
 15.1869 +	ggp_multilogon_setup(gc);
 15.1870 +	ggp_status_setup(gc);
 15.1871 +	
 15.1872 +	glp->uin = ggp_str_to_uin(purple_account_get_username(account));
 15.1873 +	glp->password = ggp_convert_to_cp1250(purple_account_get_password(account));
 15.1874  
 15.1875  	if (glp->uin == 0) {
 15.1876  		purple_connection_error(gc,
 15.1877 @@ -2302,15 +909,12 @@
 15.1878  	if (purple_account_get_bool(account, "show_links_from_strangers", 1))
 15.1879  		glp->status_flags |= GG_STATUS_FLAG_SPAM;
 15.1880  
 15.1881 -	presence = purple_account_get_presence(account);
 15.1882 -	status = purple_presence_get_active_status(presence);
 15.1883 -
 15.1884  	glp->encoding = GG_ENCODING_UTF8;
 15.1885 -	glp->protocol_features = (GG_FEATURE_STATUS80|GG_FEATURE_DND_FFC
 15.1886 -		|GG_FEATURE_TYPING_NOTIFICATION);
 15.1887 +	glp->protocol_features = (GG_FEATURE_DND_FFC |
 15.1888 +		GG_FEATURE_TYPING_NOTIFICATION | GG_FEATURE_MULTILOGON |
 15.1889 +		GG_FEATURE_USER_DATA);
 15.1890  
 15.1891  	glp->async = 1;
 15.1892 -	glp->status = ggp_to_gg_status(status, &glp->status_descr);
 15.1893  	
 15.1894  	encryption_type = purple_account_get_string(account, "encryption",
 15.1895  		"opportunistic_tls");
 15.1896 @@ -2333,28 +937,22 @@
 15.1897  		glp->tls = GG_SSL_DISABLED;
 15.1898  	purple_debug_info("gg", "TLS mode: %d\n", glp->tls);
 15.1899  
 15.1900 -	if (!info->status_broadcasting)
 15.1901 -		glp->status = glp->status|GG_STATUS_FRIENDS_MASK;
 15.1902 +	ggp_status_set_initial(gc, glp);
 15.1903  	
 15.1904  	address = purple_account_get_string(account, "gg_server", "");
 15.1905 -	if (address && *address) {
 15.1906 -		/* TODO: Make this non-blocking */
 15.1907 -		struct in_addr *addr = gg_gethostbyname(address);
 15.1908 -
 15.1909 -		purple_debug_info("gg", "Using gg server given by user (%s)\n", address);
 15.1910 -
 15.1911 -		if (addr == NULL) {
 15.1912 -			gchar *tmp = g_strdup_printf(_("Unable to resolve hostname '%s': %s"),
 15.1913 -					address, g_strerror(errno));
 15.1914 +	if (address && *address)
 15.1915 +	{
 15.1916 +		glp->server_addr = inet_addr(address);
 15.1917 +		glp->server_port = 8074;
 15.1918 +		
 15.1919 +		if (glp->server_addr == INADDR_NONE)
 15.1920 +		{
 15.1921  			purple_connection_error(gc,
 15.1922 -				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, /* should this be a settings error? */
 15.1923 -				tmp);
 15.1924 -			g_free(tmp);
 15.1925 +				PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
 15.1926 +				_("Provided server IP address is not valid"));
 15.1927 +			g_free(glp);
 15.1928  			return;
 15.1929  		}
 15.1930 -
 15.1931 -		glp->server_addr = inet_addr(inet_ntoa(*addr));
 15.1932 -		glp->server_port = 8074;
 15.1933  	} else
 15.1934  		purple_debug_info("gg", "Trying to retrieve address from gg appmsg service\n");
 15.1935  
 15.1936 @@ -2385,24 +983,23 @@
 15.1937  	info = purple_connection_get_protocol_data(gc);
 15.1938  
 15.1939  	if (info) {
 15.1940 -		PurpleStatus *status = purple_account_get_active_status(account);
 15.1941 -
 15.1942 -		if (info->session != NULL) {
 15.1943 -			ggp_set_status(account, status);
 15.1944 +		if (info->session != NULL)
 15.1945 +		{
 15.1946 +			ggp_status_set_disconnected(account);
 15.1947  			gg_logoff(info->session);
 15.1948  			gg_free_session(info->session);
 15.1949  		}
 15.1950  
 15.1951 -		purple_account_set_bool(account, "status_broadcasting", info->status_broadcasting);
 15.1952 -
 15.1953  		/* Immediately close any notifications on this handle since that process depends
 15.1954  		 * upon the contents of info->searches, which we are about to destroy.
 15.1955  		 */
 15.1956  		purple_notify_close_with_handle(gc);
 15.1957  
 15.1958 -		ggp_search_destroy(info->searches);
 15.1959 -		g_list_free(info->pending_richtext_messages);
 15.1960 -		g_hash_table_destroy(info->pending_images);
 15.1961 +		ggp_image_cleanup(gc);
 15.1962 +		ggp_avatar_cleanup(gc);
 15.1963 +		ggp_roster_cleanup(gc);
 15.1964 +		ggp_multilogon_cleanup(gc);
 15.1965 +		ggp_status_cleanup(gc);
 15.1966  
 15.1967  		if (info->inpa > 0)
 15.1968  			purple_input_remove(info->inpa);
 15.1969 @@ -2425,11 +1022,16 @@
 15.1970  	gint pos = 0;
 15.1971  	GData *attribs;
 15.1972  	const char *start, *end = NULL, *last;
 15.1973 +	ggp_buddy_data *buddy_data = ggp_buddy_get_data(
 15.1974 +		purple_find_buddy(purple_connection_get_account(gc), who));
 15.1975  
 15.1976  	if (msg == NULL || *msg == '\0') {
 15.1977  		return 0;
 15.1978  	}
 15.1979  
 15.1980 +	if (buddy_data->blocked)
 15.1981 +		return -1;
 15.1982 +
 15.1983  	last = msg;
 15.1984  
 15.1985  	/* Check if the message is richtext */
 15.1986 @@ -2439,53 +1041,58 @@
 15.1987  		GString *string_buffer = g_string_new(NULL);
 15.1988  		struct gg_msg_richtext fmt;
 15.1989  
 15.1990 -		do {
 15.1991 -			PurpleStoredImage *image;
 15.1992 -			const char *id;
 15.1993 +		do
 15.1994 +		{
 15.1995 +			const char *id = g_datalist_get_data(&attribs, "id");
 15.1996 +			struct gg_msg_richtext_format actformat;
 15.1997 +			struct gg_msg_richtext_image actimage;
 15.1998 +			ggp_image_prepare_result prepare_result;
 15.1999  
 15.2000  			/* Add text before the image */
 15.2001 -			if(start - last) {
 15.2002 +			if(start - last)
 15.2003 +			{
 15.2004  				pos = pos + g_utf8_strlen(last, start - last);
 15.2005 -				g_string_append_len(string_buffer, last, start - last);
 15.2006 +				g_string_append_len(string_buffer, last,
 15.2007 +					start - last);
 15.2008 +			}
 15.2009 +			last = end + 1;
 15.2010 +			
 15.2011 +			if (id == NULL)
 15.2012 +			{
 15.2013 +				g_datalist_clear(&attribs);
 15.2014 +				continue;
 15.2015  			}
 15.2016  
 15.2017 -			if((id = g_datalist_get_data(&attribs, "id")) && (image = purple_imgstore_find_by_id(atoi(id)))) {
 15.2018 -				struct gg_msg_richtext_format actformat;
 15.2019 -				struct gg_msg_richtext_image actimage;
 15.2020 -				gint image_size = purple_imgstore_get_size(image);
 15.2021 -				gconstpointer image_bin = purple_imgstore_get_data(image);
 15.2022 -				const char *image_filename = purple_imgstore_get_filename(image);
 15.2023 -				uint32_t crc32 = gg_crc32(0, image_bin, image_size);
 15.2024 -
 15.2025 -				g_hash_table_insert(info->pending_images, GINT_TO_POINTER(crc32), GINT_TO_POINTER(atoi(id)));
 15.2026 -				purple_imgstore_ref(image);
 15.2027 -				purple_debug_info("gg", "ggp_send_im_richtext: got crc: %u for imgid: %i\n", crc32, atoi(id));
 15.2028 -
 15.2029 +			/* add the image itself */
 15.2030 +			prepare_result = ggp_image_prepare(
 15.2031 +				gc, atoi(id), who, &actimage);
 15.2032 +			if (prepare_result == GGP_IMAGE_PREPARE_OK)
 15.2033 +			{
 15.2034  				actformat.font = GG_FONT_IMAGE;
 15.2035  				actformat.position = pos;
 15.2036  
 15.2037 -				actimage.unknown1 = 0x0109;
 15.2038 -				actimage.size = gg_fix32(image_size);
 15.2039 -				actimage.crc32 = gg_fix32(crc32);
 15.2040 -
 15.2041 -				if (actimage.size > 255000) {
 15.2042 -					purple_debug_warning("gg", "ggp_send_im_richtext: image over 255kb!\n");
 15.2043 -				} else {
 15.2044 -					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);
 15.2045 -
 15.2046 -					memcpy(format + format_length, &actformat, sizeof(actformat));
 15.2047 -					format_length += sizeof(actformat);
 15.2048 -					memcpy(format + format_length, &actimage, sizeof(actimage));
 15.2049 -					format_length += sizeof(actimage);
 15.2050 -				}
 15.2051 -			} else {
 15.2052 -				purple_debug_error("gg", "ggp_send_im_richtext: image not found in the image store!");
 15.2053 +				memcpy(format + format_length, &actformat,
 15.2054 +					sizeof(actformat));
 15.2055 +				format_length += sizeof(actformat);
 15.2056 +				memcpy(format + format_length, &actimage,
 15.2057 +					sizeof(actimage));
 15.2058 +				format_length += sizeof(actimage);
 15.2059  			}
 15.2060 -
 15.2061 -			last = end + 1;
 15.2062 +			else if (prepare_result == GGP_IMAGE_PREPARE_TOO_BIG)
 15.2063 +			{
 15.2064 +				PurpleConversation *conv =
 15.2065 +					purple_find_conversation_with_account(
 15.2066 +						PURPLE_CONV_TYPE_IM, who,
 15.2067 +						purple_connection_get_account(gc));
 15.2068 +				purple_conversation_write(conv, "",
 15.2069 +					_("Image is too large, please try "
 15.2070 +					"smaller one."), PURPLE_MESSAGE_ERROR,
 15.2071 +					time(NULL));
 15.2072 +			}
 15.2073 +			
 15.2074  			g_datalist_clear(&attribs);
 15.2075 -
 15.2076 -		} while(purple_markup_find_tag("img", last, &start, &end, &attribs));
 15.2077 +		} while (purple_markup_find_tag("img", last, &start, &end,
 15.2078 +			&attribs));
 15.2079  
 15.2080  		/* Add text after the images */
 15.2081  		if(last && *last) {
 15.2082 @@ -2505,10 +1112,7 @@
 15.2083  		plain = purple_unescape_html(msg);
 15.2084  	}
 15.2085  
 15.2086 -	/*
 15.2087 -	tmp = charset_convert(plain, "UTF-8", "CP1250");
 15.2088 -	*/
 15.2089 -	tmp = g_strdup_printf("%s", plain);
 15.2090 +	tmp = g_strdup(plain);
 15.2091  
 15.2092  	if (tmp && (format_length - sizeof(struct gg_msg_richtext))) {
 15.2093  		if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) {
 15.2094 @@ -2554,117 +1158,20 @@
 15.2095  	return 1; // wait 1 second before another notification
 15.2096  }
 15.2097  
 15.2098 -static void ggp_get_info(PurpleConnection *gc, const char *name)
 15.2099 -{
 15.2100 -	GGPInfo *info = purple_connection_get_protocol_data(gc);
 15.2101 -	GGPSearchForm *form;
 15.2102 -	guint32 seq;
 15.2103 -
 15.2104 -	form = ggp_search_form_new(GGP_SEARCH_TYPE_INFO);
 15.2105 -
 15.2106 -	form->user_data = info;
 15.2107 -	form->uin = g_strdup(name);
 15.2108 -
 15.2109 -	seq = ggp_search_start(gc, form);
 15.2110 -	ggp_search_add(info->searches, seq, form);
 15.2111 -	purple_debug_info("gg", "ggp_get_info(): Added seq %u", seq);
 15.2112 -}
 15.2113 -
 15.2114 -static int ggp_to_gg_status(PurpleStatus *status, char **msg)
 15.2115 -{
 15.2116 -	const char *status_id = purple_status_get_id(status);
 15.2117 -	int new_status, new_status_descr;
 15.2118 -	const char *new_msg;
 15.2119 -
 15.2120 -	g_return_val_if_fail(msg != NULL, 0);
 15.2121 -
 15.2122 -	purple_debug_info("gg", "ggp_to_gg_status: Requested status = %s\n",
 15.2123 -			status_id);
 15.2124 -
 15.2125 -	if (strcmp(status_id, "available") == 0) {
 15.2126 -		new_status = GG_STATUS_AVAIL;
 15.2127 -		new_status_descr = GG_STATUS_AVAIL_DESCR;
 15.2128 -	} else if (strcmp(status_id, "freeforchat") == 0) {
 15.2129 -		new_status = GG_STATUS_FFC;
 15.2130 -		new_status_descr = GG_STATUS_FFC_DESCR;
 15.2131 -	} else if (strcmp(status_id, "away") == 0) {
 15.2132 -		new_status = GG_STATUS_BUSY;
 15.2133 -		new_status_descr = GG_STATUS_BUSY_DESCR;
 15.2134 -	} else if (strcmp(status_id, "unavailable") == 0) {
 15.2135 -		new_status = GG_STATUS_DND;
 15.2136 -		new_status_descr = GG_STATUS_DND_DESCR;
 15.2137 -	} else if (strcmp(status_id, "invisible") == 0) {
 15.2138 -		new_status = GG_STATUS_INVISIBLE;
 15.2139 -		new_status_descr = GG_STATUS_INVISIBLE_DESCR;
 15.2140 -	} else if (strcmp(status_id, "offline") == 0) {
 15.2141 -		new_status = GG_STATUS_NOT_AVAIL;
 15.2142 -		new_status_descr = GG_STATUS_NOT_AVAIL_DESCR;
 15.2143 -	} else {
 15.2144 -		new_status = GG_STATUS_AVAIL;
 15.2145 -		new_status_descr = GG_STATUS_AVAIL_DESCR;
 15.2146 -		purple_debug_info("gg",
 15.2147 -			"ggp_set_status: unknown status requested (status_id=%s)\n",
 15.2148 -			status_id);
 15.2149 -	}
 15.2150 -
 15.2151 -	new_msg = purple_status_get_attr_string(status, "message");
 15.2152 -
 15.2153 -	if(new_msg) {
 15.2154 -		/*
 15.2155 -		char *tmp = purple_markup_strip_html(new_msg);
 15.2156 -		*msg = charset_convert(tmp, "UTF-8", "CP1250");
 15.2157 -		g_free(tmp);
 15.2158 -		*/
 15.2159 -		*msg = purple_markup_strip_html(new_msg);
 15.2160 -
 15.2161 -		return new_status_descr;
 15.2162 -	} else {
 15.2163 -		*msg = NULL;
 15.2164 -		return new_status;
 15.2165 -	}
 15.2166 -}
 15.2167 -
 15.2168 -static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
 15.2169 -{
 15.2170 -	PurpleConnection *gc;
 15.2171 -	GGPInfo *info;
 15.2172 -	int new_status;
 15.2173 -	char *new_msg = NULL;
 15.2174 -
 15.2175 -	if (!purple_status_is_active(status))
 15.2176 -		return;
 15.2177 -
 15.2178 -	gc = purple_account_get_connection(account);
 15.2179 -	info = purple_connection_get_protocol_data(gc);
 15.2180 -
 15.2181 -	new_status = ggp_to_gg_status(status, &new_msg);
 15.2182 -
 15.2183 -	if (!info->status_broadcasting)
 15.2184 -		new_status = new_status|GG_STATUS_FRIENDS_MASK;
 15.2185 -	
 15.2186 -	if (new_msg == NULL) {
 15.2187 -		gg_change_status(info->session, new_status);
 15.2188 -	} else {
 15.2189 -		gg_change_status_descr(info->session, new_status, new_msg);
 15.2190 -		g_free(new_msg);
 15.2191 -	}
 15.2192 -
 15.2193 -	ggp_status_fake_to_self(account);
 15.2194 -
 15.2195 -}
 15.2196 -
 15.2197  static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
 15.2198  {
 15.2199 -	PurpleAccount *account;
 15.2200 +	PurpleAccount *account = purple_connection_get_account(gc);
 15.2201  	GGPInfo *info = purple_connection_get_protocol_data(gc);
 15.2202  	const gchar *name = purple_buddy_get_name(buddy);
 15.2203  
 15.2204  	gg_add_notify(info->session, ggp_str_to_uin(name));
 15.2205  
 15.2206 -	account = purple_connection_get_account(gc);
 15.2207 -	if (strcmp(purple_account_get_username(account), name) == 0) {
 15.2208 -		ggp_status_fake_to_self(account);
 15.2209 -	}
 15.2210 +	// gg server won't tell us our status here
 15.2211 +	if (strcmp(purple_account_get_username(account), name) == 0)
 15.2212 +		ggp_status_fake_to_self(gc);
 15.2213 +	
 15.2214 +	ggp_roster_add_buddy(gc, buddy, group, message);
 15.2215 +	ggp_pubdir_request_buddy_alias(gc, buddy);
 15.2216  }
 15.2217  
 15.2218  static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
 15.2219 @@ -2673,6 +1180,7 @@
 15.2220  	GGPInfo *info = purple_connection_get_protocol_data(gc);
 15.2221  
 15.2222  	gg_remove_notify(info->session, ggp_str_to_uin(purple_buddy_get_name(buddy)));
 15.2223 +	ggp_roster_remove_buddy(gc, buddy, group);
 15.2224  }
 15.2225  
 15.2226  static void ggp_join_chat(PurpleConnection *gc, GHashTable *data)
 15.2227 @@ -2750,11 +1258,6 @@
 15.2228  		uins[count++] = uin;
 15.2229  	}
 15.2230  
 15.2231 -	/*
 15.2232 -	plain = purple_unescape_html(message);
 15.2233 -	msg = charset_convert(plain, "UTF-8", "CP1250");
 15.2234 -	g_free(plain);
 15.2235 -	*/
 15.2236  	msg = purple_unescape_html(message);
 15.2237  	gg_send_message_confer(info->session, GG_CLASS_CHAT, count, uins,
 15.2238  				(unsigned char *)msg);
 15.2239 @@ -2783,11 +1286,24 @@
 15.2240  	}
 15.2241  }
 15.2242  
 15.2243 -static void ggp_register_user(PurpleAccount *account)
 15.2244 +static void ggp_action_chpass(PurplePluginAction *action)
 15.2245  {
 15.2246 -	PurpleConnection *gc = purple_account_get_connection(account);
 15.2247 +	ggp_account_chpass((PurpleConnection *)action->context);
 15.2248 +}
 15.2249  
 15.2250 -	ggp_token_request(gc, ggp_register_user_dialog);
 15.2251 +static void ggp_action_status_broadcasting(PurplePluginAction *action)
 15.2252 +{
 15.2253 +	ggp_status_broadcasting_dialog((PurpleConnection *)action->context);
 15.2254 +}
 15.2255 +
 15.2256 +static void ggp_action_search(PurplePluginAction *action)
 15.2257 +{
 15.2258 +	ggp_pubdir_search((PurpleConnection *)action->context, NULL);
 15.2259 +}
 15.2260 +
 15.2261 +static void ggp_action_set_info(PurplePluginAction *action)
 15.2262 +{
 15.2263 +	ggp_pubdir_set_info((PurpleConnection *)action->context);
 15.2264  }
 15.2265  
 15.2266  static GList *ggp_actions(PurplePlugin *plugin, gpointer context)
 15.2267 @@ -2796,30 +1312,24 @@
 15.2268  	PurplePluginAction *act;
 15.2269  
 15.2270  	act = purple_plugin_action_new(_("Change password..."),
 15.2271 -				     ggp_change_passwd);
 15.2272 +		ggp_action_chpass);
 15.2273  	m = g_list_append(m, act);
 15.2274  
 15.2275 -	act = purple_plugin_action_new(_("Find buddies..."),
 15.2276 -				     ggp_find_buddies);
 15.2277 -	m = g_list_append(m, act);
 15.2278 -
 15.2279 -	act = purple_plugin_action_new(_("Change status broadcasting"),
 15.2280 -				     ggp_action_change_status_broadcasting);
 15.2281 +	act = purple_plugin_action_new(_("Show status only for buddies"),
 15.2282 +		ggp_action_status_broadcasting);
 15.2283  	m = g_list_append(m, act);
 15.2284  
 15.2285  	m = g_list_append(m, NULL);
 15.2286  
 15.2287 -	act = purple_plugin_action_new(_("Upload buddylist to Server"),
 15.2288 -				     ggp_action_buddylist_put);
 15.2289 +	act = purple_plugin_action_new(_("Find buddies..."),
 15.2290 +		ggp_action_search);
 15.2291  	m = g_list_append(m, act);
 15.2292  
 15.2293 -	act = purple_plugin_action_new(_("Download buddylist from Server"),
 15.2294 -				     ggp_action_buddylist_get);
 15.2295 +	act = purple_plugin_action_new(_("Set User Info"),
 15.2296 +		ggp_action_set_info);
 15.2297  	m = g_list_append(m, act);
 15.2298  
 15.2299 -	act = purple_plugin_action_new(_("Delete buddylist from Server"),
 15.2300 -				     ggp_action_buddylist_delete);
 15.2301 -	m = g_list_append(m, act);
 15.2302 +	m = g_list_append(m, NULL);
 15.2303  
 15.2304  	act = purple_plugin_action_new(_("Save buddylist to file..."),
 15.2305  				     ggp_action_buddylist_save);
 15.2306 @@ -2832,21 +1342,39 @@
 15.2307  	return m;
 15.2308  }
 15.2309  
 15.2310 +static const char* ggp_list_emblem(PurpleBuddy *buddy)
 15.2311 +{
 15.2312 +	ggp_buddy_data *buddy_data = ggp_buddy_get_data(buddy);
 15.2313 +
 15.2314 +	if (buddy_data->blocked)
 15.2315 +		return "not-authorized";
 15.2316 +
 15.2317 +	return NULL;
 15.2318 +}
 15.2319 +
 15.2320  static gboolean ggp_offline_message(const PurpleBuddy *buddy)
 15.2321  {
 15.2322  	return TRUE;
 15.2323  }
 15.2324  
 15.2325 +static GHashTable * ggp_get_account_text_table(PurpleAccount *account)
 15.2326 +{
 15.2327 +	GHashTable *table;
 15.2328 +	table = g_hash_table_new(g_str_hash, g_str_equal);
 15.2329 +	g_hash_table_insert(table, "login_label", (gpointer)_("GG number..."));
 15.2330 +	return table;
 15.2331 +}
 15.2332 +
 15.2333  static PurplePluginProtocolInfo prpl_info =
 15.2334  {
 15.2335  	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 15.2336  	OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_IM_IMAGE,
 15.2337  	NULL,				/* user_splits */
 15.2338  	NULL,				/* protocol_options */
 15.2339 -	{"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY},	/* icon_spec */
 15.2340 +	{"png", 1, 1, 200, 200, 0, PURPLE_ICON_SCALE_DISPLAY | PURPLE_ICON_SCALE_SEND},	/* icon_spec */
 15.2341  	ggp_list_icon,			/* list_icon */
 15.2342 -	NULL,				/* list_emblem */
 15.2343 -	ggp_status_text,		/* status_text */
 15.2344 +	ggp_list_emblem,		/* list_emblem */
 15.2345 +	ggp_status_buddy_text,		/* status_text */
 15.2346  	ggp_tooltip_text,		/* tooltip_text */
 15.2347  	ggp_status_types,		/* status_types */
 15.2348  	ggp_blist_node_menu,		/* blist_node_menu */
 15.2349 @@ -2857,8 +1385,8 @@
 15.2350  	ggp_send_im,			/* send_im */
 15.2351  	NULL,				/* set_info */
 15.2352  	ggp_send_typing,		/* send_typing */
 15.2353 -	ggp_get_info,			/* get_info */
 15.2354 -	ggp_set_status,			/* set_away */
 15.2355 +	ggp_pubdir_get_info_prpl,	/* get_info */
 15.2356 +	ggp_status_set_purplestatus,	/* set_away */
 15.2357  	NULL,				/* set_idle */
 15.2358  	NULL,				/* change_passwd */
 15.2359  	ggp_add_buddy,			/* add_buddy */
 15.2360 @@ -2878,15 +1406,15 @@
 15.2361  	NULL,				/* chat_whisper */
 15.2362  	ggp_chat_send,			/* chat_send */
 15.2363  	ggp_keepalive,			/* keepalive */
 15.2364 -	ggp_register_user,		/* register_user */
 15.2365 +	ggp_account_register,		/* register_user */
 15.2366  	NULL,				/* get_cb_info */
 15.2367 -	NULL,				/* alias_buddy */
 15.2368 -	NULL,				/* group_buddy */
 15.2369 -	NULL,				/* rename_group */
 15.2370 -	NULL,				/* buddy_free */
 15.2371 +	ggp_roster_alias_buddy,		/* alias_buddy */
 15.2372 +	ggp_roster_group_buddy,		/* group_buddy */
 15.2373 +	ggp_roster_rename_group,	/* rename_group */
 15.2374 +	ggp_buddy_free,			/* buddy_free */
 15.2375  	NULL,				/* convo_closed */
 15.2376  	ggp_normalize,			/* normalize */
 15.2377 -	NULL,				/* set_buddy_icon */
 15.2378 +	ggp_avatar_own_set,		/* set_buddy_icon */
 15.2379  	NULL,				/* remove_group */
 15.2380  	NULL,				/* get_cb_real_name */
 15.2381  	NULL,				/* set_chat_topic */
 15.2382 @@ -2904,14 +1432,17 @@
 15.2383  	NULL,				/* unregister_user */
 15.2384  	NULL,				/* send_attention */
 15.2385  	NULL,				/* get_attention_types */
 15.2386 -	NULL,                           /* get_account_text_table */
 15.2387 -	NULL,                           /* initiate_media */
 15.2388 -	NULL,                            /* can_do_media */
 15.2389 +	ggp_get_account_text_table,	/* get_account_text_table */
 15.2390 +	NULL,				/* initiate_media */
 15.2391 +	NULL,				/* can_do_media */
 15.2392  	NULL,				/* get_moods */
 15.2393  	NULL,				/* set_public_alias */
 15.2394  	NULL				/* get_public_alias */
 15.2395  };
 15.2396  
 15.2397 +static gboolean ggp_load(PurplePlugin *plugin);
 15.2398 +static gboolean ggp_unload(PurplePlugin *plugin);
 15.2399 +
 15.2400  static PurplePluginInfo info = {
 15.2401  	PURPLE_PLUGIN_MAGIC,			/* magic */
 15.2402  	PURPLE_MAJOR_VERSION,			/* major_version */
 15.2403 @@ -2931,8 +1462,8 @@
 15.2404  	"boler@sourceforge.net",		/* author */
 15.2405  	PURPLE_WEBSITE,				/* homepage */
 15.2406  
 15.2407 -	NULL,					/* load */
 15.2408 -	NULL,					/* unload */
 15.2409 +	ggp_load,				/* load */
 15.2410 +	ggp_unload,				/* unload */
 15.2411  	NULL,					/* destroy */
 15.2412  
 15.2413  	NULL,					/* ui_info */
 15.2414 @@ -2970,15 +1501,20 @@
 15.2415  	g_free(msg);
 15.2416  }
 15.2417  
 15.2418 +static PurpleAccountOption *ggp_server_option;
 15.2419 +
 15.2420  static void init_plugin(PurplePlugin *plugin)
 15.2421  {
 15.2422  	PurpleAccountOption *option;
 15.2423  	GList *encryption_options = NULL;
 15.2424  
 15.2425 +	purple_prefs_add_none("/plugins/prpl/gg");
 15.2426 +
 15.2427  	option = purple_account_option_string_new(_("GG server"),
 15.2428  			"gg_server", "");
 15.2429  	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 15.2430  			option);
 15.2431 +	ggp_server_option = option;
 15.2432  
 15.2433  #define ADD_VALUE(list, desc, v) { \
 15.2434  	PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \
 15.2435 @@ -3002,14 +1538,24 @@
 15.2436  	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 15.2437  		option);
 15.2438  
 15.2439 -	my_protocol = plugin;
 15.2440 +	gg_debug_handler = purple_gg_debug_handler;
 15.2441 +}
 15.2442  
 15.2443 -	gg_debug_handler = purple_gg_debug_handler;
 15.2444 +static gboolean ggp_load(PurplePlugin *plugin)
 15.2445 +{
 15.2446 +	purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with "
 15.2447 +		"libgadu %s...\n", gg_libgadu_version());
 15.2448 +
 15.2449 +	ggp_resolver_purple_setup();
 15.2450 +	ggp_servconn_setup(ggp_server_option);
 15.2451  	
 15.2452 -#ifdef _WIN32
 15.2453 -	gg_global_set_custom_resolver(ggp_resolver_win32thread_start,
 15.2454 -		ggp_resolver_win32thread_cleanup);
 15.2455 -#endif
 15.2456 +	return TRUE;
 15.2457 +}
 15.2458 +
 15.2459 +static gboolean ggp_unload(PurplePlugin *plugin)
 15.2460 +{
 15.2461 +	ggp_servconn_cleanup();
 15.2462 +	return TRUE;
 15.2463  }
 15.2464  
 15.2465  PURPLE_INIT_PLUGIN(gg, init_plugin, info);
    16.1 --- a/libpurple/protocols/gg/gg.h
    16.2 +++ b/libpurple/protocols/gg/gg.h
    16.3 @@ -24,15 +24,22 @@
    16.4  #ifndef _PURPLE_GG_H
    16.5  #define _PURPLE_GG_H
    16.6  
    16.7 -#undef printf
    16.8  #include <libgadu.h>
    16.9  #include "internal.h"
   16.10  #include "search.h"
   16.11  #include "connection.h"
   16.12  
   16.13 +#include "image.h"
   16.14 +#include "avatar.h"
   16.15 +#include "account.h"
   16.16 +#include "roster.h"
   16.17 +#include "multilogon.h"
   16.18 +#include "status.h"
   16.19  
   16.20  #define PUBDIR_RESULTS_MAX 20
   16.21  
   16.22 +#define GGP_UIN_LEN_MAX 10
   16.23 +
   16.24  
   16.25  typedef struct
   16.26  {
   16.27 @@ -41,34 +48,27 @@
   16.28  
   16.29  } GGPChat;
   16.30  
   16.31 -typedef void (*GGPTokenCallback)(PurpleConnection *);
   16.32 -
   16.33 -typedef struct
   16.34 -{
   16.35 -	char *id;
   16.36 -	char *data;
   16.37 -	unsigned int size;
   16.38 -
   16.39 -	struct gg_http *req;
   16.40 -	guint inpa;
   16.41 -
   16.42 -	GGPTokenCallback cb;
   16.43 -
   16.44 -} GGPToken;
   16.45 -
   16.46  typedef struct {
   16.47  
   16.48  	struct gg_session *session;
   16.49  	guint inpa;
   16.50 -	GGPToken *token;
   16.51  	GList *chats;
   16.52 -	GGPSearches *searches;
   16.53  	int chats_count;
   16.54 -	GList *pending_richtext_messages;
   16.55 -	GHashTable *pending_images;
   16.56 -	gboolean status_broadcasting; //When TRUE status is visible to all, when FALSE status is visible only to friends.
   16.57 +
   16.58 +	ggp_image_connection_data image_data;
   16.59 +	ggp_avatar_session_data avatar_data;
   16.60 +	ggp_roster_session_data roster_data;
   16.61 +	ggp_multilogon_session_data *multilogon_data;
   16.62 +	ggp_status_session_data *status_data;
   16.63  } GGPInfo;
   16.64  
   16.65 +typedef struct
   16.66 +{
   16.67 +	gboolean blocked;
   16.68 +} ggp_buddy_data;
   16.69 +
   16.70 +void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event_msg *ev, gboolean multilogon);
   16.71 +
   16.72 +ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy);
   16.73 +
   16.74  #endif /* _PURPLE_GG_H */
   16.75 -
   16.76 -/* vim: set ts=8 sts=0 sw=8 noet: */
    17.1 new file mode 100644
    17.2 --- /dev/null
    17.3 +++ b/libpurple/protocols/gg/image.c
    17.4 @@ -0,0 +1,294 @@
    17.5 +/* purple
    17.6 + *
    17.7 + * Purple is the legal property of its developers, whose names are too numerous
    17.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    17.9 + * source distribution.
   17.10 + *
   17.11 + * Rewritten from scratch during Google Summer of Code 2012
   17.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   17.13 + *
   17.14 + * Previously implemented by:
   17.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   17.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   17.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   17.18 + *
   17.19 + * This program is free software; you can redistribute it and/or modify
   17.20 + * it under the terms of the GNU General Public License as published by
   17.21 + * the Free Software Foundation; either version 2 of the License, or
   17.22 + * (at your option) any later version.
   17.23 + *
   17.24 + * This program is distributed in the hope that it will be useful,
   17.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   17.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17.27 + * GNU General Public License for more details.
   17.28 + *
   17.29 + * You should have received a copy of the GNU General Public License
   17.30 + * along with this program; if not, write to the Free Software
   17.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   17.32 + */
   17.33 +
   17.34 +#include "image.h"
   17.35 +
   17.36 +#include <debug.h>
   17.37 +
   17.38 +#include "gg.h"
   17.39 +#include "utils.h"
   17.40 +
   17.41 +#define GGP_PENDING_IMAGE_ID_PREFIX "gg-pending-image-"
   17.42 +
   17.43 +typedef struct
   17.44 +{
   17.45 +	uin_t from;
   17.46 +	gchar *text;
   17.47 +	time_t mtime;
   17.48 +} ggp_image_pending_message;
   17.49 +
   17.50 +typedef struct
   17.51 +{
   17.52 +	int id;
   17.53 +	gchar *conv_name;
   17.54 +} ggp_image_pending_image;
   17.55 +
   17.56 +static void ggp_image_pending_message_free(gpointer data)
   17.57 +{
   17.58 +	ggp_image_pending_message *pending_message =
   17.59 +		(ggp_image_pending_message*)data;
   17.60 +	g_free(pending_message->text);
   17.61 +	g_free(pending_message);
   17.62 +}
   17.63 +
   17.64 +static void ggp_image_pending_image_free(gpointer data)
   17.65 +{
   17.66 +	ggp_image_pending_image *pending_image =
   17.67 +		(ggp_image_pending_image*)data;
   17.68 +	g_free(pending_image->conv_name);
   17.69 +	g_free(pending_image);
   17.70 +}
   17.71 +
   17.72 +static inline ggp_image_connection_data *
   17.73 +ggp_image_get_imgdata(PurpleConnection *gc)
   17.74 +{
   17.75 +	GGPInfo *accdata = purple_connection_get_protocol_data(gc);
   17.76 +	return &accdata->image_data;
   17.77 +}
   17.78 +
   17.79 +void ggp_image_setup(PurpleConnection *gc)
   17.80 +{
   17.81 +	ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc);
   17.82 +	
   17.83 +	imgdata->pending_messages = NULL;
   17.84 +	imgdata->pending_images = g_hash_table_new_full(NULL, NULL, NULL,
   17.85 +		ggp_image_pending_image_free);
   17.86 +}
   17.87 +
   17.88 +void ggp_image_cleanup(PurpleConnection *gc)
   17.89 +{
   17.90 +	ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc);
   17.91 +	
   17.92 +	g_list_free_full(imgdata->pending_messages,
   17.93 +		&ggp_image_pending_message_free);
   17.94 +	g_hash_table_destroy(imgdata->pending_images);
   17.95 +}
   17.96 +
   17.97 +const char * ggp_image_pending_placeholder(uint32_t id)
   17.98 +{
   17.99 +	static char buff[50];
  17.100 +	
  17.101 +	g_snprintf(buff, 50, "<img id=\"" GGP_PENDING_IMAGE_ID_PREFIX
  17.102 +		"%u\">", id);
  17.103 +	
  17.104 +	return buff;
  17.105 +}
  17.106 +
  17.107 +void ggp_image_got_im(PurpleConnection *gc, uin_t from, gchar *text,
  17.108 +	time_t mtime)
  17.109 +{
  17.110 +	ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc);
  17.111 +	ggp_image_pending_message *pending_message =
  17.112 +		g_new(ggp_image_pending_message, 1);
  17.113 +	
  17.114 +	purple_debug_info("gg", "ggp_image_got_im: received message with "
  17.115 +		"images from %u: %s\n", from, text);
  17.116 +	
  17.117 +	pending_message->from = from;
  17.118 +	pending_message->text = text;
  17.119 +	pending_message->mtime = mtime;
  17.120 +	
  17.121 +	imgdata->pending_messages = g_list_append(imgdata->pending_messages,
  17.122 +		pending_message);
  17.123 +}
  17.124 +
  17.125 +ggp_image_prepare_result ggp_image_prepare(PurpleConnection *gc, const int id,
  17.126 +	const char *conv_name, struct gg_msg_richtext_image *image_info)
  17.127 +{
  17.128 +	ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc);
  17.129 +	PurpleStoredImage *image = purple_imgstore_find_by_id(id);
  17.130 +	size_t image_size;
  17.131 +	gconstpointer image_data;
  17.132 +	const char *image_filename;
  17.133 +	uint32_t image_crc;
  17.134 +	ggp_image_pending_image *pending_image;
  17.135 +	
  17.136 +	if (!image)
  17.137 +	{
  17.138 +		purple_debug_error("gg", "ggp_image_prepare_to_send: image %d "
  17.139 +			"not found in image store\n", id);
  17.140 +		return GGP_IMAGE_PREPARE_FAILURE;
  17.141 +	}
  17.142 +	
  17.143 +	image_size = purple_imgstore_get_size(image);
  17.144 +	
  17.145 +	if (image_size > GGP_IMAGE_SIZE_MAX)
  17.146 +	{
  17.147 +		purple_debug_warning("gg", "ggp_image_prepare_to_send: image "
  17.148 +			"is too big (max bytes: %d)\n", GGP_IMAGE_SIZE_MAX);
  17.149 +		return GGP_IMAGE_PREPARE_TOO_BIG;
  17.150 +	}
  17.151 +	
  17.152 +	purple_imgstore_ref(image);
  17.153 +	image_data = purple_imgstore_get_data(image);
  17.154 +	image_filename = purple_imgstore_get_filename(image);
  17.155 +	image_crc = gg_crc32(0, image_data, image_size);
  17.156 +	
  17.157 +	purple_debug_info("gg", "ggp_image_prepare_to_send: image prepared "
  17.158 +		"[id=%d, crc=%u, size=%d, filename=%s]\n",
  17.159 +		id, image_crc, image_size, image_filename);
  17.160 +	
  17.161 +	pending_image = g_new(ggp_image_pending_image, 1);
  17.162 +	pending_image->id = id;
  17.163 +	pending_image->conv_name = g_strdup(conv_name);
  17.164 +	g_hash_table_insert(imgdata->pending_images, GINT_TO_POINTER(image_crc),
  17.165 +		pending_image);
  17.166 +	
  17.167 +	image_info->unknown1 = 0x0109;
  17.168 +	image_info->size = gg_fix32(image_size);
  17.169 +	image_info->crc32 = gg_fix32(image_crc);
  17.170 +	
  17.171 +	return GGP_IMAGE_PREPARE_OK;
  17.172 +}
  17.173 +
  17.174 +void ggp_image_recv(PurpleConnection *gc,
  17.175 +	const struct gg_event_image_reply *image_reply)
  17.176 +{
  17.177 +	ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc);
  17.178 +	int stored_id;
  17.179 +	const char *imgtag_search;
  17.180 +	gchar *imgtag_replace;
  17.181 +	GList *pending_messages_it;
  17.182 +	
  17.183 +	stored_id = purple_imgstore_add_with_id(
  17.184 +		g_memdup(image_reply->image, image_reply->size),
  17.185 +		image_reply->size,
  17.186 +		image_reply->filename);
  17.187 +	
  17.188 +	purple_debug_info("gg", "ggp_image_recv: got image "
  17.189 +		"[id=%d, crc=%u, size=%u, filename=\"%s\"]\n",
  17.190 +		stored_id,
  17.191 +		image_reply->crc32,
  17.192 +		image_reply->size,
  17.193 +		image_reply->filename);
  17.194 +
  17.195 +	imgtag_search = ggp_image_pending_placeholder(image_reply->crc32);
  17.196 +	imgtag_replace = g_strdup_printf("<img src=\""
  17.197 +		PURPLE_STORED_IMAGE_PROTOCOL "%u\">", stored_id);
  17.198 +
  17.199 +	pending_messages_it = g_list_first(imgdata->pending_messages);
  17.200 +	while (pending_messages_it)
  17.201 +	{
  17.202 +		ggp_image_pending_message *pending_message =
  17.203 +			(ggp_image_pending_message*)pending_messages_it->data;
  17.204 +		gchar *newText;
  17.205 +		
  17.206 +		if (strstr(pending_message->text, imgtag_search) == NULL)
  17.207 +		{
  17.208 +			pending_messages_it = g_list_next(pending_messages_it);
  17.209 +			continue;
  17.210 +		}
  17.211 +		
  17.212 +		purple_debug_misc("gg", "ggp_image_recv: found message "
  17.213 +			"containing image: %s\n", pending_message->text);
  17.214 +		
  17.215 +		newText = purple_strreplace(pending_message->text,
  17.216 +			imgtag_search, imgtag_replace);
  17.217 +		g_free(pending_message->text);
  17.218 +		pending_message->text = newText;
  17.219 +
  17.220 +		if (strstr(pending_message->text,
  17.221 +			"<img id=\"" GGP_PENDING_IMAGE_ID_PREFIX) == NULL)
  17.222 +		{
  17.223 +			purple_debug_info("gg", "ggp_image_recv: "
  17.224 +				"message is ready to display\n");
  17.225 +			serv_got_im(gc, ggp_uin_to_str(pending_message->from),
  17.226 +				pending_message->text,
  17.227 +				PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_IMAGES,
  17.228 +				pending_message->mtime);
  17.229 +			
  17.230 +			ggp_image_pending_message_free(pending_message);
  17.231 +			imgdata->pending_messages = g_list_remove(
  17.232 +				imgdata->pending_messages, pending_message);
  17.233 +		}
  17.234 +		
  17.235 +		pending_messages_it = g_list_next(pending_messages_it);
  17.236 +	}
  17.237 +	g_free(imgtag_replace);
  17.238 +}
  17.239 +
  17.240 +void ggp_image_send(PurpleConnection *gc,
  17.241 +	const struct gg_event_image_request *image_request)
  17.242 +{
  17.243 +	GGPInfo *accdata = purple_connection_get_protocol_data(gc);
  17.244 +	ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc);
  17.245 +	ggp_image_pending_image *pending_image;
  17.246 +	PurpleStoredImage *image;
  17.247 +	PurpleConversation *conv;
  17.248 +	
  17.249 +	purple_debug_info("gg", "ggp_image_send: got image request "
  17.250 +		"[uin=%u, crc=%u, size=%u]\n",
  17.251 +		image_request->sender,
  17.252 +		image_request->crc32,
  17.253 +		image_request->size);
  17.254 +	
  17.255 +	pending_image = g_hash_table_lookup(imgdata->pending_images,
  17.256 +		GINT_TO_POINTER(image_request->crc32));
  17.257 +	
  17.258 +	if (pending_image == NULL)
  17.259 +	{
  17.260 +		purple_debug_warning("gg", "ggp_image_send: requested image "
  17.261 +			"not found\n");
  17.262 +		return;
  17.263 +	}
  17.264 +	
  17.265 +	purple_debug_misc("gg", "ggp_image_send: requested image found "
  17.266 +		"[id=%d, conv=%s]\n",
  17.267 +		pending_image->id,
  17.268 +		pending_image->conv_name);
  17.269 +	
  17.270 +	image = purple_imgstore_find_by_id(pending_image->id);
  17.271 +	
  17.272 +	if (!image)
  17.273 +	{
  17.274 +		purple_debug_error("gg", "ggp_image_send: requested image "
  17.275 +			"found, but doesn't exists in image store\n");
  17.276 +		g_hash_table_remove(imgdata->pending_images,
  17.277 +			GINT_TO_POINTER(image_request->crc32));
  17.278 +		return;
  17.279 +	}
  17.280 +	
  17.281 +	//TODO: check allowed recipients
  17.282 +	gg_image_reply(accdata->session, image_request->sender,
  17.283 +		purple_imgstore_get_filename(image),
  17.284 +		purple_imgstore_get_data(image),
  17.285 +		purple_imgstore_get_size(image));
  17.286 +	purple_imgstore_unref(image);
  17.287 +	
  17.288 +	conv = purple_find_conversation_with_account(
  17.289 +		PURPLE_CONV_TYPE_IM, pending_image->conv_name,
  17.290 +		purple_connection_get_account(gc));
  17.291 +	if (conv != NULL)
  17.292 +		purple_conversation_write(conv, "", _("Image delivered."),
  17.293 +			PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_NOTIFY,
  17.294 +			time(NULL));
  17.295 +	
  17.296 +	g_hash_table_remove(imgdata->pending_images,
  17.297 +		GINT_TO_POINTER(image_request->crc32));
  17.298 +}
    18.1 new file mode 100644
    18.2 --- /dev/null
    18.3 +++ b/libpurple/protocols/gg/image.h
    18.4 @@ -0,0 +1,66 @@
    18.5 +/* purple
    18.6 + *
    18.7 + * Purple is the legal property of its developers, whose names are too numerous
    18.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    18.9 + * source distribution.
   18.10 + *
   18.11 + * Rewritten from scratch during Google Summer of Code 2012
   18.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   18.13 + *
   18.14 + * Previously implemented by:
   18.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   18.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   18.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   18.18 + *
   18.19 + * This program is free software; you can redistribute it and/or modify
   18.20 + * it under the terms of the GNU General Public License as published by
   18.21 + * the Free Software Foundation; either version 2 of the License, or
   18.22 + * (at your option) any later version.
   18.23 + *
   18.24 + * This program is distributed in the hope that it will be useful,
   18.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   18.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18.27 + * GNU General Public License for more details.
   18.28 + *
   18.29 + * You should have received a copy of the GNU General Public License
   18.30 + * along with this program; if not, write to the Free Software
   18.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   18.32 + */
   18.33 +
   18.34 +#ifndef _GGP_IMAGE_H
   18.35 +#define _GGP_IMAGE_H
   18.36 +
   18.37 +#include <internal.h>
   18.38 +#include <libgadu.h>
   18.39 +
   18.40 +#define GGP_IMAGE_SIZE_MAX 255000
   18.41 +
   18.42 +typedef struct
   18.43 +{
   18.44 +	GList *pending_messages;
   18.45 +	GHashTable *pending_images;
   18.46 +} ggp_image_connection_data;
   18.47 +
   18.48 +typedef enum
   18.49 +{
   18.50 +	GGP_IMAGE_PREPARE_OK = 0,
   18.51 +	GGP_IMAGE_PREPARE_FAILURE,
   18.52 +	GGP_IMAGE_PREPARE_TOO_BIG
   18.53 +} ggp_image_prepare_result;
   18.54 +
   18.55 +void ggp_image_setup(PurpleConnection *gc);
   18.56 +void ggp_image_cleanup(PurpleConnection *gc);
   18.57 +
   18.58 +const char * ggp_image_pending_placeholder(uint32_t id);
   18.59 +
   18.60 +void ggp_image_got_im(PurpleConnection *gc, uin_t from, gchar *msg,
   18.61 +	time_t mtime);
   18.62 +ggp_image_prepare_result ggp_image_prepare(PurpleConnection *gc, const int id,
   18.63 +	const char *conv_name, struct gg_msg_richtext_image *image_info);
   18.64 +
   18.65 +void ggp_image_recv(PurpleConnection *gc,
   18.66 +	const struct gg_event_image_reply *image_reply);
   18.67 +void ggp_image_send(PurpleConnection *gc,
   18.68 +	const struct gg_event_image_request *image_request);
   18.69 +
   18.70 +#endif /* _GGP_IMAGE_H */
    19.1 rename from libpurple/protocols/gg/lib/libgadu-config.h
    19.2 rename to libpurple/protocols/gg/lib/config.h
    19.3 --- a/libpurple/protocols/gg/lib/libgadu-config.h
    19.4 +++ b/libpurple/protocols/gg/lib/config.h
    19.5 @@ -1,21 +1,23 @@
    19.6 -/* Local libgadu configuration. */
    19.7 +/* Local libgadu configuration file. */
    19.8  
    19.9 -#include "config.h"
   19.10 +/* libpurple's config */
   19.11 +#include <config.h>
   19.12  
   19.13 -#ifndef __GG_LIBGADU_CONFIG_H
   19.14 -#define __GG_LIBGADU_CONFIG_H
   19.15 +#define GGP_QUOTE(x) GGP_QUOTE2(x)
   19.16 +#define GGP_QUOTE2(x) #x
   19.17 +#define GG_LIBGADU_VERSION GGP_QUOTE(GG_INTERNAL_LIBGADU_VERSION)
   19.18  
   19.19  /* Defined if libgadu was compiled for bigendian machine. */
   19.20 -#undef __GG_LIBGADU_BIGENDIAN
   19.21 +#undef GG_CONFIG_BIGENDIAN
   19.22  #ifdef WORDS_BIGENDIAN
   19.23 -#  define __GG_LIBGADU_BIGENDIAN
   19.24 +#  define GG_CONFIG_BIGENDIAN
   19.25  #endif
   19.26  
   19.27  /* Defined if this machine has gethostbyname_r(). */
   19.28  #undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
   19.29  
   19.30 -/* Defined if this machine has _exit(). */
   19.31 -#define GG_CONFIG_HAVE__EXIT
   19.32 +/* Define to 1 if you have the `_exit' function. */
   19.33 +#define HAVE__EXIT 1
   19.34  
   19.35  /* Defined if libgadu was compiled and linked with fork support. */
   19.36  #undef GG_CONFIG_HAVE_FORK
   19.37 @@ -24,25 +26,25 @@
   19.38  #endif
   19.39  
   19.40  /* Defined if libgadu was compiled and linked with pthread support. */
   19.41 -/* We don't like pthreads. */
   19.42 -#undef __GG_LIBGADU_HAVE_PTHREAD
   19.43 +/* We don't use pthreads - they may not be safe. */
   19.44 +#undef GG_CONFIG_HAVE_PTHREAD
   19.45  
   19.46  /* Defined if this machine has C99-compiliant vsnprintf(). */
   19.47 -#undef __GG_LIBGADU_HAVE_C99_VSNPRINTF
   19.48 +#undef HAVE_C99_VSNPRINTF
   19.49  #ifndef _WIN32
   19.50 -#  define __GG_LIBGADU_HAVE_C99_VSNPRINTF
   19.51 +#  define HAVE_C99_VSNPRINTF
   19.52  #endif
   19.53  
   19.54  /* Defined if this machine has va_copy(). */
   19.55 -#define __GG_LIBGADU_HAVE_VA_COPY
   19.56 +#define GG_CONFIG_HAVE_VA_COPY
   19.57  
   19.58  /* Defined if this machine has __va_copy(). */
   19.59 -#define __GG_LIBGADU_HAVE___VA_COPY
   19.60 +#define GG_CONFIG_HAVE___VA_COPY
   19.61  
   19.62  /* Defined if this machine supports long long. */
   19.63 -#undef __GG_LIBGADU_HAVE_LONG_LONG
   19.64 +#undef GG_CONFIG_HAVE_LONG_LONG
   19.65  #ifdef HAVE_LONG_LONG
   19.66 -#  define __GG_LIBGADU_HAVE_LONG_LONG
   19.67 +#  define GG_CONFIG_HAVE_LONG_LONG
   19.68  #endif
   19.69  
   19.70  /* Defined if libgadu was compiled and linked with GnuTLS support. */
   19.71 @@ -52,19 +54,26 @@
   19.72  #endif
   19.73  
   19.74  /* Defined if libgadu was compiled and linked with OpenSSL support. */
   19.75 -/* Always undefined in Purple. */
   19.76 -#undef __GG_LIBGADU_HAVE_OPENSSL
   19.77 +/* OpenSSL cannot be used with libpurple due to licence type. */
   19.78 +#undef GG_CONFIG_HAVE_OPENSSL
   19.79  
   19.80  /* Defined if libgadu was compiled and linked with zlib support. */
   19.81 -#undef GG_CONFIG_HAVE_ZLIB
   19.82 +#define GG_CONFIG_HAVE_ZLIB
   19.83  
   19.84  /* Defined if uintX_t types are defined in <stdint.h>. */
   19.85  #undef GG_CONFIG_HAVE_STDINT_H
   19.86 -#if HAVE_STDINT_H
   19.87 +#ifdef HAVE_STDINT_H
   19.88  #  define GG_CONFIG_HAVE_STDINT_H
   19.89  #endif
   19.90  
   19.91 +/* Defined if uintX_t types are defined in <inttypes.h>. */
   19.92 +#undef GG_CONFIG_HAVE_INTTYPES_H
   19.93 +#ifdef HAVE_INTTYPES_H
   19.94 +#  define GG_CONFIG_HAVE_INTTYPES_H
   19.95 +#endif
   19.96  
   19.97 -#define vnsprintf g_vnsprintf
   19.98 -
   19.99 +/* Defined if uintX_t types are defined in <sys/types.h>. */
  19.100 +#undef GG_CONFIG_HAVE_SYS_TYPES_H
  19.101 +#ifdef HAVE_SYS_TYPES_H
  19.102 +#  define GG_CONFIG_HAVE_SYS_TYPES_H
  19.103  #endif
    20.1 --- a/libpurple/protocols/gg/lib/dcc7.c
    20.2 +++ b/libpurple/protocols/gg/lib/dcc7.c
    20.3 @@ -49,8 +49,8 @@
    20.4  #include "libgadu.h"
    20.5  #include "protocol.h"
    20.6  #include "resolver.h"
    20.7 -#include "libgadu-internal.h"
    20.8 -#include "libgadu-debug.h"
    20.9 +#include "internal.h"
   20.10 +#include "debug.h"
   20.11  
   20.12  #define gg_debug_dcc(dcc, level, fmt...) \
   20.13  	gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt)
    21.1 --- a/libpurple/protocols/gg/lib/debug.c
    21.2 +++ b/libpurple/protocols/gg/lib/debug.c
    21.3 @@ -32,7 +32,7 @@
    21.4  #include <string.h>
    21.5  
    21.6  #include "libgadu.h"
    21.7 -#include "libgadu-debug.h"
    21.8 +#include "debug.h"
    21.9  
   21.10  /**
   21.11   * Poziom rejestracji informacji odpluskwiajÄ…cych. Zmienna jest maskÄ… bitowÄ…
    22.1 rename from libpurple/protocols/gg/lib/libgadu-debug.h
    22.2 rename to libpurple/protocols/gg/lib/debug.h
    23.1 --- a/libpurple/protocols/gg/lib/deflate.h
    23.2 +++ b/libpurple/protocols/gg/lib/deflate.h
    23.3 @@ -1,7 +1,7 @@
    23.4  /* $Id$ */
    23.5  
    23.6  /*
    23.7 - *  (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
    23.8 + *  (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com>
    23.9   *
   23.10   *  This program is free software; you can redistribute it and/or modify
   23.11   *  it under the terms of the GNU Lesser General Public License Version
    24.1 --- a/libpurple/protocols/gg/lib/encoding.c
    24.2 +++ b/libpurple/protocols/gg/lib/encoding.c
    24.3 @@ -35,22 +35,22 @@
    24.4   */
    24.5  static const uint16_t table_cp1250[] =
    24.6  {
    24.7 -        0x20ac, '?',    0x201a, '?',    0x201e, 0x2026, 0x2020, 0x2021,
    24.8 -        '?',    0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,
    24.9 -        '?',    0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
   24.10 -        '?',    0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,
   24.11 -        0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,
   24.12 -        0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,
   24.13 -        0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
   24.14 -        0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,
   24.15 -        0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,
   24.16 -        0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,
   24.17 -        0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,
   24.18 -        0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,
   24.19 -        0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,
   24.20 -        0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,
   24.21 -        0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,
   24.22 -        0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9,
   24.23 +	0x20ac, '?',    0x201a, '?',    0x201e, 0x2026, 0x2020, 0x2021,
   24.24 +	'?',    0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,
   24.25 +	'?',    0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
   24.26 +	'?',    0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,
   24.27 +	0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,
   24.28 +	0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,
   24.29 +	0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
   24.30 +	0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,
   24.31 +	0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,
   24.32 +	0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,
   24.33 +	0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,
   24.34 +	0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,
   24.35 +	0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,
   24.36 +	0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,
   24.37 +	0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,
   24.38 +	0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9,
   24.39  };
   24.40  
   24.41  /**
   24.42 @@ -136,11 +136,8 @@
   24.43  	uint32_t uc = 0, uc_min = 0;
   24.44  
   24.45  	for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) {
   24.46 -		if ((src[i] & 0xc0) == 0xc0) {
   24.47 +		if ((src[i] & 0xc0) != 0x80)
   24.48  			len++;
   24.49 -		} else if ((src[i] & 0x80) == 0x00) {
   24.50 -			len++;
   24.51 -		}
   24.52  	}
   24.53  
   24.54  	if ((dst_length != -1) && (len > dst_length))
    25.1 --- a/libpurple/protocols/gg/lib/events.c
    25.2 +++ b/libpurple/protocols/gg/lib/events.c
    25.3 @@ -1,4 +1,4 @@
    25.4 -/* $Id: events.c 1105 2011-05-25 21:34:50Z wojtekka $ */
    25.5 +/* $Id: events.c 1144 2011-07-09 15:43:00Z wojtekka $ */
    25.6  
    25.7  /*
    25.8   *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
    25.9 @@ -33,9 +33,9 @@
   25.10  #include "compat.h"
   25.11  #include "libgadu.h"
   25.12  #include "protocol.h"
   25.13 -#include "libgadu-internal.h"
   25.14 +#include "internal.h"
   25.15  #include "encoding.h"
   25.16 -#include "libgadu-debug.h"
   25.17 +#include "debug.h"
   25.18  #include "session.h"
   25.19  
   25.20  #include <errno.h>
   25.21 @@ -806,14 +806,14 @@
   25.22  				const gnutls_datum_t *peers;
   25.23  				gnutls_x509_crt_t cert;
   25.24  
   25.25 -				if (gnutls_x509_crt_init(&cert) >= 0) {
   25.26 +				if (gnutls_x509_crt_init(&cert) == 0) {
   25.27  					peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count);
   25.28  
   25.29  					if (peers != NULL) {
   25.30  						char buf[256];
   25.31  						size_t size;
   25.32  
   25.33 -						if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) >= 0) {
   25.34 +						if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) == 0) {
   25.35  							size = sizeof(buf);
   25.36  							gnutls_x509_crt_get_dn(cert, buf, &size);
   25.37  							gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
   25.38 @@ -822,6 +822,8 @@
   25.39  							gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
   25.40  						}
   25.41  					}
   25.42 +
   25.43 +					gnutls_x509_crt_deinit(cert);
   25.44  				}
   25.45  			}
   25.46  
    26.1 --- a/libpurple/protocols/gg/lib/handlers.c
    26.2 +++ b/libpurple/protocols/gg/lib/handlers.c
    26.3 @@ -39,7 +39,7 @@
    26.4  #include "protocol.h"
    26.5  #include "encoding.h"
    26.6  #include "message.h"
    26.7 -#include "libgadu-internal.h"
    26.8 +#include "internal.h"
    26.9  #include "deflate.h"
   26.10  
   26.11  #include <errno.h>
    27.1 rename from libpurple/protocols/gg/lib/libgadu-internal.h
    27.2 rename to libpurple/protocols/gg/lib/internal.h
    27.3 --- a/libpurple/protocols/gg/lib/libgadu-internal.h
    27.4 +++ b/libpurple/protocols/gg/lib/internal.h
    27.5 @@ -22,6 +22,7 @@
    27.6  #define LIBGADU_INTERNAL_H
    27.7  
    27.8  #include "libgadu.h"
    27.9 +#include "config.h"
   27.10  
   27.11  struct gg_dcc7_relay {
   27.12  	uint32_t addr;
    28.1 --- a/libpurple/protocols/gg/lib/libgadu.c
    28.2 +++ b/libpurple/protocols/gg/lib/libgadu.c
    28.3 @@ -1,4 +1,4 @@
    28.4 -/* $Id: libgadu.c 1102 2011-05-05 21:17:57Z wojtekka $ */
    28.5 +/* $Id: libgadu.c 1245 2012-01-10 22:48:31Z wojtekka $ */
    28.6  
    28.7  /*
    28.8   *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
    28.9 @@ -37,9 +37,9 @@
   28.10  #include "libgadu.h"
   28.11  #include "protocol.h"
   28.12  #include "resolver.h"
   28.13 -#include "libgadu-internal.h"
   28.14 +#include "internal.h"
   28.15  #include "encoding.h"
   28.16 -#include "libgadu-debug.h"
   28.17 +#include "debug.h"
   28.18  #include "session.h"
   28.19  #include "message.h"
   28.20  #include "deflate.h"
   28.21 @@ -60,8 +60,6 @@
   28.22  #  include <openssl/rand.h>
   28.23  #endif
   28.24  
   28.25 -#define GG_LIBGADU_VERSION "1.11.0"
   28.26 -
   28.27  /**
   28.28   * Port gniazda nasłuchującego dla połączeń bezpośrednich.
   28.29   * 
   28.30 @@ -132,7 +130,7 @@
   28.31  #ifdef __GNUC__
   28.32  __attribute__ ((unused))
   28.33  #endif
   28.34 -= "$Id: libgadu.c 1102 2011-05-05 21:17:57Z wojtekka $";
   28.35 += "$Id: libgadu.c 1245 2012-01-10 22:48:31Z wojtekka $";
   28.36  #endif
   28.37  
   28.38  #endif /* DOXYGEN */
   28.39 @@ -149,7 +147,7 @@
   28.40  	return GG_LIBGADU_VERSION;
   28.41  }
   28.42  
   28.43 -#ifdef GG_CONFIG_HAVE_UINT64_T
   28.44 +#ifdef HAVE_UINT64_T
   28.45  /**
   28.46   * \internal Zamienia kolejność bajtów w 64-bitowym słowie.
   28.47   *
   28.48 @@ -178,7 +176,7 @@
   28.49  		((x & (uint64_t) 0xff00000000000000ULL) >> 56));
   28.50  #endif
   28.51  }
   28.52 -#endif /* GG_CONFIG_HAVE_UINT64_T */
   28.53 +#endif /* HAVE_UINT64_T */
   28.54  
   28.55  /**
   28.56   * \internal Zamienia kolejność bajtów w 32-bitowym słowie.
   28.57 @@ -443,11 +441,11 @@
   28.58  			res = written;
   28.59  		}
   28.60  	} else {
   28.61 -		res = 0;
   28.62 -
   28.63  		if (sess->send_buf == NULL) {
   28.64  			res = gg_write_common(sess, buf, length);
   28.65  
   28.66 +			if (res == -1 && errno == EAGAIN)
   28.67 +				res = 0;
   28.68  			if (res == -1)
   28.69  				return -1;
   28.70  		}
   28.71 @@ -1112,18 +1110,6 @@
   28.72  		sess->fd = -1;
   28.73  	}
   28.74  
   28.75 -#ifdef GG_CONFIG_HAVE_GNUTLS
   28.76 -	if (sess->ssl != NULL) {
   28.77 -		gg_session_gnutls_t *tmp;
   28.78 -
   28.79 -		tmp = (gg_session_gnutls_t*) sess->ssl;
   28.80 -		gnutls_deinit(tmp->session);
   28.81 -		gnutls_certificate_free_credentials(tmp->xcred);
   28.82 -		gnutls_global_deinit();
   28.83 -		free(sess->ssl);
   28.84 -	}
   28.85 -#endif
   28.86 -
   28.87  	if (sess->send_buf) {
   28.88  		free(sess->send_buf);
   28.89  		sess->send_buf = NULL;
   28.90 @@ -1155,6 +1141,18 @@
   28.91  	free(sess->recv_buf);
   28.92  	free(sess->header_buf);
   28.93  
   28.94 +#ifdef GG_CONFIG_HAVE_GNUTLS
   28.95 +	if (sess->ssl != NULL) {
   28.96 +		gg_session_gnutls_t *tmp;
   28.97 +
   28.98 +		tmp = (gg_session_gnutls_t*) sess->ssl;
   28.99 +		gnutls_deinit(tmp->session);
  28.100 +		gnutls_certificate_free_credentials(tmp->xcred);
  28.101 +		gnutls_global_deinit();
  28.102 +		free(sess->ssl);
  28.103 +	}
  28.104 +#endif
  28.105 +
  28.106  #ifdef GG_CONFIG_HAVE_OPENSSL
  28.107  	if (sess->ssl)
  28.108  		SSL_free(sess->ssl);
    29.1 --- a/libpurple/protocols/gg/lib/libgadu.h
    29.2 +++ b/libpurple/protocols/gg/lib/libgadu.h
    29.3 @@ -101,7 +101,7 @@
    29.4  /* Defined if uintX_t types are defined in <sys/types.h>. */
    29.5  #undef GG_CONFIG_HAVE_SYS_TYPES_H
    29.6  
    29.7 -#include "libgadu-config.h"
    29.8 +#include "config.h"
    29.9  
   29.10  #ifdef GG_CONFIG_HAVE_OPENSSL
   29.11  #include <openssl/ssl.h>
    30.1 --- a/libpurple/protocols/gg/lib/obsolete.c
    30.2 +++ b/libpurple/protocols/gg/lib/obsolete.c
    30.3 @@ -34,7 +34,7 @@
    30.4  #include <errno.h>
    30.5  
    30.6  #include "libgadu.h"
    30.7 -#include "libgadu-internal.h"
    30.8 +#include "internal.h"
    30.9  
   30.10  struct gg_http *gg_userlist_get(uin_t uin, const char *passwd, int async)
   30.11  {
    31.1 --- a/libpurple/protocols/gg/lib/pubdir50.c
    31.2 +++ b/libpurple/protocols/gg/lib/pubdir50.c
    31.3 @@ -31,7 +31,7 @@
    31.4  #include <time.h>
    31.5  
    31.6  #include "libgadu.h"
    31.7 -#include "libgadu-internal.h"
    31.8 +#include "internal.h"
    31.9  #include "encoding.h"
   31.10  
   31.11  /**
    32.1 --- a/libpurple/protocols/gg/lib/resolver.c
    32.2 +++ b/libpurple/protocols/gg/lib/resolver.c
    32.3 @@ -234,7 +234,7 @@
    32.4  	/* Kopiuj */
    32.5  
    32.6  	for (i = 0; he->h_addr_list[i] != NULL; i++)
    32.7 -		memcpy(&((*result)[i]), he->h_addr_list[0], sizeof(struct in_addr));
    32.8 +		memcpy(&((*result)[i]), he->h_addr_list[i], sizeof(struct in_addr));
    32.9  
   32.10  	(*result)[i].s_addr = INADDR_NONE;
   32.11  
   32.12 @@ -249,6 +249,9 @@
   32.13  /**
   32.14   * \internal RozwiÄ…zuje nazwÄ™ i zapisuje wynik do podanego desktyptora.
   32.15   *
   32.16 + * \note Użycie logowania w tej funkcji może mieć negatywny wpływ na
   32.17 + * aplikacje jednowÄ…tkowe korzystajÄ…ce.
   32.18 + *
   32.19   * \param fd Deskryptor
   32.20   * \param hostname Nazwa serwera
   32.21   *
   32.22 @@ -260,11 +263,10 @@
   32.23  	int addr_count;
   32.24  	int res = 0;
   32.25  
   32.26 -	gg_debug(GG_DEBUG_MISC, "// gg_resolver_run(%d, %s)\n", fd, hostname);
   32.27 -
   32.28  	if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) {
   32.29  		if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 1) == -1) {
   32.30  			addr_list = addr_ip;
   32.31 +			addr_count = 0;
   32.32  			/* addr_ip[0] już zawiera INADDR_NONE */
   32.33  		}
   32.34  	} else {
   32.35 @@ -273,8 +275,6 @@
   32.36  		addr_count = 1;
   32.37  	}
   32.38  
   32.39 -	gg_debug(GG_DEBUG_MISC, "// gg_resolver_run() count = %d\n", addr_count);
   32.40 -
   32.41  	if (write(fd, addr_list, (addr_count + 1) * sizeof(struct in_addr)) != (addr_count + 1) * sizeof(struct in_addr))
   32.42  		res = -1;
   32.43  
   32.44 @@ -375,7 +375,7 @@
   32.45  
   32.46  		status = (gg_resolver_run(pipes[1], hostname) == -1) ? 1 : 0;
   32.47  
   32.48 -#ifdef GG_CONFIG_HAVE__EXIT
   32.49 +#ifdef HAVE__EXIT
   32.50  		_exit(status);
   32.51  #else
   32.52  		exit(status);
    33.1 new file mode 100644
    33.2 --- /dev/null
    33.3 +++ b/libpurple/protocols/gg/libgadu-events.c
    33.4 @@ -0,0 +1,83 @@
    33.5 +/* purple
    33.6 + *
    33.7 + * Purple is the legal property of its developers, whose names are too numerous
    33.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    33.9 + * source distribution.
   33.10 + *
   33.11 + * Rewritten from scratch during Google Summer of Code 2012
   33.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   33.13 + *
   33.14 + * Previously implemented by:
   33.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   33.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   33.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   33.18 + *
   33.19 + * This program is free software; you can redistribute it and/or modify
   33.20 + * it under the terms of the GNU General Public License as published by
   33.21 + * the Free Software Foundation; either version 2 of the License, or
   33.22 + * (at your option) any later version.
   33.23 + *
   33.24 + * This program is distributed in the hope that it will be useful,
   33.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   33.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   33.27 + * GNU General Public License for more details.
   33.28 + *
   33.29 + * You should have received a copy of the GNU General Public License
   33.30 + * along with this program; if not, write to the Free Software
   33.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   33.32 + */
   33.33 +
   33.34 +#include "libgadu-events.h"
   33.35 +
   33.36 +#include <debug.h>
   33.37 +
   33.38 +#include "avatar.h"
   33.39 +
   33.40 +void ggp_events_user_data(PurpleConnection *gc, struct gg_event_user_data *data)
   33.41 +{
   33.42 +	int user_idx;
   33.43 +	gboolean is_update;
   33.44 +	
   33.45 +	purple_debug_info("gg", "GG_EVENT_USER_DATA [type=%d, user_count=%d]\n",
   33.46 +		data->type, data->user_count);
   33.47 +	
   33.48 +	/*
   33.49 +	type = 
   33.50 +		1, 3:	user information sent after connecting (divided by
   33.51 +			20 contacts; 3 - last one; 1 - rest of them)
   33.52 +		0: data update
   33.53 +	*/
   33.54 +	is_update = (data->type == 0);
   33.55 +	
   33.56 +	for (user_idx = 0; user_idx < data->user_count; user_idx++)
   33.57 +	{
   33.58 +		struct gg_event_user_data_user *data_user =
   33.59 +			&data->users[user_idx];
   33.60 +		uin_t uin = data_user->uin;
   33.61 +		int attr_idx;
   33.62 +		gboolean got_avatar = FALSE;
   33.63 +		for (attr_idx = 0; attr_idx < data_user->attr_count; attr_idx++)
   33.64 +		{
   33.65 +			struct gg_event_user_data_attr *data_attr =
   33.66 +				&data_user->attrs[attr_idx];
   33.67 +			if (strcmp(data_attr->key, "avatar") == 0)
   33.68 +			{
   33.69 +				time_t timestamp;
   33.70 +				if (data_attr->type == 0)
   33.71 +				{
   33.72 +					ggp_avatar_buddy_remove(gc, uin);
   33.73 +					continue;
   33.74 +				}
   33.75 +				
   33.76 +				timestamp = atoi(data_attr->value);
   33.77 +				if (timestamp <= 0)
   33.78 +					continue;
   33.79 +				got_avatar = TRUE;
   33.80 +				ggp_avatar_buddy_update(gc, uin, timestamp);
   33.81 +			}
   33.82 +		}
   33.83 +		
   33.84 +		if (!is_update && !got_avatar)
   33.85 +			ggp_avatar_buddy_remove(gc, uin);
   33.86 +	}
   33.87 +}
    34.1 new file mode 100644
    34.2 --- /dev/null
    34.3 +++ b/libpurple/protocols/gg/libgadu-events.h
    34.4 @@ -0,0 +1,39 @@
    34.5 +/* purple
    34.6 + *
    34.7 + * Purple is the legal property of its developers, whose names are too numerous
    34.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    34.9 + * source distribution.
   34.10 + *
   34.11 + * Rewritten from scratch during Google Summer of Code 2012
   34.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   34.13 + *
   34.14 + * Previously implemented by:
   34.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   34.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   34.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   34.18 + *
   34.19 + * This program is free software; you can redistribute it and/or modify
   34.20 + * it under the terms of the GNU General Public License as published by
   34.21 + * the Free Software Foundation; either version 2 of the License, or
   34.22 + * (at your option) any later version.
   34.23 + *
   34.24 + * This program is distributed in the hope that it will be useful,
   34.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   34.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   34.27 + * GNU General Public License for more details.
   34.28 + *
   34.29 + * You should have received a copy of the GNU General Public License
   34.30 + * along with this program; if not, write to the Free Software
   34.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   34.32 + */
   34.33 +
   34.34 +#ifndef _GGP_LIBGADU_EVENTS_H
   34.35 +#define _GGP_LIBGADU_EVENTS_H
   34.36 +
   34.37 +#include <internal.h>
   34.38 +#include <libgadu.h>
   34.39 +
   34.40 +void ggp_events_user_data(PurpleConnection *gc,
   34.41 +	struct gg_event_user_data *data);
   34.42 +
   34.43 +#endif /* _GGP_LIBGADU_EVENTS_H */
    35.1 new file mode 100644
    35.2 --- /dev/null
    35.3 +++ b/libpurple/protocols/gg/libgaduw.c
    35.4 @@ -0,0 +1,141 @@
    35.5 +/* purple
    35.6 + *
    35.7 + * Purple is the legal property of its developers, whose names are too numerous
    35.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    35.9 + * source distribution.
   35.10 + *
   35.11 + * Rewritten from scratch during Google Summer of Code 2012
   35.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   35.13 + *
   35.14 + * Previously implemented by:
   35.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   35.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   35.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   35.18 + *
   35.19 + * This program is free software; you can redistribute it and/or modify
   35.20 + * it under the terms of the GNU General Public License as published by
   35.21 + * the Free Software Foundation; either version 2 of the License, or
   35.22 + * (at your option) any later version.
   35.23 + *
   35.24 + * This program is distributed in the hope that it will be useful,
   35.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   35.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   35.27 + * GNU General Public License for more details.
   35.28 + *
   35.29 + * You should have received a copy of the GNU General Public License
   35.30 + * along with this program; if not, write to the Free Software
   35.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   35.32 + */
   35.33 +
   35.34 +#include "libgaduw.h"
   35.35 +
   35.36 +#include <debug.h>
   35.37 +
   35.38 +#include "purplew.h"
   35.39 +
   35.40 +/*******************************************************************************
   35.41 + * HTTP requests.
   35.42 + ******************************************************************************/
   35.43 +
   35.44 +static void ggp_libgaduw_http_processing_cancel(PurpleConnection *gc,
   35.45 +	void *_req);
   35.46 +
   35.47 +static void ggp_libgaduw_http_handler(gpointer _req, gint fd,
   35.48 +	PurpleInputCondition cond);
   35.49 +
   35.50 +static void ggp_libgaduw_http_finish(ggp_libgaduw_http_req *req,
   35.51 +	gboolean success);
   35.52 +
   35.53 +/******************************************************************************/
   35.54 +
   35.55 +ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc,
   35.56 +	struct gg_http *h, ggp_libgaduw_http_cb cb,
   35.57 +	gpointer user_data, gboolean show_processing)
   35.58 +{
   35.59 +	ggp_libgaduw_http_req *req;
   35.60 +	purple_debug_misc("gg", "ggp_libgaduw_http_watch(h=%x, "
   35.61 +		"show_processing=%d)\n", (unsigned int)h, show_processing);
   35.62 +	
   35.63 +	req = g_new(ggp_libgaduw_http_req, 1);
   35.64 +	req->user_data = user_data;
   35.65 +	req->cb = cb;
   35.66 +	req->cancelled = FALSE;
   35.67 +	req->h = h;
   35.68 +	req->processing = NULL;
   35.69 +	if (show_processing)
   35.70 +		req->processing = ggp_purplew_request_processing(gc, NULL,
   35.71 +			req, ggp_libgaduw_http_processing_cancel);
   35.72 +	req->inpa = ggp_purplew_http_input_add(h, ggp_libgaduw_http_handler,
   35.73 +		req);
   35.74 +	
   35.75 +	return req;
   35.76 +}
   35.77 +
   35.78 +static void ggp_libgaduw_http_processing_cancel(PurpleConnection *gc,
   35.79 +	void *_req)
   35.80 +{
   35.81 +	ggp_libgaduw_http_req *req = _req;
   35.82 +	req->processing = NULL;
   35.83 +	ggp_libgaduw_http_cancel(req);
   35.84 +}
   35.85 +
   35.86 +static void ggp_libgaduw_http_handler(gpointer _req, gint fd,
   35.87 +	PurpleInputCondition cond)
   35.88 +{
   35.89 +	ggp_libgaduw_http_req *req = _req;
   35.90 +	
   35.91 +	if (req->h->callback(req->h) == -1 || req->h->state == GG_STATE_ERROR)
   35.92 +	{
   35.93 +		purple_debug_error("gg", "ggp_libgaduw_http_handler: failed to "
   35.94 +			"make http request: %d\n", req->h->error);
   35.95 +		ggp_libgaduw_http_finish(req, FALSE);
   35.96 +		return;
   35.97 +	}
   35.98 +	
   35.99 +	//TODO: verbose mode
  35.100 +	//purple_debug_misc("gg", "ggp_libgaduw_http_handler: got fd update "
  35.101 +	//	"[check=%d, state=%d]\n", req->h->check, req->h->state);
  35.102 +	
  35.103 +	if (req->h->state != GG_STATE_DONE)
  35.104 +	{
  35.105 +		purple_input_remove(req->inpa);
  35.106 +		req->inpa = ggp_purplew_http_input_add(req->h,
  35.107 +			ggp_libgaduw_http_handler, req);
  35.108 +		return;
  35.109 +	}
  35.110 +	
  35.111 +	if (!req->h->data || !req->h->body)
  35.112 +	{
  35.113 +		purple_debug_error("gg", "ggp_libgaduw_http_handler: got empty "
  35.114 +			"http response: %d\n", req->h->error);
  35.115 +		ggp_libgaduw_http_finish(req, FALSE);
  35.116 +		return;
  35.117 +	}
  35.118 +
  35.119 +	ggp_libgaduw_http_finish(req, TRUE);
  35.120 +}
  35.121 +
  35.122 +void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req)
  35.123 +{
  35.124 +	purple_debug_misc("gg", "ggp_libgaduw_http_cancel\n");
  35.125 +	req->cancelled = TRUE;
  35.126 +	gg_http_stop(req->h);
  35.127 +	ggp_libgaduw_http_finish(req, FALSE);
  35.128 +}
  35.129 +
  35.130 +static void ggp_libgaduw_http_finish(ggp_libgaduw_http_req *req,
  35.131 +	gboolean success)
  35.132 +{
  35.133 +	purple_debug_misc("gg", "ggp_libgaduw_http_finish(h=%x, processing=%x):"
  35.134 +		" success=%d\n", (unsigned int)req->h,
  35.135 +		(unsigned int)req->processing, success);
  35.136 +	if (req->processing)
  35.137 +	{
  35.138 +		ggp_purplew_request_processing_done(req->processing);
  35.139 +		req->processing = NULL;
  35.140 +	}
  35.141 +	purple_input_remove(req->inpa);
  35.142 +	req->cb(req->h, success, req->cancelled, req->user_data);
  35.143 +	req->h->destroy(req->h);
  35.144 +	g_free(req);
  35.145 +}
    36.1 new file mode 100644
    36.2 --- /dev/null
    36.3 +++ b/libpurple/protocols/gg/libgaduw.h
    36.4 @@ -0,0 +1,58 @@
    36.5 +/* purple
    36.6 + *
    36.7 + * Purple is the legal property of its developers, whose names are too numerous
    36.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    36.9 + * source distribution.
   36.10 + *
   36.11 + * Rewritten from scratch during Google Summer of Code 2012
   36.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   36.13 + *
   36.14 + * Previously implemented by:
   36.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   36.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   36.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   36.18 + *
   36.19 + * This program is free software; you can redistribute it and/or modify
   36.20 + * it under the terms of the GNU General Public License as published by
   36.21 + * the Free Software Foundation; either version 2 of the License, or
   36.22 + * (at your option) any later version.
   36.23 + *
   36.24 + * This program is distributed in the hope that it will be useful,
   36.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   36.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   36.27 + * GNU General Public License for more details.
   36.28 + *
   36.29 + * You should have received a copy of the GNU General Public License
   36.30 + * along with this program; if not, write to the Free Software
   36.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   36.32 + */
   36.33 +
   36.34 +#ifndef _GGP_LIBGADUW_H
   36.35 +#define _GGP_LIBGADUW_H
   36.36 +
   36.37 +#include <internal.h>
   36.38 +#include <libgadu.h>
   36.39 +
   36.40 +#include "purplew.h"
   36.41 +
   36.42 +typedef void (*ggp_libgaduw_http_cb)(struct gg_http *h, gboolean success,
   36.43 +	gboolean cancelled, gpointer user_data);
   36.44 +
   36.45 +typedef struct
   36.46 +{
   36.47 +	gpointer user_data;
   36.48 +	ggp_libgaduw_http_cb cb;
   36.49 +	
   36.50 +	gboolean cancelled;
   36.51 +	struct gg_http *h;
   36.52 +	ggp_purplew_request_processing_handle *processing;
   36.53 +	guint inpa;
   36.54 +} ggp_libgaduw_http_req;
   36.55 +
   36.56 +ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc,
   36.57 +	struct gg_http *h, ggp_libgaduw_http_cb cb, gpointer user_data,
   36.58 +	gboolean show_processing);
   36.59 +void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req);
   36.60 +
   36.61 +
   36.62 +#endif /* _GGP_LIBGADUW_H */
    37.1 new file mode 100644
    37.2 --- /dev/null
    37.3 +++ b/libpurple/protocols/gg/multilogon.c
    37.4 @@ -0,0 +1,94 @@
    37.5 +/* purple
    37.6 + *
    37.7 + * Purple is the legal property of its developers, whose names are too numerous
    37.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    37.9 + * source distribution.
   37.10 + *
   37.11 + * Rewritten from scratch during Google Summer of Code 2012
   37.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   37.13 + *
   37.14 + * Previously implemented by:
   37.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   37.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   37.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   37.18 + *
   37.19 + * This program is free software; you can redistribute it and/or modify
   37.20 + * it under the terms of the GNU General Public License as published by
   37.21 + * the Free Software Foundation; either version 2 of the License, or
   37.22 + * (at your option) any later version.
   37.23 + *
   37.24 + * This program is distributed in the hope that it will be useful,
   37.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   37.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   37.27 + * GNU General Public License for more details.
   37.28 + *
   37.29 + * You should have received a copy of the GNU General Public License
   37.30 + * along with this program; if not, write to the Free Software
   37.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   37.32 + */
   37.33 +
   37.34 +#include "multilogon.h"
   37.35 +
   37.36 +#include <debug.h>
   37.37 +
   37.38 +#include "gg.h"
   37.39 +
   37.40 +struct _ggp_multilogon_session_data
   37.41 +{
   37.42 +	int session_count;
   37.43 +};
   37.44 +
   37.45 +static inline ggp_multilogon_session_data *
   37.46 +ggp_multilogon_get_mldata(PurpleConnection *gc);
   37.47 +
   37.48 +////////////
   37.49 +
   37.50 +static inline ggp_multilogon_session_data *
   37.51 +ggp_multilogon_get_mldata(PurpleConnection *gc)
   37.52 +{
   37.53 +	GGPInfo *accdata = purple_connection_get_protocol_data(gc);
   37.54 +	return accdata->multilogon_data;
   37.55 +}
   37.56 +
   37.57 +void ggp_multilogon_setup(PurpleConnection *gc)
   37.58 +{
   37.59 +	GGPInfo *accdata = purple_connection_get_protocol_data(gc);
   37.60 +	
   37.61 +	ggp_multilogon_session_data *mldata = g_new0(ggp_multilogon_session_data, 1);
   37.62 +	accdata->multilogon_data = mldata;
   37.63 +}
   37.64 +
   37.65 +void ggp_multilogon_cleanup(PurpleConnection *gc)
   37.66 +{
   37.67 +	ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
   37.68 +	g_free(mldata);
   37.69 +}
   37.70 +
   37.71 +void ggp_multilogon_msg(PurpleConnection *gc, struct gg_event_msg *msg)
   37.72 +{
   37.73 +	ggp_recv_message_handler(gc, msg, TRUE);
   37.74 +}
   37.75 +
   37.76 +void ggp_multilogon_info(PurpleConnection *gc,
   37.77 +	struct gg_event_multilogon_info *info)
   37.78 +{
   37.79 +	ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
   37.80 +	int i;
   37.81 +	
   37.82 +	purple_debug_info("gg", "ggp_multilogon_info: session list changed\n");
   37.83 +	for (i = 0; i < info->count; i++)
   37.84 +	{
   37.85 +		purple_debug_misc("gg", "ggp_multilogon_info: "
   37.86 +			"session [%s] logged in at %lu\n",
   37.87 +			info->sessions[i].name,
   37.88 +			info->sessions[i].logon_time);
   37.89 +	}
   37.90 +
   37.91 +	mldata->session_count = info->count;
   37.92 +}
   37.93 +
   37.94 +int ggp_multilogon_get_session_count(PurpleConnection *gc)
   37.95 +{
   37.96 +	ggp_multilogon_session_data *mldata = ggp_multilogon_get_mldata(gc);
   37.97 +	return mldata->session_count;
   37.98 +}
    38.1 new file mode 100644
    38.2 --- /dev/null
    38.3 +++ b/libpurple/protocols/gg/multilogon.h
    38.4 @@ -0,0 +1,47 @@
    38.5 +/* purple
    38.6 + *
    38.7 + * Purple is the legal property of its developers, whose names are too numerous
    38.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    38.9 + * source distribution.
   38.10 + *
   38.11 + * Rewritten from scratch during Google Summer of Code 2012
   38.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   38.13 + *
   38.14 + * Previously implemented by:
   38.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   38.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   38.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   38.18 + *
   38.19 + * This program is free software; you can redistribute it and/or modify
   38.20 + * it under the terms of the GNU General Public License as published by
   38.21 + * the Free Software Foundation; either version 2 of the License, or
   38.22 + * (at your option) any later version.
   38.23 + *
   38.24 + * This program is distributed in the hope that it will be useful,
   38.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   38.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   38.27 + * GNU General Public License for more details.
   38.28 + *
   38.29 + * You should have received a copy of the GNU General Public License
   38.30 + * along with this program; if not, write to the Free Software
   38.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   38.32 + */
   38.33 +
   38.34 +#ifndef _GGP_MULTILOGON_H
   38.35 +#define _GGP_MULTILOGON_H
   38.36 +
   38.37 +#include <internal.h>
   38.38 +#include <libgadu.h>
   38.39 +
   38.40 +typedef struct _ggp_multilogon_session_data ggp_multilogon_session_data;
   38.41 +
   38.42 +void ggp_multilogon_setup(PurpleConnection *gc);
   38.43 +void ggp_multilogon_cleanup(PurpleConnection *gc);
   38.44 +
   38.45 +void ggp_multilogon_msg(PurpleConnection *gc, struct gg_event_msg *msg);
   38.46 +void ggp_multilogon_info(PurpleConnection *gc,
   38.47 +	struct gg_event_multilogon_info *msg);
   38.48 +
   38.49 +int ggp_multilogon_get_session_count(PurpleConnection *gc);
   38.50 +
   38.51 +#endif /* _GGP_MULTILOGON_H */
    39.1 new file mode 100644
    39.2 --- /dev/null
    39.3 +++ b/libpurple/protocols/gg/oauth/oauth-parameter.c
    39.4 @@ -0,0 +1,159 @@
    39.5 +/* purple
    39.6 + *
    39.7 + * Purple is the legal property of its developers, whose names are too numerous
    39.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    39.9 + * source distribution.
   39.10 + *
   39.11 + * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
   39.12 + * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
   39.13 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   39.14 + *
   39.15 + * This program is free software; you can redistribute it and/or modify
   39.16 + * it under the terms of the GNU General Public License as published by
   39.17 + * the Free Software Foundation; either version 2 of the License, or
   39.18 + * (at your option) any later version.
   39.19 + *
   39.20 + * This program is distributed in the hope that it will be useful,
   39.21 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   39.22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   39.23 + * GNU General Public License for more details.
   39.24 + *
   39.25 + * You should have received a copy of the GNU General Public License
   39.26 + * along with this program; if not, write to the Free Software
   39.27 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   39.28 + */
   39.29 +
   39.30 +#include "oauth-parameter.h"
   39.31 +
   39.32 +struct gg_oauth_parameter {
   39.33 +	char *key;
   39.34 +	char *value;
   39.35 +	struct gg_oauth_parameter *next;
   39.36 +};
   39.37 +
   39.38 +int gg_oauth_parameter_set(gg_oauth_parameter_t **list, const char *key, const char *value)
   39.39 +{
   39.40 +	gg_oauth_parameter_t *p, *new_p;
   39.41 +	char *new_key;
   39.42 +	char *new_value;
   39.43 +
   39.44 +	if (value == NULL)
   39.45 +		return 0;
   39.46 +
   39.47 +	if (list == NULL)
   39.48 +		return -1;
   39.49 +
   39.50 +	new_key = strdup(key);
   39.51 +
   39.52 +	if (new_key == NULL)
   39.53 +		return -1;
   39.54 +
   39.55 +	new_value = strdup(value);
   39.56 +
   39.57 +	if (new_value == NULL) {
   39.58 +		free(new_key);
   39.59 +		return -1;
   39.60 +	}
   39.61 +
   39.62 +	new_p = malloc(sizeof(gg_oauth_parameter_t));
   39.63 +
   39.64 +	if (new_p == NULL) {
   39.65 +		free(new_key);
   39.66 +		free(new_value);
   39.67 +		return -1;
   39.68 +	}
   39.69 +
   39.70 +	memset(new_p, 0, sizeof(gg_oauth_parameter_t));
   39.71 +	new_p->key = new_key;
   39.72 +	new_p->value = new_value;
   39.73 +
   39.74 +	if (*list != NULL) {
   39.75 +		p = *list;
   39.76 +
   39.77 +		while (p != NULL && p->next != NULL)
   39.78 +			p = p->next;
   39.79 +
   39.80 +		p->next = new_p;
   39.81 +	} else {
   39.82 +		*list = new_p;
   39.83 +	}
   39.84 +
   39.85 +	return 0;
   39.86 +}
   39.87 +
   39.88 +char *gg_oauth_parameter_join(gg_oauth_parameter_t *list, int header)
   39.89 +{
   39.90 +	gg_oauth_parameter_t *p;
   39.91 +	int len = 0;
   39.92 +	char *res, *out;
   39.93 +
   39.94 +	if (header)
   39.95 +		len += strlen("Authorization: OAuth ");
   39.96 +
   39.97 +	for (p = list; p; p = p->next) {
   39.98 +		gchar *escaped;
   39.99 +		len += strlen(p->key);
  39.100 +
  39.101 +		len += (header) ? 3 : 1;
  39.102 +
  39.103 +		escaped = g_uri_escape_string(p->value, NULL, FALSE);
  39.104 +		len += strlen(escaped);
  39.105 +		g_free(escaped);
  39.106 +
  39.107 +		if (p->next)
  39.108 +			len += 1;
  39.109 +	}
  39.110 +
  39.111 +	res = malloc(len + 1);
  39.112 +
  39.113 +	if (res == NULL)
  39.114 +		return NULL;
  39.115 +
  39.116 +	out = res;
  39.117 +
  39.118 +	*out = 0;
  39.119 +
  39.120 +	if (header) {
  39.121 +		strcpy(out, "Authorization: OAuth ");
  39.122 +		out += strlen(out);
  39.123 +	}
  39.124 +
  39.125 +	for (p = list; p; p = p->next) {
  39.126 +		gchar *escaped;
  39.127 +		strcpy(out, p->key);
  39.128 +		out += strlen(p->key);
  39.129 +
  39.130 +		strcpy(out++, "=");
  39.131 +
  39.132 +		if (header)
  39.133 +			strcpy(out++, "\"");
  39.134 +
  39.135 +		escaped = g_uri_escape_string(p->value, NULL, FALSE);
  39.136 +		strcpy(out, escaped);
  39.137 +		out += strlen(escaped);
  39.138 +		g_free(escaped);
  39.139 +
  39.140 +		if (header)
  39.141 +			strcpy(out++, "\"");
  39.142 +
  39.143 +		if (p->next != NULL)
  39.144 +			strcpy(out++, (header) ? "," : "&");
  39.145 +	}
  39.146 +
  39.147 +	return res;
  39.148 +}
  39.149 +
  39.150 +void gg_oauth_parameter_free(gg_oauth_parameter_t *list)
  39.151 +{
  39.152 +	while (list != NULL) {
  39.153 +		gg_oauth_parameter_t *next;
  39.154 +
  39.155 +		next = list->next;
  39.156 +
  39.157 +		free(list->key);
  39.158 +		free(list->value);
  39.159 +		free(list);
  39.160 +
  39.161 +		list = next;
  39.162 +	}
  39.163 +}
    40.1 new file mode 100644
    40.2 --- /dev/null
    40.3 +++ b/libpurple/protocols/gg/oauth/oauth-parameter.h
    40.4 @@ -0,0 +1,37 @@
    40.5 +/* purple
    40.6 + *
    40.7 + * Purple is the legal property of its developers, whose names are too numerous
    40.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    40.9 + * source distribution.
   40.10 + *
   40.11 + * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
   40.12 + * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
   40.13 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   40.14 + *
   40.15 + * This program is free software; you can redistribute it and/or modify
   40.16 + * it under the terms of the GNU General Public License as published by
   40.17 + * the Free Software Foundation; either version 2 of the License, or
   40.18 + * (at your option) any later version.
   40.19 + *
   40.20 + * This program is distributed in the hope that it will be useful,
   40.21 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   40.22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   40.23 + * GNU General Public License for more details.
   40.24 + *
   40.25 + * You should have received a copy of the GNU General Public License
   40.26 + * along with this program; if not, write to the Free Software
   40.27 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   40.28 + */
   40.29 +
   40.30 +#ifndef _GGP_OAUTH_PARAMETER_H
   40.31 +#define _GGP_OAUTH_PARAMETER_H
   40.32 +
   40.33 +#include <internal.h>
   40.34 +
   40.35 +typedef struct gg_oauth_parameter gg_oauth_parameter_t;
   40.36 +
   40.37 +int gg_oauth_parameter_set(gg_oauth_parameter_t **list, const char *key, const char *value);
   40.38 +char *gg_oauth_parameter_join(gg_oauth_parameter_t *list, int header);
   40.39 +void gg_oauth_parameter_free(gg_oauth_parameter_t *list);
   40.40 +
   40.41 +#endif /* _GGP_OAUTH_PARAMETER_H */
    41.1 new file mode 100644
    41.2 --- /dev/null
    41.3 +++ b/libpurple/protocols/gg/oauth/oauth-purple.c
    41.4 @@ -0,0 +1,283 @@
    41.5 +/* purple
    41.6 + *
    41.7 + * Purple is the legal property of its developers, whose names are too numerous
    41.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    41.9 + * source distribution.
   41.10 + *
   41.11 + * Rewritten from scratch during Google Summer of Code 2012
   41.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   41.13 + *
   41.14 + * Previously implemented by:
   41.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   41.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   41.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   41.18 + *
   41.19 + * This program is free software; you can redistribute it and/or modify
   41.20 + * it under the terms of the GNU General Public License as published by
   41.21 + * the Free Software Foundation; either version 2 of the License, or
   41.22 + * (at your option) any later version.
   41.23 + *
   41.24 + * This program is distributed in the hope that it will be useful,
   41.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   41.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   41.27 + * GNU General Public License for more details.
   41.28 + *
   41.29 + * You should have received a copy of the GNU General Public License
   41.30 + * along with this program; if not, write to the Free Software
   41.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   41.32 + */
   41.33 +
   41.34 +#include "oauth-purple.h"
   41.35 +
   41.36 +#include "oauth.h"
   41.37 +#include "../utils.h"
   41.38 +#include "../xml.h"
   41.39 +
   41.40 +#include <debug.h>
   41.41 +
   41.42 +#define GGP_OAUTH_RESPONSE_MAX 10240
   41.43 +
   41.44 +typedef struct
   41.45 +{
   41.46 +	PurpleConnection *gc;
   41.47 +	ggp_oauth_request_cb callback;
   41.48 +	gpointer user_data;
   41.49 +	gchar *token;
   41.50 +	gchar *token_secret;
   41.51 +	
   41.52 +	gchar *sign_method, *sign_url;
   41.53 +} ggp_oauth_data;
   41.54 +
   41.55 +static void ggp_oauth_data_free(ggp_oauth_data *data);
   41.56 +
   41.57 +static void ggp_oauth_request_token_got(PurpleUtilFetchUrlData *url_data,
   41.58 +	gpointer user_data, const gchar *url_text, gsize len,
   41.59 +	const gchar *error_message);
   41.60 +
   41.61 +static void ggp_oauth_authorization_done(PurpleUtilFetchUrlData *url_data,
   41.62 +	gpointer user_data, const gchar *url_text, gsize len,
   41.63 +	const gchar *error_message);
   41.64 +
   41.65 +static void ggp_oauth_access_token_got(PurpleUtilFetchUrlData *url_data,
   41.66 +	gpointer user_data, const gchar *url_text, gsize len,
   41.67 +	const gchar *error_message);
   41.68 +
   41.69 +static void ggp_oauth_data_free(ggp_oauth_data *data)
   41.70 +{
   41.71 +	g_free(data->token);
   41.72 +	g_free(data->token_secret);
   41.73 +	g_free(data->sign_method);
   41.74 +	g_free(data->sign_url);
   41.75 +	g_free(data);
   41.76 +}
   41.77 +
   41.78 +void ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback,
   41.79 +	gpointer user_data, const gchar *sign_method, const gchar *sign_url)
   41.80 +{
   41.81 +	PurpleAccount *account = purple_connection_get_account(gc);
   41.82 +	char *auth;
   41.83 +	const char *method = "POST";
   41.84 +	const char *url = "http://api.gadu-gadu.pl/request_token";
   41.85 +	gchar *request;
   41.86 +	ggp_oauth_data *data;
   41.87 +
   41.88 +	g_return_if_fail((method == NULL) == (url == NULL));
   41.89 +
   41.90 +	purple_debug_misc("gg", "ggp_oauth_request: requesting token...\n");
   41.91 +
   41.92 +	auth = gg_oauth_generate_header(method, url,
   41.93 +		purple_account_get_username(account),
   41.94 +		purple_account_get_password(account), NULL, NULL);
   41.95 +	request = g_strdup_printf(
   41.96 +		"POST /request_token HTTP/1.1\r\n"
   41.97 +		"Host: api.gadu-gadu.pl\r\n"
   41.98 +		"%s\r\n"
   41.99 +		"Content-Length: 0\r\n"
  41.100 +		"\r\n",
  41.101 +		auth);
  41.102 +	free(auth);
  41.103 +
  41.104 +	data = g_new0(ggp_oauth_data, 1);
  41.105 +	data->gc = gc;
  41.106 +	data->callback = callback;
  41.107 +	data->user_data = user_data;
  41.108 +	data->sign_method = g_strdup(sign_method);
  41.109 +	data->sign_url = g_strdup(sign_url);
  41.110 +
  41.111 +	purple_util_fetch_url_request(account, url, FALSE, NULL, TRUE, request,
  41.112 +		FALSE, GGP_OAUTH_RESPONSE_MAX, ggp_oauth_request_token_got,
  41.113 +		data);
  41.114 +
  41.115 +	g_free(request);
  41.116 +}
  41.117 +
  41.118 +static void ggp_oauth_request_token_got(PurpleUtilFetchUrlData *url_data,
  41.119 +	gpointer user_data, const gchar *url_text, gsize len,
  41.120 +	const gchar *error_message)
  41.121 +{
  41.122 +	ggp_oauth_data *data = user_data;
  41.123 +	PurpleAccount *account;
  41.124 +	xmlnode *xml;
  41.125 +	gchar *request, *request_data;
  41.126 +	gboolean succ = TRUE;
  41.127 +
  41.128 +	if (!PURPLE_CONNECTION_IS_VALID(data->gc))
  41.129 +	{
  41.130 +		ggp_oauth_data_free(data);
  41.131 +		return;
  41.132 +	}
  41.133 +	account = purple_connection_get_account(data->gc);
  41.134 +
  41.135 +	if (len == 0)
  41.136 +	{
  41.137 +		purple_debug_error("gg", "ggp_oauth_request_token_got: "
  41.138 +			"requested token not received\n");
  41.139 +		ggp_oauth_data_free(data);
  41.140 +		return;
  41.141 +	}
  41.142 +
  41.143 +	purple_debug_misc("gg", "ggp_oauth_request_token_got: "
  41.144 +		"got request token, doing authorization...\n");
  41.145 +
  41.146 +	xml = xmlnode_from_str(url_text, -1);
  41.147 +	if (xml == NULL)
  41.148 +	{
  41.149 +		purple_debug_error("gg", "ggp_oauth_request_token_got: "
  41.150 +			"invalid xml\n");
  41.151 +		ggp_oauth_data_free(data);
  41.152 +		return;
  41.153 +	}
  41.154 +
  41.155 +	succ &= ggp_xml_get_string(xml, "oauth_token", &data->token);
  41.156 +	succ &= ggp_xml_get_string(xml, "oauth_token_secret",
  41.157 +		&data->token_secret);
  41.158 +	xmlnode_free(xml);
  41.159 +	if (!succ)
  41.160 +	{
  41.161 +		purple_debug_error("gg", "ggp_oauth_request_token_got: "
  41.162 +			"invalid xml - token is not present\n");
  41.163 +		ggp_oauth_data_free(data);
  41.164 +		return;
  41.165 +	}
  41.166 +
  41.167 +	request_data = g_strdup_printf(
  41.168 +		"callback_url=http://www.mojageneracja.pl&request_token=%s&"
  41.169 +		"uin=%s&password=%s", data->token,
  41.170 +		purple_account_get_username(account),
  41.171 +		purple_account_get_password(account));
  41.172 +	request = g_strdup_printf(
  41.173 +		"POST /authorize HTTP/1.1\r\n"
  41.174 +		"Host: login.gadu-gadu.pl\r\n"
  41.175 +		"Content-Length: %d\r\n"
  41.176 +		"Content-Type: application/x-www-form-urlencoded\r\n"
  41.177 +		"\r\n%s",
  41.178 +		strlen(request_data), request_data);
  41.179 +	g_free(request_data);
  41.180 +
  41.181 +	// we don't need any results, nor 302 redirection
  41.182 +	purple_util_fetch_url_request(account,
  41.183 +		"https://login.gadu-gadu.pl/authorize", FALSE, NULL, TRUE,
  41.184 +		request, FALSE, 0,
  41.185 +		ggp_oauth_authorization_done, data);
  41.186 +
  41.187 +	g_free(request);
  41.188 +}
  41.189 +
  41.190 +static void ggp_oauth_authorization_done(PurpleUtilFetchUrlData *url_data,
  41.191 +	gpointer user_data, const gchar *url_text, gsize len,
  41.192 +	const gchar *error_message)
  41.193 +{
  41.194 +	ggp_oauth_data *data = user_data;
  41.195 +	PurpleAccount *account;
  41.196 +	char *auth;
  41.197 +	const char *url = "http://api.gadu-gadu.pl/access_token";
  41.198 +	gchar *request;
  41.199 +	
  41.200 +	if (!PURPLE_CONNECTION_IS_VALID(data->gc))
  41.201 +	{
  41.202 +		ggp_oauth_data_free(data);
  41.203 +		return;
  41.204 +	}
  41.205 +	account = purple_connection_get_account(data->gc);
  41.206 +
  41.207 +	purple_debug_misc("gg", "ggp_oauth_authorization_done: "
  41.208 +		"authorization done, requesting access token...\n");
  41.209 +
  41.210 +	auth = gg_oauth_generate_header("POST", url,
  41.211 +		purple_account_get_username(account),
  41.212 +		purple_account_get_password(account),
  41.213 +		data->token, data->token_secret);
  41.214 +
  41.215 +	request = g_strdup_printf(
  41.216 +		"POST /access_token HTTP/1.1\r\n"
  41.217 +		"Host: api.gadu-gadu.pl\r\n"
  41.218 +		"%s\r\n"
  41.219 +		"Content-Length: 0\r\n"
  41.220 +		"\r\n",
  41.221 +		auth);
  41.222 +	free(auth);
  41.223 +	
  41.224 +	purple_util_fetch_url_request(account, url, FALSE, NULL, TRUE, request,
  41.225 +		FALSE, GGP_OAUTH_RESPONSE_MAX, ggp_oauth_access_token_got,
  41.226 +		data);
  41.227 +
  41.228 +	g_free(request);
  41.229 +}
  41.230 +
  41.231 +static void ggp_oauth_access_token_got(PurpleUtilFetchUrlData *url_data,
  41.232 +	gpointer user_data, const gchar *url_text, gsize len,
  41.233 +	const gchar *error_message)
  41.234 +{
  41.235 +	ggp_oauth_data *data = user_data;
  41.236 +	gchar *token, *token_secret;
  41.237 +	xmlnode *xml;
  41.238 +	gboolean succ = TRUE;
  41.239 +
  41.240 +	xml = xmlnode_from_str(url_text, -1);
  41.241 +	if (xml == NULL)
  41.242 +	{
  41.243 +		purple_debug_error("gg", "ggp_oauth_access_token_got: "
  41.244 +			"invalid xml\n");
  41.245 +		ggp_oauth_data_free(data);
  41.246 +		return;
  41.247 +	}
  41.248 +
  41.249 +	succ &= ggp_xml_get_string(xml, "oauth_token", &token);
  41.250 +	succ &= ggp_xml_get_string(xml, "oauth_token_secret",
  41.251 +		&token_secret);
  41.252 +	xmlnode_free(xml);
  41.253 +	if (!succ || strlen(token) < 10)
  41.254 +	{
  41.255 +		purple_debug_error("gg", "ggp_oauth_access_token_got: "
  41.256 +			"invalid xml - token is not present\n");
  41.257 +		ggp_oauth_data_free(data);
  41.258 +		return;
  41.259 +	}
  41.260 +
  41.261 +	if (data->sign_url)
  41.262 +	{
  41.263 +		PurpleAccount *account;
  41.264 +		gchar *auth;
  41.265 +		
  41.266 +		purple_debug_misc("gg", "ggp_oauth_access_token_got: "
  41.267 +			"got access token, returning signed url\n");
  41.268 +		
  41.269 +		account = purple_connection_get_account(data->gc);
  41.270 +		auth = gg_oauth_generate_header(
  41.271 +			data->sign_method, data->sign_url,
  41.272 +			purple_account_get_username(account),
  41.273 +			purple_account_get_password(account),
  41.274 +			token, token_secret);
  41.275 +		data->callback(data->gc, auth, data->user_data);
  41.276 +	}
  41.277 +	else
  41.278 +	{
  41.279 +		purple_debug_misc("gg", "ggp_oauth_access_token_got: "
  41.280 +			"got access token, returning it\n");
  41.281 +		data->callback(data->gc, token, data->user_data);
  41.282 +	}
  41.283 +
  41.284 +	g_free(token);
  41.285 +	g_free(token_secret);
  41.286 +	ggp_oauth_data_free(data);
  41.287 +}
    42.1 new file mode 100644
    42.2 --- /dev/null
    42.3 +++ b/libpurple/protocols/gg/oauth/oauth-purple.h
    42.4 @@ -0,0 +1,42 @@
    42.5 +/* purple
    42.6 + *
    42.7 + * Purple is the legal property of its developers, whose names are too numerous
    42.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    42.9 + * source distribution.
   42.10 + *
   42.11 + * Rewritten from scratch during Google Summer of Code 2012
   42.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   42.13 + *
   42.14 + * Previously implemented by:
   42.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   42.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   42.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   42.18 + *
   42.19 + * This program is free software; you can redistribute it and/or modify
   42.20 + * it under the terms of the GNU General Public License as published by
   42.21 + * the Free Software Foundation; either version 2 of the License, or
   42.22 + * (at your option) any later version.
   42.23 + *
   42.24 + * This program is distributed in the hope that it will be useful,
   42.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   42.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   42.27 + * GNU General Public License for more details.
   42.28 + *
   42.29 + * You should have received a copy of the GNU General Public License
   42.30 + * along with this program; if not, write to the Free Software
   42.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   42.32 + */
   42.33 +
   42.34 +#ifndef _GGP_OAUTH_PURPLE_H
   42.35 +#define _GGP_OAUTH_PURPLE_H
   42.36 +
   42.37 +#include <internal.h>
   42.38 +#include <libgadu.h>
   42.39 +
   42.40 +typedef void (*ggp_oauth_request_cb)(PurpleConnection *gc, const gchar *token,
   42.41 +	gpointer user_data);
   42.42 +
   42.43 +void ggp_oauth_request(PurpleConnection *gc, ggp_oauth_request_cb callback,
   42.44 +	gpointer user_data, const gchar *sign_method, const gchar *sign_url);
   42.45 +
   42.46 +#endif /* _GGP_OAUTH_PURPLE_H */
    43.1 new file mode 100644
    43.2 --- /dev/null
    43.3 +++ b/libpurple/protocols/gg/oauth/oauth.c
    43.4 @@ -0,0 +1,145 @@
    43.5 +/* purple
    43.6 + *
    43.7 + * Purple is the legal property of its developers, whose names are too numerous
    43.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    43.9 + * source distribution.
   43.10 + *
   43.11 + * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
   43.12 + * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
   43.13 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   43.14 + *
   43.15 + * This program is free software; you can redistribute it and/or modify
   43.16 + * it under the terms of the GNU General Public License as published by
   43.17 + * the Free Software Foundation; either version 2 of the License, or
   43.18 + * (at your option) any later version.
   43.19 + *
   43.20 + * This program is distributed in the hope that it will be useful,
   43.21 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   43.22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   43.23 + * GNU General Public License for more details.
   43.24 + *
   43.25 + * You should have received a copy of the GNU General Public License
   43.26 + * along with this program; if not, write to the Free Software
   43.27 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   43.28 + */
   43.29 +
   43.30 +#include "oauth.h"
   43.31 +
   43.32 +#include "oauth-parameter.h"
   43.33 +#include <cipher.h>
   43.34 +
   43.35 +char *gg_oauth_static_nonce;		/* dla unit testów */
   43.36 +char *gg_oauth_static_timestamp;	/* dla unit testów */
   43.37 +
   43.38 +static void gg_oauth_generate_nonce(char *buf, int len)
   43.39 +{
   43.40 +	const char charset[] = "0123456789";
   43.41 +
   43.42 +	if (buf == NULL || len < 1)
   43.43 +		return;
   43.44 +
   43.45 +	while (len > 1) {
   43.46 +		*buf++ = charset[(unsigned) (((float) sizeof(charset) - 1.0) * rand() / (RAND_MAX + 1.0))];
   43.47 +		len--;
   43.48 +	}
   43.49 +
   43.50 +	*buf = 0;
   43.51 +}
   43.52 +
   43.53 +static gchar *gg_hmac_sha1(const char *key, const char *message)
   43.54 +{
   43.55 +	PurpleCipherContext *context;
   43.56 +	guchar digest[20];
   43.57 +	
   43.58 +	context = purple_cipher_context_new_by_name("hmac", NULL);
   43.59 +	purple_cipher_context_set_option(context, "hash", "sha1");
   43.60 +	purple_cipher_context_set_key(context, (guchar *)key);
   43.61 +	purple_cipher_context_append(context, (guchar *)message, strlen(message));
   43.62 +	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
   43.63 +	purple_cipher_context_destroy(context);
   43.64 +	
   43.65 +	return purple_base64_encode(digest, sizeof(digest));
   43.66 +}
   43.67 +
   43.68 +static char *gg_oauth_generate_signature(const char *method, const char *url, const char *request, const char *consumer_secret, const char *token_secret)
   43.69 +{
   43.70 +	char *text, *key, *res;
   43.71 +	gchar *url_e, *request_e, *consumer_secret_e, *token_secret_e;
   43.72 +
   43.73 +	url_e = g_uri_escape_string(url, "?", FALSE);
   43.74 +	g_strdelimit(url_e, "?", '\0');
   43.75 +	request_e = g_uri_escape_string(request, NULL, FALSE);
   43.76 +	text = g_strdup_printf("%s&%s&%s", method, url_e, request_e);
   43.77 +	g_free(url_e);
   43.78 +	g_free(request_e);
   43.79 +
   43.80 +	consumer_secret_e = g_uri_escape_string(consumer_secret, NULL, FALSE);
   43.81 +	token_secret_e = token_secret ? g_uri_escape_string(token_secret, NULL, FALSE) : NULL;
   43.82 +	key = g_strdup_printf("%s&%s", consumer_secret_e, token_secret ? token_secret_e : "");
   43.83 +	g_free(consumer_secret_e);
   43.84 +	g_free(token_secret_e);
   43.85 +
   43.86 +	res = gg_hmac_sha1(key, text);
   43.87 +
   43.88 +	free(key);
   43.89 +	free(text);
   43.90 +
   43.91 +	return res;
   43.92 +}
   43.93 +
   43.94 +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)
   43.95 +{
   43.96 +	char *request, *signature, *res;
   43.97 +	char nonce[80], timestamp[16];
   43.98 +	gg_oauth_parameter_t *params = NULL;
   43.99 +
  43.100 +	if (gg_oauth_static_nonce == NULL)
  43.101 +		gg_oauth_generate_nonce(nonce, sizeof(nonce));
  43.102 +	else {
  43.103 +		strncpy(nonce, gg_oauth_static_nonce, sizeof(nonce) - 1);
  43.104 +		nonce[sizeof(nonce) - 1] = 0;
  43.105 +	}
  43.106 +
  43.107 +	if (gg_oauth_static_timestamp == NULL)
  43.108 +		snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL));
  43.109 +	else {
  43.110 +		strncpy(timestamp, gg_oauth_static_timestamp, sizeof(timestamp) - 1);
  43.111 +		timestamp[sizeof(timestamp) - 1] = 0;
  43.112 +	}
  43.113 +
  43.114 +	gg_oauth_parameter_set(&params, "oauth_consumer_key", consumer_key);
  43.115 +	gg_oauth_parameter_set(&params, "oauth_nonce", nonce);
  43.116 +	gg_oauth_parameter_set(&params, "oauth_signature_method", "HMAC-SHA1");
  43.117 +	gg_oauth_parameter_set(&params, "oauth_timestamp", timestamp);
  43.118 +	gg_oauth_parameter_set(&params, "oauth_token", token);
  43.119 +	gg_oauth_parameter_set(&params, "oauth_version", "1.0");
  43.120 +
  43.121 +	request = gg_oauth_parameter_join(params, 0);
  43.122 +
  43.123 +	signature = gg_oauth_generate_signature(method, url, request, consumer_secret, token_secret);
  43.124 +
  43.125 +	free(request);
  43.126 +
  43.127 +	gg_oauth_parameter_free(params);
  43.128 +	params = NULL;
  43.129 +
  43.130 +	if (signature == NULL)
  43.131 +		return NULL;
  43.132 +
  43.133 +	gg_oauth_parameter_set(&params, "oauth_version", "1.0");
  43.134 +	gg_oauth_parameter_set(&params, "oauth_nonce", nonce);
  43.135 +	gg_oauth_parameter_set(&params, "oauth_timestamp", timestamp);
  43.136 +	gg_oauth_parameter_set(&params, "oauth_consumer_key", consumer_key);
  43.137 +	gg_oauth_parameter_set(&params, "oauth_token", token);
  43.138 +	gg_oauth_parameter_set(&params, "oauth_signature_method", "HMAC-SHA1");
  43.139 +	gg_oauth_parameter_set(&params, "oauth_signature", signature);
  43.140 +
  43.141 +	free(signature);
  43.142 +
  43.143 +	res = gg_oauth_parameter_join(params, 1);
  43.144 +
  43.145 +	gg_oauth_parameter_free(params);
  43.146 +
  43.147 +	return res;
  43.148 +}
  43.149 +
    44.1 new file mode 100644
    44.2 --- /dev/null
    44.3 +++ b/libpurple/protocols/gg/oauth/oauth.h
    44.4 @@ -0,0 +1,34 @@
    44.5 +/* purple
    44.6 + *
    44.7 + * Purple is the legal property of its developers, whose names are too numerous
    44.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    44.9 + * source distribution.
   44.10 + *
   44.11 + * Code adapted from libgadu (C) 2008 Wojtek Kaniewski <wojtekka@irc.pl>
   44.12 + * (http://toxygen.net/libgadu/) during Google Summer of Code 2012
   44.13 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   44.14 + *
   44.15 + * This program is free software; you can redistribute it and/or modify
   44.16 + * it under the terms of the GNU General Public License as published by
   44.17 + * the Free Software Foundation; either version 2 of the License, or
   44.18 + * (at your option) any later version.
   44.19 + *
   44.20 + * This program is distributed in the hope that it will be useful,
   44.21 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   44.22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   44.23 + * GNU General Public License for more details.
   44.24 + *
   44.25 + * You should have received a copy of the GNU General Public License
   44.26 + * along with this program; if not, write to the Free Software
   44.27 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   44.28 + */
   44.29 +
   44.30 +#ifndef _GGP_OAUTH_H
   44.31 +#define _GGP_OAUTH_H
   44.32 +
   44.33 +#include <internal.h>
   44.34 +#include <libgadu.h>
   44.35 +
   44.36 +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);
   44.37 +
   44.38 +#endif /* _GGP_OAUTH_H */
    45.1 new file mode 100644
    45.2 --- /dev/null
    45.3 +++ b/libpurple/protocols/gg/pubdir-prpl.c
    45.4 @@ -0,0 +1,993 @@
    45.5 +/* purple
    45.6 + *
    45.7 + * Purple is the legal property of its developers, whose names are too numerous
    45.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    45.9 + * source distribution.
   45.10 + *
   45.11 + * Rewritten from scratch during Google Summer of Code 2012
   45.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   45.13 + *
   45.14 + * Previously implemented by:
   45.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   45.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   45.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   45.18 + *
   45.19 + * This program is free software; you can redistribute it and/or modify
   45.20 + * it under the terms of the GNU General Public License as published by
   45.21 + * the Free Software Foundation; either version 2 of the License, or
   45.22 + * (at your option) any later version.
   45.23 + *
   45.24 + * This program is distributed in the hope that it will be useful,
   45.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   45.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   45.27 + * GNU General Public License for more details.
   45.28 + *
   45.29 + * You should have received a copy of the GNU General Public License
   45.30 + * along with this program; if not, write to the Free Software
   45.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   45.32 + */
   45.33 +
   45.34 +#include "pubdir-prpl.h"
   45.35 +
   45.36 +#include <debug.h>
   45.37 +#include <request.h>
   45.38 +
   45.39 +#include "oauth/oauth-purple.h"
   45.40 +#include "xml.h"
   45.41 +#include "utils.h"
   45.42 +#include "status.h"
   45.43 +
   45.44 +typedef struct
   45.45 +{
   45.46 +	PurpleConnection *gc;
   45.47 +	ggp_pubdir_request_cb cb;
   45.48 +	void *user_data;
   45.49 +	enum
   45.50 +	{
   45.51 +		GGP_PUBDIR_REQUEST_TYPE_INFO,
   45.52 +		GGP_PUBDIR_REQUEST_TYPE_SEARCH,
   45.53 +	} type;
   45.54 +	union
   45.55 +	{
   45.56 +		struct
   45.57 +		{
   45.58 +			uin_t uin;
   45.59 +		} user_info;
   45.60 +		ggp_pubdir_search_form *search_form;
   45.61 +	} params;
   45.62 +} ggp_pubdir_request;
   45.63 +
   45.64 +void ggp_pubdir_request_free(ggp_pubdir_request *request);
   45.65 +void ggp_pubdir_record_free(ggp_pubdir_record *records, int count);
   45.66 +
   45.67 +static void ggp_pubdir_get_info_got_token(PurpleConnection *gc,
   45.68 +	const gchar *token, gpointer _request);
   45.69 +static void ggp_pubdir_got_data(PurpleUtilFetchUrlData *url_data,
   45.70 +	gpointer user_data, const gchar *url_text, gsize len,
   45.71 +	const gchar *error_message);
   45.72 +
   45.73 +static void ggp_pubdir_get_info_prpl_got(PurpleConnection *gc,
   45.74 +	int records_count, const ggp_pubdir_record *records, int next_offset,
   45.75 +	void *_uin);
   45.76 +
   45.77 +static void ggp_pubdir_request_buddy_alias_got(PurpleConnection *gc,
   45.78 +	int records_count, const ggp_pubdir_record *records, int next_offset,
   45.79 +	void *_uin);
   45.80 +
   45.81 +// Searching for buddies.
   45.82 +
   45.83 +#define GGP_PUBDIR_SEARCH_TITLE _("Gadu-Gadu Public Directory")
   45.84 +#define GGP_PUBDIR_SEARCH_PER_PAGE 20
   45.85 +
   45.86 +struct _ggp_pubdir_search_form
   45.87 +{
   45.88 +	gchar *nick, *city;
   45.89 +	ggp_pubdir_gender gender;
   45.90 +	int offset;
   45.91 +	int limit;
   45.92 +
   45.93 +	void *display_handle;
   45.94 +};
   45.95 +
   45.96 +void ggp_pubdir_search_form_free(ggp_pubdir_search_form *form);
   45.97 +ggp_pubdir_search_form * ggp_pubdir_search_form_clone(
   45.98 +	const ggp_pubdir_search_form *form);
   45.99 +
  45.100 +static void ggp_pubdir_search_request(PurpleConnection *gc,
  45.101 +	PurpleRequestFields *fields);
  45.102 +static gchar * ggp_pubdir_search_make_query(const ggp_pubdir_search_form *form);
  45.103 +static void ggp_pubdir_search_execute(PurpleConnection *gc,
  45.104 +	const ggp_pubdir_search_form *form,
  45.105 +	ggp_pubdir_request_cb cb, void *user_data);
  45.106 +static void ggp_pubdir_search_got_token(PurpleConnection *gc,
  45.107 +	const gchar *token, gpointer _request);
  45.108 +static void ggp_pubdir_search_results_display(PurpleConnection *gc,
  45.109 +	int records_count, const ggp_pubdir_record *records, int next_offset,
  45.110 +	void *user_data);
  45.111 +static void ggp_pubdir_search_results_close(gpointer _form);
  45.112 +static void ggp_pubdir_search_results_next(PurpleConnection *gc, GList *row,
  45.113 +	gpointer _form);
  45.114 +static void ggp_pubdir_search_results_add(PurpleConnection *gc, GList *row,
  45.115 +	gpointer _form);
  45.116 +static void ggp_pubdir_search_results_im(PurpleConnection *gc, GList *row,
  45.117 +	gpointer _form);
  45.118 +static void ggp_pubdir_search_results_info(PurpleConnection *gc, GList *row,
  45.119 +	gpointer _form);
  45.120 +static void ggp_pubdir_search_results_new(PurpleConnection *gc, GList *row,
  45.121 +	gpointer _form);
  45.122 +
  45.123 +// Own profile.
  45.124 +
  45.125 +static void ggp_pubdir_set_info_dialog(PurpleConnection *gc, int records_count,
  45.126 +	const ggp_pubdir_record *records, int next_offset, void *user_data);
  45.127 +static void ggp_pubdir_set_info_request(PurpleConnection *gc,
  45.128 +	PurpleRequestFields *fields);
  45.129 +static void ggp_pubdir_set_info_got_token(PurpleConnection *gc,
  45.130 +	const gchar *token, gpointer _record);
  45.131 +static void ggp_pubdir_set_info_got_response(PurpleUtilFetchUrlData *url_data,
  45.132 +	gpointer user_data, const gchar *url_text, gsize len,
  45.133 +	const gchar *error_message);
  45.134 +
  45.135 +/******************************************************************************/
  45.136 +
  45.137 +static const gchar *ggp_pubdir_provinces[] =
  45.138 +{
  45.139 +	"dolnośląskie",
  45.140 +	"kujawsko-pomorskie",
  45.141 +	"lubelskie",
  45.142 +	"lubuskie",
  45.143 +	"łódzkie",
  45.144 +	"małopolskie",
  45.145 +	"mazowieckie",
  45.146 +	"opolskie",
  45.147 +	"podkarpackie",
  45.148 +	"podlaskie",
  45.149 +	"pomorskie",
  45.150 +	"śląskie",
  45.151 +	"świętokrzyskie",
  45.152 +	"warmińsko-mazurskie",
  45.153 +	"wielkopolskie",
  45.154 +	"zachodniopomorskie",
  45.155 +};
  45.156 +
  45.157 +static int ggp_pubdir_provinces_count = sizeof(ggp_pubdir_provinces)/sizeof(gchar*);
  45.158 +
  45.159 +/******************************************************************************/
  45.160 +
  45.161 +void ggp_pubdir_record_free(ggp_pubdir_record *records, int count)
  45.162 +{
  45.163 +	int i;
  45.164 +	for (i = 0; i < count; i++)
  45.165 +	{
  45.166 +		g_free(records[i].label);
  45.167 +		g_free(records[i].nickname);
  45.168 +		g_free(records[i].first_name);
  45.169 +		g_free(records[i].last_name);
  45.170 +		g_free(records[i].city);
  45.171 +	}
  45.172 +	g_free(records);
  45.173 +}
  45.174 +
  45.175 +void ggp_pubdir_request_free(ggp_pubdir_request *request)
  45.176 +{
  45.177 +	if (request->type == GGP_PUBDIR_REQUEST_TYPE_SEARCH)
  45.178 +		ggp_pubdir_search_form_free(request->params.search_form);
  45.179 +	g_free(request);
  45.180 +}
  45.181 +
  45.182 +void ggp_pubdir_get_info(PurpleConnection *gc, uin_t uin,
  45.183 +	ggp_pubdir_request_cb cb, void *user_data)
  45.184 +{
  45.185 +	ggp_pubdir_request *request = g_new0(ggp_pubdir_request, 1);
  45.186 +	gchar *url;
  45.187 +
  45.188 +	request->type = GGP_PUBDIR_REQUEST_TYPE_INFO;
  45.189 +	request->gc = gc;
  45.190 +	request->cb = cb;
  45.191 +	request->user_data = user_data;
  45.192 +	request->params.user_info.uin = uin;
  45.193 +
  45.194 +	url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u", uin);
  45.195 +	ggp_oauth_request(gc, ggp_pubdir_get_info_got_token, request,
  45.196 +		"GET", url);
  45.197 +	g_free(url);
  45.198 +}
  45.199 +
  45.200 +static void ggp_pubdir_get_info_got_token(PurpleConnection *gc,
  45.201 +	const gchar *token, gpointer _request)
  45.202 +{
  45.203 +	gchar *http_request;
  45.204 +	ggp_pubdir_request *request = _request;
  45.205 +	gchar *url;
  45.206 +
  45.207 +	if (!token || !PURPLE_CONNECTION_IS_VALID(gc))
  45.208 +	{
  45.209 +		request->cb(gc, -1, NULL, 0, request->user_data);
  45.210 +		ggp_pubdir_request_free(request);
  45.211 +		return;
  45.212 +	}
  45.213 +
  45.214 +	url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u",
  45.215 +		request->params.user_info.uin);
  45.216 +	http_request = g_strdup_printf(
  45.217 +		"GET /users/%u HTTP/1.1\r\n"
  45.218 +		"Host: api.gadu-gadu.pl\r\n"
  45.219 +		"%s\r\n"
  45.220 +		"\r\n",
  45.221 +		request->params.user_info.uin,
  45.222 +		token);
  45.223 +	
  45.224 +	purple_util_fetch_url_request(purple_connection_get_account(gc), url,
  45.225 +		FALSE, NULL, TRUE, http_request, FALSE, -1,
  45.226 +		ggp_pubdir_got_data, request);
  45.227 +	
  45.228 +	g_free(url);
  45.229 +	g_free(http_request);
  45.230 +}
  45.231 +
  45.232 +static void ggp_pubdir_got_data(PurpleUtilFetchUrlData *url_data,
  45.233 +	gpointer _request, const gchar *url_text, gsize len,
  45.234 +	const gchar *error_message)
  45.235 +{
  45.236 +	ggp_pubdir_request *request = _request;
  45.237 +	PurpleConnection *gc = request->gc;
  45.238 +	gboolean succ = TRUE;
  45.239 +	xmlnode *xml;
  45.240 +	unsigned int status, next_offset;
  45.241 +	int record_count, i;
  45.242 +	ggp_pubdir_record *records;
  45.243 +
  45.244 +	//TODO: verbose
  45.245 +	//purple_debug_misc("gg", "ggp_pubdir_got_data: [%s]\n", url_text);
  45.246 +
  45.247 +	xml = xmlnode_from_str(url_text, -1);
  45.248 +	if (xml == NULL)
  45.249 +	{
  45.250 +		purple_debug_error("gg", "ggp_pubdir_got_data: "
  45.251 +			"invalid xml\n");
  45.252 +		request->cb(gc, -1, NULL, 0, request->user_data);
  45.253 +		ggp_pubdir_request_free(request);
  45.254 +		return;
  45.255 +	}
  45.256 +	
  45.257 +	succ &= ggp_xml_get_uint(xml, "status", &status);
  45.258 +	if (!ggp_xml_get_uint(xml, "nextOffset", &next_offset))
  45.259 +		next_offset = 0;
  45.260 +	xml = xmlnode_get_child(xml, "users");
  45.261 +	if (!succ || status != 0 || !xml)
  45.262 +	{
  45.263 +		purple_debug_error("gg", "ggp_pubdir_got_data: "
  45.264 +			"invalid reply\n");
  45.265 +		request->cb(gc, -1, NULL, 0, request->user_data);
  45.266 +		ggp_pubdir_request_free(request);
  45.267 +		return;
  45.268 +	}
  45.269 +	
  45.270 +	record_count = ggp_xml_child_count(xml, "user");
  45.271 +	records = g_new0(ggp_pubdir_record, record_count);
  45.272 +	
  45.273 +	xml = xmlnode_get_child(xml, "user");
  45.274 +	i = 0;
  45.275 +	while (xml)
  45.276 +	{
  45.277 +		ggp_pubdir_record *record = &records[i++];
  45.278 +		gchar *city = NULL, *birth_s = NULL;
  45.279 +		unsigned int gender = 0;
  45.280 +		const gchar *uin_s;
  45.281 +		
  45.282 +		g_assert(i <= record_count);
  45.283 +		
  45.284 +		record->uin = ggp_str_to_uin(xmlnode_get_attrib(xml, "uin"));
  45.285 +		if (record->uin == 0)
  45.286 +			ggp_xml_get_uint(xml, "uin", &record->uin);
  45.287 +		if (record->uin == 0)
  45.288 +			purple_debug_error("gg", "ggp_pubdir_got_data:"
  45.289 +				" invalid uin\n");
  45.290 +		uin_s = ggp_uin_to_str(record->uin);
  45.291 +		
  45.292 +		ggp_xml_get_string(xml, "label", &record->label);
  45.293 +		ggp_xml_get_string(xml, "nick", &record->nickname);
  45.294 +		ggp_xml_get_string(xml, "name", &record->first_name);
  45.295 +		ggp_xml_get_string(xml, "surname", &record->last_name);
  45.296 +		ggp_xml_get_string(xml, "city", &city);
  45.297 +		ggp_xml_get_string(xml, "birth", &birth_s);
  45.298 +		ggp_xml_get_uint(xml, "gender", &gender);
  45.299 +		ggp_xml_get_uint(xml, "age", &record->age);
  45.300 +		ggp_xml_get_uint(xml, "province", &record->province);
  45.301 +		
  45.302 +		record->label = ggp_free_if_equal(record->label, uin_s);
  45.303 +		record->label = ggp_free_if_equal(record->label, "");
  45.304 +		record->nickname = ggp_free_if_equal(record->nickname, uin_s);
  45.305 +		record->nickname = ggp_free_if_equal(record->nickname, "");
  45.306 +		record->first_name = ggp_free_if_equal(record->first_name, "");
  45.307 +		record->last_name = ggp_free_if_equal(record->last_name, "");
  45.308 +		
  45.309 +		if (record->label) {}
  45.310 +		else if (record->nickname)
  45.311 +			record->label = g_strdup(record->nickname);
  45.312 +		else if (record->first_name && record->last_name)
  45.313 +			record->label = g_strdup_printf("%s %s",
  45.314 +				record->first_name, record->last_name);
  45.315 +		else if (record->first_name)
  45.316 +			record->label = g_strdup(record->first_name);
  45.317 +		else if (record->last_name)
  45.318 +			record->label = g_strdup(record->last_name);
  45.319 +		if (record->label)
  45.320 +			g_strstrip(record->label);
  45.321 +		if (record->nickname)
  45.322 +			g_strstrip(record->nickname);
  45.323 +		
  45.324 +		if (gender == 1)
  45.325 +			record->gender = GGP_PUBDIR_GENDER_FEMALE;
  45.326 +		else if (gender == 2)
  45.327 +			record->gender = GGP_PUBDIR_GENDER_MALE;
  45.328 +		else
  45.329 +			record->gender = GGP_PUBDIR_GENDER_UNSPECIFIED;
  45.330 +		
  45.331 +		if (city && city[0] != '\0')
  45.332 +			record->city = g_strdup(city);
  45.333 +		if (record->city)
  45.334 +			g_strstrip(record->city);
  45.335 +		if (!record->city)
  45.336 +		{
  45.337 +			g_free(record->city);
  45.338 +			record->city = NULL;
  45.339 +		}
  45.340 +		
  45.341 +		record->birth = ggp_date_from_iso8601(birth_s);
  45.342 +		//TODO: calculate age from birth
  45.343 +		
  45.344 +		//TODO: verbose
  45.345 +		purple_debug_misc("gg", "ggp_pubdir_got_data: [uin:%d] "
  45.346 +			"[label:%s] [nick:%s] [first name:%s] [last name:%s] "
  45.347 +			"[city:%s] [gender:%d] [age:%d] [birth:%lu]\n",
  45.348 +			record->uin, record->label, record->nickname,
  45.349 +			record->first_name, record->last_name, record->city,
  45.350 +			record->gender, record->age, record->birth);
  45.351 +		
  45.352 +		g_free(city);
  45.353 +		
  45.354 +		xml = xmlnode_get_next_twin(xml);
  45.355 +	}
  45.356 +	
  45.357 +	request->cb(gc, record_count, records, next_offset, request->user_data);
  45.358 +	
  45.359 +	ggp_pubdir_request_free(request);
  45.360 +	ggp_pubdir_record_free(records, record_count);
  45.361 +}
  45.362 +
  45.363 +void ggp_pubdir_get_info_prpl(PurpleConnection *gc, const char *name)
  45.364 +{
  45.365 +	uin_t uin = ggp_str_to_uin(name);
  45.366 +
  45.367 +	purple_debug_info("gg", "ggp_pubdir_get_info_prpl: %u\n", uin);
  45.368 +
  45.369 +	ggp_pubdir_get_info(gc, uin, ggp_pubdir_get_info_prpl_got, (void*)uin);
  45.370 +}
  45.371 +
  45.372 +static void ggp_pubdir_get_info_prpl_got(PurpleConnection *gc,
  45.373 +	int records_count, const ggp_pubdir_record *records, int next_offset,
  45.374 +	void *_uin)
  45.375 +{
  45.376 +	uin_t uin = (uin_t)_uin;
  45.377 +	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
  45.378 +	const ggp_pubdir_record *record = &records[0];
  45.379 +	PurpleBuddy *buddy;
  45.380 +	
  45.381 +	if (records_count < 1)
  45.382 +	{
  45.383 +		purple_debug_error("gg", "ggp_pubdir_get_info_prpl_got: "
  45.384 +			"couldn't get info for %u\n", uin);
  45.385 +		purple_notify_user_info_add_pair_plaintext(info, NULL,
  45.386 +			_("Cannot get user information"));
  45.387 +		purple_notify_userinfo(gc, ggp_uin_to_str(uin), info,
  45.388 +			NULL, NULL);
  45.389 +		purple_notify_user_info_destroy(info);
  45.390 +		return;
  45.391 +	}
  45.392 +	
  45.393 +	purple_debug_info("gg", "ggp_pubdir_get_info_prpl_got: %u\n", uin);
  45.394 +	g_assert(uin == record->uin);
  45.395 +	g_assert(records_count == 1);
  45.396 +	
  45.397 +	buddy = purple_find_buddy(purple_connection_get_account(gc),
  45.398 +		ggp_uin_to_str(uin));
  45.399 +	if (buddy)
  45.400 +	{
  45.401 +		const char *alias;
  45.402 +		PurpleStatus *status;
  45.403 +		gchar *status_message;
  45.404 +		
  45.405 +		alias = purple_buddy_get_alias_only(buddy);
  45.406 +		if (alias)
  45.407 +			purple_notify_user_info_add_pair_plaintext(info,
  45.408 +				_("Alias"), alias);
  45.409 +		
  45.410 +		status = purple_presence_get_active_status(
  45.411 +			purple_buddy_get_presence(buddy));
  45.412 +		ggp_status_from_purplestatus(status, &status_message);
  45.413 +		purple_notify_user_info_add_pair_plaintext(info, _("Status"),
  45.414 +			purple_status_get_name(status));
  45.415 +		if (status_message)
  45.416 +			purple_notify_user_info_add_pair_plaintext(info,
  45.417 +				_("Message"), status_message);
  45.418 +	}
  45.419 +	
  45.420 +	if (record->nickname)
  45.421 +		purple_notify_user_info_add_pair_plaintext(info,
  45.422 +			_("Nickname"), record->nickname);
  45.423 +	if (record->first_name)
  45.424 +		purple_notify_user_info_add_pair_plaintext(info,
  45.425 +			_("First name"), record->first_name);
  45.426 +	if (record->last_name)
  45.427 +		purple_notify_user_info_add_pair_plaintext(info,
  45.428 +			_("Last name"), record->last_name);
  45.429 +	if (record->gender != GGP_PUBDIR_GENDER_UNSPECIFIED)
  45.430 +		purple_notify_user_info_add_pair_plaintext(info, _("Gender"),
  45.431 +			record->gender == GGP_PUBDIR_GENDER_FEMALE ?
  45.432 +			_("Female") : _("Male"));
  45.433 +	if (record->city)
  45.434 +		purple_notify_user_info_add_pair_plaintext(info, _("City"),
  45.435 +			record->city);
  45.436 +	if (record->birth)
  45.437 +		purple_notify_user_info_add_pair_plaintext(info, _("Birthday"),
  45.438 +			ggp_date_strftime("%Y-%m-%d", record->birth));
  45.439 +	else if (record->age)
  45.440 +	{
  45.441 +		gchar *age_s = g_strdup_printf("%d", record->age);
  45.442 +		purple_notify_user_info_add_pair_plaintext(info, _("Age"),
  45.443 +			age_s);
  45.444 +		g_free(age_s);
  45.445 +	}
  45.446 +	
  45.447 +	purple_notify_userinfo(gc, ggp_uin_to_str(uin), info, NULL, NULL);
  45.448 +	purple_notify_user_info_destroy(info);
  45.449 +}
  45.450 +
  45.451 +void ggp_pubdir_request_buddy_alias(PurpleConnection *gc, PurpleBuddy *buddy)
  45.452 +{
  45.453 +	uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
  45.454 +
  45.455 +	purple_debug_info("gg", "ggp_pubdir_request_buddy_alias: %u\n", uin);
  45.456 +
  45.457 +	ggp_pubdir_get_info(gc, uin, ggp_pubdir_request_buddy_alias_got, (void*)uin);
  45.458 +}
  45.459 +
  45.460 +static void ggp_pubdir_request_buddy_alias_got(PurpleConnection *gc,
  45.461 +	int records_count, const ggp_pubdir_record *records, int next_offset,
  45.462 +	void *_uin)
  45.463 +{
  45.464 +	uin_t uin = (uin_t)_uin;
  45.465 +	const gchar *alias;
  45.466 +	
  45.467 +	if (records_count < 0)
  45.468 +	{
  45.469 +		purple_debug_error("gg", "ggp_pubdir_request_buddy_alias_got: "
  45.470 +			"couldn't get info for %u\n", uin);
  45.471 +		return;
  45.472 +	}
  45.473 +	g_assert(uin == records[0].uin);
  45.474 +	
  45.475 +	alias = records[0].label;
  45.476 +	if (!alias)
  45.477 +	{
  45.478 +		purple_debug_info("gg", "ggp_pubdir_request_buddy_alias_got: "
  45.479 +			"public alias for %u is not available\n", uin);
  45.480 +		return;
  45.481 +	}
  45.482 +
  45.483 +	purple_debug_info("gg", "ggp_pubdir_request_buddy_alias_got: "
  45.484 +		"public alias for %u is \"%s\"\n", uin, alias);
  45.485 +	
  45.486 +	serv_got_alias(gc, ggp_uin_to_str(uin), alias);
  45.487 +}
  45.488 +
  45.489 +/*******************************************************************************
  45.490 + * Searching for buddies.
  45.491 + ******************************************************************************/
  45.492 +
  45.493 +void ggp_pubdir_search_form_free(ggp_pubdir_search_form *form)
  45.494 +{
  45.495 +	g_free(form->nick);
  45.496 +	g_free(form->city);
  45.497 +	g_free(form);
  45.498 +}
  45.499 +
  45.500 +ggp_pubdir_search_form * ggp_pubdir_search_form_clone(
  45.501 +	const ggp_pubdir_search_form *form)
  45.502 +{
  45.503 +	ggp_pubdir_search_form *dup = g_new(ggp_pubdir_search_form, 1);
  45.504 +	
  45.505 +	dup->nick = g_strdup(form->nick);
  45.506 +	dup->city = g_strdup(form->city);
  45.507 +	dup->gender = form->gender;
  45.508 +	dup->offset = form->offset;
  45.509 +	dup->limit = form->limit;
  45.510 +
  45.511 +	dup->display_handle = form->display_handle;
  45.512 +	
  45.513 +	return dup;
  45.514 +}
  45.515 +
  45.516 +void ggp_pubdir_search(PurpleConnection *gc,
  45.517 +	const ggp_pubdir_search_form *form)
  45.518 +{
  45.519 +	PurpleRequestFields *fields;
  45.520 +	PurpleRequestFieldGroup *group;
  45.521 +	PurpleRequestField *field;
  45.522 +	int default_gender;
  45.523 +	
  45.524 +	purple_debug_info("gg", "ggp_pubdir_search\n");
  45.525 +	
  45.526 +	fields = purple_request_fields_new();
  45.527 +	group = purple_request_field_group_new(NULL);
  45.528 +	purple_request_fields_add_group(fields, group);
  45.529 +	
  45.530 +	field = purple_request_field_string_new("name", _("Name"),
  45.531 +		form ? form->nick : NULL, FALSE);
  45.532 +	purple_request_field_group_add_field(group, field);
  45.533 +	
  45.534 +	field = purple_request_field_string_new("city", _("City"),
  45.535 +		form ? form->city : NULL, FALSE);
  45.536 +	purple_request_field_group_add_field(group, field);
  45.537 +	
  45.538 +	default_gender = 0;
  45.539 +	if (form && form->gender == GGP_PUBDIR_GENDER_MALE)
  45.540 +		default_gender = 1;
  45.541 +	else if (form && form->gender == GGP_PUBDIR_GENDER_FEMALE)
  45.542 +		default_gender = 2;
  45.543 +	
  45.544 +	field = purple_request_field_choice_new("gender", _("Gender"),
  45.545 +		default_gender);
  45.546 +	purple_request_field_choice_add(field, _("Male or female"));
  45.547 +	purple_request_field_choice_add(field, _("Male"));
  45.548 +	purple_request_field_choice_add(field, _("Female"));
  45.549 +	purple_request_field_group_add_field(group, field);
  45.550 +	
  45.551 +	purple_request_fields(gc, _("Find buddies"), _("Find buddies"),
  45.552 +		_("Please, enter your search criteria below"), fields,
  45.553 +		_("OK"), G_CALLBACK(ggp_pubdir_search_request),
  45.554 +		_("Cancel"), NULL,
  45.555 +		purple_connection_get_account(gc), NULL, NULL, gc);
  45.556 +}
  45.557 +
  45.558 +static void ggp_pubdir_search_request(PurpleConnection *gc,
  45.559 +	PurpleRequestFields *fields)
  45.560 +{
  45.561 +	ggp_pubdir_search_form *form = g_new0(ggp_pubdir_search_form, 1);
  45.562 +	int gender;
  45.563 +
  45.564 +	purple_debug_info("gg", "ggp_pubdir_search_request\n");
  45.565 +	
  45.566 +	form->nick = g_strdup(purple_request_fields_get_string(fields, "name"));
  45.567 +	form->city = g_strdup(purple_request_fields_get_string(fields, "city"));
  45.568 +	gender = purple_request_fields_get_choice(fields, "gender");
  45.569 +	if (gender == 1)
  45.570 +		form->gender = GGP_PUBDIR_GENDER_MALE;
  45.571 +	else if (gender == 2)
  45.572 +		form->gender = GGP_PUBDIR_GENDER_FEMALE;
  45.573 +	
  45.574 +	form->offset = 0;
  45.575 +	form->limit = GGP_PUBDIR_SEARCH_PER_PAGE;
  45.576 +	
  45.577 +	ggp_pubdir_search_execute(gc, form, ggp_pubdir_search_results_display,
  45.578 +		form);
  45.579 +}
  45.580 +
  45.581 +static gchar * ggp_pubdir_search_make_query(const ggp_pubdir_search_form *form)
  45.582 +{
  45.583 +	gchar *nick, *city, *gender;
  45.584 +	gchar *query;
  45.585 +	
  45.586 +	if (form->nick && form->nick[0] != '\0')
  45.587 +	{
  45.588 +		gchar *nick_e = g_uri_escape_string(form->nick, NULL, FALSE);
  45.589 +		nick = g_strdup_printf("&nick=%s", nick_e);
  45.590 +		g_free(nick_e);
  45.591 +	}
  45.592 +	else
  45.593 +		nick = g_strdup("");
  45.594 +	
  45.595 +	if (form->city && form->city[0] != '\0')
  45.596 +	{
  45.597 +		gchar *city_e = g_uri_escape_string(form->city, NULL, FALSE);
  45.598 +		city = g_strdup_printf("&city=%s", city_e);
  45.599 +		g_free(city_e);
  45.600 +	}
  45.601 +	else
  45.602 +		city = g_strdup("");
  45.603 +	
  45.604 +	if (form->gender != GGP_PUBDIR_GENDER_UNSPECIFIED)
  45.605 +		gender = g_strdup_printf("&gender=%d",
  45.606 +			form->gender == GGP_PUBDIR_GENDER_MALE ? 2 : 1);
  45.607 +	else
  45.608 +		gender = g_strdup("");
  45.609 +	
  45.610 +	query = g_strdup_printf("/users.xml?offset=%d&limit=%d%s%s%s",
  45.611 +		form->offset, form->limit, nick, city, gender);
  45.612 +	
  45.613 +	g_free(nick);
  45.614 +	g_free(city);
  45.615 +	g_free(gender);
  45.616 +	
  45.617 +	return query;
  45.618 +}
  45.619 +
  45.620 +static void ggp_pubdir_search_execute(PurpleConnection *gc,
  45.621 +	const ggp_pubdir_search_form *form,
  45.622 +	ggp_pubdir_request_cb cb, void *user_data)
  45.623 +{
  45.624 +	ggp_pubdir_request *request = g_new0(ggp_pubdir_request, 1);
  45.625 +	gchar *url;
  45.626 +	ggp_pubdir_search_form *local_form = ggp_pubdir_search_form_clone(form);
  45.627 +	gchar *query;
  45.628 +
  45.629 +	request->type = GGP_PUBDIR_REQUEST_TYPE_SEARCH;
  45.630 +	request->gc = gc;
  45.631 +	request->cb = cb;
  45.632 +	request->user_data = user_data;
  45.633 +	request->params.search_form = local_form;
  45.634 +
  45.635 +	query = ggp_pubdir_search_make_query(form);
  45.636 +	purple_debug_misc("gg", "ggp_pubdir_search_execute: %s\n", query);
  45.637 +	url = g_strdup_printf("http://api.gadu-gadu.pl%s", query);
  45.638 +	ggp_oauth_request(gc, ggp_pubdir_search_got_token, request,
  45.639 +		"GET", url);
  45.640 +	g_free(query);
  45.641 +	g_free(url);
  45.642 +}
  45.643 +
  45.644 +static void ggp_pubdir_search_got_token(PurpleConnection *gc,
  45.645 +	const gchar *token, gpointer _request)
  45.646 +{
  45.647 +	gchar *http_request;
  45.648 +	ggp_pubdir_request *request = _request;
  45.649 +	gchar *url;
  45.650 +	gchar *query;
  45.651 +
  45.652 +	if (!token || !PURPLE_CONNECTION_IS_VALID(gc))
  45.653 +	{
  45.654 +		request->cb(gc, -1, NULL, 0, request->user_data);
  45.655 +		ggp_pubdir_request_free(request);
  45.656 +		return;
  45.657 +	}
  45.658 +
  45.659 +	purple_debug_misc("gg", "ggp_pubdir_search_got_token\n");
  45.660 +
  45.661 +	query = ggp_pubdir_search_make_query(request->params.search_form);
  45.662 +	url = g_strdup_printf("http://api.gadu-gadu.pl%s", query);
  45.663 +	http_request = g_strdup_printf(
  45.664 +		"GET %s HTTP/1.1\r\n"
  45.665 +		"Host: api.gadu-gadu.pl\r\n"
  45.666 +		"%s\r\n"
  45.667 +		"\r\n",
  45.668 +		query, token);
  45.669 +	g_free(query);
  45.670 +	
  45.671 +	purple_util_fetch_url_request(purple_connection_get_account(gc), url,
  45.672 +		FALSE, NULL, TRUE, http_request, FALSE, -1,
  45.673 +		ggp_pubdir_got_data, request);
  45.674 +	
  45.675 +	g_free(url);
  45.676 +	g_free(http_request);
  45.677 +}
  45.678 +
  45.679 +
  45.680 +static void ggp_pubdir_search_results_display(PurpleConnection *gc,
  45.681 +	int records_count, const ggp_pubdir_record *records, int next_offset,
  45.682 +	void *_form)
  45.683 +{
  45.684 +	ggp_pubdir_search_form *form = _form;
  45.685 +	PurpleNotifySearchResults *results;
  45.686 +	int i;
  45.687 +
  45.688 +	purple_debug_info("gg", "ggp_pubdir_search_results_display: "
  45.689 +		"got %d records (next offset: %d)\n",
  45.690 +		records_count, next_offset);
  45.691 +
  45.692 +	if (records_count < 0 ||
  45.693 +		(records_count == 0 && form->offset != 0))
  45.694 +	{
  45.695 +		purple_notify_error(gc, GGP_PUBDIR_SEARCH_TITLE,
  45.696 +			_("Error while searching for buddies"), NULL);
  45.697 +		ggp_pubdir_search_form_free(form);
  45.698 +		return;
  45.699 +	}
  45.700 +	
  45.701 +	if (records_count == 0)
  45.702 +	{
  45.703 +		purple_notify_info(gc, GGP_PUBDIR_SEARCH_TITLE,
  45.704 +			_("No matching users found"),
  45.705 +			_("There are no users matching your search criteria."));
  45.706 +		ggp_pubdir_search_form_free(form);
  45.707 +		return;
  45.708 +	}
  45.709 +	
  45.710 +	form->offset = next_offset;
  45.711 +	
  45.712 +	results = purple_notify_searchresults_new();
  45.713 +	
  45.714 +	purple_notify_searchresults_column_add(results,
  45.715 +		purple_notify_searchresults_column_new(_("GG Number")));
  45.716 +	purple_notify_searchresults_column_add(results,
  45.717 +		purple_notify_searchresults_column_new(_("Name")));
  45.718 +	purple_notify_searchresults_column_add(results,
  45.719 +		purple_notify_searchresults_column_new(_("City")));
  45.720 +	purple_notify_searchresults_column_add(results,
  45.721 +		purple_notify_searchresults_column_new(_("Gender")));
  45.722 +	purple_notify_searchresults_column_add(results,
  45.723 +		purple_notify_searchresults_column_new(_("Age")));
  45.724 +	
  45.725 +	for (i = 0; i < records_count; i++)
  45.726 +	{
  45.727 +		GList *row = NULL;
  45.728 +		const ggp_pubdir_record *record = &records[i];
  45.729 +		gchar *gender = NULL, *age = NULL;
  45.730 +
  45.731 +		if (record->gender == GGP_PUBDIR_GENDER_MALE)
  45.732 +			gender = g_strdup("male");
  45.733 +		else if (record->gender == GGP_PUBDIR_GENDER_FEMALE)
  45.734 +			gender = g_strdup("female");
  45.735 +		
  45.736 +		if (record->age)
  45.737 +			age = g_strdup_printf("%d", record->age);
  45.738 +		
  45.739 +		row = g_list_append(row, g_strdup(ggp_uin_to_str(record->uin)));
  45.740 +		row = g_list_append(row, g_strdup(record->label));
  45.741 +		row = g_list_append(row, g_strdup(record->city));
  45.742 +		row = g_list_append(row, gender);
  45.743 +		row = g_list_append(row, age);
  45.744 +		purple_notify_searchresults_row_add(results, row);
  45.745 +	}
  45.746 +	
  45.747 +	purple_notify_searchresults_button_add(results,
  45.748 +		PURPLE_NOTIFY_BUTTON_ADD, ggp_pubdir_search_results_add);
  45.749 +	purple_notify_searchresults_button_add(results,
  45.750 +		PURPLE_NOTIFY_BUTTON_IM, ggp_pubdir_search_results_im);
  45.751 +	purple_notify_searchresults_button_add(results,
  45.752 +		PURPLE_NOTIFY_BUTTON_INFO, ggp_pubdir_search_results_info);
  45.753 +	purple_notify_searchresults_button_add_labeled(results, _("New search"),
  45.754 +		ggp_pubdir_search_results_new);
  45.755 +	if (next_offset != 0)
  45.756 +		purple_notify_searchresults_button_add(results,
  45.757 +			PURPLE_NOTIFY_BUTTON_CONTINUE,
  45.758 +			ggp_pubdir_search_results_next);
  45.759 +	
  45.760 +	if (!form->display_handle)
  45.761 +		form->display_handle = purple_notify_searchresults(gc,
  45.762 +			GGP_PUBDIR_SEARCH_TITLE, _("Search results"), NULL,
  45.763 +			results, ggp_pubdir_search_results_close, form);
  45.764 +	else
  45.765 +		purple_notify_searchresults_new_rows(gc, results,
  45.766 +			form->display_handle);
  45.767 +	g_assert(form->display_handle);
  45.768 +}
  45.769 +
  45.770 +static void ggp_pubdir_search_results_close(gpointer _form)
  45.771 +{
  45.772 +	ggp_pubdir_search_form *form = _form;
  45.773 +	ggp_pubdir_search_form_free(form);
  45.774 +}
  45.775 +
  45.776 +static void ggp_pubdir_search_results_next(PurpleConnection *gc, GList *row,
  45.777 +	gpointer _form)
  45.778 +{
  45.779 +	ggp_pubdir_search_form *form = _form;
  45.780 +	ggp_pubdir_search_execute(gc, form, ggp_pubdir_search_results_display,
  45.781 +		form);
  45.782 +}
  45.783 +
  45.784 +static void ggp_pubdir_search_results_add(PurpleConnection *gc, GList *row,
  45.785 +	gpointer _form)
  45.786 +{
  45.787 +	purple_blist_request_add_buddy(purple_connection_get_account(gc),
  45.788 +		g_list_nth_data(row, 0), NULL, g_list_nth_data(row, 1));
  45.789 +}
  45.790 +
  45.791 +static void ggp_pubdir_search_results_im(PurpleConnection *gc, GList *row,
  45.792 +	gpointer _form)
  45.793 +{
  45.794 +	purple_conversation_present(purple_conversation_new(PURPLE_CONV_TYPE_IM,
  45.795 +		purple_connection_get_account(gc), g_list_nth_data(row, 0)));
  45.796 +}
  45.797 +
  45.798 +static void ggp_pubdir_search_results_info(PurpleConnection *gc, GList *row,
  45.799 +	gpointer _form)
  45.800 +{
  45.801 +	ggp_pubdir_get_info_prpl(gc, g_list_nth_data(row, 0));
  45.802 +}
  45.803 +
  45.804 +static void ggp_pubdir_search_results_new(PurpleConnection *gc, GList *row,
  45.805 +	gpointer _form)
  45.806 +{
  45.807 +	ggp_pubdir_search_form *form = _form;
  45.808 +	ggp_pubdir_search(gc, form);
  45.809 +}
  45.810 +
  45.811 +/*******************************************************************************
  45.812 + * Own profile.
  45.813 + ******************************************************************************/
  45.814 +
  45.815 +void ggp_pubdir_set_info(PurpleConnection *gc)
  45.816 +{
  45.817 +	ggp_pubdir_get_info(gc, ggp_str_to_uin(purple_account_get_username(
  45.818 +		purple_connection_get_account(gc))),
  45.819 +		ggp_pubdir_set_info_dialog, NULL);
  45.820 +}
  45.821 +
  45.822 +static void ggp_pubdir_set_info_dialog(PurpleConnection *gc, int records_count,
  45.823 +	const ggp_pubdir_record *records, int next_offset, void *user_data)
  45.824 +{
  45.825 +	PurpleRequestFields *fields;
  45.826 +	PurpleRequestFieldGroup *group;
  45.827 +	PurpleRequestField *field;
  45.828 +	int default_gender, i;
  45.829 +	const ggp_pubdir_record *record;
  45.830 +	
  45.831 +	purple_debug_info("gg", "ggp_pubdir_set_info_dialog (record: %d)\n",
  45.832 +		records_count);
  45.833 +	
  45.834 +	record = (records_count == 1 ? &records[0] : NULL);
  45.835 +	
  45.836 +	fields = purple_request_fields_new();
  45.837 +	group = purple_request_field_group_new(NULL);
  45.838 +	purple_request_fields_add_group(fields, group);
  45.839 +	
  45.840 +	field = purple_request_field_string_new("first_name", _("First name"),
  45.841 +		record ? record->first_name : NULL, FALSE);
  45.842 +	purple_request_field_group_add_field(group, field);
  45.843 +
  45.844 +	field = purple_request_field_string_new("last_name", _("Last name"),
  45.845 +		record ? record->last_name : NULL, FALSE);
  45.846 +	purple_request_field_group_add_field(group, field);
  45.847 +	
  45.848 +	default_gender = -1;
  45.849 +	if (record && record->gender == GGP_PUBDIR_GENDER_MALE)
  45.850 +		default_gender = 0;
  45.851 +	else if (record && record->gender == GGP_PUBDIR_GENDER_FEMALE)
  45.852 +		default_gender = 1;
  45.853 +	
  45.854 +	field = purple_request_field_choice_new("gender", _("Gender"),
  45.855 +		default_gender);
  45.856 +	purple_request_field_set_required(field, TRUE);
  45.857 +	purple_request_field_choice_add(field, _("Male"));
  45.858 +	purple_request_field_choice_add(field, _("Female"));
  45.859 +	purple_request_field_group_add_field(group, field);
  45.860 +
  45.861 +	field = purple_request_field_string_new("birth_date", _("Birth Day"),
  45.862 +		(record && record->birth) ?
  45.863 +		ggp_date_strftime("%Y-%m-%d", record->birth) : NULL, FALSE);
  45.864 +	purple_request_field_set_required(field, TRUE);
  45.865 +	purple_request_field_group_add_field(group, field);
  45.866 +
  45.867 +	field = purple_request_field_string_new("city", _("City"),
  45.868 +		record ? record->city : NULL, FALSE);
  45.869 +	purple_request_field_group_add_field(group, field);
  45.870 +	
  45.871 +	field = purple_request_field_choice_new("province", _("Voivodeship"), 0);
  45.872 +	purple_request_field_group_add_field(group, field);
  45.873 +	purple_request_field_choice_add(field, _("Not specified"));
  45.874 +	for (i = 0; i < ggp_pubdir_provinces_count; i++)
  45.875 +	{
  45.876 +		purple_request_field_choice_add(field, ggp_pubdir_provinces[i]);
  45.877 +		if (record && i + 1 == record->province)
  45.878 +		{
  45.879 +			purple_request_field_choice_set_value(field, i + 1);
  45.880 +			purple_request_field_choice_set_default_value(field,
  45.881 +				i + 1); // TODO: libpurple bug?
  45.882 +		}
  45.883 +	}
  45.884 +	
  45.885 +	purple_request_fields(gc, _("Set User Info"), _("Set User Info"),
  45.886 +		NULL, fields,
  45.887 +		_("OK"), G_CALLBACK(ggp_pubdir_set_info_request),
  45.888 +		_("Cancel"), NULL,
  45.889 +		purple_connection_get_account(gc), NULL, NULL, gc);
  45.890 +	
  45.891 +}
  45.892 +
  45.893 +static void ggp_pubdir_set_info_request(PurpleConnection *gc,
  45.894 +	PurpleRequestFields *fields)
  45.895 +{
  45.896 +	gchar *url;
  45.897 +	uin_t uin = ggp_str_to_uin(purple_account_get_username(
  45.898 +		purple_connection_get_account(gc)));
  45.899 +	ggp_pubdir_record *record = g_new0(ggp_pubdir_record, 1);
  45.900 +	gchar *birth_s;
  45.901 +	
  45.902 +	purple_debug_info("gg", "ggp_pubdir_set_info_request\n");
  45.903 +
  45.904 +	record->uin = uin;
  45.905 +	record->first_name = g_strdup(purple_request_fields_get_string(fields,
  45.906 +		"first_name"));
  45.907 +	record->last_name = g_strdup(purple_request_fields_get_string(fields,
  45.908 +		"last_name"));
  45.909 +	if (purple_request_fields_get_choice(fields, "gender") == 0)
  45.910 +		record->gender = GGP_PUBDIR_GENDER_MALE;
  45.911 +	else
  45.912 +		record->gender = GGP_PUBDIR_GENDER_FEMALE;
  45.913 +	record->city = g_strdup(purple_request_fields_get_string(fields,
  45.914 +		"city"));
  45.915 +	record->province = purple_request_fields_get_choice(fields, "province");
  45.916 +	
  45.917 +	birth_s = g_strdup_printf("%sT10:00:00+00:00",
  45.918 +		purple_request_fields_get_string(fields, "birth_date"));
  45.919 +	record->birth = ggp_date_from_iso8601(birth_s);
  45.920 +	g_free(birth_s);
  45.921 +	purple_debug_info("gg", "ggp_pubdir_set_info_request: birth [%lu][%s]\n",
  45.922 +		record->birth, purple_request_fields_get_string(
  45.923 +		fields, "birth_date"));
  45.924 +
  45.925 +	url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u.xml", uin);
  45.926 +	ggp_oauth_request(gc, ggp_pubdir_set_info_got_token, record,
  45.927 +		"PUT", url);
  45.928 +	g_free(url);
  45.929 +}
  45.930 +
  45.931 +static void ggp_pubdir_set_info_got_token(PurpleConnection *gc,
  45.932 +	const gchar *token, gpointer _record)
  45.933 +{
  45.934 +	ggp_pubdir_record *record = _record;
  45.935 +	gchar *request, *request_data, *url;
  45.936 +	gchar *name, *surname, *city;
  45.937 +	uin_t uin = record->uin;
  45.938 +
  45.939 +	if (!token || !PURPLE_CONNECTION_IS_VALID(gc))
  45.940 +	{
  45.941 +		// TODO: notify about failure
  45.942 +		ggp_pubdir_record_free(record, 1);
  45.943 +		return;
  45.944 +	}
  45.945 +	
  45.946 +	name = g_uri_escape_string(record->first_name, NULL, FALSE);
  45.947 +	surname = g_uri_escape_string(record->last_name, NULL, FALSE);
  45.948 +	city = g_uri_escape_string(record->city, NULL, FALSE);
  45.949 +	
  45.950 +	request_data = g_strdup_printf(
  45.951 +		"name=%s&"
  45.952 +		"surname=%s&"
  45.953 +		"birth=%sT10:00:00%%2B00:00&"
  45.954 +		"birth_priv=2&"
  45.955 +		"gender=%d&"
  45.956 +		"gender_priv=2&"
  45.957 +		"city=%s&"
  45.958 +		"province=%d",
  45.959 +		name, surname,
  45.960 +		ggp_date_strftime("%Y-%m-%d", record->birth),
  45.961 +		record->gender,
  45.962 +		city,
  45.963 +		record->province);
  45.964 +	
  45.965 +	//TODO: verbose
  45.966 +	//purple_debug_misc("gg", "ggp_pubdir_set_info_got_token: query [%s]\n",
  45.967 +	//	request_data);
  45.968 +
  45.969 +	url = g_strdup_printf("http://api.gadu-gadu.pl/users/%u.xml", uin);
  45.970 +	request = g_strdup_printf(
  45.971 +		"PUT /users/%u.xml HTTP/1.1\r\n"
  45.972 +		"Host: api.gadu-gadu.pl\r\n"
  45.973 +		"%s\r\n"
  45.974 +		"Content-Length: %d\r\n"
  45.975 +		"Content-Type: application/x-www-form-urlencoded\r\n"
  45.976 +		"\r\n%s",
  45.977 +		uin, token, strlen(request_data), request_data);
  45.978 +
  45.979 +	purple_util_fetch_url_request(purple_connection_get_account(gc), url,
  45.980 +		FALSE, NULL, TRUE, request, FALSE, -1,
  45.981 +		ggp_pubdir_set_info_got_response, NULL);
  45.982 +
  45.983 +	g_free(request);
  45.984 +	g_free(request_data);
  45.985 +	g_free(url);
  45.986 +	ggp_pubdir_record_free(record, 1);
  45.987 +}
  45.988 +
  45.989 +static void ggp_pubdir_set_info_got_response(PurpleUtilFetchUrlData *url_data,
  45.990 +	gpointer user_data, const gchar *url_text, gsize len,
  45.991 +	const gchar *error_message)
  45.992 +{
  45.993 +	purple_debug_info("gg", "ggp_pubdir_set_info_got_response: [%s]\n", url_text);
  45.994 +	//<result><status>0</status></result>
  45.995 +	
  45.996 +	//TODO: notify about failure
  45.997 +}
    46.1 new file mode 100644
    46.2 --- /dev/null
    46.3 +++ b/libpurple/protocols/gg/pubdir-prpl.h
    46.4 @@ -0,0 +1,73 @@
    46.5 +/* purple
    46.6 + *
    46.7 + * Purple is the legal property of its developers, whose names are too numerous
    46.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    46.9 + * source distribution.
   46.10 + *
   46.11 + * Rewritten from scratch during Google Summer of Code 2012
   46.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   46.13 + *
   46.14 + * Previously implemented by:
   46.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   46.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   46.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   46.18 + *
   46.19 + * This program is free software; you can redistribute it and/or modify
   46.20 + * it under the terms of the GNU General Public License as published by
   46.21 + * the Free Software Foundation; either version 2 of the License, or
   46.22 + * (at your option) any later version.
   46.23 + *
   46.24 + * This program is distributed in the hope that it will be useful,
   46.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   46.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   46.27 + * GNU General Public License for more details.
   46.28 + *
   46.29 + * You should have received a copy of the GNU General Public License
   46.30 + * along with this program; if not, write to the Free Software
   46.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   46.32 + */
   46.33 +
   46.34 +#ifndef _GGP_PUBDIR_PRPL_H
   46.35 +#define _GGP_PUBDIR_PRPL_H
   46.36 +
   46.37 +#include <internal.h>
   46.38 +#include <libgadu.h>
   46.39 +
   46.40 +typedef enum
   46.41 +{
   46.42 +	GGP_PUBDIR_GENDER_UNSPECIFIED,
   46.43 +	GGP_PUBDIR_GENDER_FEMALE,
   46.44 +	GGP_PUBDIR_GENDER_MALE,
   46.45 +} ggp_pubdir_gender;
   46.46 +
   46.47 +typedef struct
   46.48 +{
   46.49 +	uin_t uin;
   46.50 +	gchar *label;
   46.51 +	gchar *nickname;
   46.52 +	gchar *first_name;
   46.53 +	gchar *last_name;
   46.54 +	ggp_pubdir_gender gender;
   46.55 +	gchar *city;
   46.56 +	unsigned int province;
   46.57 +	time_t birth;
   46.58 +	unsigned int age;
   46.59 +} ggp_pubdir_record;
   46.60 +
   46.61 +typedef struct _ggp_pubdir_search_form ggp_pubdir_search_form;
   46.62 +
   46.63 +typedef void (*ggp_pubdir_request_cb)(PurpleConnection *gc, int records_count,
   46.64 +	const ggp_pubdir_record *records, int next_offset, void *user_data);
   46.65 +
   46.66 +void ggp_pubdir_get_info(PurpleConnection *gc, uin_t uin,
   46.67 +	ggp_pubdir_request_cb cb, void *user_data);
   46.68 +
   46.69 +void ggp_pubdir_get_info_prpl(PurpleConnection *gc, const char *name);
   46.70 +void ggp_pubdir_request_buddy_alias(PurpleConnection *gc, PurpleBuddy *buddy);
   46.71 +
   46.72 +void ggp_pubdir_search(PurpleConnection *gc,
   46.73 +	const ggp_pubdir_search_form *form);
   46.74 +
   46.75 +void ggp_pubdir_set_info(PurpleConnection *gc);
   46.76 +
   46.77 +#endif /* _GGP_PUBDIR_PRPL_H */
    47.1 new file mode 100644
    47.2 --- /dev/null
    47.3 +++ b/libpurple/protocols/gg/purplew.c
    47.4 @@ -0,0 +1,149 @@
    47.5 +/* purple
    47.6 + *
    47.7 + * Purple is the legal property of its developers, whose names are too numerous
    47.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    47.9 + * source distribution.
   47.10 + *
   47.11 + * Rewritten from scratch during Google Summer of Code 2012
   47.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   47.13 + *
   47.14 + * Previously implemented by:
   47.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   47.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   47.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   47.18 + *
   47.19 + * This program is free software; you can redistribute it and/or modify
   47.20 + * it under the terms of the GNU General Public License as published by
   47.21 + * the Free Software Foundation; either version 2 of the License, or
   47.22 + * (at your option) any later version.
   47.23 + *
   47.24 + * This program is distributed in the hope that it will be useful,
   47.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   47.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   47.27 + * GNU General Public License for more details.
   47.28 + *
   47.29 + * You should have received a copy of the GNU General Public License
   47.30 + * along with this program; if not, write to the Free Software
   47.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   47.32 + */
   47.33 +
   47.34 +#include "purplew.h"
   47.35 +
   47.36 +#include <request.h>
   47.37 +#include <debug.h>
   47.38 +
   47.39 +guint ggp_purplew_http_input_add(struct gg_http *http_req,
   47.40 +	PurpleInputFunction func, gpointer user_data)
   47.41 +{
   47.42 +	PurpleInputCondition cond = 0;
   47.43 +	int check = http_req->check;
   47.44 +
   47.45 +	if (check & GG_CHECK_READ)
   47.46 +		cond |= PURPLE_INPUT_READ;
   47.47 +	if (check & GG_CHECK_WRITE)
   47.48 +		cond |= PURPLE_INPUT_WRITE;
   47.49 +
   47.50 +	//TODO: verbose mode
   47.51 +	//purple_debug_misc("gg", "ggp_purplew_http_input_add: "
   47.52 +	//	"[req=%x, fd=%d, cond=%d]\n",
   47.53 +	//	(unsigned int)http_req, http_req->fd, cond);
   47.54 +	return purple_input_add(http_req->fd, cond, func, user_data);
   47.55 +}
   47.56 +
   47.57 +static void ggp_purplew_request_processing_cancel(
   47.58 +	ggp_purplew_request_processing_handle *handle, gint id)
   47.59 +{
   47.60 +	handle->cancel_cb(handle->gc, handle->user_data);
   47.61 +	g_free(handle);
   47.62 +}
   47.63 +
   47.64 +ggp_purplew_request_processing_handle * ggp_purplew_request_processing(
   47.65 +	PurpleConnection *gc, const gchar *msg, void *user_data,
   47.66 +	ggp_purplew_request_processing_cancel_cb cancel_cb)
   47.67 +{
   47.68 +	ggp_purplew_request_processing_handle *handle =
   47.69 +		g_new(ggp_purplew_request_processing_handle, 1);
   47.70 +
   47.71 +	handle->gc = gc;
   47.72 +	handle->cancel_cb = cancel_cb;
   47.73 +	handle->user_data = user_data;
   47.74 +	handle->request_handle = purple_request_action(gc, _("Please wait..."),
   47.75 +		(msg ? msg : _("Please wait...")), NULL,
   47.76 +		PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc),
   47.77 +		NULL, NULL, handle, 1,
   47.78 +		_("Cancel"), G_CALLBACK(ggp_purplew_request_processing_cancel));
   47.79 +	
   47.80 +	return handle;
   47.81 +}
   47.82 +
   47.83 +void ggp_purplew_request_processing_done(
   47.84 +	ggp_purplew_request_processing_handle *handle)
   47.85 +{
   47.86 +	purple_request_close(PURPLE_REQUEST_ACTION, handle->request_handle);
   47.87 +	g_free(handle);
   47.88 +}
   47.89 +
   47.90 +PurpleGroup * ggp_purplew_buddy_get_group_only(PurpleBuddy *buddy)
   47.91 +{
   47.92 +	PurpleGroup *group = purple_buddy_get_group(buddy);
   47.93 +	if (!group)
   47.94 +		return NULL;
   47.95 +	if (0 == strcmp(GGP_PURPLEW_GROUP_DEFAULT, purple_group_get_name(group)))
   47.96 +		return NULL;
   47.97 +	return group;
   47.98 +}
   47.99 +
  47.100 +GList * ggp_purplew_group_get_buddies(PurpleGroup *group, PurpleAccount *account)
  47.101 +{
  47.102 +	GList *buddies = NULL;
  47.103 +	PurpleBlistNode *gnode, *cnode, *bnode;
  47.104 +	
  47.105 +	g_return_val_if_fail(group != NULL, NULL);
  47.106 +	
  47.107 +	gnode = PURPLE_BLIST_NODE(group);
  47.108 +	for (cnode = gnode->child; cnode; cnode = cnode->next)
  47.109 +	{
  47.110 +		if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
  47.111 +			continue;
  47.112 +		for (bnode = cnode->child; bnode; bnode = bnode->next)
  47.113 +		{
  47.114 +			PurpleBuddy *buddy;
  47.115 +			if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
  47.116 +				continue;
  47.117 +			
  47.118 +			buddy = PURPLE_BUDDY(bnode);
  47.119 +			if (account == NULL || buddy->account == account)
  47.120 +				buddies = g_list_append(buddies, buddy);
  47.121 +		}
  47.122 +	}
  47.123 +	
  47.124 +	return buddies;
  47.125 +}
  47.126 +
  47.127 +GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive)
  47.128 +{
  47.129 +	PurpleBlistNode *bnode;
  47.130 +	GList *groups = NULL;
  47.131 +	for (bnode = purple_blist_get_root(); bnode; bnode = bnode->next)
  47.132 +	{
  47.133 +		PurpleGroup *group;
  47.134 +		GSList *accounts;
  47.135 +		gboolean have_specified = FALSE, have_others = FALSE;
  47.136 +		
  47.137 +		if (!PURPLE_BLIST_NODE_IS_GROUP(bnode))
  47.138 +			continue;
  47.139 +		
  47.140 +		group = PURPLE_GROUP(bnode);
  47.141 +		for (accounts = purple_group_get_accounts(group); accounts; accounts = g_slist_delete_link(accounts, accounts))
  47.142 +		{
  47.143 +			if (accounts->data == account)
  47.144 +				have_specified = TRUE;
  47.145 +			else
  47.146 +				have_others = TRUE;
  47.147 +		}
  47.148 +		
  47.149 +		if (have_specified && (!exclusive || !have_others))
  47.150 +			groups = g_list_append(groups, group);
  47.151 +	}
  47.152 +	return groups;
  47.153 +}
    48.1 new file mode 100644
    48.2 --- /dev/null
    48.3 +++ b/libpurple/protocols/gg/purplew.h
    48.4 @@ -0,0 +1,78 @@
    48.5 +/* purple
    48.6 + *
    48.7 + * Purple is the legal property of its developers, whose names are too numerous
    48.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    48.9 + * source distribution.
   48.10 + *
   48.11 + * Rewritten from scratch during Google Summer of Code 2012
   48.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   48.13 + *
   48.14 + * Previously implemented by:
   48.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   48.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   48.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   48.18 + *
   48.19 + * This program is free software; you can redistribute it and/or modify
   48.20 + * it under the terms of the GNU General Public License as published by
   48.21 + * the Free Software Foundation; either version 2 of the License, or
   48.22 + * (at your option) any later version.
   48.23 + *
   48.24 + * This program is distributed in the hope that it will be useful,
   48.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   48.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   48.27 + * GNU General Public License for more details.
   48.28 + *
   48.29 + * You should have received a copy of the GNU General Public License
   48.30 + * along with this program; if not, write to the Free Software
   48.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   48.32 + */
   48.33 +
   48.34 +#ifndef _GGP_PURPLEW_H
   48.35 +#define _GGP_PURPLEW_H
   48.36 +
   48.37 +#include <internal.h>
   48.38 +#include <libgadu.h>
   48.39 +
   48.40 +#define GGP_PURPLEW_GROUP_DEFAULT _("Buddies")
   48.41 +
   48.42 +/**
   48.43 + * Adds an input handler in purple event loop for http request.
   48.44 + *
   48.45 + * @see purple_input_add
   48.46 + *
   48.47 + * @param http_req  Http connection to watch.
   48.48 + * @param func      The callback function for data.
   48.49 + * @param user_data User-specified data.
   48.50 + *
   48.51 + * @return The resulting handle (will be greater than 0).
   48.52 + */
   48.53 +guint ggp_purplew_http_input_add(struct gg_http *http_req,
   48.54 +	PurpleInputFunction func, gpointer user_data);
   48.55 +
   48.56 +typedef void (*ggp_purplew_request_processing_cancel_cb)(PurpleConnection *gc,
   48.57 +	void *user_data);
   48.58 +
   48.59 +typedef struct
   48.60 +{
   48.61 +	PurpleConnection *gc;
   48.62 +	ggp_purplew_request_processing_cancel_cb cancel_cb;
   48.63 +	void *request_handle;
   48.64 +	void *user_data;
   48.65 +} ggp_purplew_request_processing_handle;
   48.66 +
   48.67 +ggp_purplew_request_processing_handle * ggp_purplew_request_processing(
   48.68 +	PurpleConnection *gc, const gchar *msg, void *user_data,
   48.69 +	ggp_purplew_request_processing_cancel_cb oncancel);
   48.70 +
   48.71 +void ggp_purplew_request_processing_done(
   48.72 +	ggp_purplew_request_processing_handle *handle);
   48.73 +
   48.74 +// ignores default group
   48.75 +PurpleGroup * ggp_purplew_buddy_get_group_only(PurpleBuddy *buddy);
   48.76 +
   48.77 +GList * ggp_purplew_group_get_buddies(PurpleGroup *group, PurpleAccount *account);
   48.78 +
   48.79 +// you must g_free returned list
   48.80 +GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive);
   48.81 +
   48.82 +#endif /* _GGP_PURPLEW_H */
    49.1 new file mode 100644
    49.2 --- /dev/null
    49.3 +++ b/libpurple/protocols/gg/resolver-purple.c
    49.4 @@ -0,0 +1,192 @@
    49.5 +/* purple
    49.6 + *
    49.7 + * Purple is the legal property of its developers, whose names are too numerous
    49.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    49.9 + * source distribution.
   49.10 + *
   49.11 + * Rewritten from scratch during Google Summer of Code 2012
   49.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   49.13 + *
   49.14 + * Previously implemented by:
   49.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   49.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   49.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   49.18 + *
   49.19 + * This program is free software; you can redistribute it and/or modify
   49.20 + * it under the terms of the GNU General Public License as published by
   49.21 + * the Free Software Foundation; either version 2 of the License, or
   49.22 + * (at your option) any later version.
   49.23 + *
   49.24 + * This program is distributed in the hope that it will be useful,
   49.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   49.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   49.27 + * GNU General Public License for more details.
   49.28 + *
   49.29 + * You should have received a copy of the GNU General Public License
   49.30 + * along with this program; if not, write to the Free Software
   49.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   49.32 + */
   49.33 +
   49.34 +#include <internal.h>
   49.35 +#include <debug.h>
   49.36 +#include <dnsquery.h>
   49.37 +
   49.38 +#include <libgadu.h>
   49.39 +#include "resolver-purple.h"
   49.40 +
   49.41 +static int ggp_resolver_purple_start(int *fd, void **private_data,
   49.42 +	const char *hostname);
   49.43 +
   49.44 +static void ggp_resolver_purple_cleanup(void **private_data, int force);
   49.45 +
   49.46 +static void ggp_resolver_purple_cb(GSList *hosts, gpointer cbdata,
   49.47 +	const char *error_message);
   49.48 +
   49.49 +typedef struct
   49.50 +{
   49.51 +	PurpleDnsQueryData *purpleQuery;
   49.52 +	
   49.53 +	/**
   49.54 +	 * File descriptors:
   49.55 +	 *  pipes[0] - for reading
   49.56 +	 *  pipes[1] - for writing
   49.57 +	 */
   49.58 +	int pipes[2];
   49.59 +} ggp_resolver_purple_data;
   49.60 +
   49.61 +
   49.62 +extern void ggp_resolver_purple_setup(void)
   49.63 +{
   49.64 +	if (gg_global_set_custom_resolver(ggp_resolver_purple_start,
   49.65 +		ggp_resolver_purple_cleanup) != 0)
   49.66 +	{
   49.67 +		purple_debug_error("gg", "failed to set custom resolver\n");
   49.68 +	}
   49.69 +}
   49.70 +
   49.71 +void ggp_resolver_purple_cb(GSList *hosts, gpointer cbdata,
   49.72 +	const char *error_message)
   49.73 +{
   49.74 +	ggp_resolver_purple_data *data = (ggp_resolver_purple_data*)cbdata;
   49.75 +	const int fd = data->pipes[1];
   49.76 +	int ipv4_count, all_count, write_size;
   49.77 +	struct in_addr *addresses;
   49.78 +	
   49.79 +	purple_debug_misc("gg", "ggp_resolver_purple_cb(%x, %x, \"%s\")\n",
   49.80 +		(unsigned int)hosts, (unsigned int)cbdata, error_message);
   49.81 +	
   49.82 +	if (error_message)
   49.83 +	{
   49.84 +		purple_debug_error("gg", "ggp_resolver_purple_cb failed: %s\n",
   49.85 +			error_message);
   49.86 +	}
   49.87 +	
   49.88 +	all_count = g_slist_length(hosts);
   49.89 +	g_assert(all_count % 2 == 0);
   49.90 +	all_count /= 2;
   49.91 +	addresses = malloc((all_count + 1) * sizeof(struct in_addr));
   49.92 +	
   49.93 +	ipv4_count = 0;
   49.94 +	while (hosts && (hosts = g_slist_delete_link(hosts, hosts)))
   49.95 +	{
   49.96 +		const struct sockaddr *addr = hosts->data;
   49.97 +		char dst[INET6_ADDRSTRLEN];
   49.98 +		
   49.99 +		if (addr->sa_family == AF_INET6)
  49.100 +		{
  49.101 +			inet_ntop(addr->sa_family,
  49.102 +				&((struct sockaddr_in6 *) addr)->sin6_addr,
  49.103 +				dst, sizeof(dst));
  49.104 +			purple_debug_misc("gg", "ggp_resolver_purple_cb "
  49.105 +				"ipv6 (ignore): %s\n", dst);
  49.106 +		}
  49.107 +		else if (addr->sa_family == AF_INET)
  49.108 +		{
  49.109 +			const struct in_addr addr_ipv4 =
  49.110 +				((struct sockaddr_in *) addr)->sin_addr;
  49.111 +			inet_ntop(addr->sa_family, &addr_ipv4,
  49.112 +				dst, sizeof(dst));
  49.113 +			purple_debug_misc("gg", "ggp_resolver_purple_cb "
  49.114 +				"ipv4: %s\n", dst);
  49.115 +			
  49.116 +			g_assert(ipv4_count < all_count);
  49.117 +			addresses[ipv4_count++] = addr_ipv4;
  49.118 +		}
  49.119 +		else
  49.120 +		{
  49.121 +			purple_debug_warning("gg", "ggp_resolver_purple_cb "
  49.122 +				"unexpected sa_family: %d\n", addr->sa_family);
  49.123 +		}
  49.124 +		
  49.125 +		g_free(hosts->data);
  49.126 +		hosts = g_slist_delete_link(hosts, hosts);
  49.127 +	}
  49.128 +	
  49.129 +	addresses[ipv4_count].s_addr = INADDR_NONE;
  49.130 +	
  49.131 +	write_size = (ipv4_count + 1) * sizeof(struct in_addr);
  49.132 +	if (write(fd, addresses, write_size) != write_size)
  49.133 +	{
  49.134 +		purple_debug_error("gg",
  49.135 +			"ggp_resolver_purple_cb write error\n");
  49.136 +	}
  49.137 +	free(addresses);
  49.138 +}
  49.139 +
  49.140 +int ggp_resolver_purple_start(int *fd, void **private_data,
  49.141 +	const char *hostname)
  49.142 +{
  49.143 +	ggp_resolver_purple_data *data;
  49.144 +	purple_debug_misc("gg", "ggp_resolver_purple_start(%x, %x, \"%s\")\n",
  49.145 +		(unsigned int)fd, (unsigned int)private_data, hostname);
  49.146 +	
  49.147 +	data = malloc(sizeof(ggp_resolver_purple_data));
  49.148 +	*private_data = (void*)data;
  49.149 +	data->purpleQuery = NULL;
  49.150 +	data->pipes[0] = 0;
  49.151 +	data->pipes[1] = 0;
  49.152 +	
  49.153 +	if (purple_input_pipe(data->pipes) != 0)
  49.154 +	{
  49.155 +		purple_debug_error("gg", "ggp_resolver_purple_start: "
  49.156 +			"unable to create pipe\n");
  49.157 +		ggp_resolver_purple_cleanup(private_data, 0);
  49.158 +		return -1;
  49.159 +	}
  49.160 +	
  49.161 +	*fd = data->pipes[0];
  49.162 +	
  49.163 +	/* account and port is unknown in this context */
  49.164 +	data->purpleQuery = purple_dnsquery_a(NULL, hostname, 80,
  49.165 +		ggp_resolver_purple_cb, (gpointer)data);
  49.166 +
  49.167 +	if (!data->purpleQuery)
  49.168 +	{
  49.169 +		purple_debug_error("gg", "ggp_resolver_purple_start: "
  49.170 +			"unable to call purple_dnsquery_a\n");
  49.171 +		ggp_resolver_purple_cleanup(private_data, 0);
  49.172 +		return -1;
  49.173 +	}
  49.174 +	
  49.175 +	return 0;
  49.176 +}
  49.177 +
  49.178 +void ggp_resolver_purple_cleanup(void **private_data, int force)
  49.179 +{
  49.180 +	ggp_resolver_purple_data *data =
  49.181 +		(ggp_resolver_purple_data*)(*private_data);
  49.182 +	
  49.183 +	purple_debug_misc("gg", "ggp_resolver_purple_cleanup(%x, %d)\n",
  49.184 +		(unsigned int)private_data, force);
  49.185 +	
  49.186 +	if (!data)
  49.187 +		return;
  49.188 +	*private_data = NULL;
  49.189 +	
  49.190 +	if (data->pipes[0])
  49.191 +		close(data->pipes[0]);
  49.192 +	if (data->pipes[1])
  49.193 +		close(data->pipes[1]);
  49.194 +	
  49.195 +	free(data);
  49.196 +}
    50.1 new file mode 100644
    50.2 --- /dev/null
    50.3 +++ b/libpurple/protocols/gg/resolver-purple.h
    50.4 @@ -0,0 +1,38 @@
    50.5 +/* purple
    50.6 + *
    50.7 + * Purple is the legal property of its developers, whose names are too numerous
    50.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    50.9 + * source distribution.
   50.10 + *
   50.11 + * Rewritten from scratch during Google Summer of Code 2012
   50.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   50.13 + *
   50.14 + * Previously implemented by:
   50.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   50.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   50.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   50.18 + *
   50.19 + * This program is free software; you can redistribute it and/or modify
   50.20 + * it under the terms of the GNU General Public License as published by
   50.21 + * the Free Software Foundation; either version 2 of the License, or
   50.22 + * (at your option) any later version.
   50.23 + *
   50.24 + * This program is distributed in the hope that it will be useful,
   50.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   50.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   50.27 + * GNU General Public License for more details.
   50.28 + *
   50.29 + * You should have received a copy of the GNU General Public License
   50.30 + * along with this program; if not, write to the Free Software
   50.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   50.32 + */
   50.33 +
   50.34 +#ifndef _GGP_RESOLVER_PURPLE_H
   50.35 +#define _GGP_RESOLVER_PURPLE_H
   50.36 +
   50.37 +/**
   50.38 + * Registers custom resolver for libgadu, that uses libpurple for DNS queries.
   50.39 + */
   50.40 +void ggp_resolver_purple_setup(void);
   50.41 +
   50.42 +#endif /* _GGP_RESOLVER_PURPLE_H */
    51.1 new file mode 100644
    51.2 --- /dev/null
    51.3 +++ b/libpurple/protocols/gg/roster.c
    51.4 @@ -0,0 +1,1119 @@
    51.5 +/* purple
    51.6 + *
    51.7 + * Purple is the legal property of its developers, whose names are too numerous
    51.8 + * to list here.  Please refer to the COPYRIGHT file distributed with this
    51.9 + * source distribution.
   51.10 + *
   51.11 + * Rewritten from scratch during Google Summer of Code 2012
   51.12 + * by Tomek Wasilczyk (http://www.wasilczyk.pl).
   51.13 + *
   51.14 + * Previously implemented by:
   51.15 + *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
   51.16 + *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
   51.17 + *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
   51.18 + *
   51.19 + * This program is free software; you can redistribute it and/or modify
   51.20 + * it under the terms of the GNU General Public License as published by
   51.21 + * the Free Software Foundation; either version 2 of the License, or
   51.22 + * (at your option) any later version.
   51.23 + *
   51.24 + * This program is distributed in the hope that it will be useful,
   51.25 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   51.26 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   51.27 + * GNU General Public License for more details.
   51.28 + *
   51.29 + * You should have received a copy of the GNU General Public License
   51.30 + * along with this program; if not, write to the Free Software
   51.31 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   51.32 + */
   51.33 +
   51.34 +#include "roster.h"
   51.35 +
   51.36 +#include "gg.h"
   51.37 +#include "xml.h"
   51.38 +#include "utils.h"
   51.39 +#include "purplew.h"
   51.40 +
   51.41 +#include <debug.h>
   51.42 +
   51.43 +#define GGP_ROSTER_SYNC_SETT "gg-synchronized"
   51.44 +#define GGP_ROSTER_DEBUG 0
   51.45 +#define GGP_ROSTER_GROUPID_DEFAULT "00000000-0000-0000-0000-000000000000"
   51.46 +#define GGP_ROSTER_GROUPID_BOTS "0b345af6-0001-0000-0000-000000000004"
   51.47 +
   51.48 +// TODO: ignored contacts synchronization (?)
   51.49 +
   51.50 +typedef struct
   51.51 +{
   51.52 +	int version;
   51.53 +	
   51.54 +	xmlnode *xml;
   51.55 +	
   51.56 +	xmlnode *groups_node, *contacts_node;
   51.57 +	
   51.58 +	/**
   51.59 +	 * Key: (uin_t) user identifier
   51.60 +	 * Value: (xmlnode*) xml node for contact
   51.61 +	 */
   51.62 +	GHashTable *contact_nodes;
   51.63 +	
   51.64 +	/**
   51.65 +	 * Key: (gchar*) group id
   51.66 +	 * Value: (xmlnode*) xml node for group
   51.67 +	 */
   51.68 +	GHashTable *group_nodes;
   51.69 +	
   51.70 +	/**
   51.71 +	 * Key: (gchar*) group name
   51.72 +	 * Value: (gchar*) group id
   51.73 +	 */
   51.74 +	GHashTable *group_ids;
   51.75 +	
   51.76 +	/**
   51.77 +	 * Key: (gchar*) group id
   51.78 +	 * Value: (gchar*) group name
   51.79 +	 */
   51.80 +	GHashTable *group_names;
   51.81 +
   51.82 +	gchar *bots_group_id;
   51.83 +
   51.84 +	gboolean needs_update;
   51.85 +} ggp_roster_content;
   51.86 +
   51.87 +typedef struct
   51.88 +{
   51.89 +	enum
   51.90 +	{
   51.91 +		GGP_ROSTER_CHANGE_CONTACT_UPDATE,
   51.92 +		GGP_ROSTER_CHANGE_CONTACT_REMOVE,
   51.93 +		GGP_ROSTER_CHANGE_GROUP_RENAME,
   51.94 +	} type;
   51.95 +	union
   51.96 +	{
   51.97 +		uin_t uin;
   51.98 +		struct
   51.99 +		{
  51.100 +			gchar *old_name;
  51.101 +			gchar *new_name;
  51.102 +		} group_rename;
  51.103 +	} data;
  51.104 +} ggp_roster_change;
  51.105 +
  51.106 +static inline ggp_roster_session_data *
  51.107 +ggp_roster_get_rdata(PurpleConnection *gc);
  51.108 +static void ggp_roster_content_free(ggp_roster_content *content);
  51.109 +static void ggp_roster_change_free(gpointer change);
  51.110 +static int ggp_roster_get_version(PurpleConnection *gc);
  51.111 +static gboolean ggp_roster_timer_cb(gpointer _gc);
  51.112 +#if GGP_ROSTER_DEBUG
  51.113 +static void ggp_roster_dump(ggp_roster_content *content);
  51.114 +#endif
  51.115 +
  51.116 +// synchronization control
  51.117 +static gboolean ggp_roster_is_synchronized(PurpleBuddy *buddy);
  51.118 +static void ggp_roster_set_synchronized(PurpleConnection *gc,
  51.119 +	PurpleBuddy *buddy, gboolean synchronized);
  51.120 +
  51.121 +// buddy list import
  51.122 +static gboolean ggp_roster_reply_list_read_group(xmlnode *node,
  51.123 +	ggp_roster_content *content);
  51.124 +static gboolean ggp_roster_reply_list_read_buddy(PurpleConnection *gc,
  51.125 +	xmlnode *node, ggp_roster_content *content, GHashTable *remove_buddies);
  51.126 +static void ggp_roster_reply_list(PurpleConnection *gc, uint32_t version,
  51.127 +	const char *reply);
  51.128 +
  51.129 +// buddy list export
  51.130 +static const gchar * ggp_roster_send_update_group_add(
  51.131 +	ggp_roster_content *content, PurpleGroup *group);
  51.132 +static gboolean ggp_roster_send_update_contact_update(PurpleConnection *gc,
  51.133 +	ggp_roster_change *change);
  51.134 +static gboolean ggp_roster_send_update_contact_remove(PurpleConnection *gc,
  51.135 +	ggp_roster_change *change);
  51.136 +static gboolean ggp_roster_send_update_group_rename(PurpleConnection *gc,
  51.137 +	ggp_roster_change *change);
  51.138 +static void ggp_roster_send_update(PurpleConnection *gc);
  51.139 +static void ggp_roster_reply_ack(PurpleConnection *gc, uint32_t version);
  51.140 +static void ggp_roster_reply_reject(PurpleConnection *gc, uint32_t version);
  51.141 +
  51.142 +/******************************************************************************/
  51.143 +
  51.144 +static inline ggp_roster_session_data *
  51.145 +ggp_roster_get_rdata(PurpleConnection *gc)
  51.146 +{
  51.147 +	GGPInfo *accdata = purple_connection_get_protocol_data(gc);
  51.148 +	return &accdata->roster_data;
  51.149 +} 
  51.150 +
  51.151 +static void ggp_roster_content_free(ggp_roster_content *content)
  51.152 +{
  51.153 +	if (content == NULL)
  51.154 +		return;
  51.155 +	if (content->xml)
  51.156 +		xmlnode_free(content->xml);
  51.157 +	if (content->contact_nodes)
  51.158 +		g_hash_table_destroy(content->contact_nodes);
  51.159 +	if (content->group_nodes)
  51.160 +		g_hash_table_destroy(content->group_nodes);
  51.161 +	if (content->group_ids)
  51.162 +		g_hash_table_destroy(content->group_ids);
  51.163 +	if (content->group_names)
  51.164 +		g_hash_table_destroy(content->group_names);
  51.165 +	if (content->bots_group_id)
  51.166 +		g_free(content->bots_group_id);
  51.167 +	g_free(content);
  51.168 +}
  51.169 +
  51.170 +static void ggp_roster_change_free(gpointer _change)
  51.171 +{
  51.172 +	ggp_roster_change *change = _change;
  51.173 +	
  51.174 +	if (change->type == GGP_ROSTER_CHANGE_GROUP_RENAME)
  51.175 +	{
  51.176 +		g_free(change->data.group_rename.old_name);
  51.177 +		g_free(change->data.group_rename.new_name);
  51.178 +	}
  51.179 +	
  51.180 +	g_free(change);
  51.181 +}
  51.182 +
  51.183 +static int ggp_roster_get_version(PurpleConnection *gc)
  51.184 +{
  51.185 +	ggp_roster_content *content = ggp_roster_get_rdata(gc)->content;
  51.186 +	if (content == NULL)
  51.187 +		return 0;
  51.188 +	return content->version;
  51.189 +}
  51.190 +
  51.191 +static gboolean ggp_roster_timer_cb(gpointer _gc)
  51.192 +{
  51.193 +	PurpleConnection *gc = _gc;
  51.194 +	
  51.195 +	g_return_val_if_fail(PURPLE_CONNECTION_IS_VALID(gc), FALSE);
  51.196 +	
  51.197 +	ggp_roster_send_update(gc);
  51.198 +	
  51.199 +	return TRUE;
  51.200 +}
  51.201 +
  51.202 +#if GGP_ROSTER_DEBUG
  51.203 +static void ggp_roster_dump(ggp_roster_content *content)
  51.204 +{
  51.205 +	char *str;
  51.206 +	int len;
  51.207 +	
  51.208 +	g_return_if_fail(content != NULL);
  51.209 +	g_return_if_fail(content->xml != NULL);
  51.210 +	
  51.211 +	str = xmlnode_to_formatted_str(content->xml, &len);
  51.212 +	purple_debug_misc("gg", "ggp_roster_dump: [%s]\n", str);
  51.213 +	g_free(str);
  51.214 +}
  51.215 +#endif
  51.216 +
  51.217 +/*******************************************************************************
  51.218 + * Setup.
  51.219 + ******************************************************************************/
  51.220 +
  51.221 +gboolean ggp_roster_enabled(void)
  51.222 +{
  51.223 +	static gboolean checked = FALSE;
  51.224 +	static gboolean enabled;
  51.225 +	
  51.226 +	if (!checked)
  51.227 +	{
  51.228 +		enabled = gg_libgadu_check_feature(
  51.229 +			GG_LIBGADU_FEATURE_USERLIST100);
  51.230 +		checked = TRUE;
  51.231 +	}
  51.232 +	return enabled;
  51.233 +}
  51.234 +
  51.235 +void ggp_roster_setup(PurpleConnection *gc)
  51.236 +{
  51.237 +	ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
  51.238 +
  51.239 +	rdata->content = NULL;
  51.240 +	rdata->sent_updates = NULL;
  51.241 +	rdata->pending_updates = NULL;
  51.242 +	rdata->timer = 0;
  51.243 +	rdata->is_updating = FALSE;
  51.244 +	
  51.245 +	if (ggp_roster_enabled())
  51.246 +		rdata->timer = purple_timeout_add_seconds(2,
  51.247 +			ggp_roster_timer_cb, gc);
  51.248 +}
  51.249 +
  51.250 +void ggp_roster_cleanup(PurpleConnection *gc)
  51.251 +{
  51.252 +	ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
  51.253 +
  51.254 +	if (rdata->timer)
  51.255 +		purple_timeout_remove(rdata->timer);
  51.256 +	ggp_roster_content_free(rdata->content);
  51.257 +	g_list_free_full(rdata->sent_updates, ggp_roster_change_free);
  51.258 +	g_list_free_full(rdata->pending_updates, ggp_roster_change_free);
  51.259 +}
  51.260 +
  51.261 +/*******************************************************************************
  51.262 + * Synchronization control.
  51.263 + ******************************************************************************/
  51.264 +
  51.265 +static gboolean ggp_roster_is_synchronized(PurpleBuddy *buddy)
  51.266 +{
  51.267 +	gboolean ret = purple_blist_node_get_bool(PURPLE_BLIST_NODE(buddy),
  51.268 +		GGP_ROSTER_SYNC_SETT);
  51.269 +	return ret;
  51.270 +}
  51.271 +
  51.272 +static void ggp_roster_set_synchronized(PurpleConnection *gc,
  51.273 +	PurpleBuddy *buddy, gboolean synchronized)
  51.274 +{
  51.275 +	ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
  51.276 +	uin_t uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
  51.277 +	ggp_roster_change *change;
  51.278 +	
  51.279 +	purple_blist_node_set_bool(PURPLE_BLIST_NODE(buddy),
  51.280 +		GGP_ROSTER_SYNC_SETT, synchronized);
  51.281 +	if (!synchronized)
  51.282 +	{
  51.283 +		change = g_new0(ggp_roster_change, 1);
  51.284 +		change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE;
  51.285 +		change->data.uin = uin;
  51.286 +		rdata->pending_updates =
  51.287 +			g_list_append(rdata->pending_updates, change);
  51.288 +	}
  51.289 +}
  51.290 +
  51.291 +void ggp_roster_request_update(PurpleConnection *gc)
  51.292 +{
  51.293 +	GGPInfo *accdata = purple_connection_get_protocol_data(gc);
  51.294 +	int local_version = ggp_roster_get_version(gc);
  51.295 +	
  51.296 +	if (!ggp_roster_enabled())
  51.297 +	{
  51.298 +		purple_debug_warning("gg", "ggp_roster_request_update: "
  51.299 +			"feature disabled\n");
  51.300 +		return;
  51.301 +	}
  51.302 +	
  51.303 +	purple_debug_info("gg", "ggp_roster_request_update: local=%u\n",
  51.304 +		local_version);
  51.305 +	
  51.306 +	gg_userlist100_request(accdata->session, GG_USERLIST100_GET,
  51.307 +		local_version, GG_USERLIST100_FORMAT_TYPE_GG100, NULL);
  51.308 +}
  51.309 +
  51.310 +/*******************************************************************************
  51.311 + * Libgadu callbacks.
  51.312 + ******************************************************************************/
  51.313 +
  51.314 +void ggp_roster_reply(PurpleConnection *gc,
  51.315 +	struct gg_event_userlist100_reply *reply)
  51.316 +{
  51.317 +	if (GG_USERLIST100_FORMAT_TYPE_GG100 != reply->format_type)
  51.318 +	{
  51.319 +		purple_debug_warning("gg", "ggp_roster_reply: "
  51.320 +			"unsupported format type (%x)\n", reply->format_type);
  51.321 +		return;
  51.322 +	}
  51.323 +	
  51.324 +	if (reply->type == GG_USERLIST100_REPLY_LIST)
  51.325 +		ggp_roster_reply_list(gc, reply->version, reply->reply);
  51.326 +	else if (reply->type == 0x01) // list up to date (TODO: push to libgadu)
  51.327 +		purple_debug_info("gg", "ggp_roster_reply: list up to date\n");
  51.328 +	else if (reply->type == GG_USERLIST100_REPLY_ACK)
  51.329 +		ggp_roster_reply_ack(gc, reply->version);
  51.330 +	else if (reply->type == GG_USERLIST100_REPLY_REJECT)
  51.331 +		ggp_roster_reply_reject(gc, reply->version);
  51.332 +	else
  51.333 +		purple_debug_error("gg", "ggp_roster_reply: "
  51.334 +			"unsupported reply (%x)\n", reply->type);
  51.335 +}
  51.336 +
  51.337 +void ggp_roster_version(PurpleConnection *gc,
  51.338 +	struct gg_event_userlist100_version *version)
  51.339 +{
  51.340 +	int local_version = ggp_roster_get_version(gc);
  51.341 +	int remote_version = version->version;
  51.342 +
  51.343 +	purple_debug_info("gg", "ggp_roster_version: local=%u, remote=%u\n",
  51.344 +		local_version, remote_version);
  51.345 +	
  51.346 +	if (local_version < remote_version)
  51.347 +		ggp_roster_request_update(gc);
  51.348 +}
  51.349 +
  51.350 +/*******************************************************************************
  51.351 + * Libpurple callbacks.
  51.352 + ******************************************************************************/
  51.353 +
  51.354 +void ggp_roster_alias_buddy(PurpleConnection *gc, const char *who,
  51.355 +	const char *alias)
  51.356 +{
  51.357 +	PurpleBuddy *buddy;
  51.358 +	
  51.359 +	g_return_if_fail(who != NULL);
  51.360 +	
  51.361 +	if (!ggp_roster_enabled())
  51.362 +		return;
  51.363 +	
  51.364 +	purple_debug_misc("gg", "ggp_roster_alias_buddy(\"%s\", \"%s\")\n",
  51.365 +		who, alias);
  51.366 +	
  51.367 +	buddy = purple_find_buddy(purple_connection_get_account(gc), who);
  51.368 +	g_return_if_fail(buddy != NULL);
  51.369 +	
  51.370 +	ggp_roster_set_synchronized(gc, buddy, FALSE);
  51.371 +}
  51.372 +
  51.373 +void ggp_roster_group_buddy(PurpleConnection *gc, const char *who,
  51.374 +	const char *old_group, const char *new_group)
  51.375 +{
  51.376 +	ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
  51.377 +	ggp_roster_change *change = g_new0(ggp_roster_change, 1);
  51.378 +
  51.379 +	if (!ggp_roster_enabled())
  51.380 +		return;
  51.381 +	if (rdata->is_updating)
  51.382 +		return;
  51.383 +	
  51.384 +	purple_debug_misc("gg", "ggp_roster_group_buddy: "
  51.385 +		"who=\"%s\", group=\"%s\" -> \"%s\")\n",
  51.386 +		who, old_group, new_group);
  51.387 +	
  51.388 +	// purple_find_buddy(..., who) is not accessible at this moment
  51.389 +	change->type = GGP_ROSTER_CHANGE_CONTACT_UPDATE;
  51.390 +	change->data.uin = ggp_str_to_uin(who);
  51.391 +	rdata->pending_updates = g_list_append(rdata->pending_updates, change);
  51.392 +}
  51.393 +
  51.394 +void ggp_roster_rename_group(PurpleConnection *gc, const char *old_name,
  51.395 +	PurpleGroup *group, GList *moved_buddies)
  51.396 +{
  51.397 +	ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
  51.398 +	ggp_roster_change *change = g_new0(ggp_roster_change, 1);
  51.399 +	
  51.400 +	if (!ggp_roster_enabled())
  51.401 +		return;
  51.402 +	
  51.403 +	change->type = GGP_ROSTER_CHANGE_GROUP_RENAME;
  51.404 +	change->data.group_rename.old_name = g_strdup(old_name);
  51.405 +	change->data.group_rename.new_name =
  51.406 +		g_strdup(purple_group_get_name(group));
  51.407 +	rdata->pending_updates = g_list_append(rdata->pending_updates, change);
  51.408 +}
  51.409 +
  51.410 +void ggp_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
  51.411 +	PurpleGroup *group, const char *message)
  51.412 +{
  51.413 +	g_return_if_fail(gc != NULL);
  51.414 +	g_return_if_fail(buddy != NULL);
  51.415 +
  51.416 +	if (!ggp_roster_enabled())
  51.417 +		return;
  51.418 +	
  51.419 +	ggp_roster_set_synchronized(gc, buddy, FALSE);
  51.420 +}
  51.421 +
  51.422 +void ggp_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
  51.423 +	PurpleGroup *group)
  51.424 +{
  51.425 +	ggp_roster_session_data *rdata = ggp_roster_get_rdata(gc);
  51.426 +	ggp_roster_change *change = g_new0(ggp_roster_change, 1);
  51.427 +	
  51.428 +	if (!ggp_roster_enabled())
  51.429 +		return;
  51.430 +	
  51.431 +	change->type = GGP_ROSTER_CHANGE_CONTACT_REMOVE;