changeset 11360:cf15c1cdcfbd

[gaim-migrate @ 13582] New Gadu-Gadu implementation. committer: Tailor Script <tailor@pidgin.im>
author Bartoz Oler <bartosz@pidgin.im>
date Sun, 28 Aug 2005 22:46:01 +0000
parents 9480e0d0f563
children e4868370c433
files src/protocols/gg/Makefile.am src/protocols/gg/Makefile.mingw src/protocols/gg/common.c src/protocols/gg/gg.c src/protocols/gg/lib/COPYING src/protocols/gg/lib/common.c src/protocols/gg/lib/compat.h src/protocols/gg/lib/dcc.c src/protocols/gg/lib/events.c src/protocols/gg/lib/http.c src/protocols/gg/lib/libgadu-config.h src/protocols/gg/lib/libgadu.c src/protocols/gg/lib/libgadu.h src/protocols/gg/lib/obsolete.c src/protocols/gg/lib/pubdir.c src/protocols/gg/lib/pubdir50.c src/protocols/gg/libgg.c src/protocols/gg/libgg.h src/protocols/gg/protocol.txt
diffstat 19 files changed, 10848 insertions(+), 4535 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/gg/Makefile.am	Sun Aug 28 22:21:24 2005 +0000
+++ b/src/protocols/gg/Makefile.am	Sun Aug 28 22:46:01 2005 +0000
@@ -1,13 +1,18 @@
-EXTRA_DIST = \
-		protocol.txt \
-		Makefile.mingw
+EXTRA_DIST = Makefile.mingw
 
 pkgdir = $(libdir)/gaim
 
 GGSOURCES = \
-	libgg.c \
-	libgg.h \
-	common.c \
+	lib/common.c \
+	lib/dcc.c \
+	lib/events.c \
+	lib/http.c \
+	lib/libgadu.c \
+	lib/obsolete.c \
+	lib/pubdir.c \
+	lib/pubdir50.c \
+	lib/compat.h \
+	lib/libgadu.h \
 	gg.c
 
 AM_CFLAGS = $(st)
@@ -18,6 +23,7 @@
 
 st = -DGAIM_STATIC_PRPL
 noinst_LIBRARIES = libgg.a
+pkg_LTLIBRARIES =
 
 libgg_a_SOURCES = $(GGSOURCES)
 libgg_a_CFLAGS  = $(AM_CFLAGS)
@@ -26,6 +32,7 @@
 
 st =
 pkg_LTLIBRARIES = libgg.la
+noinst_LIBRARIES =
 
 libgg_la_SOURCES = $(GGSOURCES)
 
@@ -33,5 +40,7 @@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/protocols/gg/lib \
 	$(DEBUG_CFLAGS) \
 	$(GLIB_CFLAGS)
+
--- a/src/protocols/gg/Makefile.mingw	Sun Aug 28 22:21:24 2005 +0000
+++ b/src/protocols/gg/Makefile.mingw	Sun Aug 28 22:46:01 2005 +0000
@@ -48,6 +48,7 @@
 ##
 
 INCLUDE_PATHS +=	-I$(GG_ROOT) \
+			-I$(GG_ROOT)/lib \
 			-I$(GTK_TOP)/include \
 			-I$(GTK_TOP)/include/gtk-2.0 \
 			-I$(GTK_TOP)/include/glib-2.0 \
@@ -68,9 +69,18 @@
 ##  SOURCES, OBJECTS
 ##
 
-C_SRC =			libgg.c \
-		  	common.c \
-		  	gg.c
+C_SRC =	\
+	lib/common.c \
+	lib/dcc.c \
+	lib/events.c \
+	lib/http.c \
+	lib/libgadu.c \
+	lib/obsolete.c \
+	lib/pubdir.c \
+	lib/pubdir50.c \
+	lib/compat.h \
+	lib/libgadu.h \
+	gg.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
 
--- a/src/protocols/gg/common.c	Sun Aug 28 22:21:24 2005 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,338 +0,0 @@
-/*
- *  (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>,
- *                     Robert J. Woźny <speedy@ziew.org>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License Version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "debug.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifndef _WIN32
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/ioctl.h>
-#include <pwd.h>
-#include <sys/wait.h>
-#endif
-#include <sys/time.h>
-#include <errno.h>
-#ifndef _AIX
-#  include <string.h>
-#endif
-#include <stdarg.h>
-#include <time.h>
-#ifdef sun
-  #include <sys/filio.h>
-#endif
-#include "config.h"
-#include "libgg.h"
-#include <glib.h>
-
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
-/*
- * gg_debug()
- *
- * wyrzuca komunikat o danym poziomie, o ile użytkownik sobie tego życzy.
- *
- *  - level - poziom wiadomości,
- *  - format... - treść wiadomości (printf-alike.)
- *
- * niczego nie zwraca.
- */
-void gg_debug(int level, char *format, ...)
-{
-	va_list ap;
-	
-	if ((gg_debug_level & level)) {
-		va_start(ap, format);
-		/* vprintf(format, ap); */
-		gaim_debug_vargs(GAIM_DEBUG_INFO, "gg", format, ap);
-		va_end(ap);
-	}
-}
-
-/*
- * gg_alloc_sprintf()
- *
- * robi dokładnie to samo, co sprintf(), tyle że alokuje sobie wcześniej
- * miejsce na dane. powinno działać na tych maszynach, które mają funkcję
- * vsnprintf() zgodną z C99, jak i na wcześniejszych.
- *
- *  - format, ... - parametry takie same jak w innych funkcjach *printf()
- *
- * zwraca zaalokowany buforek, który wypadałoby później zwolnić, lub NULL
- * jeśli nie udało się wykonać zadania.
- */
-char *gg_alloc_sprintf(char *format, ...)
-{
-        va_list ap;
-        char *buf = NULL, *tmp;
-        int size = 0, res;
-
-        va_start(ap, format);
-
-        if ((size = g_vsnprintf(buf, 0, format, ap)) < 1) {
-                size = 128;
-                do {
-                        size *= 2;
-                        if (!(tmp = realloc(buf, size))) {
-                                free(buf);
-                                return NULL;
-                        }
-                        buf = tmp;
-                        res = g_vsnprintf(buf, size, format, ap);
-                } while (res == size - 1);
-        } else {
-                if (!(buf = malloc(size + 1)))
-                        return NULL;
-        }
-
-        g_vsnprintf(buf, size + 1, format, ap);
-
-        va_end(ap);
-
-        return buf;
-}
-
-/*
- * gg_get_line()
- * 
- * podaje kolejną linię z bufora tekstowego. psuje co bezpowrotnie, dzieląc
- * na kolejne stringi. zdarza się, nie ma potrzeby pisania funkcji dublującej
- * bufor żeby tylko mieć nieruszone dane wejściowe, skoro i tak nie będą nam
- * poźniej potrzebne. obcina `\r\n'.
- * 
- *  - ptr - wskaźnik do zmiennej, która przechowuje aktualną pozycję
- *    w przemiatanym buforze.
- * 
- * wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec bufora.
- */
-char *gg_get_line(char **ptr)
-{
-        char *foo, *res;
-
-        if (!ptr || !*ptr || !strcmp(*ptr, ""))
-                return NULL;
-
-        res = *ptr;
-
-        if (!(foo = strchr(*ptr, '\n')))
-                *ptr += strlen(*ptr);
-        else {
-                *ptr = foo + 1;
-                *foo = 0;
-                if (res[strlen(res) - 1] == '\r')
-                        res[strlen(res) - 1] = 0;
-        }
-
-        return res;
-}
-
-/*
- * gg_connect()
- *
- * łączy się z serwerem. pierwszy argument jest typu (void *), żeby nie
- * musieć niczego inkludować w libgg.h i nie psuć jakiś głupich zależności
- * na dziwnych systemach.
- *
- *  - addr - adres serwera (struct in_addr *),
- *  - port - port serwera,
- *  - async - ma być asynchroniczne połączenie?
- *
- * zwraca połączonego socketa lub -1 w przypadku błędu. zobacz errno.
- */
-int gg_connect(void *addr, int port, int async)
-{
-	int sock, ret, one = 1;
-	struct sockaddr_in sin;
-	struct in_addr *a = addr;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
-	
-	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-		gg_debug(GG_DEBUG_MISC, "-- socket() failed. errno = %d (%s)\n", errno, strerror(errno));
-		return -1;
-	}
-
-	if (async) {
-		if (ioctl(sock, FIONBIO, &one) == -1) {
-			gg_debug(GG_DEBUG_MISC, "-- ioctl() failed. errno = %d (%s)\n", errno, strerror(errno));
-			return -1;
-		}
-	}
-
-	sin.sin_port = htons(port);
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = a->s_addr;
-	
-	if ((ret = connect(sock, (struct sockaddr*) &sin, sizeof(sin))) == -1) {
-		if (errno && (!async || errno != EINPROGRESS)) {
-			gg_debug(GG_DEBUG_MISC, "-- connect() failed. errno = %d (%s)\n", errno, strerror(errno));
-			return -1;
-		}
-		gg_debug(GG_DEBUG_MISC, "-- connect() in progress\n");
-	}
-	
-	return sock;
-}
-
-/*
- * gg_read_line()
- *
- * czyta jedną linię tekstu z socketa.
- *
- *  - sock - socket,
- *  - buf - wskaźnik bufora,
- *  - length - długość bufora.
- *
- * olewa błędy. jeśli na jakiś trafi, potraktuje go jako koniec linii.
- */
-void gg_read_line(int sock, char *buf, int length)
-{
-	int ret;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_read_line(...);\n");
-	
-	for (; length > 1; buf++, length--) {
-		do {
-			if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) {
-				*buf = 0;
-				return;
-			}
-		} while (ret == -1 && errno == EINTR);
-
-		if (*buf == '\n') {
-			buf++;
-			break;
-		}
-	}
-
-	*buf = 0;
-	return;
-}
-
-/*
- * gg_chomp()
- *
- * ucina "\r\n" lub "\n" z końca linii.
- *
- *  - line - ofiara operacji plastycznej.
- *
- * niczego nie zwraca.
- */
-void gg_chomp(char *line)
-{
-	if (!line || strlen(line) < 1)
-		return;
-
-	if (line[strlen(line) - 1] == '\n')
-		line[strlen(line) - 1] = 0;
-	if (line[strlen(line) - 1] == '\r')
-		line[strlen(line) - 1] = 0;
-}
-
-
-/*
- * gg_urlencode() // funkcja wewnętrzna
- *
- * zamienia podany tekst na ciąg znaków do formularza http. przydaje się
- * przy szukaniu userów z dziwnymi znaczkami.
- *
- *  - str - ciąg znaków do poprawki.
- *
- * zwraca zaalokowany bufor, który wypadałoby kiedyś zwolnić albo NULL
- * w przypadku błędu.
- */
-char *gg_urlencode(const char *str)
-{
-	const char *p, hex[] = "0123456789abcdef";
-	char *q, *buf;
-
-	int size = 0;
-
-	if (!str)
-		str = "";
-
-	for (p = str; *p; p++, size++) {
-		if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')))
-			size += 2;
-	}
-
-	buf = g_new(char, size + 1);
-
-	for (p = str, q = buf; *p; p++, q++) {
-		if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9'))
-			*q = *p;
-		else {
-			*q++ = '%';
-			*q++ = hex[*p >> 4 & 15];
-			*q = hex[*p & 15];
-		}
-	}
-
-	*q = 0;
-
-	return buf;
-}
-
-/*
- * gg_http_hash()
- *
- * funkcja, która liczy hash dla adresu e-mail i hasła.
- *
- *  - email - adres email,
- *  - password - hasło.
- *
- * zwraca hash wykorzystywany przy rejestracji i wszelkich
- * manipulacjach własnego wpisu w katalogu publicznym.
- */
-
-int gg_http_hash(const char *email, const char *password)
-{
-	unsigned int a, c;
-	int b, i;
-	b = (-1);
-
-	i = 0;
-	while ((c = (int) email[i++]) != 0) {
-		a = (c ^ b) + (c << 8);
-		b = (a >> 24) | (a << 8);
-	}
-
-	i = 0;
-	while ((c = (int) password[i++]) != 0) {
-		a = (c ^ b) + (c << 8);
-		b = (a >> 24) | (a << 8);
-	}
-
-	return (b < 0 ? -b : b);
-}
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */
--- a/src/protocols/gg/gg.c	Sun Aug 28 22:21:24 2005 +0000
+++ b/src/protocols/gg/gg.c	Sun Aug 28 22:46:01 2005 +0000
@@ -1,89 +1,79 @@
+
 /*
- * gaim - Gadu-Gadu Protocol Plugin
- *
- * Copyright (C) 2001 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * NOTES
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
+ * I don't like automatic updates of the buddylist stored on the server, so not
+ * going to implement this. Maybe some kind of option to enable/disable this
+ * feature.
  */
+
 #include "internal.h"
 
-/* Library from EKG (Eksperymentalny Klient Gadu-Gadu) */
-#include "libgg.h"
-
-#include "account.h"
-#include "accountopt.h"
+#include "plugin.h"
+#include "version.h"
+#include "notify.h"
+#include "status.h"
 #include "blist.h"
-#include "connection.h"
+#include "accountopt.h"
 #include "debug.h"
-#include "notify.h"
-#include "proxy.h"
-#include "prpl.h"
-#include "server.h"
 #include "util.h"
-#include "version.h"
+#include "request.h"
 
-#define GG_CONNECT_STEPS 5
+#include "lib/libgadu.h"
 
-#define AGG_BUF_LEN 1024
+static GaimPlugin *my_protocol = NULL;
 
-#define AGG_GENDER_NONE -1
+typedef struct
+{
+	char *token_id;
 
-#define AGG_PUBDIR_USERLIST_EXPORT_FORM "/appsvc/fmcontactsput.asp"
-#define AGG_PUBDIR_USERLIST_IMPORT_FORM "/appsvc/fmcontactsget.asp"
-#define AGG_PUBDIR_SEARCH_FORM "/appsvc/fmpubquery2.asp"
-#define AGG_REGISTER_DATA_FORM "/appsvc/fmregister.asp"
-#define AGG_PUBDIR_MAX_ENTRIES 200
+} GGPToken;
+
+typedef struct {
 
-#define AGG_STATUS_AVAIL              _("Available")
-#define AGG_STATUS_AVAIL_FRIENDS      _("Available for friends only")
-#define AGG_STATUS_BUSY               _("Away")
-#define AGG_STATUS_BUSY_FRIENDS       _("Away for friends only")
-#define AGG_STATUS_INVISIBLE          _("Invisible")
-#define AGG_STATUS_INVISIBLE_FRIENDS  _("Invisible for friends only")
-#define AGG_STATUS_NOT_AVAIL          _("Unavailable")
+	char *uin;
+	char *lastname;
+	char *firstname;
+	char *nickname;
+	char *city;
+	char *birthyear;
+	char *gender;
+	char *active;
+	char *offset;
 
-#define AGG_HTTP_NONE			0
-#define AGG_HTTP_SEARCH			1
-#define AGG_HTTP_USERLIST_IMPORT	2
-#define AGG_HTTP_USERLIST_EXPORT	3
-#define AGG_HTTP_USERLIST_DELETE	4
-#define AGG_HTTP_PASSWORD_CHANGE	5
+	char *last_uin;
+
+} GGPSearchForm;
+
+typedef struct {
 
-#define UC_NORMAL 2
+	struct gg_session *session;
+	GGPSearchForm *search_form;
+	GGPToken *register_token;
+	GGPToken *chpasswd_token;
+	void *searchresults_window;
 
-struct agg_data {
-	struct gg_session *sess;
-	int own_status;
-};
+} GGPInfo;
 
-struct agg_http {
-	GaimConnection *gc;
-	gchar *request;
-	gchar *form;
-	gchar *host;
-	int inpa;
-	int type;
-};
-
-
+/**
+ * Convert enconding of a given string.
+ *
+ * @param locstr Input string.
+ * @param encsrc Current encoding of the string.
+ * @param encdst Target encoding of the string.
+ *
+ * @return Converted string (it must be g_free()ed when not used. Or NULL if
+ * locstr is NULL.
+ */
+/* static gchar *charset_convert(const gchar *locstr, const char *encsrc, const char *encdst) {{{ */
 static gchar *charset_convert(const gchar *locstr, const char *encsrc, const char *encdst)
 {
 	gchar *msg;
 	GError *err = NULL;
 
+	if (locstr == NULL)
+		return NULL;
+
 	msg = g_convert_with_fallback(locstr, strlen(locstr), encdst, encsrc, "?", NULL, NULL, &err);
 	if (err != NULL) {
 		gaim_debug_error("gg", "Error converting from %s to %s: %s\n",
@@ -97,673 +87,96 @@
 
 	return msg;
 }
-
-static gboolean invalid_uin(const char *uin)
-{
-	unsigned long int res = strtol(uin, (char **)NULL, 10);
-	if (res == ULONG_MAX || res == 0)
-		return TRUE;
-	return FALSE;
-}
-
-static gint args_compare(gconstpointer a, gconstpointer b)
-{
-	return g_ascii_strcasecmp((const gchar *)a,(const gchar *)b);
-}
-
-static gboolean allowed_uin(GaimConnection *gc, char *uin)
-{
-	GaimAccount *account = gaim_connection_get_account(gc);
-
-	switch (account->perm_deny) {
-	case 1:
-		/* permit all, deny none */
-		return TRUE;
-		break;
-	case 2:
-		/* deny all, permit none. */
-		return FALSE;
-		break;
-	case 3:
-		/* permit some. */
-		if (g_slist_find_custom(gc->account->permit, uin, args_compare))
-			return TRUE;
-		return FALSE;
-		break;
-	case 4:
-		/* deny some. */
-		if (g_slist_find_custom(gc->account->deny, uin, args_compare))
-			return FALSE;
-		return TRUE;
-		break;
-	default:
-		return TRUE;
-		break;
-	}
-}
-
-static char *handle_errcode(GaimConnection *gc, int errcode)
-{
-	static char msg[AGG_BUF_LEN];
-
-	switch (errcode) {
-	case GG_FAILURE_RESOLVING:
-		g_snprintf(msg, sizeof(msg), _("Unable to resolve hostname."));
-		break;
-	case GG_FAILURE_CONNECTING:
-		g_snprintf(msg, sizeof(msg), _("Unable to connect to server."));
-		break;
-	case GG_FAILURE_INVALID:
-		g_snprintf(msg, sizeof(msg), _("Invalid response from server."));
-		break;
-	case GG_FAILURE_READING:
-		g_snprintf(msg, sizeof(msg), _("Error while reading from socket."));
-		break;
-	case GG_FAILURE_WRITING:
-		g_snprintf(msg, sizeof(msg), _("Error while writing to socket."));
-		break;
-	case GG_FAILURE_PASSWORD:
-		g_snprintf(msg, sizeof(msg), _("Authentication failed."));
-		break;
-	default:
-		g_snprintf(msg, sizeof(msg), _("Unknown Error Code."));
-		break;
-	}
-
-	gaim_connection_error(gc, msg);
-
-	return msg;
-}
+/* }}} */
 
-static void agg_set_status(GaimAccount *account, GaimStatus *status)
+/*
+ * Convert string to number. Check wheter a given
+ * string is a correct UIN.
+ *
+ * Return UIN or 0 if an error occurred.
+ */
+/* static uin_t ggp_str_to_uin(const char *text) {{{ */
+static uin_t ggp_str_to_uin(const char *text)
 {
-	GaimConnection *gc;
-	struct agg_data *gd;
-	gboolean connected;
-	GaimStatusType *type;
-	int primitive;
-	int status_num;
-	const char *status_id;
-	char *msg = NULL;
-
-	connected = gaim_account_is_connected(account);
-	type = gaim_status_get_type(status);
-	primitive = gaim_status_type_get_primitive(type);
-
-	if (!gaim_status_is_active(status))
-		return;
-
-	if (!connected) {
-		if (primitive != GAIM_STATUS_OFFLINE)
-			gaim_account_connect(account);
-		return;
-	}
-
-	if (primitive == GAIM_STATUS_OFFLINE) {
-		gaim_account_disconnect(account);
-		return;
-	}
-
-	gc = gaim_account_get_connection(account);
-	gd = (struct agg_data *)gc->proto_data;
-	status_num = gd->own_status;
-	status_id = gaim_status_get_id(status);
-
-	if (!strcmp(status_id, "available"))
-		status_num = GG_STATUS_AVAIL;
-	else if (!strcmp(status_id, "available-friends"))
-		status_num = GG_STATUS_AVAIL | GG_STATUS_FRIENDS_MASK;
-	else if (!strcmp(status_id, "away"))
-		status_num = GG_STATUS_BUSY;
-	else if (!strcmp(status_id, "away-friends"))
-		status_num = GG_STATUS_BUSY | GG_STATUS_FRIENDS_MASK;
-	else if (!strcmp(status_id, "invisible"))
-		status_num = GG_STATUS_INVISIBLE;
-	else if (!strcmp(status_id, "invisible-friends"))
-		status_num = GG_STATUS_INVISIBLE | GG_STATUS_FRIENDS_MASK;
-	else if (!strcmp(status_id, "unavailable"))
-		status_num = GG_STATUS_NOT_AVAIL;
-	else
-		/* Don't need to do anything */
-		return;
-
-	/* XXX: this was added between the status_rewrite and now and needs to be fixed */
-	if (msg != NULL) {
-	    switch (status_num) {
-		case GG_STATUS_AVAIL:
-		    status_num = GG_STATUS_AVAIL_DESCR;
-		    break;
-		case GG_STATUS_BUSY:
-		    status_num = GG_STATUS_BUSY_DESCR;
-		    break;
-		case GG_STATUS_INVISIBLE:
-		    status_num = GG_STATUS_INVISIBLE_DESCR;
-		    break;
-		case GG_STATUS_NOT_AVAIL:
-		    status_num = GG_STATUS_NOT_AVAIL_DESCR;
-		    break;
-	    }
-	}
-
-	gd->own_status = status_num;
-
-	if (msg)
-	    gg_change_status_descr(gd->sess, status_num, msg);
-	else
-	    gg_change_status(gd->sess, status_num);
-}
-
+	char *tmp;
+	long num;
 
-#if 0
-static void agg_get_away(GaimConnection *gc, const char *who)
-{
-    GaimBuddy *buddy;
-    char *dialog_msg, **splitmsg;
-
-    if (invalid_uin(who))
-		return;
-    
-    buddy = gaim_find_buddy(gaim_connection_get_account(gc), who);
-    if (buddy->proto_data) {
-		/* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
-		splitmsg = g_strsplit(buddy->proto_data, "\r\n", 0);
-    
-		dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<HR>%s"), who, (char *)buddy->proto_data, g_strjoinv("<BR>", splitmsg));
-		gaim_notify_userinfo(gc, who, NULL, _("Buddy Information"), buddy->proto_data, dialog_msg, NULL, NULL);
-    }
-}
-#endif
-
-static gchar *get_away_text(GaimBuddy *buddy)
-{
-	GaimPresence *presence = gaim_buddy_get_presence(buddy);
-
-	if (gaim_presence_is_status_active(presence, "available"))
-		return AGG_STATUS_AVAIL;
-	else if (gaim_presence_is_status_active(presence, "available-friends"))
-		return AGG_STATUS_AVAIL_FRIENDS;
-	else if (gaim_presence_is_status_active(presence, "away"))
-		return AGG_STATUS_BUSY;
-	else if (gaim_presence_is_status_active(presence, "away-friends"))
-		return AGG_STATUS_BUSY_FRIENDS;
-	else if (gaim_presence_is_status_active(presence, "invisible"))
-  		return AGG_STATUS_INVISIBLE;
-	else if (gaim_presence_is_status_active(presence, "invisible-friends"))
-  		return AGG_STATUS_INVISIBLE_FRIENDS;
-	else if (gaim_presence_is_status_active(presence, "unavailable"))
-		return AGG_STATUS_NOT_AVAIL;
+	if (!text)
+		return 0;
 
-	return AGG_STATUS_AVAIL;
-}
-
-
-static GList *agg_status_types(GaimAccount *account)
-{
-	GaimStatusType *type;
-	GList *types = NULL;
-
-	type = gaim_status_type_new(GAIM_STATUS_OFFLINE, "offline",
-								_("Offline"), FALSE);
-	types = g_list_append(types, type);
-
-	type = gaim_status_type_new(GAIM_STATUS_ONLINE, "online",
-								_("Online"), FALSE);
-	types = g_list_append(types, type);
-
-	type = gaim_status_type_new_with_attrs(
-		GAIM_STATUS_AVAILABLE, "available", AGG_STATUS_AVAIL,
-		TRUE, TRUE, FALSE,
-		"message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
-
-	type = gaim_status_type_new_with_attrs(
-		GAIM_STATUS_AWAY, "away", AGG_STATUS_BUSY,
-		TRUE, TRUE, FALSE,
-		"message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
-
-	type = gaim_status_type_new_with_attrs(
-		GAIM_STATUS_HIDDEN, "invisible", AGG_STATUS_INVISIBLE,
-		TRUE, TRUE, FALSE,
-		"message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
-
-	type = gaim_status_type_new_with_attrs(
-		GAIM_STATUS_AVAILABLE, "available-friends", AGG_STATUS_AVAIL_FRIENDS,
-		TRUE, TRUE, FALSE,
-		"message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
+	errno = 0;
+	num = strtol(text, &tmp, 0);
 
-	type = gaim_status_type_new_with_attrs(
-		GAIM_STATUS_AWAY, "away-friends", AGG_STATUS_BUSY_FRIENDS,
-		TRUE, TRUE, FALSE,
-		"message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
-
-	type = gaim_status_type_new_with_attrs(
-		GAIM_STATUS_HIDDEN, "invisible-friends", AGG_STATUS_INVISIBLE_FRIENDS,
-		TRUE, TRUE, FALSE,
-		"message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
-
-	type = gaim_status_type_new_with_attrs(
-		GAIM_STATUS_UNAVAILABLE, "unavailable", AGG_STATUS_NOT_AVAIL,
-		TRUE, TRUE, FALSE,
-		"message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
-
-	return types;
-}
-
-/* Enhance these functions, more options and such stuff */
-static GList *agg_buddy_menu(GaimBuddy *buddy)
-{
-	GList *m = NULL;
-	GaimBlistNodeAction *act;
-
-	static char buf[AGG_BUF_LEN];
-	g_snprintf(buf, sizeof(buf), _("Status: %s"), get_away_text(buddy));
-
-	/* um... this seems silly. since in this pass, I'm only converting
-	   over the menu building, I'm not going to mess with it though */
-	/* XXX: shouldn't this be in the tooltip instead? */
-	act = gaim_blist_node_action_new(buf, NULL, NULL, NULL);
-	m = g_list_append(m, act);
-
-	return m;
-}
-
+	if (*text == '\0' || *tmp != '\0')
+		return 0;
 
-static GList *
-agg_blist_node_menu(GaimBlistNode *node)
-{
-	if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
-		return agg_buddy_menu((GaimBuddy *) node);
-	} else {
-		return NULL;
-	}
-}
-
-
-static void agg_load_buddy_list(GaimConnection *gc, char *buddylist)
-{
-	struct agg_data *gd = (struct agg_data *)gc->proto_data;
-	gchar *ptr = buddylist;
-	gchar **users_tbl;
-	int i;
-	uin_t *userlist = NULL;
-	int userlist_size = 0;
+	if ((errno == ERANGE || (num == LONG_MAX || num == LONG_MIN)) || num > UINT_MAX || num < 0)
+		return 0;
 
-	users_tbl = g_strsplit(ptr, "\r\n", AGG_PUBDIR_MAX_ENTRIES);
-
-	/* Parse array of Buddies List */
-	for (i = 0; users_tbl[i] != NULL; i++) {
-		gchar **data_tbl;
-		gchar *name, *show;
-
-		if (strlen(users_tbl[i])==0) {
-			gaim_debug(GAIM_DEBUG_MISC, "gg",
-					   "import_buddies_server_results: users_tbl[i] is empty\n");
-			continue;
-		}
-
-		data_tbl = g_strsplit(users_tbl[i], ";", 8);
-
-		show = charset_convert(data_tbl[3], "CP1250", "UTF-8");
-		name = data_tbl[6];
-
-		if (invalid_uin(name)) {
-			continue;
-		}
+	return (uin_t) num;
+}
+/* }}} */
 
-		gaim_debug(GAIM_DEBUG_MISC, "gg",
-				   "import_buddies_server_results: uin: %s\n", name);
-		if (!gaim_find_buddy(gc->account, name)) {
-			GaimBuddy *b;
-			GaimGroup *g;
-			/* Default group if none specified on server */
-			gchar *group = g_strdup("Gadu-Gadu");
-			if (strlen(data_tbl[5])) {
-				gchar **group_tbl = g_strsplit(data_tbl[5], ",", 2);
-				if (strlen(group_tbl[0])) {
-					g_free(group);
-					group = g_strdup(group_tbl[0]);
-				}
-				g_strfreev(group_tbl);
-			}
-			/* Add Buddy to our userlist */
-			if (!(g = gaim_find_group(group))) {
-				g = gaim_group_new(group);
-				gaim_blist_add_group(g, NULL);
-			}
-			b = gaim_buddy_new(gc->account, name, strlen(show) ? show : NULL);
-			gaim_blist_add_buddy(b,NULL,g,NULL);
-
-			userlist_size++;
-			userlist = g_renew(uin_t, userlist, userlist_size);
-			userlist[userlist_size - 1] =
-			    (uin_t) strtol((char *)name, (char **)NULL, 10);
-
-			g_free(group);
-		}
-		g_free(show);
-		g_strfreev(data_tbl);
-	}
-	g_strfreev(users_tbl);
-
-	if (userlist) {
-	    gg_notify(gd->sess, userlist, userlist_size);
-	    g_free(userlist);
-	}
+/**
+ * Get UIN of a given account.
+ *
+ * @param account Current account.
+ *
+ * @return UIN of an account.
+ */
+/* static ggp_get_uin(GaimAccount *account) {{{ */
+static uin_t ggp_get_uin(GaimAccount *account)
+{
+	return ggp_str_to_uin(gaim_account_get_username(account));
 }
-
-static void agg_save_buddy_list(GaimConnection *gc, char *existlist)
-{
-    struct agg_data *gd = (struct agg_data *)gc->proto_data;
-    GaimBlistNode *gnode, *cnode, *bnode;
-    char *buddylist = g_strdup(existlist ? existlist : "");
-    char *ptr;
-
-    for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
-		GaimGroup *g = (GaimGroup *)gnode;
-		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
-			continue;
-		for(cnode = gnode->child; cnode; cnode = cnode->next) {
-			if(!GAIM_BLIST_NODE_IS_CONTACT(cnode))
-				continue;
-			for(bnode = cnode->child; bnode; bnode = bnode->next) {
-				GaimBuddy *b = (GaimBuddy *)bnode;
-
-				if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
-					continue;
-
-				if(b->account == gc->account) {
-					gchar *newdata;
-					/* GG Number */
-					gchar *name = b->name;
-					/* GG Pseudo */
-					gchar *show = b->alias ? b->alias : b->name;
-					/* Group Name */
-					gchar *gname = g->name;
-
-					newdata = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s;%s%s\r\n",
-							show, show, show, show, "", gname, name, "", "");
-
-					ptr = buddylist;
-					buddylist = g_strconcat(ptr, newdata, NULL);
-
-					g_free(newdata);
-					g_free(ptr);
-				}
-			}
-		}
-	}
+/* }}} */
 
-	/* save the list to the gadu gadu server */
-	gg_userlist_request(gd->sess, GG_USERLIST_PUT, buddylist);
-}
-
-static void main_callback(gpointer data, gint source, GaimInputCondition cond)
+/**
+ * Create a new GGPSearchForm structure, and set the fields
+ * to the sane defaults.
+ *
+ * @return Newly allocated GGPSearchForm.
+ */
+/* GGPSearchForm *ggp_searchform_new() {{{ */
+GGPSearchForm *ggp_searchform_new()
 {
-	GaimConnection *gc = data;
-	GaimAccount *account = gaim_connection_get_account(gc);
-	struct agg_data *gd = gc->proto_data;
-	struct gg_event *e;
-
-	gaim_debug(GAIM_DEBUG_INFO, "gg", "main_callback enter: begin\n");
-
-	if (gd->sess->fd != source)
-		gd->sess->fd = source;
-
-	if (source == 0) {
-		gaim_connection_error(gc, _("Could not connect"));
-		return;
-	}
-
-	if (!(e = gg_watch_fd(gd->sess))) {
-		gaim_debug(GAIM_DEBUG_ERROR, "gg",
-				   "main_callback: gg_watch_fd failed - CRITICAL!\n");
-		gaim_connection_error(gc, _("Unable to read socket"));
-		return;
-	}
-
-	switch (e->type) {
-	case GG_EVENT_NONE:
-		/* do nothing */
-		break;
-	case GG_EVENT_CONN_SUCCESS:
-		gaim_debug(GAIM_DEBUG_WARNING, "gg",
-				   "main_callback: CONNECTED AGAIN!?\n");
-		break;
-	case GG_EVENT_CONN_FAILED:
-		if (gc->inpa)
-			gaim_input_remove(gc->inpa);
-		handle_errcode(gc, e->event.failure);
-		break;
-	case GG_EVENT_MSG:
-		{
-			gchar *imsg;
-			gchar *jmsg;
-			gchar user[20];
-
-			g_snprintf(user, sizeof(user), "%lu", e->event.msg.sender);
-			if (!allowed_uin(gc, user))
-				break;
-			imsg = charset_convert(e->event.msg.message, "CP1250", "UTF-8");
-			gaim_str_strip_cr(imsg);
-			jmsg = g_markup_escape_text(imsg, -1);
-			serv_got_im(gc, user, jmsg, 0, e->event.msg.time);
-			g_free(imsg);
-			g_free(jmsg);
-		}
-		break;
-	case GG_EVENT_NOTIFY:
-		{
-			gchar user[20];
-			struct gg_notify_reply *n = e->event.notify;
-			const char *status_id;
-
-			while (n->uin) {
-				switch (n->status) {
-					case GG_STATUS_NOT_AVAIL:
-						status_id = "unavailable";
-						break;
-
-					case GG_STATUS_AVAIL:
-						status_id = "available";
-						break;
-
-					case GG_STATUS_BUSY:
-						status_id = "away";
-						break;
-
-					case GG_STATUS_INVISIBLE:
-						status_id = "invisible";
-						break;
-
-					default:
-						status_id = "available";
-						break;
-
-				}
+	GGPSearchForm *form;
 
-				g_snprintf(user, sizeof(user), "%lu", n->uin);
-				gaim_prpl_got_user_status(account, user, status_id, NULL);
-				n++;
-			}
-		}
-		break;
-	case GG_EVENT_NOTIFY60:
-		{
-			gchar user[20];
-			const char *status_id;
-			guint i = 0;
-
-			for (i = 0; e->event.notify60[i].uin; i++) {
-				GaimBuddy *buddy;
-				
-				g_snprintf(user, sizeof(user), "%lu", (long unsigned int)e->event.notify60[i].uin);
-
-				buddy = gaim_find_buddy(gaim_connection_get_account(gc), user);
-				if (buddy && buddy->proto_data != NULL) {
-					g_free(buddy->proto_data);
-					buddy->proto_data = NULL;
-				}
-			
-				switch (e->event.notify60[i].status) {
-					case GG_STATUS_NOT_AVAIL:
-					case GG_STATUS_NOT_AVAIL_DESCR:
-						status_id = "unavailable";
-						break;
-
-					case GG_STATUS_AVAIL:
-					case GG_STATUS_AVAIL_DESCR:
-						status_id = "available";
-						break;
-
-					case GG_STATUS_BUSY:
-					case GG_STATUS_BUSY_DESCR:
-						status_id = "away";
-						break;
+	form = g_new0(GGPSearchForm, 1);
+	form->uin = NULL;
+	form->lastname = NULL;
+	form->firstname = NULL;
+	form->nickname = NULL;
+	form->city = NULL;
+	form->birthyear = NULL;
+	form->gender = NULL;
+	form->active = NULL;
+	form->offset = NULL;
 
-					case GG_STATUS_INVISIBLE:
-					case GG_STATUS_INVISIBLE_DESCR:
-						status_id = "invisible";
-						break;
-
-					default:
-						status_id = "available";
-						break;
-				}
-				
-				if (buddy && e->event.notify60[i].descr != NULL) {
-					buddy->proto_data = g_strdup(e->event.notify60[i].descr);
-				}
-
-				gaim_prpl_got_user_status(account, user, status_id, NULL);
-				i++;
-			}
-		}
-		break;
-	case GG_EVENT_STATUS:
-		{
-			gchar user[20];
-			const char *status_id;
-
-			switch (e->event.status.status) {
- 				case GG_STATUS_NOT_AVAIL:
-					status_id = "unavailable";
-					break;
-
-				case GG_STATUS_AVAIL:
-					status_id = "available";
-					break;
-
-				case GG_STATUS_BUSY:
-					status_id = "away";
-					break;
-
-				case GG_STATUS_INVISIBLE:
-					status_id = "invisible";
-					break;
-
-				default:
-					status_id = "available";
-					break;
-			}
+	form->last_uin = NULL;
 
-			g_snprintf(user, sizeof(user), "%lu", e->event.status.uin);
-			gaim_prpl_got_user_status(account, user, status_id, NULL);
-		}
-		break;
-	case GG_EVENT_STATUS60:
-		{
-			gchar user[20];
-			const char *status_id;
-
-			GaimBuddy *buddy;
-				
-			g_snprintf(user, sizeof(user), "%lu", e->event.status60.uin);
-
-			buddy = gaim_find_buddy(gaim_connection_get_account(gc), user);
-			if (buddy && buddy->proto_data != NULL) {
-				g_free(buddy->proto_data);
-				buddy->proto_data = NULL;
-			}
+	return form;
+}
+/* }}} */
 
-			switch (e->event.status60.status) {
-				case GG_STATUS_NOT_AVAIL_DESCR:
-				case GG_STATUS_NOT_AVAIL:
-					status_id = "unavailable";
-					break;
-
-				case GG_STATUS_AVAIL:
-				case GG_STATUS_AVAIL_DESCR:
-					status_id = "available";
-					break;
-
-				case GG_STATUS_BUSY:
-				case GG_STATUS_BUSY_DESCR:
-					status_id = "away";
-					break;
-
-				case GG_STATUS_INVISIBLE:
-				case GG_STATUS_INVISIBLE_DESCR:
-					status_id = "invisible";
-					break;
+/* ---------------------------------------------------------------------- */
+/* ----- BUDDYLIST STUFF ------------------------------------------------ */
+/* ---------------------------------------------------------------------- */
 
-				default:
-					status_id = "available";
-					break;
-			}
- 
-			if (buddy && e->event.status60.descr != NULL) {
-				buddy->proto_data = g_strdup(e->event.status60.descr);
-			}
-
-			gaim_prpl_got_user_status(account, user, status_id, NULL);
-		}
-		break;
-	case GG_EVENT_ACK:
-		gaim_debug(GAIM_DEBUG_MISC, "gg",
-				   "main_callback: message %d to %lu sent with status %d\n",
-			     e->event.ack.seq, e->event.ack.recipient, e->event.ack.status);
-		break;
-	case GG_EVENT_USERLIST:
-	{
-	    gaim_debug(GAIM_DEBUG_MISC, "gg", "main_callback: received userlist reply\n");
-	    switch (e->event.userlist.type) {
-		case GG_USERLIST_GET_REPLY:
-		{
+/*
+ * Adapted from the previous GG implementation in Gaim
+ * by Arkadiusz MiÂśkiewicz <misiek@pld.ORG.PL>
+ */
+/* static void ggp_buddylist_send(GaimConnection *gc) {{{ */
+static void ggp_buddylist_send(GaimConnection *gc)
+{
+	GGPInfo *info = gc->proto_data;
 
-			if (e->event.userlist.reply) {
-			    agg_load_buddy_list(gc, e->event.userlist.reply);
-			}
-			break;
-		}
-
-		case GG_USERLIST_PUT_REPLY:
-		{
-		    /* ignored */
-		}
-	    }
-	}
-
-	default:
-		gaim_debug(GAIM_DEBUG_ERROR, "gg",
-				   "main_callback: unsupported event %d\n", e->type);
-		break;
-	}
-	gg_free_event(e);
-}
-
-static void agg_send_buddylist(GaimConnection *gc)
-{
 	GaimBuddyList *blist;
 	GaimBlistNode *gnode, *cnode, *bnode;
 	GaimBuddy *buddy;
-	struct agg_data *gd = (struct agg_data *)gc->proto_data;
 	uin_t *userlist = NULL;
+	gchar *types = NULL;
 	int userlist_size = 0;
 
 	if ((blist = gaim_get_blist()) != NULL)
@@ -781,1061 +194,1612 @@
 					if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
 						continue;
 					buddy = (GaimBuddy *)bnode;
-					if (invalid_uin(buddy->name))
+
+					if (buddy->account != gc->account)
 						continue;
+
 					userlist_size++;
-					userlist = g_renew(uin_t, userlist, userlist_size);
-					userlist[userlist_size - 1] =
-					    (uin_t) strtol(buddy->name, (char **)NULL, 10);
+					userlist = (uin_t *) g_renew(uin_t, userlist, userlist_size);
+					types = (gchar *) g_renew(gchar, types, userlist_size);
+					userlist[userlist_size - 1] = ggp_str_to_uin(buddy->name);
+					types[userlist_size - 1] = GG_USER_NORMAL;
+					gaim_debug_info("gg", "ggp_buddylist_send: adding %d\n", userlist[userlist_size - 1]);
 				}
 			}
 		}
 	}
 
 	if (userlist) {
-		gg_notify(gd->sess, userlist, userlist_size);
+		int ret = gg_notify_ex(info->session, userlist, types, userlist_size);
 		g_free(userlist);
-	}
-
-	agg_save_buddy_list(gc, NULL);
-}
-
-void login_callback(gpointer data, gint source, GaimInputCondition cond)
-{
-	GaimConnection *gc = data;
-	struct agg_data *gd = gc->proto_data;
-	struct gg_event *e;
-
-	gaim_debug(GAIM_DEBUG_INFO, "gg", "login_callback...\n");
-	if (!g_list_find(gaim_connections_get_all(), gc)) {
-		close(source);
-		return;
-	}
-
-	gaim_debug(GAIM_DEBUG_INFO, "gg", "Found GG connection\n");
-
-	if (source == 0) {
-		gaim_connection_error(gc, _("Unable to connect."));
-		return;
-	}
-
-	gd->sess->fd = source;
-
-	gaim_debug(GAIM_DEBUG_MISC, "gg", "Source is valid.\n");
-	if (gc->inpa == 0) {
-		gaim_debug(GAIM_DEBUG_MISC, "gg",
-				   "login_callback.. checking gc->inpa .. is 0.. setting fd watch\n");
-		gc->inpa = gaim_input_add(gd->sess->fd, GAIM_INPUT_READ, login_callback, gc);
-		gaim_debug(GAIM_DEBUG_INFO, "gg", "Adding watch on fd\n"); 
-	}
-	gaim_debug(GAIM_DEBUG_INFO, "gg", "Checking State.\n");
-	switch (gd->sess->state) {
-	case GG_STATE_READING_DATA:
-		gaim_connection_update_progress(gc, _("Reading data"), 1, GG_CONNECT_STEPS);
-		break;
-	case GG_STATE_CONNECTING_GG:
-		gaim_connection_update_progress(gc, _("Balancer handshake"), 2, GG_CONNECT_STEPS);
-		break;
-	case GG_STATE_READING_KEY:
-		gaim_connection_update_progress(gc, _("Reading server key"), 3, GG_CONNECT_STEPS);
-		break;
-	case GG_STATE_READING_REPLY:
-		gaim_connection_update_progress(gc, _("Exchanging key hash"), 4, GG_CONNECT_STEPS);
-		break;
-	default:
-		gaim_debug(GAIM_DEBUG_INFO, "gg", "No State found\n");
-		break;
-	}
-	gaim_debug(GAIM_DEBUG_MISC, "gg", "gg_watch_fd\n");
-	if (!(e = gg_watch_fd(gd->sess))) {
-		gaim_debug(GAIM_DEBUG_ERROR, "gg",
-				   "login_callback: gg_watch_fd failed - CRITICAL!\n");
-		gaim_connection_error(gc, _("Critical error in GG library\n"));
-		return;
-	}
-
-	/* If we are GG_STATE_CONNECTING_GG then we still need to connect, as
-	   we could not use gaim_proxy_connect in libgg.c */
-	switch( gd->sess->state ) {
-	case GG_STATE_CONNECTING_GG:
-		{
-			struct in_addr ip;
-			char buf[256];
-
-			/* Remove watch on initial socket - now that we have ip and port of login server */
-			gaim_input_remove(gc->inpa);
-
-			ip.s_addr = gd->sess->server_ip;
+		g_free(types);
 
-			if (gaim_proxy_connect(gc->account, inet_ntoa(ip), gd->sess->port, login_callback, gc) < 0) {
-				g_snprintf(buf, sizeof(buf), _("Connect to %s failed"), inet_ntoa(ip));
-				gaim_connection_error(gc, buf);
-				return;
-			}
-			break;
-		}
-	case GG_STATE_READING_KEY:
-		/* Set new watch on login server ip */
-		if(gc->inpa)
-			gc->inpa = gaim_input_add(gd->sess->fd, GAIM_INPUT_READ, login_callback, gc);
-		gaim_debug(GAIM_DEBUG_INFO, "gg",
-				   "Setting watch on connection with login server.\n"); 
-		break;
-	}/* end switch() */
-
-	gaim_debug(GAIM_DEBUG_MISC, "gg", "checking gg_event\n");
-	switch (e->type) {
-	case GG_EVENT_NONE:
-		/* nothing */
-		break;
-	case GG_EVENT_CONN_SUCCESS:
-		/* Setup new input handler */
-		if (gc->inpa)
-			gaim_input_remove(gc->inpa);
-		gc->inpa = gaim_input_add(gd->sess->fd, GAIM_INPUT_READ, main_callback, gc);
-
-		/* Our signon is complete */
-		gaim_connection_set_state(gc, GAIM_CONNECTED);
-
-		/* Send the server our buddy list */
-		agg_send_buddylist(gc);
-
-		break;
-	case GG_EVENT_CONN_FAILED:
-		gaim_input_remove(gc->inpa);
-		gc->inpa = 0;
-		handle_errcode(gc, e->event.failure);
-		break;
-	default:
-		gaim_debug(GAIM_DEBUG_MISC, "gg", "no gg_event\n");
-		break;
-	}
-	gaim_debug(GAIM_DEBUG_INFO, "gg", "Returning from login_callback\n");
-	gg_free_event(e);
-}
-
-static void agg_keepalive(GaimConnection *gc)
-{
-	struct agg_data *gd = (struct agg_data *)gc->proto_data;
-	if (gg_ping(gd->sess) < 0) {
-		gaim_connection_error(gc, _("Unable to ping server"));
-		return;
-	}
-}
-
-static void agg_login(GaimAccount *account, GaimStatus *status)
-{
-	GaimConnection *gc = gaim_account_get_connection(account);
-	struct agg_data *gd = gc->proto_data = g_new0(struct agg_data, 1);
-	char buf[80];
-
-#if 0
-	gc->checkbox = _("Send as message");
-#endif
-
-	gd->sess = g_new0(struct gg_session, 1);
-
-	gaim_connection_update_progress(gc, _("Looking up GG server"), 0, GG_CONNECT_STEPS);
-
-	if (invalid_uin(account->username)) {
-		gaim_connection_error(gc, _("Invalid Gadu-Gadu UIN specified"));
-		return;
-	}
-
-	gc->inpa = 0;
-
-	/*
-	   if (gg_login(gd->sess, strtol(user->username, (char **)NULL, 10), user->password, 1) < 0) {
-	   gaim_debug(GAIM_DEBUG_MISC, "gg", "uin=%u, pass=%s", strtol(user->username, (char **)NULL, 10), user->password); 
-	   gaim_connection_error(gc, _("Unable to connect."));
-	   signoff(gc);
-	   return;
-	   }
-
-	   gg_login() sucks for me, so I'm using gaim_proxy_connect()
-	 */
-
-	gd->sess->uin = (uin_t) strtol(account->username, (char **)NULL, 10);
-	gd->sess->password = g_strdup(gaim_connection_get_password(gc));
-	gd->sess->state = GG_STATE_CONNECTING;
-	gd->sess->check = GG_CHECK_WRITE;
-	gd->sess->async = 1;
-	if (gaim_proxy_connect(account, GG_APPMSG_HOST, GG_APPMSG_PORT, login_callback, gc) < 0) {
-		g_snprintf(buf, sizeof(buf), _("Connect to %s failed"), GG_APPMSG_HOST);
-		gaim_connection_error(gc, buf);
-		return;
+		gaim_debug_info("gg", "send: ret=%d; size=%d\n", ret, userlist_size);
 	}
 }
-
-static void agg_close(GaimConnection *gc)
-{
-	struct agg_data *gd = (struct agg_data *)gc->proto_data;
-	if (gc->inpa)
-		gaim_input_remove(gc->inpa);
-	gg_logoff(gd->sess);
-	gd->own_status = GG_STATUS_NOT_AVAIL;
-	gg_free_session(gd->sess);
-	g_free(gc->proto_data);
-}
-
-static int agg_send_im(GaimConnection *gc, const char *who, const char *msg, GaimConvImFlags flags)
-{
-	struct agg_data *gd = (struct agg_data *)gc->proto_data;
-
-	if (invalid_uin(who)) {
-		gaim_notify_error(gc, NULL,
-						  _("You are trying to send a message to an "
-							"invalid Gadu-Gadu UIN."), NULL);
-		return -1;
-	}
-
-	if (strlen(msg) > 0) {
-		gchar *imsg = charset_convert(msg, "UTF-8", "CP1250");
-		if (imsg != NULL && strlen(imsg) > 0) {
-			if (gg_send_message(gd->sess, GG_CLASS_CHAT,
-				    strtol(who, (char **)NULL, 10), imsg) < 0)
-				return -1;
-		}
-		if (imsg != NULL)
-			g_free(imsg);
-	}
-	return 1;
-}
-
-static void agg_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
-{
-	struct agg_data *gd = (struct agg_data *)gc->proto_data;
-	if (invalid_uin(buddy->name))
-		return;
-	gg_add_notify(gd->sess, strtol(buddy->name, (char **)NULL, 10));
-	agg_save_buddy_list(gc, NULL);
-}
-
-static void agg_rem_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
-{
-	struct agg_data *gd = (struct agg_data *)gc->proto_data;
-	if (invalid_uin(buddy->name))
-		return;
-	gg_remove_notify(gd->sess, strtol(buddy->name, (char **)NULL, 10));
-	agg_save_buddy_list(gc, NULL);
-}
-
-static void agg_buddy_free (GaimBuddy *buddy)
-{
-    if (buddy->proto_data) {
-	g_free(buddy->proto_data);
-	buddy->proto_data = NULL;
-    }
-}
-
-static void search_results(GaimConnection *gc, gchar *webdata)
-{
-	gchar **webdata_tbl;
-	gchar *buf;
-	char *ptr;
-	int i, j;
-
-	if ((ptr = strstr(webdata, "query_results:")) == NULL || (ptr = strchr(ptr, '\n')) == NULL) {
-		gaim_debug(GAIM_DEBUG_MISC, "gg", "search_callback: pubdir result [%s]\n", webdata);
-		gaim_notify_error(gc, NULL, _("Couldn't get search results"), NULL);
-		return;
-	}
-	ptr++;
-
-	buf = g_strconcat("<B>", _("Gadu-Gadu Search Engine"), "</B><BR>\n", NULL);
-
-	webdata_tbl = g_strsplit(ptr, "\n", AGG_PUBDIR_MAX_ENTRIES);
-
-	j = 0;
-
-	/* Parse array */
-	/* XXX - Make this use a GString */
-	for (i = 0; webdata_tbl[i] != NULL; i++) {
-		gchar *p, *oldibuf;
-		static gchar *ibuf;
-
-		g_strdelimit(webdata_tbl[i], "\t\n", ' ');
+/* }}} */
 
-		/* GG_PUBDIR_HOST service returns 7 lines of data per directory entry */
-		if (i % 8 == 0)
-			j = 0;
-
-		p = charset_convert(g_strstrip(webdata_tbl[i]), "CP1250", "UTF-8");
-
-		oldibuf = ibuf;
-
-		switch (j) {
-		case 0:
-			ibuf = g_strconcat("---------------------------------<BR>\n", NULL);
-			oldibuf = ibuf;
-			ibuf = g_strconcat(oldibuf, "<B>", _("Active"), ":</B> ",
-					   (atoi(p) == 2) ? _("Yes") : _("No"), "<BR>\n", NULL);
-			g_free(oldibuf);
-			break;
-		case 1:
-			ibuf = g_strconcat(oldibuf, "<B>", _("UIN"), ":</B> ", p, "<BR>\n", NULL);
-			g_free(oldibuf);
-			break;
-		case 2:
-			ibuf = g_strconcat(oldibuf, "<B>", _("First Name"), ":</B> ", p, "<BR>\n", NULL);
-			g_free(oldibuf);
-			break;
-		case 3:
-			ibuf =
-			    g_strconcat(oldibuf, "<B>", _("Last Name"), ":</B> ", p, "<BR>\n", NULL);
-			g_free(oldibuf);
-			break;
-		case 4:
-			ibuf = g_strconcat(oldibuf, "<B>", _("Nick"), ":</B> ", p, "<BR>\n", NULL);
-			g_free(oldibuf);
-			break;
-		case 5:
-			/* Hack, invalid_uin does what we really want here but may change in future */
-			if (invalid_uin(p))
-				ibuf =
-				    g_strconcat(oldibuf, "<B>", _("Birth Year"), ":</B> <BR>\n", NULL);
-			else
-				ibuf =
-				    g_strconcat(oldibuf, "<B>", _("Birth Year"), ":</B> ", p, "<BR>\n",
-						NULL);
-			g_free(oldibuf);
-			break;
-		case 6:
-			if (atoi(p) == GG_GENDER_FEMALE)
-				ibuf = g_strconcat(oldibuf, "<B>", _("Sex"), ":</B> woman<BR>\n", NULL);
-			else if (atoi(p) == GG_GENDER_MALE)
-				ibuf = g_strconcat(oldibuf, "<B>", _("Sex"), ":</B> man<BR>\n", NULL);
-			else
-				ibuf = g_strconcat(oldibuf, "<B>", _("Sex"), ":</B> <BR>\n", NULL);
-			g_free(oldibuf);
-			break;
-		case 7:
-			ibuf = g_strconcat(oldibuf, "<B>", _("City"), ":</B> ", p, "<BR>\n", NULL);
-			g_free(oldibuf);
-
-			/* We have all lines, so add them to buffer */
-			{
-				gchar *oldbuf = buf;
-				buf = g_strconcat(oldbuf, ibuf, NULL);
-				g_free(oldbuf);
-			}
-
-			g_free(ibuf);
-			break;
-		}
-
-		g_free(p);
-
-		j++;
-	}
-
-	g_strfreev(webdata_tbl);
-
-	gaim_notify_formatted(gc, NULL, _("Buddy Information"), NULL,
-						  buf, NULL, NULL);
-
-	g_free(buf);
-}
-
-static void
-change_pass(GaimPluginAction *action)
+/**
+ * Load buddylist from server into the rooster.
+ *
+ * @param gc GaimConnection
+ * @param buddylist Pointer to the buddylist that will be loaded.
+ */
+/* static void ggp_buddylist_load(GaimConnection *gc, char *buddylist) {{{ */
+static void ggp_buddylist_load(GaimConnection *gc, char *buddylist)
 {
-	GaimConnection *gc = (GaimConnection *) action->context;
-	GaimAccount *account = gaim_connection_get_account(gc);
-	gaim_account_request_change_password(account);
-}
-
-#if 0
-static void import_buddies_server_results(GaimConnection *gc, gchar *webdata)
-{
-	gchar *ptr;
+	GaimBuddy *buddy;
+	GaimGroup *group;
 	gchar **users_tbl;
 	int i;
-	if (strstr(webdata, "no_data:")) {
-		gaim_notify_error(gc, NULL,
-						  _("There is no Buddy List stored on the "
-							"Gadu-Gadu server."), NULL);
-		return;
-	}
 
-	if ((ptr = strstr(webdata, "get_results:")) == NULL || (ptr = strchr(ptr, ':')) == NULL) {
-		gaim_debug(GAIM_DEBUG_MISC, "gg", "import_buddies_server_results: import buddies result [%s]\n", webdata);
-		gaim_notify_error(gc, NULL,
-						  _("Couldn't Import Buddy List from Server"), NULL);
-		return;
-	}
-	ptr++;
+	users_tbl = g_strsplit(buddylist, "\r\n", 200);
 
-	users_tbl = g_strsplit(ptr, "\n", AGG_PUBDIR_MAX_ENTRIES);
-
-	/* Parse array of Buddies List */
 	for (i = 0; users_tbl[i] != NULL; i++) {
 		gchar **data_tbl;
 		gchar *name, *show;
 
-		if (strlen(users_tbl[i])==0) {
-			gaim_debug(GAIM_DEBUG_MISC, "gg",
-					   "import_buddies_server_results: users_tbl[i] is empty\n");
+		if (strlen(users_tbl[i]) == 0)
 			continue;
-		}
 
-		g_strdelimit(users_tbl[i], "\r\t\n\015", ' ');
 		data_tbl = g_strsplit(users_tbl[i], ";", 8);
 
 		show = charset_convert(data_tbl[3], "CP1250", "UTF-8");
 		name = data_tbl[6];
 
-		if (invalid_uin(name)) {
+		gaim_debug_info("gg", "got buddy: name=%s show=%s\n", name, show);
+
+		if (gaim_find_buddy(gaim_connection_get_account(gc), name)) {
+			g_free(show);
+			g_strfreev(data_tbl);
 			continue;
 		}
 
-		gaim_debug(GAIM_DEBUG_MISC, "gg",
-				   "import_buddies_server_results: uin: %s\n", name);
-		if (!gaim_find_buddy(gc->account, name)) {
-			GaimBuddy *b;
-			GaimGroup *g;
-			/* Default group if none specified on server */
-			gchar *group = g_strdup("Gadu-Gadu");
-			if (strlen(data_tbl[5])) {
-				gchar **group_tbl = g_strsplit(data_tbl[5], ",", 2);
-				if (strlen(group_tbl[0])) {
-					g_free(group);
-					group = g_strdup(group_tbl[0]);
-				}
-				g_strfreev(group_tbl);
+		gchar *g = g_strdup("Gadu-Gadu");
+
+		if (strlen(data_tbl[5])) {
+			/* Hard limit to at most 50 groups */
+			gchar **group_tbl = g_strsplit(data_tbl[5], ",", 50);
+			if (strlen(group_tbl[0]) > 0) {
+				g_free(g);
+				g = g_strdup(group_tbl[0]);
 			}
-			/* Add Buddy to our userlist */
-			if (!(g = gaim_find_group(group))) {
-				g = gaim_group_new(group);
-				gaim_blist_add_group(g, NULL);
-			}
-			b = gaim_buddy_new(gc->account, name, strlen(show) ? show : NULL);
-			gaim_blist_add_buddy(b, NULL, g, NULL);
-			g_free(group);
+			g_strfreev(group_tbl);
 		}
+
+		buddy = gaim_buddy_new(gaim_connection_get_account(gc), name, strlen(show) ? show : NULL);
+		if (!(group = gaim_find_group(g))) {
+			group = gaim_group_new(g);
+			gaim_blist_add_group(group, NULL);
+		}
+
+		gaim_blist_add_buddy(buddy, NULL, group, NULL);
+		g_free(g);
+
 		g_free(show);
 		g_strfreev(data_tbl);
 	}
 	g_strfreev(users_tbl);
-}
+
+	ggp_buddylist_send(gc);
 
-static void export_buddies_server_results(GaimConnection *gc, gchar *webdata)
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_generic_status_handler(GaimConnection *gc, uin_t uin, int status, const char *descr) {{{ */
+static void ggp_generic_status_handler(GaimConnection *gc, uin_t uin, int status, const char *descr)
 {
-	if (strstr(webdata, "put_success:")) {
-		gaim_notify_info(gc, NULL,
-						 _("Buddy List successfully transferred to "
-						   "Gadu-Gadu server"), NULL);
-		return;
+	gchar *from;
+	const char *st;
+	gchar *msg;
+
+	from = g_strdup_printf("%ld", (unsigned long int)uin);
+	switch (status) {
+		case GG_STATUS_NOT_AVAIL:
+		case GG_STATUS_NOT_AVAIL_DESCR:
+			st = "offline";
+			break;
+		case GG_STATUS_AVAIL:
+		case GG_STATUS_AVAIL_DESCR:
+			st = "online";
+			break;
+		case GG_STATUS_BUSY:
+		case GG_STATUS_BUSY_DESCR:
+			st = "away";
+			break;
+		case GG_STATUS_BLOCKED:
+			/* user is blocking us.... */
+			st = "blocked";
+			break;
+		default:
+			st = "online";
+			gaim_debug_info("gg", "GG_EVENT_NOTIFY: Unknown status: %d\n", status);
+			break;
 	}
 
-	gaim_debug(GAIM_DEBUG_MISC, "gg",
-			   "export_buddies_server_results: webdata [%s]\n", webdata);
-	gaim_notify_error(gc, NULL,
-					  _("Couldn't transfer Buddy List to Gadu-Gadu server"),
-					  NULL);
+	gaim_debug_info("gg", "st = %s\n", st);
+	msg = charset_convert(descr, "CP1250", "UTF-8");
+	gaim_prpl_got_user_status(gaim_connection_get_account(gc), from, st, "message", msg, NULL);
+	g_free(from);
+	g_free(msg);
 }
+/* }}} */
 
-static void delete_buddies_server_results(GaimConnection *gc, gchar *webdata)
+/*
+ */
+/* static void ggp_pubdir_start_search(GaimConnection *gc, GGPSearchForm *form) {{{ */
+static void ggp_pubdir_start_search(GaimConnection *gc, GGPSearchForm *form)
 {
-	if (strstr(webdata, "put_success:")) {
-		gaim_notify_info(gc, NULL,
-						 _("Buddy List successfully deleted from "
-						   "Gadu-Gadu server"), NULL);
+	GGPInfo *info = gc->proto_data;
+	gg_pubdir50_t req;
+
+	gaim_debug_info("gg", "It's time to perform a search...\n");
+
+	if ((req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)) == NULL) {
+		gaim_debug_error("gg", "ggp_bmenu_show_details: Unable to create req variable.\n");
 		return;
 	}
 
-	gaim_debug(GAIM_DEBUG_MISC, "gg",
-			   "delete_buddies_server_results: webdata [%s]\n", webdata);
-	gaim_notify_error(gc, NULL,
-					  _("Couldn't delete Buddy List from Gadu-Gadu server"),
-					  NULL);
-}
-#endif
+	if (form->uin != NULL) {
+		gaim_debug_info("gg", "    uin: %s\n", form->uin);
+		gg_pubdir50_add(req, GG_PUBDIR50_UIN, form->uin);
+	} else {
+		if (form->lastname != NULL) {
+			gaim_debug_info("gg", "    lastname: %s\n", form->lastname);
+			gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, form->lastname);
+		}
+
+		if (form->firstname != NULL) {
+			gaim_debug_info("gg", "    firstname: %s\n", form->firstname);
+			gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, form->firstname);
+		}
+
+		if (form->nickname != NULL) {
+			gaim_debug_info("gg", "    nickname: %s\n", form->nickname);
+			gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, form->nickname);
+		}
 
-static void password_change_server_results(GaimConnection *gc, gchar *webdata)
-{
-	if (strstr(webdata, "reg_success:")) {
-		gaim_notify_info(gc, NULL,
-						 _("Password changed successfully"), NULL);
+		if (form->city != NULL) {
+			gaim_debug_info("gg", "    city: %s\n", form->city);
+			gg_pubdir50_add(req, GG_PUBDIR50_CITY, form->city);
+		}
+
+		if (form->birthyear != NULL) {
+			gaim_debug_info("gg", "    birthyear: %s\n", form->birthyear);
+			gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, form->birthyear);
+		}
+
+		if (form->gender != NULL) {
+			gaim_debug_info("gg", "    gender: %s\n", form->gender);
+			gg_pubdir50_add(req, GG_PUBDIR50_GENDER, form->gender);
+		}
+
+		if (form->active != NULL) {
+			gaim_debug_info("gg", "    active: %s\n", form->active);
+			gg_pubdir50_add(req, GG_PUBDIR50_ACTIVE, form->active);
+		}
+	}
+
+	gaim_debug_info("gg", "offset: %s\n", form->offset);
+	gg_pubdir50_add(req, GG_PUBDIR50_START, g_strdup(form->offset));
+
+	if (gg_pubdir50(info->session, req) == 0) {
+		gaim_debug_warning("gg", "ggp_bmenu_show_details: Search failed.\n");
 		return;
 	}
 
-	gaim_debug(GAIM_DEBUG_MISC, "gg",
-			   "password_change_server_results: webdata [%s]\n", webdata);
-	gaim_notify_error(gc, NULL,
-					  _("Password couldn't be changed"), NULL);
+	gg_pubdir50_free(req);
+}
+/* }}} */
+
+/*
+ * Return converted to the UTF-8 value of the specified field.
+ *
+ * @param res    Public directory look-up result
+ * @param num    Id of the record
+ * @param fileld Name of the field
+ * 
+ * @return UTF-8 encoded value of the field
+ */
+/* static char *ggp_get_pubdir_info(gg_pubdir50_t res, int num, const char *field) {{{ */
+static char *ggp_get_pubdir_info(gg_pubdir50_t res, int num, const char *field)
+{
+	char *tmp;
+
+	tmp = charset_convert(gg_pubdir50_get(res, num, field), "CP1250", "UTF-8");
+
+	return (tmp == NULL) ? g_strdup("") : tmp;
+}
+/* }}} */
+
+static void ggp_callback_show_next(GaimConnection *gc, GList *row)
+{
+	GGPInfo *info = gc->proto_data;
+
+	g_free(info->search_form->offset);
+	info->search_form->offset = g_strdup(info->search_form->last_uin);
+	gaim_debug_info("gg", "london calling... offset = %s\n", info->search_form->offset);
+	ggp_pubdir_start_search(gc, info->search_form);
 }
 
-static void http_results(gpointer data, gint source, GaimInputCondition cond)
+static void ggp_callback_add_buddy(GaimConnection *gc, GList *row)
 {
-	struct agg_http *hdata = data;
-	GaimConnection *gc = hdata->gc;
-	char *webdata;
-	int len;
-	char read_data;
+	gaim_blist_request_add_buddy(gaim_connection_get_account(gc),
+			g_list_nth_data(row, 0), NULL, NULL);
+}
 
-	gaim_debug(GAIM_DEBUG_INFO, "gg", "http_results: begin\n");
+/*
+ */
+/* static void ggp_callback_recv(gpointer _gc, gint fd, GaimInputCondition cond) {{{ */
+static void ggp_callback_recv(gpointer _gc, gint fd, GaimInputCondition cond)
+{
+	GaimConnection *gc = _gc;
+	GGPInfo *info = gc->proto_data;
+	struct gg_event *ev;
+	int i;
 
-	if (!g_list_find(gaim_connections_get_all(), gc)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "gg",
-				   "search_callback: g_slist_find error\n");
-		gaim_input_remove(hdata->inpa);
-		g_free(hdata);
-		close(source);
+	if (!(ev = gg_watch_fd(info->session))) {
+		gaim_debug_error("gg", "ggp_callback_recv: gg_watch_fd failed -- CRITICAL!\n");
+		gaim_connection_error(gc, _("Unable to read socket"));
 		return;
 	}
 
-	webdata = NULL;
-	len = 0;
+	switch (ev->type) {
+		case GG_EVENT_NONE:
+			/* Nothing happened. */
+			break;
+		case GG_EVENT_MSG:
+			{
+				gchar *from;
+				gchar *msg;
+				gchar *tmp;
+
+				from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
 
-	while (read(source, &read_data, 1) > 0 || errno == EWOULDBLOCK) {
-		if (errno == EWOULDBLOCK) {
-			errno = 0;
-			continue;
-		}
+				msg = charset_convert((const char *)ev->event.msg.message,
+									  "CP1250", "UTF-8");
+				gaim_str_strip_cr(msg);
+				tmp = g_markup_escape_text(msg, -1);
+				gaim_debug_info("gg", "msg form (%s): %s (class = %d)\n", from, tmp, ev->event.msg.msgclass);
+				serv_got_im(gc, from, tmp, 0, ev->event.msg.time);
+				g_free(msg);
+				g_free(tmp);
+				g_free(from);
+			}
+			break;
+		case GG_EVENT_ACK:
+			gaim_debug_info("gg", "message sent to: %ld, delivery status=%d, seq=%d\n",
+					ev->event.ack.recipient, ev->event.ack.status, ev->event.ack.seq);
+			break;
+		case GG_EVENT_NOTIFY:
+		case GG_EVENT_NOTIFY_DESCR:
+			{
+				struct gg_notify_reply *n;
+				char *descr;
 
-		if (!read_data)
-			continue;
+				gaim_debug_info("gg", "notify_pre: (%d) status: %d\n",
+						ev->event.notify->uin,
+						ev->event.notify->status);
+
+				n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify
+								  : ev->event.notify_descr.notify;
 
-		len++;
-		webdata = g_realloc(webdata, len);
-		webdata[len - 1] = read_data;
-	}
+				for (; n->uin; n++) {
+					descr = (ev->type == GG_EVENT_NOTIFY) ? NULL
+					      				      : ev->event.notify_descr.descr;
+					gaim_debug_info("gg", "notify: (%d) status: %d; descr: %s\n",
+							n->uin, n->status, descr);
+
+					ggp_generic_status_handler(gc,
+							n->uin, n->status, descr);
+				}
+			}
+			break;
+		case GG_EVENT_NOTIFY60:
+			gaim_debug_info("gg", "notify60_pre: (%d) status=%d; version=%d; descr=%s\n",
+					ev->event.notify60->uin, ev->event.notify60->status,
+					ev->event.notify60->version, ev->event.notify60->descr);
+
+			for (i = 0; ev->event.notify60[i].uin; i++) {
+				gaim_debug_info("gg", "notify60: (%d) status=%d; version=%d; descr=%s\n",
+						ev->event.notify60[i].uin, ev->event.notify60[i].status,
+						ev->event.notify60[i].version, ev->event.notify60[i].descr);
 
-	webdata = g_realloc(webdata, len + 1);
-	webdata[len] = 0;
+				ggp_generic_status_handler(gc,
+						ev->event.notify60[i].uin,
+						ev->event.notify60[i].status,
+						ev->event.notify60[i].descr);
+			}
+			break;
+		case GG_EVENT_STATUS:
+			gaim_debug_info("gg", "status: (%d) status=%d; descr=%s\n",
+					ev->event.status.uin, ev->event.status.status,
+					ev->event.status.descr);
 
-	gaim_input_remove(hdata->inpa);
-	close(source);
-
-	gaim_debug(GAIM_DEBUG_MISC, "gg",
-			   "http_results: type %d, webdata [%s]\n", hdata->type, webdata);
+			ggp_generic_status_handler(gc,
+						   ev->event.status.uin,
+						   ev->event.status.status,
+						   ev->event.status.descr);
+			break;
+		case GG_EVENT_STATUS60:
+			gaim_debug_info("gg", "status60: (%d) status=%d; version=%d; descr=%s\n",
+					ev->event.status60.uin,
+					ev->event.status60.status,
+					ev->event.status60.version,
+					ev->event.status60.descr);
 
-	switch (hdata->type) {
-	case AGG_HTTP_SEARCH:
-		search_results(gc, webdata);
-		break;
-#if 0
-	case AGG_HTTP_USERLIST_IMPORT:
-		import_buddies_server_results(gc, webdata);
-		break;
-	case AGG_HTTP_USERLIST_EXPORT:
-		export_buddies_server_results(gc, webdata);
-		break;
-	case AGG_HTTP_USERLIST_DELETE:
-	        delete_buddies_server_results(gc, webdata);
-		break;
-#endif
-	case AGG_HTTP_PASSWORD_CHANGE:
-		password_change_server_results(gc, webdata);
-		break;
-	case AGG_HTTP_NONE:
-	default:
-		gaim_debug(GAIM_DEBUG_ERROR, "gg",
-				   "http_results: unsupported type %d\n", hdata->type);
-		break;
-	}
+			ggp_generic_status_handler(gc,
+						   ev->event.status60.uin,
+						   ev->event.status60.status,
+						   ev->event.status60.descr);
+			break;
+		case GG_EVENT_USERLIST:
+	    		if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) {
+				gaim_debug_info("gg", "GG_USERLIST_GET_REPLY\n");
+				if (ev->event.userlist.reply != NULL) {
+				    ggp_buddylist_load(gc, ev->event.userlist.reply);
+				}
+			break;
+			} else {
+				gaim_debug_info("gg", "GG_USERLIST_PUT_REPLY. Userlist stored on the server.\n");
+			}
+			break;
+		case GG_EVENT_PUBDIR50_SEARCH_REPLY:
+			{
+				GaimNotifySearchResults *results;
+				GaimNotifySearchColumn *column;
+				gg_pubdir50_t req = ev->event.pubdir50;
+				int res_count = 0;
+				int start;
+				int i;
+
+				res_count = gg_pubdir50_count(req);
+				if (res_count < 1) {
+					gaim_debug_info("gg", "GG_EVENT_PUBDIR50_SEARCH_REPLY: Nothing found\n");
+					return;
+				}
+				res_count = (res_count > 20) ? 20 : res_count;
+
+				results = gaim_notify_searchresults_new();
+
+				column = gaim_notify_searchresults_column_new("UIN");
+				gaim_notify_searchresults_column_add(results, column);
+
+				column = gaim_notify_searchresults_column_new("First name");
+				gaim_notify_searchresults_column_add(results, column);
+
+				column = gaim_notify_searchresults_column_new("Nick name");
+				gaim_notify_searchresults_column_add(results, column);
 
-	g_free(webdata);
-	g_free(hdata);
-}
+				column = gaim_notify_searchresults_column_new("City");
+				gaim_notify_searchresults_column_add(results, column);
 
-static void http_req_callback(gpointer data, gint source, GaimInputCondition cond)
-{
-	struct agg_http *hdata = data;
-	GaimConnection *gc = hdata->gc;
-	gchar *request = hdata->request;
-	gchar *buf;
+				column = gaim_notify_searchresults_column_new("Birth year");
+				gaim_notify_searchresults_column_add(results, column);
 
-	gaim_debug(GAIM_DEBUG_INFO, "gg", "http_req_callback: begin\n");
+				gaim_debug_info("gg", "Going with %d entries\n", res_count);
+
+				start = (int)ggp_str_to_uin(gg_pubdir50_get(req, 0, GG_PUBDIR50_START));
+				gaim_debug_info("gg", "start = %d\n", start);
 
-	if (!g_list_find(gaim_connections_get_all(), gc)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "gg",
-				   "http_req_callback: g_slist_find error\n");
-		g_free(request);
-		g_free(hdata);
-		close(source);
-		return;
-	}
+				for (i = 0; i < res_count; i++) {
+					GList *row = NULL;
+					char *birth = ggp_get_pubdir_info(req, i, GG_PUBDIR50_BIRTHYEAR);
+					
+					/* TODO: Status will be displayed as an icon. */
+					/* row = g_list_append(row, ggp_get_pubdir_info(req, i, GG_PUBDIR50_STATUS)); */
+					row = g_list_append(row, ggp_get_pubdir_info(req, i, GG_PUBDIR50_UIN));
+					row = g_list_append(row, ggp_get_pubdir_info(req, i, GG_PUBDIR50_FIRSTNAME));
+					row = g_list_append(row, ggp_get_pubdir_info(req, i, GG_PUBDIR50_NICKNAME));
+					row = g_list_append(row, ggp_get_pubdir_info(req, i, GG_PUBDIR50_CITY));
+					row = g_list_append(row, (birth && strncmp(birth, "0", 1)) ? birth : g_strdup("-"));
+					gaim_notify_searchresults_row_add(results, row);
+					if (i == res_count - 1) {
+						g_free(info->search_form->last_uin);
+						info->search_form->last_uin = ggp_get_pubdir_info(req, i, GG_PUBDIR50_UIN);
+					}
+				}
 
-	if (source == 0) {
-		g_free(request);
-		g_free(hdata);
-		return;
+				gaim_notify_searchresults_button_add(results, GAIM_NOTIFY_BUTTON_CONTINUE, ggp_callback_show_next);
+				gaim_notify_searchresults_button_add(results, GAIM_NOTIFY_BUTTON_ADD_BUDDY, ggp_callback_add_buddy);
+				if (info->searchresults_window == NULL) {
+					void *h = gaim_notify_searchresults(gc, _("Gadu-Gadu Public Directory"),
+											  _("Search results"), NULL, results, NULL, NULL);
+					info->searchresults_window = h;
+				} else {
+					gaim_notify_searchresults_new_rows(gc, results, info->searchresults_window, NULL);
+				}
+			}
+			break;
+		default:
+			gaim_debug_error("gg", "unsupported event type=%d\n", ev->type);
+			break;
 	}
 
-	gaim_debug(GAIM_DEBUG_MISC, "gg",
-			   "http_req_callback: http request [%s]\n", request);
-
-	buf = g_strdup_printf("POST %s HTTP/1.0\r\n"
-			      "Host: %s\r\n"
-			      "Content-Type: application/x-www-form-urlencoded\r\n"
-			      "User-Agent: " GG_HTTP_USERAGENT "\r\n"
-			      "Content-Length: %d\r\n"
-			      "Pragma: no-cache\r\n" "\r\n" "%s\r\n",
-			      hdata->form, hdata->host, (int)strlen(request), request);
-
-	g_free(request);
+	gg_free_event(ev);
+}
+/* }}} */
 
-	if (write(source, buf, strlen(buf)) < strlen(buf)) {
-		g_free(buf);
-		g_free(hdata);
-		close(source);
-		gaim_notify_error(gc, NULL,
-						  _("Error communicating with Gadu-Gadu server"),
-						  _("Gaim was unable to complete your request due "
-							"to a problem communicating with the Gadu-Gadu "
-							"HTTP server.  Please try again later."));
-		return;
-	}
-
-	g_free(buf);
+/**
+ * Set offline status for all buddies.
+ *
+ * @param gc Connection handler
+ */
+/* static void ggp_buddylist_offline(GaimConnection *gc) {{{ */
+static void ggp_buddylist_offline(GaimConnection *gc)
+{
+	GaimBuddyList *blist;
+	GaimBlistNode *gnode, *cnode, *bnode;
+	GaimBuddy *buddy;
 
-	hdata->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_results, hdata);
-}
-
-#if 0
-static void import_buddies_server(GaimConnection *gc)
-{
-	struct agg_http *hi = g_new0(struct agg_http, 1);
-	gchar *u = gg_urlencode(gaim_account_get_username(gc->account));
-	gchar *p = gg_urlencode(gaim_connection_get_password(gc));
+	if ((blist = gaim_get_blist()) != NULL)
+	{
+		for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
+		{
+			if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
+				continue;
+			for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
+			{
+				if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
+					continue;
+				for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
+				{
+					if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
+						continue;
 
-	hi->gc = gc;
-	hi->type = AGG_HTTP_USERLIST_IMPORT;
-	hi->form = AGG_PUBDIR_USERLIST_IMPORT_FORM;
-	hi->host = GG_PUBDIR_HOST;
-	hi->request = g_strdup_printf("FmNum=%s&Pass=%s", u, p);
+					buddy = (GaimBuddy *)bnode;
 
-	g_free(u);
-	g_free(p);
+					if (buddy->account != gc->account)
+						continue;
 
-	if (gaim_proxy_connect(gc->account, GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, hi) < 0) {
-		gaim_notify_error(gc, NULL,
-						  _("Unable to import Gadu-Gadu buddy list"),
-						  _("Gaim was unable to connect to the Gadu-Gadu "
-							"buddy list server.  Please try again later."));
-		g_free(hi->request);
-		g_free(hi);
-		return;
+					gaim_prpl_got_user_status(
+						gaim_connection_get_account(gc),
+						buddy->name, "offline", NULL);
+					gaim_debug_info("gg", "ggp_buddylist_offline: gone: %s\n", buddy->name);
+				}
+			}
+		}
 	}
 }
-
-static void export_buddies_server(GaimConnection *gc)
-{
-	struct agg_http *he = g_new0(struct agg_http, 1);
-	gchar *ptr;
-	gchar *u = gg_urlencode(gaim_account_get_username(gc->account));
-	gchar *p = gg_urlencode(gaim_connection_get_password(gc));
+/* }}} */
 
+/**
+ * Get all the buddies in the current account.
+ *
+ * @param account Current account.
+ * 
+ * @return List of buddies.
+ */
+/* static char *ggp_buddylist_dump(GaimAccount *account) {{{ */
+static char *ggp_buddylist_dump(GaimAccount *account)
+{
+	GaimBuddyList *blist;
 	GaimBlistNode *gnode, *cnode, *bnode;
-
-	he->gc = gc;
-	he->type = AGG_HTTP_USERLIST_EXPORT;
-	he->form = AGG_PUBDIR_USERLIST_EXPORT_FORM;
-	he->host = GG_PUBDIR_HOST;
-	he->request = g_strdup_printf("FmNum=%s&Pass=%s&Contacts=", u, p);
+	GaimGroup *group;
+	GaimBuddy *buddy;
 
-	g_free(u);
-	g_free(p);
+	char *buddylist = g_strdup("");
+	char *ptr;
 
-	for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
-		GaimGroup *g = (GaimGroup *)gnode;
-		int num_added=0;
-		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
+	if ((blist = gaim_get_blist()) == NULL)
+		return NULL;
+
+	for (gnode = blist->root; gnode != NULL; gnode = gnode->next) {
+		if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		for(cnode = gnode->child; cnode; cnode = cnode->next) {
-			if(!GAIM_BLIST_NODE_IS_CONTACT(cnode))
+
+		group = (GaimGroup *)gnode;
+
+		for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) {
+			if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for(bnode = cnode->child; bnode; bnode = bnode->next) {
-				GaimBuddy *b = (GaimBuddy *)bnode;
 
-				if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
+			for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) {
+				if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 
-				if(b->account == gc->account) {
-					gchar *newdata;
-					/* GG Number */
-					gchar *name = gg_urlencode(b->name);
-					/* GG Pseudo */
-					gchar *show = gg_urlencode(b->alias ? b->alias : b->name);
-					/* Group Name */
-					gchar *gname = gg_urlencode(g->name);
-
-					ptr = he->request;
-					newdata = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s",
-							show, show, show, show, "", gname, name);
+				buddy = (GaimBuddy *)bnode;
+				if (buddy->account != account)
+					continue;
 
-					if(num_added > 0)
-						he->request = g_strconcat(ptr, "%0d%0a", newdata, NULL);
-					else
-						he->request = g_strconcat(ptr, newdata, NULL);
-
-					num_added++;
+				gchar *newdata;
+				/* GG Number */
+				gchar *name = buddy->name;
+				/* GG Pseudo */
+				gchar *show = buddy->alias ? buddy->alias : buddy->name;
+				/* Group Name */
+				gchar *gname = group->name;
 
-					g_free(newdata);
-					g_free(ptr);
+				newdata = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s;%s%s\r\n",
+						show, show, show, show, "", gname, name, "", "");
 
-					g_free(gname);
-					g_free(show);
-					g_free(name);
-				}
+				ptr = buddylist;
+				buddylist = g_strconcat(ptr, newdata, NULL);
+
+				g_free(newdata);
+				g_free(ptr);
 			}
 		}
 	}
 
-	if (gaim_proxy_connect(gc->account, GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, he) < 0) {
-		gaim_notify_error(gc, NULL,
-						  _("Couldn't export buddy list"),
-						  _("Gaim was unable to connect to the buddy "
-							"list server.  Please try again later."));
-		g_free(he->request);
-		g_free(he);
+	return buddylist;
+}
+/* }}} */
+
+/**
+ * Request buddylist from the server.
+ * Buddylist is received in the ggp_callback_recv().
+ *
+ * @param Current action handler.
+ */
+/* static void ggp_action_buddylist_get(GaimPluginAction *action) {{{ */
+static void ggp_action_buddylist_get(GaimPluginAction *action)
+{
+	GaimConnection *gc = (GaimConnection *)action->context;
+	GGPInfo *info = gc->proto_data;
+
+	gaim_debug_info("gg", "Downloading...\n");
+
+	gg_userlist_request(info->session, GG_USERLIST_GET, NULL);
+}
+/* }}} */
+
+/**
+ * Upload the buddylist to the server.
+ *
+ * @param action Current action handler.
+ */
+/* static void ggp_action_buddylist_put(GaimPluginAction *action) {{{ */
+static void ggp_action_buddylist_put(GaimPluginAction *action)
+{
+	GaimConnection *gc = (GaimConnection *)action->context;
+	GGPInfo *info = gc->proto_data;
+
+	char *buddylist = ggp_buddylist_dump(gaim_connection_get_account(gc));
+
+	gaim_debug_info("gg", "Uploading...\n");
+	
+	if (buddylist == NULL)
+		return;
+
+	gg_userlist_request(info->session, GG_USERLIST_PUT, buddylist);
+	g_free(buddylist);
+}
+/* }}} */
+
+/**
+ * Delete buddylist from the server.
+ *
+ * @param action Current action handler.
+ */
+/* static void ggp_action_buddylist_delete(GaimPluginAction *action) {{{ */
+static void ggp_action_buddylist_delete(GaimPluginAction *action)
+{
+	GaimConnection *gc = (GaimConnection *)action->context;
+	GGPInfo *info = gc->proto_data;
+
+	gaim_debug_info("gg", "Deleting...\n");
+
+	gg_userlist_request(info->session, GG_USERLIST_PUT, NULL);
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_callback_buddylist_save_ok(GaimConnection *gc, gchar *file) {{{ */
+static void ggp_callback_buddylist_save_ok(GaimConnection *gc, gchar *file)
+{
+	GaimAccount *account = gaim_connection_get_account(gc);
+
+	FILE *fh;
+	char *buddylist = ggp_buddylist_dump(account);
+	gchar *msg;
+
+	gaim_debug_info("gg", "Saving...\n");
+	gaim_debug_info("gg", "file = %s\n", file);
+
+	if (buddylist == NULL) {
+		gaim_notify_info(account, _("Save Buddylist..."),
+				 _("Your buddylist is empty, nothing was written to the file."),
+				 NULL);
 		return;
 	}
-}
 
-static void delete_buddies_server(GaimConnection *gc)
-{
-	struct agg_http *he = g_new0(struct agg_http, 1);
-	gchar *u = gg_urlencode(gaim_account_get_username(gc->account));
-	gchar *p = gg_urlencode(gaim_connection_get_password(gc));
+	if ((fh = g_fopen(file, "wb")) == NULL) {
+		msg = g_strconcat(_("Couldn't open file"), ": ", file, "\n", NULL);
+		gaim_debug_error("gg", "Could not open file: %s\n", file);
+		gaim_notify_error(account, _("Couldn't open file"), msg, NULL);
+		g_free(msg);
+		g_free(file);
+		return;
+	}
+
+	fwrite(buddylist, sizeof(char), g_utf8_strlen(buddylist, -1), fh);
+	fclose(fh);
+	g_free(buddylist);
+
+	gaim_notify_info(account, _("Save Buddylist..."),
+			 _("Buddylist saved successfully!"), NULL);
+}
+/* }}} */
 
-	he->gc = gc;
-	he->type = AGG_HTTP_USERLIST_DELETE;
-	he->form = AGG_PUBDIR_USERLIST_EXPORT_FORM;
-	he->host = GG_PUBDIR_HOST;
-	he->request = g_strdup_printf("FmNum=%s&Pass=%s&Delete=1", u, p);
+/*
+ */
+/* static void ggp_callback_buddylist_load_ok(GaimConnection *gc, gchar *file) {{{ */
+static void ggp_callback_buddylist_load_ok(GaimConnection *gc, gchar *file)
+{
+	GaimAccount *account = gaim_connection_get_account(gc);
+	char *buddylist, *tmp, *ptr;
+	FILE *fh;
+	gchar *msg;
 
-	if (gaim_proxy_connect(gc->account, GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, he) < 0) {
-		gaim_notify_error(gc, NULL,
-						  _("Unable to delete Gadu-Gadu buddy list"),
-						  _("Gaim was unable to connect to the buddy "
-							"list server.  Please try again later."));
-		g_free(he->request);
-		g_free(he);
+	buddylist = g_strdup("");
+	tmp = g_new0(gchar, 50);
+
+	gaim_debug_info("gg", "file_name = %s\n", file);
+
+	if ((fh = g_fopen(file, "rb")) == NULL) {
+		msg = g_strconcat(_("Couldn't open file"), ": ", file, "\n", NULL);
+		gaim_debug_error("gg", "Could not open file: %s\n", file);
+		gaim_notify_error(account, _("Could't open file"), msg, NULL);
+		g_free(msg);
+		g_free(file);
 		return;
 	}
+
+	while (fread(tmp, sizeof(gchar), 49, fh) == 49) {
+		tmp[49] = '\0';
+		/* gaim_debug_info("gg", "read: %s\n", tmp); */
+		ptr = g_strconcat(buddylist, tmp, NULL);
+		memset(tmp, '\0', 50); 
+		g_free(buddylist);
+		buddylist = ptr;
+	}
+	fclose(fh);
+	g_free(tmp);
+
+	ggp_buddylist_load(gc, buddylist);
+	g_free(buddylist);
+
+	gaim_notify_info(account,
+			 _("Load Buddylist..."),
+			 _("Buddylist loaded successfully!"), NULL);
 }
-#endif
+/* }}} */
+
+/*
+ */
+/* static void ggp_action_buddylist_save(GaimPluginAction *action) {{{ */
+static void ggp_action_buddylist_save(GaimPluginAction *action)
+{
+	GaimConnection *gc = (GaimConnection *)action->context;
+
+	gaim_request_file(action, _("Save buddylist..."), NULL, TRUE,
+				G_CALLBACK(ggp_callback_buddylist_save_ok), NULL, gc);
+}
+/* }}} */
 
-#if 0
-static void agg_dir_search(GaimConnection *gc, const char *first, const char *middle,
-			   const char *last, const char *maiden, const char *city, const char *state,
-			   const char *country, const char *email)
+/*
+ */
+/* static void ggp_action_buddylist_load(GaimPluginAction *action) {{{ */
+static void ggp_action_buddylist_load(GaimPluginAction *action)
+{
+	GaimConnection *gc = (GaimConnection *)action->context;
+
+	gaim_request_file(action, "Load buddylist from file...", NULL, FALSE,
+				G_CALLBACK(ggp_callback_buddylist_load_ok), NULL, gc);
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_callback_change_passwd_ok(GaimConnection *gc, GaimRequestFields *fields) {{{ */
+static void ggp_callback_change_passwd_ok(GaimConnection *gc, GaimRequestFields *fields)
 {
-	struct agg_http *srch = g_new0(struct agg_http, 1);
-	srch->gc = gc;
-	srch->type = AGG_HTTP_SEARCH;
-	srch->form = AGG_PUBDIR_SEARCH_FORM;
-	srch->host = GG_PUBDIR_HOST;
+	GaimAccount *account;
+	GGPInfo *info = gc->proto_data;
+	struct gg_http *h;
+	gchar *cur, *p1, *p2, *t;
+
+	cur = charset_convert(gaim_request_fields_get_string(fields, "password_cur"),
+			     "UTF-8", "CP1250");
+	p1  = charset_convert(gaim_request_fields_get_string(fields, "password1"),
+			     "UTF-8", "CP1250");
+	p2  = charset_convert(gaim_request_fields_get_string(fields, "password2"),
+			     "UTF-8", "CP1250");
+	t   = charset_convert(gaim_request_fields_get_string(fields, "token"),
+			     "UTF-8", "CP1250");
+
+	account = gaim_connection_get_account(gc);
+
+	if (cur == NULL || p1 == NULL || p2 == NULL || t == NULL ||
+	    *cur == '\0' || *p1 == '\0' || *p2 == '\0' || *t == '\0') {
+		gaim_notify_error(account, NULL, _("Fill in the fields."), NULL);
+		goto exit_err;
+	}
+
+	if (g_utf8_collate(p1, p2) != 0) {
+		gaim_notify_error(account, NULL, _("New passwords do not match."), NULL);
+		goto exit_err;
+	}
 
-	if (email && strlen(email)) {
-		gchar *eemail = gg_urlencode(email);
-		srch->request = g_strdup_printf("Mode=1&Email=%s", eemail);
-		g_free(eemail);
-	} else {
-		gchar *new_first = charset_convert(first, "UTF-8", "CP1250");
-		gchar *new_last = charset_convert(last, "UTF-8", "CP1250");
-		gchar *new_city = charset_convert(city, "UTF-8", "CP1250");
+	if (g_utf8_collate(cur, gaim_account_get_password(account)) != 0) {
+		gaim_notify_error(account, NULL,
+			_("Your current password is different from the one that you specified."),
+			NULL);
+		goto exit_err;
+	}
+
+	gaim_debug_info("gg", "change_passwd: old=%s; p1=%s; token=%s\n",
+			cur, p1, info->chpasswd_token->token_id);
+
+	/* XXX: this e-mail should be a pref... */
+	h = gg_change_passwd4(ggp_get_uin(account),
+			      "user@example.net", gaim_account_get_password(account),
+			      p1, info->chpasswd_token->token_id, t, 0);
 
-		gchar *enew_first = gg_urlencode(new_first);
-		gchar *enew_last = gg_urlencode(new_last);
-		gchar *enew_city = gg_urlencode(new_city);
+	if (h == NULL) {
+		gaim_notify_error(account, NULL,
+			_("Unable to change password. Error occured.\n"),
+			NULL);
+		goto exit_err;
+	}
+
+	gaim_account_set_password(account, p1);
+
+	gg_change_passwd_free(h);
+
+	gaim_notify_info(account, _("Change password for the Gadu-Gadu account"),
+			 _("Password was changed successfully!"), NULL);
+
+exit_err:
+	g_free(cur);
+	g_free(p1);
+	g_free(p2);
+	g_free(t);
+	g_free(info->chpasswd_token->token_id);
+	g_free(info->chpasswd_token);
+}
+/* }}} */
 
-		g_free(new_first);
-		g_free(new_last);
-		g_free(new_city);
+/*
+ */
+/* static void ggp_callback_register_account_ok(GaimConnection *gc, GaimRequestFields *fields) {{{ */
+static void ggp_callback_register_account_ok(GaimConnection *gc, GaimRequestFields *fields)
+{
+	GaimAccount *account;
+	GGPInfo *info = gc->proto_data;
+	struct gg_http *h = NULL;
+	struct gg_pubdir *s;
+	uin_t uin;
+	gchar *email, *p1, *p2, *t;
 
-		/* For active only add &ActiveOnly= */
-		srch->request = g_strdup_printf("Mode=0&FirstName=%s&LastName=%s&Gender=%d"
-						"&NickName=%s&City=%s&MinBirth=%d&MaxBirth=%d",
-						enew_first, enew_last, AGG_GENDER_NONE,
-						"", enew_city, 0, 0);
+	email = charset_convert(gaim_request_fields_get_string(fields, "email"),
+			     "UTF-8", "CP1250");
+	p1  = charset_convert(gaim_request_fields_get_string(fields, "password1"),
+			     "UTF-8", "CP1250");
+	p2  = charset_convert(gaim_request_fields_get_string(fields, "password2"),
+			     "UTF-8", "CP1250");
+	t   = charset_convert(gaim_request_fields_get_string(fields, "token"),
+			     "UTF-8", "CP1250");
+
+	account = gaim_connection_get_account(gc);
 
-		g_free(enew_first);
-		g_free(enew_last);
-		g_free(enew_city);
+	if (email == NULL || p1 == NULL || p2 == NULL || t == NULL ||
+	    *email == '\0' || *p1 == '\0' || *p2 == '\0' || *t == '\0') {
+		gaim_notify_error(account, NULL, _("Fill in the fields."), NULL);
+		goto exit_err;
+	}
+
+	if (g_utf8_collate(p1, p2) != 0) {
+		gaim_notify_error(account, NULL, _("Passwords do not match."), NULL);
+		goto exit_err;
+	}
+
+	h = gg_register3(email, p1, info->register_token->token_id, t, 0);
+	if (h == NULL || !(s = h->data) || !s->success) {
+		gaim_notify_error(account, NULL,
+			_("Unable to register new account. Error occured.\n"),
+			NULL);
+		goto exit_err;
 	}
 
-	if (gaim_proxy_connect(gc->account, GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, srch) < 0) {
-		gaim_notify_error(gc, NULL,
-						  _("Unable to access directory"),
-						  _("Gaim was unable to search the Directory "
-							"because it was unable to connect to the "
-							"directory server.  Please try again later."));
-		g_free(srch->request);
-		g_free(srch);
+	uin = s->uin;
+	gaim_debug_info("gg", "registered uin: %d\n", uin);
+
+	gaim_notify_info(NULL, _("New Gadu-Gadu Account Registered"),
+			 _("Registration completed successfully!"), NULL);
+
+exit_err:
+	gg_register_free(h);
+	g_free(email);
+	g_free(p1);
+	g_free(p2);
+	g_free(t);
+	g_free(info->register_token->token_id);
+	g_free(info->register_token);
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_callback_find_buddies(GaimConnection *gc, GaimRequestFields *fields) {{{ */
+static void ggp_callback_find_buddies(GaimConnection *gc, GaimRequestFields *fields)
+{
+	GGPInfo *info = gc->proto_data;
+	GGPSearchForm *form;
+
+	form = ggp_searchform_new();
+	/*
+	 * TODO: Fail if we have already a form attached. Only one search
+	 * at a time will be allowed for now.
+	 */
+	info->search_form = form;
+
+	form->lastname  = charset_convert(gaim_request_fields_get_string(fields, "lastname"),
+									 "UTF-8", "CP1250");
+	form->firstname = charset_convert(gaim_request_fields_get_string(fields, "firstname"),
+									 "UTF-8", "CP1250");
+	form->nickname  = charset_convert(gaim_request_fields_get_string(fields, "nickname"),
+									 "UTF-8", "CP1250");
+	form->city      = charset_convert(gaim_request_fields_get_string(fields, "city"),
+									 "UTF-8", "CP1250");
+	form->birthyear = charset_convert(gaim_request_fields_get_string(fields, "year"),
+								     "UTF-8", "CP1250");
+
+	switch (gaim_request_fields_get_choice(fields, "gender")) {
+		case 1:
+			form->gender = g_strdup(GG_PUBDIR50_GENDER_MALE);
+			break;
+		case 2:
+			form->gender = g_strdup(GG_PUBDIR50_GENDER_FEMALE);
+			break;
+		default:
+			form->gender = NULL;
+			break;
+	}
+
+	form->active = gaim_request_fields_get_bool(fields, "active")
+				   ? g_strdup(GG_PUBDIR50_ACTIVE_TRUE) : NULL;
+
+	form->offset = g_strdup("0");
+
+	ggp_pubdir_start_search(gc, form);
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_find_buddies(GaimPluginAction *action) {{{ */
+static void ggp_find_buddies(GaimPluginAction *action)
+{
+	GaimConnection *gc = (GaimConnection *)action->context;
+
+	GaimRequestFields *fields;
+	GaimRequestFieldGroup *group;
+	GaimRequestField *field;
+
+	fields = gaim_request_fields_new();
+	group = gaim_request_field_group_new(NULL);
+	gaim_request_fields_add_group(fields, group);
+
+	field = gaim_request_field_string_new("lastname", _("Last name"), NULL, FALSE);
+	gaim_request_field_string_set_masked(field, FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("firstname", _("First name"), NULL, FALSE);
+	gaim_request_field_string_set_masked(field, FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("nickname", _("Nickname"), NULL, FALSE);
+	gaim_request_field_string_set_masked(field, FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("city", _("City"), NULL, FALSE);
+	gaim_request_field_string_set_masked(field, FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("year", _("Year of birth"), NULL, FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_choice_new("gender", "Gender", 0);
+	gaim_request_field_choice_add(field, "Male or female");
+	gaim_request_field_choice_add(field, "Male");
+	gaim_request_field_choice_add(field, "Female");
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_bool_new("active", _("Only online"), FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	gaim_request_fields(gc,
+		_("Find buddies"),
+		_("Find buddies"),
+		_("Please, enter your search criteria below"),
+		fields,
+		_("OK"), G_CALLBACK(ggp_callback_find_buddies),
+		_("Cancel"), NULL,
+		gc);
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_change_passwd(GaimPluginAction *action) {{{ */
+static void ggp_change_passwd(GaimPluginAction *action)
+{
+	GaimConnection *gc = (GaimConnection *)action->context;
+	GGPInfo *info = gc->proto_data;
+	GGPToken *token;
+
+	GaimRequestFields *fields;
+	GaimRequestFieldGroup *group;
+	GaimRequestField *field;
+
+	struct gg_http *req;
+	struct gg_token *t;
+	gchar *msg;
+
+	gaim_debug_info("gg", "token: requested.\n");
+
+	/* TODO: This should be async. */
+	if ((req = gg_token(0)) == NULL) {
+		gaim_notify_error(gaim_connection_get_account(gc),
+						  _("Token Error"),
+						  _("Unable to fetch the token.\n"), NULL);
 		return;
 	}
+
+	t = req->data;
+
+	token = g_new0(GGPToken, 1);
+	token->token_id = g_strdup(t->tokenid);
+	info->chpasswd_token = token;
+
+
+	fields = gaim_request_fields_new();
+	group = gaim_request_field_group_new(NULL);
+	gaim_request_fields_add_group(fields, group);
+
+	field = gaim_request_field_string_new("password_cur", _("Current password"), "", FALSE);
+	gaim_request_field_string_set_masked(field, TRUE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("password1", _("Password"), "", FALSE);
+	gaim_request_field_string_set_masked(field, TRUE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("password2", _("Password (retype)"), "", FALSE);
+	gaim_request_field_string_set_masked(field, TRUE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("token", _("Enter current token"), "", FALSE);
+	gaim_request_field_string_set_masked(field, FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	/* original size: 60x24 */
+	field = gaim_request_field_image_new("token_img", _("Current token"), req->body, req->body_size);
+	gaim_request_field_group_add_field(group, field);
+
+	gg_token_free(req);
+
+	msg = g_strdup_printf("%s %d",
+			_("Please, enter your current password and your new password for UIN: "),
+			ggp_get_uin(gaim_connection_get_account(gc)));
+
+	gaim_request_fields(gc,
+		_("Change Gadu-Gadu Password"),
+		_("Change Gadu-Gadu Password"),
+		msg,
+		fields, _("OK"), G_CALLBACK(ggp_callback_change_passwd_ok),
+		_("Cancel"), NULL, gc);
+
+	g_free(msg);
+}
+/* }}} */
+
+/* ---------------------------------------------------------------------- */
+/* ----- GaimPluginProtocolInfo ----------------------------------------- */
+/* ---------------------------------------------------------------------- */
+
+/*
+ */
+/* static const char *ggp_list_icon(GaimAccount *account, GaimBuddy *buddy) {{{ */
+static const char *ggp_list_icon(GaimAccount *account, GaimBuddy *buddy)
+{
+	return "gadu-gadu";
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_list_emblems(GaimBuddy *b, const char **se, const char **sw, const char **nw, const char **ne) {{{ */
+static void ggp_list_emblems(GaimBuddy *b, const char **se, const char **sw, const char **nw, const char **ne)
+{
+	GaimPresence *presence = gaim_buddy_get_presence(b);
+
+	/* 
+	 * Note to myself:
+	 * 	The only valid status types are those defined
+	 * 	in prpl_info->status_types.
+	 *
+	 * Usable icons: away, blocked, dnd, extendedaway,
+	 * freeforchat, ignored, invisible, na, offline.
+	 */
+
+	if (!GAIM_BUDDY_IS_ONLINE(b)) {
+		*se = "offline";
+	} else if (gaim_presence_is_status_active(presence, "away")) {
+		*se = "away";
+	} else if (gaim_presence_is_status_active(presence, "online")) {
+		*se = "online";
+	} else if (gaim_presence_is_status_active(presence, "offline")) {
+		*se = "offline";
+	} else if (gaim_presence_is_status_active(presence, "blocked")) {
+		*se = "blocked";
+	} else {
+		*se = "offline";
+		gaim_debug_info("gg", "ggp_list_emblems: unknown status\n");
+	}
 }
-#endif
+/* }}} */
 
-static void agg_change_passwd(GaimConnection *gc, const char *old, const char *new)
+/*
+ */
+/* static char *ggp_status_text(GaimBuddy *b) {{{ */
+static char *ggp_status_text(GaimBuddy *b)
 {
-	struct agg_http *hpass = g_new0(struct agg_http, 1);
-	gchar *u = gg_urlencode(gaim_account_get_username(gc->account));
-	gchar *p = gg_urlencode(gaim_connection_get_password(gc));
-	gchar *enew = gg_urlencode(new);
-	gchar *eold = gg_urlencode(old);
+	GaimStatus *status;
+	const char *msg;
+	char *text;
+	char *tmp;
 
-	hpass->gc = gc;
-	hpass->type = AGG_HTTP_PASSWORD_CHANGE;
-	hpass->form = AGG_REGISTER_DATA_FORM;
-	hpass->host = GG_REGISTER_HOST;
+	status = gaim_presence_get_active_status(gaim_buddy_get_presence(b));
 
-	/* We are using old password as place for email - it's ugly */
-	hpass->request = g_strdup_printf("fmnumber=%s&fmpwd=%s&pwd=%s&email=%s&code=%u",
-					 u, p, enew, eold, gg_http_hash(old, new));
+	msg = gaim_status_get_attr_string(status, "message");
 
-	g_free(u);
-	g_free(p);
-	g_free(enew);
-	g_free(eold);
+	if (msg != NULL) {
+		tmp = gaim_markup_strip_html(msg);
+		text = g_markup_escape_text(tmp, -1);
+		g_free(tmp);
 
-	if (gaim_proxy_connect(gc->account, GG_REGISTER_HOST, GG_REGISTER_PORT, http_req_callback, hpass) < 0) {
-		gaim_notify_error(gc, NULL,
-							  _("Unable to change Gadu-Gadu password"),
-							  _("Gaim was unable to change your password "
-								"due to an error connecting to the "
-								"Gadu-Gadu server.  Please try again "
-								"later."));
-		g_free(hpass->request);
-		g_free(hpass);
-		return;
+		return text;
+	} else {
+		tmp = g_strdup(gaim_status_get_name(status));
+		text = g_markup_escape_text(tmp, -1);
+		g_free(tmp);
+
+		return text;
 	}
 }
+/* }}} */
 
-static GList *agg_actions(GaimPlugin *plugin, gpointer context)
+/*
+ */
+/* static char *ggp_tooltip_text(GaimBuddy *b) {{{ */
+static char *ggp_tooltip_text(GaimBuddy *b)
+{
+	GaimStatus *status;
+	char *text;
+	gchar *ret;
+	const char *msg, *name;
+
+	status = gaim_presence_get_active_status(gaim_buddy_get_presence(b));
+	msg = gaim_status_get_attr_string(status, "message");
+	name = gaim_status_get_name(status);
+
+	if (msg != NULL) {
+		char *tmp = gaim_markup_strip_html(msg);
+		text = g_markup_escape_text(tmp, -1);
+		g_free(tmp);
+
+		ret = g_strdup_printf("\n<b>%s:</b> %s: %s",
+							  _("Status"), name, text);
+
+		g_free(text);
+	} else {
+		ret = g_strdup_printf("\n<b>%s:</b> %s",
+							  _("Status"), name);
+	}
+
+	return ret;
+}
+/* }}} */
+
+/*
+ */
+/* static GList *ggp_status_types(GaimAccount *account) {{{ */
+static GList *ggp_status_types(GaimAccount *account)
+{
+	GaimStatusType *type;
+	GList *types = NULL;
+
+	type = gaim_status_type_new_with_attrs(GAIM_STATUS_OFFLINE, "offline", _("Offline"),
+	                                       TRUE, TRUE, FALSE, "message", _("Message"),
+					       gaim_value_new(GAIM_TYPE_STRING), NULL);
+	types = g_list_append(types, type);
+
+	type = gaim_status_type_new_with_attrs(GAIM_STATUS_ONLINE, "online", _("Online"),
+	                                       TRUE, TRUE, FALSE, "message", _("Message"),
+					       gaim_value_new(GAIM_TYPE_STRING), NULL);
+	types = g_list_append(types, type);
+
+	/*
+	 * Without this selecting Available or Invisible as own status doesn't
+	 * work. It's not used and not needed to show status of buddies.
+	 */
+	type = gaim_status_type_new_with_attrs(GAIM_STATUS_AVAILABLE, "available", _("Available"),
+					       TRUE, TRUE, FALSE, "message", _("Message"),
+					       gaim_value_new(GAIM_TYPE_STRING), NULL);
+	types = g_list_append(types, type);
+
+	type = gaim_status_type_new_with_attrs(GAIM_STATUS_HIDDEN, "invisible", _("Invisible"),
+					       TRUE, TRUE, FALSE, "message", _("Message"),
+					       gaim_value_new(GAIM_TYPE_STRING), NULL);
+	types = g_list_append(types, type);
+
+	/* type = gaim_status_type_new_with_attrs(GAIM_STATUS_UNAVAILABLE, "not-available", "Not Available", */
+	/*                                        TRUE, TRUE, FALSE, "message", _("Message"),                */
+	/*                                        gaim_value_new(GAIM_TYPE_STRING), NULL);                   */
+	/* types = g_list_append(types, type);                                                               */
+
+	type = gaim_status_type_new_with_attrs(GAIM_STATUS_AWAY, "away", _("Busy"),
+					       TRUE, TRUE, FALSE, "message", _("Message"),
+					       gaim_value_new(GAIM_TYPE_STRING), NULL);
+	types = g_list_append(types, type);
+
+	type = gaim_status_type_new_with_attrs(GAIM_STATUS_HIDDEN, "blocked", _("Blocked"),
+					       TRUE, TRUE, FALSE, "message", _("Message"),
+					       gaim_value_new(GAIM_TYPE_STRING), NULL);
+	types = g_list_append(types, type);
+
+	return types;
+}
+/* }}} */
+
+/*
+ */
+/* static GList *ggp_blist_node_menu(GaimBlistNode *node) {{{ */
+static GList *ggp_blist_node_menu(GaimBlistNode *node)
 {
 	GList *m = NULL;
-	GaimPluginAction *act = NULL;
-
-#if 0
-	act = gaim_plugin_action_new(_("Directory Search"), show_find_info);
-	m = g_list_append(m, act);
-	m = g_list_append(m, NULL);
-#endif
-
-	act = gaim_plugin_action_new(_("Change Password"), change_pass);
-	m = g_list_append(m, act);
 
-#if 0
-	act = gaim_plugin_action_new(_("Import Buddy List from Server"),
-			import_buddies_server);
-	m = g_list_append(m, act);
+	if (!GAIM_BLIST_NODE_IS_BUDDY(node))
+		return NULL;
 
-	act = gaim_plugin_action_new(_("Export Buddy List to Server"),
-			export_buddies_server);
-	m = g_list_append(m, act);
-
-	act = gaim_plugin_action_new(_("Delete Buddy List from Server"),
-			delete_buddies_server);
-	m = g_list_append(m, act);
-#endif
+	/* act = gaim_blist_node_action_new("Change Password", ggp_bmenu_change_passwd, NULL, NULL); */
+	/* m = g_list_append(m, act);                                                                */
 
 	return m;
 }
+/* }}} */
 
-static void agg_get_info(GaimConnection *gc, const char *who)
+/*
+ */
+/* static void ggp_login(GaimAccount *account, GaimStatus *status) {{{ */
+static void ggp_login(GaimAccount *account, GaimStatus *status)
 {
-	struct agg_http *srch = g_new0(struct agg_http, 1);
-	srch->gc = gc;
-	srch->type = AGG_HTTP_SEARCH;
-	srch->form = AGG_PUBDIR_SEARCH_FORM;
-	srch->host = GG_PUBDIR_HOST;
+	GaimConnection *gc = gaim_account_get_connection(account);
+	struct gg_login_params *glp = g_new0(struct gg_login_params, 1);
+	GGPInfo *info = g_new0(GGPInfo, 1);
+
+	info->session = NULL;
+	info->searchresults_window = NULL;
+
+	gc->proto_data = info;
+
+	glp->uin = ggp_get_uin(account);
+	glp->password = (char *)gaim_account_get_password(account);
+
+	glp->async = 0;
+	glp->status = GG_STATUS_AVAIL;
+	glp->tls = 0;
+
+	info->session = gg_login(glp);
+	if (info->session == NULL) {
+		gaim_connection_error(gc, _("Connection failed."));
+		g_free(glp);
+		return;
+	}
+	gaim_debug_info("gg", "ggp_login: so far so good.\n");
+
+	gc->inpa = gaim_input_add(info->session->fd, GAIM_INPUT_READ, ggp_callback_recv, gc);
 
-	/* If it's invalid uin then maybe it's nickname? */
-	if (invalid_uin(who)) {
-		gchar *new_who = charset_convert(who, "UTF-8", "CP1250");
-		gchar *enew_who = gg_urlencode(new_who);
+	gg_change_status(info->session, GG_STATUS_AVAIL);
+	gaim_connection_set_state(gc, GAIM_CONNECTED);
+	ggp_buddylist_send(gc);
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_close(GaimConnection *gc) {{{ */
+static void ggp_close(GaimConnection *gc)
+{
+	GGPInfo *info;
 
-		g_free(new_who);
+	if (gc == NULL) {
+		gaim_debug_info("gg", "gc == NULL\n");
+		return;
+	}
+
+	info = gc->proto_data;
+
+	/* XXX: Any way to pass description here? */
+	if (info->session != NULL)
+		gg_change_status(info->session, GG_STATUS_NOT_AVAIL);
+
+	if (gc->inpa > 0)
+		gaim_input_remove(gc->inpa);
+
+	gg_logoff(info->session);
+	gg_free_session(info->session);
+	ggp_buddylist_offline(gc);
 
-		srch->request = g_strdup_printf("Mode=0&FirstName=%s&LastName=%s&Gender=%d"
-						"&NickName=%s&City=%s&MinBirth=%d&MaxBirth=%d",
-						"", "", AGG_GENDER_NONE, enew_who, "", 0, 0);
+	gaim_debug_info("gg", "Connection closed.\n");
+}
+/* }}} */
+
+/*
+ */
+/* static int ggp_send_im(GaimConnection *gc, const char *who, const char *msg, GaimConvImFlags flags) {{{ */
+static int ggp_send_im(GaimConnection *gc, const char *who, const char *msg, GaimConvImFlags flags)
+{
+	GGPInfo *info = gc->proto_data;
+	const char *tmp;
+
+	if (strlen(msg) == 0)
+		return 1;
+
+	tmp = charset_convert(msg, "UTF-8", "CP1250");
 
-		g_free(enew_who);
-	} else
-		srch->request = g_strdup_printf("Mode=3&UserId=%s", who);
+	if (tmp != NULL && strlen(tmp) > 0) {
+		if (gg_send_message(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), tmp) < 0) {
+			return -1;
+		}
+	}
+
+	return 1;
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_get_info(GaimConnection *gc, const char *name) { {{{ */
+static void ggp_get_info(GaimConnection *gc, const char *name)
+{
+	GGPInfo *info = gc->proto_data;
+	GGPSearchForm *form;
 
-	if (gaim_proxy_connect(gc->account, GG_PUBDIR_HOST, GG_PUBDIR_PORT, http_req_callback, srch) < 0) {
-		gaim_notify_error(gc, NULL,
-						  _("Unable to access user profile."),
-						  _("Gaim was unable to access this user's "
-							"profile due to an error connecting to the "
-							"directory server.  Please try again later."));
-		g_free(srch->request);
-		g_free(srch);
+	form = ggp_searchform_new();
+	info->search_form = form;
+
+	form->uin = g_strdup(name);
+	form->offset = g_strdup("0");
+	form->last_uin = g_strdup("0");
+
+	ggp_pubdir_start_search(gc, form);
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_set_status(GaimAccount *account, GaimStatus *status) {{{ */
+static void ggp_set_status(GaimAccount *account, GaimStatus *status)
+{
+	GaimStatusPrimitive prim;
+	GaimConnection *gc;
+	GGPInfo *info;
+	const char *status_id;
+	int new_status, new_status_descr;
+
+	prim = gaim_status_type_get_primitive(gaim_status_get_type(status));
+
+	if (!gaim_status_is_active(status))
+		return;
+
+	if (prim == GAIM_STATUS_OFFLINE) {
+		gaim_account_disconnect(account);
+		return;
+	}
+
+	if (!gaim_account_is_connected(account)) {
+		gaim_account_connect(account);
 		return;
 	}
-}
+
+	gc = gaim_account_get_connection(account);
+	info = gc->proto_data;
+
+	status_id = gaim_status_get_id(status);
+
+	gaim_debug_info("gg", "ggp_set_status: Requested status = %s\n", status_id);
 
-static const char *agg_list_icon(GaimAccount *a, GaimBuddy *b)
-{
-	return "gadu-gadu";
-}
+	if (strcmp(status_id, "available") == 0) {
+		new_status = GG_STATUS_AVAIL;
+		new_status_descr = GG_STATUS_AVAIL_DESCR;
+	} else if (strcmp(status_id, "away") == 0) {
+		new_status = GG_STATUS_BUSY;
+		new_status_descr = GG_STATUS_BUSY_DESCR;
+	} else if (strcmp(status_id, "invisible") == 0) {
+		new_status = GG_STATUS_INVISIBLE;
+		new_status_descr = GG_STATUS_INVISIBLE_DESCR;
+	} else {
+		new_status = GG_STATUS_AVAIL;
+		new_status_descr = GG_STATUS_AVAIL_DESCR;
+		gaim_debug_info("gg", "ggp_set_status: uknown status requested (status_id=%s)\n", status_id);
+	}
 
-static void agg_list_emblems(GaimBuddy *b, const char **se, const char **sw,
-							 const char **nw, const char **ne)
-{
-	GaimPresence *presence = gaim_buddy_get_presence(b);
+	const char *msg = gaim_status_get_attr_string(status, "message");
+
+	if (msg == NULL) {
+		gaim_debug_info("gg", "ggp_set_status: msg == NULL\n");
+		gg_change_status(info->session, new_status);
+	} else {
+		char *tmp = charset_convert(msg, "UTF-8", "CP1250");
+		gaim_debug_info("gg", "ggp_set_status: msg != NULL. msg = %s\n", tmp);
+		gaim_debug_info("gg", "ggp_set_status: gg_change_status_descr() = %d\n",
+				gg_change_status_descr(info->session, new_status_descr, tmp));
+		g_free(tmp);
+	}
 
-	if (!GAIM_BUDDY_IS_ONLINE(b))
-		*se = "offline";
-	else if (gaim_presence_is_status_active(presence, "away") ||
-			 gaim_presence_is_status_active(presence, "away-friends"))
-	{
-		*se = "away";
-	}
-	else if (gaim_presence_is_status_active(presence, "invisible") ||
-			 gaim_presence_is_status_active(presence, "invisible-friends"))
-	{
-		*se = "invisible";
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group) {{{ */
+static void ggp_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
+{
+	GGPInfo *info = gc->proto_data;
+
+	gg_add_notify(info->session, ggp_str_to_uin(buddy->name));
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group) {{{ */
+static void ggp_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
+{
+	GGPInfo *info = gc->proto_data;
+
+	gg_remove_notify(info->session, ggp_str_to_uin(buddy->name));
+}
+/* }}} */
+
+/*
+ */
+/* static void ggp_keepalive(GaimConnection *gc) {{{ */
+static void ggp_keepalive(GaimConnection *gc)
+{
+	GGPInfo *info = gc->proto_data;
+
+	/* gaim_debug_info("gg", "Keeping connection alive....\n"); */
+
+	if (gg_ping(info->session) < 0) {
+		gaim_debug_info("gg", "Not connected to the server "
+				"or gg_session is not correct\n");
+		gaim_connection_error(gc, _("Not connected to the server."));
 	}
 }
+/* }}} */
+
+/*
+ */
+/* static void ggp_register_user(GaimAccount *account) {{{ */
+static void ggp_register_user(GaimAccount *account)
+{
+	GaimConnection *gc;
+	GaimRequestFields *fields;
+	GaimRequestFieldGroup *group;
+	GaimRequestField *field;
+	GGPInfo *info;
+	GGPToken *token;
+
+	struct gg_http *req;
+	struct gg_token *t;
+
+	gaim_debug_info("gg", "token: requested.\n");
+
+	if ((req = gg_token(0)) == NULL) {
+		gaim_notify_error(account, _("Token Error"),
+				_("Unable to fetch the token.\n"), NULL);
+		return;
+	}
+	t = req->data;
+
+	gc = gaim_account_get_connection(account);
+
+	info = g_new0(GGPInfo, 1);
+	gc->proto_data = info;
+
+	token = g_new0(GGPToken, 1);
+	token->token_id = g_strdup(t->tokenid);
+	info->register_token = token;
+
+	fields = gaim_request_fields_new();
+	group = gaim_request_field_group_new(NULL);
+	gaim_request_fields_add_group(fields, group);
+
+	field = gaim_request_field_string_new("email", _("e-Mail"), "", FALSE);
+	gaim_request_field_string_set_masked(field, FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("password1", _("Password"), "", FALSE);
+	gaim_request_field_string_set_masked(field, TRUE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("password2", _("Password (retype)"), "", FALSE);
+	gaim_request_field_string_set_masked(field, TRUE);
+	gaim_request_field_group_add_field(group, field);
+
+	field = gaim_request_field_string_new("token", _("Enter current token"), "", FALSE);
+	gaim_request_field_string_set_masked(field, FALSE);
+	gaim_request_field_group_add_field(group, field);
+
+	/* original size: 60x24 */
+	field = gaim_request_field_image_new("token_img", _("Current token"), req->body, req->body_size);
+	gaim_request_field_group_add_field(group, field);
+
+	gg_token_free(req);
 
 
-static void agg_set_permit_deny_dummy(GaimConnection *gc)
-{
-	/* It's implemented on client side because GG server doesn't support this */
+	gaim_request_fields(account,
+		_("Register New Gadu-Gadu Account"),
+		_("Register New Gadu-Gadu Account"),
+		_("Please, fill in the following fields"),
+		fields, _("OK"), G_CALLBACK(ggp_callback_register_account_ok),
+		_("Cancel"), NULL, gc);
 }
+/* }}} */
 
-static void agg_permit_deny_dummy(GaimConnection *gc, const char *who)
-{
-	/* It's implemented on client side because GG server doesn't support this */
-}
-
-static void agg_group_buddy (GaimConnection *gc, const char *who,
-			const char *old_group, const char *new_group)
+/*
+ */
+/* static GList *ggp_actions(GaimPlugin *plugin, gpointer context) {{{ */
+static GList *ggp_actions(GaimPlugin *plugin, gpointer context)
 {
-    GaimBuddy *buddy = gaim_find_buddy(gaim_connection_get_account(gc), who);
-    gchar *newdata;
-    /* GG Number */
-    gchar *name = buddy->name;
-    /* GG Pseudo */
-    gchar *show = buddy->alias ? buddy->alias : buddy->name;
-    /* Group Name */
-    const gchar *gname = new_group;
+	GList *m = NULL;
+	GaimPluginAction *act;
+
+	act = gaim_plugin_action_new(_("Find buddies"), ggp_find_buddies);
+	m = g_list_append(m, act);
+
+	m = g_list_append(m, NULL);
+
+	act = gaim_plugin_action_new(_("Change password"), ggp_change_passwd);
+	m = g_list_append(m, act);
+
+	m = g_list_append(m, NULL);
+
+	act = gaim_plugin_action_new(_("Upload buddylist to Server"), ggp_action_buddylist_put);
+	m = g_list_append(m, act);
 
-    newdata = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s;%s%s\r\n",
-		    show, show, show, show, "", gname, name, "", "");
-    agg_save_buddy_list(gc, newdata);
-    g_free(newdata);
-}
+	act = gaim_plugin_action_new(_("Download buddylist from Server"), ggp_action_buddylist_get);
+	m = g_list_append(m, act);
+
+	act = gaim_plugin_action_new(_("Delete buddylist from Server"), ggp_action_buddylist_delete);
+	m = g_list_append(m, act);
+
+	act = gaim_plugin_action_new(_("Save buddylist to file"), ggp_action_buddylist_save);
+	m = g_list_append(m, act);
 
-static void agg_rename_group (GaimConnection *gc, const char *old_name,
-		     GaimGroup *group, GList *moved_buddies)
-{
-    agg_save_buddy_list(gc, NULL);
+	act = gaim_plugin_action_new(_("Load buddylist from file"), ggp_action_buddylist_load);
+	m = g_list_append(m, act);
+
+	return m;
 }
+/* }}} */
 
-static GaimPlugin *my_protocol = NULL;
-
+/* prpl_info setup {{{ */
 static GaimPluginProtocolInfo prpl_info =
 {
-	0,
-	NULL,						/* user_splits */
-	NULL,						/* protocol_options */
+	OPT_PROTO_REGISTER_NOSCREENNAME,
+	NULL,					/* user_splits */
+	NULL,					/* protocol_options */
 	NO_BUDDY_ICONS,			/* icon_spec */
-	agg_list_icon,			/* list_icon */
-	agg_list_emblems,		/* list_emblems */
-	NULL,						/* status_text */
-	NULL,						/* tooltip_text */
-	agg_status_types,		/* status_types */
-	agg_blist_node_menu,		/* blist_node_menu */
-	NULL,						/* chat_info */
-	NULL,						/* chat_info_defaults */
-	agg_login,			/* login */
-	agg_close,			/* close */
-	agg_send_im,			/* send_im */
-	NULL,						/* set_info */
-	NULL,						/* send_typing */
-	agg_get_info,			/* get_info */
-	agg_set_status,			/* set_away */
-	NULL,						/* set_idle */
-	agg_change_passwd,			/* change_passwd */
-	agg_add_buddy,				/* add_buddy */
-	NULL,						/* add_buddies */
-	agg_rem_buddy,				/* remove_buddy */
-	NULL,						/* remove_buddies */
-	agg_permit_deny_dummy,		/* add_permit */
-	agg_permit_deny_dummy,		/* add_deny */
-	agg_permit_deny_dummy,		/* rem_permit */
-	agg_permit_deny_dummy,		/* rem_deny */
-	agg_set_permit_deny_dummy,	/* set_permit_deny */
-	NULL,						/* join_chat */
-	NULL,						/* reject_chat */
-	NULL,						/* get_chat_name */
-	NULL,						/* chat_invite */
-	NULL,						/* chat_leave */
-	NULL,						/* chat_whisper */
-	NULL,						/* chat_send */
-	agg_keepalive,			/* keepalive */
-	NULL,						/* register_user */
-	NULL,						/* get_cb_info */
-	NULL,						/* get_cb_away */
-	NULL,						/* alias_buddy */
-	agg_group_buddy,		/* group_buddy */
-	agg_rename_group,		/* rename_group */
-	agg_buddy_free,			/* buddy_free */
-	NULL,						/* convo_closed */
-	NULL,						/* normalize */
-	NULL,						/* set_buddy_icon */
-	NULL,						/* remove_group */
-	NULL,						/* get_cb_real_name */
-	NULL,						/* set_chat_topic */
-	NULL,						/* find_blist_chat */
-	NULL,						/* roomlist_get_list */
-	NULL,						/* roomlist_cancel */
-	NULL,						/* roomlist_expand_category */
-	NULL,						/* can_receive_file */
-	NULL						/* send_file */
+	ggp_list_icon,			/* list_icon */
+	ggp_list_emblems,		/* list_emblems */
+	ggp_status_text,		/* status_text */
+	ggp_tooltip_text,		/* tooltip_text */
+	ggp_status_types,		/* status_types */
+	ggp_blist_node_menu,	/* blist_node_menu */
+	NULL,					/* chat_info */
+	NULL,					/* chat_info_defaults */
+	ggp_login,				/* login */
+	ggp_close,				/* close */
+	ggp_send_im,			/* send_im */
+	NULL,					/* set_info */
+	NULL,					/* send_typing */
+	ggp_get_info,			/* get_info */
+	ggp_set_status,			/* set_away */
+	NULL,					/* set_idle */
+	NULL,					/* change_passwd */
+	ggp_add_buddy,			/* add_buddy */
+	NULL,					/* add_buddies */
+	ggp_remove_buddy,		/* remove_buddy */
+	NULL,					/* remove_buddies */
+	NULL,					/* add_permit */
+	NULL,					/* add_deny */
+	NULL,					/* rem_permit */
+	NULL,					/* rem_deny */
+	NULL,					/* set_permit_deny */
+	NULL,					/* join_chat */
+	NULL,					/* reject_chat */
+	NULL,					/* get_chat_name */
+	NULL,					/* chat_invite */
+	NULL,					/* chat_leave */
+	NULL,					/* chat_whisper */
+	NULL,					/* chat_send */
+	ggp_keepalive,			/* keepalive */
+	ggp_register_user,		/* register_user */
+	NULL,					/* get_cb_info */
+	NULL,					/* get_cb_away */
+	NULL,					/* alias_buddy */
+	NULL,					/* group_buddy */
+	NULL,					/* rename_group */
+	NULL,					/* buddy_free */
+	NULL,					/* convo_closed */
+	NULL,					/* normalize */
+	NULL,					/* set_buddy_icon */
+	NULL,					/* remove_group */
+	NULL,					/* get_cb_real_name */
+	NULL,					/* set_chat_topic */
+	NULL,					/* find_blist_chat */
+	NULL,					/* roomlist_get_list */
+	NULL,					/* roomlist_cancel */
+	NULL,					/* roomlist_expand_category */
+	NULL,					/* can_receive_file */
+	NULL					/* send_file */
 };
+/* }}} */
 
-static GaimPluginInfo info =
-{
-	GAIM_PLUGIN_MAGIC,
-	GAIM_MAJOR_VERSION,
-	GAIM_MINOR_VERSION,
-	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
-	NULL,                                             /**< ui_requirement */
-	0,                                                /**< flags          */
-	NULL,                                             /**< dependencies   */
-	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+/* GaimPluginInfo setup {{{ */
+static GaimPluginInfo info = {
+	GAIM_PLUGIN_MAGIC,		/* magic */
+	GAIM_MAJOR_VERSION,		/* major_version */
+	GAIM_MINOR_VERSION,		/* minor_version */
+	GAIM_PLUGIN_PROTOCOL,		/* plugin type */
+	NULL,				/* ui_requirement */
+	0,				/* flags */
+	NULL,				/* dependencies */
+	GAIM_PRIORITY_DEFAULT,		/* priority */
+
+	"prpl-gg",			/* id */
+	"Gadu-Gadu",			/* name */
+	VERSION,			/* version */
 
-	"prpl-gg",		                          /**< id             */
-	"Gadu-Gadu",                                      /**< name           */
-	VERSION,                                          /**< version        */
-	                                                  /**  summary        */
-	N_("Gadu-Gadu Protocol Plugin"),
-	                                                  /**  description    */
-	N_("Gadu-Gadu Protocol Plugin"),
-	"Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>",       /**< author         */
-	GAIM_WEBSITE,                                     /**< homepage       */
+	N_("Gadu-Gadu Protocol Plugin"),	/* summary */
+	N_("Polish popular IM"),		/* description */
+	"boler@sourceforge.net",	/* author */
+	GAIM_WEBSITE,			/* homepage */
+
+	NULL,				/* load */
+	NULL,				/* unload */
+	NULL,				/* destroy */
 
-	NULL,                                             /**< load           */
-	NULL,                                             /**< unload         */
-	NULL,                                             /**< destroy        */
+	NULL,				/* ui_info */
+	&prpl_info,			/* extra_info */
+	NULL,				/* prefs_info */
+	ggp_actions			/* actions */
+};
+/* }}} */
 
-	NULL,                                             /**< ui_info        */
-	&prpl_info,                                       /**< extra_info     */
-	NULL,
-	agg_actions
-};
-
-static void
-init_plugin(GaimPlugin *plugin)
+/*
+ */
+/* static void init_plugin(GaimPlugin *plugin) {{{ */
+static void init_plugin(GaimPlugin *plugin)
 {
 	GaimAccountOption *option;
 
-	option = gaim_account_option_string_new(_("Nick"), "nick",
-			"Gadu-Gadu User");
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-			option);
+	option = gaim_account_option_string_new(_("Nickname"), "nick", _("Gadu-Gadu User"));
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
 	my_protocol = plugin;
 }
+/* }}} */
 
-GAIM_INIT_PLUGIN(gg, init_plugin, info)
+GAIM_INIT_PLUGIN(gadu-gadu, init_plugin, info);
+
+/* vim: set ts=4 sts=0 sw=4 noet: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/COPYING	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/common.c	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,822 @@
+/* $Id: common.c 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+#  include <sys/filio.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+
+FILE *gg_debug_file = NULL;
+
+#ifndef GG_DEBUG_DISABLE
+
+/*
+ * gg_debug() // funkcja wewnętrzna
+ *
+ * wyświetla komunikat o danym poziomie, o ile użytkownik sobie tego życzy.
+ *
+ *  - level - poziom wiadomości
+ *  - format... - treść wiadomości (kompatybilna z printf())
+ */
+void gg_debug(int level, const char *format, ...)
+{
+	va_list ap;
+	int old_errno = errno;
+	
+	if (gg_debug_handler) {
+		va_start(ap, format);
+		(*gg_debug_handler)(level, format, ap);
+		va_end(ap);
+
+		goto cleanup;
+	}
+	
+	if ((gg_debug_level & level)) {
+		va_start(ap, format);
+		vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap);
+		va_end(ap);
+	}
+
+cleanup:
+	errno = old_errno;
+}
+
+#endif
+
+/*
+ * gg_vsaprintf() // funkcja pomocnicza
+ *
+ * robi dokładnie to samo, co vsprintf(), tyle że alokuje sobie wcześniej
+ * miejsce na dane. powinno działać na tych maszynach, które mają funkcję
+ * vsnprintf() zgodną z C99, jak i na wcześniejszych.
+ *
+ *  - format - opis wyświetlanego tekstu jak dla printf()
+ *  - ap - lista argumentów dla printf()
+ *
+ * zaalokowany bufor, który należy później zwolnić, lub NULL
+ * jeśli nie udało się wykonać zadania.
+ */
+char *gg_vsaprintf(const char *format, va_list ap)
+{
+	int size = 0;
+	const char *start;
+	char *buf = NULL;
+	
+#ifdef __GG_LIBGADU_HAVE_VA_COPY
+	va_list aq;
+
+	va_copy(aq, ap);
+#else
+#  ifdef __GG_LIBGADU_HAVE___VA_COPY
+	va_list aq;
+
+	__va_copy(aq, ap);
+#  endif
+#endif
+
+	start = format; 
+
+#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF
+	{
+		int res;
+		char *tmp;
+		
+		size = 128;
+		do {
+			size *= 2;
+			if (!(tmp = realloc(buf, size))) {
+				free(buf);
+				return NULL;
+			}
+			buf = tmp;
+			res = vsnprintf(buf, size, format, ap);
+		} while (res == size - 1 || res == -1);
+	}
+#else
+	{
+		char tmp[2];
+		
+		/* libce Solarisa przy buforze NULL zawsze zwracają -1, więc
+		 * musimy podać coś istniejącego jako cel printf()owania. */
+		size = vsnprintf(tmp, sizeof(tmp), format, ap);
+		if (!(buf = malloc(size + 1)))
+			return NULL;
+	}
+#endif
+
+	format = start;
+	
+#ifdef __GG_LIBGADU_HAVE_VA_COPY
+	vsnprintf(buf, size + 1, format, aq);
+	va_end(aq);
+#else
+#  ifdef __GG_LIBGADU_HAVE___VA_COPY
+	vsnprintf(buf, size + 1, format, aq);
+	va_end(aq);
+#  else
+	vsnprintf(buf, size + 1, format, ap);
+#  endif
+#endif
+	
+	return buf;
+}
+
+/*
+ * gg_saprintf() // funkcja pomocnicza
+ *
+ * robi dokładnie to samo, co sprintf(), tyle że alokuje sobie wcześniej
+ * miejsce na dane. powinno działać na tych maszynach, które mają funkcję
+ * vsnprintf() zgodną z C99, jak i na wcześniejszych.
+ *
+ *  - format... - treść taka sama jak w funkcji printf()
+ *
+ * zaalokowany bufor, który należy później zwolnić, lub NULL
+ * jeśli nie udało się wykonać zadania.
+ */
+char *gg_saprintf(const char *format, ...)
+{
+	va_list ap;
+	char *res;
+
+	va_start(ap, format);
+	res = gg_vsaprintf(format, ap);
+	va_end(ap);
+
+	return res;
+}
+
+/*
+ * gg_get_line() // funkcja pomocnicza
+ * 
+ * podaje kolejną linię z bufora tekstowego. niszczy go bezpowrotnie, dzieląc
+ * na kolejne stringi. zdarza się, nie ma potrzeby pisania funkcji dublującej
+ * bufor żeby tylko mieć nieruszone dane wejściowe, skoro i tak nie będą nam
+ * poźniej potrzebne. obcina `\r\n'.
+ * 
+ *  - ptr - wskaźnik do zmiennej, która przechowuje aktualną pozycję
+ *    w przemiatanym buforze
+ * 
+ * wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec bufora.
+ */
+char *gg_get_line(char **ptr)
+{
+	char *foo, *res;
+
+	if (!ptr || !*ptr || !strcmp(*ptr, ""))
+		return NULL;
+
+	res = *ptr;
+
+	if (!(foo = strchr(*ptr, '\n')))
+		*ptr += strlen(*ptr);
+	else {
+		*ptr = foo + 1;
+		*foo = 0;
+		if (strlen(res) > 1 && res[strlen(res) - 1] == '\r')
+			res[strlen(res) - 1] = 0;
+	}
+
+	return res;
+}
+
+/*
+ * gg_connect() // funkcja pomocnicza
+ *
+ * łączy się z serwerem. pierwszy argument jest typu (void *), żeby nie
+ * musieć niczego inkludować w libgadu.h i nie psuć jakiś głupich zależności
+ * na dziwnych systemach.
+ *
+ *  - addr - adres serwera (struct in_addr *)
+ *  - port - port serwera
+ *  - async - asynchroniczne połączenie
+ *
+ * deskryptor gniazda lub -1 w przypadku błędu (kod błędu w zmiennej errno).
+ */
+int gg_connect(void *addr, int port, int async)
+{
+	int sock, one = 1, errno2;
+	struct sockaddr_in sin;
+	struct in_addr *a = addr;
+	struct sockaddr_in myaddr;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
+	
+	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno));
+		return -1;
+	}
+
+	memset(&myaddr, 0, sizeof(myaddr));
+	myaddr.sin_family = AF_INET;
+
+	myaddr.sin_addr.s_addr = gg_local_ip;
+
+	if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno));
+		return -1;
+	}
+
+#ifdef ASSIGN_SOCKETS_TO_THREADS
+	gg_win32_thread_socket(0, sock);
+#endif
+
+	if (async) {
+#ifdef FIONBIO
+		if (ioctl(sock, FIONBIO, &one) == -1) {
+#else
+		if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+			gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno));
+			errno2 = errno;
+			close(sock);
+			errno = errno2;
+			return -1;
+		}
+	}
+
+	sin.sin_port = htons(port);
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = a->s_addr;
+	
+	if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
+		if (errno && (!async || errno != EINPROGRESS)) {
+			gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno));
+			errno2 = errno;
+			close(sock);
+			errno = errno2;
+			return -1;
+		}
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
+	}
+	
+	return sock;
+}
+
+/*
+ * gg_read_line() // funkcja pomocnicza
+ *
+ * czyta jedną linię tekstu z gniazda.
+ *
+ *  - sock - deskryptor gniazda
+ *  - buf - wskaźnik do bufora
+ *  - length - długość bufora
+ *
+ * jeśli trafi na błąd odczytu lub podano nieprawidłowe parametry, zwraca NULL.
+ * inaczej zwraca buf.
+ */
+char *gg_read_line(int sock, char *buf, int length)
+{
+	int ret;
+
+	if (!buf || length < 0)
+		return NULL;
+
+	for (; length > 1; buf++, length--) {
+		do {
+			if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) {
+				gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno));
+				*buf = 0;
+				return NULL;
+			} else if (ret == 0) {
+				gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n");
+				*buf = 0;
+				return NULL;
+			}
+		} while (ret == -1 && errno == EINTR);
+
+		if (*buf == '\n') {
+			buf++;
+			break;
+		}
+	}
+
+	*buf = 0;
+	return buf;
+}
+
+/*
+ * gg_chomp() // funkcja pomocnicza
+ *
+ * ucina "\r\n" lub "\n" z końca linii.
+ *
+ *  - line - linia do przycięcia
+ */
+void gg_chomp(char *line)
+{
+	int len;
+	
+	if (!line)
+		return;
+
+	len = strlen(line);
+	
+	if (len > 0 && line[len - 1] == '\n')
+		line[--len] = 0;
+	if (len > 0 && line[len - 1] == '\r')
+		line[--len] = 0;
+}
+
+/*
+ * gg_urlencode() // funkcja wewnętrzna
+ *
+ * zamienia podany tekst na ciąg znaków do formularza http. przydaje się
+ * przy różnych usługach katalogu publicznego.
+ *
+ *  - str - ciąg znaków do zakodowania
+ *
+ * zaalokowany bufor, który należy później zwolnić albo NULL
+ * w przypadku błędu.
+ */
+char *gg_urlencode(const char *str)
+{
+	char *q, *buf, hex[] = "0123456789abcdef";
+	const char *p;
+	unsigned int size = 0;
+
+	if (!str)
+		str = "";
+
+	for (p = str; *p; p++, size++) {
+		if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') || (*p == '.') || (*p == '-'))
+			size += 2;
+	}
+
+	if (!(buf = malloc(size + 1)))
+		return NULL;
+
+	for (p = str, q = buf; *p; p++, q++) {
+		if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || (*p == '@') || (*p == '.') || (*p == '-'))
+			*q = *p;
+		else {
+			if (*p == ' ')
+				*q = '+';
+			else {
+				*q++ = '%';
+				*q++ = hex[*p >> 4 & 15];
+				*q = hex[*p & 15];
+			}
+		}
+	}
+
+	*q = 0;
+
+	return buf;
+}
+
+/*
+ * gg_http_hash() // funkcja wewnętrzna
+ *
+ * funkcja licząca hash dla adresu e-mail, hasła i paru innych.
+ *
+ *  - format... - format kolejnych parametrów ('s' jeśli dany parametr jest
+ *                ciągiem znaków lub 'u' jeśli numerem GG)
+ *
+ * hash wykorzystywany przy rejestracji i wszelkich manipulacjach własnego
+ * wpisu w katalogu publicznym.
+ */
+int gg_http_hash(const char *format, ...)
+{
+	unsigned int a, c, i, j;
+	va_list ap;
+	int b = -1;
+
+	va_start(ap, format);
+
+	for (j = 0; j < strlen(format); j++) {
+		char *arg, buf[16];
+
+		if (format[j] == 'u') {
+			snprintf(buf, sizeof(buf), "%d", va_arg(ap, uin_t));
+			arg = buf;
+		} else {
+			if (!(arg = va_arg(ap, char*)))
+				arg = "";
+		}	
+
+		i = 0;
+		while ((c = (unsigned char) arg[i++]) != 0) {
+			a = (c ^ b) + (c << 8);
+			b = (a >> 24) | (a << 8);
+		}
+	}
+
+	va_end(ap);
+
+	return (b < 0 ? -b : b);
+}
+
+/*
+ * gg_gethostbyname() // funkcja pomocnicza
+ *
+ * odpowiednik gethostbyname() troszczący się o współbieżność, gdy mamy do
+ * dyspozycji funkcję gethostbyname_r().
+ *
+ *  - hostname - nazwa serwera
+ *
+ * zwraca wskaźnik na strukturę in_addr, którą należy zwolnić.
+ */
+struct in_addr *gg_gethostbyname(const char *hostname)
+{
+	struct in_addr *addr = NULL;
+
+#ifdef HAVE_GETHOSTBYNAME_R
+	char *tmpbuf = NULL, *buf = NULL;
+	struct hostent *hp = NULL, *hp2 = NULL;
+	int h_errnop, ret;
+	size_t buflen = 1024;
+	int new_errno;
+	
+	new_errno = ENOMEM;
+	
+	if (!(addr = malloc(sizeof(struct in_addr))))
+		goto cleanup;
+	
+	if (!(hp = calloc(1, sizeof(*hp))))
+		goto cleanup;
+
+	if (!(buf = malloc(buflen)))
+		goto cleanup;
+
+	tmpbuf = buf;
+	
+	while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) {
+		buflen *= 2;
+		
+		if (!(tmpbuf = realloc(buf, buflen)))
+			break;
+		
+		buf = tmpbuf;
+	}
+	
+	if (ret)
+		new_errno = h_errnop;
+
+	if (ret || !hp2 || !tmpbuf)
+		goto cleanup;
+	
+	memcpy(addr, hp->h_addr, sizeof(struct in_addr));
+	
+	free(buf);
+	free(hp);
+	
+	return addr;
+	
+cleanup:
+	errno = new_errno;
+	
+	if (addr)
+		free(addr);
+	if (hp)
+		free(hp);
+	if (buf)
+		free(buf);
+	
+	return NULL;
+#else
+	struct hostent *hp;
+
+	if (!(addr = malloc(sizeof(struct in_addr)))) {
+		goto cleanup;
+	}
+
+	if (!(hp = gethostbyname(hostname)))
+		goto cleanup;
+
+	memcpy(addr, hp->h_addr, sizeof(struct in_addr));
+
+	return addr;
+	
+cleanup:
+	if (addr)
+		free(addr);
+
+	return NULL;
+#endif
+}
+
+#ifdef ASSIGN_SOCKETS_TO_THREADS
+
+typedef struct gg_win32_thread {
+	int id;
+	int socket;
+	struct gg_win32_thread *next;
+} gg_win32_thread;
+
+struct gg_win32_thread *gg_win32_threads = 0;
+
+/*
+ * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32
+ *
+ * zwraca deskryptor gniazda, które było ostatnio tworzone dla wątku
+ * o podanym identyfikatorze.
+ *
+ * jeśli na win32 przy połączeniach synchronicznych zapamiętamy w jakim
+ * wątku uruchomiliśmy funkcję, która się z czymkolwiek łączy, to z osobnego
+ * wątku możemy anulować połączenie poprzez gg_win32_thread_socket(watek, -1);
+ * 
+ * - thread_id - id wątku. jeśli jest równe 0, brany jest aktualny wątek,
+ *               jeśli równe -1, usuwa wpis o podanym sockecie.
+ * - socket - deskryptor gniazda. jeśli równe 0, zwraca deskryptor gniazda
+ *            dla podanego wątku, jeśli równe -1, usuwa wpis, jeśli coś
+ *            innego, ustawia dla podanego wątku dany numer deskryptora.
+ *
+ * jeśli socket jest równe 0, zwraca deskryptor gniazda dla podanego wątku.
+ */
+int gg_win32_thread_socket(int thread_id, int socket)
+{
+	char close = (thread_id == -1) || socket == -1;
+	gg_win32_thread *wsk = gg_win32_threads;
+	gg_win32_thread **p_wsk = &gg_win32_threads;
+
+	if (!thread_id)
+		thread_id = GetCurrentThreadId();
+	
+	while (wsk) {
+		if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) {
+			if (close) {
+				/* socket zostaje usuniety */
+				closesocket(wsk->socket);
+				*p_wsk = wsk->next;
+				free(wsk);
+				return 1;
+			} else if (!socket) {
+				/* socket zostaje zwrocony */
+				return wsk->socket;
+			} else {
+				/* socket zostaje ustawiony */
+				wsk->socket = socket;
+				return socket;
+			}
+		}
+		p_wsk = &(wsk->next);
+		wsk = wsk->next;
+	}
+
+	if (close && socket != -1)
+		closesocket(socket);
+	if (close || !socket)
+		return 0;
+	
+	/* Dodaje nowy element */
+	wsk = malloc(sizeof(gg_win32_thread));
+	wsk->id = thread_id;
+	wsk->socket = socket;
+	wsk->next = 0;
+	*p_wsk = wsk;
+
+	return socket;
+}
+
+#endif /* ASSIGN_SOCKETS_TO_THREADS */
+
+static char gg_base64_charset[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * gg_base64_encode()
+ *
+ * zapisuje ciąg znaków w base64.
+ *
+ *  - buf - ciąg znaków.
+ *
+ * zaalokowany bufor.
+ */
+char *gg_base64_encode(const char *buf)
+{
+	char *out, *res;
+	unsigned int i = 0, j = 0, k = 0, len = strlen(buf);
+	
+	res = out = malloc((len / 3 + 1) * 4 + 2);
+
+	if (!res)
+		return NULL;
+	
+	while (j <= len) {
+		switch (i % 4) {
+			case 0:
+				k = (buf[j] & 252) >> 2;
+				break;
+			case 1:
+				if (j < len)
+					k = ((buf[j] & 3) << 4) | ((buf[j + 1] & 240) >> 4);
+				else
+					k = (buf[j] & 3) << 4;
+
+				j++;
+				break;
+			case 2:
+				if (j < len)
+					k = ((buf[j] & 15) << 2) | ((buf[j + 1] & 192) >> 6);
+				else
+					k = (buf[j] & 15) << 2;
+
+				j++;
+				break;
+			case 3:
+				k = buf[j++] & 63;
+				break;
+		}
+		*out++ = gg_base64_charset[k];
+		i++;
+	}
+
+	if (i % 4)
+		for (j = 0; j < 4 - (i % 4); j++, out++)
+			*out = '=';
+	
+	*out = 0;
+	
+	return res;
+}
+
+/*
+ * gg_base64_decode()
+ *
+ * dekoduje ciąg znaków z base64.
+ *
+ *  - buf - ciąg znaków.
+ *
+ * zaalokowany bufor.
+ */
+char *gg_base64_decode(const char *buf)
+{
+	char *res, *save, *foo, val;
+	const char *end;
+	unsigned int index = 0;
+
+	if (!buf)
+		return NULL;
+	
+	save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
+
+	if (!save)
+		return NULL;
+
+	end = buf + strlen(buf);
+
+	while (*buf && buf < end) {
+		if (*buf == '\r' || *buf == '\n') {
+			buf++;
+			continue;
+		}
+		if (!(foo = strchr(gg_base64_charset, *buf)))
+			foo = gg_base64_charset;
+		val = (int)(foo - gg_base64_charset);
+		buf++;
+		switch (index) {
+			case 0:
+				*res |= val << 2;
+				break;
+			case 1:
+				*res++ |= val >> 4;
+				*res |= val << 4;
+				break;
+			case 2:
+				*res++ |= val >> 2;
+				*res |= val << 6;
+				break;
+			case 3:
+				*res++ |= val;
+				break;
+		}
+		index++;
+		index %= 4;
+	}
+	*res = 0;
+	
+	return save;
+}
+
+/*
+ * gg_proxy_auth() // funkcja wewnętrzna
+ *
+ * tworzy nagłówek autoryzacji dla proxy.
+ * 
+ * zaalokowany tekst lub NULL, jeśli proxy nie jest włączone lub nie wymaga
+ * autoryzacji.
+ */
+char *gg_proxy_auth()
+{
+	char *tmp, *enc, *out;
+	unsigned int tmp_size;
+	
+	if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password)
+		return NULL;
+
+	if (!(tmp = malloc((tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2))))
+		return NULL;
+
+	snprintf(tmp, tmp_size, "%s:%s", gg_proxy_username, gg_proxy_password);
+
+	if (!(enc = gg_base64_encode(tmp))) {
+		free(tmp);
+		return NULL;
+	}
+	
+	free(tmp);
+
+	if (!(out = malloc(strlen(enc) + 40))) {
+		free(enc);
+		return NULL;
+	}
+	
+	snprintf(out, strlen(enc) + 40,  "Proxy-Authorization: Basic %s\r\n", enc);
+
+	free(enc);
+
+	return out;
+}
+
+static uint32_t gg_crc32_table[256];
+static int gg_crc32_initialized = 0;
+
+/*
+ * gg_crc32_make_table()  // funkcja wewnętrzna
+ */
+static void gg_crc32_make_table()
+{
+	uint32_t h = 1;
+	unsigned int i, j;
+
+	memset(gg_crc32_table, 0, sizeof(gg_crc32_table));
+
+	for (i = 128; i; i >>= 1) {
+		h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
+
+		for (j = 0; j < 256; j += 2 * i)
+			gg_crc32_table[i + j] = gg_crc32_table[j] ^ h;
+	}
+
+	gg_crc32_initialized = 1;
+}
+
+/*
+ * gg_crc32()
+ *
+ * wyznacza sumę kontrolną CRC32 danego bloku danych.
+ *
+ *  - crc - suma kontrola poprzedniego bloku danych lub 0 jeśli pierwszy
+ *  - buf - bufor danych
+ *  - size - ilość danych
+ *
+ * suma kontrolna CRC32.
+ */
+uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
+{
+	if (!gg_crc32_initialized)
+		gg_crc32_make_table();
+
+	if (!buf || len < 0)
+		return crc;
+
+	crc ^= 0xffffffffL;
+
+	while (len--)
+		crc = (crc >> 8) ^ gg_crc32_table[(crc ^ *buf++) & 0xff];
+
+	return crc ^ 0xffffffffL;
+}
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/compat.h	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,29 @@
+/* $Id: compat.h 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifndef __COMPAT_H
+#define __COMPAT_H
+
+#ifdef sun
+#  define INADDR_NONE   ((in_addr_t) 0xffffffff)
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/dcc.c	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,1296 @@
+/* $Id: dcc.c 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+#  include <sys/filio.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "libgadu.h"
+
+#ifndef GG_DEBUG_DISABLE
+/*
+ * gg_dcc_debug_data() // funkcja wewnętrzna
+ *
+ * wyświetla zrzut pakietu w hexie.
+ * 
+ *  - prefix - prefiks zrzutu pakietu
+ *  - fd - deskryptor gniazda
+ *  - buf - bufor z danymi
+ *  - size - rozmiar danych
+ */
+static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size)
+{
+	unsigned int i;
+	
+	gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size);
+	
+	for (i = 0; i < size; i++)
+		gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]);
+	
+	gg_debug(GG_DEBUG_MISC, "\n");
+}
+#else
+#define gg_dcc_debug_data(a,b,c,d) do { } while (0)
+#endif
+
+/*
+ * gg_dcc_request()
+ *
+ * wysyła informację o tym, że dany klient powinien się z nami połączyć.
+ * wykorzystywane, kiedy druga strona, której chcemy coś wysłać jest za
+ * maskaradą.
+ *
+ *  - sess - struktura opisująca sesję GG
+ *  - uin - numerek odbiorcy
+ *
+ * patrz gg_send_message_ctcp().
+ */
+int gg_dcc_request(struct gg_session *sess, uin_t uin)
+{
+	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, "\002", 1);
+}
+
+/* 
+ * gg_dcc_fill_filetime()  // funkcja wewnętrzna
+ *
+ * zamienia czas w postaci unixowej na windowsowy.
+ *
+ *  - unix - czas w postaci unixowej
+ *  - filetime - czas w postaci windowsowej
+ */
+void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
+{
+#ifdef __GG_LIBGADU_HAVE_LONG_LONG
+	unsigned long long tmp;
+
+	tmp = ut;
+	tmp += 11644473600LL;
+	tmp *= 10000000LL;
+
+#ifndef __GG_LIBGADU_BIGENDIAN
+	ft[0] = (uint32_t) tmp;
+	ft[1] = (uint32_t) (tmp >> 32);
+#else
+	ft[0] = gg_fix32((uint32_t) (tmp >> 32));
+	ft[1] = gg_fix32((uint32_t) tmp);
+#endif
+
+#endif
+}
+
+/*
+ * gg_dcc_fill_file_info()
+ *
+ * wypełnia pola struct gg_dcc niezbędne do wysłania pliku.
+ *
+ *  - d - struktura opisująca połączenie DCC
+ *  - filename - nazwa pliku
+ *
+ * 0, -1.
+ */
+int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename)
+{
+	return gg_dcc_fill_file_info2(d, filename, filename);
+}
+
+/*
+ * gg_dcc_fill_file_info2()
+ *
+ * wypełnia pola struct gg_dcc niezbędne do wysłania pliku.
+ *
+ *  - d - struktura opisująca połączenie DCC
+ *  - filename - nazwa pliku
+ *  - local_filename - nazwa na lokalnym systemie plików
+ *
+ * 0, -1.
+ */
+int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename)
+{
+	struct stat st;
+	const char *name, *ext, *p;
+	unsigned char *q;
+	int i, j;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename);
+
+	if (!d || d->type != GG_SESSION_DCC_SEND) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (stat(local_filename, &st) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno));
+		return -1;
+	}
+
+	if ((st.st_mode & S_IFDIR)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno));
+		return -1;
+	}
+
+	memset(&d->file_info, 0, sizeof(d->file_info));
+
+	if (!(st.st_mode & S_IWUSR))
+		d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY);
+
+	gg_dcc_fill_filetime(st.st_atime, d->file_info.atime);
+	gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime);
+	gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime);
+
+	d->file_info.size = gg_fix32(st.st_size);
+	d->file_info.mode = gg_fix32(0x20);	/* FILE_ATTRIBUTE_ARCHIVE */
+
+	if (!(name = strrchr(filename, '/')))
+		name = filename;
+	else
+		name++;
+
+	if (!(ext = strrchr(name, '.')))
+		ext = name + strlen(name);
+
+	for (i = 0, p = name; i < 8 && p < ext; i++, p++)
+		d->file_info.short_filename[i] = toupper(name[i]);
+
+	if (i == 8 && p < ext) {
+		d->file_info.short_filename[6] = '~';
+		d->file_info.short_filename[7] = '1';
+	}
+
+	if (strlen(ext) > 0) {
+		for (j = 0; *ext && j < 4; j++, p++)
+			d->file_info.short_filename[i + j] = toupper(ext[j]);
+	}
+
+	for (q = d->file_info.short_filename; *q; q++) {
+		if (*q == 185) {
+			*q = 165;
+		} else if (*q == 230) {
+			*q = 198;
+		} else if (*q == 234) {
+			*q = 202;
+		} else if (*q == 179) {
+			*q = 163;
+		} else if (*q == 241) {
+			*q = 209;
+		} else if (*q == 243) {
+			*q = 211;
+		} else if (*q == 156) {
+			*q = 140;
+		} else if (*q == 159) {
+			*q = 143;
+		} else if (*q == 191) {
+			*q = 175;
+		}
+	}
+	
+	gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename);
+	strncpy(d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
+
+	return 0;
+}
+
+/*
+ * gg_dcc_transfer() // funkcja wewnętrzna
+ * 
+ * inicjuje proces wymiany pliku z danym klientem.
+ *
+ *  - ip - adres ip odbiorcy
+ *  - port - port odbiorcy
+ *  - my_uin - własny numer
+ *  - peer_uin - numer obiorcy
+ *  - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET)
+ *
+ * zaalokowana struct gg_dcc lub NULL jeśli wystąpił błąd.
+ */
+static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
+{
+	struct gg_dcc *d = NULL;
+	struct in_addr addr;
+
+	addr.s_addr = ip;
+	
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET");
+	
+	if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!(d = (void*) calloc(1, sizeof(*d)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n");
+		return NULL;
+	}
+
+	d->check = GG_CHECK_WRITE;
+	d->state = GG_STATE_CONNECTING;
+	d->type = type;
+	d->timeout = GG_DEFAULT_TIMEOUT;
+	d->file_fd = -1;
+	d->active = 1;
+	d->fd = -1;
+	d->uin = my_uin;
+	d->peer_uin = peer_uin;
+
+	if ((d->fd = gg_connect(&addr, port, 1)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n");
+		free(d);
+		return NULL;
+	}
+
+	return d;
+}
+
+/*
+ * gg_dcc_get_file()
+ * 
+ * inicjuje proces odbierania pliku od danego klienta, gdy ten wysłał do
+ * nas żądanie połączenia.
+ *
+ *  - ip - adres ip odbiorcy
+ *  - port - port odbiorcy
+ *  - my_uin - własny numer
+ *  - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL jeśli wystąpił błąd.
+ */
+struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_dcc_get_file() handing over to gg_dcc_transfer()\n");
+
+	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET);
+}
+
+/*
+ * gg_dcc_send_file()
+ * 
+ * inicjuje proces wysyłania pliku do danego klienta.
+ *
+ *  - ip - adres ip odbiorcy
+ *  - port - port odbiorcy
+ *  - my_uin - własny numer
+ *  - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL jeśli wystąpił błąd.
+ */
+struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n");
+
+	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND);
+}
+
+/*
+ * gg_dcc_voice_chat()
+ * 
+ * próbuje nawiązać połączenie głosowe.
+ *
+ *  - ip - adres ip odbiorcy
+ *  - port - port odbiorcy
+ *  - my_uin - własny numer
+ *  - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL jeśli wystąpił błąd.
+ */
+struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n");
+
+	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE);
+}
+
+/*
+ * gg_dcc_set_type()
+ *
+ * po zdarzeniu GG_EVENT_DCC_CALLBACK należy ustawić typ połączenia za
+ * pomocą tej funkcji.
+ *
+ *  - d - struktura opisująca połączenie
+ *  - type - typ połączenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE)
+ */
+void gg_dcc_set_type(struct gg_dcc *d, int type)
+{
+	d->type = type;
+	d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST;
+}
+	
+/*
+ * gg_dcc_callback() // funkcja wewnętrzna
+ *
+ * wywoływana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza
+ * rezultat w struct gg_dcc->event.
+ *
+ *  - d - structura opisująca połączenie
+ *
+ * 0, -1.
+ */
+static int gg_dcc_callback(struct gg_dcc *d)
+{
+	struct gg_event *e = gg_dcc_watch_fd(d);
+
+	d->event = e;
+
+	return (e != NULL) ? 0 : -1;
+}
+
+/*
+ * gg_dcc_socket_create()
+ *
+ * tworzy gniazdo dla bezpośredniej komunikacji między klientami.
+ *
+ *  - uin - własny numer
+ *  - port - preferowany port, jeśli równy 0 lub -1, próbuje domyślnego
+ *
+ * zaalokowana struct gg_dcc, którą poźniej należy zwolnić funkcją
+ * gg_dcc_free(), albo NULL jeśli wystąpił błąd.
+ */
+struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
+{
+	struct gg_dcc *c;
+	struct sockaddr_in sin;
+	int sock, bound = 0, errno2;
+	
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port);
+	
+	if (!uin) {
+		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno));
+		return NULL;
+	}
+
+	if (!port)
+		port = GG_DEFAULT_DCC_PORT;
+	
+	while (!bound) {
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = INADDR_ANY;
+		sin.sin_port = htons(port);
+	
+		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port);
+		if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin)))
+			bound = 1;
+		else {
+			if (++port == 65535) {
+				gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n");
+				close(sock);
+				return NULL;
+			}
+		}
+	}
+
+	if (listen(sock, 10)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno));
+		errno2 = errno;
+		close(sock);
+		errno = errno2;
+		return NULL;
+	}
+	
+	gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port);
+
+	if (!(c = malloc(sizeof(*c)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n");
+		close(sock);
+		return NULL;
+	}
+	memset(c, 0, sizeof(*c));
+
+	c->port = c->id = port;
+	c->fd = sock;
+	c->type = GG_SESSION_DCC_SOCKET;
+	c->uin = uin;
+	c->timeout = -1;
+	c->state = GG_STATE_LISTENING;
+	c->check = GG_CHECK_READ;
+	c->callback = gg_dcc_callback;
+	c->destroy = gg_dcc_free;
+	
+	return c;
+}
+
+/*
+ * gg_dcc_voice_send()
+ *
+ * wysyła ramkę danych dla rozmowy głosowej.
+ *
+ *  - d - struktura opisująca połączenie dcc
+ *  - buf - bufor z danymi
+ *  - length - rozmiar ramki
+ *
+ * 0, -1.
+ */
+int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
+{
+	struct packet_s {
+		uint8_t type;
+		uint32_t length;
+	} GG_PACKED;
+	struct packet_s packet;
+
+	gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length);
+	if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	packet.type = 0x03; /* XXX */
+	packet.length = gg_fix32(length);
+
+	if (write(d->fd, &packet, sizeof(packet)) < (signed)sizeof(packet)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n");
+		return -1;
+	}
+	gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet));
+
+	if (write(d->fd, buf, length) < length) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n");
+		return -1;
+	}
+	gg_dcc_debug_data("write", d->fd, buf, length);
+
+	return 0;
+}
+
+#define gg_read(fd, buf, size) \
+{ \
+	int tmp = read(fd, buf, size); \
+	\
+	if (tmp < (int) size) { \
+		if (tmp == -1) { \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); \
+		} else if (tmp == 0) { \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); \
+		} else { \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (%d bytes, %d needed)\n", tmp, size); \
+		} \
+		e->type = GG_EVENT_DCC_ERROR; \
+		e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
+		return e; \
+	} \
+	gg_dcc_debug_data("read", fd, buf, size); \
+} 
+
+#define gg_write(fd, buf, size) \
+{ \
+	int tmp; \
+	gg_dcc_debug_data("write", fd, buf, size); \
+	tmp = write(fd, buf, size); \
+	if (tmp < (int) size) { \
+		if (tmp == -1) { \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); \
+		} else { \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d needed, %d done)\n", size, tmp); \
+		} \
+		e->type = GG_EVENT_DCC_ERROR; \
+		e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
+		return e; \
+	} \
+}
+
+/*
+ * gg_dcc_watch_fd()
+ *
+ * funkcja, którą należy wywołać, gdy coś się zmieni na gg_dcc->fd.
+ *
+ *  - h - struktura zwrócona przez gg_create_dcc_socket()
+ *
+ * zaalokowana struct gg_event lub NULL, jeśli zabrakło pamięci na nią.
+ */
+struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
+{
+	struct gg_event *e;
+	int foo;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h);
+	
+	if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!(e = (void*) calloc(1, sizeof(*e)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n");
+		return NULL;
+	}
+
+	e->type = GG_EVENT_NONE;
+
+	if (h->type == GG_SESSION_DCC_SOCKET) {
+		struct sockaddr_in sin;
+		struct gg_dcc *c;
+		int fd, sin_len = sizeof(sin), one = 1;
+		
+		if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno));
+			return e;
+		}
+
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+
+#ifdef FIONBIO
+		if (ioctl(fd, FIONBIO, &one) == -1) {
+#else
+		if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno));
+			close(fd);
+			e->type = GG_EVENT_DCC_ERROR;
+			e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+			return e;
+		}
+
+		if (!(c = (void*) calloc(1, sizeof(*c)))) {
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n");
+
+			free(e);
+			close(fd);
+			return NULL;
+		}
+
+		c->fd = fd;
+		c->check = GG_CHECK_READ;
+		c->state = GG_STATE_READING_UIN_1;
+		c->type = GG_SESSION_DCC;
+		c->timeout = GG_DEFAULT_TIMEOUT;
+		c->file_fd = -1;
+		c->remote_addr = sin.sin_addr.s_addr;
+		c->remote_port = ntohs(sin.sin_port);
+		
+		e->type = GG_EVENT_DCC_NEW;
+		e->event.dcc_new = c;
+
+		return e;
+	} else {
+		struct gg_dcc_tiny_packet tiny;
+		struct gg_dcc_small_packet small;
+		struct gg_dcc_big_packet big;
+		int size, tmp, res, res_size = sizeof(res);
+		unsigned int utmp;
+		char buf[1024], ack[] = "UDAG";
+
+		struct gg_dcc_file_info_packet {
+			struct gg_dcc_big_packet big;
+			struct gg_file_info file_info;
+		} GG_PACKED;
+		struct gg_dcc_file_info_packet file_info_packet;
+
+		switch (h->state) {
+			case GG_STATE_READING_UIN_1:
+			case GG_STATE_READING_UIN_2:
+			{
+				uin_t uin;
+
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
+				
+				gg_read(h->fd, &uin, sizeof(uin));
+
+				if (h->state == GG_STATE_READING_UIN_1) {
+					h->state = GG_STATE_READING_UIN_2;
+					h->check = GG_CHECK_READ;
+					h->timeout = GG_DEFAULT_TIMEOUT;
+					h->peer_uin = gg_fix32(uin);
+				} else {
+					h->state = GG_STATE_SENDING_ACK;
+					h->check = GG_CHECK_WRITE;
+					h->timeout = GG_DEFAULT_TIMEOUT;
+					h->uin = gg_fix32(uin);
+					e->type = GG_EVENT_DCC_CLIENT_ACCEPT;
+				}
+
+				return e;
+			}
+
+			case GG_STATE_SENDING_ACK:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
+
+				gg_write(h->fd, ack, 4);
+
+				h->state = GG_STATE_READING_TYPE;
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+
+				return e;
+
+			case GG_STATE_READING_TYPE:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n");
+				
+				gg_read(h->fd, &small, sizeof(small));
+
+				small.type = gg_fix32(small.type);
+
+				switch (small.type) {
+					case 0x0003:	/* XXX */
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n");
+						h->type = GG_SESSION_DCC_SEND;
+						h->state = GG_STATE_SENDING_FILE_INFO;
+						h->check = GG_CHECK_WRITE;
+						h->timeout = GG_DEFAULT_TIMEOUT;
+
+						e->type = GG_EVENT_DCC_CALLBACK;
+			
+						break;
+
+					case 0x0002:	/* XXX */
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n");
+						h->type = GG_SESSION_DCC_GET;
+						h->state = GG_STATE_READING_REQUEST;
+						h->check = GG_CHECK_READ;
+						h->timeout = GG_DEFAULT_TIMEOUT;
+						h->incoming = 1;
+
+						break;
+
+					default:
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %ld\n", small.type, h->peer_uin);
+						e->type = GG_EVENT_DCC_ERROR;
+						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+				}
+
+				return e;
+
+			case GG_STATE_READING_REQUEST:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n");
+				
+				gg_read(h->fd, &small, sizeof(small));
+
+				small.type = gg_fix32(small.type);
+
+				switch (small.type) {
+					case 0x0001:	/* XXX */
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n");
+						h->state = GG_STATE_READING_FILE_INFO;
+						h->check = GG_CHECK_READ;
+						h->timeout = GG_DEFAULT_TIMEOUT;
+						break;
+						
+					case 0x0003:	/* XXX */
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n");
+						h->state = GG_STATE_SENDING_VOICE_ACK;
+						h->check = GG_CHECK_WRITE;
+						h->timeout = GG_DCC_TIMEOUT_VOICE_ACK;
+						h->type = GG_SESSION_DCC_VOICE;
+						e->type = GG_EVENT_DCC_NEED_VOICE_ACK;
+
+						break;
+						
+					default:
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin);
+						e->type = GG_EVENT_DCC_ERROR;
+						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+				}
+		 	
+				return e;
+
+			case GG_STATE_READING_FILE_INFO:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n");
+				
+				gg_read(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+				memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info));
+		
+				h->file_info.mode = gg_fix32(h->file_info.mode);
+				h->file_info.size = gg_fix32(h->file_info.size);
+
+				h->state = GG_STATE_SENDING_FILE_ACK;
+				h->check = GG_CHECK_WRITE;
+				h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
+
+				e->type = GG_EVENT_DCC_NEED_FILE_ACK;
+				
+				return e;
+
+			case GG_STATE_SENDING_FILE_ACK:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n");
+				
+				big.type = gg_fix32(0x0006);	/* XXX */
+				big.dunno1 = gg_fix32(h->offset);
+				big.dunno2 = 0;
+
+				gg_write(h->fd, &big, sizeof(big));
+
+				h->state = GG_STATE_READING_FILE_HEADER;
+				h->chunk_size = sizeof(big);
+				h->chunk_offset = 0;
+				if (!(h->chunk_buf = malloc(sizeof(big)))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
+					free(e);
+					return NULL;
+				}
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+
+				return e;
+				
+			case GG_STATE_SENDING_VOICE_ACK:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n");
+				
+				tiny.type = 0x01;	/* XXX */
+
+				gg_write(h->fd, &tiny, sizeof(tiny));
+
+				h->state = GG_STATE_READING_VOICE_HEADER;
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+
+				h->offset = 0;
+				
+				return e;
+				
+			case GG_STATE_READING_FILE_HEADER:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n");
+				
+				tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+
+				if (tmp == -1) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno));
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+					return e;
+				}
+
+				gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+				
+				h->chunk_offset += tmp;
+
+				if (h->chunk_offset < h->chunk_size)
+					return e;
+
+				memcpy(&big, h->chunk_buf, sizeof(big));
+				free(h->chunk_buf);
+				h->chunk_buf = NULL;
+
+				big.type = gg_fix32(big.type);
+				h->chunk_size = gg_fix32(big.dunno1);
+				h->chunk_offset = 0;
+
+				if (big.type == 0x0005)	{ /* XXX */
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n");
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_REFUSED;
+					return e;
+				}
+
+				if (h->chunk_size == 0) { 
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n");
+					e->type = GG_EVENT_DCC_DONE;
+					return e;
+				}
+
+				h->state = GG_STATE_GETTING_FILE;
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+				h->established = 1;
+			 	
+				return e;
+
+			case GG_STATE_READING_VOICE_HEADER:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n");
+				
+				gg_read(h->fd, &tiny, sizeof(tiny));
+
+				switch (tiny.type) {
+					case 0x03:	/* XXX */
+						h->state = GG_STATE_READING_VOICE_SIZE;
+						h->check = GG_CHECK_READ;
+						h->timeout = GG_DEFAULT_TIMEOUT;
+						h->established = 1;
+						break;
+					case 0x04:	/* XXX */
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n");
+						/* XXX zwracać odpowiedni event */
+					default:
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type);
+						e->type = GG_EVENT_DCC_ERROR;
+						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+				}
+			 	
+				return e;
+
+			case GG_STATE_READING_VOICE_SIZE:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n");
+				
+				gg_read(h->fd, &small, sizeof(small));
+
+				small.type = gg_fix32(small.type);
+
+				if (small.type < 16 || small.type > sizeof(buf)) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type);
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+					
+					return e;
+				}
+
+				h->chunk_size = small.type;
+				h->chunk_offset = 0;
+
+				if (!(h->voice_buf = malloc(h->chunk_size))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
+					return NULL;
+				}
+
+				h->state = GG_STATE_READING_VOICE_DATA;
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+			 	
+				return e;
+
+			case GG_STATE_READING_VOICE_DATA:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n");
+				
+				tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+				if (tmp < 1) {
+					if (tmp == -1) {
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno));
+					} else {
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n");
+					}
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+					return e;
+				}
+
+				gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp);
+
+				h->chunk_offset += tmp;
+
+				if (h->chunk_offset >= h->chunk_size) {
+					e->type = GG_EVENT_DCC_VOICE_DATA;
+					e->event.dcc_voice_data.data = h->voice_buf;
+					e->event.dcc_voice_data.length = h->chunk_size;
+					h->state = GG_STATE_READING_VOICE_HEADER;
+					h->voice_buf = NULL;
+				}
+
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+				
+				return e;
+
+			case GG_STATE_CONNECTING:
+			{
+				uin_t uins[2];
+
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n");
+				
+				res = 0;
+				if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res));
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+					return e;
+				}
+
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n");
+				
+				uins[0] = gg_fix32(h->uin);
+				uins[1] = gg_fix32(h->peer_uin);
+
+				gg_write(h->fd, uins, sizeof(uins));
+				
+				h->state = GG_STATE_READING_ACK;
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+				
+				return e;
+			}
+
+			case GG_STATE_READING_ACK:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n");
+				
+				gg_read(h->fd, buf, 4);
+
+				if (strncmp(buf, ack, 4)) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
+
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+					return e;
+				}
+
+				h->check = GG_CHECK_WRITE;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+				h->state = GG_STATE_SENDING_REQUEST;
+				
+				return e;
+
+			case GG_STATE_SENDING_VOICE_REQUEST:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n");
+
+				small.type = gg_fix32(0x0003);
+				
+				gg_write(h->fd, &small, sizeof(small));
+
+				h->state = GG_STATE_READING_VOICE_ACK;
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+				
+				return e;
+			
+			case GG_STATE_SENDING_REQUEST:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n");
+
+				small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002);	/* XXX */
+				
+				gg_write(h->fd, &small, sizeof(small));
+				
+				switch (h->type) {
+					case GG_SESSION_DCC_GET:
+						h->state = GG_STATE_READING_REQUEST;
+						h->check = GG_CHECK_READ;
+						h->timeout = GG_DEFAULT_TIMEOUT;
+						break;
+
+					case GG_SESSION_DCC_SEND:
+						h->state = GG_STATE_SENDING_FILE_INFO;
+						h->check = GG_CHECK_WRITE;
+						h->timeout = GG_DEFAULT_TIMEOUT;
+
+						if (h->file_fd == -1)
+							e->type = GG_EVENT_DCC_NEED_FILE_INFO;
+						break;
+						
+					case GG_SESSION_DCC_VOICE:
+						h->state = GG_STATE_SENDING_VOICE_REQUEST;
+						h->check = GG_CHECK_WRITE;
+						h->timeout = GG_DEFAULT_TIMEOUT;
+						break;
+				}
+
+				return e;
+			
+			case GG_STATE_SENDING_FILE_INFO:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
+
+				if (h->file_fd == -1) {
+					e->type = GG_EVENT_DCC_NEED_FILE_INFO;
+					return e;
+				}
+
+				small.type = gg_fix32(0x0001);	/* XXX */
+				
+				gg_write(h->fd, &small, sizeof(small));
+
+				file_info_packet.big.type = gg_fix32(0x0003);	/* XXX */
+				file_info_packet.big.dunno1 = 0;
+				file_info_packet.big.dunno2 = 0;
+
+				memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info));
+
+				/* zostają teraz u nas, więc odwracamy z powrotem */
+				h->file_info.size = gg_fix32(h->file_info.size);
+				h->file_info.mode = gg_fix32(h->file_info.mode);
+				
+				gg_write(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+				h->state = GG_STATE_READING_FILE_ACK;
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
+
+				return e;
+				
+			case GG_STATE_READING_FILE_ACK:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n");
+				
+				gg_read(h->fd, &big, sizeof(big));
+
+				/* XXX sprawdzać wynik */
+				h->offset = gg_fix32(big.dunno1);
+				
+				h->state = GG_STATE_SENDING_FILE_HEADER;
+				h->check = GG_CHECK_WRITE;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+
+				e->type = GG_EVENT_DCC_ACK;
+
+				return e;
+
+			case GG_STATE_READING_VOICE_ACK:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n");
+				
+				gg_read(h->fd, &tiny, sizeof(tiny));
+
+				if (tiny.type != 0x01) {
+					gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type);
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_REFUSED;
+					return e;
+				}
+
+				h->state = GG_STATE_READING_VOICE_HEADER;
+				h->check = GG_CHECK_READ;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+
+				e->type = GG_EVENT_DCC_ACK;
+				
+				return e;
+
+			case GG_STATE_SENDING_FILE_HEADER:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n");
+				
+				h->chunk_offset = 0;
+				
+				if ((h->chunk_size = h->file_info.size - h->offset) > 4096) {
+					h->chunk_size = 4096;
+					big.type = gg_fix32(0x0003);  /* XXX */
+				} else
+					big.type = gg_fix32(0x0002);  /* XXX */
+
+				big.dunno1 = gg_fix32(h->chunk_size);
+				big.dunno2 = 0;
+				
+				gg_write(h->fd, &big, sizeof(big));
+
+				h->state = GG_STATE_SENDING_FILE;
+				h->check = GG_CHECK_WRITE;
+				h->timeout = GG_DEFAULT_TIMEOUT;
+				h->established = 1;
+
+				return e;
+				
+			case GG_STATE_SENDING_FILE:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n");
+				
+				if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
+					utmp = sizeof(buf);
+				
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size);
+
+				/* koniec pliku? */
+				if (h->file_info.size == 0) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n");
+					e->type = GG_EVENT_DCC_DONE;
+
+					return e;
+				}
+
+				lseek(h->file_fd, h->offset, SEEK_SET);
+
+				size = read(h->file_fd, buf, utmp);
+
+				/* błąd */
+				if (size == -1) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
+
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_FILE;
+
+					return e;
+				}
+
+				/* koniec pliku? */
+				if (size == 0) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n");
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_EOF;
+
+					return e;
+				}
+				
+				/* jeśli wczytaliśmy więcej, utnijmy. */
+				if (h->offset + size > h->file_info.size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size);
+					size = h->file_info.size - h->offset;
+
+					if (size < 1) {
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n");
+						e->type = GG_EVENT_DCC_DONE;
+						return e;
+					}
+				}
+
+				tmp = write(h->fd, buf, size);
+
+				if (tmp == -1) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%s)\n", strerror(errno));
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+					return e;
+				}
+
+				h->offset += size;
+				
+				if (h->offset >= h->file_info.size) {
+					e->type = GG_EVENT_DCC_DONE;
+					return e;
+				}
+				
+				h->chunk_offset += size;
+				
+				if (h->chunk_offset >= h->chunk_size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
+					h->state = GG_STATE_SENDING_FILE_HEADER;
+					h->timeout = GG_DEFAULT_TIMEOUT;
+				} else {
+					h->state = GG_STATE_SENDING_FILE;
+					h->timeout = GG_DCC_TIMEOUT_SEND;
+				}
+				
+				h->check = GG_CHECK_WRITE;
+
+				return e;
+
+			case GG_STATE_GETTING_FILE:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n");
+				
+				if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
+					utmp = sizeof(buf);
+				
+				size = read(h->fd, buf, utmp);
+
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size);
+				
+				/* błąd */
+				if (size == -1) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
+
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+
+					return e;
+				}
+
+				/* koniec? */
+				if (size == 0) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n");
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_EOF;
+
+					return e;
+				}
+				
+				tmp = write(h->file_fd, buf, size);
+				
+				if (tmp == -1 || tmp < size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno));
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+					return e;
+				}
+
+				h->offset += size;
+				
+				if (h->offset >= h->file_info.size) {
+					e->type = GG_EVENT_DCC_DONE;
+					return e;
+				}
+
+				h->chunk_offset += size;
+				
+				if (h->chunk_offset >= h->chunk_size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
+					h->state = GG_STATE_READING_FILE_HEADER;
+					h->timeout = GG_DEFAULT_TIMEOUT;
+					h->chunk_offset = 0;
+					h->chunk_size = sizeof(big);
+					if (!(h->chunk_buf = malloc(sizeof(big)))) {
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
+						free(e);
+						return NULL;
+					}
+				} else {
+					h->state = GG_STATE_GETTING_FILE;
+					h->timeout = GG_DCC_TIMEOUT_GET;
+				}
+				
+				h->check = GG_CHECK_READ;
+
+				return e;
+				
+			default:
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n");
+				e->type = GG_EVENT_DCC_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+
+				return e;
+		}
+	}
+	
+	return e;
+}
+
+#undef gg_read
+#undef gg_write
+
+/*
+ * gg_dcc_free()
+ *
+ * zwalnia pamięć po strukturze połączenia dcc.
+ *
+ *  - d - zwalniana struktura
+ */
+void gg_dcc_free(struct gg_dcc *d)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d);
+	
+	if (!d)
+		return;
+
+	if (d->fd != -1)
+		close(d->fd);
+
+	if (d->chunk_buf) {
+		free(d->chunk_buf);
+		d->chunk_buf = NULL;
+	}
+
+	free(d);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/events.c	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,1566 @@
+/* $Id: events.c 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "libgadu-config.h"
+
+#include <errno.h>
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+#  include <pthread.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#  include <openssl/err.h>
+#  include <openssl/x509.h>
+#endif
+
+#include "compat.h"
+#include "libgadu.h"
+
+/*
+ * gg_event_free()
+ *
+ * zwalnia pamięć zajmowaną przez informację o zdarzeniu.
+ *
+ *  - e - wskaźnik do informacji o zdarzeniu
+ */
+void gg_event_free(struct gg_event *e)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
+			
+	if (!e)
+		return;
+	
+	switch (e->type) {
+		case GG_EVENT_MSG:
+			free(e->event.msg.message);
+			free(e->event.msg.formats);
+			free(e->event.msg.recipients);
+			break;
+	
+		case GG_EVENT_NOTIFY:
+			free(e->event.notify);
+			break;
+	
+		case GG_EVENT_NOTIFY60:
+		{
+			int i;
+
+			for (i = 0; e->event.notify60[i].uin; i++)
+				free(e->event.notify60[i].descr);
+		
+			free(e->event.notify60);
+
+			break;
+		}
+
+		case GG_EVENT_STATUS60:
+			free(e->event.status60.descr);
+			break;
+	
+		case GG_EVENT_STATUS:
+			free(e->event.status.descr);
+			break;
+
+		case GG_EVENT_NOTIFY_DESCR:
+			free(e->event.notify_descr.notify);
+			free(e->event.notify_descr.descr);
+			break;
+
+		case GG_EVENT_DCC_VOICE_DATA:
+			free(e->event.dcc_voice_data.data);
+			break;
+
+		case GG_EVENT_PUBDIR50_SEARCH_REPLY:
+		case GG_EVENT_PUBDIR50_READ:
+		case GG_EVENT_PUBDIR50_WRITE:
+			gg_pubdir50_free(e->event.pubdir50);
+			break;
+
+		case GG_EVENT_USERLIST:
+			free(e->event.userlist.reply);
+			break;
+	
+		case GG_EVENT_IMAGE_REPLY:
+			free(e->event.image_reply.filename);
+			free(e->event.image_reply.image);
+			break;
+	}
+
+	free(e);
+}
+
+/*
+ * gg_image_queue_remove()
+ *
+ * usuwa z kolejki dany wpis.
+ *
+ *  - s - sesja
+ *  - q - kolejka
+ *  - freeq - czy zwolnić kolejkę
+ *
+ * 0/-1
+ */
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
+{
+	if (!s || !q) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (s->images == q)
+		s->images = q->next;
+	else {
+		struct gg_image_queue *qq;
+
+		for (qq = s->images; qq; qq = qq->next) {
+			if (qq->next == q) {
+				qq->next = q->next;
+				break;
+			}
+		}
+	}
+
+	if (freeq) {
+		free(q->image);
+		free(q->filename);
+		free(q);
+	}
+
+	return 0;
+}
+
+/*
+ * gg_image_queue_parse() // funkcja wewnętrzna
+ *
+ * parsuje przychodzący pakiet z obrazkiem.
+ *
+ *  - e - opis zdarzenia
+ *  - 
+ */
+static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender)
+{
+	struct gg_msg_image_reply *i = (void*) p;
+	struct gg_image_queue *q, *qq;
+
+	if (!p || !sess || !e) {
+		errno = EFAULT;
+		return;
+	}
+
+	/* znajdź dany obrazek w kolejce danej sesji */
+	
+	for (qq = sess->images, q = NULL; qq; qq = qq->next) {
+		if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) {
+			q = qq;
+			break;
+		}
+	}
+
+	if (!q) {
+		gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
+		return;
+	}
+
+	if (p[0] == 0x05) {
+		int i, ok = 0;
+		
+		q->done = 0;
+
+		len -= sizeof(struct gg_msg_image_reply);
+		p += sizeof(struct gg_msg_image_reply);
+
+		/* sprawdź, czy mamy tekst zakończony \0 */
+
+		for (i = 0; i < len; i++) {
+			if (!p[i]) {
+				ok = 1;
+				break;
+			}
+		}
+
+		if (!ok) {
+			gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
+			return;
+		}
+
+		if (!(q->filename = strdup(p))) {
+			gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
+			return;
+		}
+
+		len -= strlen(p) + 1;
+		p += strlen(p) + 1;
+	} else {
+		len -= sizeof(struct gg_msg_image_reply);
+		p += sizeof(struct gg_msg_image_reply);
+	}
+
+	if (q->done + len > q->size)
+		len = q->size - q->done;
+		
+	memcpy(q->image + q->done, p, len);
+	q->done += len;
+
+	/* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
+
+	if (q->done >= q->size) {
+		e->type = GG_EVENT_IMAGE_REPLY;
+		e->event.image_reply.sender = sender;
+		e->event.image_reply.size = q->size;
+		e->event.image_reply.crc32 = q->crc32;
+		e->event.image_reply.filename = q->filename;
+		e->event.image_reply.image = q->image;
+
+		gg_image_queue_remove(sess, q, 0);
+
+		free(q);
+	}
+}
+
+/*
+ * gg_handle_recv_msg() // funkcja wewnętrzna
+ *
+ * obsługuje pakiet z przychodzącą wiadomością, rozbijając go na dodatkowe
+ * struktury (konferencje, kolorki) w razie potrzeby.
+ *
+ *  - h - nagłówek pakietu
+ *  - e - opis zdarzenia
+ *
+ * 0, -1.
+ */
+static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+	struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
+	char *p, *packet_end = (char*) r + h->length;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
+
+	if (!r->seq && !r->msgclass) {
+		gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
+		e->type = GG_EVENT_NONE;
+		return 0;
+	}
+	
+	for (p = (char*) r + sizeof(*r); *p; p++) {
+		if (*p == 0x02 && p == packet_end - 1) {
+			gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
+			break;
+		}
+		if (p >= packet_end) {
+			gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
+			goto malformed;
+		}
+	}
+	
+	p++;
+
+	/* przeanalizuj dodatkowe opcje */
+	while (p < packet_end) {
+		switch (*p) {
+			case 0x01:		/* konferencja */
+			{
+				struct gg_msg_recipients *m = (void*) p;
+				uint32_t i, count;
+			
+				p += sizeof(*m);
+			
+				if (p > packet_end) {
+					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
+					goto malformed;
+				}
+
+				count = gg_fix32(m->count);
+
+				if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) {
+					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
+					goto malformed;
+				}
+			
+				if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
+					goto fail;
+				}
+			
+				for (i = 0; i < count; i++, p += sizeof(uint32_t)) {
+					uint32_t u;
+					memcpy(&u, p, sizeof(uint32_t));
+					e->event.msg.recipients[i] = gg_fix32(u);
+				}
+				
+				e->event.msg.recipients_count = count;
+				
+				break;
+			}
+
+			case 0x02:		/* richtext */
+			{
+				uint16_t len;
+				char *buf;
+			
+				if (p + 3 > packet_end) {
+					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
+					goto malformed;
+				}
+
+				memcpy(&len, p + 1, sizeof(uint16_t));
+				len = gg_fix16(len);
+
+				if (!(buf = malloc(len))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
+					goto fail;
+				}
+
+				p += 3;
+
+				if (p + len > packet_end) {
+					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+					free(buf);
+					goto malformed;
+				}
+				
+				memcpy(buf, p, len);
+
+				e->event.msg.formats = buf;
+				e->event.msg.formats_length = len;
+
+				p += len;
+
+				break;
+			}
+
+			case 0x04:		/* image_request */
+			{
+				struct gg_msg_image_request *i = (void*) p;
+
+				if (p + sizeof(*i) > packet_end) {
+					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+					goto malformed;
+				}
+
+				e->event.image_request.sender = gg_fix32(r->sender);
+				e->event.image_request.size = gg_fix32(i->size);
+				e->event.image_request.crc32 = gg_fix32(i->crc32);
+
+				e->type = GG_EVENT_IMAGE_REQUEST;
+
+				return 0;
+			}
+
+			case 0x05:		/* image_reply */
+			case 0x06:
+			{
+				struct gg_msg_image_reply *rep = (void*) p;
+
+				if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
+
+					/* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
+
+					e->type = GG_EVENT_IMAGE_REPLY;
+					e->event.image_reply.sender = gg_fix32(r->sender);
+					e->event.image_reply.size = 0;
+					e->event.image_reply.crc32 = gg_fix32(rep->crc32);
+					e->event.image_reply.filename = NULL;
+					e->event.image_reply.image = NULL;
+					return 0;
+
+				} else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
+
+					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n");
+					goto malformed;
+				}
+
+				rep->size = gg_fix32(rep->size);
+				rep->crc32 = gg_fix32(rep->crc32);
+				gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender));
+
+				return 0;
+			}
+
+			default:
+			{
+				gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
+				p = packet_end;
+			}
+		}
+	}
+
+	e->type = GG_EVENT_MSG;
+	e->event.msg.msgclass = gg_fix32(r->msgclass);
+	e->event.msg.sender = gg_fix32(r->sender);
+	e->event.msg.time = gg_fix32(r->time);
+	e->event.msg.message = strdup((char*) r + sizeof(*r));
+
+	return 0;
+
+malformed:
+	e->type = GG_EVENT_NONE;
+
+	free(e->event.msg.recipients);
+	free(e->event.msg.formats);
+
+	return 0;
+
+fail:
+	free(e->event.msg.recipients);
+	free(e->event.msg.formats);
+	return -1;
+}
+
+/*
+ * gg_watch_fd_connected() // funkcja wewnętrzna
+ *
+ * patrzy na gniazdo, odbiera pakiet i wypełnia strukturę zdarzenia.
+ *
+ *  - sess - struktura opisująca sesję
+ *  - e - opis zdarzenia
+ *
+ * 0, -1.
+ */
+static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
+{
+	struct gg_header *h = NULL;
+	char *p;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (!(h = gg_recv_packet(sess))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
+		goto fail;
+	}
+
+	p = (char*) h + sizeof(struct gg_header);
+	
+	switch (h->type) {
+		case GG_RECV_MSG:
+		{
+			if (h->length >= sizeof(struct gg_recv_msg))
+				if (gg_handle_recv_msg(h, e, sess))
+					goto fail;
+			
+			break;
+		}
+
+		case GG_NOTIFY_REPLY:
+		{
+			struct gg_notify_reply *n = (void*) p;
+			unsigned int count, i;
+			char *tmp;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+			if (h->length < sizeof(*n)) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
+				errno = EINVAL;
+				goto fail;
+			}
+
+			if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) {
+				e->type = GG_EVENT_NOTIFY_DESCR;
+				
+				if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					goto fail;
+				}
+				e->event.notify_descr.notify[1].uin = 0;
+				memcpy(e->event.notify_descr.notify, p, sizeof(*n));
+				e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin);
+				e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status);
+				e->event.notify_descr.notify[0].remote_ip = e->event.notify_descr.notify[0].remote_ip;
+				e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port);
+
+				count = h->length - sizeof(*n);
+				if (!(tmp = malloc(count + 1))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					goto fail;
+				}
+				memcpy(tmp, p + sizeof(*n), count);
+				tmp[count] = 0;
+				e->event.notify_descr.descr = tmp;
+				
+			} else {
+				e->type = GG_EVENT_NOTIFY;
+				
+				if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					goto fail;
+				}
+				
+				memcpy(e->event.notify, p, h->length);
+				count = h->length / sizeof(*n);
+				e->event.notify[count].uin = 0;
+				
+				for (i = 0; i < count; i++) {
+					e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin);
+					e->event.notify[i].status = gg_fix32(e->event.notify[i].status);
+					e->event.notify[i].remote_ip = e->event.notify[i].remote_ip;
+					e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port);
+				}
+			}
+
+			break;
+		}
+
+		case GG_STATUS:
+		{
+			struct gg_status *s = (void*) p;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+			if (h->length >= sizeof(*s)) {
+				e->type = GG_EVENT_STATUS;
+				memcpy(&e->event.status, p, sizeof(*s));
+				e->event.status.uin = gg_fix32(e->event.status.uin);
+				e->event.status.status = gg_fix32(e->event.status.status);
+				if (h->length > sizeof(*s)) {
+					int len = h->length - sizeof(*s);
+					char *buf = malloc(len + 1);
+					if (buf) {
+						memcpy(buf, p + sizeof(*s), len);
+						buf[len] = 0;
+					}
+					e->event.status.descr = buf;
+				} else
+					e->event.status.descr = NULL;
+			}
+
+			break;
+		}
+
+		case GG_NOTIFY_REPLY60:
+		{
+			struct gg_notify_reply60 *n = (void*) p;
+			unsigned int length = h->length, i = 0;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+			e->type = GG_EVENT_NOTIFY60;
+			e->event.notify60 = malloc(sizeof(*e->event.notify60));
+
+			if (!e->event.notify60) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+				goto fail;
+			}
+
+			e->event.notify60[0].uin = 0;
+			
+			while (length >= sizeof(struct gg_notify_reply60)) {
+				uin_t uin = gg_fix32(n->uin);
+				char *tmp;
+
+				e->event.notify60[i].uin = uin & 0x00ffffff;
+				e->event.notify60[i].status = n->status;
+				e->event.notify60[i].remote_ip = n->remote_ip;
+				e->event.notify60[i].remote_port = gg_fix16(n->remote_port);
+				e->event.notify60[i].version = n->version;
+				e->event.notify60[i].image_size = n->image_size;
+				e->event.notify60[i].descr = NULL;
+				e->event.notify60[i].time = 0;
+
+				if (uin & 0x40000000)
+					e->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
+				if (uin & 0x08000000)
+					e->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
+
+				if (GG_S_D(n->status)) {
+					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
+
+					if (descr_len < length) {
+						if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) {
+							gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							goto fail;
+						}
+
+						memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len);
+						e->event.notify60[i].descr[descr_len] = 0;
+
+						/* XXX czas */
+					}
+					
+					length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
+					n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+				} else {
+					length -= sizeof(struct gg_notify_reply60);
+					n = (void*) ((char*) n + sizeof(struct gg_notify_reply60));
+				}
+
+				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					free(e->event.notify60);
+					goto fail;
+				}
+
+				e->event.notify60 = (void*) tmp;
+				e->event.notify60[++i].uin = 0;
+			}
+
+			break;
+		}
+		
+		case GG_STATUS60:
+		{
+			struct gg_status60 *s = (void*) p;
+			uint32_t uin;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+			if (h->length < sizeof(*s))
+				break;
+
+			uin = gg_fix32(s->uin);
+
+			e->type = GG_EVENT_STATUS60;
+			e->event.status60.uin = uin & 0x00ffffff;
+			e->event.status60.status = s->status;
+			e->event.status60.remote_ip = s->remote_ip;
+			e->event.status60.remote_port = gg_fix16(s->remote_port);
+			e->event.status60.version = s->version;
+			e->event.status60.image_size = s->image_size;
+			e->event.status60.descr = NULL;
+			e->event.status60.time = 0;
+
+			if (uin & 0x40000000)
+				e->event.status60.version |= GG_HAS_AUDIO_MASK;
+			if (uin & 0x08000000)
+				e->event.status60.version |= GG_ERA_OMNIX_MASK;
+
+			if (h->length > sizeof(*s)) {
+				int len = h->length - sizeof(*s);
+				char *buf = malloc(len + 1);
+
+				if (buf) {
+					memcpy(buf, (char*) p + sizeof(*s), len);
+					buf[len] = 0;
+				}
+
+				e->event.status60.descr = buf;
+
+				if (len > 4 && p[h->length - 5] == 0) {
+					uint32_t t;
+					memcpy(&t, p + h->length - 4, sizeof(uint32_t));
+					e->event.status60.time = gg_fix32(t);
+				}
+			}
+
+			break;
+		}
+
+		case GG_SEND_MSG_ACK:
+		{
+			struct gg_send_msg_ack *s = (void*) p;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
+
+			if (h->length < sizeof(*s))
+				break;
+
+			e->type = GG_EVENT_ACK;
+			e->event.ack.status = gg_fix32(s->status);
+			e->event.ack.recipient = gg_fix32(s->recipient);
+			e->event.ack.seq = gg_fix32(s->seq);
+
+			break;
+		}
+
+		case GG_PONG: 
+		{
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
+
+			e->type = GG_EVENT_PONG;
+			sess->last_pong = time(NULL);
+
+			break;
+		}
+
+		case GG_DISCONNECTING:
+		{
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
+			e->type = GG_EVENT_DISCONNECT;
+			break;
+		}
+
+		case GG_PUBDIR50_REPLY:
+		{
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
+			if (gg_pubdir50_handle_reply(e, p, h->length) == -1)
+				goto fail;
+			break;
+		}
+
+		case GG_USERLIST_REPLY:
+		{
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
+
+			if (h->length < 1)
+				break;
+
+			/* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
+			 * gdy otrzymano wszystkie odpowiedzi */
+			if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) {
+				if (--sess->userlist_blocks)
+					break;
+
+				p[0] = GG_USERLIST_PUT_REPLY;
+			}
+
+			if (h->length > 1) {
+				char *tmp;
+				unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0;
+				
+				gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
+				
+				if (!(tmp = realloc(sess->userlist_reply, len + h->length))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
+					free(sess->userlist_reply);
+					sess->userlist_reply = NULL;
+					goto fail;
+				}
+
+				sess->userlist_reply = tmp;
+				sess->userlist_reply[len + h->length - 1] = 0;
+				memcpy(sess->userlist_reply + len, p + 1, h->length - 1);
+			}
+
+			if (p[0] == GG_USERLIST_GET_MORE_REPLY)
+				break;
+
+			e->type = GG_EVENT_USERLIST;
+			e->event.userlist.type = p[0];
+			e->event.userlist.reply = sess->userlist_reply;
+			sess->userlist_reply = NULL;
+
+			break;
+		}
+
+		default:
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
+	}
+	
+	free(h);
+	return 0;
+
+fail:
+	free(h);
+	return -1;
+}
+
+/*
+ * gg_watch_fd()
+ *
+ * funkcja, którą należy wywołać, gdy coś się stanie z obserwowanym
+ * deskryptorem. zwraca klientowi informację o tym, co się dzieje.
+ *
+ *  - sess - opis sesji
+ *
+ * wskaźnik do struktury gg_event, którą trzeba zwolnić później
+ * za pomocą gg_event_free(). jesli rodzaj zdarzenia jest równy
+ * GG_EVENT_NONE, należy je zignorować. jeśli zwróciło NULL,
+ * stało się coś niedobrego -- albo zabrakło pamięci albo zerwało
+ * połączenie.
+ */
+struct gg_event *gg_watch_fd(struct gg_session *sess)
+{
+	struct gg_event *e;
+	int res = 0;
+	int port = 0;
+	int errno2 = 0;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
+	
+	if (!sess) {
+		errno = EFAULT;
+		return NULL;
+	}
+
+	if (!(e = (void*) calloc(1, sizeof(*e)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
+		return NULL;
+	}
+
+	e->type = GG_EVENT_NONE;
+
+	switch (sess->state) {
+		case GG_STATE_RESOLVING:
+		{
+			struct in_addr addr;
+			int failed = 0;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
+
+			if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+				failed = 1;
+				errno2 = errno;
+			}
+			
+			close(sess->fd);
+			sess->fd = -1;
+
+#ifndef __GG_LIBGADU_HAVE_PTHREAD
+			waitpid(sess->pid, NULL, 0);
+			sess->pid = -1;
+#else
+			if (sess->resolver) {
+				pthread_cancel(*((pthread_t*) sess->resolver));
+				free(sess->resolver);
+				sess->resolver = NULL;
+			}
+#endif
+
+			if (failed) {
+				errno = errno2;
+				goto fail_resolving;
+			}
+
+			/* jeśli jesteśmy w resolverze i mamy ustawiony port
+			 * proxy, znaczy, że resolvowaliśmy proxy. zatem
+			 * wpiszmy jego adres. */
+			if (sess->proxy_port)
+				sess->proxy_addr = addr.s_addr;
+
+			/* zapiszmy sobie adres huba i adres serwera (do
+			 * bezpośredniego połączenia, jeśli hub leży)
+			 * z resolvera. */
+			if (sess->proxy_addr && sess->proxy_port)
+				port = sess->proxy_port;
+			else {
+				sess->server_addr = sess->hub_addr = addr.s_addr;
+				port = GG_APPMSG_PORT;
+			}
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
+			
+			/* łączymy się albo z hubem, albo z proxy, zależnie
+			 * od tego, co resolvowaliśmy. */
+			if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
+				/* jeśli w trybie asynchronicznym gg_connect()
+				 * zwróci błąd, nie ma sensu próbować dalej. */
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+				goto fail_connecting;
+			}
+
+			/* jeśli podano serwer i łączmy się przez proxy,
+			 * jest to bezpośrednie połączenie, inaczej jest
+			 * do huba. */
+			sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB;
+			sess->check = GG_CHECK_WRITE;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+
+			break;
+		}
+
+		case GG_STATE_CONNECTING_HUB:
+		{
+			char buf[1024], *client, *auth;
+			int res = 0, res_size = sizeof(res);
+			const char *host, *appmsg;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
+
+			/* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
+			 * przypadkiem jakiś błąd. */
+			if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+				/* no tak, nie udało się połączyć z proxy. nawet
+				 * nie próbujemy dalej. */
+				if (sess->proxy_addr && sess->proxy_port) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+					goto fail_connecting;
+				}
+					
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res));
+				close(sess->fd);
+
+				if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) {
+					/* przy asynchronicznych, gg_connect()
+					 * zwraca -1 przy błędach socket(),
+					 * ioctl(), braku routingu itd. dlatego
+					 * nawet nie próbujemy dalej. */
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+					goto fail_connecting;
+				}
+
+				sess->state = GG_STATE_CONNECTING_GG;
+				sess->check = GG_CHECK_WRITE;
+				sess->timeout = GG_DEFAULT_TIMEOUT;
+				break;
+			}
+			
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
+
+			if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
+				goto fail_connecting;
+			}
+
+			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port)
+				host = "http://" GG_APPMSG_HOST;
+			else
+				host = "";
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+			if (sess->ssl)
+				appmsg = "appmsg3.asp";
+			else
+#endif
+				appmsg = "appmsg2.asp";
+
+			auth = gg_proxy_auth();
+
+			snprintf(buf, sizeof(buf) - 1,
+				"GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
+				"Host: " GG_APPMSG_HOST "\r\n"
+				"User-Agent: " GG_HTTP_USERAGENT "\r\n"
+				"Pragma: no-cache\r\n"
+				"%s" 
+				"\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
+
+			if (auth)
+				free(auth);
+			
+			free(client);
+
+			/* zwolnij pamięć po wersji klienta. */
+			if (sess->client_version) {
+				free(sess->client_version);
+				sess->client_version = NULL;
+			}
+
+			gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
+	 
+			/* zapytanie jest krótkie, więc zawsze zmieści się
+			 * do bufora gniazda. jeśli write() zwróci mniej,
+			 * stało się coś złego. */
+			if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
+
+				e->type = GG_EVENT_CONN_FAILED;
+				e->event.failure = GG_FAILURE_WRITING;
+				sess->state = GG_STATE_IDLE;
+				close(sess->fd);
+				sess->fd = -1;
+				break;
+			}
+
+			sess->state = GG_STATE_READING_DATA;
+			sess->check = GG_CHECK_READ;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+
+			break;
+		}
+
+		case GG_STATE_READING_DATA:
+		{
+			char buf[1024], *tmp, *host;
+			int port = GG_DEFAULT_PORT;
+			struct in_addr addr;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
+
+			/* czytamy linię z gniazda i obcinamy \r\n. */
+			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+			gg_chomp(buf);
+			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
+	
+			/* sprawdzamy, czy wszystko w porządku. */
+			if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n");
+
+				close(sess->fd);
+
+				/* jeśli otrzymaliśmy jakieś dziwne informacje,
+				 * próbujemy się łączyć z pominięciem huba. */
+				if (sess->proxy_addr && sess->proxy_port) {
+					if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
+						/* trudno. nie wyszło. */
+						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
+						goto fail_connecting;
+					}
+
+					sess->state = GG_STATE_CONNECTING_GG;
+					sess->check = GG_CHECK_WRITE;
+					sess->timeout = GG_DEFAULT_TIMEOUT;
+					break;
+				}
+				
+				sess->port = GG_DEFAULT_PORT;
+
+				/* łączymy się na port 8074 huba. */
+				if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+
+					sess->port = GG_HTTPS_PORT;
+					
+					/* łączymy się na port 443. */
+					if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
+						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+						goto fail_connecting;
+					}
+				}
+				
+				sess->state = GG_STATE_CONNECTING_GG;
+				sess->check = GG_CHECK_WRITE;
+				sess->timeout = GG_DEFAULT_TIMEOUT;
+				break;
+			}
+	
+			/* ignorujemy resztę nagłówka. */
+			while (strcmp(buf, "\r\n") && strcmp(buf, ""))
+				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+
+			/* czytamy pierwszą linię danych. */
+			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+			gg_chomp(buf);
+			
+			/* jeśli pierwsza liczba w linii nie jest równa zeru,
+			 * oznacza to, że mamy wiadomość systemową. */
+			if (atoi(buf)) {
+				char tmp[1024], *foo, *sysmsg_buf = NULL;
+				int len = 0;
+				
+				while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) {
+					if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
+						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
+						break;
+					}
+
+					sysmsg_buf = foo;
+
+					if (!len)
+						strcpy(sysmsg_buf, tmp);
+					else
+						strcat(sysmsg_buf, tmp);
+					
+					len += strlen(tmp);
+				}
+				
+				e->type = GG_EVENT_MSG;
+				e->event.msg.msgclass = atoi(buf);
+				e->event.msg.sender = 0;
+				e->event.msg.message = sysmsg_buf;
+			}
+	
+			close(sess->fd);
+	
+			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
+
+			/* analizujemy otrzymane dane. */
+			tmp = buf;
+			
+			while (*tmp && *tmp != ' ')
+				tmp++;
+			while (*tmp && *tmp == ' ')
+				tmp++;
+			host = tmp;
+			while (*tmp && *tmp != ' ')
+				tmp++;
+			*tmp = 0;
+
+			if ((tmp = strchr(host, ':'))) {
+				*tmp = 0;
+				port = atoi(tmp + 1);
+			}
+
+			addr.s_addr = inet_addr(host);
+			sess->server_addr = addr.s_addr;
+
+			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) {
+				/* jeśli mamy proxy, łączymy się z nim. */
+				if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
+					/* nie wyszło? trudno. */
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
+					goto fail_connecting;
+				}
+				
+				sess->state = GG_STATE_CONNECTING_GG;
+				sess->check = GG_CHECK_WRITE;
+				sess->timeout = GG_DEFAULT_TIMEOUT;
+				break;
+			}
+
+			sess->port = port;
+
+			/* łączymy się z właściwym serwerem. */
+			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+
+				sess->port = GG_HTTPS_PORT;
+
+				/* nie wyszło? próbujemy portu 443. */
+				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
+					/* ostatnia deska ratunku zawiodła?
+					 * w takim razie zwijamy manatki. */
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					goto fail_connecting;
+				}
+			}
+
+			sess->state = GG_STATE_CONNECTING_GG;
+			sess->check = GG_CHECK_WRITE;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+		
+			break;
+		}
+
+		case GG_STATE_CONNECTING_GG:
+		{
+			int res = 0, res_size = sizeof(res);
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
+
+			/* jeśli wystąpił błąd podczas łączenia się... */
+			if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+				/* jeśli nie udało się połączenie z proxy,
+				 * nie mamy czego próbować więcej. */
+				if (sess->proxy_addr && sess->proxy_port) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+					goto fail_connecting;
+				}
+
+				close(sess->fd);
+				sess->fd = -1;
+
+#ifdef ETIMEDOUT
+				if (sess->timeout == 0)
+					errno = ETIMEDOUT;
+#endif
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+				/* jeśli logujemy się po TLS, nie próbujemy
+				 * się łączyć już z niczym innym w przypadku
+				 * błędu. nie dość, że nie ma sensu, to i
+				 * trzeba by się bawić w tworzenie na nowo
+				 * SSL i SSL_CTX. */
+
+				if (sess->ssl) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+					goto fail_connecting;
+				}
+#endif
+
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
+
+				sess->port = GG_HTTPS_PORT;
+
+				/* próbujemy na port 443. */
+				if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					goto fail_connecting;
+				}
+			}
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+			
+			if (gg_proxy_http_only)
+				sess->proxy_port = 0;
+
+			/* jeśli mamy proxy, wyślijmy zapytanie. */
+			if (sess->proxy_addr && sess->proxy_port) {
+				char buf[100], *auth = gg_proxy_auth();
+				struct in_addr addr;
+
+				if (sess->server_addr)
+					addr.s_addr = sess->server_addr;
+				else
+					addr.s_addr = sess->hub_addr;
+
+				snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
+
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf);
+				
+				/* wysyłamy zapytanie. jest ono na tyle krótkie,
+				 * że musi się zmieścić w buforze gniazda. jeśli
+				 * write() zawiedzie, stało się coś złego. */
+				if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					goto fail_connecting;
+				}
+
+				if (auth) {
+					gg_debug(GG_DEBUG_MISC, "//   %s", auth);
+					if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) {
+						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+						goto fail_connecting;
+					}
+
+					free(auth);
+				}
+
+				if (write(sess->fd, "\r\n", 2) < 2) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					goto fail_connecting;
+				}
+			}
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+			if (sess->ssl) {
+				SSL_set_fd(sess->ssl, sess->fd);
+
+				sess->state = GG_STATE_TLS_NEGOTIATION;
+				sess->check = GG_CHECK_WRITE;
+				sess->timeout = GG_DEFAULT_TIMEOUT;
+
+				break;
+			}
+#endif
+
+			sess->state = GG_STATE_READING_KEY;
+			sess->check = GG_CHECK_READ;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+
+			break;
+		}
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+		case GG_STATE_TLS_NEGOTIATION:
+		{
+			int res;
+			X509 *peer;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+
+			if ((res = SSL_connect(sess->ssl)) <= 0) {
+				int err = SSL_get_error(sess->ssl, res);
+
+				if (res == 0) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
+
+					e->type = GG_EVENT_CONN_FAILED;
+					e->event.failure = GG_FAILURE_TLS;
+					sess->state = GG_STATE_IDLE;
+					close(sess->fd);
+					sess->fd = -1;
+					break;
+				}
+				
+				if (err == SSL_ERROR_WANT_READ) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
+
+					sess->state = GG_STATE_TLS_NEGOTIATION;
+					sess->check = GG_CHECK_READ;
+					sess->timeout = GG_DEFAULT_TIMEOUT;
+
+					break;
+				} else if (err == SSL_ERROR_WANT_WRITE) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
+
+					sess->state = GG_STATE_TLS_NEGOTIATION;
+					sess->check = GG_CHECK_WRITE;
+					sess->timeout = GG_DEFAULT_TIMEOUT;
+
+					break;
+				} else {
+					char buf[1024];
+
+					ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
+ 
+					e->type = GG_EVENT_CONN_FAILED;
+					e->event.failure = GG_FAILURE_TLS;
+					sess->state = GG_STATE_IDLE;
+					close(sess->fd);
+					sess->fd = -1;
+					break;
+				}
+			}
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl));
+
+			peer = SSL_get_peer_certificate(sess->ssl);
+
+			if (!peer)
+				gg_debug(GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
+			else {
+				char buf[1024];
+
+				X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
+				gg_debug(GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
+
+				X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
+				gg_debug(GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
+			}
+
+			sess->state = GG_STATE_READING_KEY;
+			sess->check = GG_CHECK_READ;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+
+			break;
+		}
+#endif
+
+		case GG_STATE_READING_KEY:
+		{
+			struct gg_header *h;			
+			struct gg_welcome *w;
+			struct gg_login60 l;
+			unsigned int hash;
+			unsigned char *password = sess->password;
+			int ret;
+			
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
+
+			memset(&l, 0, sizeof(l));
+			l.dunno2 = 0xbe;
+
+			/* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie
+			 * się tekstu wrzucanego przez proxy. */
+			if (sess->proxy_addr && sess->proxy_port) {
+				char buf[100];
+
+				strcpy(buf, "");
+				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+				gg_chomp(buf);
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n//   %s\n", buf);
+				
+				while (strcmp(buf, "")) {
+					gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+					gg_chomp(buf);
+					if (strcmp(buf, ""))
+						gg_debug(GG_DEBUG_MISC, "//   %s\n", buf);
+				}
+
+				/* XXX niech czeka jeszcze raz w tej samej
+				 * fazie. głupio, ale działa. */
+				sess->proxy_port = 0;
+				
+				break;
+			}
+
+			/* czytaj pierwszy pakiet. */
+			if (!(h = gg_recv_packet(sess))) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+
+				e->type = GG_EVENT_CONN_FAILED;
+				e->event.failure = GG_FAILURE_READING;
+				sess->state = GG_STATE_IDLE;
+				errno2 = errno;
+				close(sess->fd);
+				errno = errno2;
+				sess->fd = -1;
+				break;
+			}
+
+			if (h->type != GG_WELCOME) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
+				free(h);
+				close(sess->fd);
+				sess->fd = -1;
+				errno = EINVAL;
+				e->type = GG_EVENT_CONN_FAILED;
+				e->event.failure = GG_FAILURE_INVALID;
+				sess->state = GG_STATE_IDLE;
+				break;
+			}
+	
+			w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header));
+			w->key = gg_fix32(w->key);
+
+			hash = gg_login_hash(password, w->key);
+	
+			gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash);
+	
+			free(h);
+
+			free(sess->password);
+			sess->password = NULL;
+
+			{
+				struct in_addr dcc_ip;
+				dcc_ip.s_addr = gg_dcc_ip;
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip));
+			}
+			
+			if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
+				struct sockaddr_in sin;
+				int sin_len = sizeof(sin);
+
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
+
+				if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
+					l.local_ip = sin.sin_addr.s_addr;
+				} else {
+					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
+					l.local_ip = 0;
+				}
+			} else 
+				l.local_ip = gg_dcc_ip;
+		
+			l.uin = gg_fix32(sess->uin);
+			l.hash = gg_fix32(hash);
+			l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+			l.version = gg_fix32(sess->protocol_version);
+			l.local_port = gg_fix16(gg_dcc_port);
+			l.image_size = sess->image_size;
+			
+			if (sess->external_addr && sess->external_port > 1023) {
+				l.external_ip = sess->external_addr;
+				l.external_port = gg_fix16(sess->external_port);
+			}
+
+			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n");
+			ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL);
+
+			free(sess->initial_descr);
+			sess->initial_descr = NULL;
+
+			if (ret == -1) {
+				gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
+				errno2 = errno;
+				close(sess->fd);
+				errno = errno2;
+				sess->fd = -1;
+				e->type = GG_EVENT_CONN_FAILED;
+				e->event.failure = GG_FAILURE_WRITING;
+				sess->state = GG_STATE_IDLE;
+				break;
+			}
+	
+			sess->state = GG_STATE_READING_REPLY;
+
+			break;
+		}
+
+		case GG_STATE_READING_REPLY:
+		{
+			struct gg_header *h;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
+
+			if (!(h = gg_recv_packet(sess))) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+				e->type = GG_EVENT_CONN_FAILED;
+				e->event.failure = GG_FAILURE_READING;
+				sess->state = GG_STATE_IDLE;
+				errno2 = errno;
+				close(sess->fd);
+				errno = errno2;
+				sess->fd = -1;
+				break;
+			}
+	
+			if (h->type == GG_LOGIN_OK) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
+				e->type = GG_EVENT_CONN_SUCCESS;
+				sess->state = GG_STATE_CONNECTED;
+				sess->timeout = -1;
+				sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL;
+				free(h);
+				break;
+			}
+
+			if (h->type == GG_LOGIN_FAILED) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
+				e->event.failure = GG_FAILURE_PASSWORD;
+				errno = EACCES;
+			} else if (h->type == GG_NEED_EMAIL) {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n");
+				e->event.failure = GG_FAILURE_NEED_EMAIL;
+				errno = EACCES;
+			} else {
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
+				e->event.failure = GG_FAILURE_INVALID;
+				errno = EINVAL;
+			}
+
+			e->type = GG_EVENT_CONN_FAILED;
+			sess->state = GG_STATE_IDLE;
+			errno2 = errno;
+			close(sess->fd);
+			errno = errno2;
+			sess->fd = -1;
+			free(h);
+
+			break;
+		}
+
+		case GG_STATE_CONNECTED:
+		{
+			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
+
+			sess->last_event = time(NULL);
+			
+			if ((res = gg_watch_fd_connected(sess, e)) == -1) {
+
+				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
+
+ 				if (errno == EAGAIN) {
+					e->type = GG_EVENT_NONE;
+					res = 0;
+				} else
+					res = -1;
+			}
+			break;
+		}
+	}
+
+done:
+	if (res == -1) {
+		free(e);
+		e = NULL;
+	}
+
+	return e;
+	
+fail_connecting:
+	if (sess->fd != -1) {
+		errno2 = errno;
+		close(sess->fd);
+		errno = errno2;
+		sess->fd = -1;
+	}
+	e->type = GG_EVENT_CONN_FAILED;
+	e->event.failure = GG_FAILURE_CONNECTING;
+	sess->state = GG_STATE_IDLE;
+	goto done;
+
+fail_resolving:
+	e->type = GG_EVENT_CONN_FAILED;
+	e->event.failure = GG_FAILURE_RESOLVING;
+	sess->state = GG_STATE_IDLE;
+	goto done;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/http.c	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,522 @@
+/* $Id: http.c 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "libgadu-config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+#  include <pthread.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "libgadu.h"
+
+/*
+ * gg_http_connect() // funkcja pomocnicza
+ *
+ * rozpoczyna połączenie po http.
+ *
+ *  - hostname - adres serwera
+ *  - port - port serwera
+ *  - async - asynchroniczne połączenie
+ *  - method - metoda http (GET, POST, cokolwiek)
+ *  - path - ścieżka do zasobu (musi być poprzedzona ,,/'')
+ *  - header - nagłówek zapytania plus ewentualne dane dla POST
+ *
+ * zaalokowana struct gg_http, którą poźniej należy
+ * zwolnić funkcją gg_http_free(), albo NULL jeśli wystąpił błąd.
+ */
+struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header)
+{
+	struct gg_http *h;
+
+	if (!hostname || !port || !method || !path || !header) {
+		gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n");
+		errno = EFAULT;
+		return NULL;
+	}
+	
+	if (!(h = malloc(sizeof(*h))))
+		return NULL;
+	memset(h, 0, sizeof(*h));
+
+	h->async = async;
+	h->port = port;
+	h->fd = -1;
+	h->type = GG_SESSION_HTTP;
+
+	if (gg_proxy_enabled) {
+		char *auth = gg_proxy_auth();
+
+		h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s",
+				method, hostname, port, path, (auth) ? auth :
+				"", header);
+		hostname = gg_proxy_host;
+		h->port = port = gg_proxy_port;
+
+		if (auth)
+			free(auth);
+	} else {
+		h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
+				method, path, header);
+	}
+
+	if (!h->query) {
+		gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n");
+		free(h);
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
+
+	if (async) {
+#ifndef __GG_LIBGADU_HAVE_PTHREAD
+		if (gg_resolve(&h->fd, &h->pid, hostname)) {
+#else
+		if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
+#endif
+			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
+			gg_http_free(h);
+			errno = ENOENT;
+			return NULL;
+		}
+
+		gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver);
+
+		h->state = GG_STATE_RESOLVING;
+		h->check = GG_CHECK_READ;
+		h->timeout = GG_DEFAULT_TIMEOUT;
+	} else {
+		struct in_addr *hn, a;
+
+		if (!(hn = gg_gethostbyname(hostname))) {
+			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
+			gg_http_free(h);
+			errno = ENOENT;
+			return NULL;
+		} else {
+			a.s_addr = hn->s_addr;
+			free(hn);
+		}
+
+		if (!(h->fd = gg_connect(&a, port, 0)) == -1) {
+			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+			gg_http_free(h);
+			return NULL;
+		}
+
+		h->state = GG_STATE_CONNECTING;
+
+		while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) {
+			if (gg_http_watch_fd(h) == -1)
+				break;
+		}
+
+		if (h->state != GG_STATE_PARSING) {
+			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n");
+			gg_http_free(h);
+			return NULL;
+		}
+	}
+
+	h->callback = gg_http_watch_fd;
+	h->destroy = gg_http_free;
+	
+	return h;
+}
+
+#define gg_http_error(x) \
+	close(h->fd); \
+	h->fd = -1; \
+	h->state = GG_STATE_ERROR; \
+	h->error = x; \
+	return 0;
+
+/*
+ * gg_http_watch_fd()
+ *
+ * przy asynchronicznej obsłudze HTTP funkcję tą należy wywołać, jeśli
+ * zmieniło się coś na obserwowanym deskryptorze.
+ *
+ *  - h - struktura opisująca połączenie
+ *
+ * jeśli wszystko poszło dobrze to 0, inaczej -1. połączenie będzie
+ * zakończone, jeśli h->state == GG_STATE_PARSING. jeśli wystąpi jakiś
+ * błąd, to będzie tam GG_STATE_ERROR i odpowiedni kod błędu w h->error.
+ */
+int gg_http_watch_fd(struct gg_http *h)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h);
+
+	if (!h) {
+		gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (h->state == GG_STATE_RESOLVING) {
+		struct in_addr a;
+
+		gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n");
+
+		if (read(h->fd, &a, sizeof(a)) < (signed)sizeof(a) || a.s_addr == INADDR_NONE) {
+			gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n");
+			gg_http_error(GG_ERROR_RESOLVING);
+		}
+
+		close(h->fd);
+		h->fd = -1;
+
+#ifndef __GG_LIBGADU_HAVE_PTHREAD
+		waitpid(h->pid, NULL, 0);
+#else
+		if (h->resolver) {
+			pthread_cancel(*((pthread_t *) h->resolver));
+			free(h->resolver);
+			h->resolver = NULL;
+		}
+#endif
+
+		gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port);
+
+		if ((h->fd = gg_connect(&a, h->port, h->async)) == -1) {
+			gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno));
+			gg_http_error(GG_ERROR_CONNECTING);
+		}
+
+		h->state = GG_STATE_CONNECTING;
+		h->check = GG_CHECK_WRITE;
+		h->timeout = GG_DEFAULT_TIMEOUT;
+
+		return 0;
+	}
+
+	if (h->state == GG_STATE_CONNECTING) {
+		int res = 0;
+		unsigned int res_size = sizeof(res);
+
+		if (h->async && (getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+			gg_debug(GG_DEBUG_MISC, "=> http, async connection failed (errno=%d, %s)\n", (res) ? res : errno , strerror((res) ? res : errno));
+			close(h->fd);
+			h->fd = -1;
+			h->state = GG_STATE_ERROR;
+			h->error = GG_ERROR_CONNECTING;
+			if (res)
+				errno = res;
+			return 0;
+		}
+
+		gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n");
+
+		h->state = GG_STATE_SENDING_QUERY;
+	}
+
+	if (h->state == GG_STATE_SENDING_QUERY) {
+		int res;
+
+		if ((res = write(h->fd, h->query, strlen(h->query))) < 1) {
+			gg_debug(GG_DEBUG_MISC, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h->query), res, errno);
+			gg_http_error(GG_ERROR_WRITING);
+		}
+
+		if (res < strlen(h->query)) {
+			gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res);
+
+			memmove(h->query, h->query + res, strlen(h->query) - res + 1);
+			h->state = GG_STATE_SENDING_QUERY;
+			h->check = GG_CHECK_WRITE;
+			h->timeout = GG_DEFAULT_TIMEOUT;
+		} else {
+			gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%d)\n", strlen(h->query));
+			free(h->query);
+			h->query = NULL;
+
+			h->state = GG_STATE_READING_HEADER;
+			h->check = GG_CHECK_READ;
+			h->timeout = GG_DEFAULT_TIMEOUT;
+		}
+
+		return 0;
+	}
+
+	if (h->state == GG_STATE_READING_HEADER) {
+		char buf[1024], *tmp;
+		int res;
+
+		if ((res = read(h->fd, buf, sizeof(buf))) == -1) {
+			gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno);
+			if (h->header) {
+				free(h->header);
+				h->header = NULL;
+			}
+			gg_http_error(GG_ERROR_READING);
+		}
+
+		if (!res) {
+			gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n");
+			if (h->header) {
+				free(h->header);
+				h->header = NULL;
+			}
+			gg_http_error(GG_ERROR_READING);
+		}
+
+		gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res);
+
+		if (!(tmp = realloc(h->header, h->header_size + res + 1))) {
+			gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n");
+			free(h->header);
+			h->header = NULL;
+			gg_http_error(GG_ERROR_READING);
+		}
+
+		h->header = tmp;
+
+		memcpy(h->header + h->header_size, buf, res);
+		h->header_size += res;
+
+		gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size);
+
+		h->header[h->header_size] = 0;
+
+		if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) {
+			int sep_len = (*tmp == '\r') ? 4 : 2;
+			unsigned int left;
+			char *line;
+
+			left = h->header_size - ((long)(tmp) - (long)(h->header) + sep_len);
+
+			gg_debug(GG_DEBUG_MISC, "=> http, got all header (%d bytes, %d left)\n", h->header_size - left, left);
+
+			/* HTTP/1.1 200 OK */
+			if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) {
+				gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
+
+				gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n");
+				free(h->header);
+				h->header = NULL;
+				gg_http_error(GG_ERROR_CONNECTING);
+			}
+
+			h->body_size = 0;
+			line = h->header;
+			*tmp = 0;
+                        
+			gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
+
+			while (line) {
+				if (!strncasecmp(line, "Content-length: ", 16)) {
+					h->body_size = atoi(line + 16);
+				}
+				line = strchr(line, '\n');
+				if (line)
+					line++;
+			}
+
+			if (h->body_size <= 0) {
+				gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n");
+				h->body_size = left;
+			}
+
+			if (left > h->body_size) {
+				gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left);
+				h->body_size = left;
+			}
+
+			gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size);
+
+			if (!(h->body = malloc(h->body_size + 1))) {
+				gg_debug(GG_DEBUG_MISC, "=> http, not enough memory (%d bytes for body_buf)\n", h->body_size + 1);
+				free(h->header);
+				h->header = NULL;
+				gg_http_error(GG_ERROR_READING);
+			}
+
+			if (left) {
+				memcpy(h->body, tmp + sep_len, left);
+				h->body_done = left;
+			}
+
+			h->body[left] = 0;
+
+			h->state = GG_STATE_READING_DATA;
+			h->check = GG_CHECK_READ;
+			h->timeout = GG_DEFAULT_TIMEOUT;
+		}
+
+		return 0;
+	}
+
+	if (h->state == GG_STATE_READING_DATA) {
+		char buf[1024];
+		int res;
+
+		if ((res = read(h->fd, buf, sizeof(buf))) == -1) {
+			gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno);
+			if (h->body) {
+				free(h->body);
+				h->body = NULL;
+			}
+			gg_http_error(GG_ERROR_READING);
+		}
+
+		if (!res) {
+			if (h->body_done >= h->body_size) {
+				gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n");
+				h->state = GG_STATE_PARSING;
+				close(h->fd);
+				h->fd = -1;
+			} else {
+				gg_debug(GG_DEBUG_MISC, "=> http, connection closed while reading (have %d, need %d)\n", h->body_done, h->body_size);
+				if (h->body) {
+					free(h->body);
+					h->body = NULL;
+				}
+				gg_http_error(GG_ERROR_READING);
+			}
+
+			return 0;
+		}
+
+		gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res);
+
+		if (h->body_done + res > h->body_size) {
+			char *tmp;
+
+			gg_debug(GG_DEBUG_MISC, "=> http, too much data (%d bytes, %d needed), enlarging buffer\n", h->body_done + res, h->body_size);
+
+			if (!(tmp = realloc(h->body, h->body_done + res + 1))) {
+				gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for data (%d needed)\n", h->body_done + res + 1);
+				free(h->body);
+				h->body = NULL;
+				gg_http_error(GG_ERROR_READING);
+			}
+
+			h->body = tmp;
+			h->body_size = h->body_done + res;
+		}
+
+		h->body[h->body_done + res] = 0;
+		memcpy(h->body + h->body_done, buf, res);
+		h->body_done += res;
+
+		gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size);
+
+		return 0;
+	}
+	
+	if (h->fd != -1)
+		close(h->fd);
+
+	h->fd = -1;
+	h->state = GG_STATE_ERROR;
+	h->error = 0;
+
+	return -1;
+}
+
+#undef gg_http_error
+
+/*
+ * gg_http_stop()
+ *
+ * jeśli połączenie jest w trakcie, przerywa je. nie zwalnia h->data.
+ * 
+ *  - h - struktura opisująca połączenie
+ */
+void gg_http_stop(struct gg_http *h)
+{
+	if (!h)
+		return;
+
+	if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
+		return;
+
+	if (h->fd != -1)
+		close(h->fd);
+	h->fd = -1;
+}
+
+/*
+ * gg_http_free_fields() // funkcja wewnętrzna
+ *
+ * zwalnia pola struct gg_http, ale nie zwalnia samej struktury.
+ */
+void gg_http_free_fields(struct gg_http *h)
+{
+	if (!h)
+		return;
+
+	if (h->body) {
+		free(h->body);
+		h->body = NULL;
+	}
+
+	if (h->query) {
+		free(h->query);
+		h->query = NULL;
+	}
+	
+	if (h->header) {
+		free(h->header);
+		h->header = NULL;
+	}
+}
+
+/*
+ * gg_http_free()
+ *
+ * próbuje zamknąć połączenie i zwalnia pamięć po nim.
+ *
+ *  - h - struktura, którą należy zlikwidować
+ */
+void gg_http_free(struct gg_http *h)
+{
+	if (!h)
+		return;
+
+	gg_http_stop(h);
+	gg_http_free_fields(h);
+	free(h);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/libgadu-config.h	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,42 @@
+/* Local libgadu configuration. */
+
+#include "../../../config.h"
+
+#ifndef __GG_LIBGADU_CONFIG_H
+#define __GG_LIBGADU_CONFIG_H
+
+/* Defined if libgadu was compiled for bigendian machine. */
+#undef __GG_LIBGADU_BIGENDIAN
+#ifdef WORDS_BIGENDIAN
+#define __GG_LIBGADU_BIGENDIAN
+#endif /* WORDS_BIGENDIAN */
+
+/* Defined if this machine has va_copy(). */
+#define __GG_LIBGADU_HAVE_VA_COPY
+
+/* Defined if this machine has __va_copy(). */
+#define __GG_LIBGADU_HAVE___VA_COPY
+
+/* Defined if this machine supports long long. */
+#undef __GG_LIBGADU_HAVE_LONG_LONG
+#ifdef HAVE_LONG_LONG
+#define __GG_LIBGADU_HAVE_LONG_LONG
+#endif /* HAVE_LONG_LONG */
+
+/* Defined if libgadu was compiled and linked with pthread support. */
+/* We don't like pthreads. */
+#undef __GG_LIBGADU_HAVE_PTHREAD
+
+/* Defined if libgadu was compiled and linked with TLS support. */
+/* Always undefined in Gaim. */
+#undef __GG_LIBGADU_HAVE_OPENSSL
+
+/* Include file containing uintXX_t declarations. */
+#include <stdint.h>
+
+/* Defined if this machine has C99-compiliant vsnprintf(). */
+#define __GG_LIBGADU_HAVE_C99_VSNPRINTF
+
+#define vnsprintf g_vnsprintf
+
+#endif /* __GG_LIBGADU_CONFIG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/libgadu.c	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,1811 @@
+/* $Id: libgadu.c 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+#  include <sys/filio.h>
+#endif
+
+#include "libgadu-config.h"
+
+#include <errno.h>
+#include <netdb.h>
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+#  include <pthread.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#  include <openssl/err.h>
+#  include <openssl/rand.h>
+#endif
+
+#include "compat.h"
+#include "libgadu.h"
+
+int gg_debug_level = 0;
+void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
+
+int gg_dcc_port = 0;
+unsigned long gg_dcc_ip = 0;
+
+unsigned long gg_local_ip = 0;
+/*
+ * zmienne opisujące parametry proxy http.
+ */
+char *gg_proxy_host = NULL;
+int gg_proxy_port = 0;
+int gg_proxy_enabled = 0;
+int gg_proxy_http_only = 0;
+char *gg_proxy_username = NULL;
+char *gg_proxy_password = NULL;
+
+#ifndef lint 
+static char rcsid[]
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+= "$Id: libgadu.c 13582 2005-08-28 22:46:01Z boler $";
+#endif 
+
+/*
+ * gg_libgadu_version()
+ *
+ * zwraca wersję libgadu.
+ *
+ *  - brak
+ *
+ * wersja libgadu.
+ */
+const char *gg_libgadu_version()
+{
+	return GG_LIBGADU_VERSION;
+}
+
+/*
+ * gg_fix32()
+ *
+ * zamienia kolejność bajtów w liczbie 32-bitowej tak, by odpowiadała
+ * kolejności bajtów w protokole GG. ze względu na LE-owość serwera,
+ * zamienia tylko na maszynach BE-wych.
+ *
+ *  - x - liczba do zamiany
+ *
+ * liczba z odpowiednią kolejnością bajtów.
+ */
+uint32_t gg_fix32(uint32_t x)
+{
+#ifndef __GG_LIBGADU_BIGENDIAN
+	return x;
+#else
+	return (uint32_t)
+		(((x & (uint32_t) 0x000000ffU) << 24) |
+		((x & (uint32_t) 0x0000ff00U) << 8) |
+		((x & (uint32_t) 0x00ff0000U) >> 8) |
+		((x & (uint32_t) 0xff000000U) >> 24));
+#endif		
+}
+
+/*
+ * gg_fix16()
+ *
+ * zamienia kolejność bajtów w liczbie 16-bitowej tak, by odpowiadała
+ * kolejności bajtów w protokole GG. ze względu na LE-owość serwera,
+ * zamienia tylko na maszynach BE-wych.
+ *
+ *  - x - liczba do zamiany
+ *
+ * liczba z odpowiednią kolejnością bajtów.
+ */
+uint16_t gg_fix16(uint16_t x)
+{
+#ifndef __GG_LIBGADU_BIGENDIAN
+	return x;
+#else
+	return (uint16_t)
+		(((x & (uint16_t) 0x00ffU) << 8) |
+		((x & (uint16_t) 0xff00U) >> 8));
+#endif
+}
+
+/* 
+ * gg_login_hash() // funkcja wewnętrzna
+ * 
+ * liczy hash z hasła i danego seeda.
+ * 
+ *  - password - hasło do hashowania
+ *  - seed - wartość podana przez serwer
+ *
+ * hash.
+ */
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
+{
+	unsigned int x, y, z;
+
+	y = seed;
+
+	for (x = 0; *password; password++) {
+		x = (x & 0xffffff00) | *password;
+		y ^= x;
+		y += x;
+		x <<= 8;
+		y ^= x;
+		x <<= 8;
+		y -= x;
+		x <<= 8;
+		y ^= x;
+
+		z = y & 0x1F;
+		y = (y << z) | (y >> (32 - z));
+	}
+
+	return y;
+}
+
+/*
+ * gg_resolve() // funkcja wewnętrzna
+ *
+ * tworzy potok, forkuje się i w drugim procesie zaczyna resolvować 
+ * podanego hosta. zapisuje w sesji deskryptor potoku. jeśli coś tam
+ * będzie gotowego, znaczy, że można wczytać struct in_addr. jeśli
+ * nie znajdzie, zwraca INADDR_NONE.
+ *
+ *  - fd - wskaźnik gdzie wrzucić deskryptor
+ *  - pid - gdzie wrzucić pid procesu potomnego
+ *  - hostname - nazwa hosta do zresolvowania
+ *
+ * 0, -1.
+ */
+int gg_resolve(int *fd, int *pid, const char *hostname)
+{
+	int pipes[2], res;
+	struct in_addr a;
+	int errno2;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname);
+	
+	if (!fd || !pid) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (pipe(pipes) == -1)
+		return -1;
+
+	if ((res = fork()) == -1) {
+		errno2 = errno;
+		close(pipes[0]);
+		close(pipes[1]);
+		errno = errno2;
+		return -1;
+	}
+
+	if (!res) {
+		if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+			struct in_addr *hn;
+		
+			if (!(hn = gg_gethostbyname(hostname)))
+				a.s_addr = INADDR_NONE;
+			else {
+				a.s_addr = hn->s_addr;
+				free(hn);
+			}
+		}
+
+		write(pipes[1], &a, sizeof(a));
+
+		exit(0);
+	}
+
+	close(pipes[1]);
+
+	*fd = pipes[0];
+	*pid = res;
+
+	return 0;
+}
+
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+
+struct gg_resolve_pthread_data {
+	char *hostname;
+	int fd;
+};
+
+static void *gg_resolve_pthread_thread(void *arg)
+{
+	struct gg_resolve_pthread_data *d = arg;
+	struct in_addr a;
+
+	pthread_detach(pthread_self());
+
+	if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
+		struct in_addr *hn;
+		
+		if (!(hn = gg_gethostbyname(d->hostname)))
+			a.s_addr = INADDR_NONE;
+		else {
+			a.s_addr = hn->s_addr;
+			free(hn);
+		}
+	}
+
+	write(d->fd, &a, sizeof(a));
+	close(d->fd);
+
+	free(d->hostname);
+	d->hostname = NULL;
+
+	free(d);
+
+	pthread_exit(NULL);
+
+	return NULL;	/* żeby kompilator nie marudził */
+}
+
+/*
+ * gg_resolve_pthread() // funkcja wewnętrzna
+ *
+ * tworzy potok, nowy wątek i w nim zaczyna resolvować podanego hosta.
+ * zapisuje w sesji deskryptor potoku. jeśli coś tam będzie gotowego,
+ * znaczy, że można wczytać struct in_addr. jeśli nie znajdzie, zwraca
+ * INADDR_NONE.
+ *
+ *  - fd - wskaźnik do zmiennej przechowującej desktyptor resolvera
+ *  - resolver - wskaźnik do wskaźnika resolvera
+ *  - hostname - nazwa hosta do zresolvowania
+ *
+ * 0, -1.
+ */
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
+{
+	struct gg_resolve_pthread_data *d = NULL;
+	pthread_t *tmp;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname);
+	
+	if (!resolver || !fd || !hostname) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (!(tmp = malloc(sizeof(pthread_t)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n");
+		return -1;
+	}
+	
+	if (pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		free(tmp);
+		return -1;
+	}
+
+	if (!(d = malloc(sizeof(*d)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+	
+	d->hostname = NULL;
+
+	if (!(d->hostname = strdup(hostname))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	d->fd = pipes[1];
+
+	if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp);
+
+	*resolver = tmp;
+
+	*fd = pipes[0];
+
+	return 0;
+
+cleanup:
+	if (d) {
+		free(d->hostname);
+		free(d);
+	}
+
+	close(pipes[0]);
+	close(pipes[1]);
+
+	free(tmp);
+
+	errno = new_errno;
+
+	return -1;
+}
+
+#endif
+
+/*
+ * gg_read() // funkcja pomocnicza
+ *
+ * czyta z gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy
+ * połączenie zwykłe czy TLS.
+ *
+ *  - sess - sesja,
+ *  - buf - bufor,
+ *  - length - ilość bajtów,
+ *
+ * takie same wartości jak read().
+ */
+int gg_read(struct gg_session *sess, char *buf, int length)
+{
+	int res;
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+	if (sess->ssl) {
+		int err;
+
+		res = SSL_read(sess->ssl, buf, length);
+
+		if (res < 0) {
+			err = SSL_get_error(sess->ssl, res);
+
+			if (err == SSL_ERROR_WANT_READ)
+				errno = EAGAIN;
+
+			return -1;
+		}
+	} else
+#endif
+		res = read(sess->fd, buf, length);
+
+	return res;
+}
+
+/*
+ * gg_write() // funkcja pomocnicza
+ *
+ * zapisuje do gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy
+ * połączenie zwykłe czy TLS.
+ *
+ *  - sess - sesja,
+ *  - buf - bufor,
+ *  - length - ilość bajtów,
+ *
+ * takie same wartości jak write().
+ */
+int gg_write(struct gg_session *sess, const char *buf, int length)
+{
+	int res = 0;
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+	if (sess->ssl) {
+		int err;
+
+		res = SSL_write(sess->ssl, buf, length);
+
+		if (res < 0) {
+			err = SSL_get_error(sess->ssl, res);
+
+			if (err == SSL_ERROR_WANT_WRITE)
+				errno = EAGAIN;
+
+			return -1;
+		}
+	} else
+#endif
+	{
+		int written = 0;
+		
+		while (written < length) {
+			res = write(sess->fd, buf + written, length - written);
+
+			if (res == -1) {
+				if (errno == EAGAIN)
+					continue;
+				else
+					break;
+			} else {
+				written += res;
+				res = written;
+			}
+		}
+	}
+
+	return res;
+}
+
+/*
+ * gg_recv_packet() // funkcja wewnętrzna
+ *
+ * odbiera jeden pakiet i zwraca wskaźnik do niego. pamięć po nim
+ * należy zwolnić za pomocą free().
+ *
+ *  - sess - opis sesji
+ *
+ * w przypadku błędu NULL, kod błędu w errno. należy zwrócić uwagę, że gdy
+ * połączenie jest nieblokujące, a kod błędu wynosi EAGAIN, nie udało się
+ * odczytać całego pakietu i nie należy tego traktować jako błąd.
+ */
+void *gg_recv_packet(struct gg_session *sess)
+{
+	struct gg_header h;
+	char *buf = NULL;
+	int ret = 0;
+	unsigned int offset, size = 0;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
+	
+	if (!sess) {
+		errno = EFAULT;
+		return NULL;
+	}
+
+	if (sess->recv_left < 1) {
+		if (sess->header_buf) {
+			memcpy(&h, sess->header_buf, sess->header_done);
+			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done);
+			free(sess->header_buf);
+			sess->header_buf = NULL;
+		} else
+			sess->header_done = 0;
+
+		while (sess->header_done < sizeof(h)) {
+			ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done);
+
+			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret);
+
+			if (!ret) {
+				errno = ECONNRESET;
+				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n");
+				return NULL;
+			}
+
+			if (ret == -1) {
+				if (errno == EINTR) {
+					gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n");
+					continue;
+				}
+
+				if (errno == EAGAIN) {
+					gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n");
+
+					if (!(sess->header_buf = malloc(sess->header_done))) {
+						gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n");
+						return NULL;
+					}
+
+					memcpy(sess->header_buf, &h, sess->header_done);
+
+					return NULL;
+				}
+
+				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
+
+				return NULL;
+			}
+
+			sess->header_done += ret;
+
+		}
+
+		h.type = gg_fix32(h.type);
+		h.length = gg_fix32(h.length);
+	} else
+		memcpy(&h, sess->recv_buf, sizeof(h));
+	
+	/* jakieś sensowne limity na rozmiar pakietu */
+	if (h.length > 65535) {
+		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length);
+		errno = ERANGE;
+		return NULL;
+	}
+
+	if (sess->recv_left > 0) {
+		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n");
+		size = sess->recv_left;
+		offset = sess->recv_done;
+		buf = sess->recv_buf;
+	} else {
+		if (!(buf = malloc(sizeof(h) + h.length + 1))) {
+			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
+			return NULL;
+		}
+
+		memcpy(buf, &h, sizeof(h));
+
+		offset = 0;
+		size = h.length;
+	}
+
+	while (size > 0) {
+		ret = gg_read(sess, buf + sizeof(h) + offset, size);
+		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret);
+		if (!ret) {
+			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
+			errno = ECONNRESET;
+			return NULL;
+		}
+		if (ret > -1 && ret <= size) {
+			offset += ret;
+			size -= ret;
+		} else if (ret == -1) {	
+			int errno2 = errno;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno));
+			errno = errno2;
+
+			if (errno == EAGAIN) {
+				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size);
+				sess->recv_buf = buf;
+				sess->recv_left = size;
+				sess->recv_done = offset;
+				return NULL;
+			}
+			if (errno != EINTR) {
+				free(buf);
+				return NULL;
+			}
+		}
+	}
+
+	sess->recv_left = 0;
+
+	if ((gg_debug_level & GG_DEBUG_DUMP)) {
+		unsigned int i;
+
+		gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type);
+		for (i = 0; i < sizeof(h) + h.length; i++) 
+			gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
+		gg_debug(GG_DEBUG_DUMP, "\n");
+	}
+
+	return buf;
+}
+
+/*
+ * gg_send_packet() // funkcja wewnętrzna
+ *
+ * konstruuje pakiet i wysyła go do serwera.
+ *
+ *  - sock - deskryptor gniazda
+ *  - type - typ pakietu
+ *  - payload_1 - pierwsza część pakietu
+ *  - payload_length_1 - długość pierwszej części
+ *  - payload_2 - druga część pakietu
+ *  - payload_length_2 - długość drugiej części
+ *  - ... - kolejne części pakietu i ich długości
+ *  - NULL - końcowym parametr (konieczny!)
+ *
+ * jeśli się powiodło, zwraca 0, w przypadku błędu -1. jeśli errno == ENOMEM,
+ * zabrakło pamięci. inaczej był błąd przy wysyłaniu pakietu. dla errno == 0
+ * nie wysłano całego pakietu.
+ */
+int gg_send_packet(struct gg_session *sess, int type, ...)
+{
+	struct gg_header *h;
+	char *tmp;
+	unsigned int tmp_length;
+	void *payload;
+	unsigned int payload_length;
+	va_list ap;
+	int res;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type);
+
+	tmp_length = sizeof(struct gg_header);
+
+	if (!(tmp = malloc(tmp_length))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
+		return -1;
+	}
+
+	va_start(ap, type);
+
+	payload = va_arg(ap, void *);
+
+	while (payload) {
+		char *tmp2;
+
+		payload_length = va_arg(ap, unsigned int);
+
+		if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
+			gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n");
+			free(tmp);
+			va_end(ap);
+			return -1;
+		}
+
+		tmp = tmp2;
+		
+		memcpy(tmp + tmp_length, payload, payload_length);
+		tmp_length += payload_length;
+
+		payload = va_arg(ap, void *);
+	}
+
+	va_end(ap);
+
+	h = (struct gg_header*) tmp;
+	h->type = gg_fix32(type);
+	h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
+
+	if ((gg_debug_level & GG_DEBUG_DUMP)) {
+		unsigned int i;
+
+		gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
+		for (i = 0; i < tmp_length; ++i)
+			gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
+		gg_debug(GG_DEBUG_DUMP, "\n");
+	}
+	
+	if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) {
+		gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
+		free(tmp);
+		return -1;
+	}
+	
+	free(tmp);	
+	return 0;
+}
+
+/*
+ * gg_session_callback() // funkcja wewnętrzna
+ *
+ * wywoływany z gg_session->callback, wykonuje gg_watch_fd() i pakuje
+ * do gg_session->event jego wynik.
+ */
+static int gg_session_callback(struct gg_session *s)
+{
+	if (!s) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1;
+}
+
+/*
+ * gg_login()
+ *
+ * rozpoczyna procedurę łączenia się z serwerem. resztę obsługuje się przez
+ * gg_watch_fd().
+ *
+ * UWAGA! program musi obsłużyć SIGCHLD, jeśli łączy się asynchronicznie,
+ * żeby poprawnie zamknąć proces resolvera.
+ *
+ *  - p - struktura opisująca początkowy stan. wymagane pola: uin, 
+ *    password
+ *
+ * w przypadku błędu NULL, jeśli idzie dobrze (async) albo poszło
+ * dobrze (sync), zwróci wskaźnik do zaalokowanej struct gg_session.
+ */
+struct gg_session *gg_login(const struct gg_login_params *p)
+{
+	struct gg_session *sess = NULL;
+	char *hostname;
+	int port;
+
+	if (!p) {
+		gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
+		errno = EFAULT;
+		return NULL;
+	}
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);
+
+	if (!(sess = malloc(sizeof(struct gg_session)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
+		goto fail;
+	}
+
+	memset(sess, 0, sizeof(struct gg_session));
+
+	if (!p->password || !p->uin) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
+		errno = EFAULT;
+		goto fail;
+	}
+
+	if (!(sess->password = strdup(p->password))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
+		goto fail;
+	}
+
+	if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
+		goto fail;
+	}
+
+	sess->uin = p->uin;
+	sess->state = GG_STATE_RESOLVING;
+	sess->check = GG_CHECK_READ;
+	sess->timeout = GG_DEFAULT_TIMEOUT;
+	sess->async = p->async;
+	sess->type = GG_SESSION_GG;
+	sess->initial_status = p->status;
+	sess->callback = gg_session_callback;
+	sess->destroy = gg_free_session;
+	sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT);
+	sess->server_addr = p->server_addr;
+	sess->external_port = p->external_port;
+	sess->external_addr = p->external_addr;
+	sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION;
+	if (p->era_omnix)
+		sess->protocol_version |= GG_ERA_OMNIX_MASK;
+	if (p->has_audio)
+		sess->protocol_version |= GG_HAS_AUDIO_MASK;
+	sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL;
+	sess->last_sysmsg = p->last_sysmsg;
+	sess->image_size = p->image_size;
+	sess->pid = -1;
+
+	if (p->tls == 1) {
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+		char buf[1024];
+
+		OpenSSL_add_ssl_algorithms();
+
+		if (!RAND_status()) {
+			char rdata[1024];
+			struct {
+				time_t time;
+				void *ptr;
+			} rstruct;
+
+			time(&rstruct.time);
+			rstruct.ptr = (void *) &rstruct;			
+
+			RAND_seed((void *) rdata, sizeof(rdata));
+			RAND_seed((void *) &rstruct, sizeof(rstruct));
+		}
+
+		sess->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
+
+		if (!sess->ssl_ctx) {
+			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+			gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf);
+			goto fail;
+		}
+
+		SSL_CTX_set_verify(sess->ssl_ctx, SSL_VERIFY_NONE, NULL);
+
+		sess->ssl = SSL_new(sess->ssl_ctx);
+
+		if (!sess->ssl) {
+			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+			gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf);
+			goto fail;
+		}
+#else
+		gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
+#endif
+	}
+	
+	if (gg_proxy_enabled) {
+		hostname = gg_proxy_host;
+		sess->proxy_port = port = gg_proxy_port;
+	} else {
+		hostname = GG_APPMSG_HOST;
+		port = GG_APPMSG_PORT;
+	}
+
+	if (!p->async) {
+		struct in_addr a;
+
+		if (!p->server_addr || !p->server_port) {
+			if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+				struct in_addr *hn;
+	
+				if (!(hn = gg_gethostbyname(hostname))) {
+					gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname);
+					goto fail;
+				} else {
+					a.s_addr = hn->s_addr;
+					free(hn);
+				}
+			}
+		} else {
+			a.s_addr = p->server_addr;
+			port = p->server_port;
+		}
+
+		sess->hub_addr = a.s_addr;
+
+		if (gg_proxy_enabled)
+			sess->proxy_addr = a.s_addr;
+
+		if ((sess->fd = gg_connect(&a, port, 0)) == -1) {
+			gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+			goto fail;
+		}
+
+		if (p->server_addr && p->server_port)
+			sess->state = GG_STATE_CONNECTING_GG;
+		else
+			sess->state = GG_STATE_CONNECTING_HUB;
+
+		while (sess->state != GG_STATE_CONNECTED) {
+			struct gg_event *e;
+
+			if (!(e = gg_watch_fd(sess))) {
+				gg_debug(GG_DEBUG_MISC, "// gg_login() critical error in gg_watch_fd()\n");
+				goto fail;
+			}
+
+			if (e->type == GG_EVENT_CONN_FAILED) {
+				errno = EACCES;
+				gg_debug(GG_DEBUG_MISC, "// gg_login() could not login\n");
+				gg_event_free(e);
+				goto fail;
+			}
+
+			gg_event_free(e);
+		}
+
+		return sess;
+	}
+	
+	if (!sess->server_addr || gg_proxy_enabled) {
+#ifndef __GG_LIBGADU_HAVE_PTHREAD
+		if (gg_resolve(&sess->fd, &sess->pid, hostname)) {
+#else
+		if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) {
+#endif
+			gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
+			goto fail;
+		}
+	} else {
+		if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
+			gg_debug(GG_DEBUG_MISC, "// gg_login() direct connection failed (errno=%d, %s)\n", errno, strerror(errno));
+			goto fail;
+		}
+		sess->state = GG_STATE_CONNECTING_GG;
+		sess->check = GG_CHECK_WRITE;
+	}
+
+	return sess;
+
+fail:
+	if (sess) {
+		if (sess->password)
+			free(sess->password);
+		if (sess->initial_descr)
+			free(sess->initial_descr);
+		free(sess);
+	}
+	
+	return NULL;
+}
+
+/* 
+ * gg_free_session()
+ *
+ * próbuje zamknąć połączenia i zwalnia pamięć zajmowaną przez sesję.
+ *
+ *  - sess - opis sesji
+ */
+void gg_free_session(struct gg_session *sess)
+{
+	if (!sess)
+		return;
+
+	/* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
+
+	if (sess->password)
+		free(sess->password);
+	
+	if (sess->initial_descr)
+		free(sess->initial_descr);
+
+	if (sess->client_version)
+		free(sess->client_version);
+
+	if (sess->header_buf)
+		free(sess->header_buf);
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+	if (sess->ssl)
+		SSL_free(sess->ssl);
+
+	if (sess->ssl_ctx)
+		SSL_CTX_free(sess->ssl_ctx);
+#endif
+
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+	if (sess->resolver) {
+		pthread_cancel(*((pthread_t*) sess->resolver));
+		free(sess->resolver);
+		sess->resolver = NULL;
+	}
+#else
+	if (sess->pid != -1)
+		waitpid(sess->pid, NULL, WNOHANG);
+#endif
+
+	if (sess->fd != -1)
+		close(sess->fd);
+
+	while (sess->images)
+		gg_image_queue_remove(sess, sess->images, 1);
+
+	free(sess);
+}
+
+/*
+ * gg_change_status()
+ *
+ * zmienia status użytkownika. przydatne do /away i /busy oraz /quit.
+ *
+ *  - sess - opis sesji
+ *  - status - nowy status użytkownika
+ *
+ * 0, -1.
+ */
+int gg_change_status(struct gg_session *sess, int status)
+{
+	struct gg_new_status p;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	p.status = gg_fix32(status);
+
+	sess->status = status;
+
+	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL);
+}
+
+/*
+ * gg_change_status_descr()
+ *
+ * zmienia status użytkownika na opisowy.
+ *
+ *  - sess - opis sesji
+ *  - status - nowy status użytkownika
+ *  - descr - opis statusu
+ *
+ * 0, -1.
+ */
+int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
+{
+	struct gg_new_status p;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
+
+	if (!sess || !descr) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	p.status = gg_fix32(status);
+
+	sess->status = status;
+
+	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL);
+}
+
+/*
+ * gg_change_status_descr_time()
+ *
+ * zmienia status użytkownika na opisowy z godziną powrotu.
+ *
+ *  - sess - opis sesji
+ *  - status - nowy status użytkownika
+ *  - descr - opis statusu
+ *  - time - czas w formacie uniksowym
+ *
+ * 0, -1.
+ */
+int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time)
+{
+	struct gg_new_status p;
+	uint32_t newtime;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+
+	if (!sess || !descr || !time) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	p.status = gg_fix32(status);
+
+	sess->status = status;
+
+	newtime = gg_fix32(time);
+
+	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL);
+}
+
+/*
+ * gg_logoff()
+ *
+ * wylogowuje użytkownika i zamyka połączenie, ale nie zwalnia pamięci.
+ *
+ *  - sess - opis sesji
+ */
+void gg_logoff(struct gg_session *sess)
+{
+	if (!sess)
+		return;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
+
+	if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK))
+		gg_change_status(sess, GG_STATUS_NOT_AVAIL);
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+	if (sess->ssl)
+		SSL_shutdown(sess->ssl);
+#endif
+
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+	if (sess->resolver) {
+		pthread_cancel(*((pthread_t*) sess->resolver));
+		free(sess->resolver);
+		sess->resolver = NULL;
+	}
+#else
+	if (sess->pid != -1) {
+		waitpid(sess->pid, NULL, WNOHANG);
+		sess->pid = -1;
+	}
+#endif
+	
+	if (sess->fd != -1) {
+		shutdown(sess->fd, SHUT_RDWR);
+		close(sess->fd);
+		sess->fd = -1;
+	}
+}
+
+/*
+ * gg_image_request()
+ *
+ * wysyła żądanie wysłania obrazka o podanych parametrach.
+ *
+ *  - sess - opis sesji
+ *  - recipient - numer adresata
+ *  - size - rozmiar obrazka
+ *  - crc32 - suma kontrolna obrazka
+ *
+ * 0/-1
+ */
+int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
+{
+	struct gg_send_msg s;
+	struct gg_msg_image_request r;
+	char dummy = 0;
+	int res;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+	
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (size < 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	s.recipient = gg_fix32(recipient);
+	s.seq = gg_fix32(0);
+	s.msgclass = gg_fix32(GG_CLASS_MSG);
+
+	r.flag = 0x04;
+	r.size = gg_fix32(size);
+	r.crc32 = gg_fix32(crc32);
+	
+	res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);
+
+	if (!res) {
+		struct gg_image_queue *q = malloc(sizeof(*q));
+		char *buf;
+
+		if (!q) {
+			gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
+			return -1;
+		}
+
+		buf = malloc(size);
+		if (size && !buf)
+		{
+			gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
+			free(q);
+			return -1;
+		}
+
+		memset(q, 0, sizeof(*q));
+
+		q->sender = recipient;
+		q->size = size;
+		q->crc32 = crc32;
+		q->image = buf;
+
+		if (!sess->images)
+			sess->images = q;
+		else {
+			struct gg_image_queue *qq;
+
+			for (qq = sess->images; qq->next; qq = qq->next)
+				;
+
+			qq->next = q;
+		}
+	}
+
+	return res;
+}
+
+/*
+ * gg_image_reply()
+ *
+ * wysyła żądany obrazek.
+ *
+ *  - sess - opis sesji
+ *  - recipient - numer adresata
+ *  - filename - nazwa pliku
+ *  - image - bufor z obrazkiem
+ *  - size - rozmiar obrazka
+ *
+ * 0/-1
+ */
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
+{
+	struct gg_msg_image_reply *r;
+	struct gg_send_msg s;
+	const char *tmp;
+	char buf[1910];
+	int res = -1;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);
+
+	if (!sess || !filename || !image) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (size < 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	/* wytnij ścieżki, zostaw tylko nazwę pliku */
+	while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
+		filename = tmp + 1;
+
+	if (strlen(filename) < 1 || strlen(filename) > 1024) {
+		errno = EINVAL;
+		return -1;
+	}
+	
+	s.recipient = gg_fix32(recipient);
+	s.seq = gg_fix32(0);
+	s.msgclass = gg_fix32(GG_CLASS_MSG);
+
+	buf[0] = 0;
+	r = (void*) &buf[1];
+
+	r->flag = 0x05;
+	r->size = gg_fix32(size);
+	r->crc32 = gg_fix32(gg_crc32(0, image, size));
+
+	while (size > 0) {
+		int buflen, chunklen;
+		
+		/* \0 + struct gg_msg_image_reply */
+		buflen = sizeof(struct gg_msg_image_reply) + 1;
+
+		/* w pierwszym kawałku jest nazwa pliku */
+		if (r->flag == 0x05) {
+			strcpy(buf + buflen, filename);
+			buflen += strlen(filename) + 1;
+		}
+
+		chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size;
+
+		memcpy(buf + buflen, image, chunklen);
+		size -= chunklen;
+		image += chunklen;
+		
+		res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL);
+
+		if (res == -1)
+			break;
+
+		r->flag = 0x06;
+	}
+
+	return res;
+}
+
+/*
+ * gg_send_message_ctcp()
+ *
+ * wysyła wiadomość do innego użytkownika. zwraca losowy numer
+ * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
+ *
+ *  - sess - opis sesji
+ *  - msgclass - rodzaj wiadomości
+ *  - recipient - numer adresata
+ *  - message - treść wiadomości
+ *  - message_len - długość
+ *
+ * numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ */
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
+{
+	struct gg_send_msg s;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+	
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	s.recipient = gg_fix32(recipient);
+	s.seq = gg_fix32(0);
+	s.msgclass = gg_fix32(msgclass);
+	
+	return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
+}
+
+/*
+ * gg_send_message()
+ *
+ * wysyła wiadomość do innego użytkownika. zwraca losowy numer
+ * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
+ *
+ *  - sess - opis sesji
+ *  - msgclass - rodzaj wiadomości
+ *  - recipient - numer adresata
+ *  - message - treść wiadomości
+ *
+ * numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ */
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);
+
+	return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0);
+}
+
+/*
+ * gg_send_message_richtext()
+ *
+ * wysyła kolorową wiadomość do innego użytkownika. zwraca losowy numer
+ * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
+ *
+ *  - sess - opis sesji
+ *  - msgclass - rodzaj wiadomości
+ *  - recipient - numer adresata
+ *  - message - treść wiadomości
+ *  - format - informacje o formatowaniu
+ *  - formatlen - długość informacji o formatowaniu
+ *
+ * numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ */
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+	struct gg_send_msg s;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (!message) {
+		errno = EFAULT;
+		return -1;
+	}
+	
+	s.recipient = gg_fix32(recipient);
+	if (!sess->seq)
+		sess->seq = 0x01740000 | (rand() & 0xffff);
+	s.seq = gg_fix32(sess->seq);
+	s.msgclass = gg_fix32(msgclass);
+	sess->seq += (rand() % 0x300) + 0x300;
+	
+	if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, format, formatlen, NULL) == -1)
+		return -1;
+
+	return gg_fix32(s.seq);
+}
+
+/*
+ * gg_send_message_confer()
+ *
+ * wysyła wiadomość do kilku użytkownikow (konferencja). zwraca losowy numer
+ * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
+ *
+ *  - sess - opis sesji
+ *  - msgclass - rodzaj wiadomości
+ *  - recipients_count - ilość adresatów
+ *  - recipients - numerki adresatów
+ *  - message - treść wiadomości
+ *
+ * numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ */
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message);
+
+	return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0);
+}
+
+/*
+ * gg_send_message_confer_richtext()
+ *
+ * wysyła kolorową wiadomość do kilku użytkownikow (konferencja). zwraca
+ * losowy numer sekwencyjny, który można zignorować albo wykorzystać do
+ * potwierdzenia.
+ *
+ *  - sess - opis sesji
+ *  - msgclass - rodzaj wiadomości
+ *  - recipients_count - ilość adresatów
+ *  - recipients - numerki adresatów
+ *  - message - treść wiadomości
+ *  - format - informacje o formatowaniu
+ *  - formatlen - długość informacji o formatowaniu
+ *
+ * numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ */
+int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+	struct gg_send_msg s;
+	struct gg_msg_recipients r;
+	int i, j, k;
+	uin_t *recps;
+		
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) {
+		errno = EINVAL;
+		return -1;
+	}
+	
+	r.flag = 0x01;
+	r.count = gg_fix32(recipients_count - 1);
+	
+	if (!sess->seq)
+		sess->seq = 0x01740000 | (rand() & 0xffff);
+	s.seq = gg_fix32(sess->seq);
+	s.msgclass = gg_fix32(msgclass);
+
+	recps = malloc(sizeof(uin_t) * recipients_count);
+	if (!recps)
+		return -1;
+
+	for (i = 0; i < recipients_count; i++) {
+	 
+		s.recipient = gg_fix32(recipients[i]);
+		
+		for (j = 0, k = 0; j < recipients_count; j++)
+			if (recipients[j] != recipients[i]) {
+				recps[k] = gg_fix32(recipients[j]);
+				k++;
+			}
+				
+		if (!i)
+			sess->seq += (rand() % 0x300) + 0x300;
+		
+		if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) {
+			free(recps);
+			return -1;
+		}
+	}
+
+	free(recps);
+	
+	return gg_fix32(s.seq);
+}
+
+/*
+ * gg_ping()
+ *
+ * wysyła do serwera pakiet ping.
+ *
+ *  - sess - opis sesji
+ *
+ * 0, -1.
+ */
+int gg_ping(struct gg_session *sess)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	return gg_send_packet(sess, GG_PING, NULL);
+}
+
+/*
+ * gg_notify_ex()
+ *
+ * wysyła serwerowi listę kontaktów (wraz z odpowiadającymi im typami userów),
+ * dzięki czemu wie, czyj stan nas interesuje.
+ *
+ *  - sess - opis sesji
+ *  - userlist - wskaźnik do tablicy numerów
+ *  - types - wskaźnik do tablicy typów użytkowników
+ *  - count - ilość numerków
+ *
+ * 0, -1.
+ */
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
+{
+	struct gg_notify *n;
+	uin_t *u;
+	char *t;
+	int i, res = 0;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
+	
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+	
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (!userlist || !count)
+		return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
+	
+	while (count > 0) {
+		int part_count, packet_type;
+		
+		if (count > 400) {
+			part_count = 400;
+			packet_type = GG_NOTIFY_FIRST;
+		} else {
+			part_count = count;
+			packet_type = GG_NOTIFY_LAST;
+		}
+
+		if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
+			return -1;
+	
+		for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { 
+			n[i].uin = gg_fix32(*u);
+			n[i].dunno1 = *t;
+		}
+	
+		if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
+			free(n);
+			res = -1;
+			break;
+		}
+
+		count -= part_count;
+		userlist += part_count;
+		types += part_count;
+
+		free(n);
+	}
+
+	return res;
+}
+
+/*
+ * gg_notify()
+ *
+ * wysyła serwerowi listę kontaktów, dzięki czemu wie, czyj stan nas
+ * interesuje.
+ *
+ *  - sess - opis sesji
+ *  - userlist - wskaźnik do tablicy numerów
+ *  - count - ilość numerków
+ *
+ * 0, -1.
+ */
+int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
+{
+	struct gg_notify *n;
+	uin_t *u;
+	int i, res = 0;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count);
+	
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+	
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (!userlist || !count)
+		return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
+	
+	while (count > 0) {
+		int part_count, packet_type;
+		
+		if (count > 400) {
+			part_count = 400;
+			packet_type = GG_NOTIFY_FIRST;
+		} else {
+			part_count = count;
+			packet_type = GG_NOTIFY_LAST;
+		}
+			
+		if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
+			return -1;
+	
+		for (u = userlist, i = 0; i < part_count; u++, i++) { 
+			n[i].uin = gg_fix32(*u);
+			n[i].dunno1 = GG_USER_NORMAL;
+		}
+	
+		if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
+			res = -1;
+			free(n);
+			break;
+		}
+
+		free(n);
+
+		userlist += part_count;
+		count -= part_count;
+	}
+
+	return res;
+}
+
+/*
+ * gg_add_notify_ex()
+ *
+ * dodaje do listy kontaktów dany numer w trakcie połączenia.
+ * dodawanemu użytkownikowi określamy jego typ (patrz protocol.html)
+ *
+ *  - sess - opis sesji
+ *  - uin - numer
+ *  - type - typ
+ *
+ * 0, -1.
+ */
+int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
+{
+	struct gg_add_remove a;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
+	
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+	
+	a.uin = gg_fix32(uin);
+	a.dunno1 = type;
+	
+	return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
+}
+
+/*
+ * gg_add_notify()
+ *
+ * dodaje do listy kontaktów dany numer w trakcie połączenia.
+ *
+ *  - sess - opis sesji
+ *  - uin - numer
+ *
+ * 0, -1.
+ */
+int gg_add_notify(struct gg_session *sess, uin_t uin)
+{
+	return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
+}
+
+/*
+ * gg_remove_notify_ex()
+ *
+ * usuwa z listy kontaktów w trakcie połączenia.
+ * usuwanemu użytkownikowi określamy jego typ (patrz protocol.html)
+ *
+ *  - sess - opis sesji
+ *  - uin - numer
+ *  - type - typ
+ *
+ * 0, -1.
+ */
+int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
+{
+	struct gg_add_remove a;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
+	
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	a.uin = gg_fix32(uin);
+	a.dunno1 = type;
+	
+	return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
+}
+
+/*
+ * gg_remove_notify()
+ *
+ * usuwa z listy kontaktów w trakcie połączenia.
+ *
+ *  - sess - opis sesji
+ *  - uin - numer
+ *
+ * 0, -1.
+ */
+int gg_remove_notify(struct gg_session *sess, uin_t uin)
+{
+	return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
+}
+
+/*
+ * gg_userlist_request()
+ *
+ * wysyła żądanie/zapytanie listy kontaktów na serwerze.
+ *
+ *  - sess - opis sesji
+ *  - type - rodzaj zapytania/żądania
+ *  - request - treść zapytania/żądania (może być NULL)
+ *
+ * 0, -1
+ */
+int gg_userlist_request(struct gg_session *sess, char type, const char *request)
+{
+	int len;
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+	
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (!request) {
+		sess->userlist_blocks = 1;
+		return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
+	}
+	
+	len = strlen(request);
+
+	sess->userlist_blocks = 0;
+
+	while (len > 2047) {
+		sess->userlist_blocks++;
+
+		if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
+			return -1;
+
+		if (type == GG_USERLIST_PUT)
+			type = GG_USERLIST_PUT_MORE;
+
+		request += 2047;
+		len -= 2047;
+	}
+
+	sess->userlist_blocks++;
+
+	return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/libgadu.h	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,1308 @@
+/* $Id: libgadu.h 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Piotr Wysocki <wysek@linux.bydg.org>
+ *                          Dawid Jarosz <dawjar@poczta.onet.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifndef __GG_LIBGADU_H
+#define __GG_LIBGADU_H
+
+#ifdef __cplusplus
+#ifdef _WIN32
+#pragma pack(push, 1)
+#endif
+extern "C" {
+#endif
+
+#include <libgadu-config.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#include <openssl/ssl.h>
+#endif
+
+/*
+ * typedef uin_t
+ *
+ * typ reprezentujący numer osoby.
+ */
+typedef uint32_t uin_t;
+
+/*
+ * ogólna struktura opisująca różne sesje. przydatna w klientach.
+ */
+#define gg_common_head(x) \
+	int fd;			/* podglądany deskryptor */ \
+	int check;		/* sprawdzamy zapis czy odczyt */ \
+	int state;		/* aktualny stan maszynki */ \
+	int error;		/* kod błędu dla GG_STATE_ERROR */ \
+	int type;		/* rodzaj sesji */ \
+	int id;			/* identyfikator */ \
+	int timeout;		/* sugerowany timeout w sekundach */ \
+	int (*callback)(x*); 	/* callback przy zmianach */ \
+	void (*destroy)(x*); 	/* funkcja niszczenia */
+
+struct gg_common {
+	gg_common_head(struct gg_common)
+};
+
+struct gg_image_queue;
+
+/*
+ * struct gg_session
+ *
+ * struktura opisująca daną sesję. tworzona przez gg_login(), zwalniana
+ * przez gg_free_session().
+ */
+struct gg_session {
+	gg_common_head(struct gg_session)
+
+	int async;      	/* czy połączenie jest asynchroniczne */
+	int pid;        	/* pid procesu resolvera */
+	int port;       	/* port, z którym się łączymy */
+	int seq;        	/* numer sekwencyjny ostatniej wiadomości */
+	int last_pong;  	/* czas otrzymania ostatniego ping/pong */
+	int last_event;		/* czas otrzymania ostatniego pakietu */
+
+	struct gg_event *event;	/* zdarzenie po ->callback() */
+
+	uint32_t proxy_addr;	/* adres proxy, keszowany */
+	uint16_t proxy_port;	/* port proxy */
+
+	uint32_t hub_addr;	/* adres huba po resolvnięciu */
+	uint32_t server_addr;	/* adres serwera, od huba */
+
+	uint32_t client_addr;	/* adres klienta */
+	uint16_t client_port;	/* port, na którym klient słucha */
+
+	uint32_t external_addr;	/* adres zewnetrzny klienta */
+	uint16_t external_port;	/* port zewnetrzny klienta */
+	
+	uin_t uin;		/* numerek klienta */
+	char *password;		/* i jego hasło. zwalniane automagicznie */
+        
+	int initial_status;	/* początkowy stan klienta */
+	int status;		/* aktualny stan klienta */
+
+	char *recv_buf;		/* bufor na otrzymywane pakiety */
+	int recv_done;		/* ile już wczytano do bufora */
+	int recv_left;		/* i ile jeszcze trzeba wczytać */
+
+	int protocol_version;	/* wersja używanego protokołu */
+	char *client_version;	/* wersja używanego klienta */
+	int last_sysmsg;	/* ostatnia wiadomość systemowa */
+
+	char *initial_descr;	/* początkowy opis stanu klienta */
+
+	void *resolver;		/* wskaźnik na informacje resolvera */
+
+	char *header_buf;	/* bufor na początek nagłówka */
+	unsigned int header_done;/* ile już mamy */
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+	SSL *ssl;		/* sesja TLS */
+	SSL_CTX *ssl_ctx;	/* kontekst sesji? */
+#else
+	void *ssl;		/* zachowujemy ABI */
+	void *ssl_ctx;
+#endif
+
+	int image_size;		/* maksymalny rozmiar obrazków w KiB */
+
+	char *userlist_reply;	/* fragment odpowiedzi listy kontaktów */
+
+	int userlist_blocks;	/* na ile kawałków podzielono listę kontaktów */
+
+	struct gg_image_queue *images;	/* aktualnie wczytywane obrazki */
+};
+
+/*
+ * struct gg_http
+ * 
+ * ogólna struktura opisująca stan wszystkich operacji HTTP. tworzona
+ * przez gg_http_connect(), zwalniana przez gg_http_free().
+ */
+struct gg_http {
+	gg_common_head(struct gg_http)
+
+	int async;              /* czy połączenie asynchroniczne */
+	int pid;                /* pid procesu resolvera */
+	int port;               /* port, z którym się łączymy */
+
+	char *query;            /* bufor zapytania http */
+	char *header;           /* bufor nagłówka */
+	int header_size;        /* rozmiar wczytanego nagłówka */
+	char *body;             /* bufor otrzymanych informacji */
+	unsigned int body_size; /* oczekiwana ilość informacji */
+
+	void *data;             /* dane danej operacji http */
+
+	char *user_data;	/* dane użytkownika, nie są zwalniane przez gg_http_free() */
+
+	void *resolver;		/* wskaźnik na informacje resolvera */
+
+	unsigned int body_done;	/* ile już treści odebrano? */
+};
+
+#ifdef __GNUC__
+#define GG_PACKED __attribute__ ((packed))
+#else
+#define GG_PACKED
+#endif
+
+#define GG_MAX_PATH 276
+
+/*
+ * struct gg_file_info
+ * 
+ * odpowiednik windowsowej struktury WIN32_FIND_DATA niezbędnej przy
+ * wysyłaniu plików.
+ */
+struct gg_file_info {
+	uint32_t mode;			/* dwFileAttributes */
+	uint32_t ctime[2];		/* ftCreationTime */
+	uint32_t atime[2];		/* ftLastAccessTime */
+	uint32_t mtime[2];		/* ftLastWriteTime */
+	uint32_t size_hi;		/* nFileSizeHigh */
+	uint32_t size;			/* nFileSizeLow */
+	uint32_t reserved0;		/* dwReserved0 */
+	uint32_t reserved1;		/* dwReserved1 */
+	unsigned char filename[GG_MAX_PATH - 14];	/* cFileName */
+	unsigned char short_filename[14];		/* cAlternateFileName */
+} GG_PACKED;
+
+/*
+ * struct gg_dcc
+ * 
+ * struktura opisująca nasłuchujące gniazdo połączeń między klientami.
+ * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free().
+ */
+struct gg_dcc {
+	gg_common_head(struct gg_dcc)
+
+	struct gg_event *event;	/* opis zdarzenia */
+
+	int active;		/* czy to my się łączymy? */
+	int port;		/* port, na którym siedzi */
+	uin_t uin;		/* uin klienta */
+	uin_t peer_uin;		/* uin drugiej strony */
+	int file_fd;		/* deskryptor pliku */
+	unsigned int offset;	/* offset w pliku */
+	unsigned int chunk_size;/* rozmiar kawałka */
+	unsigned int chunk_offset;/* offset w aktualnym kawałku */
+	struct gg_file_info file_info;
+				/* informacje o pliku */
+	int established;	/* połączenie ustanowione */
+	char *voice_buf;	/* bufor na pakiet połączenia głosowego */
+	int incoming;		/* połączenie przychodzące */
+	char *chunk_buf;	/* bufor na kawałek danych */
+	uint32_t remote_addr;	/* adres drugiej strony */
+	uint16_t remote_port;	/* port drugiej strony */
+};
+
+/*
+ * enum gg_session_t
+ *
+ * rodzaje sesji.
+ */
+enum gg_session_t {
+	GG_SESSION_GG = 1,	/* połączenie z serwerem gg */
+	GG_SESSION_HTTP,	/* ogólna sesja http */
+	GG_SESSION_SEARCH,	/* szukanie */
+	GG_SESSION_REGISTER,	/* rejestrowanie */
+	GG_SESSION_REMIND,	/* przypominanie hasła */
+	GG_SESSION_PASSWD,	/* zmiana hasła */
+	GG_SESSION_CHANGE,	/* zmiana informacji o sobie */
+	GG_SESSION_DCC,		/* ogólne połączenie DCC */
+	GG_SESSION_DCC_SOCKET,	/* nasłuchujący socket */
+	GG_SESSION_DCC_SEND,	/* wysyłanie pliku */
+	GG_SESSION_DCC_GET,	/* odbieranie pliku */
+	GG_SESSION_DCC_VOICE,	/* rozmowa głosowa */
+	GG_SESSION_USERLIST_GET,	/* pobieranie userlisty */
+	GG_SESSION_USERLIST_PUT,	/* wysyłanie userlisty */
+	GG_SESSION_UNREGISTER,	/* usuwanie konta */
+	GG_SESSION_USERLIST_REMOVE,	/* usuwanie userlisty */
+	GG_SESSION_TOKEN,	/* pobieranie tokenu */
+	
+	GG_SESSION_USER0 = 256,	/* zdefiniowana dla użytkownika */
+	GG_SESSION_USER1,	/* j.w. */
+	GG_SESSION_USER2,	/* j.w. */
+	GG_SESSION_USER3,	/* j.w. */
+	GG_SESSION_USER4,	/* j.w. */
+	GG_SESSION_USER5,	/* j.w. */
+	GG_SESSION_USER6,	/* j.w. */
+	GG_SESSION_USER7	/* j.w. */
+};
+
+/*
+ * enum gg_state_t
+ *
+ * opisuje stan asynchronicznej maszyny.
+ */
+enum gg_state_t {
+		/* wspólne */
+	GG_STATE_IDLE = 0,		/* nie powinno wystąpić. */
+	GG_STATE_RESOLVING,             /* wywołał gethostbyname() */
+	GG_STATE_CONNECTING,            /* wywołał connect() */
+	GG_STATE_READING_DATA,		/* czeka na dane http */
+	GG_STATE_ERROR,			/* wystąpił błąd. kod w x->error */
+
+		/* gg_session */
+	GG_STATE_CONNECTING_HUB,	/* wywołał connect() na huba */
+	GG_STATE_CONNECTING_GG,         /* wywołał connect() na serwer */
+	GG_STATE_READING_KEY,           /* czeka na klucz */
+	GG_STATE_READING_REPLY,         /* czeka na odpowiedź */
+	GG_STATE_CONNECTED,             /* połączył się */
+
+		/* gg_http */
+	GG_STATE_SENDING_QUERY,		/* wysyła zapytanie http */
+	GG_STATE_READING_HEADER,	/* czeka na nagłówek http */
+	GG_STATE_PARSING,               /* przetwarza dane */
+	GG_STATE_DONE,                  /* skończył */
+
+		/* gg_dcc */
+	GG_STATE_LISTENING,		/* czeka na połączenia */
+	GG_STATE_READING_UIN_1,		/* czeka na uin peera */
+	GG_STATE_READING_UIN_2,		/* czeka na swój uin */
+	GG_STATE_SENDING_ACK,		/* wysyła potwierdzenie dcc */
+	GG_STATE_READING_ACK,		/* czeka na potwierdzenie dcc */
+	GG_STATE_READING_REQUEST,	/* czeka na komendę */
+	GG_STATE_SENDING_REQUEST,	/* wysyła komendę */
+	GG_STATE_SENDING_FILE_INFO,	/* wysyła informacje o pliku */
+	GG_STATE_READING_PRE_FILE_INFO,	/* czeka na pakiet przed file_info */
+	GG_STATE_READING_FILE_INFO,	/* czeka na informacje o pliku */
+	GG_STATE_SENDING_FILE_ACK,	/* wysyła potwierdzenie pliku */
+	GG_STATE_READING_FILE_ACK,	/* czeka na potwierdzenie pliku */
+	GG_STATE_SENDING_FILE_HEADER,	/* wysyła nagłówek pliku */
+	GG_STATE_READING_FILE_HEADER,	/* czeka na nagłówek */
+	GG_STATE_GETTING_FILE,		/* odbiera plik */
+	GG_STATE_SENDING_FILE,		/* wysyła plik */
+	GG_STATE_READING_VOICE_ACK,	/* czeka na potwierdzenie voip */
+	GG_STATE_READING_VOICE_HEADER,	/* czeka na rodzaj bloku voip */
+	GG_STATE_READING_VOICE_SIZE,	/* czeka na rozmiar bloku voip */
+	GG_STATE_READING_VOICE_DATA,	/* czeka na dane voip */
+	GG_STATE_SENDING_VOICE_ACK,	/* wysyła potwierdzenie voip */
+	GG_STATE_SENDING_VOICE_REQUEST,	/* wysyła żądanie voip */
+	GG_STATE_READING_TYPE,		/* czeka na typ połączenia */
+
+	/* nowe. bez sensu jest to API. */
+	GG_STATE_TLS_NEGOTIATION	/* negocjuje połączenie TLS */
+};
+
+/*
+ * enum gg_check_t
+ *
+ * informuje, co proces klienta powinien sprawdzić na deskryptorze danego
+ * połączenia.
+ */
+enum gg_check_t {
+	GG_CHECK_NONE = 0,		/* nic. nie powinno wystąpić */
+	GG_CHECK_WRITE = 1,		/* sprawdzamy możliwość zapisu */
+	GG_CHECK_READ = 2		/* sprawdzamy możliwość odczytu */
+};
+
+/*
+ * struct gg_login_params
+ *
+ * parametry gg_login(). przeniesiono do struktury, żeby uniknąć problemów
+ * z ciągłymi zmianami API, gdy dodano coś nowego do protokołu.
+ */
+struct gg_login_params {
+	uin_t uin;			/* numerek */
+	char *password;			/* hasło */
+	int async;			/* asynchroniczne sockety? */
+	int status;			/* początkowy status klienta */
+	char *status_descr;		/* opis statusu */
+	uint32_t server_addr;		/* adres serwera gg */
+	uint16_t server_port;		/* port serwera gg */
+	uint32_t client_addr;		/* adres dcc klienta */
+	uint16_t client_port;		/* port dcc klienta */
+	int protocol_version;		/* wersja protokołu */
+	char *client_version;		/* wersja klienta */
+	int has_audio;			/* czy ma dźwięk? */
+	int last_sysmsg;		/* ostatnia wiadomość systemowa */
+	uint32_t external_addr;		/* adres widziany na zewnatrz */
+	uint16_t external_port;		/* port widziany na zewnatrz */
+	int tls;			/* czy łączymy po TLS? */
+	int image_size;			/* maksymalny rozmiar obrazka w KiB */
+	int era_omnix;			/* czy udawać klienta era omnix? */
+
+	char dummy[6 * sizeof(int)];	/* miejsce na kolejnych 6 zmiennych,
+					 * żeby z dodaniem parametru nie 
+					 * zmieniał się rozmiar struktury */
+};
+
+struct gg_session *gg_login(const struct gg_login_params *p);
+void gg_free_session(struct gg_session *sess);
+void gg_logoff(struct gg_session *sess);
+int gg_change_status(struct gg_session *sess, int status);
+int gg_change_status_descr(struct gg_session *sess, int status, const char *descr);
+int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time);
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message);
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen);
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message);
+int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen);
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len);
+int gg_ping(struct gg_session *sess);
+int gg_userlist_request(struct gg_session *sess, char type, const char *request);
+int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size);
+
+uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
+
+struct gg_image_queue {
+	uin_t sender;			/* nadawca obrazka */
+	uint32_t size;			/* rozmiar */
+	uint32_t crc32;			/* suma kontrolna */
+	char *filename;			/* nazwa pliku */
+	char *image;			/* bufor z obrazem */
+	uint32_t done;			/* ile już wczytano */
+
+	struct gg_image_queue *next;	/* następny na liście */
+};
+
+/*
+ * enum gg_event_t
+ *
+ * rodzaje zdarzeń.
+ */
+enum gg_event_t {
+	GG_EVENT_NONE = 0,		/* nic się nie wydarzyło */
+	GG_EVENT_MSG,			/* otrzymano wiadomość */
+	GG_EVENT_NOTIFY,		/* ktoś się pojawił */
+	GG_EVENT_NOTIFY_DESCR,		/* ktoś się pojawił z opisem */
+	GG_EVENT_STATUS,		/* ktoś zmienił stan */
+	GG_EVENT_ACK,			/* potwierdzenie wysłania wiadomości */
+	GG_EVENT_PONG,			/* pakiet pong */
+	GG_EVENT_CONN_FAILED,		/* połączenie się nie udało */
+	GG_EVENT_CONN_SUCCESS,		/* połączenie się powiodło */
+	GG_EVENT_DISCONNECT,		/* serwer zrywa połączenie */
+
+	GG_EVENT_DCC_NEW,		/* nowe połączenie między klientami */
+	GG_EVENT_DCC_ERROR,		/* błąd połączenia między klientami */
+	GG_EVENT_DCC_DONE,		/* zakończono połączenie */
+	GG_EVENT_DCC_CLIENT_ACCEPT,	/* moment akceptacji klienta */
+	GG_EVENT_DCC_CALLBACK,		/* klient się połączył na żądanie */
+	GG_EVENT_DCC_NEED_FILE_INFO,	/* należy wypełnić file_info */
+	GG_EVENT_DCC_NEED_FILE_ACK,	/* czeka na potwierdzenie pliku */
+	GG_EVENT_DCC_NEED_VOICE_ACK,	/* czeka na potwierdzenie rozmowy */
+	GG_EVENT_DCC_VOICE_DATA, 	/* ramka danych rozmowy głosowej */
+
+	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/* odpowiedz wyszukiwania */
+	GG_EVENT_PUBDIR50_READ,		/* odczytano własne dane z katalogu */
+	GG_EVENT_PUBDIR50_WRITE,	/* wpisano własne dane do katalogu */
+
+	GG_EVENT_STATUS60,		/* ktoś zmienił stan w GG 6.0 */
+	GG_EVENT_NOTIFY60,		/* ktoś się pojawił w GG 6.0 */
+	GG_EVENT_USERLIST,		/* odpowiedź listy kontaktów w GG 6.0 */
+	GG_EVENT_IMAGE_REQUEST,		/* prośba o wysłanie obrazka GG 6.0 */
+	GG_EVENT_IMAGE_REPLY,		/* podesłany obrazek GG 6.0 */
+	GG_EVENT_DCC_ACK		/* potwierdzenie transmisji */
+};
+
+#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
+
+/*
+ * enum gg_failure_t
+ *
+ * określa powód nieudanego połączenia.
+ */
+enum gg_failure_t {
+	GG_FAILURE_RESOLVING = 1,	/* nie znaleziono serwera */
+	GG_FAILURE_CONNECTING,		/* nie można się połączyć */
+	GG_FAILURE_INVALID,		/* serwer zwrócił nieprawidłowe dane */
+	GG_FAILURE_READING,		/* zerwano połączenie podczas odczytu */
+	GG_FAILURE_WRITING,		/* zerwano połączenie podczas zapisu */
+	GG_FAILURE_PASSWORD,		/* nieprawidłowe hasło */
+	GG_FAILURE_404, 		/* XXX nieużywane */
+	GG_FAILURE_TLS,			/* błąd negocjacji TLS */
+	GG_FAILURE_NEED_EMAIL 		/* serwer rozłączył nas z prośbą o zmianę emaila */
+};
+
+/*
+ * enum gg_error_t
+ *
+ * określa rodzaj błędu wywołanego przez daną operację. nie zawiera
+ * przesadnie szczegółowych informacji o powodzie błędu, by nie komplikować
+ * obsługi błędów. jeśli wymagana jest większa dokładność, należy sprawdzić
+ * zawartość zmiennej errno.
+ */
+enum gg_error_t {
+	GG_ERROR_RESOLVING = 1,		/* błąd znajdowania hosta */
+	GG_ERROR_CONNECTING,		/* błąd łaczenia się */
+	GG_ERROR_READING,		/* błąd odczytu */
+	GG_ERROR_WRITING,		/* błąd wysyłania */
+
+	GG_ERROR_DCC_HANDSHAKE,		/* błąd negocjacji */
+	GG_ERROR_DCC_FILE,		/* błąd odczytu/zapisu pliku */
+	GG_ERROR_DCC_EOF,		/* plik się skończył? */
+	GG_ERROR_DCC_NET,		/* błąd wysyłania/odbierania */
+	GG_ERROR_DCC_REFUSED 		/* połączenie odrzucone przez usera */
+};
+
+/*
+ * struktury dotyczące wyszukiwania w GG 5.0. NIE NALEŻY SIĘ DO NICH
+ * ODWOŁYWAĆ BEZPOŚREDNIO! do dostępu do nich służą funkcje gg_pubdir50_*()
+ */
+struct gg_pubdir50_entry {
+	int num;
+	char *field;
+	char *value;
+};
+
+struct gg_pubdir50_s {
+	int count;
+	uin_t next;
+	int type;
+	uint32_t seq;
+	struct gg_pubdir50_entry *entries;
+	int entries_count;
+};
+
+/*
+ * typedef gg_pubdir_50_t
+ *
+ * typ opisujący zapytanie lub wynik zapytania katalogu publicznego
+ * z protokołu GG 5.0. nie należy się odwoływać bezpośrednio do jego
+ * pól -- służą do tego funkcje gg_pubdir50_*()
+ */
+typedef struct gg_pubdir50_s *gg_pubdir50_t;
+
+/*
+ * struct gg_event
+ *
+ * struktura opisująca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub
+ * z gg_dcc_watch_fd()
+ */
+struct gg_event {
+	int type;	/* rodzaj zdarzenia -- gg_event_t */
+	union {		/* @event */
+		struct gg_notify_reply *notify;	/* informacje o liście kontaktów -- GG_EVENT_NOTIFY */
+
+		enum gg_failure_t failure;	/* błąd połączenia -- GG_EVENT_FAILURE */
+
+		struct gg_dcc *dcc_new;		/* nowe połączenie bezpośrednie -- GG_EVENT_DCC_NEW */
+		
+		int dcc_error;			/* błąd połączenia bezpośredniego -- GG_EVENT_DCC_ERROR */
+
+		gg_pubdir50_t pubdir50;		/* wynik operacji związanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */
+	
+		struct {			/* @msg odebrano wiadomość -- GG_EVENT_MSG */
+			uin_t sender;		/* numer nadawcy */
+			int msgclass;		/* klasa wiadomości */
+			time_t time;		/* czas nadania */
+			unsigned char *message;	/* treść wiadomości */
+
+			int recipients_count;	/* ilość odbiorców konferencji */
+			uin_t *recipients;	/* odbiorcy konferencji */
+			
+			int formats_length;	/* długość informacji o formatowaniu tekstu */
+			void *formats;		/* informacje o formatowaniu tekstu */
+		} msg;
+		
+		struct {			/* @notify_descr informacje o liście kontaktów z opisami stanu -- GG_EVENT_NOTIFY_DESCR */
+			struct gg_notify_reply *notify;	/* informacje o liście kontaktów */
+			char *descr;		/* opis stanu */
+		} notify_descr;
+		
+		struct {			/* @status zmiana stanu -- GG_EVENT_STATUS */
+			uin_t uin;		/* numer */
+			uint32_t status;	/* nowy stan */
+			char *descr;		/* opis stanu */
+		} status;
+
+		struct {			/* @status60 zmiana stanu -- GG_EVENT_STATUS60 */
+			uin_t uin;		/* numer */
+			int status;	/* nowy stan */
+			uint32_t remote_ip;	/* adres ip */
+			uint16_t remote_port;	/* port */
+			int version;	/* wersja klienta */
+			int image_size;	/* maksymalny rozmiar grafiki w KiB */
+			char *descr;		/* opis stanu */
+			time_t time;		/* czas powrotu */
+		} status60;
+
+		struct {			/* @notify60 informacja o liście kontaktów -- GG_EVENT_NOTIFY60 */
+			uin_t uin;		/* numer */
+			int status;	/* stan */
+			uint32_t remote_ip;	/* adres ip */
+			uint16_t remote_port;	/* port */
+			int version;	/* wersja klienta */
+			int image_size;	/* maksymalny rozmiar grafiki w KiB */
+			char *descr;		/* opis stanu */
+			time_t time;		/* czas powrotu */
+		} *notify60;
+		
+		struct {			/* @ack potwierdzenie wiadomości -- GG_EVENT_ACK */
+			uin_t recipient;	/* numer odbiorcy */
+			int status;		/* stan doręczenia wiadomości */
+			int seq;		/* numer sekwencyjny wiadomości */
+		} ack;
+
+		struct {			/* @dcc_voice_data otrzymano dane dźwiękowe -- GG_EVENT_DCC_VOICE_DATA */
+			uint8_t *data;		/* dane dźwiękowe */
+			int length;		/* ilość danych dźwiękowych */
+		} dcc_voice_data;
+
+		struct {			/* @userlist odpowiedź listy kontaktów serwera */
+			char type;		/* rodzaj odpowiedzi */
+			char *reply;		/* treść odpowiedzi */
+		} userlist;
+
+		struct {			/* @image_request prośba o obrazek */
+			uin_t sender;		/* nadawca prośby */
+			uint32_t size;		/* rozmiar obrazka */
+			uint32_t crc32;		/* suma kontrolna */
+		} image_request;
+
+		struct {			/* @image_reply odpowiedź z obrazkiem */
+			uin_t sender;		/* nadawca odpowiedzi */
+			uint32_t size;		/* rozmiar obrazka */
+			uint32_t crc32;		/* suma kontrolna */
+			char *filename;		/* nazwa pliku */
+			char *image;		/* bufor z obrazkiem */
+		} image_reply;
+	} event;
+};
+
+struct gg_event *gg_watch_fd(struct gg_session *sess);
+void gg_event_free(struct gg_event *e);
+#define gg_free_event gg_event_free
+
+/*
+ * funkcje obsługi listy kontaktów.
+ */
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
+int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
+int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
+int gg_add_notify(struct gg_session *sess, uin_t uin);
+int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
+int gg_remove_notify(struct gg_session *sess, uin_t uin);
+
+/*
+ * funkcje obsługi http.
+ */
+struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
+int gg_http_watch_fd(struct gg_http *h);
+void gg_http_stop(struct gg_http *h);
+void gg_http_free(struct gg_http *h);
+void gg_http_free_fields(struct gg_http *h);
+#define gg_free_http gg_http_free
+
+/*
+ * struktury opisująca kryteria wyszukiwania dla gg_search(). nieaktualne,
+ * zastąpione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI.
+ */
+struct gg_search_request {
+	int active;
+	unsigned int start;
+	char *nickname;
+	char *first_name;
+	char *last_name;
+	char *city;
+	int gender;
+	int min_birth;
+	int max_birth;
+	char *email;
+	char *phone;
+	uin_t uin;
+};
+
+struct gg_search {
+	int count;
+	struct gg_search_result *results;
+};
+
+struct gg_search_result {
+	uin_t uin;
+	char *first_name;
+	char *last_name;
+	char *nickname;
+	int born;
+	int gender;
+	char *city;
+	int active;
+};
+
+#define GG_GENDER_NONE 0
+#define GG_GENDER_FEMALE 1
+#define GG_GENDER_MALE 2
+
+/*
+ * funkcje wyszukiwania.
+ */
+struct gg_http *gg_search(const struct gg_search_request *r, int async);
+int gg_search_watch_fd(struct gg_http *f);
+void gg_free_search(struct gg_http *f);
+#define gg_search_free gg_free_search
+
+const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start);
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start);
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start);
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start);
+void gg_search_request_free(struct gg_search_request *r);
+
+/*
+ * funkcje obsługi katalogu publicznego zgodne z GG 5.0. tym razem funkcje
+ * zachowują pewien poziom abstrakcji, żeby uniknąć zmian ABI przy zmianach
+ * w protokole.
+ *
+ * NIE NALEŻY SIĘ ODWOŁYWAĆ DO PÓL gg_pubdir50_t BEZPOŚREDNIO!
+ */
+uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req);
+gg_pubdir50_t gg_pubdir50_new(int type);
+int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value);
+int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq);
+const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field);
+int gg_pubdir50_type(gg_pubdir50_t res);
+int gg_pubdir50_count(gg_pubdir50_t res);
+uin_t gg_pubdir50_next(gg_pubdir50_t res);
+uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
+void gg_pubdir50_free(gg_pubdir50_t res);
+
+#define GG_PUBDIR50_UIN "FmNumber"
+#define GG_PUBDIR50_STATUS "FmStatus"
+#define GG_PUBDIR50_FIRSTNAME "firstname"
+#define GG_PUBDIR50_LASTNAME "lastname"
+#define GG_PUBDIR50_NICKNAME "nickname"
+#define GG_PUBDIR50_BIRTHYEAR "birthyear"
+#define GG_PUBDIR50_CITY "city"
+#define GG_PUBDIR50_GENDER "gender"
+#define GG_PUBDIR50_GENDER_FEMALE "1"
+#define GG_PUBDIR50_GENDER_MALE "2"
+#define GG_PUBDIR50_GENDER_SET_FEMALE "2"
+#define GG_PUBDIR50_GENDER_SET_MALE "1"
+#define GG_PUBDIR50_ACTIVE "ActiveOnly"
+#define GG_PUBDIR50_ACTIVE_TRUE "1"
+#define GG_PUBDIR50_START "fmstart"
+#define GG_PUBDIR50_FAMILYNAME "familyname"
+#define GG_PUBDIR50_FAMILYCITY "familycity"
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length);
+
+/*
+ * struct gg_pubdir
+ *
+ * operacje na katalogu publicznym.
+ */
+struct gg_pubdir {
+	int success;		/* czy się udało */
+	uin_t uin;		/* otrzymany numerek. 0 jeśli błąd */
+};
+
+/* ogólne funkcje, nie powinny być używane */
+int gg_pubdir_watch_fd(struct gg_http *f);
+void gg_pubdir_free(struct gg_http *f);
+#define gg_free_pubdir gg_pubdir_free
+
+struct gg_token {
+	int width;		/* szerokość obrazka */
+	int height;		/* wysokość obrazka */
+	int length;		/* ilość znaków w tokenie */
+	char *tokenid;		/* id tokenu */
+};
+
+/* funkcje dotyczące tokenów */
+struct gg_http *gg_token(int async);
+int gg_token_watch_fd(struct gg_http *h);
+void gg_token_free(struct gg_http *h);
+
+/* rejestracja nowego numerka */
+struct gg_http *gg_register(const char *email, const char *password, int async);
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async);
+struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async);
+#define gg_register_watch_fd gg_pubdir_watch_fd
+#define gg_register_free gg_pubdir_free
+#define gg_free_register gg_pubdir_free
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async);
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async);
+struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async);
+#define gg_unregister_watch_fd gg_pubdir_watch_fd
+#define gg_unregister_free gg_pubdir_free
+
+/* przypomnienie hasła e-mailem */
+struct gg_http *gg_remind_passwd(uin_t uin, int async);
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async);
+struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async);
+#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd
+#define gg_remind_passwd_free gg_pubdir_free
+#define gg_free_remind_passwd gg_pubdir_free
+
+/* zmiana hasła */
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async);
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async);
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async);
+struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
+#define gg_change_passwd_free gg_pubdir_free
+#define gg_free_change_passwd gg_pubdir_free
+
+/*
+ * struct gg_change_info_request
+ * 
+ * opis żądania zmiany informacji w katalogu publicznym.
+ */
+struct gg_change_info_request {
+	char *first_name;	/* imię */
+	char *last_name;	/* nazwisko */
+	char *nickname;		/* pseudonim */
+	char *email;		/* email */
+	int born;		/* rok urodzenia */
+	int gender;		/* płeć */
+	char *city;		/* miasto */
+};
+
+struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city);
+void gg_change_info_request_free(struct gg_change_info_request *r);
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async);
+#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
+#define gg_change_pubdir_free gg_pubdir_free
+#define gg_free_change_pubdir gg_pubdir_free
+
+/*
+ * funkcje dotyczące listy kontaktów na serwerze.
+ */
+struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async);
+int gg_userlist_get_watch_fd(struct gg_http *f);
+void gg_userlist_get_free(struct gg_http *f);
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async);
+int gg_userlist_put_watch_fd(struct gg_http *f);
+void gg_userlist_put_free(struct gg_http *f);
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async);
+int gg_userlist_remove_watch_fd(struct gg_http *f);
+void gg_userlist_remove_free(struct gg_http *f);
+
+
+
+/*
+ * funkcje dotyczące komunikacji między klientami.
+ */
+extern int gg_dcc_port;			/* port, na którym nasłuchuje klient */
+extern unsigned long gg_dcc_ip;		/* adres, na którym nasłuchuje klient */
+
+int gg_dcc_request(struct gg_session *sess, uin_t uin);
+
+struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+void gg_dcc_set_type(struct gg_dcc *d, int type);
+int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename);
+int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename);
+int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length);
+
+#define GG_DCC_VOICE_FRAME_LENGTH 195
+#define GG_DCC_VOICE_FRAME_LENGTH_505 326
+
+struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
+#define gg_dcc_socket_free gg_free_dcc
+#define gg_dcc_socket_watch_fd gg_dcc_watch_fd
+
+struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d);
+
+void gg_dcc_free(struct gg_dcc *c);
+#define gg_free_dcc gg_dcc_free
+
+/*
+ * jeśli chcemy sobie podebugować, wystarczy ustawić `gg_debug_level'.
+ * niestety w miarę przybywania wpisów `gg_debug(...)' nie chciało mi
+ * się ustawiać odpowiednich leveli, więc większość szła do _MISC.
+ */
+extern int gg_debug_level;	/* poziom debugowania. mapa bitowa stałych GG_DEBUG_* */
+
+/*
+ * można podać wskaźnik do funkcji obsługującej wywołania gg_debug().
+ * nieoficjalne, nieudokumentowane, może się zmienić. jeśli ktoś jest 
+ * zainteresowany, niech da znać na ekg-devel.
+ */
+extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
+
+/*
+ * można podać plik, do którego będą zapisywane teksty z gg_debug().
+ */
+extern FILE *gg_debug_file;
+
+#define GG_DEBUG_NET 1
+#define GG_DEBUG_TRAFFIC 2
+#define GG_DEBUG_DUMP 4
+#define GG_DEBUG_FUNCTION 8
+#define GG_DEBUG_MISC 16
+
+#ifdef GG_DEBUG_DISABLE
+#define gg_debug(x, y...) do { } while(0)
+#else
+void gg_debug(int level, const char *format, ...);
+#endif
+
+const char *gg_libgadu_version(void);
+
+/*
+ * konfiguracja http proxy.
+ */
+extern int gg_proxy_enabled;		/* włącza obsługę proxy */
+extern char *gg_proxy_host;		/* określa adres serwera proxy */
+extern int gg_proxy_port;		/* określa port serwera proxy */
+extern char *gg_proxy_username;		/* określa nazwę użytkownika przy autoryzacji serwera proxy */
+extern char *gg_proxy_password;		/* określa hasło użytkownika przy autoryzacji serwera proxy */
+extern int gg_proxy_http_only;		/* włącza obsługę proxy wyłącznie dla usług HTTP */
+
+
+/* 
+ * adres, z którego ślemy pakiety (np łączymy się z serwerem)
+ * używany przy gg_connect()
+ */
+extern unsigned long gg_local_ip; 
+/*
+ * -------------------------------------------------------------------------
+ * poniżej znajdują się wewnętrzne sprawy biblioteki. zwykły klient nie
+ * powinien ich w ogóle ruszać, bo i nie ma po co. wszystko można załatwić
+ * procedurami wyższego poziomu, których definicje znajdują się na początku
+ * tego pliku.
+ * -------------------------------------------------------------------------
+ */
+
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
+#endif
+
+#ifdef _WIN32
+int gg_thread_socket(int thread_id, int socket);
+#endif
+
+int gg_resolve(int *fd, int *pid, const char *hostname);
+
+#ifdef __GNUC__
+char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+#else
+char *gg_saprintf(const char *format, ...);
+#endif
+
+char *gg_vsaprintf(const char *format, va_list ap);
+
+#define gg_alloc_sprintf gg_saprintf
+
+char *gg_get_line(char **ptr);
+
+int gg_connect(void *addr, int port, int async);
+struct in_addr *gg_gethostbyname(const char *hostname);
+char *gg_read_line(int sock, char *buf, int length);
+void gg_chomp(char *line);
+char *gg_urlencode(const char *str);
+int gg_http_hash(const char *format, ...);
+int gg_read(struct gg_session *sess, char *buf, int length);
+int gg_write(struct gg_session *sess, const char *buf, int length);
+void *gg_recv_packet(struct gg_session *sess);
+int gg_send_packet(struct gg_session *sess, int type, ...);
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed);
+uint32_t gg_fix32(uint32_t x);
+uint16_t gg_fix16(uint16_t x);
+#define fix16 gg_fix16
+#define fix32 gg_fix32
+char *gg_proxy_auth(void);
+char *gg_base64_encode(const char *buf);
+char *gg_base64_decode(const char *buf);
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq);
+
+#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
+#define GG_APPMSG_PORT 80
+#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
+#define GG_PUBDIR_PORT 80
+#define GG_REGISTER_HOST "register.gadu-gadu.pl"
+#define GG_REGISTER_PORT 80
+#define GG_REMIND_HOST "retr.gadu-gadu.pl"
+#define GG_REMIND_PORT 80
+
+#define GG_DEFAULT_PORT 8074
+#define GG_HTTPS_PORT 443
+#define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)"
+
+#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158"
+#define GG_DEFAULT_PROTOCOL_VERSION 0x24
+#define GG_DEFAULT_TIMEOUT 30
+#define GG_HAS_AUDIO_MASK 0x40000000
+#define GG_ERA_OMNIX_MASK 0x04000000
+#define GG_LIBGADU_VERSION "1.5.20050718"
+
+#define GG_DEFAULT_DCC_PORT 1550
+
+struct gg_header {
+	uint32_t type;			/* typ pakietu */
+	uint32_t length;		/* długość reszty pakietu */
+} GG_PACKED;
+
+#define GG_WELCOME 0x0001
+#define GG_NEED_EMAIL 0x0014
+
+struct gg_welcome {
+	uint32_t key;			/* klucz szyfrowania hasła */
+} GG_PACKED;
+	
+#define GG_LOGIN 0x000c
+
+struct gg_login {
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
+	uint32_t version;		/* moja wersja klienta */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+} GG_PACKED;
+
+#define GG_LOGIN_EXT 0x0013
+
+struct gg_login_ext {
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
+	uint32_t version;		/* moja wersja klienta */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip */
+	uint16_t external_port;		/* zewnętrzny port */
+} GG_PACKED;
+
+#define GG_LOGIN60 0x0015
+
+struct gg_login60 {
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
+	uint32_t version;		/* moja wersja klienta */
+	uint8_t dunno1;			/* 0x00 */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip */
+	uint16_t external_port;		/* zewnętrzny port */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno2;			/* 0xbe */
+} GG_PACKED;
+
+#define GG_LOGIN_OK 0x0003
+
+#define GG_LOGIN_FAILED 0x0009
+
+#define GG_PUBDIR50_REQUEST 0x0014
+
+#define GG_PUBDIR50_WRITE 0x01
+#define GG_PUBDIR50_READ 0x02
+#define GG_PUBDIR50_SEARCH 0x03
+#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
+#define GG_PUBDIR50_SEARCH_REPLY 0x05
+
+struct gg_pubdir50_request {
+	uint8_t type;			/* GG_PUBDIR50_* */
+	uint32_t seq;			/* czas wysłania zapytania */
+} GG_PACKED;
+
+#define GG_PUBDIR50_REPLY 0x000e
+
+struct gg_pubdir50_reply {
+	uint8_t type;			/* GG_PUBDIR50_* */
+	uint32_t seq;			/* czas wysłania zapytania */
+} GG_PACKED;
+
+#define GG_NEW_STATUS 0x0002
+
+#define GG_STATUS_NOT_AVAIL 0x0001		/* niedostępny */
+#define GG_STATUS_NOT_AVAIL_DESCR 0x0015	/* niedostępny z opisem (4.8) */
+#define GG_STATUS_AVAIL 0x0002			/* dostępny */
+#define GG_STATUS_AVAIL_DESCR 0x0004		/* dostępny z opisem (4.9) */
+#define GG_STATUS_BUSY 0x0003			/* zajęty */
+#define GG_STATUS_BUSY_DESCR 0x0005		/* zajęty z opisem (4.8) */
+#define GG_STATUS_INVISIBLE 0x0014		/* niewidoczny (4.6) */
+#define GG_STATUS_INVISIBLE_DESCR 0x0016	/* niewidoczny z opisem (4.9) */
+#define GG_STATUS_BLOCKED 0x0006		/* zablokowany */
+
+#define GG_STATUS_FRIENDS_MASK 0x8000		/* tylko dla znajomych (4.6) */
+
+#define GG_STATUS_DESCR_MAXSIZE 70
+
+/*
+ * makra do łatwego i szybkiego sprawdzania stanu.
+ */
+
+/* GG_S_F() tryb tylko dla znajomych */
+#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
+
+/* GG_S() stan bez uwzględnienia trybu tylko dla znajomych */
+#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
+
+/* GG_S_A() dostępny */
+#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_NA() niedostępny */
+#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
+
+/* GG_S_B() zajęty */
+#define GG_S_B(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
+
+/* GG_S_I() niewidoczny */
+#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+
+/* GG_S_D() stan opisowy */
+#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+
+/* GG_S_BL() blokowany lub blokujący */
+#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
+
+struct gg_new_status {
+	uint32_t status;			/* na jaki zmienić? */
+} GG_PACKED;
+
+#define GG_NOTIFY_FIRST 0x000f
+#define GG_NOTIFY_LAST 0x0010
+
+#define GG_NOTIFY 0x0010
+	
+struct gg_notify {
+	uint32_t uin;				/* numerek danej osoby */
+	uint8_t dunno1;				/* rodzaj wpisu w liście */
+} GG_PACKED;
+
+#define GG_USER_OFFLINE 0x01	/* będziemy niewidoczni dla użytkownika */
+#define GG_USER_NORMAL 0x03	/* zwykły użytkownik */
+#define GG_USER_BLOCKED 0x04	/* zablokowany użytkownik */
+
+#define GG_LIST_EMPTY 0x0012
+	
+#define GG_NOTIFY_REPLY 0x000c	/* tak, to samo co GG_LOGIN */
+	
+struct gg_notify_reply {
+	uint32_t uin;			/* numerek */
+	uint32_t status;		/* status danej osoby */
+	uint32_t remote_ip;		/* adres ip delikwenta */
+	uint16_t remote_port;		/* port, na którym słucha klient */
+	uint32_t version;		/* wersja klienta */
+	uint16_t dunno2;		/* znowu port? */
+} GG_PACKED;
+
+#define GG_NOTIFY_REPLY60 0x0011
+	
+struct gg_notify_reply60 {
+	uint32_t uin;			/* numerek plus flagi w MSB */
+	uint8_t status;			/* status danej osoby */
+	uint32_t remote_ip;		/* adres ip delikwenta */
+	uint16_t remote_port;		/* port, na którym słucha klient */
+	uint8_t version;		/* wersja klienta */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno1;			/* 0x00 */
+} GG_PACKED;
+
+#define GG_STATUS60 0x000f
+	
+struct gg_status60 {
+	uint32_t uin;			/* numerek plus flagi w MSB */
+	uint8_t status;			/* status danej osoby */
+	uint32_t remote_ip;		/* adres ip delikwenta */
+	uint16_t remote_port;		/* port, na którym słucha klient */
+	uint8_t version;		/* wersja klienta */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno1;			/* 0x00 */
+} GG_PACKED;
+
+#define GG_ADD_NOTIFY 0x000d
+#define GG_REMOVE_NOTIFY 0x000e
+	
+struct gg_add_remove {
+	uint32_t uin;			/* numerek */
+	uint8_t dunno1;			/* bitmapa */
+} GG_PACKED;
+
+#define GG_STATUS 0x0002
+
+struct gg_status {
+	uint32_t uin;			/* numerek */
+	uint32_t status;		/* nowy stan */
+} GG_PACKED;
+	
+#define GG_SEND_MSG 0x000b
+
+#define GG_CLASS_QUEUED 0x0001
+#define GG_CLASS_OFFLINE GG_CLASS_QUEUED
+#define GG_CLASS_MSG 0x0004
+#define GG_CLASS_CHAT 0x0008
+#define GG_CLASS_CTCP 0x0010
+#define GG_CLASS_ACK 0x0020
+#define GG_CLASS_EXT GG_CLASS_ACK	/* kompatybilność wstecz */
+
+#define GG_MSG_MAXSIZE 2000
+
+struct gg_send_msg {
+	uint32_t recipient;
+	uint32_t seq;
+	uint32_t msgclass;
+} GG_PACKED;
+
+struct gg_msg_richtext {
+	uint8_t flag;		
+	uint16_t length;	  
+} GG_PACKED;
+
+struct gg_msg_richtext_format {
+	uint16_t position;
+	uint8_t font;	  
+} GG_PACKED;
+
+struct gg_msg_richtext_image {
+	uint16_t unknown1;
+	uint32_t size;
+	uint32_t crc32;
+} GG_PACKED;
+
+#define GG_FONT_BOLD 0x01
+#define GG_FONT_ITALIC 0x02
+#define GG_FONT_UNDERLINE 0x04
+#define GG_FONT_COLOR 0x08
+#define GG_FONT_IMAGE 0x80
+
+struct gg_msg_richtext_color { 
+	uint8_t red;
+	uint8_t green;
+	uint8_t blue;
+} GG_PACKED;
+
+struct gg_msg_recipients {
+	uint8_t flag;
+	uint32_t count;
+} GG_PACKED;
+
+struct gg_msg_image_request {
+	uint8_t flag;
+	uint32_t size;
+	uint32_t crc32;
+} GG_PACKED;
+
+struct gg_msg_image_reply {
+	uint8_t flag;
+	uint32_t size;
+	uint32_t crc32;
+	/* char filename[]; */
+	/* char image[]; */
+} GG_PACKED;
+
+#define GG_SEND_MSG_ACK 0x0005
+
+#define GG_ACK_BLOCKED 0x0001
+#define GG_ACK_DELIVERED 0x0002
+#define GG_ACK_QUEUED 0x0003
+#define GG_ACK_MBOXFULL 0x0004
+#define GG_ACK_NOT_DELIVERED 0x0006
+	
+struct gg_send_msg_ack {
+	uint32_t status;
+	uint32_t recipient;
+	uint32_t seq;
+} GG_PACKED;
+
+#define GG_RECV_MSG 0x000a
+	
+struct gg_recv_msg {
+	uint32_t sender;
+	uint32_t seq;
+	uint32_t time;
+	uint32_t msgclass;
+} GG_PACKED;
+
+#define GG_PING 0x0008
+	
+#define GG_PONG 0x0007
+
+#define GG_DISCONNECTING 0x000b
+
+#define GG_USERLIST_REQUEST 0x0016
+
+#define GG_USERLIST_PUT 0x00
+#define GG_USERLIST_PUT_MORE 0x01
+#define GG_USERLIST_GET 0x02
+
+struct gg_userlist_request {
+	uint8_t type;
+} GG_PACKED;
+
+#define GG_USERLIST_REPLY 0x0010
+
+#define GG_USERLIST_PUT_REPLY 0x00
+#define GG_USERLIST_PUT_MORE_REPLY 0x02
+#define GG_USERLIST_GET_REPLY 0x06
+#define GG_USERLIST_GET_MORE_REPLY 0x04
+
+struct gg_userlist_reply {
+	uint8_t type;
+} GG_PACKED;
+
+/*
+ * pakiety, stałe, struktury dla DCC
+ */
+
+struct gg_dcc_tiny_packet {
+	uint8_t type;		/* rodzaj pakietu */
+} GG_PACKED;
+
+struct gg_dcc_small_packet {
+	uint32_t type;		/* rodzaj pakietu */
+} GG_PACKED;
+
+struct gg_dcc_big_packet {
+	uint32_t type;		/* rodzaj pakietu */
+	uint32_t dunno1;		/* niewiadoma */
+	uint32_t dunno2;		/* niewiadoma */
+} GG_PACKED;
+
+/*
+ * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada.
+ * nazwy są niepoważne i tymczasowe.
+ */
+#define GG_DCC_WANT_FILE 0x0003		/* peer chce plik */
+#define GG_DCC_HAVE_FILE 0x0001		/* więc mu damy */
+#define GG_DCC_HAVE_FILEINFO 0x0003	/* niech ma informacje o pliku */
+#define GG_DCC_GIMME_FILE 0x0006	/* peer jest pewny */
+#define GG_DCC_CATCH_FILE 0x0002	/* wysyłamy plik */
+
+#define GG_DCC_FILEATTR_READONLY 0x0020
+
+#define GG_DCC_TIMEOUT_SEND 1800	/* 30 minut */
+#define GG_DCC_TIMEOUT_GET 1800		/* 30 minut */
+#define GG_DCC_TIMEOUT_FILE_ACK 300	/* 5 minut */
+#define GG_DCC_TIMEOUT_VOICE_ACK 300	/* 5 minut */
+
+#ifdef __cplusplus
+}
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+#endif
+
+#endif /* __GG_LIBGADU_H */
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/obsolete.c	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,207 @@
+/* $Id: obsolete.c 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+/*
+ * Plik zawiera deklaracje funkcji, które są już nieaktualne ze względu
+ * na zmiany w protokole, ale są wymagane przez aplikacje linkowane ze
+ * starszymi wersjami bibliotek.
+ */
+
+#include <errno.h>
+
+#include "libgadu.h"
+
+struct gg_http *gg_userlist_get(uin_t uin, const char *passwd, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_userlist_get() is obsolete. use gg_userlist_request() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+int gg_userlist_get_watch_fd(struct gg_http *h)
+{
+	errno = EINVAL;
+	return -1;
+}
+
+void gg_userlist_get_free(struct gg_http *h)
+{
+
+}
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_userlist_put() is obsolete. use gg_userlist_request() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+int gg_userlist_put_watch_fd(struct gg_http *h)
+{
+	errno = EINVAL;
+	return -1;
+}
+
+void gg_userlist_put_free(struct gg_http *h)
+{
+
+}
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *passwd, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_userlist_remove() is obsolete. use gg_userlist_request() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+int gg_userlist_remove_watch_fd(struct gg_http *h)
+{
+	errno = EINVAL;
+	return -1;
+}
+
+void gg_userlist_remove_free(struct gg_http *h)
+{
+
+}
+
+struct gg_http *gg_search(const struct gg_search_request *r, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_search() is obsolete. use gg_search50() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+int gg_search_watch_fd(struct gg_http *h)
+{
+	errno = EINVAL;
+	return -1;
+}
+
+void gg_search_free(struct gg_http *h)
+{
+
+}
+
+const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start)
+{
+	return NULL;
+}
+
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start)
+{
+	return NULL;
+}
+
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start)
+{
+	return NULL;
+}
+
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start)
+{
+	return NULL;
+}
+
+void gg_search_request_free(struct gg_search_request *r)
+{
+
+}
+
+struct gg_http *gg_register(const char *email, const char *password, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_register() is obsolete. use gg_register3() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_register2() is obsolete. use gg_register3() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_unregister() is obsolete. use gg_unregister3() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_unregister2() is obsolete. use gg_unregister3() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_change_passwd() is obsolete. use gg_change_passwd4() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_change_passwd2() is obsolete. use gg_change_passwd4() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_change_passwd3() is obsolete. use gg_change_passwd4() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_http *gg_remind_passwd(uin_t uin, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_remind_passwd() is obsolete. use gg_remind_passwd3() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_remind_passwd2() is obsolete. use gg_remind_passwd3() instead!\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async)
+{
+	gg_debug(GG_DEBUG_MISC, "// gg_change_info() is obsolete. use gg_pubdir50() instead\n");
+	errno = EINVAL;
+	return NULL;
+}
+
+struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city)
+{
+	return NULL;
+}
+
+void gg_change_info_request_free(struct gg_change_info_request *r)
+{
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/pubdir.c	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,684 @@
+/* $Id: pubdir.c 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Dawid Jarosz <dawjar@poczta.onet.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+
+/*
+ * gg_register3()
+ *
+ * rozpoczyna rejestrację użytkownika protokołem GG 6.0. wymaga wcześniejszego
+ * pobrania tokenu za pomocą funkcji gg_token().
+ *
+ *  - email - adres e-mail klienta
+ *  - password - hasło klienta
+ *  - tokenid - identyfikator tokenu
+ *  - tokenval - wartość tokenu
+ *  - async - połączenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, którą poźniej należy zwolnić
+ * funkcją gg_register_free(), albo NULL jeśli wystąpił błąd.
+ */
+struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async)
+{
+	struct gg_http *h;
+	char *__pwd, *__email, *__tokenid, *__tokenval, *form, *query;
+
+	if (!email || !password || !tokenid || !tokenval) {
+		gg_debug(GG_DEBUG_MISC, "=> register, NULL parameter\n");
+		errno = EFAULT;
+		return NULL;
+	}
+
+	__pwd = gg_urlencode(password);
+	__email = gg_urlencode(email);
+	__tokenid = gg_urlencode(tokenid);
+	__tokenval = gg_urlencode(tokenval);
+
+	if (!__pwd || !__email || !__tokenid || !__tokenval) {
+		gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form fields\n");
+		free(__pwd);
+		free(__email);
+		free(__tokenid);
+		free(__tokenval);
+		return NULL;
+	}
+
+	form = gg_saprintf("pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u",
+			__pwd, __email, __tokenid, __tokenval,
+			gg_http_hash("ss", email, password));
+
+	free(__pwd);
+	free(__email);
+	free(__tokenid);
+	free(__tokenval);
+
+	if (!form) {
+		gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form query\n");
+		return NULL;
+	}
+
+	gg_debug(GG_DEBUG_MISC, "=> register, %s\n", form);
+
+	query = gg_saprintf(
+		"Host: " GG_REGISTER_HOST "\r\n"
+		"Content-Type: application/x-www-form-urlencoded\r\n"
+		"User-Agent: " GG_HTTP_USERAGENT "\r\n"
+		"Content-Length: %d\r\n"
+		"Pragma: no-cache\r\n"
+		"\r\n"
+		"%s",
+		(int) strlen(form), form);
+
+	free(form);
+
+	if (!query) {
+		gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for query\n");
+		return NULL;
+	}
+
+	if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+		gg_debug(GG_DEBUG_MISC, "=> register, gg_http_connect() failed mysteriously\n");
+		free(query);
+		return NULL;
+	}
+
+	h->type = GG_SESSION_REGISTER;
+
+	free(query);
+
+	h->callback = gg_pubdir_watch_fd;
+	h->destroy = gg_pubdir_free;
+	
+	if (!async)
+		gg_pubdir_watch_fd(h);
+	
+	return h;
+}
+
+/*
+ * gg_unregister3()
+ *
+ * usuwa konto użytkownika z serwera protokołem GG 6.0
+ *
+ *  - uin - numerek GG
+ *  - password - hasło klienta
+ *  - tokenid - identyfikator tokenu
+ *  - tokenval - wartość tokenu
+ *  - async - połączenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, którą poźniej należy zwolnić
+ * funkcją gg_unregister_free(), albo NULL jeśli wystąpił błąd.
+ */
+struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async)
+{
+	struct gg_http *h;
+	char *__fmpwd, *__pwd, *__tokenid, *__tokenval, *form, *query;
+
+	if (!password || !tokenid || !tokenval) {
+		gg_debug(GG_DEBUG_MISC, "=> unregister, NULL parameter\n");
+		errno = EFAULT;
+		return NULL;
+	}
+    
+	__pwd = gg_saprintf("%ld", random());
+	__fmpwd = gg_urlencode(password);
+	__tokenid = gg_urlencode(tokenid);
+	__tokenval = gg_urlencode(tokenval);
+
+	if (!__fmpwd || !__pwd || !__tokenid || !__tokenval) {
+		gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form fields\n");
+		free(__pwd);
+		free(__fmpwd);
+		free(__tokenid);
+		free(__tokenval);
+		return NULL;
+	}
+
+	form = gg_saprintf("fmnumber=%d&fmpwd=%s&delete=1&pwd=%s&email=deletedaccount@gadu-gadu.pl&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __tokenid, __tokenval, gg_http_hash("ss", "deletedaccount@gadu-gadu.pl", __pwd));
+
+	free(__fmpwd);
+	free(__pwd);
+	free(__tokenid);
+	free(__tokenval);
+
+	if (!form) {
+		gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form query\n");
+		return NULL;
+	}
+
+	gg_debug(GG_DEBUG_MISC, "=> unregister, %s\n", form);
+
+	query = gg_saprintf(
+		"Host: " GG_REGISTER_HOST "\r\n"
+		"Content-Type: application/x-www-form-urlencoded\r\n"
+		"User-Agent: " GG_HTTP_USERAGENT "\r\n"
+		"Content-Length: %d\r\n"
+		"Pragma: no-cache\r\n"
+		"\r\n"
+		"%s",
+		(int) strlen(form), form);
+
+	free(form);
+
+	if (!query) {
+		gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for query\n");
+		return NULL;
+	}
+
+	if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+		gg_debug(GG_DEBUG_MISC, "=> unregister, gg_http_connect() failed mysteriously\n");
+		free(query);
+		return NULL;
+	}
+
+	h->type = GG_SESSION_UNREGISTER;
+
+	free(query);
+
+	h->callback = gg_pubdir_watch_fd;
+	h->destroy = gg_pubdir_free;
+	
+	if (!async)
+		gg_pubdir_watch_fd(h);
+	
+	return h;
+}
+
+/*
+ * gg_change_passwd4()
+ *
+ * wysyła żądanie zmiany hasła zgodnie z protokołem GG 6.0. wymaga
+ * wcześniejszego pobrania tokenu za pomocą funkcji gg_token().
+ *
+ *  - uin - numer
+ *  - email - adres e-mail
+ *  - passwd - stare hasło
+ *  - newpasswd - nowe hasło
+ *  - tokenid - identyfikator tokenu
+ *  - tokenval - wartość tokenu
+ *  - async - połączenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, którą poźniej należy zwolnić
+ * funkcją gg_change_passwd_free(), albo NULL jeśli wystąpił błąd.
+ */
+struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async)
+{
+	struct gg_http *h;
+	char *form, *query, *__email, *__fmpwd, *__pwd, *__tokenid, *__tokenval;
+
+	if (!uin || !email || !passwd || !newpasswd || !tokenid || !tokenval) {
+		gg_debug(GG_DEBUG_MISC, "=> change, NULL parameter\n");
+		errno = EFAULT;
+		return NULL;
+	}
+	
+	__fmpwd = gg_urlencode(passwd);
+	__pwd = gg_urlencode(newpasswd);
+	__email = gg_urlencode(email);
+	__tokenid = gg_urlencode(tokenid);
+	__tokenval = gg_urlencode(tokenval);
+
+	if (!__fmpwd || !__pwd || !__email || !__tokenid || !__tokenval) {
+		gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n");
+		free(__fmpwd);
+		free(__pwd);
+		free(__email);
+		free(__tokenid);
+		free(__tokenval);
+		return NULL;
+	}
+	
+	if (!(form = gg_saprintf("fmnumber=%d&fmpwd=%s&pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __email, __tokenid, __tokenval, gg_http_hash("ss", email, newpasswd)))) {
+		gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n");
+		free(__fmpwd);
+		free(__pwd);
+		free(__email);
+		free(__tokenid);
+		free(__tokenval);
+
+		return NULL;
+	}
+	
+	free(__fmpwd);
+	free(__pwd);
+	free(__email);
+	free(__tokenid);
+	free(__tokenval);
+	
+	gg_debug(GG_DEBUG_MISC, "=> change, %s\n", form);
+
+	query = gg_saprintf(
+		"Host: " GG_REGISTER_HOST "\r\n"
+		"Content-Type: application/x-www-form-urlencoded\r\n"
+		"User-Agent: " GG_HTTP_USERAGENT "\r\n"
+		"Content-Length: %d\r\n"
+		"Pragma: no-cache\r\n"
+		"\r\n"
+		"%s",
+		(int) strlen(form), form);
+
+	free(form);
+
+	if (!query) {
+		gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for query\n");
+		return NULL;
+	}
+
+	if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+		gg_debug(GG_DEBUG_MISC, "=> change, gg_http_connect() failed mysteriously\n");
+		free(query);
+		return NULL;
+	}
+
+	h->type = GG_SESSION_PASSWD;
+
+	free(query);
+
+	h->callback = gg_pubdir_watch_fd;
+	h->destroy = gg_pubdir_free;
+
+	if (!async)
+		gg_pubdir_watch_fd(h);
+
+	return h;
+}
+
+/*
+ * gg_remind_passwd3()
+ *
+ * wysyła żądanie przypomnienia hasła e-mailem.
+ *
+ *  - uin - numer
+ *  - email - adres e-mail taki, jak ten zapisany na serwerze
+ *  - async - połączenie asynchroniczne
+ *  - tokenid - identyfikator tokenu
+ *  - tokenval - wartość tokenu
+ *
+ * zaalokowana struct gg_http, którą poźniej należy zwolnić
+ * funkcją gg_remind_passwd_free(), albo NULL jeśli wystąpił błąd.
+ */
+struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async)
+{
+	struct gg_http *h;
+	char *form, *query, *__tokenid, *__tokenval, *__email;
+
+	if (!tokenid || !tokenval || !email) {
+		gg_debug(GG_DEBUG_MISC, "=> remind, NULL parameter\n");
+		errno = EFAULT;
+		return NULL;
+	}
+	
+	__tokenid = gg_urlencode(tokenid);
+	__tokenval = gg_urlencode(tokenval);
+	__email = gg_urlencode(email);
+
+	if (!__tokenid || !__tokenval || !__email) {
+		gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n");
+		free(__tokenid);
+		free(__tokenval);
+		free(__email);
+		return NULL;
+	}
+
+	if (!(form = gg_saprintf("userid=%d&code=%u&tokenid=%s&tokenval=%s&email=%s", uin, gg_http_hash("u", uin), __tokenid, __tokenval, __email))) {
+		gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n");
+		free(__tokenid);
+		free(__tokenval);
+		free(__email);
+		return NULL;
+	}
+
+	free(__tokenid);
+	free(__tokenval);
+	free(__email);
+	
+	gg_debug(GG_DEBUG_MISC, "=> remind, %s\n", form);
+
+	query = gg_saprintf(
+		"Host: " GG_REMIND_HOST "\r\n"
+		"Content-Type: application/x-www-form-urlencoded\r\n"
+		"User-Agent: " GG_HTTP_USERAGENT "\r\n"
+		"Content-Length: %d\r\n"
+		"Pragma: no-cache\r\n"
+		"\r\n"
+		"%s",
+		(int) strlen(form), form);
+
+	free(form);
+
+	if (!query) {
+		gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for query\n");
+		return NULL;
+	}
+
+	if (!(h = gg_http_connect(GG_REMIND_HOST, GG_REMIND_PORT, async, "POST", "/appsvc/fmsendpwd3.asp", query))) {
+		gg_debug(GG_DEBUG_MISC, "=> remind, gg_http_connect() failed mysteriously\n");
+		free(query);
+		return NULL;
+	}
+
+	h->type = GG_SESSION_REMIND;
+
+	free(query);
+
+	h->callback = gg_pubdir_watch_fd;
+	h->destroy = gg_pubdir_free;
+
+	if (!async)
+		gg_pubdir_watch_fd(h);
+
+	return h;
+}
+
+/*
+ * gg_pubdir_watch_fd()
+ *
+ * przy asynchronicznych operacjach na katalogu publicznym należy wywoływać
+ * tę funkcję przy zmianach na obserwowanym deskryptorze.
+ *
+ *  - h - struktura opisująca połączenie
+ *
+ * jeśli wszystko poszło dobrze to 0, inaczej -1. operacja będzie
+ * zakończona, jeśli h->state == GG_STATE_DONE. jeśli wystąpi jakiś
+ * błąd, to będzie tam GG_STATE_ERROR i odpowiedni kod błędu w h->error.
+ */
+int gg_pubdir_watch_fd(struct gg_http *h)
+{
+	struct gg_pubdir *p;
+	char *tmp;
+
+	if (!h) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (h->state == GG_STATE_ERROR) {
+		gg_debug(GG_DEBUG_MISC, "=> pubdir, watch_fd issued on failed session\n");
+		errno = EINVAL;
+		return -1;
+	}
+	
+	if (h->state != GG_STATE_PARSING) {
+		if (gg_http_watch_fd(h) == -1) {
+			gg_debug(GG_DEBUG_MISC, "=> pubdir, http failure\n");
+			errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (h->state != GG_STATE_PARSING)
+		return 0;
+	
+	h->state = GG_STATE_DONE;
+	
+	if (!(h->data = p = malloc(sizeof(struct gg_pubdir)))) {
+		gg_debug(GG_DEBUG_MISC, "=> pubdir, not enough memory for results\n");
+		return -1;
+	}
+
+	p->success = 0;
+	p->uin = 0;
+	
+	gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body);
+
+	if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
+		p->success = 1;
+		if (tmp[7] == ':')
+			p->uin = strtol(tmp + 8, NULL, 0);
+		gg_debug(GG_DEBUG_MISC, "=> pubdir, success (uin=%d)\n", p->uin);
+	} else
+		gg_debug(GG_DEBUG_MISC, "=> pubdir, error.\n");
+
+	return 0;
+}
+
+/*
+ * gg_pubdir_free()
+ *
+ * zwalnia pamięć po efektach operacji na katalogu publicznym.
+ *
+ *  - h - zwalniana struktura
+ */
+void gg_pubdir_free(struct gg_http *h)
+{
+	if (!h)
+		return;
+	
+	free(h->data);
+	gg_http_free(h);
+}
+
+/*
+ * gg_token()
+ *
+ * pobiera z serwera token do autoryzacji zakładania konta, usuwania
+ * konta i zmiany hasła.
+ *
+ * zaalokowana struct gg_http, którą poźniej należy zwolnić
+ * funkcją gg_token_free(), albo NULL jeśli wystąpił błąd.
+ */
+struct gg_http *gg_token(int async)
+{
+	struct gg_http *h;
+	const char *query;
+
+	query = "Host: " GG_REGISTER_HOST "\r\n"
+		"Content-Type: application/x-www-form-urlencoded\r\n"
+		"User-Agent: " GG_HTTP_USERAGENT "\r\n"
+		"Content-Length: 0\r\n"
+		"Pragma: no-cache\r\n"
+		"\r\n";
+
+	if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/regtoken.asp", query))) {
+		gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n");
+		return NULL;
+	}
+
+	h->type = GG_SESSION_TOKEN;
+
+	h->callback = gg_token_watch_fd;
+	h->destroy = gg_token_free;
+	
+	if (!async)
+		gg_token_watch_fd(h);
+	
+	return h;
+}
+
+/*
+ * gg_token_watch_fd()
+ *
+ * przy asynchronicznych operacjach związanych z tokenem należy wywoływać
+ * tę funkcję przy zmianach na obserwowanym deskryptorze.
+ *
+ *  - h - struktura opisująca połączenie
+ *
+ * jeśli wszystko poszło dobrze to 0, inaczej -1. operacja będzie
+ * zakończona, jeśli h->state == GG_STATE_DONE. jeśli wystąpi jakiś
+ * błąd, to będzie tam GG_STATE_ERROR i odpowiedni kod błędu w h->error.
+ */
+int gg_token_watch_fd(struct gg_http *h)
+{
+	if (!h) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (h->state == GG_STATE_ERROR) {
+		gg_debug(GG_DEBUG_MISC, "=> token, watch_fd issued on failed session\n");
+		errno = EINVAL;
+		return -1;
+	}
+	
+	if (h->state != GG_STATE_PARSING) {
+		if (gg_http_watch_fd(h) == -1) {
+			gg_debug(GG_DEBUG_MISC, "=> token, http failure\n");
+			errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (h->state != GG_STATE_PARSING)
+		return 0;
+	
+	/* jeśli h->data jest puste, to ściągaliśmy tokenid i url do niego,
+	 * ale jeśli coś tam jest, to znaczy, że mamy drugi etap polegający
+	 * na pobieraniu tokenu. */
+	if (!h->data) {
+		int width, height, length;
+		char *url = NULL, *tokenid = NULL, *path, *headers;
+		const char *host;
+		struct gg_http *h2;
+		struct gg_token *t;
+
+		gg_debug(GG_DEBUG_MISC, "=> token body \"%s\"\n", h->body);
+
+		if (h->body && (!(url = malloc(strlen(h->body))) || !(tokenid = malloc(strlen(h->body))))) {
+			gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for results\n");
+			free(url);
+			return -1;
+		}
+		
+		if (!h->body || sscanf(h->body, "%d %d %d\r\n%s\r\n%s", &width, &height, &length, tokenid, url) != 5) {
+			gg_debug(GG_DEBUG_MISC, "=> token, parsing failed\n");
+			free(url);
+			free(tokenid);
+			errno = EINVAL;
+			return -1;
+		}
+		
+		/* dostaliśmy tokenid i wszystkie niezbędne informacje,
+		 * więc pobierzmy obrazek z tokenem */
+
+		if (strncmp(url, "http://", 7)) {
+			path = gg_saprintf("%s?tokenid=%s", url, tokenid);
+			host = GG_REGISTER_HOST;
+		} else {
+			char *slash = strchr(url + 7, '/');
+
+			if (slash) {
+				path = gg_saprintf("%s?tokenid=%s", slash, tokenid);
+				*slash = 0;
+				host = url + 7;
+			} else {
+				gg_debug(GG_DEBUG_MISC, "=> token, url parsing failed\n");
+				free(url);
+				free(tokenid);
+				errno = EINVAL;
+				return -1;
+			}
+		}
+
+		if (!path) {
+			gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n");
+			free(url);
+			free(tokenid);
+			return -1;
+		}
+
+		if (!(headers = gg_saprintf("Host: %s\r\nUser-Agent: " GG_HTTP_USERAGENT "\r\n\r\n", host))) {
+			gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n");
+			free(path);
+			free(url);
+			free(tokenid);
+			return -1;
+		}			
+
+		if (!(h2 = gg_http_connect(host, GG_REGISTER_PORT, h->async, "GET", path, headers))) {
+			gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n");
+			free(headers);
+			free(url);
+			free(path);
+			free(tokenid);
+			return -1;
+		}
+
+		free(headers);
+		free(path);
+		free(url);
+
+		memcpy(h, h2, sizeof(struct gg_http));
+		free(h2);
+
+		h->type = GG_SESSION_TOKEN;
+
+		h->callback = gg_token_watch_fd;
+		h->destroy = gg_token_free;
+	
+		if (!h->async)
+			gg_token_watch_fd(h);
+
+		if (!(h->data = t = malloc(sizeof(struct gg_token)))) {
+			gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token data\n");
+			free(tokenid);
+			return -1;
+		}
+
+		t->width = width;
+		t->height = height;
+		t->length = length;
+		t->tokenid = tokenid;
+	} else {
+		/* obrazek mamy w h->body */
+		h->state = GG_STATE_DONE;
+	}
+	
+	return 0;
+}
+
+/*
+ * gg_token_free()
+ *
+ * zwalnia pamięć po efektach pobierania tokenu.
+ *
+ *  - h - zwalniana struktura
+ */
+void gg_token_free(struct gg_http *h)
+{
+	struct gg_token *t;
+
+	if (!h)
+		return;
+
+	if ((t = h->data))
+		free(t->tokenid);
+	
+	free(h->data);
+	gg_http_free(h);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/gg/lib/pubdir50.c	Sun Aug 28 22:46:01 2005 +0000
@@ -0,0 +1,467 @@
+/* $Id: pubdir50.c 13582 2005-08-28 22:46:01Z boler $ */
+
+/*
+ *  (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "libgadu.h"
+
+/*
+ * gg_pubdir50_new()
+ *
+ * tworzy nową zmienną typu gg_pubdir50_t.
+ *
+ * zaalokowana zmienna lub NULL w przypadku braku pamięci.
+ */
+gg_pubdir50_t gg_pubdir50_new(int type)
+{
+	gg_pubdir50_t res = malloc(sizeof(struct gg_pubdir50_s));
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_new(%d);\n", type);
+
+	if (!res) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_new() out of memory\n");
+		return NULL;
+	}
+
+	memset(res, 0, sizeof(struct gg_pubdir50_s));
+
+	res->type = type;
+
+	return res;
+}
+
+/*
+ * gg_pubdir50_add_n()  // funkcja wewnętrzna
+ *
+ * funkcja dodaje lub zastępuje istniejące pole do zapytania lub odpowiedzi.
+ *
+ *  - req - wskaźnik opisu zapytania,
+ *  - num - numer wyniku (0 dla zapytania),
+ *  - field - nazwa pola,
+ *  - value - wartość pola,
+ *
+ * 0/-1
+ */
+int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
+{
+	struct gg_pubdir50_entry *tmp = NULL, *entry;
+	char *dupfield, *dupvalue;
+	int i;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_add_n(%p, %d, \"%s\", \"%s\");\n", req, num, field, value);
+
+	if (!(dupvalue = strdup(value))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+		return -1;
+	}
+
+	for (i = 0; i < req->entries_count; i++) {
+		if (req->entries[i].num != num || strcmp(req->entries[i].field, field))
+			continue;
+
+		free(req->entries[i].value);
+		req->entries[i].value = dupvalue;
+
+		return 0;
+	}
+		
+	if (!(dupfield = strdup(field))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+		free(dupvalue);
+		return -1;
+	}
+
+	if (!(tmp = realloc(req->entries, sizeof(struct gg_pubdir50_entry) * (req->entries_count + 1)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+		free(dupfield);
+		free(dupvalue);
+		return -1;
+	}
+
+	req->entries = tmp;
+
+	entry = &req->entries[req->entries_count];
+	entry->num = num;
+	entry->field = dupfield;
+	entry->value = dupvalue;
+
+	req->entries_count++;
+
+	return 0;
+}
+
+/*
+ * gg_pubdir50_add()
+ *
+ * funkcja dodaje pole do zapytania.
+ *
+ *  - req - wskaźnik opisu zapytania,
+ *  - field - nazwa pola,
+ *  - value - wartość pola,
+ *
+ * 0/-1
+ */
+int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
+{
+	return gg_pubdir50_add_n(req, 0, field, value);
+}
+
+/*
+ * gg_pubdir50_seq_set()
+ *
+ * ustawia numer sekwencyjny zapytania.
+ *
+ *  - req - zapytanie,
+ *  - seq - nowy numer sekwencyjny.
+ *
+ * 0/-1.
+ */
+int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_seq_set(%p, %d);\n", req, seq);
+	
+	if (!req) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_seq_set() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	req->seq = seq;
+
+	return 0;
+}
+
+/*
+ * gg_pubdir50_free()
+ *
+ * zwalnia pamięć po zapytaniu lub rezultacie szukania użytkownika.
+ *
+ *  - s - zwalniana zmienna,
+ */
+void gg_pubdir50_free(gg_pubdir50_t s)
+{
+	int i;
+
+	if (!s)
+		return;
+	
+	for (i = 0; i < s->entries_count; i++) {
+		free(s->entries[i].field);
+		free(s->entries[i].value);
+	}
+
+	free(s->entries);
+	free(s);
+}
+
+/*
+ * gg_pubdir50()
+ *
+ * wysyła zapytanie katalogu publicznego do serwera.
+ *
+ *  - sess - sesja,
+ *  - req - zapytanie.
+ *
+ * numer sekwencyjny wyszukiwania lub 0 w przypadku błędu.
+ */
+uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
+{
+	int i, size = 5;
+	uint32_t res;
+	char *buf, *p;
+	struct gg_pubdir50_request *r;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
+	
+	if (!sess || !req) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
+		errno = EFAULT;
+		return 0;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
+		errno = ENOTCONN;
+		return 0;
+	}
+
+	for (i = 0; i < req->entries_count; i++) {
+		/* wyszukiwanie bierze tylko pierwszy wpis */
+		if (req->entries[i].num)
+			continue;
+		
+		size += strlen(req->entries[i].field) + 1;
+		size += strlen(req->entries[i].value) + 1;
+	}
+
+	if (!(buf = malloc(size))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
+		return 0;
+	}
+
+	r = (struct gg_pubdir50_request*) buf;
+	res = time(NULL);
+	r->type = req->type;
+	r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL));
+	req->seq = gg_fix32(r->seq);
+
+	for (i = 0, p = buf + 5; i < req->entries_count; i++) {
+		if (req->entries[i].num)
+			continue;
+
+		strcpy(p, req->entries[i].field);
+		p += strlen(p) + 1;
+
+		strcpy(p, req->entries[i].value);
+		p += strlen(p) + 1;
+	}
+
+	if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
+		res = 0;
+
+	free(buf);
+
+	return res;
+}
+
+/*
+ * gg_pubdir50_handle_reply()  // funkcja wewnętrzna
+ *
+ * analizuje przychodzący pakiet odpowiedzi i zapisuje wynik w struct gg_event.
+ *
+ *  - e - opis zdarzenia
+ *  - packet - zawartość pakietu odpowiedzi
+ *  - length - długość pakietu odpowiedzi
+ *
+ * 0/-1
+ */
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+{
+	const char *end = packet + length, *p;
+	struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet;
+	gg_pubdir50_t res;
+	int num = 0;
+	
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length);
+
+	if (!e || !packet) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (length < 5) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!(res = gg_pubdir50_new(r->type))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n");
+		return -1;
+	}
+
+	e->event.pubdir50 = res;
+
+	res->seq = gg_fix32(r->seq);
+
+	switch (res->type) {
+		case GG_PUBDIR50_READ:
+			e->type = GG_EVENT_PUBDIR50_READ;
+			break;
+
+		case GG_PUBDIR50_WRITE:
+			e->type = GG_EVENT_PUBDIR50_WRITE;
+			break;
+
+		default:
+			e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY;
+			break;
+	}
+
+	/* brak wyników? */
+	if (length == 5)
+		return 0;
+
+	/* pomiń początek odpowiedzi */
+	p = packet + 5;
+
+	while (p < end) {
+		const char *field, *value;
+
+		field = p;
+
+		/* sprawdź, czy nie mamy podziału na kolejne pole */
+		if (!*field) {
+			num++;
+			field++;
+		}
+
+		value = NULL;
+		
+		for (p = field; p < end; p++) {
+			/* jeśli mamy koniec tekstu... */
+			if (!*p) {
+				/* ...i jeszcze nie mieliśmy wartości pola to
+				 * wiemy, że po tym zerze jest wartość... */
+				if (!value)
+					value = p + 1;
+				else
+					/* ...w przeciwym wypadku koniec
+					 * wartości i możemy wychodzić
+					 * grzecznie z pętli */
+					break;
+			}
+		}
+		
+		/* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie
+		 * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
+		 * przez \0 */
+
+		if (p == end) {
+			gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n");
+			goto failure;
+		}
+
+		p++;
+
+		/* jeśli dostaliśmy namier na następne wyniki, to znaczy że
+		 * mamy koniec wyników i nie jest to kolejna osoba. */
+		if (!strcasecmp(field, "nextstart")) {
+			res->next = atoi(value);
+			num--;
+		} else {
+			if (gg_pubdir50_add_n(res, num, field, value) == -1)
+				goto failure;
+		}
+	}	
+
+	res->count = num + 1;
+	
+	return 0;
+
+failure:
+	gg_pubdir50_free(res);
+	return -1;
+}
+
+/*
+ * gg_pubdir50_get()
+ *
+ * pobiera informację z rezultatu wyszukiwania.
+ *
+ *  - res - rezultat wyszukiwania,
+ *  - num - numer odpowiedzi,
+ *  - field - nazwa pola (wielkość liter nie ma znaczenia).
+ *
+ * wartość pola lub NULL, jeśli nie znaleziono.
+ */
+const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
+{
+	char *value = NULL;
+	int i;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field);
+
+	if (!res || num < 0 || !field) {
+		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	for (i = 0; i < res->entries_count; i++) {
+		if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) {
+			value = res->entries[i].value;
+			break;
+		}
+	}
+
+	return value;
+}
+
+/*
+ * gg_pubdir50_count()
+ *
+ * zwraca ilość wyników danego zapytania.
+ *
+ *  - res - odpowiedź
+ *
+ * ilość lub -1 w przypadku błędu.
+ */
+int gg_pubdir50_count(gg_pubdir50_t res)
+{
+	return (!res) ? -1 : res->count;
+}
+
+/*
+ * gg_pubdir50_type()
+ *
+ * zwraca rodzaj zapytania lub odpowiedzi.
+ *
+ *  - res - zapytanie lub odpowiedź
+ *
+ * ilość lub -1 w przypadku błędu.
+ */
+int gg_pubdir50_type(gg_pubdir50_t res)
+{
+	return (!res) ? -1 : res->type;
+}
+
+/*
+ * gg_pubdir50_next()
+ *
+ * zwraca numer, od którego należy rozpocząć kolejne wyszukiwanie, jeśli
+ * zależy nam na kolejnych wynikach.
+ *
+ *  - res - odpowiedź
+ *
+ * numer lub -1 w przypadku błędu.
+ */
+uin_t gg_pubdir50_next(gg_pubdir50_t res)
+{
+	return (!res) ? (unsigned) -1 : res->next;
+}
+
+/*
+ * gg_pubdir50_seq()
+ *
+ * zwraca numer sekwencyjny zapytania lub odpowiedzi.
+ *
+ *  - res - zapytanie lub odpowiedź
+ *
+ * numer lub -1 w przypadku błędu.
+ */
+uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
+{
+	return (!res) ? (unsigned) -1 : res->seq;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
--- a/src/protocols/gg/libgg.c	Sun Aug 28 22:21:24 2005 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1591 +0,0 @@
-/*
- *  (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>,
- *                     Robert J. Woźny <speedy@ziew.org>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License Version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <sys/types.h>
-#ifndef _WIN32
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <netdb.h>
-#include <pwd.h>
-#else
-#include <fcntl.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/time.h>
-#include <errno.h>
-#ifndef _AIX
-#  include <string.h>
-#endif
-#include <stdarg.h>
-#include <time.h>
-#ifdef sun
-  #include <sys/filio.h>
-#endif
-#include <glib.h>
-#if G_BYTE_ORDER == G_BIG_ENDIAN
-#  define WORDS_BIGENDIAN 1
-#endif
-#include "internal.h"
-#include "libgg.h"
-
-#include "proxy.h"
-#include "debug.h"
-
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
-int gg_debug_level = (GG_DEBUG_NET | GG_DEBUG_TRAFFIC | GG_DEBUG_DUMP | GG_DEBUG_FUNCTION | GG_DEBUG_MISC);
-int gg_http_use_proxy = 0;
-int gg_http_proxy_port = 0;
-char *gg_http_proxy_host = NULL;
-
-/*
- * fix32() // funkcja wewnętrzna
- *
- * dla maszyn big-endianowych zamienia kolejność bajtów w ,,long''ach.
- */
-static inline unsigned long fix32(unsigned long x)
-{
-#ifndef WORDS_BIGENDIAN
-	return x;
-#else
-	return (unsigned long)
-		(((x & (unsigned long) 0x000000ffU) << 24) |
-                 ((x & (unsigned long) 0x0000ff00U) << 8) |
-                 ((x & (unsigned long) 0x00ff0000U) >> 8) |
-                 ((x & (unsigned long) 0xff000000U) >> 24));
-#endif		
-}
-
-/*
- * fix16() // funkcja wewnętrzna
- *
- * dla maszyn big-endianowych zamienia kolejność bajtów w ,,short''ach.
- */
-
-static inline unsigned short fix16(unsigned short x)
-{
-#ifndef WORDS_BIGENDIAN
-	return x;
-#else
-	return (unsigned short)
-		(((x & (unsigned short) 0x00ffU) << 8) |
-                 ((x & (unsigned short) 0xff00U) >> 8));
-#endif
-}
-
-#ifndef _WIN32
-/*
- * gg_resolve() // funkcja wewnętrzna
- *
- * tworzy pipe'y, forkuje się i w drugim procesie zaczyna resolvować 
- * podanego hosta. zapisuje w sesji deskryptor pipe'u. jeśli coś tam
- * będzie gotowego, znaczy, że można wczytać ,,struct in_addr''. jeśli
- * nie znajdzie, zwraca INADDR_NONE.
- *
- *  - fd - wskaźnik gdzie wrzucić deskryptor,
- *  - pid - gdzie wrzucić pid dzieciaka,
- *  - hostname - nazwa hosta do zresolvowania.
- *
- * zwraca 0 jeśli udało się odpalić proces lub -1 w przypadku błędu.
- */
-int gg_resolve(int *fd, int *pid, char *hostname)
-{
-	int pipes[2], res;
-	struct in_addr a;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(..., \"%s\");\n", hostname);
-	
-	if (!fd | !pid) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (pipe(pipes) == -1)
-		return -1;
-
-	if ((res = fork()) == -1)
-		return -1;
-
-	if (!res) {
-		if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
-			struct hostent *he;
-		
-			if (!(he = gethostbyname(hostname)))
-				a.s_addr = INADDR_NONE;
-			else
-				memcpy((char*) &a, he->h_addr, sizeof(a));
-		}
-
-		write(pipes[1], &a, sizeof(a));
-
-		exit(0);
-	}
-
-	close(pipes[1]);
-
-	*fd = pipes[0];
-	*pid = res;
-
-	return 0;
-}
-#endif /*!_WIN32*/
-
-/*
- * gg_recv_packet() // funkcja wewnętrzna
- *
- * odbiera jeden pakiet gg i zwraca wskaźnik do niego. pamięć po nim
- * wypadałoby uwolnić.
- *
- *  - sock - połączony socket.
- *
- * jeśli wystąpił błąd, zwraca NULL. reszta w errno.
- */
-static void *gg_recv_packet(struct gg_session *sess)
-{
-	struct gg_header h;
-	char *buf = NULL;
-	int ret = 0, offset, size = 0;
-	int sizeh = sizeof(struct gg_header);
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(...);\n");
-	
-	if (!sess) {
-		errno = EFAULT;
-		return NULL;
-	}
-
-	if (sess->recv_left < 1) {
-		while (ret != sizeh) {
-			ret = read(sess->fd, &h, sizeh);
-			gg_debug(GG_DEBUG_MISC, "-- header recv(..., %d) = %d\n", sizeh, ret);
-			if (ret < sizeh) {
-				if (errno != EINTR) {
-					gg_debug(GG_DEBUG_MISC, "-- errno = %d (%s)\n", errno, strerror(errno));
-					return NULL;
-				}
-			}
-		}
-
-		h.type = fix32(h.type);
-		h.length = fix32(h.length);
-	} else {
-		memcpy(&h, sess->recv_buf, sizeh);
-	}
-
-	/* jakieś sensowne limity na rozmiar pakietu */
-	if (h.length < 0 || h.length > 65535) {
-		gg_debug(GG_DEBUG_MISC, "-- invalid packet length (%d)\n", h.length);
-		errno = ERANGE;
-		return NULL;
-	}
-
-	if (sess->recv_left > 0) {
-		gg_debug(GG_DEBUG_MISC, "-- resuming last gg_recv_packet()\n");
-		size = sess->recv_left;
-		offset = sess->recv_done;
-		buf = sess->recv_buf;
-	} else {
-		if (!(buf = malloc(sizeh + h.length + 1))) {
-			gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
-			return NULL;
-		}
-
-		memcpy(buf, &h, sizeh);
-
-		offset = 0;
-		size = h.length;
-	}
-
-	while (size > 0) {
-		ret = read(sess->fd, buf + sizeh + offset, size);
-		gg_debug(GG_DEBUG_MISC, "-- body recv(..., %d) = %d\n", size, ret);
-		if (ret > -1 && ret <= size) {
-			offset += ret;
-			size -= ret;
-		} else if (ret == -1) {	
-			gg_debug(GG_DEBUG_MISC, "-- errno = %d (%s)\n", errno, strerror(errno));
-			if (errno == EAGAIN) {
-				gg_debug(GG_DEBUG_MISC, "-- %d bytes received, %d left\n", offset, size);
-				sess->recv_buf = buf;
-				sess->recv_left = size;
-				sess->recv_done = offset;
-				return NULL;
-			}
-			if (errno != EINTR) {
-				/* errno = EINVAL; */
-				free(buf);
-				return NULL;
-			}
-		}
-	}
-
-	sess->recv_left = 0;
-
-	if ((gg_debug_level & GG_DEBUG_DUMP)) {
-		int i;
-
-		gg_debug(GG_DEBUG_DUMP, ">> received packet (type=%.2x):", h.type);
-		for (i = 0; i < sizeh + h.length; i++) 
-			gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
-		gg_debug(GG_DEBUG_DUMP, "\n");
-	}
-
-	return buf;
-}
-
-/*
- * gg_send_packet() // funkcja wewnętrzna
- *
- * konstruuje pakiet i wysyła go do serwera.
- *
- *  - sock - deskryptor gniazda
- *  - type - typ pakietu
- *  - payload_1 - pierwsza część pakietu
- *  - payload_length_1 - długość pierwszej części
- *  - payload_2 - druga część pakietu
- *  - payload_length_2 - długość drugiej części
- *  - ... - kolejne części pakietu i ich długości
- *  - NULL - końcowym parametr (konieczny!)
- *
- * jeśli się powiodło, zwraca 0, w przypadku błędu -1. jeśli errno == ENOMEM,
- * zabrakło pamięci. inaczej był błąd przy wysyłaniu pakietu. dla errno == 0
- * nie wysłano całego pakietu.
- */
-int gg_send_packet(struct gg_session *sess, int type, ...)
-{
-	struct gg_header *h;
-	char *tmp;
-	int tmp_length;
-	void *payload;
-	int payload_length;
-	va_list ap;
-	int res;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type);
-
-	tmp_length = 0;
-
-	if (!(tmp = malloc(sizeof(struct gg_header)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
-		return -1;
-	}
-
-	h = (struct gg_header*) tmp;
-	h->type = fix32(type);
-	h->length = fix32(0);
-
-	va_start(ap, type);
-
-	payload = va_arg(ap, void *);
-
-	while (payload) {
-		char *tmp2;
-
-		payload_length = va_arg(ap, int);
-
-		if (payload_length < 0)
-			gg_debug(GG_DEBUG_MISC, "// gg_send_packet() invalid payload length (%d)\n", payload_length);
-	
-		if (!(tmp2 = realloc(tmp, sizeof(struct gg_header) + tmp_length + payload_length))) {
-                        gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n");
-			free(tmp);
-			va_end(ap);
-                        return -1;
-                }
-
-		tmp = tmp2;
-		
-		memcpy(tmp + sizeof(struct gg_header) + tmp_length, payload, payload_length);
-		tmp_length += payload_length;
-
-		payload = va_arg(ap, void *);
-	}
-
-	va_end(ap);
-
-	h = (struct gg_header*) tmp;
-	h->length = fix32(tmp_length);
-
-	if ((gg_debug_level & GG_DEBUG_DUMP)) {
-                unsigned int i;
-		
-                gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", fix32(h->type));
-                for (i = 0; i < sizeof(struct gg_header) + fix32(h->length); i++)
-                        gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
-                gg_debug(GG_DEBUG_DUMP, "\n");
-        }
-	
-	tmp_length += sizeof(struct gg_header);
-	
-	if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) {
-		gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
-		free(tmp);
-		return -1;
-	}
-	
-	free(tmp);	
-	return 0;
-}
-
-/*
- * gg_write() // funkcja pomocnicza
- *
- * zapisuje do gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy
- * połączenie zwykłe czy TLS.
- *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - ilość bajtów,
- *
- * takie same wartości jak write().
- */
-int gg_write(struct gg_session *sess, const char *buf, int length)
-{
-	int res;
-
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-	if (sess->ssl) {
-		int err;
-
-		res = SSL_write(sess->ssl, buf, length);
-
-		if (res < 0) {
-			err = SSL_get_error(sess->ssl, res);
-
-			if (err == SSL_ERROR_WANT_WRITE)
-				errno = EAGAIN;
-
-			return -1;
-		}
-	} else
-#endif
-		res = write(sess->fd, buf, length);
-
-	return res;
-}
-
-
-#ifndef _WIN32
-/*
- * gg_login()
- *
- * rozpoczyna procedurę łączenia się z serwerem. resztę obsłguje się przez
- * gg_watch_event.
- *
- *  - uin - numerek usera,
- *  - password - jego hasełko,
- *  - async - ma być asynchronicznie?
- *
- * UWAGA! program musi obsłużyć SIGCHLD, jeśli łączy się asynchronicznie,
- * żeby zrobić pogrzeb zmarłemu procesowi resolvera.
- *
- * w przypadku błędu zwraca NULL, jeśli idzie dobrze (async) albo poszło
- * dobrze (sync), zwróci wskaźnik do zaalokowanej struktury `gg_session'.
- */
-struct gg_session *gg_login(uin_t uin, char *password, int async)
-{
-	struct gg_session *sess;
-	char *hostname;
-	int port;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%u, \"...\", %d);\n", uin, async);
-
-	if (!(sess = malloc(sizeof(*sess))))
-		return NULL;
-
-	sess->uin = uin;
-	if (!(sess->password = strdup(password))) {
-		free(sess);
-		return NULL;
-	}
-	sess->state = GG_STATE_RESOLVING;
-	sess->check = GG_CHECK_READ;
-	sess->async = async;
-	sess->seq = 0;
-	sess->recv_left = 0;
-	sess->last_pong = 0;
-	sess->server_ip = 0;
-	sess->initial_status = 0;
-        sess->type = GG_SESSION_GG;
-	
-	if (gg_http_use_proxy) {
-		hostname = gg_http_proxy_host;
-		port = gg_http_proxy_port;
-	} else {
-		hostname = GG_APPMSG_HOST;
-		port = GG_APPMSG_PORT;
-	};
-
-	if (async) {
-		if (gg_resolve(&sess->fd, &sess->pid, hostname)) {
-			gg_debug(GG_DEBUG_MISC, "-- resolving failed\n");
-			free(sess);
-			return NULL;
-		}
-	} else {
-		struct in_addr a;
-
-		if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
-			struct hostent *he;
-	
-			if (!(he = gethostbyname(hostname))) {
-				gg_debug(GG_DEBUG_MISC, "-- host %s not found\n", hostname);
-				free(sess);
-				return NULL;
-			} else
-				memcpy((char*) &a, he->h_addr, sizeof(a));
-		}
-
-		if (!(sess->fd = gg_connect(&a, port, 0)) == -1) {
-			gg_debug(GG_DEBUG_MISC, "-- connection failed\n");
-			free(sess);
-			return NULL;
-		}
-
-		sess->state = GG_STATE_CONNECTING;
-
-		while (sess->state != GG_STATE_CONNECTED) {
-			struct gg_event *e;
-
-			if (!(e = gg_watch_fd(sess))) {
-				gg_debug(GG_DEBUG_MISC, "-- some nasty error in gg_watch_fd()\n");
-				free(sess);
-				return NULL;
-			}
-
-			if (e->type == GG_EVENT_CONN_FAILED) {
-				errno = EACCES;
-				gg_debug(GG_DEBUG_MISC, "-- could not login\n");
-				gg_free_event(e);
-				free(sess);
-				return NULL;
-			}
-
-			gg_free_event(e);
-		}
-	}
-
-	return sess;
-}
-#endif /*!_WIN32*/
-
-/* 
- * gg_login_hash() // funkcja wewnętrzna
- * 
- * liczy hash z hasła i danego seeda.
- * 
- *  - password - hasło do hashowania
- *  - seed - wartość podana przez serwer
- *
- * hash.
- */
-unsigned int gg_login_hash(const char *password, unsigned int seed)
-{
-	unsigned int x, y, z;
-
-	y = seed;
-
-	for (x = 0; *password; password++) {
-		x = (x & 0xffffff00) | *password;
-		y ^= x;
-		y += x;
-		x <<= 8;
-		y ^= x;
-		x <<= 8;
-		y -= x;
-		x <<= 8;
-		y ^= x;
-
-		z = y & 0x1F;
-		y = (y << z) | (y >> (32 - z));
-	}
-
-	return y;
-}
-
-/* 
- * gg_free_session()
- *
- * zwalnia pamięć zajmowaną przez opis sesji.
- *
- *  - sess - opis sesji.
- *
- * nie zwraca niczego, bo i po co?
- */
-void gg_free_session(struct gg_session *sess)
-{
-	if (!sess)
-		return;
-
-	free(sess->password);
-	free(sess);
-}
-
-/*
- * gg_change_status()
- *
- * zmienia status użytkownika. przydatne do /away i /busy oraz /quit.
- *
- *  - sess - opis sesji,
- *  - status - nowy status użytkownika.
- *
- * jeśli wysłał pakiet zwraca 0, jeśli nie udało się, zwraca -1.
- */
-int gg_change_status(struct gg_session *sess, int status)
-{
-	struct gg_new_status p;
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(..., %d);\n", status);
-
-	p.status = fix32(status);
-
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL, 0);
-}
-
-/*
- * gg_change_status_descr()
- *
- * zmienia status uøytkownika na opisowy.
- *
- *  - sess - opis sesji
- *  - status - nowy status uøytkownika
- *  - descr - opis statusu
- *
- * 0, -1.
- */
-int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
-{
-	struct gg_new_status p;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
-
-	if (!sess || !descr) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	p.status = fix32(status);
-
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL);
-}
-
-/*
- * gg_logoff()
- *
- * wylogowuje użytkownika i zamyka połączenie.
- *
- *  - sock - deskryptor socketu.
- *
- * nie zwraca błędów. skoro się żegnamy, to olewamy wszystko.
- */
-void gg_logoff(struct gg_session *sess)
-{
-	if (!sess)
-		return;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(...);\n");
-
-	if (sess->state == GG_STATE_CONNECTED)
-		gg_change_status(sess, GG_STATUS_NOT_AVAIL);
-	
-	if (sess->fd) {
-		shutdown(sess->fd, 2);
-		close(sess->fd);
-	}
-}
-
-/*
- * gg_send_message()
- *
- * wysyła wiadomość do innego użytkownika. zwraca losowy numer
- * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomości
- *  - recipient - numer adresata
- *  - message - treść wiadomości
- *
- * numer sekwencyjny wiadomości lub -1 w przypadku błędu.
- */
-int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const char *message)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);
-
-	return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0);
-}
-
-/*
- * gg_send_message_richtext()
- *
- * wysyła kolorową wiadomość do innego użytkownika. zwraca losowy numer
- * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomości
- *  - recipient - numer adresata
- *  - message - treść wiadomości
- *  - format - informacje o formatowaniu
- *  - formatlen - długość informacji o formatowaniu
- *
- * numer sekwencyjny wiadomości lub -1 w przypadku błędu.
- */
-int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const char *message, const unsigned char *format, int formatlen)
-{
-	struct gg_send_msg s;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	s.recipient = fix32(recipient);
-	if (!sess->seq)
-		sess->seq = 0x01740000 | (rand() & 0xffff);
-	s.seq = fix32(sess->seq);
-	s.msgclass = fix32(msgclass);
-	sess->seq += (rand() % 0x300) + 0x300;
-
-	if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, format, formatlen, NULL) == -1)
-		return -1;
-
-	return fix32(s.seq);
-}
-
-/*
- * gg_ping()
- *
- * wysyła do serwera pakiet typu yeah-i'm-still-alive.
- *
- *  - sess - zgadnij.
- *
- * jeśli nie powiodło się wysłanie pakietu, zwraca -1. otherwise 0.
- */
-int gg_ping(struct gg_session *sess)
-{
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(...);\n");
-
-	return gg_send_packet(sess, GG_PING, NULL);
-}
-
-/*
- * gg_free_event()
- *
- * zwalnia pamięć zajmowaną przez informację o zdarzeniu
- *
- *  - event - wskaźnik do informacji o zdarzeniu
- *
- * nie ma czego zwracać.
- */
-void gg_free_event(struct gg_event *e)
-{
-	if (!e)
-		return;
-	if (e->type == GG_EVENT_MSG)
-		free(e->event.msg.message);
-	if (e->type == GG_EVENT_NOTIFY)
-		free(e->event.notify);
-	free(e);
-}
-
-/*
- * gg_notify()
- *
- * wysyła serwerowi listę ludków, za którymi tęsknimy.
- *
- *  - sess - identyfikator sesji,
- *  - userlist - wskaźnik do tablicy numerów,
- *  - count - ilość numerków.
- *
- * jeśli udało się, zwraca 0. jeśli błąd, dostajemy -1.
- */
-int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
-{
-	struct gg_notify *n;
-	uin_t *u;
-	int i, res = 0;
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-	
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(..., %d);\n", count);
-	
-	if (!userlist || !count)
-		return 0;
-	
-	if (!(n = (struct gg_notify*) malloc(sizeof(*n) * count)))
-		return -1;
-	
-	for (u = userlist, i = 0; i < count; u++, i++) { 
-		n[i].uin = fix32(*u);
-		n[i].dunno1 = 3;
-	}
-	
-	if (gg_send_packet(sess, GG_NOTIFY, n, sizeof(*n) * count, NULL, 0) == -1)
-		res = -1;
-
-	free(n);
-
-	return res;
-}
-
-/*
- * gg_add_notify()
- *
- * dodaje w locie do listy ukochanych dany numerek.
- *
- *  - sess - identyfikator sesji,
- *  - uin - numerek ukochanej.
- *
- * jeśli udało się wysłać, daje 0. inaczej -1.
- */
-int gg_add_notify(struct gg_session *sess, uin_t uin)
-{
-	struct gg_add_remove a;
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-	
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify(..., %u);\n", uin);
-	
-	a.uin = fix32(uin);
-	a.dunno1 = 3;
-	
-	return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
-}
-
-/*
- * gg_remove_notify()
- *
- * w locie usuwa z listy zainteresowanych.
- *
- *  - sess - id sesji,
- *  - uin - numerek.
- *
- * zwraca -1 jeśli był błąd, 0 jeśli się udało wysłać pakiet.
- */
-int gg_remove_notify(struct gg_session *sess, uin_t uin)
-{
-	struct gg_add_remove a;
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify(..., %u);\n", uin);
-	
-	a.uin = fix32(uin);
-	a.dunno1 = 3;
-	
-	return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL, 0);
-}
-
-/*
- * gg_userlist_request()
- *
- * wysyła żądanie/zapytanie listy kontaktów na serwerze.
- *
- *  - sess - opis sesji
- *  - type - rodzaj zapytania/żądania
- *  - request - treść zapytania/żądania (może być NULL)
- *
- * 0, -1
- */
-int gg_userlist_request(struct gg_session *sess, char type, const char *request)
-{
-	int len;
-
-	if (!sess) {
-		errno = EINVAL;
-		return -1;
-	}
-	
-	if (!request) {
-		sess->userlist_blocks = 1;
-		return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
-	}
-	
-	len = strlen(request);
-
-	sess->userlist_blocks = 0;
-
-	while (len > 2047) {
-		sess->userlist_blocks++;
-
-		if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
-			return -1;
-
-		if (type == GG_USERLIST_PUT)
-			type = GG_USERLIST_PUT_MORE;
-
-		request += 2047;
-		len -= 2047;
-	}
-
-	sess->userlist_blocks++;
-
-	return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
-}
-
-/*
- * gg_watch_fd_connected() // funkcja wewnętrzna
- *
- * patrzy na socketa, odbiera pakiet i wypełnia strukturę zdarzenia.
- *
- *  - sock - lalala, trudno zgadnąć.
- *
- * jeśli błąd -1, jeśli dobrze 0.
- */
-static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
-{
-	struct gg_header *h;
-	char *p;
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(...);\n");
-
-	if (!(h = gg_recv_packet(sess))) {
-		gg_debug(GG_DEBUG_MISC, "-- gg_recv_packet failed. errno = %d (%d)\n", errno, strerror(errno));
-		return -1;
-	}
-
-	p = (void *)h + sizeof(struct gg_header);
-
-
-	switch (h->type) {
-	    case GG_RECV_MSG:
-	    {
-		struct gg_recv_msg *r = (void *)p;
-
-		gg_debug(GG_DEBUG_MISC, "-- received a message\n");
-
-		if (h->length >= sizeof(*r)) {
-			e->type = GG_EVENT_MSG;
-			e->event.msg.msgclass = fix32(r->msgclass);
-			e->event.msg.sender = fix32(r->sender);
-			e->event.msg.message = strdup((char*) r + sizeof(*r));
-			e->event.msg.time = fix32(r->time);
-		}
-		break;
-	    }
-	    case GG_NOTIFY_REPLY:
-	    {
-		struct gg_notify_reply *n = (void *)p;
-		int count, i;
-
-		gg_debug(GG_DEBUG_MISC, "-- received a notify reply\n");
-		
-		e->type = GG_EVENT_NOTIFY;
-		if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) {
-			gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
-			free(h);
-			return -1;
-		}
-		count = h->length / sizeof(*n);
-		memcpy(e->event.notify, p, h->length);
-		e->event.notify[count].uin = 0;
-		for (i = 0; i < count; i++) {
-			e->event.notify[i].uin = fix32(e->event.notify[i].uin);
-			e->event.notify[i].status = fix32(e->event.notify[i].status);
-		}
-		break;
-	    }
-	    
-	    case GG_NOTIFY_REPLY60:
-	    {
-		    struct gg_notify_reply60 *n = (void*) p;
-		    unsigned int length = h->length, i = 0;
-
-		    gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-		    e->type = GG_EVENT_NOTIFY60;
-		    e->event.notify60 = malloc(sizeof(*e->event.notify60));
-
-		    if (!e->event.notify60) {
-			    gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-			    goto fail;
-		    }
-
-		    e->event.notify60[0].uin = 0;
-		    
-		    while (length >= sizeof(struct gg_notify_reply60)) {
-			    uin_t uin = fix32(n->uin);
-			    char *tmp;
-
-			    e->event.notify60[i].uin = uin & 0x00ffffff;
-			    e->event.notify60[i].status = n->status;
-			    e->event.notify60[i].remote_ip = n->remote_ip;
-			    e->event.notify60[i].remote_port = fix16(n->remote_port);
-			    e->event.notify60[i].version = n->version;
-			    e->event.notify60[i].image_size = n->image_size;
-			    e->event.notify60[i].descr = NULL;
-			    e->event.notify60[i].time = 0;
-
-			    if (GG_S_D(n->status)) {
-				    unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
-
-				    if (descr_len < length) {
-					    if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) {
-						    gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-						    goto fail;
-					    }
-
-					    memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len);
-					    e->event.notify60[i].descr[descr_len] = 0;
-
-					    /* XXX czas */
-				    }
-				    
-				    length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
-				    n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
-			    } else {
-				    length -= sizeof(struct gg_notify_reply60);
-				    n = (void*) ((char*) n + sizeof(struct gg_notify_reply60));
-			    }
-
-			    if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
-				    gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-				    free(e->event.notify60);
-				    goto fail;
-			    }
-
-			    e->event.notify60 = (void*) tmp;
-			    e->event.notify60[++i].uin = 0;
-		    }
-
-		    break;
-	    }
-
-	    case GG_STATUS:
-	    {
-		struct gg_status *s = (void *)p;
-
-		gg_debug(GG_DEBUG_MISC, "-- received a status change\n");
-
-		if (h->length >= sizeof(*s)) {
-			e->type = GG_EVENT_STATUS;
-			memcpy(&e->event.status, p, sizeof(*s));
-			e->event.status.uin = fix32(e->event.status.uin);
-			e->event.status.status = fix32(e->event.status.status);
-		}
-		break;
-	    }
-	    
-	    case GG_STATUS60:
-	    {
-		    struct gg_status60 *s = (void*) p;
-		    uint32_t uin;
-
-		    gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
-
-		    if (h->length < sizeof(*s))
-			    break;
-
-		    uin = fix32(s->uin);
-
-		    e->type = GG_EVENT_STATUS60;
-		    e->event.status60.uin = uin & 0x00ffffff;
-		    e->event.status60.status = s->status;
-		    e->event.status60.remote_ip = s->remote_ip;
-		    e->event.status60.remote_port = fix16(s->remote_port);
-		    e->event.status60.version = s->version;
-		    e->event.status60.image_size = s->image_size;
-		    e->event.status60.descr = NULL;
-		    e->event.status60.time = 0;
-
-		    if (uin & 0x40000000)
-			    e->event.status60.version |= GG_HAS_AUDIO_MASK;
-
-		    if (h->length > sizeof(*s)) {
-			    int len = h->length - sizeof(*s);
-			    char *buf = malloc(len + 1);
-
-			    if (buf) {
-				    memcpy(buf, (char*) p + sizeof(*s), len);
-				    buf[len] = 0;
-			    }
-
-			    e->event.status60.descr = buf;
-
-			    if (len > 4 && p[h->length - 5] == 0) {
-				    uint32_t t;
-				    memcpy(&t, p + h->length - 4, sizeof(uint32_t));
-				    e->event.status60.time = t;
-			    }
-		    }
-
-		    break;
-	    }
-
-	    case GG_SEND_MSG_ACK:
-	    {
-		struct gg_send_msg_ack *s = (void *)p;
-
-		gg_debug(GG_DEBUG_MISC, "-- received a message ack\n");
-
-		if (h->length >= sizeof(*s)) {
-			e->type = GG_EVENT_ACK;
-			e->event.ack.status = fix32(s->status);
-			e->event.ack.recipient = fix32(s->recipient);
-			e->event.ack.seq = fix32(s->seq);
-		}
-		break;
-	    }
-
-	    case GG_PONG:
-	    {
-		gg_debug(GG_DEBUG_MISC, "-- received a pong\n");
-		sess->last_pong = time(NULL);
-		break;
-	    }
-
-	    case GG_USERLIST_REPLY:
-	    {
-		    gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
-
-		    if (h->length < 1)
-			    break;
-
-		    /* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
-		     * gdy otrzymano wszystkie odpowiedzi */
-		    if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) {
-			    if (--sess->userlist_blocks)
-				    break;
-
-			    p[0] = GG_USERLIST_PUT_REPLY;
-		    }
-
-		    if (h->length > 1) {
-			    char *tmp;
-			    int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0;
-			    
-			    gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
-			    
-			    if (!(tmp = realloc(sess->userlist_reply, len + h->length))) {
-				    gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
-				    free(sess->userlist_reply);
-				    sess->userlist_reply = NULL;
-				    goto fail;
-			    }
-
-			    sess->userlist_reply = tmp;
-			    sess->userlist_reply[len + h->length - 1] = 0;
-			    memcpy(sess->userlist_reply + len, p + 1, h->length - 1);
-		    }
-
-		    if (p[0] == GG_USERLIST_GET_MORE_REPLY)
-			    break;
-
-		    e->type = GG_EVENT_USERLIST;
-		    e->event.userlist.type = p[0];
-		    e->event.userlist.reply = sess->userlist_reply;
-		    sess->userlist_reply = NULL;
-
-		    break;
-		}
-
-	    default:
-		gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
-	}
-
-	free(h);
-
-	return 0;
-	
-fail:
-	free(h);
-	return -1;
-}
-
-/*
- * gg_watch_fd()
- *
- * funkcja wywoływana, gdy coś się stanie na obserwowanym deskryptorze.
- * zwraca klientowi informację o tym, co się dzieje.
- *
- *  - sess - identyfikator sesji.
- *
- * zwraca wskaźnik do struktury gg_event, którą trzeba zwolnić później
- * za pomocą gg_free_event(). jesli rodzaj zdarzenia jest równy
- * GG_EVENT_NONE, należy je olać kompletnie. jeśli zwróciło NULL,
- * stało się coś niedobrego -- albo brakło pamięci albo zerwało
- * połączenie albo coś takiego.
- */
-struct gg_event *gg_watch_fd(struct gg_session *sess)
-{
-	struct gg_event *e;
-	int res = 0;
-#ifndef _WIN32
-	int port;
-#endif
-
-	if (!sess) {
-		errno = EFAULT;
-		return NULL;
-	}
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(...);\n");
-
-	if (!(e = (void*) malloc(sizeof(*e)))) {
-		gg_debug(GG_DEBUG_MISC, "-- not enough memory\n");
-		return NULL;
-	}
-
-	e->type = GG_EVENT_NONE;
-
-	switch (sess->state) {
-#ifndef _WIN32
-		/* Apparantly we will never be in this state as long as we are
-		   using gaim_proxy_connect instead of gg_login - Herman */
-		case GG_STATE_RESOLVING:
-		{
-			struct in_addr a;
-
-			gg_debug(GG_DEBUG_MISC, "== GG_STATE_RESOLVING\n");
-
-			if (read(sess->fd, &a, sizeof(a)) < sizeof(a) || a.s_addr == INADDR_NONE) {
-				gg_debug(GG_DEBUG_MISC, "-- resolving failed\n");				
-
-				errno = ENOENT;
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_RESOLVING;
-				sess->state = GG_STATE_IDLE;
-
-				close(sess->fd);
-
-				break;
-			}
-			
-			sess->server_ip = a.s_addr;
-
-			close(sess->fd);
-
-			waitpid(sess->pid, NULL, 0);
-
-			gg_debug(GG_DEBUG_MISC, "-- resolved, now connecting\n");
-			
-			if (gg_http_use_proxy) {
-				port = gg_http_proxy_port;
-			} else {
-				port = GG_APPMSG_PORT;
-			};
-
-			if ((sess->fd = gg_connect(&a, port, sess->async)) == -1) {
-				struct in_addr *addr = (struct in_addr*) &sess->server_ip;
-				
-				gg_debug(GG_DEBUG_MISC, "-- connection failed, trying direct connection\n");
-
-				if ((sess->fd = gg_connect(addr, GG_DEFAULT_PORT, sess->async)) == -1) {
-				    gg_debug(GG_DEBUG_MISC, "-- connection failed, trying https connection\n");
-				    if ((sess->fd = gg_connect(&a, GG_HTTPS_PORT, sess->async)) == -1) {		
-					gg_debug(GG_DEBUG_MISC, "-- connect() failed. errno = %d (%s)\n", errno, strerror(errno));
-
-					e->type = GG_EVENT_CONN_FAILED;
-					e->event.failure = GG_FAILURE_CONNECTING;
-					sess->state = GG_STATE_IDLE;
-					break;
-				    }
-				}
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->check = GG_CHECK_WRITE;
-			} else {
-				sess->state = GG_STATE_CONNECTING;
-				sess->check = GG_CHECK_WRITE;
-			}
-				
-			break;
-		}
-#endif /* !_WIN32 */
-		case GG_STATE_CONNECTING:
-		{
-			char buf[1024];
-			unsigned int res, res_size = sizeof(res);
-
-			gg_debug(GG_DEBUG_MISC, "== GG_STATE_CONNECTING\n");
-
-			if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-#if 0
-				struct in_addr *addr = (struct in_addr*) &sess->server_ip;
-				gg_debug(GG_DEBUG_MISC, "-- http connection failed, errno = %d (%s), trying direct connection\n", res, strerror(res));
-				if ((sess->fd = gg_connect(addr, GG_DEFAULT_PORT, sess->async)) == -1) {
-				    gg_debug(GG_DEBUG_MISC, "-- connection failed, trying https connection\n");
-				    if ((sess->fd = gg_connect(addr, GG_HTTPS_PORT, sess->async)) == -1) {
-					gg_debug(GG_DEBUG_MISC, "-- connect() failed. errno = %d (%s)\n", errno, strerror(errno));
-
-					e->type = GG_EVENT_CONN_FAILED;
-					e->event.failure = GG_FAILURE_CONNECTING;
-					sess->state = GG_STATE_IDLE;
-					break;
-				    }
-				}
-
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->check = GG_CHECK_WRITE;
-#else
-				gg_debug(GG_DEBUG_MISC, "-- http connection failed, errno = %d\n", res);
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_CONNECTING;
-				sess->state = GG_STATE_IDLE;
-#endif
-				break;
-			}
-			
-			gg_debug(GG_DEBUG_MISC, "-- http connection succeded, sending query\n");
-
-			if (gg_http_use_proxy) {
-				g_snprintf(buf, sizeof(buf) - 1,
-					"GET http://" GG_APPMSG_HOST "/appsvc/appmsg2.asp?fmnumber=%lu&version=%s&lastmsg=0 HTTP/1.0\r\n"
-					"Host: " GG_APPMSG_HOST "\r\n"
-					"User-Agent: " GG_HTTP_USERAGENT "\r\n"
-					"Pragma: no-cache\r\n"
-					"\r\n", sess->uin, gg_urlencode(GG_DEFAULT_CLIENT_VERSION));
-			} else {
-				g_snprintf(buf, sizeof(buf) - 1,
-					"GET /appsvc/appmsg2.asp?fmnumber=%lu&version=%s&lastmsg=0 HTTP/1.0\r\n"
-					"Host: " GG_APPMSG_HOST "\r\n"
-					"User-Agent: " GG_HTTP_USERAGENT "\r\n"
-					"Pragma: no-cache\r\n"
-					"\r\n", sess->uin, gg_urlencode(GG_DEFAULT_CLIENT_VERSION));
-			};
-
-    			gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
-			if (write(sess->fd, buf, strlen(buf)) < strlen(buf)) {
-				gg_debug(GG_DEBUG_MISC, "-- sending query failed\n");
-				errno = EIO;
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_WRITING;
-				sess->state = GG_STATE_IDLE;
-				break;
-			}
-
-			sess->state = GG_STATE_READING_DATA;
-			sess->check = GG_CHECK_READ;
-
-			break;
-		}
-
-		case GG_STATE_READING_DATA:
-		{
-			char buf[1024], *tmp, *host;
-			int port = GG_DEFAULT_PORT;
-			struct in_addr a;
-
-			gg_debug(GG_DEBUG_MISC, "== GG_STATE_READING_DATA\n");
-
-			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-			gg_chomp(buf);
-	
-			gg_debug(GG_DEBUG_TRAFFIC, "-- got http response (%s)\n", buf);
-			if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
-				gg_debug(GG_DEBUG_MISC, "-- but that's not what we've expected\n");
-
-				errno = EINVAL;
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_INVALID;
-				sess->state = GG_STATE_IDLE;
-				break;
-			}
-	
-			while (strcmp(buf, "\r\n") && strcmp(buf, ""))
-				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-
-			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-			gg_chomp(buf);
-	
-			close(sess->fd);
-	
-			gg_debug(GG_DEBUG_TRAFFIC, "-- received http data (%s)\n", buf);
-						
-			/* analizujemy otrzymane dane. */
-			tmp = buf;
-			
-			while (*tmp && *tmp != ' ')
-                                tmp++;
-                        while (*tmp && *tmp == ' ')
-                                tmp++;
-                        host = tmp;
-                        while (*tmp && *tmp != ' ')
-                                tmp++;
-                        *tmp = 0;
-
-			if ((tmp = strchr(host, ':'))) {
-				*tmp = 0;
-				port = atoi(tmp+1);
-			}
-
-			a.s_addr = inet_addr(host);
-			sess->server_ip = a.s_addr;
-
-#if 0
-			/* We need to watch this non-blocking socket so lets use gaim_proxy_connect 
-			   in gg.c - Herman */
-			if((sess->fd = gg_connect(&a, port, sess->assync)) == -1) {
-				gg_debug(GG_DEBUG_MISC, "-- connection failed, trying https connection\n");
-				if ((sess->fd = gg_connect(&a, GG_HTTPS_PORT, sess->async)) == -1) {
-				    gg_debug(GG_DEBUG_MISC, "-- connection failed, errno = %d (%s)\n", errno, strerror(errno));
-
-				    e->type = GG_EVENT_CONN_FAILED;
-				    e->event.failure = GG_FAILURE_CONNECTING;
-				    sess->state = GG_STATE_IDLE;
-				    break;
-				}
-			}
-#else
-			sess->port = port;
-#endif
-			sess->state = GG_STATE_CONNECTING_GG;
-			sess->check = GG_CHECK_WRITE;
-		
-			break;
-		}
-
-		case GG_STATE_CONNECTING_GG:
-		{
-			unsigned int res, res_size = sizeof(res);
-
-			gg_debug(GG_DEBUG_MISC, "== GG_STATE_CONNECTING_GG\n");
-
-			if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-				struct in_addr *addr = (struct in_addr*) &sess->server_ip;
-
-				gg_debug(GG_DEBUG_MISC, "-- connection failed, trying https connection\n");
-				if ((sess->fd = gg_connect(addr, GG_HTTPS_PORT, sess->async)) == -1) {
-				    gg_debug(GG_DEBUG_MISC, "-- connection failed, errno = %d (%s)\n", errno, strerror(errno));
-				    
-				    e->type = GG_EVENT_CONN_FAILED;
-				    e->event.failure = GG_FAILURE_CONNECTING;
-				    sess->state = GG_STATE_IDLE;
-				    break;
-				}
-			}
-
-			gg_debug(GG_DEBUG_MISC, "-- connected\n");
-			
-			sess->state = GG_STATE_READING_KEY;
-			sess->check = GG_CHECK_READ;
-
-			break;
-		}
-
-		case GG_STATE_READING_KEY:
-		{
-			struct gg_header *h;			
-			struct gg_welcome *w;
-			struct gg_login60 l;
-			unsigned int hash;
-			char *password = sess->password;
-
-			gg_debug(GG_DEBUG_MISC, "== GG_STATE_READING_KEY\n");
-
-			if (!(h = gg_recv_packet(sess))) {
-				gg_debug(GG_DEBUG_MISC, "-- gg_recv_packet() failed. errno = %d (%s)\n", errno, strerror(errno));
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_READING;
-				sess->state = GG_STATE_IDLE;
-				close(sess->fd);
-				break;
-			}
-	
-			if (h->type != GG_WELCOME) {
-				gg_debug(GG_DEBUG_MISC, "-- invalid packet received\n");
-
-				free(h);
-				close(sess->fd);
-				errno = EINVAL;
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_INVALID;
-				sess->state = GG_STATE_IDLE;
-				break;
-			}
-	
-			w = (struct gg_welcome *)((void *)h + sizeof(struct gg_header));
-			w->key = fix32(w->key);
-			
-			hash = gg_login_hash(password, w->key);
-	
-			gg_debug(GG_DEBUG_DUMP, "%%%% klucz serwera %.4x, hash hasła %.8x\n", w->key, hash);
-	
-			free(h);
-
-			free(sess->password);
-			sess->password = NULL;
-	
-			l.uin = fix32(sess->uin);
-			l.hash = fix32(hash);
-			l.status = fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
-			l.version = fix32(0x20);
-			l.local_ip = 0;
-			l.local_port = 0;
-	
-			gg_debug(GG_DEBUG_TRAFFIC, "-- sending GG_LOGIN packet\n");
-
-			if (gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), NULL, 0) == -1) {
-				gg_debug(GG_DEBUG_TRAFFIC, "-- oops, failed. errno = %d (%s)\n", errno, strerror(errno));
-
-				close(sess->fd);
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_WRITING;
-				sess->state = GG_STATE_IDLE;
-				break;
-			}
-	
-			sess->state = GG_STATE_READING_REPLY;
-
-			break;
-		}
-
-		case GG_STATE_READING_REPLY:
-		{
-			struct gg_header *h;
-
-			gg_debug(GG_DEBUG_MISC, "== GG_STATE_READING_REPLY\n");
-
-			if (!(h = gg_recv_packet(sess))) {
-				gg_debug(GG_DEBUG_MISC, "-- recv_packet failed\n");
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_READING;
-				sess->state = GG_STATE_IDLE;
-				close(sess->fd);
-				break;
-			}
-	
-			if (h->type == GG_LOGIN_OK) {
-				gg_debug(GG_DEBUG_MISC, "-- login succeded\n");
-				e->type = GG_EVENT_CONN_SUCCESS;
-				sess->state = GG_STATE_CONNECTED;
-				free(h);
-				break;
-			}
-
-			if (h->type == GG_LOGIN_FAILED) {
-				gg_debug(GG_DEBUG_MISC, "-- login failed\n");
-				e->event.failure = GG_FAILURE_PASSWORD;
-				errno = EACCES;
-			} else {
-				gg_debug(GG_DEBUG_MISC, "-- invalid packet\n");
-				e->event.failure = GG_FAILURE_INVALID;
-				errno = EINVAL;
-			}
-
-			e->type = GG_EVENT_CONN_FAILED;
-			sess->state = GG_STATE_IDLE;
-			close(sess->fd);
-			free(h);
-
-			break;
-		}
-
-		case GG_STATE_CONNECTED:
-		{
-			gg_debug(GG_DEBUG_MISC, "== GG_STATE_CONNECTED\n");
-
-			if ((res = gg_watch_fd_connected(sess, e)) == -1) {
-
-				gg_debug(GG_DEBUG_MISC, "-- watch_fd_connected failed. errno = %d (%s)\n", errno, strerror(errno));
-
- 				if (errno == EAGAIN) {
-					e->type = GG_EVENT_NONE;
-					res = 0;
-				} else
-					res = -1;
-			}
-			break;
-		}
-	}
-
-	if (res == -1) {
-		free(e);
-		e = NULL;
-	}
-
-	return e;
-}
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */
--- a/src/protocols/gg/libgg.h	Sun Aug 28 22:21:24 2005 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,683 +0,0 @@
-/*
- *  (C) Copyright 2001 Wojtek Kaniewski <wojtekka@irc.pl>,
- *                     Robert J. Woźny <speedy@ziew.org>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License Version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __GG_LIBGG_H
-#define __GG_LIBGG_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(sun) && !defined(INADDR_NONE)
-  #define INADDR_NONE 0xffffffff
-#endif
-
-#ifdef HAVE_STDINT_H
-#  include <stdint.h>
-#endif
-#include <sys/types.h>
-
-/*
- * typ zmiennej określającej numerek danej osoby.
- */
-typedef unsigned long uin_t;
-
-/*
- * struktura opisująca daną sesję. tworzona przez gg_login().
- */
-struct gg_session {
-        int fd;         	/* podglądany deskryptor */
-        int check;      	/* sprawdzamy zapis czy odczyt */
-        int state;      	/* aktualny stan maszynki */
-        int error;      	/* kod błędu dla GG_STATE_ERROR */
-	int type;		/* rodzaj sesji. == GG_SESSION_GG */
-	
-        int async;      	/* czy połączenie jest asynchroniczne */
-	int pid;        	/* pid procesu resolvera */
-	int port;       	/* port, z którym się łączymy */
-	int seq;        	/* numer sekwencyjny ostatniej wiadomości */
-	int last_pong;  	/* czas otrzymania ostatniego ping/pong */
-
-	unsigned int server_ip;	/* adres serwera */
-	unsigned int client_ip;	/* adres klienta */
-	int client_port;	/* port, na którym klient słucha */
-
-	uin_t uin;		/* numerek klienta */
-	char *password;		/* i jego hasło. zwalniane automagicznie */
-        
-	int initial_status;	/* początkowy stan klienta */
-
-	char *recv_buf;		/* bufor na otrzymywane pakiety */
-	int recv_done;		/* ile już wczytano do bufora */
-        int recv_left;		/* i ile jeszcze trzeba wczytać */
-	
-	char *userlist_reply;	/* fragment odpowiedzi listy kontaktów */
-
-	int userlist_blocks;	/* na ile kawałków podzielono listę kontaktów */
-};
-
-/*
- * ogólna struktura opisująca stan wszystkich operacji http.
- */
-struct gg_http {
-        int fd;                 /* podglądany deskryptor */
-        int check;              /* sprawdzamy zapis czy odczyt */
-        int state;              /* aktualny stan maszynki */
-        int error;              /* kod błędu dla GG_STATE_ERROR */
- 	int type;		/* rodzaj sesji. == GG_SESSION_HTTP */
-	
-        int async;              /* czy połączenie asynchroniczne */
-	int pid;                /* pid procesu resolvera */
-	int port;               /* port, z którym się łączymy */
-
-        char *query;            /* bufor zapytania http */
-        char *header;           /* bufor nagłówka */
-        int header_size;        /* rozmiar wczytanego nagłówka */
-        char *body;             /* bufor otrzymanych informacji */
-        int body_size;          /* ilość informacji */
-
-        void *data;             /* dane danej operacji http */
-};
-
-/*
- * ogólna struktura opisująca różne sesje. przydatna w klientach.
- */
-struct gg_common {
-        int fd;                 /* podglądany deskryptor */
-        int check;              /* sprawdzamy zapis czy odczyt */
-        int state;              /* aktualny stan maszynki */
-        int error;              /* kod błędu dla GG_STATE_ERROR */
-	int type;		/* rodzaj sesji */
-};
-
-/*
- * rodzaje sesji.
- */
-enum {
-	GG_SESSION_GG = 1,	/* połączenie z serwerem gg */
-	GG_SESSION_HTTP,	/* ogólna sesja http */
-	GG_SESSION_SEARCH,	/* szukanie */
-	GG_SESSION_REGISTER	/* rejestrowanie */
-};
-
-/*
- * różne stany asynchronicznej maszynki.
- */
-enum {
-        /* wspólne */
-        GG_STATE_IDLE = 0,		/* nie powinno wystąpić. */
-        GG_STATE_RESOLVING,             /* wywołał gethostbyname() */
-	GG_STATE_CONNECTING,            /* wywołał connect() */
-	GG_STATE_READING_DATA,		/* czeka na dane http */
-	GG_STATE_ERROR,			/* wystąpił błąd. kod w x->error */
-
-        /* gg_session */
-	GG_STATE_CONNECTING_GG,         /* wywołał connect() */
-	GG_STATE_READING_KEY,           /* czeka na klucz */
-	GG_STATE_READING_REPLY,         /* czeka na odpowiedź */
-	GG_STATE_CONNECTED,             /* połączył się */
-
-        /* gg_http */
-	GG_STATE_READING_HEADER,	/* czeka na nagłówek http */
-	GG_STATE_PARSING,               /* przetwarza dane */
-	GG_STATE_DONE                  /* skończył */
-};
-
-/*
- * dla zachowania kompatybilności wstecz. w wersji 1.0 będzie usunięte. oby.
- */
-#define GG_STATE_WRITING_HTTP GG_STATE_READING_DATA
-#define GG_STATE_WAITING_FOR_KEY GG_STATE_READING_KEY
-#define GG_STATE_SENDING_KEY GG_STATE_READING_REPLY
-#define GG_STATE_FINISHED GG_STATE_DONE
-
-/*
- * co proces klienta powinien sprawdzać w deskryptorach?
- */
-enum {
-	GG_CHECK_NONE = 0,		/* nic. nie powinno wystąpić */
-	GG_CHECK_WRITE = 1,		/* sprawdzamy możliwość zapisu */
-	GG_CHECK_READ = 2		/* sprawdzamy możliwość odczytu */
-};
-
-struct gg_session *gg_login(uin_t uin, char *password, int async);
-void gg_free_session(struct gg_session *sess);
-void gg_logoff(struct gg_session *sess);
-int gg_write(struct gg_session *sess, const char *buf, int length);
-int gg_change_status(struct gg_session *sess, int status);
-int gg_change_status_descr(struct gg_session *sess, int status, const char *descr);
-int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const char *message);
-int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const char *message, const unsigned char *format, int formatlen);
-int gg_ping(struct gg_session *sess);
-int gg_userlist_request(struct gg_session *sess, char type, const char *request);
-
-struct gg_notify_reply {
-	uin_t uin;			/* numerek */
-	unsigned long status;		/* status danej osoby */
-	unsigned long remote_ip;	/* adres ip delikwenta */
-	unsigned short remote_port;	/* port, na którym słucha klient */
-	unsigned long version;		/* == 0x0b */
-	unsigned short dunno2;		/* znowu port? */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_NOTIFY_REPLY60 0x0011
-	
-struct gg_notify_reply60 {
-	uint32_t uin;			/* numerek plus flagi w MSB */
-	uint8_t status;			/* status danej osoby */
-	uint32_t remote_ip;		/* adres ip delikwenta */
-	uint16_t remote_port;		/* port, na którym słucha klient */
-	uint8_t version;		/* wersja klienta */
-	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
-	uint8_t dunno1;			/* 0x00 */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_STATUS60 0x000f
-	
-struct gg_status60 {
-	uint32_t uin;			/* numerek plus flagi w MSB */
-	uint8_t status;			/* status danej osoby */
-	uint32_t remote_ip;		/* adres ip delikwenta */
-	uint16_t remote_port;		/* port, na którym słucha klient */
-	uint8_t version;		/* wersja klienta */
-	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
-	uint8_t dunno1;			/* 0x00 */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-
-struct gg_status {
-	uin_t uin;			/* numerek */
-	unsigned long status;		/* nowy stan */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-enum {
-	GG_EVENT_NONE = 0,
-	GG_EVENT_MSG,
-	GG_EVENT_NOTIFY,
-	GG_EVENT_STATUS,
-	GG_EVENT_ACK,
-	GG_EVENT_CONN_FAILED,
-	GG_EVENT_CONN_SUCCESS,
-	GG_EVENT_STATUS60,		/* ktoś zmienił stan w GG 6.0 */
-	GG_EVENT_NOTIFY60,		/* ktoś się pojawił w GG 6.0 */
-	GG_EVENT_USERLIST		/* odpowiedź listy kontaktów w GG 6.0 */
-};
-
-/*
- * niedługo się tego pozbędę na rzecz sensownej obsługi błędów. --w
- */
-enum {
-	GG_FAILURE_RESOLVING = 1,
-	GG_FAILURE_CONNECTING,
-	GG_FAILURE_INVALID,
-	GG_FAILURE_READING,
-	GG_FAILURE_WRITING,
-	GG_FAILURE_PASSWORD,
-	GG_FAILURE_404
-};
-
-/*
- * rodzaje błędów, na razie używane przez http. bez rozczulania się nad
- * powodami. klient powie, że albo nie znalazł hosta, albo nie mógł się
- * połączyć, albo nie mógł wysłać, albo nie mógł odebrac. i tyle. jak
- * ktoś będzie chciał, to będzie mógł sprawdzić errno. ale po co?
- */
-enum {
-	GG_ERROR_RESOLVING = 1,
-	GG_ERROR_CONNECTING,
-	GG_ERROR_READING,
-	GG_ERROR_WRITING
-};
-
-/*
- * struktura opisująca rodzaj zdarzenia. wychodzi z gg_watch_fd()
- */
-struct gg_event {
-        int type;
-        union {
-                struct {
-                        uin_t sender;
-			int msgclass;
-			time_t time;
-                        char *message;
-                } msg;
-                struct gg_notify_reply *notify;
-		struct {			/* @notify60 informacja o liście kontaktów -- GG_EVENT_NOTIFY60 */
-			uin_t uin;		/* numer */
-			int status;	/* stan */
-			uint32_t remote_ip;	/* adres ip */
-			uint16_t remote_port;	/* port */
-			int version;	/* wersja klienta */
-			int image_size;	/* maksymalny rozmiar grafiki w KiB */
-			char *descr;		/* opis stanu */
-			time_t time;		/* czas powrotu */
-		} *notify60;
-                struct gg_status status;
-		struct {			/* @status60 zmiana stanu -- GG_EVENT_STATUS60 */
-			uin_t uin;		/* numer */
-			int status;	/* nowy stan */
-			uint32_t remote_ip;	/* adres ip */
-			uint16_t remote_port;	/* port */
-			int version;	/* wersja klienta */
-			int image_size;	/* maksymalny rozmiar grafiki w KiB */
-			char *descr;		/* opis stanu */
-			time_t time;		/* czas powrotu */
-		} status60;
-		struct {			/* @userlist odpowiedź listy kontaktów serwera */
-			char type;		/* rodzaj odpowiedzi */
-			char *reply;		/* treść odpowiedzi */
-		} userlist;
-                struct {
-                        uin_t recipient;
-                        int status;
-                        int seq;
-                } ack;
-		int failure;
-        } event;
-};
-
-struct gg_event *gg_watch_fd(struct gg_session *sess);
-void gg_free_event(struct gg_event *e);
-
-int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
-int gg_add_notify(struct gg_session *sess, uin_t uin);
-int gg_remove_notify(struct gg_session *sess, uin_t uin);
-
-
-/*
- * OBSŁUGA HTTP
- */
-
-struct gg_http *gg_http_connect(char *hostname, int port, int async, char *method, char *path, char *header);
-int gg_http_watch_fd(struct gg_http *h);
-void gg_http_stop(struct gg_http *h);
-void gg_free_http(struct gg_http *h);
-
-/* 
- * SZUKANIE UŻYTKOWNIKÓW
- */
-
-/*
- * struktura opisująca kryteria wyszukiwania. argument gg_search().
- */
-struct gg_search_request {
-	int active;		/* czy ma szukać tylko aktywnych? */
-
-	/* mode 0 */
-	char *nickname;		/* pseudonim */
-	char *first_name;	/* imię */
-	char *last_name;	/* nazwisko */
-	char *city;		/* miasto */
-	int gender;		/* płeć */
-	int min_birth;		/* urodzony od roku... */
-	int max_birth;		/* urodzony do roku... */
-	
-	/* mode 1 */
-	char *email;		/* adres e-mail */
-
-	/* mode 2 */
-	char *phone;		/* numer telefonu */
-	
-	/* mode 3 */
-	uin_t uin;		/* numerek */
-};
-
-/*
- * struktura opisująca rezultat wyszukiwania. pole gg_http.
- */
-struct gg_search {
-	int count;				/* ilość znalezionych */
-	struct gg_search_result *results;	/* tabelka z nimi */
-};
-
-/*
- * pojedynczy rezultat wyszukiwania.
- */
-struct gg_search_result {
-	uin_t uin;		/* numerek */
-	char *first_name;	/* imię */
-	char *last_name;	/* nazwisko */
-	char *nickname;		/* pseudonim */
-	int born;		/* rok urodzenia */
-	int gender;		/* płeć */
-	char *city;		/* miasto */
-	int active;		/* czy jest aktywny */
-};
-
-#define GG_GENDER_NONE 0	/* nie podano lub bez znaczenia */
-#define GG_GENDER_FEMALE 1	/* kobieta */
-#define GG_GENDER_MALE 2	/* mężczyzna */
-
-struct gg_http *gg_search(struct gg_search_request *r, int async);
-int gg_search_watch_fd(struct gg_http *f);
-void gg_free_search(struct gg_http *f);
-
-struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active);
-struct gg_search_request *gg_search_request_mode_1(char *email, int active);
-struct gg_search_request *gg_search_request_mode_2(char *phone, int active);
-struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active);
-
-/*
- * OPERACJE NA KATALOGU PUBLICZNYM
- */
-
-struct gg_pubdir {
-	int success;		/* czy się udało */
-	uin_t uin;		/* otrzymany numerek. 0 jeśli błąd */
-};
-
-struct gg_http *gg_register(char *email, char *password, int async);
-void gg_free_register(struct gg_http *f);
-
-int gg_pubdir_watch_fd(struct gg_http *f);
-#define gg_register_watch_fd gg_pubdir_watch_fd
-
-/*
- * jeśli chcemy sobie podebugować, wystarczy ustawić `gg_debug_level'.
- * niestety w miarę przybywania wpisów `gg_debug(...)' nie chciało mi
- * się ustawiać odpowiednich leveli, więc większość szła do _MISC.
- */
-
-extern int gg_debug_level;
-
-#define GG_DEBUG_NET 1
-#define GG_DEBUG_TRAFFIC 2
-#define GG_DEBUG_DUMP 4
-#define GG_DEBUG_FUNCTION 8
-#define GG_DEBUG_MISC 16
-
-void gg_debug(int level, char *format, ...);
-
-/*
- * Pare małych zmiennych do obsługi "http proxy"
- *   
- */
- 
-extern int gg_http_use_proxy;
-extern char *gg_http_proxy_host;
-extern int gg_http_proxy_port;
-
-/*
- * -------------------------------------------------------------------------
- * poniżej znajdują się wewnętrzne sprawy biblioteki. zwykły klient nie
- * powinien ich w ogóle ruszać, bo i nie ma po co. wszystko można załatwić
- * procedurami wyższego poziomu, których definicje znajdują się na początku
- * tego pliku.
- * -------------------------------------------------------------------------
- */
-
-int gg_resolve(int *fd, int *pid, char *hostname);
-void gg_debug(int level, char *format, ...);
-char *gg_alloc_sprintf(char *format, ...);
-char *gg_get_line(char **ptr);
-int gg_connect(void *addr, int port, int async);
-void gg_read_line(int sock, char *buf, int length);
-void gg_chomp(char *line);
-char *gg_urlencode(const char *str);
-int gg_http_hash(const char *email, const char *password);
-
-#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
-#define GG_APPMSG_PORT 80
-#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
-#define GG_PUBDIR_PORT 80
-#define GG_REGISTER_HOST "register.gadu-gadu.pl"
-#define GG_REGISTER_PORT 80
-#define GG_DEFAULT_PORT 8074
-#define GG_HTTPS_PORT 443
-#define GG_HTTP_USERAGENT "Mozilla/4.0 (compatible MSIE 5.0; Windows 98; I)"
-#define GG_HAS_AUDIO_MASK 0x40000000
-#define GG_DEFAULT_CLIENT_VERSION "6, 0, 0, 132"
-
-struct gg_header {
-	unsigned long type;		/* typ pakietu */
-	unsigned long length;		/* długość reszty pakietu */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_WELCOME 0x0001
-
-struct gg_welcome {
-	unsigned long key;		/* klucz szyfrowania hasła */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-	
-#define GG_LOGIN 0x000c
-
-struct gg_login {
-	uin_t uin;			/* twój numerek */
-	unsigned long hash;		/* hash hasła */
-	unsigned long status;		/* status na dzień dobry */
-	unsigned long version;		/* == 0x20 */
-	unsigned long local_ip;		/* mój adres ip */
-	unsigned short local_port;	/* port, na którym słucham */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_LOGIN60 0x0015
-
-struct gg_login60 {
-	uint32_t uin;			/* mój numerek */
-	uint32_t hash;			/* hash hasła */
-	uint32_t status;		/* status na dzień dobry */
-	uint32_t version;		/* moja wersja klienta */
-	uint8_t dunno1;			/* 0x00 */
-	uint32_t local_ip;		/* mój adres ip */
-	uint16_t local_port;		/* port, na którym słucham */
-	uint32_t external_ip;		/* zewnętrzny adres ip */
-	uint16_t external_port;		/* zewnętrzny port */
-	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
-	uint8_t dunno2;			/* 0xbe */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_LOGIN_OK 0x0003
-
-#define GG_LOGIN_FAILED 0x0009
-
-#define GG_NEW_STATUS 0x0002
-
-#define GG_STATUS_NOT_AVAIL 0x0001		/* niedostępny */
-#define GG_STATUS_NOT_AVAIL_DESCR 0x0015	/* niedostępny z opisem (4.8) */
-#define GG_STATUS_AVAIL 0x0002			/* dostępny */
-#define GG_STATUS_AVAIL_DESCR 0x0004		/* dostępny z opisem (4.9) */
-#define GG_STATUS_BUSY 0x0003			/* zajęty */
-#define GG_STATUS_BUSY_DESCR 0x0005		/* zajęty z opisem (4.8) */
-#define GG_STATUS_INVISIBLE 0x0014		/* niewidoczny (4.6) */
-#define GG_STATUS_INVISIBLE_DESCR 0x0016	/* niewidoczny z opisem (4.9) */
-#define GG_STATUS_BLOCKED 0x0006		/* zablokowany */
-
-#define GG_STATUS_FRIENDS_MASK 0x8000		/* tylko dla znajomych (4.6) */
-
-#define GG_STATUS_DESCR_MAXSIZE 70
-
-/* GG_S() stan bez uwzględnienia trybu tylko dla znajomych */
-#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
-
-/* GG_S_D() stan opisowy */
-#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
-
-struct gg_new_status {
-	unsigned long status;			/* na jaki zmienić? */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_NOTIFY 0x0010
-	
-struct gg_notify {
-	uin_t uin;		/* numerek danej osoby */
-	char dunno1;		/* == 3 */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-	
-#define GG_NOTIFY_REPLY 0x000c	/* tak, to samo co GG_LOGIN */
-	
-/* struct gg_notify_reply zadeklarowane wyżej */
-
-#define GG_ADD_NOTIFY 0x000d
-#define GG_REMOVE_NOTIFY 0x000e
-	
-struct gg_add_remove {
-	uin_t uin;		/* numerek */
-	char dunno1;		/* == 3 */
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_STATUS 0x0002
-
-/* struct gg_status zadeklarowane wcześniej */
-	
-#define GG_SEND_MSG 0x000b
-
-#define GG_CLASS_QUEUED 0x0001
-#define GG_CLASS_OFFLINE GG_CLASS_QUEUED
-#define GG_CLASS_MSG 0x0004
-#define GG_CLASS_CHAT 0x0008
-
-struct gg_send_msg {
-	unsigned long recipient;
-	unsigned long seq;
-	unsigned long msgclass;
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_SEND_MSG_ACK 0x0005
-
-#define GG_ACK_DELIVERED 0x0002
-#define GG_ACK_QUEUED 0x0003
-	
-struct gg_send_msg_ack {
-	unsigned long status;
-	unsigned long recipient;
-	unsigned long seq;
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_RECV_MSG 0x000a
-	
-struct gg_recv_msg {
-	unsigned long sender;
-	unsigned long seq;
-	unsigned long time;
-	unsigned long msgclass;
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_PING 0x0008
-	
-#define GG_PONG 0x0007
-
-#define GG_USERLIST_REQUEST 0x0016
-
-#define GG_USERLIST_PUT 0x00
-#define GG_USERLIST_PUT_MORE 0x01
-#define GG_USERLIST_GET 0x02
-
-struct gg_userlist_request {
-	uint8_t type;
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-#define GG_USERLIST_REPLY 0x0010
-
-#define GG_USERLIST_PUT_REPLY 0x00
-#define GG_USERLIST_PUT_MORE_REPLY 0x02
-#define GG_USERLIST_GET_REPLY 0x06
-#define GG_USERLIST_GET_MORE_REPLY 0x04
-
-struct gg_userlist_reply {
-	uint8_t type;
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-;
-
-/* listy */
-
-struct list {
-	void *data;
-	struct list *next;
-};
-
-typedef struct list * list_t;
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __GG_LIBGG_H */
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */
--- a/src/protocols/gg/protocol.txt	Sun Aug 28 22:21:24 2005 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,316 +0,0 @@
----------------------------------------------------------------------------
-
-                          Protokół G*du-G*du 4.x
-
-     (C) Copyright 2001 by Wojtek Kaniewski <wojtekka@irc.pl>,
-                           Robert J. Woźny <speedy@atman.pl>,
-                           Tomasz Jarzynka <tomee@cpi.pl>,
-                           Adam Ludwikowski <adam.ludwikowski@wp.pl>,
-                           Marek Kozina <klith@hybrid.art.pl>,
-			   Rafał Florek <raf@regionet.regionet.pl>,
-			   Igor Popik <igipop@wsfiz.edu.pl>
-
---- 0) disclaimer ---------------------------------------------------------
-
-opis protokołu bazują na doświadczeniach przeprowadzonych na moim
-domowym komputerze oraz informacjach przysłanych do mnie przez różnych
-ludzi. żaden klient g*du-g*du nie został skrzywdzony podczas
-przeprowadzania badań, blabla.
-
---- 1) transmisja, format wszystkich pakietów -----------------------------
-
-w przeciwieństwie do zabawek typu icq, g*du-g*du korzysta z protokołu tcp.
-każdy pakiet zawiera dwa stałe pola:
-
-        struct gg_header {
-		int type;		/* typ pakietu */
-		int length;		/* długość reszty pakietu */
-	};
-
-dla ułatwienia przyjmuję następujące długości zmiennych: sizeof(char) = 1,
-sizeof(short) = 2, sizeof(int) = 4. oczywiście wszystkie liczby są zgodnie
-z intelowym endianem. zakładam też, że wszystkie zmienne są bez znaku. nie
-chce mi się wszędzie pisać `unsigned'.
-
-pola, co do których znaczenia nie mam pewności, lub w ogóle nie mam pojęcia,
-skąd się tam wzięły, oznaczam `dunno'.
-
---- 2) zanim się połączymy -------------------------------------------------
-
-żeby wiedzieć, z jakim serwerem mamy się połączyć, należy poudawać przez
-chwilę Internet Explorera, połączyć się z hostem `appmsg.gadu-gadu.pl'.
-
-        GET /appsvc/appmsg.asp?fmnumber=<tutaj_numerek_gg> HTTP/1.0
-	Host: appmsg.gadu-gadu.pl
-	User-Agent: Mozilla/4.7 [en] (Win98; I)
-	Pragma: no-cache
-
-oryginalny klient może wysłać jeden z podanych identyfikatorów przeglądarki:
-
-	Mozilla/4.04 [en] (Win95; I ;Nav)
-	Mozilla/4.7 [en] (Win98; I)
-	Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)
-	Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)
-	Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)
-	Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)
-
-nowsze wersje klienta do zapytania dodają również `version=...' opisujące,
-z jakim klientem serwer ma do czynienia. jednak ze względu na możliwe
-różnice w protokole, lepiej pomijać ten parametr i uwagać GG 4.0. w każdym
-razie na to zapytanie serwer powinien odpowiedzieć:
-
-	HTTP/1.0 200 OK
-	
-	0 1 0 217.17.33.21:8074 217.17.33.21 217.17.33.21
-
-co to oznacza? nie mam pojęcia ;) wygląda na to, że cały g*du-g*du jest
-przemyślany i w przyszłości będzie można używać różnych serwerów do różnych
-rzeczy, typu szukanie, obsługa klientów itd. póki co, łączyć się trzeba na
-pierwszy adres (tak, ten z portem).
-Jeżeli połączenie z portem 8074 nie wyjdzie z jakiś specyficznych powodów -
-można się łączyć na port 443. 
-
---- 3) logowanie się -------------------------------------------------------
-
-po połączeniu się portem serwera g*du-g*du, dostajemy pakiet typu 0x0001,
-który na potrzeby tego dokumentu nazwiemy:
-
-	#define GG_WELCOME 0x0001
-
-reszta pakietu zawiera liczbę, na podstawie której liczony jest hash z hasła
-klienta:
-
-	struct gg_welcome {
-		int key;		/* klucz szyfrowania hasła */
-	};
-	
-kiedy mamy już tą wartość możemy odesłać pakiet logowania
-
-	#define GG_LOGIN 0x000c
-
-musimy podać kilka informacji:
-
-	struct gg_login {
-		int uin;		/* twój numerek */
-		int hash;		/* hash hasła */
-		int status;		/* status na dzień dobry */
-		int version;		/* wersja klienta */
-		int local_ip;		/* mój adres ip */
-		short local_port;	/* port, na którym słucham */
-	};
-
-jak obliczyć hash hasła? hmm... nic prostszego. do każdej literki hasła
-dodaje się jedynkę, mnoży wszystko razem, a potem przez liczbę podaną przez
-serwer. 
-
-	for (hash = 1; *passwd; passwd++)
-		hash *= (*passwd) + 1;
-
-zrozumiałe, racja? liczba oznaczająca wersję może być jedną z poniższych:
-
-	0x11 - 4.6.1
-	0x10 - 4.5.22, 4.5.21, 4.5.19, 4.5.17, 4.5.15
-	0x0f - 4.5.12
-	0x0b - 4.0.30, 4.0.29, 4.0.28, 4.0.25
-
-oczywiście nie są to wszystkie możliwe wersje klientów, lecz te, które
-udało się sprawdzić. najbezpieczniej będzie przedstawiać się jako ta
-wersja, której ficzerów używamy. wiadomo, że 4.0.x nie obsługiwały trybu
-ukrytego, ani tylko dla znajomych itd.
-
-jeśli wszystko się powiedzie, dostaniemy w odpowiedzi pakiet typu
-
-	#define GG_LOGIN_OK 0x0003
-
-z polem header->length = 0, lub pakiet
-	
-	#define GG_LOGIN_FAILED 0x0009
-
---- 4) zmiana statusu -----------------------------------------------------
-
-g*du-g*du przewiduje trzy stany klienta, które zmieniamy pakietem
-
-	#define GG_NEW_STATUS 0x0002
-
-	#define GG_STATUS_NOT_AVAIL 0x0001	/* rozłączony */
-	#define GG_STATUS_AVAIL 0x0002		/* dostępny */
-	#define GG_STATUS_BUSY 0x0003		/* zajęty */
-	#define GG_STATUS_INVISIBLE 0x0014	/* niewidoczny */
-
-	#define GG_STATUS_FRIENDS_MASK 0x8000	/* tylko dla przyjaciół */
-	
-	struct gg_new_status {
-		int status;			/* na jaki zmienić? */
-	}
-
-należy pamiętać, żeby przed rozłączeniem się z serwerem należy zmienić
-stan na GG_STATUS_NOT_AVAIL. jeśli ma być widoczny tylko dla przyjaciół,
-należy dodać GG_STATUS_FRIENDS do normalnej wartości stanu.
-
---- 5) ludzie przychodzą, ludzie odchodzą ---------------------------------
-
-zaraz po zalogowaniu możemy wysłać serwerowi listę ludzików w naszej liście
-kontaktów, żeby dowiedzieć się, czy są w tej chwili dostępni. pakiet zawiera
-dowolną ilość struktur gg_notify:
-
-	#define GG_NOTIFY 0x0010
-	
-	struct gg_notify {
-		int uin;		/* numerek danej osoby */
-		char dunno1;		/* == 3 */
-	};
-	
-jeśli ktoś jest, serwer odpowie pakietem zawierającym jedną lub więcej
-struktur gg_notify_reply:
-
-	#define GG_NOTIFY_REPLY 0x000c	/* tak, to samo co GG_LOGIN */
-	
-	struct gg_notify_reply {
-		int uin;		/* numerek */
-		int status;		/* status danej osoby */
-		int remote_ip;		/* adres ip delikwenta */
-		short remote_port;	/* port, na którym słucha klient */
-		int version;		/* wersja klienta */
-		short dunno1;		/* znowu port? */
-	};
-
-jeśli klient nie obsługuje połączeń między klientami (np. g*du-g*du 3.x)
-zamiast adresu ip jest 0, zamiast portu może być 0, 1, 2... nieważne ;)
-port może przyjmować wartość 1, jeśli klient znajduje się za jakimś
-firewallem lub innym urządzeniem robiącym NAT. w każdym razie, jeśli ktoś
-się pojawi w trakcie pracy, również zostanie przysłany ten pakiet.
-proste? proste :)
-
-żeby dodać kogoś do listy w trakcie pracy, trzeba wysłać niżej opisany
-pakiet. jego format jest identyczny jak przy GG_NOTIFY.
-
-	#define GG_ADD 0x000d
-	
-	struct gg_add {
-		int uin;		/* numerek */
-		char dunno1;		/* == 3 */
-	};
-
-jeśli ktoś opuści g*du-g*du lub zmieni stan, otrzymamy pakiet
-
-	#define GG_STATUS 0x0002
-	
-	struct gg_status {
-		int uin;		/* numerek */
-		int status;		/* nowy stan */
-	};
-
---- 6) wysyłanie wiadomości ------------------------------------------------
-	
-przejdźmy do sedna sprawy ;)
-
-	#define GG_SEND_MSG 0x000b
-
-	#define GG_CLASS_QUEUED 0x0001	/* tylko przy odbieraniu */
-	#define GG_CLASS_MSG 0x0004
-	#define GG_CLASS_CHAT 0x0008
-	#define GG_CLASS_UNKNOWN_1 0x0020
-
-	struct gg_send_msg {
-		int recipient;
-		int seq;
-		int class;
-		char message[];
-	};
-
-wiadomo, odbiorca. numer sekwencyjny, który wykorzystujemy potem do
-potwierdzenia. nie wykluczone, że w jakis sposób odróżnia się różne
-rozmowy za pomocą części bajtów, ale raczej nie ma znaczenia. klasa
-wiadomości pozwala odróżnić, czy wiadomość ma się pokazać w osobym
-okienku czy jako kolejna linijka w okienku rozmowy. wygląda na to,
-że to jakaś bitmapa, więc najlepiej olać inne bity niż 0x0e. (czasem
-klienty wysyłają 0x04, czasem 0x24 -- widocznie 0x20 to też jakaś
-flaga). jeśli odbiorca był niedostępny podczas wysyłania wiadomości,
-zostanie zaznaczony bit 0x01.
-
-oryginalny klient wysyłając wiadomość do kilku użytkowników, wysyła po
-prostu kilka takich samych pakietów z różnymi numerkami odbiorców. nie
-ma osobnego pakietu do tego. natomiast jeśli chodzi o ,,konferencyjnę''
-do pakietu doklejana jest za ,,char message[];'' następująca struktura:
-
-	struct gg_send_recipients {
-		char flag;		/* == 1 */
-		int count;		/* ilość odbiorców */
-		int recipients[];	/* tablica odbiorców */
-	};
-
-na przykład, by wysłać do trzech ludzi, należy wysłać pakiet:
-
-	- -- --- --+--+--+--+--+--+--+-----------+-----------+
-          treść    |\0|\1|    0x02   |    uin1   |   uin2    |
-	- -- -- ---+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-serwer po otrzymaniu wiadomości odsyła informację o tym. przy okazji
-mówi, czy wiadomość dotarła do odbiorcy (status == GG_ACK_DELIVERED),
-czy może jest offline i została zakolejkowana (GG_ACK_QUEUED):
-
-	#define GG_SEND_MSG_ACK 0x0005
-	
-	#define GG_ACK_DELIVERED 0x0002
-	#define GG_ACK_QUEUED 0x0003
-
-	struct gg_send_msg_ack {
-		int status;
-		int recipient;
-		int seq;
-	};
-
-numer sekwencyjny i adresat ten sam, co przy wysyłaniu.
-
---- 7) otrzymywanie wiadomości ---------------------------------------------
-
-zbyt wiele wyjaśnień chyba nie trzeba. wiadomo od kogo. drugie pole to
-najprawdopodobniej jakiś numerek sekwencyjny. trzecie oznacza czas nadania
-wiadomości. klasa wiadomości taka sama jak przy wysyłaniu:
-
-	#define GG_RECV_MSG 0x000a
-	
-	struct gg_recv_msg {
-		int sender;
-		int seq;
-		int time;
-		int class;
-		char message[];
-	};
-
-w przypadku pakietów ,,konferencyjnych'' na koncu pakietu doklejona jest
-struktura identyczna ze struct gg_send_recipients zawierająca pozostałych
-rozmówców.
-
---- 8) ping/pong -----------------------------------------------------------
-
-od czasu do czasu klient wysyła pakiet a'la ping do serwera i dostaje pustą
-odpowiedź. o ile dobrze pamiętam, serwer rozłącza się po upływie 5 minut od
-otrzymania ostatniej informacji.
-
-	#define GG_PING 0x0008
-	
-	/* nie ma niczego */
-	
-	#define GG_PONG 0x0007
-	
-	/* nie ma niczego */
-
---- 9) podziękowania -------------------------------------------------------
-
-swój wkład w poznanie protokołu mieli:
- - Robert J. Woźny <speedy@atman.pl>:
-   opis nowości w protokole GG 4.6,
- - Tomasz Jarzynka <tomee@cpi.pl>:
-   badanie timeoutów,
- - Adam Ludwikowski <adam.ludwikowski@wp.pl>:
-   wiele różnych poprawek do tekstu, badanie wersji,
- - Marek Kozina <klith@hybrid.art.pl>:
-   czas otrzymania wiadomości,
- - Rafał Florek <raf@regionet.regionet.pl>:
-   konferencje,
- - Igor Popik <igipop@wsfiz.edu.pl>:
-   klasy wiadomości przy odbieraniu zakolejkowanej.
-
-----------------------------------------------------------------------------
-