cpw.rekkanoryo.ggupdate: 3e11646a: Update our internal libgadu to 1.9.0-rc2...

rekkanoryo at pidgin.im rekkanoryo at pidgin.im
Sun Feb 21 11:55:48 EST 2010


-----------------------------------------------------------------
Revision: 3e11646a9e21dbb7eae8d07d2782cac0c85fa2c0
Ancestor: 9484d318c55139eed1c1919a2e793af4bab3b9e5
Author: rekkanoryo at pidgin.im
Date: 2010-02-21T16:52:42
Branch: im.pidgin.cpw.rekkanoryo.ggupdate
URL: http://d.pidgin.im/viewmtn/revision/info/3e11646a9e21dbb7eae8d07d2782cac0c85fa2c0

Added files:
        libpurple/protocols/gg/lib/dcc7.c
        libpurple/protocols/gg/lib/libgadu-internal.h
        libpurple/protocols/gg/lib/protocol.h
        libpurple/protocols/gg/lib/resolver.c
        libpurple/protocols/gg/lib/resolver.h
        libpurple/protocols/gg/lib/sha1.c
Modified files:
        configure.ac libpurple/protocols/gg/Makefile.am
        libpurple/protocols/gg/Makefile.mingw
        libpurple/protocols/gg/lib/common.c
        libpurple/protocols/gg/lib/compat.h
        libpurple/protocols/gg/lib/dcc.c
        libpurple/protocols/gg/lib/events.c
        libpurple/protocols/gg/lib/http.c
        libpurple/protocols/gg/lib/libgadu.c
        libpurple/protocols/gg/lib/libgadu.h
        libpurple/protocols/gg/lib/obsolete.c
        libpurple/protocols/gg/lib/pubdir.c
        libpurple/protocols/gg/lib/pubdir50.c

ChangeLog: 

Update our internal libgadu to 1.9.0-rc2.  This does not yet build on Windows.
Refs #10542.  The Windows build errors are the only reason this isn't on
`im.pidgin.pidgin` already.

-------------- next part --------------
============================================================
--- libpurple/protocols/gg/lib/dcc7.c	7e301bb7bd0d1c4a4ee8a627590a1d8b4b50aaad
+++ libpurple/protocols/gg/lib/dcc7.c	7e301bb7bd0d1c4a4ee8a627590a1d8b4b50aaad
@@ -0,0 +1,1212 @@
+/* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */
+
+/*
+ *  (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka at irc.pl>
+ *                          Tomasz Chili?ski <chilek at chilan.com>
+ *                          Adam Wysocki <gophi at ekg.chmurka.net>
+ *  
+ *  Thanks to Jakub Zawadzki <darkjames at darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110,
+ *  USA.
+ */
+
+/**
+ * \file dcc7.c
+ *
+ * \brief Obs?uga po??cze? bezpo?rednich od wersji Gadu-Gadu 7.x
+ */
+
+#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 <time.h>
+
+#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"
+
+#define gg_debug_dcc(dcc, fmt...) \
+	gg_debug_session((dcc) ? (dcc)->sess : NULL, fmt)
+
+/**
+ * \internal Dodaje po??czenie bezpo?rednie do sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc || dcc->next) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	dcc->next = sess->dcc7_list;
+	sess->dcc7_list = dcc;
+
+	return 0;
+}
+
+/**
+ * \internal Usuwa po??czenie bezpo?rednie z sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	struct gg_dcc7 *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (sess->dcc7_list == dcc) {
+		sess->dcc7_list = dcc->next;
+		dcc->next = NULL;
+		return 0;
+	}
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		if (tmp->next == dcc) {
+			tmp = dcc->next;
+			dcc->next = NULL;
+			return 0;
+		}
+	}
+
+	errno = ENOENT;
+	return -1;
+}
+
+/**
+ * \internal Zwraca struktur? po??czenia o danym identyfikatorze.
+ *
+ * \param sess Struktura sesji
+ * \param id Identyfikator po??czenia
+ * \param uin Numer nadawcy lub odbiorcy
+ *
+ * \return Struktura po??czenia lub \c NULL je?li nie znaleziono
+ */
+static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
+{
+	struct gg_dcc7 *tmp;
+	int empty;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
+
+	empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		if (empty) {
+			if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
+				return tmp;
+		} else {
+			if (!memcmp(&tmp->cid, &id, sizeof(id)))
+				return tmp;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * \internal Nawi?zuje po??czenie bezpo?rednie
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
+		return -1;
+	}
+
+	dcc->state = GG_STATE_CONNECTING;
+	dcc->check = GG_CHECK_WRITE;
+	dcc->timeout = GG_DCC7_TIMEOUT_CONNECT;
+	dcc->soft_timeout = 1;
+
+	return 0;
+}
+
+/**
+ * \internal Tworzy gniazdo nas?uchuj?ce dla po??czenia bezpo?redniego
+ *
+ * \param dcc Struktura po??czenia
+ * \param port Preferowany port (je?li r?wny 0 lub -1, pr?buje si? domy?lnego)
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port)
+{
+	struct sockaddr_in sin;
+	int fd;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
+
+	if (!dcc) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
+		return -1;
+	}
+
+	// XXX losowa? porty?
+	
+	if (!port)
+		port = GG_DEFAULT_DCC_PORT;
+
+	while (1) {
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = INADDR_ANY;
+		sin.sin_port = htons(port);
+
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port);
+
+		if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin)))
+			break;
+
+		if (port++ == 65535) {
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n");
+			close(fd);
+			errno = ENOENT;
+			return -1;
+		}
+	}
+
+	if (listen(fd, 1)) {
+		int errsv = errno;
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
+		close(fd);
+		errno = errsv;
+		return -1;
+	}
+
+	dcc->fd = fd;
+	dcc->local_port = port;
+	
+	dcc->state = GG_STATE_LISTENING;
+	dcc->check = GG_CHECK_READ;
+	dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+	return 0;
+}
+
+/**
+ * \internal Tworzy gniazdo nas?uchuj?ce i wysy?a jego parametry
+ *
+ * \param dcc Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
+{
+	struct gg_dcc7_info pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
+
+	// XXX da? mo?liwo?? konfiguracji?
+	
+	dcc->local_addr = dcc->sess->client_addr;
+
+	if (gg_dcc7_listen(dcc, 0) == -1)
+		return -1;
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.type = GG_DCC7_TYPE_P2P;
+	pkt.id = dcc->cid;
+	snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), dcc->local_port);
+
+	return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Odwraca po??czenie po nieudanym connect()
+ *
+ * \param dcc Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
+
+	if (dcc->reverse) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
+		return -1;
+	}
+
+	gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
+	close(dcc->fd);
+	dcc->fd = -1;
+	dcc->reverse = 1;
+
+	return gg_dcc7_listen_and_send_info(dcc);
+}
+
+/**
+ * \internal Wysy?a do serwera ??danie nadania identyfikatora sesji
+ *
+ * \param sess Struktura sesji
+ * \param type Rodzaj po??czenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE)
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
+{
+	struct gg_dcc7_id_request pkt;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type);
+
+	if (!sess) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n");
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type);
+		errno = EINVAL;
+		return -1;
+	}
+	
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.type = gg_fix32(type);
+
+	return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Rozpoczyna wysy?anie pliku.
+ *
+ * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz
+ * \c gg_dcc_send_file_fd().
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param fd Deskryptor pliku
+ * \param size Rozmiar pliku
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skr?t SHA-1 pliku
+ * \param seek Flaga m?wi?ca, czy mo?na u?ywa? lseek()
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku b??du
+ *
+ * \ingroup dcc7
+ */
+static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash, int seek)
+{
+	struct gg_dcc7 *dcc = NULL;
+
+	if (!sess || !rcpt || !filename1250 || !hash || fd == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n");
+		goto fail;
+	}
+
+	if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
+		goto fail;
+
+	memset(dcc, 0, sizeof(struct gg_dcc7));
+	dcc->type = GG_SESSION_DCC7_SEND;
+	dcc->dcc_type = GG_DCC7_TYPE_FILE;
+	dcc->state = GG_STATE_REQUESTING_ID;
+	dcc->timeout = GG_DEFAULT_TIMEOUT;
+	dcc->sess = sess;
+	dcc->fd = -1;
+	dcc->uin = sess->uin;
+	dcc->peer_uin = rcpt;
+	dcc->file_fd = fd;
+	dcc->size = size;
+	dcc->seek = seek;
+
+	strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1);
+	dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+
+	memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
+
+	if (gg_dcc7_session_add(sess, dcc) == -1)
+		goto fail;
+
+	return dcc;
+
+fail:
+	free(dcc);
+	return NULL;
+}
+
+/**
+ * Rozpoczyna wysy?anie pliku o danej nazwie.
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param filename Nazwa pliku w lokalnym systemie plik?w
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skr?t SHA-1 pliku (lub \c NULL je?li ma by? wyznaczony)
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku b??du
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash)
+{
+	struct gg_dcc7 *dcc = NULL;
+	const char *tmp;
+	char hash_buf[GG_DCC7_HASH_LEN];
+	struct stat st;
+	int fd = -1;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p)\n", sess, rcpt, filename, hash);
+
+	if (!sess || !rcpt || !filename) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if (!filename1250)
+		filename1250 = filename;
+
+	if (stat(filename, &st) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if ((st.st_mode & S_IFDIR)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if ((fd = open(filename, O_RDONLY)) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if (!hash) {
+		if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
+			goto fail;
+
+		hash = hash_buf;
+	}
+
+	if ((tmp = strrchr(filename1250, '/')))
+		filename1250 = tmp + 1;
+
+	if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1)))
+		goto fail;
+
+	return dcc;
+
+fail:
+	if (fd != -1) {
+		int errsv = errno;
+		close(fd);
+		errno = errsv;
+	}
+
+	free(dcc);
+	return NULL;
+}
+
+/**
+ * \internal Rozpoczyna wysy?anie pliku o danym deskryptorze.
+ *
+ * \note Wysy?anie pliku nie b?dzie dzia?a? poprawnie, je?li deskryptor
+ * ?r?d?owy jest w trybie nieblokuj?cym i w pewnym momencie zabraknie danych.
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param fd Deskryptor pliku
+ * \param size Rozmiar pliku
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skr?t SHA-1 pliku
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku b??du
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, %d, %d, %u, \"%s\", %p)\n", sess, rcpt, fd, size, filename1250, hash);
+
+	return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0);
+}
+
+
+/**
+ * Potwierdza ch?? odebrania pliku.
+ *
+ * \param dcc Struktura po??czenia
+ * \param offset Pocz?tkowy offset przy wznawianiu przesy?ania pliku
+ *
+ * \note Biblioteka nie zmienia po?o?enia w odbieranych plikach. Je?li offset
+ * pocz?tkowy jest r??ny od zera, nale?y ustawi? go funkcj? \c lseek() lub
+ * podobn?.
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
+{
+	struct gg_dcc7_accept pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
+
+	if (!dcc || !dcc->sess) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.id = dcc->cid;
+	pkt.offset = gg_fix32(offset);
+
+	if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1)
+		return -1;
+
+	dcc->offset = offset;
+
+	return gg_dcc7_listen_and_send_info(dcc);
+}
+
+/**
+ * Odrzuca pr?b? przes?ania pliku.
+ *
+ * \param dcc Struktura po??czenia
+ * \param reason Pow?d odrzucenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
+{
+	struct gg_dcc7_reject pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
+
+	if (!dcc || !dcc->sess) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.id = dcc->cid;
+	pkt.reason = gg_fix32(reason);
+
+	return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Obs?uguje pakiet identyfikatora po??czenia bezpo?redniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Tre?? pakietu
+ * \param len D?ugo?? pakietu
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_id_reply *p = payload;
+	struct gg_dcc7 *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
+
+		if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != gg_fix32(p->type))
+			continue;
+		
+		tmp->cid = p->id;
+
+		switch (tmp->dcc_type) {
+			case GG_DCC7_TYPE_FILE:
+			{
+				struct gg_dcc7_new s;
+
+				memset(&s, 0, sizeof(s));
+				s.id = tmp->cid;
+				s.type = gg_fix32(GG_DCC7_TYPE_FILE);
+				s.uin_from = gg_fix32(tmp->uin);
+				s.uin_to = gg_fix32(tmp->peer_uin);
+				s.size = gg_fix32(tmp->size);
+
+				strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN);
+
+				tmp->state = GG_STATE_WAITING_FOR_ACCEPT;
+				tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+				return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Obs?uguje pakiet akceptacji po??czenia bezpo?redniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Tre?? pakietu
+ * \param len D?ugo?? pakietu
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_accept *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n");
+		// XXX wys?a? reject?
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+	
+	// XXX czy dla odwrotnego po??czenia powinni?my wywo?a? ju? zdarzenie GG_DCC7_ACCEPT?
+	
+	dcc->offset = gg_fix32(p->offset);
+	dcc->state = GG_STATE_WAITING_FOR_INFO;
+
+	return 0;
+}
+
+/**
+ * \internal Obs?uguje pakiet informacji o po??czeniu bezpo?rednim.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Tre?? pakietu
+ * \param len D?ugo?? pakietu
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_info *p = payload;
+	struct gg_dcc7 *dcc;
+	char *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
+		return 0;
+	}
+	
+	if (p->type != GG_DCC7_TYPE_P2P) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	// je?li nadal czekamy na po??czenie przychodz?ce, a druga strona nie
+	// daje rady i oferuje namiary na siebie, bierzemy co daj?.
+
+	if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (dcc->state == GG_STATE_LISTENING) {
+		close(dcc->fd);
+		dcc->fd = -1;
+		dcc->reverse = 1;
+	}
+	
+	if (dcc->type == GG_SESSION_DCC7_SEND) {
+		e->type = GG_EVENT_DCC7_ACCEPT;
+		e->event.dcc7_accept.dcc7 = dcc;
+		e->event.dcc7_accept.type = gg_fix32(p->type);
+		e->event.dcc7_accept.remote_ip = dcc->remote_addr;
+		e->event.dcc7_accept.remote_port = dcc->remote_port;
+	} else {
+		e->type = GG_EVENT_DCC7_PENDING;
+	}
+
+	if (gg_dcc7_connect(sess, dcc) == -1) {
+		if (gg_dcc7_reverse_connect(dcc) == -1) {
+			e->type = GG_EVENT_DCC7_ERROR;
+			e->event.dcc7_error = GG_ERROR_DCC7_NET;
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Obs?uguje pakiet odrzucenia po??czenia bezpo?redniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Tre?? pakietu
+ * \param len D?ugo?? pakietu
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_reject *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n");
+		return 0;
+	}
+	
+	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	e->type = GG_EVENT_DCC7_REJECT;
+	e->event.dcc7_reject.dcc7 = dcc;
+	e->event.dcc7_reject.reason = gg_fix32(p->reason);
+
+	// XXX ustawi? state na rejected?
+
+	return 0;
+}
+
+/**
+ * \internal Obs?uguje pakiet nowego po??czenia bezpo?redniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Tre?? pakietu
+ * \param len D?ugo?? pakietu
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_new *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	switch (gg_fix32(p->type)) {
+		case GG_DCC7_TYPE_FILE:
+			if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n");
+				return -1;
+			}
+			
+			memset(dcc, 0, sizeof(struct gg_dcc7));
+			dcc->type = GG_SESSION_DCC7_GET;
+			dcc->dcc_type = GG_DCC7_TYPE_FILE;
+			dcc->fd = -1;
+			dcc->file_fd = -1;
+			dcc->uin = sess->uin;
+			dcc->peer_uin = gg_fix32(p->uin_from);
+			dcc->cid = p->id;
+			dcc->sess = sess;
+
+			if (gg_dcc7_session_add(sess, dcc) == -1) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+				gg_dcc7_free(dcc);
+				return -1;
+			}
+
+			dcc->size = gg_fix32(p->size);
+			strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1);
+			dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+			memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN);
+
+			e->type = GG_EVENT_DCC7_NEW;
+			e->event.dcc7_new = dcc;
+
+			break;
+
+		case GG_DCC7_TYPE_VOICE:
+			if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n");
+				return -1;
+			}
+			
+			memset(dcc, 0, sizeof(struct gg_dcc7));
+
+			dcc->type = GG_SESSION_DCC7_VOICE;
+			dcc->dcc_type = GG_DCC7_TYPE_VOICE;
+			dcc->fd = -1;
+			dcc->file_fd = -1;
+			dcc->uin = sess->uin;
+			dcc->peer_uin = gg_fix32(p->uin_from);
+			dcc->cid = p->id;
+			dcc->sess = sess;
+
+			if (gg_dcc7_session_add(sess, dcc) == -1) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+				gg_dcc7_free(dcc);
+				return -1;
+			}
+
+			e->type = GG_EVENT_DCC7_NEW;
+			e->event.dcc7_new = dcc;
+
+			break;
+
+		default:
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from));
+
+			break;
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Ustawia odpowiednie stany wewn?trzne w zale?no?ci od rodzaju
+ * po??czenia.
+ * 
+ * \param dcc Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du.
+ */
+static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
+
+	if (!dcc) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	switch (dcc->type) {
+		case GG_SESSION_DCC7_GET:
+			dcc->state = GG_STATE_GETTING_FILE;
+			dcc->check = GG_CHECK_READ;
+			return 0;
+
+		case GG_SESSION_DCC7_SEND:
+			dcc->state = GG_STATE_SENDING_FILE;
+			dcc->check = GG_CHECK_WRITE;
+			return 0;
+
+		case GG_SESSION_DCC7_VOICE:
+			dcc->state = GG_STATE_READING_VOICE_DATA;
+			dcc->check = GG_CHECK_READ;
+			return 0;
+	}
+
+	errno = EINVAL;
+
+	return -1;
+}
+
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
+ *
+ * Funkcja zwraca struktur? zdarzenia \c gg_event. Je?li rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzy?o si? jeszcze nic wartego odnotowania.
+ * Struktur? zdarzenia nale?y zwolni? funkcja \c gg_event_free().
+ *
+ * \param dcc Struktura po??czenia
+ *
+ * \return Struktura zdarzenia lub \c NULL je?li wyst?pi? b??d
+ *
+ * \ingroup dcc7
+ */
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
+{
+	struct gg_event *e;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
+
+	if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!(e = malloc(sizeof(struct gg_event)))) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
+		return NULL;
+	}
+
+	memset(e, 0, sizeof(struct gg_event));
+	e->type = GG_EVENT_NONE;
+
+	switch (dcc->state) {
+		case GG_STATE_LISTENING:
+		{
+			struct sockaddr_in sin;
+			int fd, one = 1;
+			unsigned int sin_len = sizeof(sin);
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
+
+			if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
+				return e;
+			}
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() 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_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
+				close(fd);
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			close(dcc->fd);
+			dcc->fd = fd;
+
+			dcc->state = GG_STATE_READING_ID;
+			dcc->check = GG_CHECK_READ;
+			dcc->timeout = GG_DEFAULT_TIMEOUT;
+			dcc->incoming = 1;
+
+			dcc->remote_port = ntohs(sin.sin_port);
+			dcc->remote_addr = sin.sin_addr.s_addr;
+
+			e->type = GG_EVENT_DCC7_CONNECTED;
+			e->event.dcc7_connected.dcc7 = dcc;
+
+			return e;
+		}
+
+		case GG_STATE_CONNECTING:
+		{
+			int res = 0, error = 0;
+			unsigned int error_size = sizeof(error);
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
+
+			dcc->soft_timeout = 0;
+
+			if (dcc->timeout == 0)
+				error = ETIMEDOUT;
+
+			if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
+
+				if (gg_dcc7_reverse_connect(dcc) != -1) {
+					e->type = GG_EVENT_DCC7_PENDING;
+				} else {
+					e->type = GG_EVENT_DCC7_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC7_NET;
+				}
+
+				return e;
+			}
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
+
+			dcc->state = GG_STATE_SENDING_ID;
+			dcc->check = GG_CHECK_WRITE;
+			dcc->timeout = GG_DEFAULT_TIMEOUT;
+			dcc->incoming = 0;
+
+			return e;
+		}
+
+		case GG_STATE_READING_ID:
+		{
+			gg_dcc7_id_t id;
+			int res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
+
+			if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (memcmp(&id, &dcc->cid, sizeof(id))) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (dcc->incoming) {
+				dcc->state = GG_STATE_SENDING_ID;
+				dcc->check = GG_CHECK_WRITE;
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			} else {
+				gg_dcc7_postauth_fixup(dcc);
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			}
+
+			return e;
+		}
+
+		case GG_STATE_SENDING_ID:
+		{
+			int res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
+
+			if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (dcc->incoming) {
+				gg_dcc7_postauth_fixup(dcc);
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			} else {
+				dcc->state = GG_STATE_READING_ID;
+				dcc->check = GG_CHECK_READ;
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			}
+
+			return e;
+		}
+
+		case GG_STATE_SENDING_FILE:
+		{
+			char buf[1024];
+			int chunk, res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_FILE;
+				return e;
+			}
+
+			if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
+				chunk = sizeof(buf);
+
+			if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
+				return e;
+			}
+
+			if ((res = write(dcc->fd, buf, res)) == -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_NET;
+				return e;
+			}
+
+			dcc->offset += res;
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			dcc->state = GG_STATE_SENDING_FILE;
+			dcc->check = GG_CHECK_WRITE;
+			dcc->timeout = GG_DCC7_TIMEOUT_SEND;
+
+			return e;
+		}
+
+		case GG_STATE_GETTING_FILE:
+		{
+			char buf[1024];
+			int res, wres;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
+				return e;
+			}
+
+			// XXX zapisywa? do skutku?
+
+			if ((wres = write(dcc->file_fd, buf, res)) < res) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_FILE;
+				return e;
+			}
+
+			dcc->offset += res;
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			dcc->state = GG_STATE_GETTING_FILE;
+			dcc->check = GG_CHECK_READ;
+			dcc->timeout = GG_DCC7_TIMEOUT_GET;
+
+			return e;
+		}
+
+		default:
+		{
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
+			e->type = GG_EVENT_DCC7_ERROR;
+			e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+
+			return e;
+		}
+	}
+
+	return e;
+}
+
+/**
+ * Zwalnia zasoby u?ywane przez po??czenie bezpo?rednie.
+ *
+ * \param dcc Struktura po??czenia
+ *
+ * \ingroup dcc7
+ */
+void gg_dcc7_free(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
+
+	if (!dcc)
+		return;
+
+	if (dcc->fd != -1)
+		close(dcc->fd);
+
+	if (dcc->file_fd != -1)
+		close(dcc->file_fd);
+
+	if (dcc->sess)
+		gg_dcc7_session_remove(dcc->sess, dcc);
+
+	free(dcc);
+}
+
============================================================
--- libpurple/protocols/gg/lib/libgadu-internal.h	b15a395fa150b059e459ac0fd8d7969f3334daa8
+++ libpurple/protocols/gg/lib/libgadu-internal.h	b15a395fa150b059e459ac0fd8d7969f3334daa8
@@ -0,0 +1,30 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2009 Jakub Zawadzki <darkjames at darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifndef LIBGADU_INTERNAL_H
+#define LIBGADU_INTERNAL_H
+
+#include "libgadu.h"
+
+char *gg_cp_to_utf8(const char *b);
+char *gg_utf8_to_cp(const char *b);
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length);
+
+#endif /* LIBGADU_INTERNAL_H */
============================================================
--- libpurple/protocols/gg/lib/protocol.h	6d28a9ca321450996c77258f2ac94607a7b53a4f
+++ libpurple/protocols/gg/lib/protocol.h	6d28a9ca321450996c77258f2ac94607a7b53a4f
@@ -0,0 +1,165 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2009 Jakub Zawadzki <darkjames at darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifndef LIBGADU_PROTOCOL_H
+#define LIBGADU_PROTOCOL_H
+
+#include "libgadu.h"
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#endif
+
+#define GG_LOGIN80BETA 0x0029
+
+#define GG_LOGIN80 0x0031
+
+#undef GG_FEATURE_STATUS80BETA
+#undef GG_FEATURE_MSG80
+#undef GG_FEATURE_STATUS80
+#define GG_FEATURE_STATUS80BETA		0x01
+#define GG_FEATURE_MSG80		0x02
+#define GG_FEATURE_STATUS80 		0x05
+
+#define GG8_LANG	"pl"
+#define GG8_VERSION	"Gadu-Gadu Client Build 8.0.0.8731"
+
+struct gg_login80 {
+	uint32_t uin;			/* m?j numerek */
+	uint8_t language[2];		/* j?zyk: GG8_LANG */
+	uint8_t hash_type;		/* rodzaj hashowania has?a */
+	uint8_t hash[64];		/* hash has?a dope?niony zerami */
+	uint32_t status;		/* status na dzie? dobry */
+	uint32_t flags;			/* flagi (przeznaczenie nieznane) */
+	uint32_t features;		/* opcje protoko?u (GG8_FEATURES) */
+	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;			/* 0x64 */
+} GG_PACKED;
+
+#define GG_LOGIN_HASH_TYPE_INVALID 0x0016
+
+#define GG_LOGIN80_OK 0x0035
+
+#define GG_NEW_STATUS80BETA 0x0028
+
+#define GG_NEW_STATUS80 0x0038
+
+/**
+ * Zmiana stanu (pakiet \c GG_NEW_STATUS80)
+ */
+struct gg_new_status80 {
+	uint32_t status;			/**< Nowy status */
+	uint32_t flags;				/**< flagi (nieznane przeznaczenie) */
+	uint32_t description_size;		/**< rozmiar opisu */
+} GG_PACKED;
+
+#define GG_STATUS80BETA 0x002a
+#define GG_NOTIFY_REPLY80BETA 0x002b
+
+#define GG_STATUS80 0x0036
+#define GG_NOTIFY_REPLY80 0x0037
+
+struct gg_notify_reply80 {
+	uint32_t uin;		/* numerek plus flagi w najstarszym bajcie */
+	uint32_t status;	/* status danej osoby */
+	uint32_t flags;		/* flagi (przeznaczenie nieznane) */
+	uint32_t remote_ip;	/* adres IP bezpo?rednich po??cze? */
+	uint16_t remote_port;	/* port bezpo?rednich po??cze? */
+	uint8_t image_size;	/* maksymalny rozmiar obrazk?w w KB */
+	uint8_t unknown2;	/* 0x00 */
+	uint32_t unknown3;	/* 0x00000000 */
+	uint32_t descr_len;	/* rozmiar opisu */
+} GG_PACKED;
+
+#define GG_SEND_MSG80 0x002d
+
+struct gg_send_msg80 {
+	uint32_t recipient;
+	uint32_t seq;
+	uint32_t msgclass;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_RECV_MSG80 0x002e
+
+struct gg_recv_msg80 {
+	uint32_t sender;
+	uint32_t seq;
+	uint32_t time;
+	uint32_t msgclass;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_DISCONNECT_ACK 0x000d
+
+#define GG_DCC7_VOICE_RETRIES 0x11	/* 17 powtorzen */
+
+#define GG_DCC7_RESERVED1		0xdeadc0de
+#define GG_DCC7_RESERVED2		0xdeadbeaf
+
+struct gg_dcc7_voice_auth {
+	uint8_t type;			/* 0x00 -> wysylanie ID
+					   0x01 -> potwierdzenie ID
+					*/
+	gg_dcc7_id_t id;		/* identyfikator po??czenia */
+	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */
+	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */
+} GG_PACKED;
+
+struct gg_dcc7_voice_nodata {	/* wyciszony mikrofon, ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac polaczenie) */
+	uint8_t type;			/* 0x02 */
+	gg_dcc7_id_t id;		/* identyfikator po??czenia */
+	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */
+	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */
+} GG_PACKED;
+
+struct gg_dcc7_voice_data {
+	uint8_t type;			/* 0x03 */
+	uint32_t did;			/* XXX: co ile zwieksza sie u nas id pakietu [uzywac 0x28] */
+	uint32_t len;			/* rozmiar strukturki - 1 (sizeof(type)) */
+	uint32_t packet_id;		/* numerek pakietu */
+	uint32_t datalen;		/* rozmiar danych */
+	/* char data[]; */		/* ramki: albo gsm, albo speex, albo melp, albo inne. */
+} GG_PACKED;
+
+struct gg_dcc7_voice_init {
+	uint8_t type;			/* 0x04 */
+	uint32_t id;			/* nr kroku [0x1 - 0x5] */
+	uint32_t protocol;		/* XXX: wersja protokolu (0x29, 0x2a, 0x2b) */
+	uint32_t len;			/* rozmiar sizeof(protocol)+sizeof(len)+sizeof(data) = 0x08 + sizeof(data) */
+	/* char data[]; */		/* reszta danych */
+} GG_PACKED;
+
+struct gg_dcc7_voice_init_confirm {
+	uint8_t type;			/* 0x05 */
+	uint32_t id;			/* id tego co potwierdzamy [0x1 - 0x5] */
+} GG_PACKED;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+
+#endif /* LIBGADU_PROTOCOL_H */
============================================================
--- libpurple/protocols/gg/lib/resolver.c	48326d4fd8687a81f7ab4ca06866c7d4d644b14a
+++ libpurple/protocols/gg/lib/resolver.c	48326d4fd8687a81f7ab4ca06866c7d4d644b14a
@@ -0,0 +1,753 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka at irc.pl>
+ *                          Robert J. Wo?ny <speedy at ziew.org>
+ *                          Arkadiusz Mi?kiewicz <arekm at pld-linux.org>
+ *                          Tomasz Chili?ski <chilek at chilan.com>
+ *                          Adam Wysocki <gophi at ekg.chmurka.net>
+ *
+ *  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.
+ */
+
+/**
+ * \file resolver.c
+ *
+ * \brief Funkcje rozwi?zywania nazw
+ */
+
+#include <sys/wait.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "libgadu.h"
+#include "resolver.h"
+#include "compat.h"
+
+/** Spos?b rozwi?zywania nazw serwer?w */
+static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT;
+
+/** Funkcja rozpoczynaj?ca rozwi?zywanie nazwy */
+static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname);
+
+/** Funkcja zwalniaj?ca zasoby po rozwi?zaniu nazwy */
+static void (*gg_global_resolver_cleanup)(void **private_data, int force);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+#include <pthread.h>
+
+/**
+ * \internal Funkcja pomocnicza zwalniaj?ca zasoby po rozwi?zywaniu nazwy
+ * w w?tku.
+ *
+ * \param data Wska?nik na wska?nik bufora zaalokowanego w w?tku
+ */
+static void gg_gethostbyname_cleaner(void *data)
+{
+	char **buf_ptr = (char**) data;
+
+	if (buf_ptr != NULL) {
+		free(*buf_ptr);
+		*buf_ptr = NULL;
+	}
+}
+
+#endif /* GG_CONFIG_HAVE_PTHREAD */
+
+/**
+ * \internal Odpowiednik \c gethostbyname zapewniaj?cy wsp??bie?no??.
+ *
+ * Je?li dany system dostarcza \c gethostbyname_r, u?ywa si? tej wersji, je?li
+ * nie, to zwyk?ej \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ * \param addr Wska?nik na rezultat rozwi?zywania nazwy
+ * \param pthread Flaga blokowania unicestwiania w?tku podczas alokacji pami?ci
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthread)
+{
+#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+	char *buf = NULL;
+	char *new_buf = NULL;
+	struct hostent he;
+	struct hostent *he_ptr = NULL;
+	size_t buf_len = 1024;
+	int result = -1;
+	int h_errnop;
+	int ret = 0;
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	int old_state;
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	pthread_cleanup_push(gg_gethostbyname_cleaner, &buf);
+
+	if (pthread)
+		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+	buf = malloc(buf_len);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	if (pthread)
+		pthread_setcancelstate(old_state, NULL);
+#endif
+
+	if (buf != NULL) {
+#ifndef sun
+		while ((ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop)) == ERANGE) {
+#else
+		while (((he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop)) == NULL) && (errno == ERANGE)) {
+#endif
+			buf_len *= 2;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+			if (pthread)
+				pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+			new_buf = realloc(buf, buf_len);
+
+			if (new_buf != NULL)
+				buf = new_buf;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+			if (pthread)
+				pthread_setcancelstate(old_state, NULL);
+#endif
+
+			if (new_buf == NULL) {
+				ret = ENOMEM;
+				break;
+			}
+		}
+
+		if (ret == 0 && he_ptr != NULL) {
+			memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr));
+			result = 0;
+		}
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		if (pthread)
+			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+		free(buf);
+		buf = NULL;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		if (pthread)
+			pthread_setcancelstate(old_state, NULL);
+#endif
+	}
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	pthread_cleanup_pop(1);
+#endif
+
+	return result;
+#else
+	struct hostent *he;
+
+	he = gethostbyname(hostname);
+
+	if (he == NULL)
+		return -1;
+
+	memcpy(addr, he->h_addr, sizeof(struct in_addr));
+
+	return 0;
+#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
+}
+
+/**
+ * \internal Odpowiednik \c gethostbyname zapewniaj?cy wsp??bie?no??.
+ *
+ * Je?li dany system dostarcza \c gethostbyname_r, u?ywa si? tej wersji, je?li
+ * nie, to zwyk?ej \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ *
+ * \return Zaalokowana struktura \c in_addr lub NULL w przypadku b??du.
+ */
+struct in_addr *gg_gethostbyname(const char *hostname)
+{
+	struct in_addr *addr;
+
+	if (!(addr = malloc(sizeof(struct in_addr))))
+		return NULL;
+
+	if (gg_gethostbyname_real(hostname, addr, 0)) {
+		free(addr);
+		return NULL;
+	}
+	return addr;
+}
+
+/**
+ * \internal Struktura przekazywana do w?tku rozwi?zuj?cego nazw?.
+ */
+struct gg_resolver_fork_data {
+	int pid;		/*< Identyfikator procesu */
+};
+
+/**
+ * \internal Rozwi?zuje nazw? serwera w osobnym procesie.
+ *
+ * Po??czenia asynchroniczne nie mog? blokowa? procesu w trakcie rozwi?zywania
+ * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim
+ * przeprowadzane jest rozwi?zywanie nazwy. Deskryptor strony do odczytu 
+ * zapisuje si? w strukturze sieci i czeka na dane w postaci struktury
+ * \c in_addr. Je?li nie znaleziono nazwy, zwracana jest \c INADDR_NONE.
+ *
+ * \param fd Wska?nik na zmienn?, gdzie zostanie umieszczony deskryptor
+ *           potoku
+ * \param priv_data Wska?nik na zmienn?, gdzie zostanie umieszczony wska?nik
+ *                  do numeru procesu potomnego rozwi?zuj?cego nazw?
+ * \param hostname Nazwa serwera do rozwi?zania
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname)
+{
+	struct gg_resolver_fork_data *data = NULL;
+	struct in_addr addr;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
+
+	if (fd == NULL || priv_data == NULL || hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	data = malloc(sizeof(struct gg_resolver_fork_data));
+
+	if (data == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n");
+		return -1;
+	}
+
+	if (pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	data->pid = fork();
+
+	if (data->pid == -1) {
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	if (data->pid == 0) {
+		close(pipes[0]);
+
+		if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+			/* W przypadku b??du gg_gethostbyname_real() zwr?ci -1
+                         * i nie zmieni &addr. Tam jest ju? INADDR_NONE,
+                         * wi?c nie musimy robi? nic wi?cej. */
+			gg_gethostbyname_real(hostname, &addr, 0);
+		}
+
+		if (write(pipes[1], &addr, sizeof(addr)) != sizeof(addr))
+			exit(1);
+
+		exit(0);
+	}
+
+	close(pipes[1]);
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data);
+
+	*fd = pipes[0];
+	*priv_data = data;
+
+	return 0;
+
+cleanup:
+	free(data);
+	close(pipes[0]);
+	close(pipes[1]);
+
+	errno = new_errno;
+
+	return -1;
+}
+
+/**
+ * \internal Usuwanie zasob?w po procesie rozwi?zywaniu nazwy.
+ *
+ * Funkcja wywo?ywana po zako?czeniu rozwi?zanywania nazwy lub przy zwalnianiu
+ * zasob?w sesji podczas rozwi?zywania nazwy.
+ *
+ * \param priv_data Wska?nik na zmienn? przechowuj?c? wska?nik do prywatnych
+ *                  danych
+ * \param force Flaga usuwania zasob?w przed zako?czeniem dzia?ania
+ */
+void gg_resolver_fork_cleanup(void **priv_data, int force)
+{
+	struct gg_resolver_fork_data *data;
+
+	if (priv_data == NULL || *priv_data == NULL)
+		return;
+
+	data = (struct gg_resolver_fork_data*) *priv_data;
+	*priv_data = NULL;
+
+	if (force)
+		kill(data->pid, SIGKILL);
+
+	waitpid(data->pid, NULL, WNOHANG);
+
+	free(data);
+}
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+/**
+ * \internal Struktura przekazywana do w?tku rozwi?zuj?cego nazw?.
+ */
+struct gg_resolver_pthread_data {
+	pthread_t thread;	/*< Identyfikator w?tku */
+	char *hostname;		/*< Nazwa serwera */
+	int rfd;		/*< Deskryptor do odczytu */
+	int wfd;		/*< Deskryptor do zapisu */
+};
+
+/**
+ * \internal Usuwanie zasob?w po w?tku rozwi?zywaniu nazwy.
+ *
+ * Funkcja wywo?ywana po zako?czeniu rozwi?zanywania nazwy lub przy zwalnianiu
+ * zasob?w sesji podczas rozwi?zywania nazwy.
+ *
+ * \param priv_data Wska?nik na zmienn? przechowuj?c? wska?nik do prywatnych
+ *                  danych
+ * \param force Flaga usuwania zasob?w przed zako?czeniem dzia?ania
+ */
+static void gg_resolver_pthread_cleanup(void **priv_data, int force)
+{
+	struct gg_resolver_pthread_data *data;
+
+	if (priv_data == NULL || *priv_data == NULL)
+		return;
+
+	data = (struct gg_resolver_pthread_data *) *priv_data;
+	*priv_data = NULL;
+
+	if (force) {
+		pthread_cancel(data->thread);
+		pthread_join(data->thread, NULL);
+	}
+
+	free(data->hostname);
+	data->hostname = NULL;
+
+	if (data->wfd != -1) {
+		close(data->wfd);
+		data->wfd = -1;
+	}
+
+	free(data);
+}
+
+/**
+ * \internal W?tek rozwi?zuj?cy nazw?.
+ *
+ * \param arg Wska?nik na struktur? \c gg_resolver_pthread_data
+ */
+static void *gg_resolver_pthread_thread(void *arg)
+{
+	struct gg_resolver_pthread_data *data = arg;
+	struct in_addr addr;
+
+	pthread_detach(pthread_self());
+
+	if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) {
+		/* W przypadku b??du gg_gethostbyname_real() zwr?ci -1
+                 * i nie zmieni &addr. Tam jest ju? INADDR_NONE,
+                 * wi?c nie musimy robi? nic wi?cej. */
+		gg_gethostbyname_real(data->hostname, &addr, 1);
+	}
+
+	if (write(data->wfd, &addr, sizeof(addr)) == sizeof(addr))
+		pthread_exit(NULL);
+	else 
+		pthread_exit((void*) -1);
+
+	return NULL;	/* ?eby kompilator nie marudzi? */
+}
+
+/**
+ * \internal Rozwi?zuje nazw? serwera w osobnym w?tku.
+ *
+ * Funkcja dzia?a analogicznie do \c gg_resolver_fork_start(), z t? r??nic?,
+ * ?e dzia?a na w?tkach, nie procesach. Jest dost?pna wy??cznie gdy podczas
+ * kompilacji w??czono odpowiedni? opcj?.
+ *
+ * \param fd Wska?nik na zmienn?, gdzie zostanie umieszczony deskryptor
+ *           potoku
+ * \param priv_data Wska?nik na zmienn?, gdzie zostanie umieszczony wska?nik
+ *                  do prywatnych danych w?tku rozwi?zuj?cego nazw?
+ * \param hostname Nazwa serwera do rozwi?zania
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname)
+{
+	struct gg_resolver_pthread_data *data = NULL;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
+
+	if (fd == NULL || priv_data == NULL || hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	data = malloc(sizeof(struct gg_resolver_pthread_data));
+
+	if (data == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n");
+		return -1;
+	}
+
+	if (pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	data->hostname = strdup(hostname);
+
+	if (data->hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	data->rfd = pipes[0];
+	data->wfd = pipes[1];
+
+	if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, data)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data);
+
+	*fd = pipes[0];
+	*priv_data = data;
+
+	return 0;
+
+cleanup:
+	if (data) {
+		free(data->hostname);
+		free(data);
+	}
+
+	close(pipes[0]);
+	close(pipes[1]);
+
+	errno = new_errno;
+
+	return -1;
+}
+
+#endif /* GG_CONFIG_HAVE_PTHREAD */
+
+/**
+ * Ustawia spos?b rozwi?zywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ * \param type Spos?b rozwi?zywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)
+{
+	if (gs == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (type == GG_RESOLVER_DEFAULT) {
+		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
+			gs->resolver_type = gg_global_resolver_type;
+			gs->resolver_start = gg_global_resolver_start;
+			gs->resolver_cleanup = gg_global_resolver_cleanup;
+			return 0;
+		}
+
+#if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT)
+		type = GG_RESOLVER_FORK;
+#else
+		type = GG_RESOLVER_PTHREAD;
+#endif
+	}
+
+	switch (type) {
+		case GG_RESOLVER_FORK:
+			gs->resolver_type = type;
+			gs->resolver_start = gg_resolver_fork_start;
+			gs->resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gs->resolver_type = type;
+			gs->resolver_start = gg_resolver_pthread_start;
+			gs->resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca spos?b rozwi?zywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ *
+ * \return Spos?b rozwi?zywania nazw
+ */
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs)
+{
+	if (gs == NULL) {
+		errno = EINVAL;
+		return GG_RESOLVER_INVALID;
+	}
+
+	return gs->resolver_type;
+}
+
+/**
+ * Ustawia w?asny spos?b rozwi?zywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ * \param resolver_start Funkcja rozpoczynaj?ca rozwi?zywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniaj?ca zasoby
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gs->resolver_type = GG_RESOLVER_CUSTOM;
+	gs->resolver_start = resolver_start;
+	gs->resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
+/**
+ * Ustawia spos?b rozwi?zywania nazw po??czenia HTTP.
+ *
+ * \param gh Struktura po??czenia
+ * \param type Spos?b rozwi?zywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type)
+{
+	if (gh == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (type == GG_RESOLVER_DEFAULT) {
+		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
+			gh->resolver_type = gg_global_resolver_type;
+			gh->resolver_start = gg_global_resolver_start;
+			gh->resolver_cleanup = gg_global_resolver_cleanup;
+			return 0;
+		}
+
+#if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT)
+		type = GG_RESOLVER_FORK;
+#else
+		type = GG_RESOLVER_PTHREAD;
+#endif
+	}
+
+	switch (type) {
+		case GG_RESOLVER_FORK:
+			gh->resolver_type = type;
+			gh->resolver_start = gg_resolver_fork_start;
+			gh->resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gh->resolver_type = type;
+			gh->resolver_start = gg_resolver_pthread_start;
+			gh->resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca spos?b rozwi?zywania nazw po??czenia HTTP.
+ *
+ * \param gh Struktura po??czenia
+ *
+ * \return Spos?b rozwi?zywania nazw
+ */
+gg_resolver_t gg_http_get_resolver(struct gg_http *gh)
+{
+	if (gh == NULL) {
+		errno = EINVAL;
+		return GG_RESOLVER_INVALID;
+	}
+
+	return gh->resolver_type;
+}
+
+/**
+ * Ustawia w?asny spos?b rozwi?zywania nazw po??czenia HTTP.
+ *
+ * \param gh Struktura sesji
+ * \param resolver_start Funkcja rozpoczynaj?ca rozwi?zywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniaj?ca zasoby
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gh->resolver_type = GG_RESOLVER_CUSTOM;
+	gh->resolver_start = resolver_start;
+	gh->resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
+/**
+ * Ustawia spos?b rozwi?zywania nazw globalnie dla biblioteki.
+ *
+ * \param type Spos?b rozwi?zywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_global_set_resolver(gg_resolver_t type)
+{
+	switch (type) {
+		case GG_RESOLVER_DEFAULT:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = NULL;
+			gg_global_resolver_cleanup = NULL;
+			return 0;
+
+		case GG_RESOLVER_FORK:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = gg_resolver_fork_start;
+			gg_global_resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = gg_resolver_pthread_start;
+			gg_global_resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca spos?b rozwi?zywania nazw globalnie dla biblioteki.
+ *
+ * \return Spos?b rozwi?zywania nazw
+ */
+gg_resolver_t gg_global_get_resolver(void)
+{
+	return gg_global_resolver_type;
+}
+
+/**
+ * Ustawia w?asny spos?b rozwi?zywania nazw globalnie dla biblioteki.
+ *
+ * \param resolver_start Funkcja rozpoczynaj?ca rozwi?zywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniaj?ca zasoby
+ *
+ * Parametry funkcji rozpoczynaj?cej rozwi?zywanie nazwy wygl?daj? nast?puj?co:
+ *  - \c "int *fd" &mdash; wska?nik na zmienn?, gdzie zostanie umieszczony deskryptor potoku
+ *  - \c "void **priv_data" &mdash; wska?nik na zmienn?, gdzie mo?na umie?ci? wska?nik do prywatnych danych na potrzeby rozwi?zywania nazwy
+ *  - \c "const char *name" &mdash; nazwa serwera do rozwi?zania
+ *
+ * Parametry funkcji zwalniaj?cej zasoby wygl?daj? nast?puj?co:
+ *  - \c "void **priv_data" &mdash; wska?nik na zmienn? przechowuj?c? wska?nik do prywatnych danych, nale?y go ustawi? na \c NULL po zako?czeniu
+ *  - \c "int force" &mdash; flaga m?wi?ca o tym, ?e zasoby s? zwalniane przed zako?czeniem rozwi?zywania nazwy, np. z powodu zamkni?cia sesji.
+ *
+ * W?asny kod rozwi?zywania nazwy powinien stworzy? potok, par? gniazd lub
+ * inny deskryptor pozwalaj?cy na co najmniej jednostronn? komunikacj? i 
+ * przekaza? go w parametrze \c fd. Po zako?czeniu rozwi?zywania nazwy,
+ * powinien wys?a? otrzymany adres IP w postaci sieciowej (big-endian) do
+ * deskryptora. Je?li rozwi?zywanie nazwy si? nie powiedzie, nale?y wys?a?
+ * \c INADDR_NONE. Nast?pnie zostanie wywo?ana funkcja zwalniaj?ca zasoby
+ * z parametrem \c force r?wnym \c 0. Gdyby sesja zosta?a zako?czona przed
+ * rozwi?zaniem nazwy, np. za pomoc? funkcji \c gg_logoff(), funkcja
+ * zwalniaj?ca zasoby zostanie wywo?ana z parametrem \c force r?wnym \c 1.
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gg_global_resolver_type = GG_RESOLVER_CUSTOM;
+	gg_global_resolver_start = resolver_start;
+	gg_global_resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
============================================================
--- libpurple/protocols/gg/lib/resolver.h	a02b61e069991489c5f0b2804ef8bf3708e198a4
+++ libpurple/protocols/gg/lib/resolver.h	a02b61e069991489c5f0b2804ef8bf3708e198a4
@@ -0,0 +1,28 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2008 Wojtek Kaniewski <wojtekka at irc.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifndef LIBGADU_RESOLVER_H
+#define LIBGADU_RESOLVER_H
+
+#include <arpa/inet.h>
+
+int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread);
+
+#endif /* LIBGADU_RESOLVER_H */
============================================================
--- libpurple/protocols/gg/lib/sha1.c	a435e563568a4d09e68a08f659c64ae99bd8ec66
+++ libpurple/protocols/gg/lib/sha1.c	a435e563568a4d09e68a08f659c64ae99bd8ec66
@@ -0,0 +1,303 @@
+/* $Id: sha1.c 632 2008-07-30 18:40:06Z darkjames $ */
+
+/*
+ *  (C) Copyright 2007 Wojtek Kaniewski <wojtekka at irc.pl>
+ *
+ *  Public domain SHA-1 implementation by Steve Reid <steve at edmweb.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.
+ */
+
+/**
+ * \file sha1.c
+ *
+ * \brief Funkcje wyznaczania skr??tu SHA1
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+
+/** \cond ignore */
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+
+#include <openssl/sha.h>
+
+#else
+
+/*
+SHA-1 in C
+By Steve Reid <steve at edmweb.com>
+100% Public Domain
+
+Modified by Wojtek Kaniewski <wojtekka at toxygen.net> for compatibility
+with libgadu and OpenSSL API.
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <string.h>
+
+typedef struct {
+    uint32_t state[5];
+    uint32_t count[2];
+    unsigned char buffer[64];
+} SHA_CTX;
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]);
+static void SHA1_Init(SHA_CTX* context);
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len);
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef GG_CONFIG_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+uint32_t a, b, c, d, e;
+typedef union {
+    unsigned char c[64];
+    uint32_t l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+static unsigned char workspace[64];
+    block = (CHAR64LONG16*)workspace;
+    memcpy(block, buffer, 64);
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1_Init - Initialize new context */
+
+static void SHA1_Init(SHA_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len)
+{
+unsigned int i, j;
+
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63) {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1_Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1_Transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context)
+{
+uint32_t i, j;
+unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1_Update(context, (unsigned char *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+        SHA1_Update(context, (unsigned char *)"\0", 1);
+    }
+    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1_Transform() */
+    for (i = 0; i < 20; i++) {
+        digest[i] = (unsigned char)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    i = j = 0;
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF  /* make SHA1_Transform overwrite it's own static vars */
+    SHA1_Transform(context->state, context->buffer);
+#endif
+}
+
+#endif /* GG_CONFIG_HAVE_OPENSSL */
+
+/** \endcond */
+
+/** \cond internal */
+
+/**
+ * \internal Liczy skr??t SHA1 z ziarna i has??a.
+ *
+ * \param password Has??o
+ * \param seed Ziarno
+ * \param result Bufor na wynik funkcji skr??tu (20 bajt??w)
+ */
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
+{
+	SHA_CTX ctx;
+	
+	SHA1_Init(&ctx);
+	SHA1_Update(&ctx, (const unsigned char*) password, strlen(password));
+	seed = gg_fix32(seed);
+	SHA1_Update(&ctx, (uint8_t*) &seed, 4);
+	
+	SHA1_Final(result, &ctx);
+}
+
+/**
+ * \internal Liczy skr??t SHA1 z pliku.
+ *
+ * \param fd Deskryptor pliku
+ * \param result Wska??nik na skr??t
+ *
+ * \return 0 lub -1
+ */
+int gg_file_hash_sha1(int fd, uint8_t *result)
+{
+	unsigned char buf[4096];
+	SHA_CTX ctx;
+	off_t pos, len;
+	int res;
+
+	if ((pos = lseek(fd, 0, SEEK_CUR)) == (off_t) -1)
+		return -1;
+
+	if ((len = lseek(fd, 0, SEEK_END)) == (off_t) -1)
+		return -1;
+
+	if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+		return -1;
+
+	SHA1_Init(&ctx);
+
+	if (len <= 10485760) {
+		while ((res = read(fd, buf, sizeof(buf))) > 0)
+			SHA1_Update(&ctx, buf, res);
+	} else {
+		int i;
+
+		for (i = 0; i < 9; i++) {
+			int j;
+
+			if (lseek(fd, (len - 1048576) / 9 * i, SEEK_SET) == (off_t) - 1)
+				return -1;
+
+			for (j = 0; j < 1048576 / sizeof(buf); j++) {
+				if ((res = read(fd, buf, sizeof(buf))) != sizeof(buf)) {
+					res = -1;
+					break;
+				}
+
+				SHA1_Update(&ctx, buf, res);
+			}
+
+			if (res == -1)
+				break;
+		}
+	}
+
+	if (res == -1)
+		return -1;
+
+	SHA1_Final(result, &ctx);
+
+	if (lseek(fd, pos, SEEK_SET) == (off_t) -1)
+		return -1;
+
+	return 0;
+}
+
+/** \endcond */
============================================================
--- configure.ac	a7a976454d1e0fe1ad9cc0c46604253929212e6e
+++ configure.ac	d874317d299024e8b3ea2b77feab7efa9c95c0f7
@@ -2583,6 +2583,7 @@ echo Use external libzephyr........ : $z
 echo Build with Cyrus SASL support. : $enable_cyrus_sasl
 echo Use kerberos 4 with zephyr.... : $kerberos
 echo Use external libzephyr........ : $zephyr
+echo Use external libgadu.......... : $gadu_libs
 echo Install pixmaps............... : $enable_pixmaps
 echo Install translations.......... : $enable_i18n
 echo Has you....................... : yes
============================================================
--- libpurple/protocols/gg/Makefile.am	f5308fc67360a9eea653c1d7c6b624e6e9bc8525
+++ libpurple/protocols/gg/Makefile.am	542a088114f4543df45354d4a9a4cc9201ca6bc6
@@ -4,14 +4,19 @@ EXTRA_DIST = \
 	lib/compat.h \
 	lib/COPYING \
 	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/libgadu-config.h \
 	lib/libgadu.h \
 	lib/obsolete.c \
+	lib/protocol.h \
+	lib/pubdir.c \
 	lib/pubdir50.c \
-	lib/pubdir.c
+	lib/resolver.c \
+	lib/resolver.h \
+	lib/sha1.c
 
 pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
 
@@ -20,16 +25,21 @@ INTGGSOURCES = \
 	lib/common.c \
 	lib/compat.h \
 	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/libgadu-config.h \
 	lib/libgadu.h \
 	lib/obsolete.c \
+	lib/protocol.h \
+	lib/pubdir.c \
 	lib/pubdir50.c \
-	lib/pubdir.c
+	lib/resolver.c \
+	lib/resolver.h \
+	lib/sha1.c
 
-INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib
+INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED
 endif
 
 GGSOURCES = \
============================================================
--- libpurple/protocols/gg/Makefile.mingw	b29901fe3c5723e10a76dceb61ae347642b56d90
+++ libpurple/protocols/gg/Makefile.mingw	9016708650c8b5beae3603a2d60b409acd92b476
@@ -41,12 +41,15 @@ C_SRC =	\
 ##
 C_SRC =	\
 	lib/common.c \
+	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/obsolete.c \
 	lib/pubdir.c \
 	lib/pubdir50.c \
+	lib/resolver.c \
 	buddylist.c \
 	confer.c \
 	gg.c \
============================================================
--- libpurple/protocols/gg/lib/common.c	855c2847a27d4059ecaadb0ead0a982ac965ebc8
+++ libpurple/protocols/gg/lib/common.c	a333f8fc06cb82eb7ddf704db071f4b13af7ad70
@@ -1,8 +1,8 @@
-/* $Id: common.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: common.c 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka at irc.pl>
- *                          Robert J. Wo?ny <speedy at ziew.org>
+ *                          Robert J. Wo?ny <speedy at 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
@@ -15,13 +15,38 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/*
+ * Funkcje konwersji mi?dzy UTF-8 i CP1250 s? oparte o kod biblioteki iconv.
+ * Informacje o prawach autorskich oryginalnego kodu zamieszczono poni?ej:
+ *
+ * Copyright (C) 1999-2001, 2004 Free Software Foundation, Inc.
+ * This file is part of the GNU LIBICONV Library.
+ *
+ * The GNU LIBICONV Library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * The GNU LIBICONV 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU LIBICONV Library; see the file COPYING.LIB.
+ * If not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Fifth Floor, Boston, MA 02110-1301, USA.
+ */
 
-#ifndef _WIN32
+/**
+ * \file common.c
+ *
+ * \brief Funkcje wykorzystywane przez r??ne modu?y biblioteki
+ */
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
@@ -30,94 +55,133 @@
 #ifdef sun
 #  include <sys/filio.h>
 #endif
-#endif
 
 #include <errno.h>
 #include <fcntl.h>
-#ifndef _WIN32
 #include <netdb.h>
-#endif
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+#include "libgadu.h"
+#include "libgadu-internal.h"
+
+/**
+ * Plik, do kt?rego b?d? przekazywane informacje odpluskwiania.
+ *
+ * Funkcja \c gg_debug() i pochodne mog? by? przechwytywane przez aplikacj?
+ * korzystaj?c? z biblioteki, by wy?wietli? je na ??danie u?ytkownika lub
+ * zapisa? do p??niejszej analizy. Je?li nie okre?lono pliku, wybrane
+ * informacje b?d? wysy?ane do standardowego wyj?cia b??du (\c stderr).
+ *
+ * \ingroup debug
+ */
 FILE *gg_debug_file = NULL;
 
 #ifndef GG_DEBUG_DISABLE
 
-/*
- * gg_debug() // funkcja wewn?zna
+/**
+ * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji.
  *
- * wy?wietla komunikat o danym poziomie, o ile u?ytkownik sobie tego ?yczy.
+ * Je?li aplikacja ustawi?a odpowiedni? funkcj? obs?ugi w
+ * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywo?ywana.
+ * W przeciwnym wypadku wynik jest wysy?any do standardowego wyj?cia b??du.
  *
- *  - level - poziom wiadomo?ci
- *  - format... - tre??iadomo?ci (kompatybilna z printf())
+ * \param sess Struktura sesji (mo?e by? \c NULL)
+ * \param level Poziom informacji
+ * \param format Format wiadomo?ci (zgodny z \c printf)
+ * \param ap Lista argument?w (zgodna z \c printf)
  */
+void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
+{
+	if (gg_debug_handler_session)
+		(*gg_debug_handler_session)(sess, level, format, ap);
+	else if (gg_debug_handler)
+		(*gg_debug_handler)(level, format, ap);
+	else if (gg_debug_level & level)
+		vfprintf(gg_debug_file ? gg_debug_file : stderr, format, ap);
+}
+
+
+/**
+ * \internal Przekazuje informacj? odpluskawiania.
+ *
+ * \param level Poziom wiadomo?ci
+ * \param format Format wiadomo?ci (zgodny z \c printf)
+ *
+ * \ingroup debug
+ */
 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);
+	va_start(ap, format);
+	gg_debug_common(NULL, level, format, ap);
+	va_end(ap);
+	errno = old_errno;
+}
 
-		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:
+/**
+ * \internal Przekazuje informacj? odpluskwiania zwi?zan? z sesj?.
+ *
+ * \param sess Struktura sesji
+ * \param level Poziom wiadomo?ci
+ * \param format Format wiadomo?ci (zgodny z \c printf)
+ *
+ * \ingroup debug
+ */
+void gg_debug_session(struct gg_session *sess, int level, const char *format, ...)
+{
+	va_list ap;
+	int old_errno = errno;
+	va_start(ap, format);
+	gg_debug_common(sess, level, format, ap);
+	va_end(ap);
 	errno = old_errno;
 }
 
 #endif
 
-/*
- * gg_vsaprintf() // funkcja pomocnicza
+/**
+ * \internal Odpowiednik funkcji \c vsprintf alokuj?cy miejsce na wynik.
  *
- * robi dok?adnie to samo, co vsprintf(), tyle ?e alokuje sobie wcze?niej
- * miejsce na dane. powinno dzia?a?a tych maszynach, kt?maj? funkcj? * vsnprintf() zgodn? z C99, jak i na wcze?niejszych.
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzaj?c czy dost?pna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcze?niejszymi.
  *
- *  - format - opis wy?wietlanego tekstu jak dla printf()
- *  - ap - lista argument?la printf()
+ * \param format Format wiadomo?ci (zgodny z \c printf)
+ * \param ap Lista argument?w (zgodna z \c printf)
  *
- * zaalokowany bufor, kt?nale?y p?ej zwolni?lub NULL
- * je?li nie uda?o si?ykona?adania.
+ * \return Zaalokowany bufor lub NULL, je?li zabrak?o pami?ci.
+ *
+ * \ingroup helper
  */
 char *gg_vsaprintf(const char *format, va_list ap)
 {
 	int size = 0;
 	const char *start;
 	char *buf = NULL;
-	
-#ifdef __GG_LIBGADU_HAVE_VA_COPY
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
 	va_list aq;
 
 	va_copy(aq, ap);
 #else
-#  ifdef __GG_LIBGADU_HAVE___VA_COPY
+#  ifdef GG_CONFIG_HAVE___VA_COPY
 	va_list aq;
 
 	__va_copy(aq, ap);
 #  endif
 #endif
 
-	start = format; 
+	start = format;
 
-#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF
+#ifndef GG_CONFIG_HAVE_C99_VSNPRINTF
 	{
 		int res;
 		char *tmp;
-		
+
 		size = 128;
 		do {
 			size *= 2;
@@ -132,9 +196,9 @@ char *gg_vsaprintf(const char *format, v
 #else
 	{
 		char tmp[2];
-		
-		/* libce Solarisa przy buforze NULL zawsze zwracaj? -1, wi?-		 * musimy poda?o? istniej?cego jako cel printf()owania. */
+
+		/* 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;
@@ -142,33 +206,33 @@ char *gg_vsaprintf(const char *format, v
 #endif
 
 	format = start;
-	
-#ifdef __GG_LIBGADU_HAVE_VA_COPY
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
 	vsnprintf(buf, size + 1, format, aq);
 	va_end(aq);
 #else
-#  ifdef __GG_LIBGADU_HAVE___VA_COPY
+#  ifdef GG_CONFIG_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
+/**
+ * \internal Odpowiednik funkcji \c sprintf alokuj?cy miejsce na wynik.
  *
- * robi dok?adnie to samo, co sprintf(), tyle ?e alokuje sobie wcze?niej
- * miejsce na dane. powinno dzia?a?a tych maszynach, kt?maj? funkcj? * vsnprintf() zgodn? z C99, jak i na wcze?niejszych.
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzaj?c czy dost?pna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcze?niejszymi.
  *
- *  - format... - tre??aka sama jak w funkcji printf()
+ * \param format Format wiadomo?ci (zgodny z \c printf)
  *
- * zaalokowany bufor, kt?nale?y p?ej zwolni?lub NULL
- * je?li nie uda?o si?ykona?adania.
+ * \return Zaalokowany bufor lub NULL, je?li zabrak?o pami?ci.
+ *
+ * \ingroup helper
  */
 char *gg_saprintf(const char *format, ...)
 {
@@ -182,18 +246,17 @@ char *gg_saprintf(const char *format, ..
 	return res;
 }
 
-/*
- * gg_get_line() // funkcja pomocnicza
- * 
- * podaje kolejn? lini? bufora tekstowego. niszczy go bezpowrotnie, dziel?c
- * na kolejne stringi. zdarza si?nie ma potrzeby pisania funkcji dubluj?cej
- * bufor ?eby tylko mie?ieruszone dane wej?ciowe, skoro i tak nie b? nam
- * po?niej potrzebne. obcina `\r\n'.
- * 
- *  - ptr - wska?nik do zmiennej, kt?przechowuje aktualn? pozycj? *    w przemiatanym buforze
- * 
- * wska?nik do kolejnej linii tekstu lub NULL, je?li to ju? koniec bufora.
+/**
+ * \internal Pobiera lini? tekstu z bufora.
+ *
+ * Funkcja niszczy bufor ?r?d?owy bezpowrotnie, dziel?c go na kolejne ci?gi
+ * znak?w i obcina znaki ko?ca linii.
+ *
+ * \param ptr Wska?nik do zmiennej, kt?ra przechowuje aktualne po?o?enie
+ *            w analizowanym buforze
+ *
+ * \return Wska?nik do kolejnej linii tekstu lub NULL, je?li to ju? koniec
+ *         bufora.
  */
 char *gg_get_line(char **ptr)
 {
@@ -207,28 +270,72 @@ char *gg_get_line(char **ptr)
 	if (!(foo = strchr(*ptr, '\n')))
 		*ptr += strlen(*ptr);
 	else {
+		size_t len;
 		*ptr = foo + 1;
 		*foo = 0;
-		if (strlen(res) > 1 && res[strlen(res) - 1] == '\r')
-			res[strlen(res) - 1] = 0;
+
+		len = strlen(res);
+
+		if (len > 1 && res[len - 1] == '\r')
+			res[len - 1] = 0;
 	}
 
 	return res;
 }
 
-/*
- * gg_connect() // funkcja pomocnicza
+/**
+ * \internal Czyta lini? tekstu z gniazda.
  *
- * ??czy si? serwerem. pierwszy argument jest typu (void *), ?eby nie
- * musie?iczego inkludowa? libgadu.h i nie psu?aki? g?upich zale?no?ci
- * na dziwnych systemach.
+ * Funkcja czyta tekst znak po znaku, wi?c nie jest efektywna, ale dzi?ki
+ * brakowi buforowania, nie koliduje z innymi funkcjami odczytu.
  *
- *  - addr - adres serwera (struct in_addr *)
- *  - port - port serwera
- *  - async - asynchroniczne po??czenie
+ * \param sock Deskryptor gniazda
+ * \param buf Wska?nik do bufora
+ * \param length D?ugo?? bufora
  *
- * deskryptor gniazda lub -1 w przypadku b?? (kod b?? w zmiennej errno).
+ * \return Zwraca \c buf je?li si? powiod?o, lub \c NULL w przypadku b??du.
  */
+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;
+}
+
+/**
+ * \internal Nawi?zuje po??czenie TCP.
+ *
+ * \param addr Wska?nik na struktur? \c in_addr z adresem serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego po??czenia
+ *
+ * \return Deskryptor gniazda lub -1 w przypadku b??du
+ *
+ * \ingroup helper
+ */
 int gg_connect(void *addr, int port, int async)
 {
 	int sock, one = 1, errno2;
@@ -237,7 +344,7 @@ int gg_connect(void *addr, int port, int
 	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;
@@ -250,6 +357,9 @@ int gg_connect(void *addr, int port, int
 
 	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));
+		errno2 = errno;
+		close(sock);
+		errno = errno2;
 		return -1;
 	}
 
@@ -274,7 +384,7 @@ int gg_connect(void *addr, int port, int
 	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));
@@ -285,84 +395,46 @@ int gg_connect(void *addr, int port, int
 		}
 		gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
 	}
-	
+
 	return sock;
 }
 
-/*
- * gg_read_line() // funkcja pomocnicza
+/**
+ * \internal Usuwa znaki ko?ca linii.
  *
- * czyta jedn? lini?ekstu z gniazda.
+ * Funkcja dzia?a bezpo?rednio na buforze.
  *
- *  - sock - deskryptor gniazda
- *  - buf - wska?nik do bufora
- *  - length - d?ugo??ufora
+ * \param line Bufor z tekstem
  *
- * je?li trafi na b??d odczytu lub podano nieprawid?owe parametry, zwraca NULL.
- * inaczej zwraca buf.
+ * \ingroup helper
  */
-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?linii.
- *
- *  - line - linia do przyci?a
- */
 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?zna
+/**
+ * \internal Koduje ci?g znak?w do postacji adresu HTTP.
  *
- * zamienia podany tekst na ci?g znak?o formularza http. przydaje si? * przy r?ch us?ugach katalogu publicznego.
+ * Zamienia znaki niedrukowalne, spoza ASCII i maj?ce specjalne znaczenie
+ * dla protoko?u HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkow?
+ * warto?ci? znaku.
+ * 
+ * \param str Ci?g znak?w do zakodowania
  *
- *  - str - ci?g znak?o zakodowania
+ * \return Zaalokowany bufor lub \c NULL w przypadku b??du.
  *
- * zaalokowany bufor, kt?nale?y p?ej zwolni?lbo NULL
- * w przypadku b??.
+ * \ingroup helper
  */
 char *gg_urlencode(const char *str)
 {
@@ -400,16 +472,19 @@ char *gg_urlencode(const char *str)
 	return buf;
 }
 
-/*
- * gg_http_hash() // funkcja wewn?zna
+/**
+ * \internal Wyznacza skr?t dla us?ug HTTP.
  *
- * funkcja licz?ca hash dla adresu e-mail, has?a i paru innych.
+ * Funkcja jest wykorzystywana do wyznaczania skr?tu adresu e-mail, has?a
+ * i innych warto?ci przekazywanych jako parametry us?ug HTTP.
  *
- *  - format... - format kolejnych parametr?'s' je?li dany parametr jest
- *                ci?giem znak?ub 'u' je?li numerem GG)
+ * W parametrze \c format nale?y umie?ci? znaki okre?laj?ce posta? kolejnych
+ * parametr?w: \c 's' je?li parametr jest ci?giem znak?w, \c 'u' je?li jest
+ * liczb?.
  *
- * hash wykorzystywany przy rejestracji i wszelkich manipulacjach w?asnego
- * wpisu w katalogu publicznym.
+ * \param format Format kolejnych parametr?w (niezgodny z \c printf)
+ *
+ * \return Warto?? skr?tu
  */
 int gg_http_hash(const char *format, ...)
 {
@@ -428,7 +503,7 @@ int gg_http_hash(const char *format, ...
 		} else {
 			if (!(arg = va_arg(ap, char*)))
 				arg = "";
-		}	
+		}
 
 		i = 0;
 		while ((c = (unsigned char) arg[i++]) != 0) {
@@ -442,95 +517,6 @@ int gg_http_hash(const char *format, ...
 	return (b < 0 ? -b : b);
 }
 
-/*
- * gg_gethostbyname() // funkcja pomocnicza
- *
- * odpowiednik gethostbyname() troszcz?cy si? wsp?e?no??gdy mamy do
- * dyspozycji funkcj?ethostbyname_r().
- *
- *  - hostname - nazwa serwera
- *
- * zwraca wska?nik na struktur?n_addr, kt?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 {
@@ -541,23 +527,22 @@ struct gg_win32_thread *gg_win32_threads
 
 struct gg_win32_thread *gg_win32_threads = 0;
 
-/*
- * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32
+/**
+ * \internal Zwraca deskryptor gniazda, kt?re by?o ostatnio tworzone dla w?tku.
  *
- * zwraca deskryptor gniazda, kt?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 \c gg_win32_thread_socket(watek,-1)
  *
- * je?li na win32 przy po??czeniach synchronicznych zapami?my w jakim
- * w?tku uruchomili?my funkcj?kt?si? czymkolwiek ??czy, to z osobnego
- * w?tku mo?emy anulowa?o??czenie poprzez gg_win32_thread_socket(watek, -1);
- * 
- * - thread_id - id w?tku. je?li jest r? 0, brany jest aktualny w?tek,
- *               je?li r? -1, usuwa wpis o podanym sockecie.
- * - socket - deskryptor gniazda. je?li r? 0, zwraca deskryptor gniazda
- *            dla podanego w?tku, je?li r? -1, usuwa wpis, je?li co?
- *            innego, ustawia dla podanego w?tku dany numer deskryptora.
+ * \param thread_id Identyfikator w?tku (je?li jest r?wne 0, brany jest
+ *                  aktualny w?tek, je?li r?wne -1, usuwa wpis dotycz?cy
+ *                  danego gniazda sockecie)
+ * \param 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? 0, zwraca deskryptor gniazda dla podanego w?tku.
+ * \return Je?li socket jest r?wne 0, zwraca deskryptor gniazda dla podanego
+ *         w?tku.
  */
 int gg_win32_thread_socket(int thread_id, int socket)
 {
@@ -567,7 +552,7 @@ int gg_win32_thread_socket(int thread_id
 
 	if (!thread_id)
 		thread_id = GetCurrentThreadId();
-	
+
 	while (wsk) {
 		if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) {
 			if (close) {
@@ -593,7 +578,7 @@ int gg_win32_thread_socket(int thread_id
 		closesocket(socket);
 	if (close || !socket)
 		return 0;
-	
+
 	/* Dodaje nowy element */
 	wsk = malloc(sizeof(gg_win32_thread));
 	wsk->id = thread_id;
@@ -606,28 +591,33 @@ int gg_win32_thread_socket(int thread_id
 
 #endif /* ASSIGN_SOCKETS_TO_THREADS */
 
+/**
+ * \internal Zestaw znak?w kodowania base64.
+ */
 static char gg_base64_charset[] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-/*
- * gg_base64_encode()
+/**
+ * \internal Koduje ci?g znak?w do base64.
  *
- * zapisuje ci?g znak? base64.
+ * Wynik funkcji nale?y zwolni? za pomoc? \c free.
  *
- *  - buf - ci?g znak?+ * \param buf Bufor z danami do zakodowania
  *
- * zaalokowany bufor.
+ * \return Zaalokowany bufor z zakodowanymi danymi
+ *
+ * \ingroup helper
  */
 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:
@@ -660,20 +650,22 @@ char *gg_base64_encode(const char *buf)
 	if (i % 4)
 		for (j = 0; j < 4 - (i % 4); j++, out++)
 			*out = '=';
-	
+
 	*out = 0;
-	
+
 	return res;
 }
 
-/*
- * gg_base64_decode()
+/**
+ * \internal Dekoduje ci?g znak?w zapisany w base64.
  *
- * dekoduje ci?g znak? base64.
+ * Wynik funkcji nale?y zwolni? za pomoc? \c free.
  *
- *  - buf - ci?g znak?+ * \param buf Bufor ?r?d?owy z danymi do zdekodowania
  *
- * zaalokowany bufor.
+ * \return Zaalokowany bufor ze zdekodowanymi danymi
+ *
+ * \ingroup helper
  */
 char *gg_base64_decode(const char *buf)
 {
@@ -683,7 +675,7 @@ char *gg_base64_decode(const char *buf)
 
 	if (!buf)
 		return NULL;
-	
+
 	save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
 
 	if (!save)
@@ -720,23 +712,24 @@ char *gg_base64_decode(const char *buf)
 		index %= 4;
 	}
 	*res = 0;
-	
+
 	return save;
 }
 
-/*
- * gg_proxy_auth() // funkcja wewn?zna
+/**
+ * \internal Tworzy nag??wek autoryzacji serwera po?rednicz?cego.
  *
- * tworzy nag?? autoryzacji dla proxy.
- * 
- * zaalokowany tekst lub NULL, je?li proxy nie jest w??czone lub nie wymaga
- * autoryzacji.
+ * Dane pobiera ze zmiennych globalnych \c gg_proxy_username i
+ * \c gg_proxy_password.
+ *
+ * \return Zaalokowany bufor z tekstem lub NULL, je?li serwer po?rednicz?cy
+ *         nie jest u?ywany 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;
 
@@ -749,14 +742,14 @@ char *gg_proxy_auth()
 		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);
@@ -764,13 +757,21 @@ char *gg_proxy_auth()
 	return out;
 }
 
+/**
+ * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej.
+ */
 static uint32_t gg_crc32_table[256];
+
+/**
+ * \internal Flaga wype?nienia tablicy pomocniczej do wyznaczania sumy
+ * kontrolnej.
+ */
 static int gg_crc32_initialized = 0;
 
-/*
- * gg_crc32_make_table()  // funkcja wewn?zna
+/**
+ * \internal Tworzy tablic? pomocnicz? do wyznaczania sumy kontrolnej.
  */
-static void gg_crc32_make_table()
+static void gg_crc32_make_table(void)
 {
 	uint32_t h = 1;
 	unsigned int i, j;
@@ -787,16 +788,15 @@ static void gg_crc32_make_table()
 	gg_crc32_initialized = 1;
 }
 
-/*
- * gg_crc32()
+/**
+ * Wyznacza sum? kontroln? CRC32.
  *
- * wyznacza sum?ontroln? CRC32 danego bloku danych.
+ * \param crc Suma kontrola poprzedniego bloku danych lub 0 je?li liczona
+ *            jest suma kontrolna pierwszego bloku
+ * \param buf Bufor danych
+ * \param len D?ugo?? bufora danych
  *
- *  - crc - suma kontrola poprzedniego bloku danych lub 0 je?li pierwszy
- *  - buf - bufor danych
- *  - size - ilo??anych
- *
- * suma kontrolna CRC32.
+ * \return Suma kontrolna.
  */
 uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
 {
@@ -814,7 +814,187 @@ uint32_t gg_crc32(uint32_t crc, const un
 	return crc ^ 0xffffffffL;
 }
 
+/**
+ * \internal Tablica konwersji mi?dzy CP1250 a UTF-8.
+ */
+static const uint16_t table_cp1250[] = {
+	0x20ac, '?',    0x201a,    '?', 0x201e, 0x2026, 0x2020, 0x2021, 
+	   '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, 
+	   '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 
+	   '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, 
+	0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, 
+	0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, 
+	0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 
+	0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, 
+	0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, 
+	0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, 
+	0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, 
+	0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, 
+	0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, 
+	0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, 
+	0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, 
+	0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, 
+};
 
+/**
+ * \internal Zamienia tekst kodowany CP1250 na UTF-8.
+ *
+ * \param b Tekst ?r?d?owy w CP1250.
+ *
+ * \return Zaalokowany bufor z tekstem w UTF-8.
+ */
+char *gg_cp_to_utf8(const char *b)
+{
+	unsigned char *buf = (unsigned char *) b;
+	char *newbuf;
+	int newlen = 0;
+	int i, j;
+
+	for (i = 0; buf[i]; i++) {
+		uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
+
+		if (znak < 0x80)	newlen += 1;
+		else if (znak < 0x800)	newlen += 2;
+		else			newlen += 3;
+	}
+
+	if (!(newbuf = malloc(newlen+1))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_cp_to_utf8() not enough memory\n");
+		return NULL;
+	}
+
+	for (i = 0, j = 0; buf[i]; i++) {
+		uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
+		int count;
+
+		if (znak < 0x80)	count = 1;
+		else if (znak < 0x800)	count = 2;
+		else			count = 3;
+
+		switch (count) {
+			case 3: newbuf[j+2] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0x800;
+			case 2: newbuf[j+1] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0xc0;
+			case 1: newbuf[j] = znak;
+		}
+		j += count;
+	}
+	newbuf[j] = '\0';
+
+	return newbuf;
+}
+
+/**
+ * \internal Dekoduje jeden znak UTF-8.
+ *
+ * \note Funkcja nie jest kompletn? implementacj? UTF-8, a wersj? uproszczon?
+ * do potrzeb kodowania CP1250.
+ *
+ * \param s Tekst ?r?d?owy.
+ * \param n D?ugo?? tekstu ?r?d?owego.
+ * \param ch Wska?nik na wynik dekodowania.
+ *
+ * \return D?ugo?? zdekodowanej sekwencji w bajtach lub warto?? mniejsza
+ * od zera w przypadku b??du.
+ */
+static int gg_utf8_helper(unsigned char *s, int n, uint16_t *ch)
+{
+	unsigned char c = s[0];
+
+	if (c < 0x80) {
+		*ch = c;
+		return 1;
+	}
+
+	if (c < 0xc2) 
+		return -1;
+
+	if (c < 0xe0) {
+		if (n < 2)
+			return -2;
+		if (!((s[1] ^ 0x80) < 0x40))
+			return -1;
+		*ch = ((uint16_t) (c & 0x1f) << 6) | (uint16_t) (s[1] ^ 0x80);
+		return 2;
+	} 
+	
+	if (c < 0xf0) {
+		if (n < 3)
+			return -2;
+		if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (c >= 0xe1 || s[1] >= 0xa0)))
+			return -1;
+		*ch = ((uint16_t) (c & 0x0f) << 12) | ((uint16_t) (s[1] ^ 0x80) << 6) | (uint16_t) (s[2] ^ 0x80);
+		return 3;
+	}
+
+	return -1;
+}
+
+/**
+ * \internal Zamienia tekst kodowany UTF-8 na CP1250.
+ *
+ * \param b Tekst ?r?d?owy w UTF-8.
+ *
+ * \return Zaalokowany bufor z tekstem w CP1250.
+ */
+char *gg_utf8_to_cp(const char *b)
+{
+	unsigned char *buf = (unsigned char *) b;
+	char *newbuf;
+	int newlen = 0;
+	int len;
+	int i, j;
+
+	len = strlen(b);
+
+	for (i = 0; i < len; newlen++) {
+		uint16_t discard;
+		int ret;
+		
+		ret = gg_utf8_helper(&buf[i], len - i, &discard);
+
+		if (ret > 0)
+			i += ret;
+		else
+			i++;
+	}
+
+	if (!(newbuf = malloc(newlen+1))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_utf8_to_cp() not enough memory\n");
+		return NULL;
+	}
+
+	for (i = 0, j = 0; buf[i]; j++) {
+		uint16_t znak;
+		int ret, k;
+
+		ret = gg_utf8_helper(&buf[i], len - i, &znak);
+
+		if (ret > 0) {
+			i += ret;
+		} else {
+			znak = '?';
+			i++;
+		}
+
+		if (znak < 0x80) {
+			newbuf[j] = znak;
+			continue;
+		}
+
+		newbuf[j] = '?';
+
+		for (k = 0; k < (sizeof(table_cp1250)/sizeof(table_cp1250[0])); k++) {
+			if (table_cp1250[k] == znak) {
+				newbuf[j] = (0x80 | k);
+				break;
+			}
+		}
+	}
+	newbuf[j] = '\0';
+
+	return newbuf;
+}
+
 /*
  * Local variables:
  * c-indentation-style: k&r
============================================================
--- libpurple/protocols/gg/lib/compat.h	7210f63b2111df73ae502497570837403898c532
+++ libpurple/protocols/gg/lib/compat.h	296e21e553d08f3c02f03d3f68b05fc4e08e519a
@@ -1,8 +1,8 @@
-/* $Id: compat.h 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: compat.h 506 2008-01-14 22:15:05Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka at irc.pl>
- *                          Robert J. Wo?ny <speedy at ziew.org>
+ *                          Robert J. Wo?ny <speedy at 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
@@ -15,10 +15,16 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file compat.h
+ *
+ * \brief Makra zapewniaj?ce kompatybilno?? API na r??nych systemach
+ */
+
 #ifndef __COMPAT_H
 #define __COMPAT_H
 
============================================================
--- libpurple/protocols/gg/lib/dcc.c	867fe790e6f9058a85e11133ccf815b2c159cf1f
+++ libpurple/protocols/gg/lib/dcc.c	c7ac957798f90e5ac8b8ccd1a43ecb9656d8cff1
@@ -1,8 +1,9 @@
-/* $Id: dcc.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: dcc.c 711 2009-04-16 00:52:47Z darkjames $ */
 
 /*
- *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka at irc.pl>
- *                          Tomasz Chili? <chilek at chilan.com>
+ *  (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka at irc.pl>
+ *                          Tomasz Chili?ski <chilek at chilan.com>
+ *                          Adam Wysocki <gophi at ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,15 +16,18 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/**
+ * \file dcc.c
+ *
+ * \brief Obs?uga po??cze? bezpo?rednich do wersji Gadu-Gadu 6.x
+ */
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#ifndef _WIN32
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -31,7 +35,6 @@
 #ifdef sun
 #  include <sys/filio.h>
 #endif
-#endif
 
 #include <ctype.h>
 #include <errno.h>
@@ -43,67 +46,72 @@
 #include <unistd.h>
 
 #include "compat.h"
+#include "libgadu.h"
+
 #ifndef GG_DEBUG_DISABLE
-/*
- * gg_dcc_debug_data() // funkcja wewn?zna
+
+/**
+ * \internal Przekazuje zawarto?? pakietu do odpluskwiania.
  *
- * wy?wietla zrzut pakietu w hexie.
- * 
- *  - prefix - prefiks zrzutu pakietu
- *  - fd - deskryptor gniazda
- *  - buf - bufor z danymi
- *  - size - rozmiar danych
+ * \param prefix Prefiks informacji
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danumi
+ * \param size Rozmiar bufora z danymi
  */
 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
 
-/*
+/**
+ * Wysy?a ??danie zwrotnego po??czenia bezpo?redniego.
+ *
+ * Funkcj? wykorzystuje si?, je?li nie ma mo?liwo?ci po??czenia si? z odbiorc?
+ * pliku lub rozmowy g?osowej. Po otrzymaniu ??dania druga strona spr?buje
+ * nawi?za? zwrotne po??czenie bezpo?rednie z nadawc?.
  * gg_dcc_request()
  *
- * wysy?a informacj? tym, ?e dany klient powinien si? nami po??czy?- * wykorzystywane, kiedy druga strona, kt? chcemy co? wys?a?est za
- * maskarad?.
+ * \param sess Struktura sesji
+ * \param uin Numer odbiorcy
  *
- *  - sess - struktura opisuj?ca sesj?G
- *  - uin - numerek odbiorcy
+ * \return Patrz \c gg_send_message_ctcp()
  *
- * patrz gg_send_message_ctcp().
+ * \ingroup dcc6
  */
 int gg_dcc_request(struct gg_session *sess, uin_t uin)
 {
-	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char *)"\002", 1);
+	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (unsigned char*) "\002", 1);
 }
 
-/* 
- * gg_dcc_fill_filetime()  // funkcja wewn?zna
+/**
+ * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32.
  *
- * zamienia czas w postaci unixowej na windowsowy.
+ * \note Funkcja dzia?a jedynie gdy kompilator obs?uguje typ danych
+ * \c long \c long.
  *
- *  - unix - czas w postaci unixowej
- *  - filetime - czas w postaci windowsowej
+ * \param ut Czas w postaci uniksowej
+ * \param ft Czas w postaci API WIN32
  */
 static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
 {
-#ifdef __GG_LIBGADU_HAVE_LONG_LONG
+#ifdef GG_CONFIG_HAVE_LONG_LONG
 	unsigned long long tmp;
 
 	tmp = ut;
 	tmp += 11644473600LL;
 	tmp *= 10000000LL;
 
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	ft[0] = (uint32_t) tmp;
 	ft[1] = (uint32_t) (tmp >> 32);
 #else
@@ -114,31 +122,33 @@ static void gg_dcc_fill_filetime(uint32_
 #endif
 }
 
-/*
- * gg_dcc_fill_file_info()
+/**
+ * Wype?nia pola struktury \c gg_dcc niezb?dne do wys?ania pliku.
  *
- * wype?nia pola struct gg_dcc niezb?e do wys?ania pliku.
+ * \note Wi?ksz? funkcjonalno?? zapewnia funkcja \c gg_dcc_fill_file_info2().
  *
- *  - d - struktura opisuj?ca po??czenie DCC
- *  - filename - nazwa pliku
+ * \param d Struktura po??czenia
+ * \param filename Nazwa pliku
  *
- * 0, -1.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup dcc6
  */
 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 struktury \c gg_dcc niezb?dne do wys?ania pliku.
  *
- * wype?nia pola struct gg_dcc niezb?e do wys?ania pliku.
+ * \param d Struktura po??czenia
+ * \param filename Nazwa pliku zapisywana w strukturze
+ * \param local_filename Nazwa pliku w lokalnym systemie plik?w
  *
- *  - d - struktura opisuj?ca po??czenie DCC
- *  - filename - nazwa pliku
- *  - local_filename - nazwa na lokalnym systemie plik? * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  *
- * 0, -1.
+ * \ingroup dcc6
  */
 int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename)
 {
@@ -225,25 +235,23 @@ int gg_dcc_fill_file_info2(struct gg_dcc
 			*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((char *)d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
+	strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
 
 	return 0;
 }
 
-/*
- * gg_dcc_transfer() // funkcja wewn?zna
- * 
- * inicjuje proces wymiany pliku z danym klientem.
+/**
+ * \internal Rozpoczyna po??czenie bezpo?rednie 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)
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin W?asny numer
+ * \param peer_uin Numer odbiorcy
+ * \param type Rodzaj po??czenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET)
  *
- * zaalokowana struct gg_dcc lub NULL je?li wyst?pi? b??d.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku b??du
  */
 static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
 {
@@ -251,9 +259,9 @@ static struct gg_dcc *gg_dcc_transfer(ui
 	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;
@@ -284,18 +292,17 @@ static struct gg_dcc *gg_dcc_transfer(ui
 	return d;
 }
 
-/*
- * gg_dcc_get_file()
- * 
- * inicjuje proces odbierania pliku od danego klienta, gdy ten wys?a? do
- * nas ??danie po??czenia.
+/**
+ * Rozpoczyna odbieranie pliku przez zwrotne po??czenie bezpo?rednie.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - w?asny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP nadawcy
+ * \param port Port nadawcy
+ * \param my_uin W?asny numer
+ * \param peer_uin Numer nadawcy
  *
- * zaalokowana struct gg_dcc lub NULL je?li wyst?pi? b??d.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku b??du
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -304,17 +311,17 @@ struct gg_dcc *gg_dcc_get_file(uint32_t 
 	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.
+/**
+ * Rozpoczyna wysy?anie pliku.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - w?asny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin W?asny numer
+ * \param peer_uin Numer odbiorcy
  *
- * zaalokowana struct gg_dcc lub NULL je?li wyst?pi? b??d.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku b??du
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -323,17 +330,17 @@ struct gg_dcc *gg_dcc_send_file(uint32_t
 	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND);
 }
 
-/*
- * gg_dcc_voice_chat()
- * 
- * pr?e nawi?za?o??czenie g?osowe.
+/**
+ * Rozpoczyna po??czenie g?osowe.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - w?asny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin W?asny numer
+ * \param peer_uin Numer odbiorcy
  *
- * zaalokowana struct gg_dcc lub NULL je?li wyst?pi? b??d.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku b??du
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -342,30 +349,34 @@ struct gg_dcc *gg_dcc_voice_chat(uint32_
 	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE);
 }
 
-/*
- * gg_dcc_set_type()
+/**
+ * Ustawia typ przychodz?cego po??czenia bezpo?redniego.
  *
- * po zdarzeniu GG_EVENT_DCC_CALLBACK nale?y ustawi?yp po??czenia za
- * pomoc? tej funkcji.
+ * Funkcj? nale?y wywo?a? po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK.
  *
- *  - d - struktura opisuj?ca po??czenie
- *  - type - typ po??czenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE)
+ * \param d Struktura po??czenia
+ * \param type Rodzaj po??czenia (\c GG_SESSION_DCC_SEND lub
+ *             \c GG_SESSION_DCC_VOICE)
+ *
+ * \ingroup dcc6
  */
 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?zna
+
+/**
+ * \internal Funkcja zwrotna po??czenia bezpo?redniego.
  *
- * wywo?ywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza
- * rezultat w struct gg_dcc->event.
+ * Pole \c callback struktury \c gg_dcc zawiera wska?nik do tej funkcji.
+ * Wywo?uje ona \c gg_watch_fd() i zachowuje wynik w polu \c event.
  *
- *  - d - structura opisuj?ca po??czenie
+ * \note Funkcjonalno?? funkcjo zwrotnej nie jest ju? wspierana.
  *
- * 0, -1.
+ * \param d Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  */
 static int gg_dcc_callback(struct gg_dcc *d)
 {
@@ -376,25 +387,26 @@ static int gg_dcc_callback(struct gg_dcc
 	return (e != NULL) ? 0 : -1;
 }
 
-/*
- * gg_dcc_socket_create()
+/**
+ * Tworzy gniazdo nas?uchuj?ce dla po??cze? bezpo?rednich.
  *
- * tworzy gniazdo dla bezpo?redniej komunikacji mi?y klientami.
+ * Funkcja przywi?zuje gniazdo do pierwszego wolnego portu TCP.
  *
- *  - uin - w?asny numer
- *  - port - preferowany port, je?li r? 0 lub -1, pr?e domy?lnego
+ * \param uin W?asny numer
+ * \param port Preferowany port (je?li r?wny 0 lub -1, pr?buje si? domy?lnego)
  *
- * zaalokowana struct gg_dcc, kt?po?niej nale?y zwolni?unkcj?
- * gg_dcc_free(), albo NULL je?li wyst?pi? b??d.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku b??du
+ *
+ * \ingroup dcc6
  */
 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;
@@ -408,12 +420,12 @@ struct gg_dcc *gg_dcc_socket_create(uin_
 
 	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;
@@ -433,7 +445,7 @@ struct gg_dcc *gg_dcc_socket_create(uin_
 		errno = errno2;
 		return NULL;
 	}
-	
+
 	gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port);
 
 	if (!(c = malloc(sizeof(*c)))) {
@@ -452,20 +464,20 @@ struct gg_dcc *gg_dcc_socket_create(uin_
 	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 po??czenia g?osowego.
  *
- * wysy?a ramk?anych dla rozmowy g?osowej.
+ * \param d Struktura po??czenia
+ * \param buf Bufor z danymi
+ * \param length D?ugo?? bufora z danymi
  *
- *  - d - struktura opisuj?ca po??czenie dcc
- *  - buf - bufor z danymi
- *  - length - rozmiar ramki
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  *
- * 0, -1.
+ * \ingroup dcc6
  */
 int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
 {
@@ -500,7 +512,14 @@ int gg_dcc_voice_send(struct gg_dcc *d, 
 	return 0;
 }
 
-#define gg_read(fd, buf, size) \
+/**
+ * \internal Odbiera dane z po??czenia bezpo?redniego z obs?ug? b??d?w.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor na dane
+ * \param size Rozmiar bufora na dane
+ */
+#define gg_dcc_read(fd, buf, size) \
 { \
 	int tmp = read(fd, buf, size); \
 	\
@@ -517,9 +536,16 @@ int gg_dcc_voice_send(struct gg_dcc *d, 
 		return e; \
 	} \
 	gg_dcc_debug_data("read", fd, buf, size); \
-} 
+}
 
-#define gg_write(fd, buf, size) \
+/**
+ * \internal Wysy?a dane do po??czenia bezpo?redniego z obs?ug? b??d?w.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danymi
+ * \param size Rozmiar bufora z danymi
+ */
+#define gg_dcc_write(fd, buf, size) \
 { \
 	int tmp; \
 	gg_dcc_debug_data("write", fd, buf, size); \
@@ -536,14 +562,18 @@ int gg_dcc_voice_send(struct gg_dcc *d, 
 	} \
 }
 
-/*
- * gg_dcc_watch_fd()
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
  *
- * funkcja, kt?nale?y wywo?a?gdy co? si?mieni na gg_dcc->fd.
+ * Funkcja zwraca struktur? zdarzenia \c gg_event. Je?li rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzy?o si? jeszcze nic wartego odnotowania.
+ * Struktur? zdarzenia nale?y zwolni? funkcja \c gg_event_free.
  *
- *  - h - struktura zwr?a przez gg_create_dcc_socket()
+ * \param h Struktura po??czenia
  *
- * zaalokowana struct gg_event lub NULL, je?li zabrak?o pami? na ni?.
+ * \return Struktura zdarzenia lub \c NULL je?li wyst?pi? b??d
+ *
+ * \ingroup dcc6
  */
 struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
 {
@@ -551,7 +581,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 	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;
@@ -568,10 +598,9 @@ struct gg_event *gg_dcc_watch_fd(struct 
 	if (h->type == GG_SESSION_DCC_SOCKET) {
 		struct sockaddr_in sin;
 		struct gg_dcc *c;
-		int fd;
-		socklen_t sin_len = sizeof(sin);
-		int one = 1;
-		
+		int fd, one = 1;
+		unsigned int sin_len = sizeof(sin);
+
 		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;
@@ -607,7 +636,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 		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;
 
@@ -617,8 +646,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 		struct gg_dcc_small_packet small;
 		struct gg_dcc_big_packet big;
 		int size, tmp, res;
-		socklen_t res_size = sizeof(res);
-		unsigned int utmp;
+		unsigned int utmp, res_size = sizeof(res);
 		char buf[1024], ack[] = "UDAG";
 
 		struct gg_dcc_file_info_packet {
@@ -634,9 +662,9 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				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));
 
+				gg_dcc_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;
@@ -656,7 +684,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 			case GG_STATE_SENDING_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
 
-				gg_write(h->fd, ack, 4);
+				gg_dcc_write(h->fd, ack, 4);
 
 				h->state = GG_STATE_READING_TYPE;
 				h->check = GG_CHECK_READ;
@@ -666,9 +694,9 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 			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));
 
+				gg_dcc_read(h->fd, &small, sizeof(small));
+
 				small.type = gg_fix32(small.type);
 
 				switch (small.type) {
@@ -680,7 +708,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 						h->timeout = GG_DEFAULT_TIMEOUT;
 
 						e->type = GG_EVENT_DCC_CALLBACK;
-			
+
 						break;
 
 					case 0x0002:	/* XXX */
@@ -703,9 +731,9 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 			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));
 
+				gg_dcc_read(h->fd, &small, sizeof(small));
+
 				small.type = gg_fix32(small.type);
 
 				switch (small.type) {
@@ -715,7 +743,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 						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;
@@ -725,22 +753,22 @@ struct gg_event *gg_dcc_watch_fd(struct 
 						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));
 
+				gg_dcc_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);
 
@@ -749,17 +777,17 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				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));
+				gg_dcc_write(h->fd, &big, sizeof(big));
 
 				h->state = GG_STATE_READING_FILE_HEADER;
 				h->chunk_size = sizeof(big);
@@ -773,25 +801,25 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				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));
+				gg_dcc_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) {
@@ -802,7 +830,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				}
 
 				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)
@@ -823,7 +851,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 					return e;
 				}
 
-				if (h->chunk_size == 0) { 
+				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;
@@ -833,14 +861,14 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				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));
 
+				gg_dcc_read(h->fd, &tiny, sizeof(tiny));
+
 				switch (tiny.type) {
 					case 0x03:	/* XXX */
 						h->state = GG_STATE_READING_VOICE_SIZE;
@@ -850,27 +878,27 @@ struct gg_event *gg_dcc_watch_fd(struct 
 						break;
 					case 0x04:	/* XXX */
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n");
-						/* XXX zwraca?dpowiedni event */
+						/* 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));
 
+				gg_dcc_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;
 				}
 
@@ -879,18 +907,19 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 				if (!(h->voice_buf = malloc(h->chunk_size))) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
+					free(e);
 					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) {
@@ -909,7 +938,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 				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.data = (unsigned char*) h->voice_buf;
 					e->event.dcc_voice_data.length = h->chunk_size;
 					h->state = GG_STATE_READING_VOICE_HEADER;
 					h->voice_buf = NULL;
@@ -917,7 +946,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-				
+
 				return e;
 
 			case GG_STATE_CONNECTING:
@@ -925,7 +954,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				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));
@@ -935,24 +964,24 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				}
 
 				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));
-				
+				gg_dcc_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);
 
+				gg_dcc_read(h->fd, buf, 4);
+
 				if (strncmp(buf, ack, 4)) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
 
@@ -964,29 +993,29 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				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));
 
+				gg_dcc_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));
-				
+
+				gg_dcc_write(h->fd, &small, sizeof(small));
+
 				switch (h->type) {
 					case GG_SESSION_DCC_GET:
 						h->state = GG_STATE_READING_REQUEST;
@@ -1002,7 +1031,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 						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;
@@ -1011,7 +1040,7 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				}
 
 				return e;
-			
+
 			case GG_STATE_SENDING_FILE_INFO:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
 
@@ -1021,35 +1050,35 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				}
 
 				small.type = gg_fix32(0x0001);	/* XXX */
-				
-				gg_write(h->fd, &small, sizeof(small));
 
+				gg_dcc_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?odwracamy z powrotem */
+				/* 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));
 
+				gg_dcc_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?ynik */
+				gg_dcc_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;
@@ -1060,9 +1089,9 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 			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));
 
+				gg_dcc_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;
@@ -1075,14 +1104,14 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				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 */
@@ -1091,22 +1120,22 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 				big.dunno1 = gg_fix32(h->chunk_size);
 				big.dunno2 = 0;
-				
-				gg_write(h->fd, &big, sizeof(big));
 
+				gg_dcc_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? */
@@ -1117,11 +1146,17 @@ struct gg_event *gg_dcc_watch_fd(struct 
 					return e;
 				}
 
+				if (h->offset >= h->file_info.size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\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 */
+				/* b??d */
 				if (size == -1) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
 
@@ -1139,8 +1174,8 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 					return e;
 				}
-				
-				/* je?li wczytali?my wi?j, utnijmy. */
+
+				/* 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;
@@ -1161,15 +1196,22 @@ struct gg_event *gg_dcc_watch_fd(struct 
 					return e;
 				}
 
-				h->offset += size;
-				
+				if (tmp == 0) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (connection reset)\n");
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+					return e;
+				}
+
+				h->offset += tmp;
+
 				if (h->offset >= h->file_info.size) {
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
 				}
-				
-				h->chunk_offset += size;
-				
+
+				h->chunk_offset += tmp;
+
 				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;
@@ -1178,22 +1220,28 @@ struct gg_event *gg_dcc_watch_fd(struct 
 					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);
-				
+
+				if (h->offset >= h->file_info.size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+					e->type = GG_EVENT_DCC_DONE;
+					return e;
+				}
+
 				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 */
+
+				/* b??d */
 				if (size == -1) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
 
@@ -1211,9 +1259,9 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 					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;
@@ -1222,14 +1270,14 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				}
 
 				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;
@@ -1245,11 +1293,11 @@ struct gg_event *gg_dcc_watch_fd(struct 
 					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;
@@ -1258,35 +1306,28 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				return e;
 		}
 	}
-	
+
 	return e;
 }
 
-#undef gg_read
-#undef gg_write
-
-/*
- * gg_dcc_free()
+/**
+ * Zwalnia zasoby u?ywane przez po??czenie bezpo?rednie.
  *
- * zwalnia pami?po strukturze po??czenia dcc.
+ * \param d Struktura po??czenia
  *
- *  - d - zwalniana struktura
+ * \ingroup dcc6
  */
 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->chunk_buf);
 	free(d);
 }
 
============================================================
--- libpurple/protocols/gg/lib/events.c	fb7019f802ba39ab09e97c57282607ffd02580de
+++ libpurple/protocols/gg/lib/events.c	fdb0fc390bc39635a6393ac441b5971c397f94d4
@@ -1,9 +1,10 @@
-/* $Id: events.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: events.c 855 2009-10-12 21:42:51Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka at irc.pl>
- *                          Robert J. Wo?ny <speedy at ziew.org>
- *                          Arkadiusz Mi?kiewicz <arekm at pld-linux.org>
+ *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka at irc.pl>
+ *                          Robert J. Wo?ny <speedy at ziew.org>
+ *                          Arkadiusz Mi?kiewicz <arekm at pld-linux.org>
+ *                          Adam Wysocki <gophi at ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -16,71 +17,74 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/**
+ * \file events.c
+ *
+ * \brief Obs?uga zdarze?
+ */
 
 #include <sys/types.h>
-#ifndef _WIN32
-#include <sys/wait.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "libgadu.h"
+#include "protocol.h"
+#include "libgadu-internal.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 <ctype.h>
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #  include <openssl/err.h>
 #  include <openssl/x509.h>
 #endif
 
-#include "compat.h"
-
-/*
- * gg_event_free()
+/**
+ * Zwalnia pami?? zajmowan? przez informacj? o zdarzeniu.
  *
- * zwalnia pami?zajmowan? przez informacj? zdarzeniu.
+ * Funkcj? nale?y wywo?ywa? za ka?dym razem gdy funkcja biblioteki zwr?ci
+ * struktur? \c gg_event.
  *
- *  - e - wska?nik do informacji o zdarzeniu
+ * \param e Struktura zdarzenia
+ *
+ * \ingroup events
  */
 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;
@@ -89,7 +93,7 @@ void gg_event_free(struct gg_event *e)
 		case GG_EVENT_STATUS60:
 			free(e->event.status60.descr);
 			break;
-	
+
 		case GG_EVENT_STATUS:
 			free(e->event.status.descr);
 			break;
@@ -112,26 +116,30 @@ void gg_event_free(struct gg_event *e)
 		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;
+
+		case GG_EVENT_XML_EVENT:
+			free(e->event.xml_event.data);
+			break;
 	}
 
 	free(e);
 }
 
-/*
- * gg_image_queue_remove()
+/** \cond internal */
+
+/**
+ * \internal Usuwa obrazek z kolejki do wys?ania.
  *
- * usuwa z kolejki dany wpis.
+ * \param s Struktura sesji
+ * \param q Struktura obrazka
+ * \param freeq Flaga zwolnienia elementu kolejki
  *
- *  - s - sesja
- *  - q - kolejka
- *  - freeq - czy zwolni?olejk? *
- * 0/-1
+ * \return 0 je?li si? powiod?o, -1 je?li wyst?pi? b??d
  */
 int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
 {
@@ -162,13 +170,14 @@ int gg_image_queue_remove(struct gg_sess
 	return 0;
 }
 
-/*
- * gg_image_queue_parse() // funkcja wewn?zna
+/**
+ * \internal Analizuje przychodz?cy pakiet z obrazkiem.
  *
- * parsuje przychodz?cy pakiet z obrazkiem.
- *
- *  - e - opis zdarzenia
- *  - 
+ * \param e Struktura zdarzenia
+ * \param p Bufor z danymi
+ * \param len D?ugo?? bufora
+ * \param sess Struktura sesji
+ * \param sender Numer nadawcy
  */
 static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender)
 {
@@ -180,8 +189,8 @@ static void gg_image_queue_parse(struct 
 		return;
 	}
 
-	/* znajd? dany obrazek w kolejce danej sesji */
-	
+	/* 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;
@@ -190,34 +199,23 @@ static void gg_image_queue_parse(struct 
 	}
 
 	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);
+		gg_debug_session(sess, 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) {
-		unsigned 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?ny \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);
+		if (memchr(p, 0, len) == NULL) {
+			gg_debug_session(sess, 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");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
 			return;
 		}
 
@@ -230,11 +228,11 @@ static void gg_image_queue_parse(struct 
 
 	if (q->done + len > q->size)
 		len = q->size - q->done;
-		
+
 	memcpy(q->image + q->done, p, len);
 	q->done += len;
 
-	/* je?li sko?no odbiera?brazek, wygeneruj zdarzenie */
+	/* je?li sko?czono odbiera? obrazek, wygeneruj zdarzenie */
 
 	if (q->done >= q->size) {
 		e->type = GG_EVENT_IMAGE_REPLY;
@@ -250,78 +248,55 @@ static void gg_image_queue_parse(struct 
 	}
 }
 
-/*
- * gg_handle_recv_msg() // funkcja wewn?zna
+/**
+ * \internal Analizuje informacje rozszerzone wiadomo?ci.
+ * 
+ * \param sess Struktura sesji.
+ * \param e Struktura zdarzenia.
+ * \param sender Numer nadawcy.
+ * \param p Wska?nik na dane rozszerzone.
+ * \param packet_end Wska?nik na koniec pakietu.
  *
- * obs?uguje pakiet z przychodz?c? wiadomo?ci?, rozbijaj?c go na dodatkowe
- * struktury (konferencje, kolorki) w razie potrzeby.
- *
- *  - h - nag?? pakietu
- *  - e - opis zdarzenia
- *
- * 0, -1.
+ * \return 0 je?li si? powiod?o, -1 je?li wiadomo?? obs?u?ono i wynik ma
+ * zosta? przekazany aplikacji, -2 je?li wyst?pi? b??d og?lny, -3 je?li
+ * wiadomo?? jest niepoprawna.
  */
-static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end)
 {
-	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");
+					gg_debug_session(sess, 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");
+					gg_debug_session(sess, 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");
+					gg_debug_session(sess, 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;
 			}
 
@@ -329,9 +304,9 @@ static int gg_handle_recv_msg(struct gg_
 			{
 				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");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
 					goto malformed;
 				}
 
@@ -339,18 +314,18 @@ static int gg_handle_recv_msg(struct gg_
 				len = gg_fix16(len);
 
 				if (!(buf = malloc(len))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
+					gg_debug_session(sess, 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");
+					gg_debug_session(sess, 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;
@@ -366,17 +341,17 @@ static int gg_handle_recv_msg(struct gg_
 				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");
+					gg_debug_session(sess, 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.sender = 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;
+				goto handled;
 			}
 
 			case 0x05:		/* image_reply */
@@ -386,75 +361,347 @@ static int gg_handle_recv_msg(struct gg_
 
 				if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
 
-					/* pusta odpowied? - klient po drugiej stronie nie ma ??danego obrazka */
+					/* 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.sender = 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;
+					goto handled;
 
 				} 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");
+					gg_debug_session(sess, 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));
+				gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender);
 
-				return 0;
+				goto handled;
 			}
 
 			default:
 			{
-				gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
 				p = packet_end;
 			}
 		}
 	}
 
+	return 0;
+
+handled:
+	return -1;
+
+fail:
+	return -2;
+
+malformed:
+	return -3;
+}
+
+/**
+ * \internal Analizuje przychodz?cy pakiet z wiadomo?ci?.
+ *
+ * Rozbija pakiet na poszczeg?lne sk?adniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wska?nik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+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_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
+
+	if (!r->seq && !r->msgclass) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
+		e->type = GG_EVENT_NONE;
+		return 0;
+	}
+
+	/* znajd? \0 */
+	for (p = (char*) r + sizeof(*r); ; p++) {
+		if (p >= packet_end) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
+			goto malformed;
+		}
+
+		if (*p == 0x02 && p == packet_end - 1) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
+			break;
+		}
+
+		if (!*p)
+			break;
+	}
+
+	p++;
+
+	switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) {
+		case -1:	// handled
+			return 0;
+
+		case -2:	// failed
+			goto fail;
+
+		case -3:	// malformed
+			goto malformed;
+	}
+
 	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 = (unsigned char *)strdup((char*) r + sizeof(*r));
+	e->event.msg.seq = gg_fix32(r->seq);
+	e->event.msg.message = (unsigned char*) strdup((char*) r + sizeof(*r));
 
 	return 0;
 
 malformed:
 	e->type = GG_EVENT_NONE;
+	free(e->event.msg.message);
+	free(e->event.msg.recipients);
+	free(e->event.msg.formats);
 
+	return 0;
+
+fail:
+	free(e->event.msg.message);
 	free(e->event.msg.recipients);
 	free(e->event.msg.formats);
+	return -1;
+}
 
+/**
+ * \internal Zamienia tekst w formacie HTML na czysty tekst.
+ *
+ * \param dst Bufor wynikowy (mo?e by? \c NULL)
+ * \param html Tekst ?r?d?owy
+ *
+ * \note Dokleja \c \\0 na ko?cu bufora wynikowego.
+ *
+ * \return D?ugo?? tekstu wynikowego bez \c \\0 (nawet je?li \c dst to \c NULL).
+ */
+static int gg_convert_from_html(char *dst, const char *html)
+{
+	const char *src, *entity, *tag;
+	int len, in_tag, in_entity;
+
+	len = 0;
+	in_tag = 0;
+	tag = NULL;
+	in_entity = 0;
+	entity = NULL;
+
+	for (src = html; *src != 0; src++) {
+		if (*src == '<') {
+			tag = src;
+			in_tag = 1;
+			continue;
+		}
+
+		if (in_tag && (*src == '>')) {
+			if (strncmp(tag, "<br", 3) == 0) {
+				if (dst != NULL)
+					dst[len] = '\n';
+				len++;
+			}
+			in_tag = 0;
+			continue;
+		}
+
+		if (in_tag)
+			continue;
+
+		if (*src == '&') {
+			in_entity = 1;
+			entity = src;
+			continue;
+		}
+
+		if (in_entity && *src == ';') {
+			in_entity = 0;
+			if (dst != NULL) {
+				if (strncmp(entity, "&lt;", 4) == 0)
+					dst[len] = '<';
+				else if (strncmp(entity, "&gt;", 4) == 0)
+					dst[len] = '>';
+				else if (strncmp(entity, "&quot;", 6) == 0)
+					dst[len] = '"';
+				else if (strncmp(entity, "&apos;", 6) == 0)
+					dst[len] = '\'';
+				else if (strncmp(entity, "&amp;", 5) == 0)
+					dst[len] = '&';
+				else
+					dst[len] = '?';
+			}
+			len++;
+			continue;
+		}
+
+		if (in_entity && !(isalnum(*src) || *src == '#'))
+			in_entity = 0;
+
+		if (in_entity)
+			continue;
+
+		if (dst != NULL)
+			dst[len] = *src;
+
+		len++;
+	}
+
+	if (dst != NULL)
+		dst[len] = 0;
+	
+	return len;
+}
+
+/**
+ * \internal Analizuje przychodz?cy pakiet z wiadomo?ci? protoko?u Gadu-Gadu 8.0.
+ *
+ * Rozbija pakiet na poszczeg?lne sk?adniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wska?nik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
+static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+	char *packet = (char*) h + sizeof(struct gg_header);
+	struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e);
+
+	if (!r->seq && !r->msgclass) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n");
+		goto malformed;
+	}
+
+	offset_plain = gg_fix32(r->offset_plain);
+	offset_attr  = gg_fix32(r->offset_attr);
+
+	if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n");
+		goto malformed;
+	}
+
+	if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n");
+		offset_attr = 0;	/* nie parsuj attr. */
+		/* goto ignore; */
+	}
+
+	/* Normalna sytuacja, wi?c nie podpada pod powy?szy warunek. */
+	if (offset_attr == h->length)
+		offset_attr = 0;
+
+	if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n");
+		goto malformed;
+	}
+
+	if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n");
+		goto malformed;
+	}
+
+	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.seq = gg_fix32(r->seq);
+
+	if (sess->encoding == GG_ENCODING_CP1250) {
+		e->event.msg.message = (unsigned char*) strdup(packet + offset_plain);
+	} else {
+		if (offset_plain > sizeof(struct gg_recv_msg80)) {
+			int len;
+
+			len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80));
+
+			e->event.msg.message = malloc(len + 1);
+
+			if (e->event.msg.message == NULL)
+				goto fail;
+
+			gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80));
+		} else {
+			e->event.msg.message = (unsigned char*) gg_cp_to_utf8(packet + offset_plain);
+		}
+	}
+
+	if (offset_plain > sizeof(struct gg_recv_msg80)) {
+		if (sess->encoding == GG_ENCODING_UTF8)
+			e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80));
+		else
+			e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80));
+	} else {
+		e->event.msg.xhtml_message = NULL;
+	}
+
+	if (offset_attr != 0) {
+		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) {
+			case -1:	// handled
+				return 0;
+
+			case -2:	// failed
+				goto fail;
+
+			case -3:	// malformed
+				goto malformed;
+		}
+	}
+
 	return 0;
 
 fail:
+	free(e->event.msg.message);
+	free(e->event.msg.xhtml_message);
 	free(e->event.msg.recipients);
 	free(e->event.msg.formats);
 	return -1;
+
+malformed:
+	e->type = GG_EVENT_NONE;
+	free(e->event.msg.message);
+	free(e->event.msg.xhtml_message);
+	free(e->event.msg.recipients);
+	free(e->event.msg.formats);
+	return 0;
 }
 
-/*
- * gg_watch_fd_connected() // funkcja wewn?zna
+/**
+ * \internal Odbiera pakiet od serwera.
  *
- * patrzy na gniazdo, odbiera pakiet i wype?nia struktur?darzenia.
+ * Analizuje pakiet i wype?nia struktur? zdarzenia.
  *
- *  - sess - struktura opisuj?ca sesj? *  - e - opis zdarzenia
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
  *
- * 0, -1.
+ * \return 0 je?li si? powiod?o, -1 je?li wyst?pi? b??d
  */
 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);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
 
 	if (!sess) {
 		errno = EFAULT;
@@ -462,76 +709,86 @@ static int gg_watch_fd_connected(struct 
 	}
 
 	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));
+		gg_debug_session(sess, 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_RECV_MSG80:
+		{
+			if (h->length >= sizeof(struct gg_recv_msg80))
+				if (gg_handle_recv_msg80(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");
+			gg_debug_session(sess, 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");
+				gg_debug_session(sess, 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");
+					gg_debug_session(sess, 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);
+				e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version);
 
 				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");
+					gg_debug_session(sess, 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");
+					gg_debug_session(sess, 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);
+					e->event.notify[i].version = gg_fix32(e->event.notify[i].version);
 				}
 			}
 
@@ -542,7 +799,7 @@ static int gg_watch_fd_connected(struct 
 		{
 			struct gg_status *s = (void*) p;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
 
 			if (h->length >= sizeof(*s)) {
 				e->type = GG_EVENT_STATUS;
@@ -564,23 +821,176 @@ static int gg_watch_fd_connected(struct 
 			break;
 		}
 
+		case GG_NOTIFY_REPLY77:
+		case GG_NOTIFY_REPLY80BETA:
+		{
+			struct gg_notify_reply77 *n = (void*) p;
+			unsigned int length = h->length, i = 0;
+
+			gg_debug_session(sess, 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_session(sess, 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_reply77)) {
+				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 & 0x20000000)
+					e->event.notify60[i].version |= GG_HAS_AUDIO7_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_reply77));
+
+					if (sizeof(struct gg_notify_reply77) + descr_len <= length) {
+						char *descr;
+
+						if (!(descr = malloc(descr_len + 1))) {
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							goto fail;
+						}
+
+						memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len);
+						descr[descr_len] = 0;
+
+						if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) {
+							char *cp_descr = gg_utf8_to_cp(descr);
+
+							if (!cp_descr) {
+								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+								free(descr);
+								goto fail;
+							}
+
+							free(descr);
+							descr = cp_descr;
+						}
+
+						e->event.notify60[i].descr = descr;
+
+						/* XXX czas */
+							
+						length -= sizeof(struct gg_notify_reply77) + descr_len + 1;
+						n = (void*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1);
+					} else {
+						length = 0;
+					}
+
+				} else {
+					length -= sizeof(struct gg_notify_reply77);
+					n = (void*) ((char*) n + sizeof(struct gg_notify_reply77));
+				}
+
+				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+					gg_debug_session(sess, 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_STATUS77:
+		case GG_STATUS80BETA:
+		{
+			struct gg_status77 *s = (void*) p;
+			uint32_t uin;
+
+			gg_debug_session(sess, 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 & 0x20000000)
+				e->event.status60.version |= GG_HAS_AUDIO7_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);
+
+				/* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ?
+				 * 	- goto fail; (?)
+				 */
+				if (buf) {
+					memcpy(buf, (char*) p + sizeof(*s), len);
+					buf[len] = 0;
+
+					if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) {
+						char *cp_buf = gg_utf8_to_cp(buf);
+						free(buf);
+						buf = cp_buf;
+					}
+				}
+
+				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_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");
+			gg_debug_session(sess, 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");
+				gg_debug_session(sess, 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;
@@ -602,9 +1012,9 @@ static int gg_watch_fd_connected(struct 
 				if (GG_S_D(n->status)) {
 					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
 
-					if (descr_len < length) {
+					if (sizeof(struct gg_notify_reply60) + 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");
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 							goto fail;
 						}
 
@@ -612,17 +1022,20 @@ static int gg_watch_fd_connected(struct 
 						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 = 0;
 					}
-					
-					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");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					free(e->event.notify60);
 					goto fail;
 				}
@@ -633,13 +1046,13 @@ static int gg_watch_fd_connected(struct 
 
 			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");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
 
 			if (h->length < sizeof(*s))
 				break;
@@ -682,11 +1095,132 @@ static int gg_watch_fd_connected(struct 
 			break;
 		}
 
+		case GG_STATUS80:
+		{
+			struct gg_notify_reply80 *s = (void*) p;
+			uint32_t descr_len;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+			if (h->length < sizeof(*s))
+				break;
+
+			e->type = GG_EVENT_STATUS60;
+			e->event.status60.uin		= gg_fix32(s->uin);
+			e->event.status60.status	= gg_fix32(s->status);
+			e->event.status60.remote_ip	= s->remote_ip;
+			e->event.status60.remote_port	= gg_fix16(s->remote_port);
+			e->event.status60.image_size	= s->image_size;
+			e->event.status60.descr		= NULL;
+			e->event.status60.version	= 0x00;	/* not-supported */
+			e->event.status60.time		= 0;	/* not-supported */
+
+			descr_len = gg_fix32(s->descr_len);
+
+			if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) {
+				char *buf = malloc(descr_len + 1);
+
+				if (buf) {
+					memcpy(buf, (char*) p + sizeof(*s), descr_len);
+					buf[descr_len] = 0;
+
+					if (sess->encoding != GG_ENCODING_UTF8) {
+						char *cp_buf = gg_utf8_to_cp(buf);
+						free(buf);
+						buf = cp_buf;
+					}
+				}
+
+				e->event.status60.descr = buf;
+			}
+			break;
+		}
+
+		case GG_NOTIFY_REPLY80:
+		{
+			struct gg_notify_reply80 *n = (void*) p;
+			unsigned int length = h->length, i = 0;
+
+			gg_debug_session(sess, 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_session(sess, 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_reply80)) {
+				uint32_t descr_len;
+				char *tmp;
+
+				e->event.notify60[i].uin	= gg_fix32(n->uin);
+				e->event.notify60[i].status	= gg_fix32(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].image_size	= n->image_size;
+				e->event.notify60[i].descr	= NULL;
+				e->event.notify60[i].version	= 0x00;	/* not-supported */
+				e->event.notify60[i].time	= 0;	/* not-supported */
+
+				descr_len = gg_fix32(n->descr_len);
+
+				length -= sizeof(struct gg_notify_reply80);
+				n = (void*) ((char*) n + sizeof(struct gg_notify_reply80));
+
+				if (descr_len) {
+					if (length >= descr_len) {
+						/* XXX, GG_S_D(n->status) */
+						char *descr;
+
+						if (!(descr = malloc(descr_len + 1))) {
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							goto fail;
+						}
+
+						memcpy(descr, n, descr_len);
+						descr[descr_len] = 0;
+
+						if (sess->encoding != GG_ENCODING_UTF8) {
+							char *cp_descr = gg_utf8_to_cp(descr);
+
+							if (!cp_descr) {
+								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+								free(descr);
+								goto fail;
+							}
+
+							free(descr);
+							descr = cp_descr;
+						}
+						e->event.notify60[i].descr = descr;
+
+						length -= descr_len;
+						n = (void*) ((char*) n + descr_len);
+					} else
+						length = 0;
+				}
+
+				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+					gg_debug_session(sess, 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_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");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
 
 			if (h->length < sizeof(*s))
 				break;
@@ -699,9 +1233,9 @@ static int gg_watch_fd_connected(struct 
 			break;
 		}
 
-		case GG_PONG: 
+		case GG_PONG:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
 
 			e->type = GG_EVENT_PONG;
 			sess->last_pong = time(NULL);
@@ -711,27 +1245,47 @@ static int gg_watch_fd_connected(struct 
 
 		case GG_DISCONNECTING:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
 			e->type = GG_EVENT_DISCONNECT;
 			break;
 		}
 
+		case GG_DISCONNECT_ACK:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n");
+			e->type = GG_EVENT_DISCONNECT_ACK;
+			break;
+		}
+
+		case GG_XML_EVENT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n");
+			e->type = GG_EVENT_XML_EVENT;
+			if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n");
+				goto fail;
+			}
+			memcpy(e->event.xml_event.data, p, h->length);
+			e->event.xml_event.data[h->length] = 0;
+			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)
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
+			if (gg_pubdir50_handle_reply_sess(sess, 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");
+			gg_debug_session(sess, 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
+			/* 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)
@@ -743,11 +1297,11 @@ static int gg_watch_fd_connected(struct 
 			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);
-				
+
+				gg_debug_session(sess, 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");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
 					free(sess->userlist_reply);
 					sess->userlist_reply = NULL;
 					goto fail;
@@ -769,10 +1323,75 @@ static int gg_watch_fd_connected(struct 
 			break;
 		}
 
+		case GG_DCC7_ID_REPLY:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
+
+			if (h->length < sizeof(struct gg_dcc7_id_reply))
+				break;
+
+			if (gg_dcc7_handle_id(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_ACCEPT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
+
+			if (h->length < sizeof(struct gg_dcc7_accept))
+				break;
+
+			if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_NEW:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
+
+			if (h->length < sizeof(struct gg_dcc7_new))
+				break;
+
+			if (gg_dcc7_handle_new(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_REJECT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
+
+			if (h->length < sizeof(struct gg_dcc7_reject))
+				break;
+
+			if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_INFO:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
+
+			if (h->length < sizeof(struct gg_dcc7_info))
+				break;
+
+			if (gg_dcc7_handle_info(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
 		default:
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
 	}
-	
+
 	free(h);
 	return 0;
 
@@ -781,19 +1400,20 @@ fail:
 	return -1;
 }
 
-/*
- * gg_watch_fd()
+/** \endcond */
+
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze sesji.
  *
- * funkcja, kt?nale?y wywo?a?gdy co? si?tanie z obserwowanym
- * deskryptorem. zwraca klientowi informacj? tym, co si?zieje.
+ * Funkcja zwraca struktur? zdarzenia \c gg_event. Je?li rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzy?o si? jeszcze nic wartego odnotowania.
+ * Struktur? zdarzenia nale?y zwolni? funkcja \c gg_event_free().
  *
- *  - sess - opis sesji
+ * \param sess Struktura sesji
  *
- * wska?nik do struktury gg_event, kt?trzeba zwolni??ej
- * za pomoc? gg_event_free(). jesli rodzaj zdarzenia jest r?
- * GG_EVENT_NONE, nale?y je zignorowa?je?li zwr?o NULL,
- * sta?o si?o? niedobrego -- albo zabrak?o pami? albo zerwa?o
- * po??czenie.
+ * \return Struktura zdarzenia lub \c NULL je?li wyst?pi? b??d
+ *
+ * \ingroup events
  */
 struct gg_event *gg_watch_fd(struct gg_session *sess)
 {
@@ -802,68 +1422,79 @@ struct gg_event *gg_watch_fd(struct gg_s
 	int port = 0;
 	int errno2 = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
-	
+	gg_debug_session(sess, 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");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
 		return NULL;
 	}
 
 	e->type = GG_EVENT_NONE;
 
+	if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
+
+		res = write(sess->fd, sess->send_buf, sess->send_left);
+
+		if (res == -1 && errno != EAGAIN) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno));
+
+			if (sess->state == GG_STATE_READING_REPLY)
+				goto fail_connecting;
+			else
+				goto done;
+		}
+
+		if (res == sess->send_left) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
+			free(sess->send_buf);
+			sess->send_buf = NULL;
+			sess->send_left = 0;
+		} else if (res > 0) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res);
+
+			memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
+			sess->send_left -= res;
+		}
+	}
+
 	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");
+			gg_debug_session(sess, 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");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
 				failed = 1;
 				errno2 = errno;
 			}
-			
+
 			close(sess->fd);
 			sess->fd = -1;
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-			if (sess->resolver) {
-				pthread_cancel(*((pthread_t*) sess->resolver));
-				free(sess->resolver);
-				sess->resolver = NULL;
-			}
-#elif defined _WIN32
-			if (sess->resolver) {
-				HANDLE h = sess->resolver;
-				TerminateThread(h, 0);
-				CloseHandle(h);
-				sess->resolver = NULL;
-			}
-#else
-			waitpid(sess->pid, NULL, 0);
-			sess->pid = -1;
-#endif
+			sess->resolver_cleanup(&sess->resolver, 0);
 
 			if (failed) {
 				errno = errno2;
 				goto fail_resolving;
 			}
 
-			/* je?li jeste?my w resolverze i mamy ustawiony port
-			 * proxy, znaczy, ?e resolvowali?my proxy. zatem
+			/* 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)
+			 * bezpo?redniego po??czenia, je?li hub le?y)
 			 * z resolvera. */
 			if (sess->proxy_addr && sess->proxy_port)
 				port = sess->proxy_port;
@@ -872,21 +1503,27 @@ struct gg_event *gg_watch_fd(struct gg_s
 				port = GG_APPMSG_PORT;
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
-			
-			/* ??czymy si?lbo z hubem, albo z proxy, zale?nie
-			 * od tego, co resolvowali?my. */
+			gg_debug_session(sess, 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?b??d, nie ma sensu pr?a?alej. */
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+				/* je?li w trybie asynchronicznym gg_connect()
+				 * zwr?ci b??d, nie ma sensu pr?bowa? dalej. */
+				gg_debug_session(sess, 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?rzez proxy,
-			 * jest to bezpo?rednie po??czenie, inaczej jest
+			/* 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;
+
+			if (sess->proxy_addr && sess->proxy_port && sess->server_addr) {
+				sess->state = GG_STATE_CONNECTING_GG;
+				sess->soft_timeout = 1;
+			} else
+				sess->state = GG_STATE_CONNECTING_HUB;
+
 			sess->check = GG_CHECK_WRITE;
 			sess->timeout = GG_DEFAULT_TIMEOUT;
 
@@ -897,43 +1534,26 @@ struct gg_event *gg_watch_fd(struct gg_s
 		{
 			char buf[1024], *client, *auth;
 			int res = 0;
-			socklen_t res_size = sizeof(res);
-			const char *host, *appmsg;
+			unsigned int res_size = sizeof(res);
+			const char *host;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
 
-			/* je?li asynchroniczne, sprawdzamy, czy nie wyst?pi?
-			 * przypadkiem jaki? b??d. */
+			/* 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?o??czy? proxy. nawet
-				 * nie pr?emy 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->proxy_addr && sess->proxy_port)
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+				else
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res));
 
-				if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) {
-					/* przy asynchronicznych, gg_connect()
-					 * zwraca -1 przy b??ch socket(),
-					 * ioctl(), braku routingu itd. dlatego
-					 * nawet nie pr?emy 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;
+				goto fail_connecting;
 			}
-			
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
 
+			gg_debug_session(sess, 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");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
 				goto fail_connecting;
 			}
 
@@ -942,41 +1562,43 @@ struct gg_event *gg_watch_fd(struct gg_s
 			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 : "");
+#ifdef GG_CONFIG_HAVE_OPENSSL
+			if (sess->ssl) {
+				snprintf(buf, sizeof(buf) - 1,
+					"GET %s/appsvc/appmsg3.asp?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, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
+			} else
+#endif
+			{
+				snprintf(buf, sizeof(buf) - 1,
+					"GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
+					"Host: " GG_APPMSG_HOST "\r\n"
+					"%s"
+					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+			}
 
-			if (auth)
-				free(auth);
-			
+			free(auth);
 			free(client);
 
-			/* zwolnij pami?po wersji klienta. */
+			/* 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?e, wi?zawsze zmie?ci si?			 * do bufora gniazda. je?li write() zwr?mniej,
-			 * sta?o si?o? z?ego. */
+			gg_debug_session(sess, 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");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
 
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_WRITING;
@@ -999,72 +1621,36 @@ struct gg_event *gg_watch_fd(struct gg_s
 			int port = GG_DEFAULT_PORT;
 			struct in_addr addr;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
 
-			/* czytamy lini? gniazda i obcinamy \r\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");
+			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
 
-				close(sess->fd);
+			/* sprawdzamy, czy wszystko w porz?dku. */
+			if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
+				goto fail_connecting;
+			}
 
-				/* je?li otrzymali?my jakie? dziwne informacje,
-				 * pr?emy si??czy? pomini?em 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?a 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?a 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?ag??. */
+			/* ignorujemy reszt? nag??wka. */
 			while (strcmp(buf, "\r\n") && strcmp(buf, ""))
 				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 
-			/* czytamy pierwsz? lini?anych. */
+			/* 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? zeru,
-			 * oznacza to, ?e mamy wiadomo??ystemow?. */
+
+			/* 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");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
 						break;
 					}
 
@@ -1074,23 +1660,32 @@ struct gg_event *gg_watch_fd(struct gg_s
 						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 = (unsigned char *)sysmsg_buf;
+				e->event.msg.message = (unsigned char*) sysmsg_buf;
 			}
-	
+
 			close(sess->fd);
-	
-			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
 
+			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
+
 			/* analizujemy otrzymane dane. */
 			tmp = buf;
-			
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+			if (!sess->ssl)
+#endif
+			{
+				while (*tmp && *tmp != ' ')
+					tmp++;
+				while (*tmp && *tmp == ' ')
+					tmp++;
+			}
 			while (*tmp && *tmp != ' ')
 				tmp++;
 			while (*tmp && *tmp == ' ')
@@ -1105,36 +1700,43 @@ struct gg_event *gg_watch_fd(struct gg_s
 				port = atoi(tmp + 1);
 			}
 
+			if (!strcmp(host, "notoperating")) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
+				sess->fd = -1;
+				goto fail_unavailable;
+			}
+
 			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? nim. */
+				/* 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));
+					/* nie wysz?o? trudno. */
+					gg_debug_session(sess, 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;
+				sess->soft_timeout = 1;
 				break;
 			}
 
 			sess->port = port;
 
-			/* ??czymy si? w?a?ciwym serwerem. */
+			/* ??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));
+				gg_debug_session(sess, 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?emy portu 443. */
+				/* nie wysz?o? pr?bujemy portu 443. */
 				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
-					/* ostatnia deska ratunku zawiod?a?
+					/* 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));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 					goto fail_connecting;
 				}
 			}
@@ -1142,23 +1744,26 @@ struct gg_event *gg_watch_fd(struct gg_s
 			sess->state = GG_STATE_CONNECTING_GG;
 			sess->check = GG_CHECK_WRITE;
 			sess->timeout = GG_DEFAULT_TIMEOUT;
-		
+			sess->soft_timeout = 1;
+
 			break;
 		}
 
 		case GG_STATE_CONNECTING_GG:
 		{
 			int res = 0;
-			socklen_t res_size = sizeof(res);
+			unsigned int res_size = sizeof(res);
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
 
-			/* je?li wyst?pi? b??d podczas ??czenia si?. */
+			sess->soft_timeout = 0;
+
+			/* 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?o??czenie z proxy,
-				 * nie mamy czego pr?a?i?j. */
+				/* 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));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
 					goto fail_connecting;
 				}
 
@@ -1170,36 +1775,46 @@ struct gg_event *gg_watch_fd(struct gg_s
 					errno = ETIMEDOUT;
 #endif
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-				/* je?li logujemy si?o TLS, nie pr?emy
-				 * si??czy?u? z niczym innym w przypadku
-				 * b??. nie do???e nie ma sensu, to i
-				 * trzeba by si?awi? tworzenie na nowo
+#ifdef GG_CONFIG_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));
+					gg_debug_session(sess, 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));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
 
+				if (sess->port == GG_HTTPS_PORT)
+					goto fail_connecting;
+
 				sess->port = GG_HTTPS_PORT;
 
-				/* pr?emy na port 443. */
+				/* 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));
+					gg_debug_session(sess, 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;
+				sess->soft_timeout = 1;
+
+				break;
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
-			
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+
 			if (gg_proxy_http_only)
 				sess->proxy_port = 0;
 
-			/* je?li mamy proxy, wy?lijmy zapytanie. */
+			/* 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;
@@ -1211,20 +1826,22 @@ struct gg_event *gg_watch_fd(struct gg_s
 
 				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?e,
-				 * ?e musi si?mie?ci? buforze gniazda. je?li
-				 * write() zawiedzie, sta?o si?o? z?ego. */
+				gg_debug_session(sess, 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");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					free(auth);
 					goto fail_connecting;
 				}
 
 				if (auth) {
-					gg_debug(GG_DEBUG_MISC, "//   %s", auth);
+					gg_debug_session(sess, 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");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+						free(auth);
 						goto fail_connecting;
 					}
 
@@ -1232,12 +1849,12 @@ struct gg_event *gg_watch_fd(struct gg_s
 				}
 
 				if (write(sess->fd, "\r\n", 2) < 2) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
 					goto fail_connecting;
 				}
 			}
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 			if (sess->ssl) {
 				SSL_set_fd(sess->ssl, sess->fd);
 
@@ -1256,19 +1873,19 @@ struct gg_event *gg_watch_fd(struct gg_s
 			break;
 		}
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 		case GG_STATE_TLS_NEGOTIATION:
 		{
 			int res;
 			X509 *peer;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+			gg_debug_session(sess, 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");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
 
 					e->type = GG_EVENT_CONN_FAILED;
 					e->event.failure = GG_FAILURE_TLS;
@@ -1277,9 +1894,9 @@ struct gg_event *gg_watch_fd(struct gg_s
 					sess->fd = -1;
 					break;
 				}
-				
+
 				if (err == SSL_ERROR_WANT_READ) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
 
 					sess->state = GG_STATE_TLS_NEGOTIATION;
 					sess->check = GG_CHECK_READ;
@@ -1287,7 +1904,7 @@ struct gg_event *gg_watch_fd(struct gg_s
 
 					break;
 				} else if (err == SSL_ERROR_WANT_WRITE) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
 
 					sess->state = GG_STATE_TLS_NEGOTIATION;
 					sess->check = GG_CHECK_WRITE;
@@ -1299,8 +1916,8 @@ struct gg_event *gg_watch_fd(struct gg_s
 
 					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);
- 
+					gg_debug_session(sess, 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;
@@ -1310,20 +1927,20 @@ struct gg_event *gg_watch_fd(struct gg_s
 				}
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl));
+			gg_debug_session(sess, 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");
+				gg_debug_session(sess, 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);
+				gg_debug_session(sess, 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);
+				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
 			}
 
 			sess->state = GG_STATE_READING_KEY;
@@ -1336,46 +1953,49 @@ struct gg_event *gg_watch_fd(struct gg_s
 
 		case GG_STATE_READING_KEY:
 		{
-			struct gg_header *h;			
+			struct gg_header *h;
 			struct gg_welcome *w;
-			struct gg_login60 l;
-			unsigned int hash;
-			char *password = sess->password;
+			unsigned char *password = (unsigned char*) sess->password;
 			int ret;
-			
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
+			uint8_t login_hash[64];
 
-			memset(&l, 0, sizeof(l));
-			l.dunno2 = 0xbe;
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
 
-			/* XXX bardzo, bardzo, bardzo g?upi pomys? na pozbycie
-			 * si?ekstu wrzucanego przez proxy. */
+			memset(login_hash, 0, sizeof(login_hash));
+
+			/* 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);
-				
+				gg_debug_session(sess, 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);
+						gg_debug_session(sess, GG_DEBUG_MISC, "//   %s\n", buf);
 				}
 
 				/* XXX niech czeka jeszcze raz w tej samej
-				 * fazie. g?upio, ale dzia?a. */
+				 * 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));
+				if (errno == EAGAIN) {
+					sess->check = GG_CHECK_READ;
+					break;
+				}
 
+				gg_debug_session(sess, 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;
@@ -1387,7 +2007,7 @@ struct gg_event *gg_watch_fd(struct gg_s
 			}
 
 			if (h->type != GG_WELCOME) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
 				free(h);
 				close(sess->fd);
 				sess->fd = -1;
@@ -1397,61 +2017,127 @@ struct gg_event *gg_watch_fd(struct gg_s
 				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((unsigned char *)password, w->key);
-	
-			gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash);
-	
-			free(h);
+			switch (sess->hash_type) {
+				case GG_LOGIN_HASH_GG32:
+				{
+					unsigned int hash;
 
+					hash = gg_fix32(gg_login_hash(password, w->key));
+					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash);
+					memcpy(login_hash, &hash, sizeof(hash));
+
+					break;
+				}
+
+				case GG_LOGIN_HASH_SHA1:
+				{
+					char tmp[41];
+					int i;
+
+					gg_login_hash_sha1((char*) password, w->key, login_hash);
+					for (i = 0; i < 40; i += 2)
+						snprintf(tmp + i, sizeof(tmp) - i, "%02x", login_hash[i / 2]);
+
+					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp);
+
+					break;
+				}
+			}
+
+			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;
-				socklen_t sin_len = sizeof(sin);
+				unsigned int sin_len = sizeof(sin);
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
+				gg_debug_session(sess, 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;
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
+					sess->client_addr = sin.sin_addr.s_addr;
 				} else {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
-					l.local_ip = 0;
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
+					sess->client_addr = 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);
-			}
+			} else
+				sess->client_addr = gg_dcc_ip;
 
-			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);
+			if (sess->protocol_version >= 0x2e) {
+				struct gg_login80 l;
 
+				uint32_t tmp_version_len	= gg_fix32(strlen(GG8_VERSION));
+				uint32_t tmp_descr_len		= gg_fix32((sess->initial_descr) ? strlen(sess->initial_descr) : 0);
+				
+				memset(&l, 0, sizeof(l));
+				l.uin           = gg_fix32(sess->uin);
+				memcpy(l.language, GG8_LANG, sizeof(l.language));
+				l.hash_type     = sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.status        = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.flags		= gg_fix32(0x00800001);
+				l.features	= gg_fix32(sess->protocol_features);
+				l.image_size    = sess->image_size;
+				l.dunno2        = 0x64;
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN80, 
+						&l, sizeof(l), 
+						&tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION),
+						&tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+						NULL);
+
+			} else if (sess->protocol_version == 0x2d) {
+				struct gg_login70 l;
+
+				memset(&l, 0, sizeof(l));
+				l.uin		= gg_fix32(sess->uin);
+				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr;
+				l.local_port	= gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
+				l.hash_type	= sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.image_size	= sess->image_size;
+				l.dunno2 	= 0x64;
+				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags);
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN80BETA,
+						&l, sizeof(l),
+						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+						(sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0,
+						NULL);
+			} else {
+				struct gg_login70 l;
+
+				memset(&l, 0, sizeof(l));
+				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr;
+				l.uin		= gg_fix32(sess->uin);
+				l.local_port	= gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
+				l.hash_type	= sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.image_size	= sess->image_size;
+				l.dunno2	= 0xbe;
+				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags);
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN70,
+						&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));
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
 				errno2 = errno;
 				close(sess->fd);
 				errno = errno2;
@@ -1461,8 +2147,9 @@ struct gg_event *gg_watch_fd(struct gg_s
 				sess->state = GG_STATE_IDLE;
 				break;
 			}
-	
+
 			sess->state = GG_STATE_READING_REPLY;
+			sess->check = GG_CHECK_READ;
 
 			break;
 		}
@@ -1471,10 +2158,15 @@ struct gg_event *gg_watch_fd(struct gg_s
 		{
 			struct gg_header *h;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
+			gg_debug_session(sess, 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));
+				if (errno == EAGAIN) {
+					sess->check = GG_CHECK_READ;
+					break;
+				}
+
+				gg_debug_session(sess, 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;
@@ -1484,11 +2176,12 @@ struct gg_event *gg_watch_fd(struct gg_s
 				sess->fd = -1;
 				break;
 			}
-	
-			if (h->type == GG_LOGIN_OK) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
+
+			if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
 				e->type = GG_EVENT_CONN_SUCCESS;
 				sess->state = GG_STATE_CONNECTED;
+				sess->check = GG_CHECK_READ;
 				sess->timeout = -1;
 				sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL;
 				free(h);
@@ -1496,15 +2189,15 @@ struct gg_event *gg_watch_fd(struct gg_s
 			}
 
 			if (h->type == GG_LOGIN_FAILED) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
+				gg_debug_session(sess, 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;
+			} else if (h->type == GG_DISCONNECTING) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
+				e->event.failure = GG_FAILURE_INTRUDER;
 				errno = EACCES;
 			} else {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
 				e->event.failure = GG_FAILURE_INVALID;
 				errno = EINVAL;
 			}
@@ -1522,13 +2215,13 @@ struct gg_event *gg_watch_fd(struct gg_s
 
 		case GG_STATE_CONNECTED:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
+			gg_debug_session(sess, 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));
+				gg_debug_session(sess, 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;
@@ -1536,6 +2229,9 @@ struct gg_event *gg_watch_fd(struct gg_s
 				} else
 					res = -1;
 			}
+
+			sess->check = GG_CHECK_READ;
+
 			break;
 		}
 	}
@@ -1544,10 +2240,13 @@ done:
 	if (res == -1) {
 		free(e);
 		e = NULL;
+	} else {
+		if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED))
+			sess->check |= GG_CHECK_WRITE;
 	}
 
 	return e;
-	
+
 fail_connecting:
 	if (sess->fd != -1) {
 		errno2 = errno;
@@ -1565,6 +2264,12 @@ fail_resolving:
 	e->event.failure = GG_FAILURE_RESOLVING;
 	sess->state = GG_STATE_IDLE;
 	goto done;
+
+fail_unavailable:
+	e->type = GG_EVENT_CONN_FAILED;
+	e->event.failure = GG_FAILURE_UNAVAILABLE;
+	sess->state = GG_STATE_IDLE;
+	goto done;
 }
 
 /*
============================================================
--- libpurple/protocols/gg/lib/http.c	a7960315a3525e78bc4ad6a8f8b4b2b1e39ff9cb
+++ libpurple/protocols/gg/lib/http.c	7846a9ed5275969a45c31e2ccdabae2ea9e29569
@@ -1,4 +1,4 @@
-/* $Id: http.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: http.c 833 2009-10-01 20:48:01Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka at irc.pl>
@@ -14,52 +14,60 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/**
+ * \file http.c
+ *
+ * \brief Obs?uga po??cze? HTTP
+ */
 
 #include <sys/types.h>
-#ifndef _WIN32
-#include <sys/wait.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "libgadu.h"
+#include "resolver.h"
 
 #include <ctype.h>
 #include <errno.h>
-#ifndef _WIN32
 #include <netdb.h>
-#endif
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-#  include <pthread.h>
-#endif
+#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "compat.h"
-
-/*
- * gg_http_connect() // funkcja pomocnicza
+/**
+ * Rozpoczyna po??czenie HTTP.
  *
- * rozpoczyna po??czenie po http.
+ * Funkcja przeprowadza po??czenie HTTP przy po??czeniu synchronicznym,
+ * zwracaj?c wynik w polach struktury \c gg_http, lub b??d, gdy sesja si?
+ * nie powiedzie.
  *
- *  - hostname - adres serwera
- *  - port - port serwera
- *  - async - asynchroniczne po??czenie
- *  - method - metoda http (GET, POST, cokolwiek)
- *  - path - ?cie?ka do zasobu (musi by?oprzedzona ,,/'')
- *  - header - nag?? zapytania plus ewentualne dane dla POST
+ * Przy po??czeniu asynchronicznym, funkcja rozpoczyna po??czenie, a dalsze
+ * etapy b?d? przeprowadzane po wykryciu zmian (\c watch) na obserwowanym
+ * deskryptorze (\c fd) i wywo?aniu funkcji \c gg_http_watch_fd().
  *
- * zaalokowana struct gg_http, kt?po?niej nale?y
- * zwolni?unkcj? gg_http_free(), albo NULL je?li wyst?pi? b??d.
+ * Po zako?czeniu, nale?y zwolni? struktur? za pomoc? funkcji
+ * \c gg_http_free(). Po??czenie asynchroniczne mo?na zatrzyma? w ka?dej
+ * chwili za pomoc? \c gg_http_stop().
+ *
+ * \param hostname Adres serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego po??czenia
+ * \param method Metoda HTTP
+ * \param path ?cie?ka do zasobu (musi by? poprzedzona znakiem '/')
+ * \param header Nag??wek zapytania plus ewentualne dane dla POST
+ *
+ * \return Zaalokowana struktura \c gg_http lub NULL, je?li wyst?pi? b??d.
+ *
+ * \ingroup http
  */
 struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header)
 {
@@ -70,7 +78,7 @@ struct gg_http *gg_http_connect(const ch
 		errno = EFAULT;
 		return NULL;
 	}
-	
+
 	if (!(h = malloc(sizeof(*h))))
 		return NULL;
 	memset(h, 0, sizeof(*h));
@@ -80,6 +88,8 @@ struct gg_http *gg_http_connect(const ch
 	h->fd = -1;
 	h->type = GG_SESSION_HTTP;
 
+	gg_http_set_resolver(h, GG_RESOLVER_DEFAULT);
+
 	if (gg_proxy_enabled) {
 		char *auth = gg_proxy_auth();
 
@@ -88,9 +98,8 @@ struct gg_http *gg_http_connect(const ch
 				"", header);
 		hostname = gg_proxy_host;
 		h->port = port = gg_proxy_port;
+		free(auth);
 
-		if (auth)
-			free(auth);
 	} else {
 		h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
 				method, path, header);
@@ -102,17 +111,11 @@ struct gg_http *gg_http_connect(const ch
 		errno = ENOMEM;
 		return NULL;
 	}
-	
+
 	gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
 
 	if (async) {
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
-#elif defined _WIN32
-		if (gg_resolve_win32thread(&h->fd, &h->resolver, hostname)) {
-#else
-		if (gg_resolve(&h->fd, &h->pid, hostname)) {
-#endif
+		if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
 			gg_http_free(h);
 			errno = ENOENT;
@@ -125,19 +128,16 @@ struct gg_http *gg_http_connect(const ch
 		h->check = GG_CHECK_READ;
 		h->timeout = GG_DEFAULT_TIMEOUT;
 	} else {
-		struct in_addr *hn, a;
+		struct in_addr addr;
 
-		if (!(hn = gg_gethostbyname(hostname))) {
+		if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
 			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) {
+		if ((h->fd = gg_connect(&addr, 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;
@@ -159,10 +159,12 @@ struct gg_http *gg_http_connect(const ch
 
 	h->callback = gg_http_watch_fd;
 	h->destroy = gg_http_free;
-	
+
 	return h;
 }
 
+#ifndef DOXYGEN
+
 #define gg_http_error(x) \
 	close(h->fd); \
 	h->fd = -1; \
@@ -170,17 +172,22 @@ struct gg_http *gg_http_connect(const ch
 	h->error = x; \
 	return 0;
 
-/*
- * gg_http_watch_fd()
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
  *
- * przy asynchronicznej obs?udze HTTP funkcj?? nale?y wywo?a?je?li
- * zmieni?o si?o? na obserwowanym deskryptorze.
+ * Operacja b?dzie zako?czona, gdy pole \c state b?dzie r?wne
+ * \c GG_STATE_PARSING. W tym miejscu dzia?anie przejmuje zwykle funkcja
+ * korzystaj?ca z \c gg_http_watch_fd(). W przypadku b??du po??czenia,
+ * pole \c state b?dzie r?wne \c GG_STATE_ERROR, a kod b??du znajdzie si?
+ * w polu \c error.
  *
- *  - h - struktura opisuj?ca po??czenie
+ * \param h Struktura po??czenia
  *
- * je?li wszystko posz?o dobrze to 0, inaczej -1. po??czenie b?ie
- * zako?ne, je?li h->state == GG_STATE_PARSING. je?li wyst?pi jaki?
- * b??d, to b?ie tam GG_STATE_ERROR i odpowiedni kod b?? w h->error.
+ * \return \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup http
  */
 int gg_http_watch_fd(struct gg_http *h)
 {
@@ -205,22 +212,7 @@ int gg_http_watch_fd(struct gg_http *h)
 		close(h->fd);
 		h->fd = -1;
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (h->resolver) {
-			pthread_cancel(*((pthread_t *) h->resolver));
-			free(h->resolver);
-			h->resolver = NULL;
-		}
-#elif defined _WIN32
-		if (h->resolver) {
-			HANDLE hnd = h->resolver;
-			TerminateThread(hnd, 0);
-			CloseHandle(hnd);
-			h->resolver = NULL;
-		}
-#else
-		waitpid(h->pid, NULL, 0);
-#endif
+		h->resolver_cleanup(&h->resolver, 0);
 
 		gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port);
 
@@ -238,7 +230,7 @@ int gg_http_watch_fd(struct gg_http *h)
 
 	if (h->state == GG_STATE_CONNECTING) {
 		int res = 0;
-		socklen_t res_size = sizeof(res);
+		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));
@@ -257,14 +249,14 @@ int gg_http_watch_fd(struct gg_http *h)
 	}
 
 	if (h->state == GG_STATE_SENDING_QUERY) {
-		ssize_t res;
+		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 < 0 || (size_t)res < strlen(h->query)) {
+		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);
@@ -346,7 +338,7 @@ int gg_http_watch_fd(struct gg_http *h)
 			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) {
@@ -449,7 +441,7 @@ int gg_http_watch_fd(struct gg_http *h)
 
 		return 0;
 	}
-	
+
 	if (h->fd != -1)
 		close(h->fd);
 
@@ -460,14 +452,14 @@ int gg_http_watch_fd(struct gg_http *h)
 	return -1;
 }
 
-#undef gg_http_error
-
-/*
- * gg_http_stop()
+/**
+ * Ko?czy asynchroniczne po??czenie HTTP.
  *
- * je?li po??czenie jest w trakcie, przerywa je. nie zwalnia h->data.
- * 
- *  - h - struktura opisuj?ca po??czenie
+ * Po zatrzymaniu nale?y zwolni? zasoby funkcj? \c gg_http_free().
+ *
+ * \param h Struktura po??czenia
+ *
+ * \ingroup http
  */
 void gg_http_stop(struct gg_http *h)
 {
@@ -477,15 +469,20 @@ void gg_http_stop(struct gg_http *h)
 	if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
 		return;
 
-	if (h->fd != -1)
+	if (h->fd != -1) {
 		close(h->fd);
-	h->fd = -1;
+		h->fd = -1;
+	}
+
+	h->resolver_cleanup(&h->resolver, 1);
 }
 
-/*
- * gg_http_free_fields() // funkcja wewn?zna
+/**
+ * \internal Zwalnia pola struktury \c gg_http.
  *
- * zwalnia pola struct gg_http, ale nie zwalnia samej struktury.
+ * Funkcja zwalnia same pola, nie zwalnia struktury.
+ *
+ * \param h Struktura po??czenia
  */
 void gg_http_free_fields(struct gg_http *h)
 {
@@ -501,19 +498,21 @@ void gg_http_free_fields(struct gg_http 
 		free(h->query);
 		h->query = NULL;
 	}
-	
+
 	if (h->header) {
 		free(h->header);
 		h->header = NULL;
 	}
 }
 
-/*
- * gg_http_free()
+/**
+ * Zwalnia zasoby po po??czeniu HTTP.
  *
- * pr?e zamkn??o??czenie i zwalnia pami?po nim.
+ * Je?li po??czenie nie zosta?o jeszcze zako?czone, jest przerywane.
  *
- *  - h - struktura, kt?nale?y zlikwidowa? * \param h Struktura po??czenia
+ *
+ * \ingroup http
  */
 void gg_http_free(struct gg_http *h)
 {
============================================================
--- libpurple/protocols/gg/lib/libgadu.c	df490de7213ffd531c7033135c3e396d72315d1c
+++ libpurple/protocols/gg/lib/libgadu.c	bd29701f580355495b583cdaff5d7d9837e0bb51
@@ -1,10 +1,11 @@
-/* $Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka at irc.pl>
- *                          Robert J. Wo?ny <speedy at ziew.org>
- *                          Arkadiusz Mi?kiewicz <arekm at pld-linux.org>
- *                          Tomasz Chili? <chilek at chilan.com>
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka at irc.pl>
+ *                          Robert J. Wo?ny <speedy at ziew.org>
+ *                          Arkadiusz Mi?kiewicz <arekm at pld-linux.org>
+ *                          Tomasz Chili?ski <chilek at chilan.com>
+ *                          Adam Wysocki <gophi at ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -17,227 +18,187 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/**
+ * \file libgadu.c
+ *
+ * \brief G??wny modu? biblioteki
+ */
 
 #include <sys/types.h>
-#ifndef _WIN32
-#include <sys/wait.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #ifdef sun
 #  include <sys/filio.h>
 #endif
-#else
-#include <io.h>
-#include <fcntl.h>
-#include <errno.h>
-#define SHUT_RDWR SD_BOTH
-#endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "libgadu.h"
+#include "protocol.h"
+#include "resolver.h"
+#include "libgadu-internal.h"
 
 #include <errno.h>
-#ifndef _WIN32
 #include <netdb.h>
-#endif
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-#  include <pthread.h>
-#endif
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <signal.h>
+#include <time.h>
 #include <unistd.h>
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #  include <openssl/err.h>
 #  include <openssl/rand.h>
 #endif
 
-#include "compat.h"
+#define GG_LIBGADU_VERSION "1.9.0-rc2"
 
+/**
+ * Poziom rejestracji informacji odpluskwiaj?cych. Zmienna jest mask? bitow?
+ * sk?adaj?c? si? ze sta?ych \c GG_DEBUG_...
+ *
+ * \ingroup debug
+ */
 int gg_debug_level = 0;
+
+/**
+ * Funkcja, do kt?rej s? przekazywane informacje odpluskwiaj?ce. Je?li zar?wno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, s? r?wne
+ * \c NULL, informacje s? wysy?ane do standardowego wyj?cia b??du (\c stderr).
+ *
+ * \param level Poziom rejestracji
+ * \param format Format wiadomo?ci (zgodny z \c printf)
+ * \param ap Lista argument?w (zgodna z \c printf)
+ *
+ * \note Funkcja jest przes?aniana przez \c gg_debug_handler_session.
+ *
+ * \ingroup debug
+ */
 void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
 
+/**
+ * Funkcja, do kt?rej s? przekazywane informacje odpluskwiaj?ce. Je?li zar?wno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, s? r?wne
+ * \c NULL, informacje s? wysy?ane do standardowego wyj?cia b??du.
+ *
+ * \param sess Sesja kt?rej dotyczy informacja lub \c NULL
+ * \param level Poziom rejestracji
+ * \param format Format wiadomo?ci (zgodny z \c printf)
+ * \param ap Lista argument?w (zgodna z \c printf)
+ *
+ * \note Funkcja przes?ania przez \c gg_debug_handler_session.
+ *
+ * \ingroup debug
+ */
+void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL;
+
+/**
+ * Port gniazda nas?uchuj?cego dla po??cze? bezpo?rednich.
+ * 
+ * \ingroup ip
+ */
 int gg_dcc_port = 0;
+
+/**
+ * Adres IP gniazda nas?uchuj?cego dla po??cze? bezpo?rednich.
+ *
+ * \ingroup ip
+ */
 unsigned long gg_dcc_ip = 0;
 
+/**
+ * Adres lokalnego interfejsu IP, z kt?rego wywo?ywane s? wszystkie po??czenia.
+ *
+ * \ingroup ip
+ */
 unsigned long gg_local_ip = 0;
-/*
- * zmienne opisuj?ce parametry proxy http.
+
+/**
+ * Flaga w??czenia po??cze? przez serwer po?rednicz?cy.
+ *
+ * \ingroup proxy
  */
-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 16856 2006-08-19 01:13:25Z evands $";
-#endif 
-
-#ifdef _WIN32
 /**
- *  Deal with the fact that you can't select() on a win32 file fd.
- *  This makes it practically impossible to tie into purple's event loop.
+ * Adres serwera po?rednicz?cego.
  *
- *  -This is thanks to Tor Lillqvist.
- *  XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu.
+ * \ingroup proxy
  */
-static int
-socket_pipe (int *fds)
-{
-	SOCKET temp, socket1 = -1, socket2 = -1;
-	struct sockaddr_in saddr;
-	int len;
-	u_long arg;
-	fd_set read_set, write_set;
-	struct timeval tv;
+char *gg_proxy_host = NULL;
 
-	temp = socket(AF_INET, SOCK_STREAM, 0);
+/**
+ * Port serwera po?rednicz?cego.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_port = 0;
 
-	if (temp == INVALID_SOCKET) {
-		goto out0;
-	}
+/**
+ * Flaga u?ywania serwera po?rednicz?cego jedynie dla us?ug HTTP.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_http_only = 0;
 
-	arg = 1;
-	if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out0;
-	}
+/**
+ * Nazwa u?ytkownika do autoryzacji serwera po?rednicz?cego.
+ *
+ * \ingroup proxy
+ */
+char *gg_proxy_username = NULL;
 
-	memset(&saddr, 0, sizeof(saddr));
-	saddr.sin_family = AF_INET;
-	saddr.sin_port = 0;
-	saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+/**
+ * Has?o u?ytkownika do autoryzacji serwera po?rednicz?cego.
+ *
+ * \ingroup proxy
+ */
+char *gg_proxy_password = NULL;
 
-	if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) {
-		goto out0;
-	}
+#ifndef DOXYGEN
 
-	if (listen(temp, 1) == SOCKET_ERROR) {
-		goto out0;
-	}
+#ifndef lint
+static char rcsid[]
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+= "$Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $";
+#endif
 
-	len = sizeof(saddr);
-	if (getsockname(temp, (struct sockaddr *)&saddr, &len)) {
-		goto out0;
-	}
+#endif /* DOXYGEN */
 
-	socket1 = socket(AF_INET, SOCK_STREAM, 0);
-
-	if (socket1 == INVALID_SOCKET) {
-		goto out0;
-	}
-
-	arg = 1;
-	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out1;
-	}
-
-	if (connect(socket1, (struct sockaddr  *)&saddr, len) != SOCKET_ERROR ||
-			WSAGetLastError() != WSAEWOULDBLOCK) {
-		goto out1;
-	}
-
-	FD_ZERO(&read_set);
-	FD_SET(temp, &read_set);
-
-	tv.tv_sec = 0;
-	tv.tv_usec = 0;
-
-	if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) {
-		goto out1;
-	}
-
-	if (!FD_ISSET(temp, &read_set)) {
-		goto out1;
-	}
-
-	socket2 = accept(temp, (struct sockaddr *) &saddr, &len);
-	if (socket2 == INVALID_SOCKET) {
-		goto out1;
-	}
-
-	FD_ZERO(&write_set);
-	FD_SET(socket1, &write_set);
-
-	tv.tv_sec = 0;
-	tv.tv_usec = 0;
-
-	if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	if (!FD_ISSET(socket1, &write_set)) {
-		goto out2;
-	}
-
-	arg = 0;
-	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	arg = 0;
-	if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	fds[0] = socket1;
-	fds[1] = socket2;
-
-	closesocket (temp);
-
-	return 0;
-
-out2:
-	closesocket (socket2);
-out1:
-	closesocket (socket1);
-out0:
-	closesocket (temp);
-	errno = EIO;            /* XXX */
-
-	return -1;
-}
-#endif
-
-/*
- * gg_libgadu_version()
+/**
+ * Zwraca wersj? biblioteki.
  *
- * zwraca wersj?ibgadu.
+ * \return Wska?nik na statyczny bufor z wersj? biblioteki.
  *
- *  - brak
- *
- * wersja libgadu.
+ * \ingroup version
  */
 const char *gg_libgadu_version()
 {
 	return GG_LIBGADU_VERSION;
 }
 
-/*
- * gg_fix32()
+/**
+ * \internal Zamienia kolejno?? bajt?w w 32-bitowym s?owie.
  *
- * zamienia kolejno??ajt? liczbie 32-bitowej tak, by odpowiada?a
- * kolejno?ci bajt? protokole GG. ze wzgl? na LE-owo??erwera,
- * zamienia tylko na maszynach BE-wych.
+ * Ze wzgl?du na little-endianowo?? protoko?u Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejno?? bajt?w w s?owie.
  *
- *  - x - liczba do zamiany
+ * \param x Liczba do zamiany
  *
- * liczba z odpowiedni? kolejno?ci? bajt?+ * \return Liczba z odpowiedni? kolejno?ci? bajt?w
+ *
+ * \ingroup helper
  */
 uint32_t gg_fix32(uint32_t x)
 {
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	return x;
 #else
 	return (uint32_t)
@@ -248,20 +209,21 @@ uint32_t gg_fix32(uint32_t x)
 #endif
 }
 
-/*
- * gg_fix16()
+/**
+ * \internal Zamienia kolejno?? bajt?w w 16-bitowym s?owie.
  *
- * zamienia kolejno??ajt? liczbie 16-bitowej tak, by odpowiada?a
- * kolejno?ci bajt? protokole GG. ze wzgl? na LE-owo??erwera,
- * zamienia tylko na maszynach BE-wych.
+ * Ze wzgl?du na little-endianowo?? protoko?u Gadu-Gadu, na maszynach
+ * big-endianowych zamienia kolejno?? bajt?w w s?owie.
  *
- *  - x - liczba do zamiany
+ * \param x Liczba do zamiany
  *
- * liczba z odpowiedni? kolejno?ci? bajt?+ * \return Liczba z odpowiedni? kolejno?ci? bajt?w
+ *
+ * \ingroup helper
  */
 uint16_t gg_fix16(uint16_t x)
 {
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	return x;
 #else
 	return (uint16_t)
@@ -270,15 +232,13 @@ uint16_t gg_fix16(uint16_t x)
 #endif
 }
 
-/* 
- * gg_login_hash() // funkcja wewn?zna
- * 
- * liczy hash z has?a i danego seeda.
- * 
- *  - password - has?o do hashowania
- *  - seed - warto??odana przez serwer
+/**
+ * \internal Liczy skr?t z has?a i ziarna.
  *
- * hash.
+ * \param password Has?o
+ * \param seed Ziarno podane przez serwer
+ *
+ * \return Warto?? skr?tu
  */
 unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
 {
@@ -304,313 +264,22 @@ unsigned int gg_login_hash(const unsigne
 	return y;
 }
 
-#ifndef _WIN32
-
-/*
- * gg_resolve() // funkcja wewn?zna
+/**
+ * \internal Odbiera od serwera dane binarne.
  *
- * tworzy potok, forkuje si? w drugim procesie zaczyna resolvowa?- * podanego hosta. zapisuje w sesji deskryptor potoku. je?li co? tam
- * b?ie gotowego, znaczy, ?e mo?na wczyta?truct in_addr. je?li
- * nie znajdzie, zwraca INADDR_NONE.
+ * Funkcja odbiera dane od serwera zajmuj?c si? TLS w razie konieczno?ci.
  *
- *  - fd - wska?nik gdzie wrzuci?eskryptor
- *  - pid - gdzie wrzuci?id procesu potomnego
- *  - hostname - nazwa hosta do zresolvowania
+ * \param sess Struktura sesji
+ * \param buf Bufor na danymi
+ * \param length D?ugo?? bufora
  *
- * 0, -1.
+ * \return To samo co funkcja systemowa \c read
  */
-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;
-}
-#endif
-
-#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?zna
- *
- * tworzy potok, nowy w?tek i w nim zaczyna resolvowa?odanego hosta.
- * zapisuje w sesji deskryptor potoku. je?li co? tam b?ie gotowego,
- * znaczy, ?e mo?na wczyta?truct 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;
-}
-
-#elif defined _WIN32
-
-struct gg_resolve_win32thread_data {
-	char *hostname;
-	int fd;
-};
-
-static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg)
-{
-	struct gg_resolve_win32thread_data *d = arg;
-	struct in_addr a;
-
-	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);
-
-	return 0;
-}
-
-
-int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname)
-{
-	struct gg_resolve_win32thread_data *d = NULL;
-	HANDLE h;
-	DWORD dwTId;
-	int pipes[2], new_errno;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname);
-	
-	if (!resolver || !fd || !hostname) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (socket_pipe(pipes) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
-		return -1;
-	}
-
-	if (!(d = malloc(sizeof(*d)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	d->hostname = NULL;
-
-	if (!(d->hostname = strdup(hostname))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	d->fd = pipes[1];
-
-	h = CreateThread(NULL, 0, gg_resolve_win32thread_thread,
-		d, 0, &dwTId);
-
-	if (h == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	*resolver = h;
-	*fd = pipes[0];
-
-	return 0;
-
-cleanup:
-	if (d) {
-		free(d->hostname);
-		free(d);
-	}
-
-	close(pipes[0]);
-	close(pipes[1]);
-
-	errno = new_errno;
-
-	return -1;
-
-}
-#endif
-
-/*
- * gg_read() // funkcja pomocnicza
- *
- * czyta z gniazda okre?lon? ilo??ajt?bierze pod uwag?czy mamy
- * po??czenie zwyk?e czy TLS.
- *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - ilo??ajt?- *
- * takie same warto?ci jak read().
- */
 int gg_read(struct gg_session *sess, char *buf, int length)
 {
 	int res;
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl) {
 		int err;
 
@@ -631,23 +300,22 @@ int gg_read(struct gg_session *sess, cha
 	return res;
 }
 
-/*
- * gg_write() // funkcja pomocnicza
+/**
+ * \internal Wysy?a do serwera dane binarne.
  *
- * zapisuje do gniazda okre?lon? ilo??ajt?bierze pod uwag?czy mamy
- * po??czenie zwyk?e czy TLS.
+ * Funkcja wysy?a dane do serwera zajmuj?c si? TLS w razie konieczno?ci.
  *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - ilo??ajt?+ * \param sess Struktura sesji
+ * \param buf Bufor z danymi
+ * \param length D?ugo?? bufora
  *
- * takie same warto?ci jak write().
+ * \return To samo co funkcja systemowa \c write
  */
 int gg_write(struct gg_session *sess, const char *buf, int length)
 {
 	int res = 0;
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl) {
 		int err;
 
@@ -664,46 +332,78 @@ int gg_write(struct gg_session *sess, co
 	} else
 #endif
 	{
-		int written = 0;
-		
-		while (written < length) {
-			res = write(sess->fd, buf + written, length - written);
+		if (!sess->async) {
+			int written = 0;
 
-			if (res == -1) {
-				if (errno == EAGAIN)
+			while (written < length) {
+				res = write(sess->fd, buf + written, length - written);
+
+				if (res == -1) {
+					if (errno != EINTR)
+						break;
+
 					continue;
-				else
-					break;
-			} else {
+				}
+
 				written += res;
 				res = written;
 			}
+		} else {
+			if (!sess->send_buf)
+				res = write(sess->fd, buf, length);
+			else
+				res = 0;
+
+			if (res == -1) {
+				if (errno != EAGAIN)
+					return res;
+
+				res = 0;
+			}
+
+			if (res < length) {
+				char *tmp;
+
+				if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) {
+					errno = ENOMEM;
+					return -1;
+				}
+
+				sess->send_buf = tmp;
+
+				memcpy(sess->send_buf + sess->send_left, buf + res, length - res);
+
+				sess->send_left += length - res;
+			}
 		}
 	}
 
 	return res;
 }
 
-/*
- * gg_recv_packet() // funkcja wewn?zna
+/**
+ * \internal Odbiera pakiet od serwera.
  *
- * odbiera jeden pakiet i zwraca wska?nik do niego. pami?po nim
- * nale?y zwolni?a pomoc? free().
+ * Funkcja odczytuje nag??wek pakietu, a nast?pnie jego zawarto?? i zwraca
+ * w zaalokowanym buforze.
  *
- *  - sess - opis sesji
+ * Przy po??czeniach asynchronicznych, funkcja mo?e nie by? w stanie
+ * skompletowa? ca?ego pakietu -- w takim przypadku zwr?ci -1, a kodem b??du
+ * b?dzie \c EAGAIN.
  *
- * w przypadku b?? NULL, kod b?? w errno. nale?y zwr? uwag??e gdy
- * po??czenie jest nieblokuj?ce, a kod b?? wynosi EAGAIN, nie uda?o si? * odczyta?a?ego pakietu i nie nale?y tego traktowa?ako b??d.
+ * \param sess Struktura sesji
+ *
+ * \return Wska?nik do zaalokowanego bufora
  */
 void *gg_recv_packet(struct gg_session *sess)
 {
 	struct gg_header h;
 	char *buf = NULL;
-	int ret = 0, offset, size = 0;
+	int ret = 0;
+	unsigned int offset, size = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
+
 	if (!sess) {
 		errno = EFAULT;
 		return NULL;
@@ -712,7 +412,7 @@ void *gg_recv_packet(struct gg_session *
 	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);
+			gg_debug_session(sess, 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
@@ -721,34 +421,36 @@ void *gg_recv_packet(struct gg_session *
 		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);
+			gg_debug_session(sess, 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");
+				gg_debug_session(sess, 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");
+					gg_debug_session(sess, 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");
+					gg_debug_session(sess, 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");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n");
 						return NULL;
 					}
 
 					memcpy(sess->header_buf, &h, sess->header_done);
 
+					errno = EAGAIN;
+
 					return NULL;
 				}
 
-				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
 
 				return NULL;
 			}
@@ -761,22 +463,22 @@ void *gg_recv_packet(struct gg_session *
 		h.length = gg_fix32(h.length);
 	} else
 		memcpy(&h, sess->recv_buf, sizeof(h));
-	
-	/* jakie? sensowne limity na rozmiar pakietu */
+
+	/* 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);
+		gg_debug_session(sess, 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");
+		gg_debug_session(sess, 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");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
 			return NULL;
 		}
 
@@ -788,24 +490,23 @@ void *gg_recv_packet(struct gg_session *
 
 	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);
+		gg_debug_session(sess, 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");
-			free(buf);
+			gg_debug_session(sess, 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) {	
+		} 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));
+			gg_debug_session(sess, 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);
+				gg_debug_session(sess, 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;
@@ -823,49 +524,45 @@ void *gg_recv_packet(struct gg_session *
 	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");
+		gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type);
+		for (i = 0; i < sizeof(h) + h.length; i++)
+			gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
+		gg_debug_session(sess, GG_DEBUG_DUMP, "\n");
 	}
 
 	return buf;
 }
 
-/*
- * gg_send_packet() // funkcja wewn?zna
+/**
+ * \internal Wysy?a pakiet do serwera.
  *
- * konstruuje pakiet i wysy?a go do serwera.
+ * Funkcja konstruuje pakiet do wys?ania z dowolnej liczby fragment?w. Je?li
+ * rozmiar pakietu jest za du?y, by m?c go wys?a? za jednym razem, pozosta?a
+ * cz??? zostanie zakolejkowana i wys?ana, gdy b?dzie to mo?liwe.
  *
- *  - sock - deskryptor gniazda
- *  - type - typ pakietu
- *  - payload_1 - pierwsza cz? pakietu
- *  - payload_length_1 - d?ugo??ierwszej cz?i
- *  - payload_2 - druga cz? pakietu
- *  - payload_length_2 - d?ugo??rugiej cz?i
- *  - ... - kolejne cz?i pakietu i ich d?ugo?ci
- *  - NULL - ko?ym parametr (konieczny!)
+ * \param sess Struktura sesji
+ * \param type Rodzaj pakietu
+ * \param ... Lista kolejnych cz??ci pakietu (wska?nik na bufor i d?ugo??
+ *            typu \c int) zako?czona \c NULL
  *
- * je?li si?owiod?o, zwraca 0, w przypadku b?? -1. je?li errno == ENOMEM,
- * zabrak?o pami?. inaczej by? b??d przy wysy?aniu pakietu. dla errno == 0
- * nie wys?ano ca?ego pakietu.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  */
 int gg_send_packet(struct gg_session *sess, int type, ...)
 {
 	struct gg_header *h;
 	char *tmp;
-	int tmp_length;
+	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);
+	gg_debug_session(sess, 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");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
 		return -1;
 	}
 
@@ -879,14 +576,14 @@ int gg_send_packet(struct gg_session *se
 		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");
+			gg_debug_session(sess, 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;
 
@@ -900,54 +597,83 @@ int gg_send_packet(struct gg_session *se
 	h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
 
 	if ((gg_debug_level & GG_DEBUG_DUMP)) {
-		int i;
+		unsigned int i;
 
-		gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
+		gg_debug_session(sess, 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");
+			gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
+		gg_debug_session(sess, 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);
+
+	res = gg_write(sess, tmp, tmp_length);
+
+	free(tmp);
+
+	if (res == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
 		return -1;
 	}
-	
-	free(tmp);	
+
+	if (sess->async)
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() partial write(), %d sent, %d left, %d total left\n", res, tmp_length - res, sess->send_left);
+
+	if (sess->send_buf)
+		sess->check |= GG_CHECK_WRITE;
+
 	return 0;
 }
 
-/*
- * gg_session_callback() // funkcja wewn?zna
+/**
+ * \internal Funkcja zwrotna sesji.
  *
- * wywo?ywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje
- * do gg_session->event jego wynik.
+ * Pole \c callback struktury \c gg_session zawiera wska?nik do tej funkcji.
+ * Wywo?uje ona \c gg_watch_fd i zachowuje wynik w polu \c event.
+ *
+ * \note Korzystanie z tej funkcjonalno?ci nie jest ju? zalecane.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  */
-static int gg_session_callback(struct gg_session *s)
+static int gg_session_callback(struct gg_session *sess)
 {
-	if (!s) {
+	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
 
-	return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1;
+	return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
 }
 
-/*
- * gg_login()
+/**
+ * ??czy si? z serwerem Gadu-Gadu.
  *
- * rozpoczyna procedur??czenia si? serwerem. reszt?bs?uguje si?rzez
- * gg_watch_fd().
+ * Przy po??czeniu synchronicznym funkcja zako?czy dzia?anie po nawi?zaniu
+ * po??czenia lub gdy wyst?pi b??d. Po udanym po??czeniu nale?y wywo?ywa?
+ * funkcj? \c gg_watch_fd(), kt?ra odbiera informacje od serwera i zwraca
+ * informacje o zdarzeniach.
  *
- * UWAGA! program musi obs?u?y?IGCHLD, je?li ??czy si?synchronicznie,
- * ?eby poprawnie zamkn??roces resolvera.
+ * Przy po??czeniu asynchronicznym funkcja rozpocznie procedur? po??czenia
+ * i zwr?ci zaalokowan? struktur?. Pole \c fd struktury \c gg_session zawiera
+ * deskryptor, kt?ry nale?y obserwowa? funkcj? \c select, \c poll lub za
+ * pomoc? mechanizm?w u?ytej p?tli zdarze? (Glib, Qt itp.). Pole \c check
+ * jest mask? bitow? m?wi?c?, czy biblioteka chce by? informowana o mo?liwo?ci
+ * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE).
+ * Po zaobserwowaniu zmian na deskryptorze nale?y wywo?a? funkcj?
+ * \c gg_watch_fd(). Podczas korzystania z po??cze? asynchronicznych, w trakcie
+ * po??czenia mo?e zosta? stworzony dodatkowy proces rozwi?zuj?cy nazw?
+ * serwera -- z tego powodu program musi poprawnie obs?u?y? sygna? SIGCHLD.
  *
- *  - p - struktura opisuj?ca pocz?tkowy stan. wymagane pola: uin, 
- *    password
+ * \note Po nawi?zaniu po??czenia z serwerem nale?y wys?a? list? kontakt?w
+ * za pomoc? funkcji \c gg_notify() lub \c gg_notify_ex().
  *
- * w przypadku b?? NULL, je?li idzie dobrze (async) albo posz?o
- * dobrze (sync), zwr?wska?nik do zaalokowanej struct gg_session.
+ * \param p Struktura opisuj?ca parametry po??czenia. Wymagane pola: uin,
+ *          password, async.
+ *
+ * \return Wska?nik do zaalokowanej struktury sesji \c gg_session lub NULL
+ *         w przypadku b??du.
+ *
+ * \ingroup login
  */
 struct gg_session *gg_login(const struct gg_login_params *p)
 {
@@ -981,8 +707,9 @@ struct gg_session *gg_login(const struct
 		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");
+	if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type);
+		errno = EFAULT;
 		goto fail;
 	}
 
@@ -999,18 +726,59 @@ struct gg_session *gg_login(const struct
 	sess->server_addr = p->server_addr;
 	sess->external_port = p->external_port;
 	sess->external_addr = p->external_addr;
+
+	sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77));
+
+	if (!(p->protocol_features & GG_FEATURE_STATUS77))
+		sess->protocol_features |= GG_FEATURE_STATUS80;
+
+	if (!(p->protocol_features & GG_FEATURE_MSG77))
+		sess->protocol_features |= GG_FEATURE_MSG80;
+
 	sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION;
+
 	if (p->era_omnix)
-		sess->protocol_version |= GG_ERA_OMNIX_MASK;
+		sess->protocol_flags |= GG_ERA_OMNIX_MASK;
 	if (p->has_audio)
-		sess->protocol_version |= GG_HAS_AUDIO_MASK;
+		sess->protocol_flags |= 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;
+	sess->encoding = p->encoding;
 
+	if (gg_session_set_resolver(sess, p->resolver) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unsupported resolver type (%d)\n", p->resolver);
+		errno = EFAULT;
+		goto fail;
+	}
+
+	if (p->status_descr) {
+		int max_length;
+
+		if (sess->protocol_version >= 0x2d)
+			max_length = GG_STATUS_DESCR_MAXSIZE;
+		else
+			max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0;
+
+		if (sess->protocol_version >= 0x2d && p->encoding != GG_ENCODING_UTF8)
+			sess->initial_descr = gg_cp_to_utf8(p->status_descr);
+		else
+			sess->initial_descr = strdup(p->status_descr);
+
+		if (!sess->initial_descr) {
+			gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
+			goto fail;
+		}
+		
+		// XXX pami?ta?, ?eby nie ci?? w ?rodku znaku utf-8
+		
+		if (strlen(sess->initial_descr) > max_length)
+			sess->initial_descr[max_length] = 0;
+	}
+
 	if (p->tls == 1) {
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 		char buf[1024];
 
 		OpenSSL_add_ssl_algorithms();
@@ -1023,7 +791,7 @@ struct gg_session *gg_login(const struct
 			} rstruct;
 
 			time(&rstruct.time);
-			rstruct.ptr = (void *) &rstruct;			
+			rstruct.ptr = (void *) &rstruct;
 
 			RAND_seed((void *) rdata, sizeof(rdata));
 			RAND_seed((void *) &rstruct, sizeof(rstruct));
@@ -1050,7 +818,7 @@ struct gg_session *gg_login(const struct
 		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;
@@ -1059,37 +827,50 @@ struct gg_session *gg_login(const struct
 		port = GG_APPMSG_PORT;
 	}
 
+	if (p->hash_type)
+		sess->hash_type = p->hash_type;
+	else
+		sess->hash_type = GG_LOGIN_HASH_SHA1;
+
 	if (!p->async) {
-		struct in_addr a;
+		struct in_addr addr;
 
-		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))) {
+		if (!sess->server_addr) {
+			if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+				if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
 					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;
+			addr.s_addr = sess->server_addr;
+			port = sess->port;
 		}
 
-		sess->hub_addr = a.s_addr;
+		sess->hub_addr = addr.s_addr;
 
 		if (gg_proxy_enabled)
-			sess->proxy_addr = a.s_addr;
+			sess->proxy_addr = addr.s_addr;
 
-		if ((sess->fd = gg_connect(&a, port, 0)) == -1) {
+		if ((sess->fd = gg_connect(&addr, port, 0)) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
-			goto fail;
+
+			/* nie wysz?o? pr?bujemy portu 443. */
+			if (sess->server_addr) {
+				sess->port = GG_HTTPS_PORT;
+
+				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, 0)) == -1) {
+					/* ostatnia deska ratunku zawiod?a?
+					 * w takim razie zwijamy manatki. */
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					goto fail;
+				}
+			} else {
+				goto fail;
+			}
 		}
 
-		if (p->server_addr && p->server_port)
+		if (sess->server_addr)
 			sess->state = GG_STATE_CONNECTING_GG;
 		else
 			sess->state = GG_STATE_CONNECTING_HUB;
@@ -1114,15 +895,9 @@ struct gg_session *gg_login(const struct
 
 		return sess;
 	}
-	
+
 	if (!sess->server_addr || gg_proxy_enabled) {
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) {
-#elif defined _WIN32
-		if (gg_resolve_win32thread(&sess->fd, &sess->resolver, hostname)) {
-#else
-		if (gg_resolve(&sess->fd, &sess->pid, hostname)) {
-#endif
+		if (sess->resolver_start(&sess->fd, &sess->resolver, hostname) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
 			goto fail;
 		}
@@ -1133,49 +908,121 @@ struct gg_session *gg_login(const struct
 		}
 		sess->state = GG_STATE_CONNECTING_GG;
 		sess->check = GG_CHECK_WRITE;
+		sess->soft_timeout = 1;
 	}
 
 	return sess;
 
 fail:
 	if (sess) {
-		if (sess->password)
-			free(sess->password);
-		if (sess->initial_descr)
-			free(sess->initial_descr);
+		free(sess->password);
+		free(sess->initial_descr);
 		free(sess);
 	}
-	
+
 	return NULL;
 }
 
-/* 
- * gg_free_session()
+/**
+ * Wysy?a do serwera pakiet utrzymania po??czenia.
  *
- * pr?e zamkn??o??czenia i zwalnia pami?zajmowan? przez sesj?+ * Klient powinien regularnie co minut? wysy?a? pakiet utrzymania po??czenia,
+ * inaczej serwer uzna, ?e klient straci? ??czno?? z sieci? i zerwie
+ * po??czenie.
  *
- *  - sess - opis sesji
+ * \param sess Struktura sesji
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup login
  */
-void gg_free_session(struct gg_session *sess)
+int gg_ping(struct gg_session *sess)
 {
+	gg_debug_session(sess, 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);
+}
+
+/**
+ * Ko?czy po??czenie z serwerem.
+ *
+ * Funkcja nie zwalnia zasob?w, wi?c po jej wywo?aniu nale?y u?y?
+ * \c gg_free_session(). Je?li chce si? ustawi? opis niedost?pno?ci, nale?y
+ * wcze?niej wywo?a? funkcj? \c gg_change_status_descr() lub
+ * \c gg_change_status_descr_time().
+ *
+ * \note Je?li w buforze nadawczym po??czenia z serwerem znajduj? si? jeszcze
+ * dane (np. z powodu strat pakiet?w na ??czu), prawdopodobnie zostan? one
+ * utracone przy zrywaniu po??czenia.
+ *
+ * \param sess Struktura sesji
+ *
+ * \ingroup login
+ */
+void gg_logoff(struct gg_session *sess)
+{
 	if (!sess)
 		return;
 
-	/* XXX dopisa?walnianie i zamykanie wszystkiego, co mog?o zosta?/
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
 
-	if (sess->password)
-		free(sess->password);
-	
-	if (sess->initial_descr)
-		free(sess->initial_descr);
+	if (GG_S_NA(sess->status))
+		gg_change_status(sess, GG_STATUS_NOT_AVAIL);
 
-	if (sess->client_version)
-		free(sess->client_version);
+#ifdef GG_CONFIG_HAVE_OPENSSL
+	if (sess->ssl)
+		SSL_shutdown(sess->ssl);
+#endif
 
-	if (sess->header_buf)
-		free(sess->header_buf);
+	sess->resolver_cleanup(&sess->resolver, 1);
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+	if (sess->fd != -1) {
+		shutdown(sess->fd, SHUT_RDWR);
+		close(sess->fd);
+		sess->fd = -1;
+	}
+
+	if (sess->send_buf) {
+		free(sess->send_buf);
+		sess->send_buf = NULL;
+		sess->send_left = 0;
+	}
+}
+
+/**
+ * Zwalnia zasoby u?ywane przez po??czenie z serwerem. Funkcj? nale?y wywo?a?
+ * po zamkni?ciu po??czenia z serwerem, by nie doprowadzi? do wycieku zasob?w
+ * systemowych.
+ *
+ * \param sess Struktura sesji
+ *
+ * \ingroup login
+ */
+void gg_free_session(struct gg_session *sess)
+{
+	struct gg_dcc7 *dcc;
+
+	if (!sess)
+		return;
+
+	/* XXX dopisa? zwalnianie i zamykanie wszystkiego, co mog?o zosta? */
+
+	free(sess->password);
+	free(sess->initial_descr);
+	free(sess->client_version);
+	free(sess->header_buf);
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl)
 		SSL_free(sess->ssl);
 
@@ -1183,23 +1030,7 @@ void gg_free_session(struct gg_session *
 		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;
-	}
-#elif defined _WIN32
-	if (sess->resolver) {
-		HANDLE h = sess->resolver;
-		TerminateThread(h, 0);
-		CloseHandle(h);
-		sess->resolver = NULL;
-	}
-#else
-	if (sess->pid != -1)
-		waitpid(sess->pid, NULL, WNOHANG);
-#endif
+	sess->resolver_cleanup(&sess->resolver, 1);
 
 	if (sess->fd != -1)
 		close(sess->fd);
@@ -1207,25 +1038,38 @@ void gg_free_session(struct gg_session *
 	while (sess->images)
 		gg_image_queue_remove(sess, sess->images, 1);
 
+	free(sess->send_buf);
+
+	for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
+		dcc->sess = NULL;
+
 	free(sess);
 }
 
-/*
- * gg_change_status()
+#ifndef DOXYGEN
+
+/**
+ * \internal Funkcja wysy?aj?ca pakiet zmiany statusu u?ytkownika.
  *
- * zmienia status u?ytkownika. przydatne do /away i /busy oraz /quit.
+ * \param sess Struktura sesji
+ * \param status Nowy status u?ytkownika
+ * \param descr Opis statusu u?ytkownika (lub \c NULL)
+ * \param time Czas powrotu w postaci uniksowego znacznika czasu (lub 0)
  *
- *  - sess - opis sesji
- *  - status - nowy status u?ytkownika
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  *
- * 0, -1.
+ * \ingroup status
  */
-int gg_change_status(struct gg_session *sess, int status)
+static int gg_change_status_common(struct gg_session *sess, int status, const char *descr, int time)
 {
-	struct gg_new_status p;
+	char *new_descr = NULL;
+	uint32_t new_time;
+	int descr_len = 0;
+	int descr_len_max;
+	int packet_type;
+	int append_null = 0;
+	int res;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
-
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
@@ -1236,67 +1080,422 @@ int gg_change_status(struct gg_session *
 		return -1;
 	}
 
-	p.status = gg_fix32(status);
+	/* XXX, obcina? stany kt?rych stary protok?? niezna (czyt. dnd->aw; ffc->av) */
 
+	/* dodaj flag? obs?ugi po??cze? g?osowych zgodn? z GG 7.x */
+	if ((sess->protocol_version >= 0x2a) && (sess->protocol_version < 0x2d /* ? */ ) && (sess->protocol_flags & GG_HAS_AUDIO_MASK) && !GG_S_I(status))
+		status |= GG_STATUS_VOICE_MASK;
+
 	sess->status = status;
 
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL);
+	if (sess->protocol_version >= 0x2d) {
+		if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
+			new_descr = gg_cp_to_utf8(descr);
+
+			if (!new_descr)
+				return -1;
+		}
+
+		if (sess->protocol_version >= 0x2e)
+			packet_type = GG_NEW_STATUS80;
+		else /* sess->protocol_version == 0x2d */
+			packet_type = GG_NEW_STATUS80BETA;
+		descr_len_max = GG_STATUS_DESCR_MAXSIZE;
+		append_null = 1;
+
+	} else {
+		packet_type = GG_NEW_STATUS;
+		descr_len_max = GG_STATUS_DESCR_MAXSIZE_PRE_8_0;
+
+		if (time != 0)
+			append_null = 1;
+	}
+
+	if (descr) {
+		descr_len = strlen((new_descr) ? new_descr : descr);
+
+		if (descr_len > descr_len_max)
+			descr_len = descr_len_max;
+
+		// XXX pami?ta? o tym, ?eby nie ucina? w ?rodku znaku utf-8
+	}
+
+	if (time)
+		new_time = gg_fix32(time);
+
+	if (packet_type == GG_NEW_STATUS80) {
+		struct gg_new_status80 p;
+
+		p.status		= gg_fix32(status);
+		p.flags			= gg_fix32(0x00800001);
+		p.description_size	= gg_fix32(descr_len);
+		res = gg_send_packet(sess,
+				packet_type,
+				&p,
+				sizeof(p),
+				(new_descr) ? new_descr : descr,
+				descr_len,
+				NULL);
+
+	} else {
+		struct gg_new_status p;
+
+		p.status = gg_fix32(status);
+		res = gg_send_packet(sess,
+				packet_type,
+				&p,
+				sizeof(p),
+				(new_descr) ? new_descr : descr,
+				descr_len,
+				(append_null) ? "\0" : NULL,
+				(append_null) ? 1 : 0,
+				(time) ? &new_time : NULL,
+				(time) ? sizeof(new_time) : 0,
+				NULL);
+	}
+
+	free(new_descr);
+	return res;
 }
 
-/*
- * gg_change_status_descr()
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia status u?ytkownika.
  *
- * zmienia status u?ytkownika na opisowy.
+ * \param sess Struktura sesji
+ * \param status Nowy status u?ytkownika
  *
- *  - sess - opis sesji
- *  - status - nowy status u?ytkownika
- *  - descr - opis statusu
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  *
- * 0, -1.
+ * \ingroup status
  */
+int gg_change_status(struct gg_session *sess, int status)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
+
+	return gg_change_status_common(sess, status, NULL, 0);
+}
+
+/**
+ * Zmienia status u?ytkownika na status opisowy.
+ *
+ * \param sess Struktura sesji
+ * \param status Nowy status u?ytkownika
+ * \param descr Opis statusu u?ytkownika
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup status
+ */
 int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
 {
-	struct gg_new_status p;
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
+	return gg_change_status_common(sess, status, descr, 0);
+}
 
-	if (!sess || !descr) {
-		errno = EFAULT;
-		return -1;
+/**
+ * Zmienia status u?ytkownika na status opisowy z podanym czasem powrotu.
+ *
+ * \param sess Struktura sesji
+ * \param status Nowy status u?ytkownika
+ * \param descr Opis statusu u?ytkownika
+ * \param time Czas powrotu w postaci uniksowego znacznika czasu
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup status
+ */
+int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+
+	return gg_change_status_common(sess, status, descr, time);
+}
+
+/**
+ * Wysy?a wiadomo?? do u?ytkownika.
+ *
+ * Zwraca losowy numer sekwencyjny, kt?ry mo?na zignorowa? albo wykorzysta?
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomo?ci
+ * \param recipient Numer adresata
+ * \param message Tre?? wiadomo?ci
+ *
+ * \return Numer sekwencyjny wiadomo?ci lub -1 w przypadku b??du.
+ *
+ * \ingroup messages
+ */
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);
+
+	return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, NULL, 0);
+}
+
+/**
+ * Wysy?a wiadomo?? formatowan?.
+ *
+ * Zwraca losowy numer sekwencyjny, kt?ry mo?na zignorowa? albo wykorzysta?
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomo?ci
+ * \param recipient Numer adresata
+ * \param message Tre?? wiadomo?ci
+ * \param format Informacje o formatowaniu
+ * \param formatlen D?ugo?? informacji o formatowaniu
+ *
+ * \return Numer sekwencyjny wiadomo?ci lub -1 w przypadku b??du.
+ *
+ * \ingroup messages
+ */
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);
+
+	return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, format, formatlen);
+}
+
+/**
+ * Wysy?a wiadomo?? w ramach konferencji.
+ *
+ * Zwraca losowy numer sekwencyjny, kt?ry mo?na zignorowa? albo wykorzysta?
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomo?ci
+ * \param recipients_count Liczba adresat?w
+ * \param recipients Wska?nik do tablicy z numerami adresat?w
+ * \param message Tre?? wiadomo?ci
+ *
+ * \return Numer sekwencyjny wiadomo?ci lub -1 w przypadku b??du.
+ *
+ * \ingroup messages
+ */
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message)
+{
+	gg_debug_session(sess, 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);
+}
+
+/**
+ * \internal Dodaje tekst na koniec bufora.
+ * 
+ * \param dst Wska?nik na bufor roboczy
+ * \param pos Wska?nik na aktualne po?o?enie w buforze roboczym
+ * \param src Dodawany tekst
+ * \param len D?ugo?? dodawanego tekstu
+ */
+static void gg_append(char *dst, int *pos, const void *src, int len)
+{
+	if (dst != NULL)
+		memcpy(&dst[*pos], src, len);
+
+	*pos += len;
+}
+
+/**
+ * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML.
+ *
+ * \param dst Bufor wynikowy (mo?e by? \c NULL)
+ * \param utf_msg Tekst ?r?d?owy
+ * \param format Atrybuty tekstu ?r?d?owego
+ * \param format_len D?ugo?? bloku atrybut?w tekstu ?r?d?owego
+ *
+ * \note Dokleja \c \\0 na ko?cu bufora wynikowego.
+ *
+ * \return D?ugo?? tekstu wynikowego bez \c \\0 (nawet je?li \c dst to \c NULL).
+ */
+static int gg_convert_to_html(char *dst, const char *utf_msg, const unsigned char *format, int format_len)
+{
+	const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">";
+	const int span_len = 75;
+	const char img_fmt[] = "<img src=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
+	const int img_len = 28;
+	int char_pos = 0;
+	int format_idx = 3;
+	unsigned char old_attr = 0;
+	const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
+	int len, i;
+
+	len = 0;
+
+	for (i = 0; utf_msg[i] != 0; i++) {
+		unsigned char attr;
+		int attr_pos;
+
+		if (format_idx + 3 <= format_len) {
+			attr_pos = format[format_idx] | (format[format_idx + 1] << 8);
+			attr = format[format_idx + 2];
+		} else {
+			attr_pos = -1;
+			attr = 0;
+		}
+
+		if (attr_pos == char_pos) {
+			format_idx += 3;
+
+			if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) {
+				if (char_pos != 0) {
+					if ((old_attr & GG_FONT_UNDERLINE) != 0)
+						gg_append(dst, &len, "</u>", 4);
+
+					if ((old_attr & GG_FONT_ITALIC) != 0)
+						gg_append(dst, &len, "</i>", 4);
+
+					if ((old_attr & GG_FONT_BOLD) != 0)
+						gg_append(dst, &len, "</b>", 4);
+
+					gg_append(dst, &len, "</span>", 7);
+				}
+
+				if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
+					color = &format[format_idx];
+					format_idx += 3;
+				} else {
+					color = (const unsigned char*) "\x00\x00\x00";
+				}
+
+				if (dst != NULL)
+					sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
+				len += span_len;
+			} else if (char_pos == 0) {
+				if (dst != NULL)
+					sprintf(&dst[len], span_fmt, 0, 0, 0);
+				len += span_len;
+			}
+
+			if ((attr & GG_FONT_BOLD) != 0)
+				gg_append(dst, &len, "<b>", 3);
+
+			if ((attr & GG_FONT_ITALIC) != 0)
+				gg_append(dst, &len, "<i>", 3);
+
+			if ((attr & GG_FONT_UNDERLINE) != 0)
+				gg_append(dst, &len, "<u>", 3);
+
+			if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
+				if (dst != NULL) {
+					sprintf(&dst[len], img_fmt,
+						format[format_idx + 9],
+						format[format_idx + 8], 
+						format[format_idx + 7],
+						format[format_idx + 6], 
+						format[format_idx + 5],
+						format[format_idx + 4],
+						format[format_idx + 3],
+						format[format_idx + 2]);
+				}
+
+				len += img_len;
+				format_idx += 10;
+			}
+
+			old_attr = attr;
+		} else if (i == 0) {
+			if (dst != NULL)
+				sprintf(&dst[len], span_fmt, 0, 0, 0);
+
+			len += span_len;
+		}
+
+		switch (utf_msg[i]) {
+			case '&':
+				gg_append(dst, &len, "&amp;", 5);
+				break;
+			case '<':
+				gg_append(dst, &len, "&lt;", 4);
+				break;
+			case '>':
+				gg_append(dst, &len, "&gt;", 4);
+				break;
+			case '\'':
+				gg_append(dst, &len, "&apos;", 6);
+				break;
+			case '\"':
+				gg_append(dst, &len, "&quot;", 6);
+				break;
+			case '\n':
+				gg_append(dst, &len, "<br>", 4);
+				break;
+			case '\r':
+				break;
+			default:
+				if (dst != NULL)
+					dst[len] = utf_msg[i];
+				len++;
+		}
+
+		/* Sprawd?, czy bajt nie jest kontynuacj? znaku unikodowego. */
+
+		if ((utf_msg[i] & 0xc0) != 0xc0)
+			char_pos++;
 	}
 
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
+	if ((old_attr & GG_FONT_UNDERLINE) != 0)
+		gg_append(dst, &len, "</u>", 4);
+
+	if ((old_attr & GG_FONT_ITALIC) != 0)
+		gg_append(dst, &len, "</i>", 4);
+
+	if ((old_attr & GG_FONT_BOLD) != 0)
+		gg_append(dst, &len, "</b>", 4);
+
+	/* Dla pustych tekst?w dodaj pusty <span>. */
+
+	if (i == 0) {
+		if (dst != NULL)
+			sprintf(&dst[len], span_fmt, 0, 0, 0);
+
+		len += span_len;
 	}
 
-	p.status = gg_fix32(status);
+	gg_append(dst, &len, "</span>", 7);
 
-	sess->status = status;
+	if (dst != NULL)
+		dst[len] = 0;
 
-	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);
+	return len;
 }
 
-/*
- * gg_change_status_descr_time()
+/**
+ * Wysy?a wiadomo?? formatowan? w ramach konferencji.
  *
- * zmienia status u?ytkownika na opisowy z godzin? powrotu.
+ * Zwraca losowy numer sekwencyjny, kt?ry mo?na zignorowa? albo wykorzysta?
+ * do potwierdzenia.
  *
- *  - sess - opis sesji
- *  - status - nowy status u?ytkownika
- *  - descr - opis statusu
- *  - time - czas w formacie uniksowym
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomo?ci
+ * \param recipients_count Liczba adresat?w
+ * \param recipients Wska?nik do tablicy z numerami adresat?w
+ * \param message Tre?? wiadomo?ci
+ * \param format Informacje o formatowaniu
+ * \param formatlen D?ugo?? informacji o formatowaniu
  *
- * 0, -1.
+ * \return Numer sekwencyjny wiadomo?ci lub -1 w przypadku b??du.
+ * 
+ * \ingroup messages
  */
-int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time)
+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_new_status p;
-	uint32_t newtime;
+	struct gg_send_msg s;
+	struct gg_send_msg80 s80;
+	struct gg_msg_recipients r;
+	char *cp_msg = NULL;
+	char *utf_msg = NULL;
+	char *html_msg = NULL;
+	int seq_no;
+	int i, j, k;
+	uin_t *recps;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+	gg_debug_session(sess, 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 || !descr || !time) {
+	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
@@ -1306,75 +1505,184 @@ int gg_change_status_descr_time(struct g
 		return -1;
 	}
 
-	p.status = gg_fix32(status);
+	if (message == NULL || recipients_count <= 0 || recipients_count > 0xffff || (recipients_count != 1 && recipients == NULL)) {
+		errno = EINVAL;
+		return -1;
+	}
 
-	sess->status = status;
+	if (sess->encoding == GG_ENCODING_UTF8) {
+		if (!(cp_msg = gg_utf8_to_cp((const char *) message)))
+			return -1;
 
-	newtime = gg_fix32(time);
+		utf_msg = (char*) message;
+	} else {
+		if (sess->protocol_version >= 0x2d) {
+			if (!(utf_msg = gg_cp_to_utf8((const char *) message)))
+				return -1;
+		}
 
-	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);
+		cp_msg = (char*) message;
+	}
+
+	if (sess->protocol_version < 0x2d) {
+		if (!sess->seq)
+			sess->seq = 0x01740000 | (rand() & 0xffff);
+		seq_no = sess->seq;
+		sess->seq += (rand() % 0x300) + 0x300;
+
+		s.msgclass = gg_fix32(msgclass);
+		s.seq = gg_fix32(seq_no);
+	} else {
+		int len;
+		
+		// Drobne odchylenie od protoko?u. Je?li wysy?amy kilka
+		// wiadomo?ci w ci?gu jednej sekundy, zwi?kszamy poprzedni?
+		// warto??, ?eby ka?da wiadomo?? mia?a unikalny numer.
+
+		seq_no = time(NULL);
+
+		if (seq_no <= sess->seq)
+			seq_no = sess->seq + 1;
+
+		sess->seq = seq_no;
+
+		if (format == NULL || formatlen < 3) {
+			format = (unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00";
+			formatlen = 9;
+		}
+
+		len = gg_convert_to_html(NULL, utf_msg, format, formatlen);
+
+		html_msg = malloc(len + 1);
+
+		if (html_msg == NULL) {
+			seq_no = -1;
+			goto cleanup;
+		}
+
+		gg_convert_to_html(html_msg, utf_msg, format, formatlen);
+
+		s80.seq = gg_fix32(seq_no);
+		s80.msgclass = gg_fix32(msgclass);
+		s80.offset_plain = gg_fix32(sizeof(s80) + strlen(html_msg) + 1);
+		s80.offset_attr = gg_fix32(sizeof(s80) + strlen(html_msg) + 1 + strlen(cp_msg) + 1);
+	}
+
+	if (recipients_count > 1) {
+		r.flag = 0x01;
+		r.count = gg_fix32(recipients_count - 1);
+
+		recps = malloc(sizeof(uin_t) * recipients_count);
+
+		if (!recps) {
+			seq_no = -1;
+			goto cleanup;
+		}
+
+		for (i = 0; i < recipients_count; i++) {
+			for (j = 0, k = 0; j < recipients_count; j++) {
+				if (recipients[j] != recipients[i]) {
+					recps[k] = gg_fix32(recipients[j]);
+					k++;
+				}
+			}
+
+			if (sess->protocol_version < 0x2d) {
+				s.recipient = gg_fix32(recipients[i]);
+
+				if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1)
+					seq_no = -1;
+			} else {
+				s80.recipient = gg_fix32(recipients[i]);
+
+				if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1)
+					seq_no = -1;
+			}
+		}
+
+		free(recps);
+	} else {
+		if (sess->protocol_version < 0x2d) {
+			s.recipient = gg_fix32(recipients[0]);
+
+			if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
+				seq_no = -1;
+		} else {
+			s80.recipient = gg_fix32(recipients[0]);
+
+			if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
+				seq_no = -1;
+		}
+	}
+
+cleanup:
+	if (cp_msg != (char*) message)
+		free(cp_msg);
+
+	if (utf_msg != (char*) message)
+		free(utf_msg);
+
+	free(html_msg);
+
+	return seq_no;
 }
 
-/*
- * gg_logoff()
+/**
+ * Wysy?a wiadomo?? binarn? przeznaczon? dla klienta.
  *
- * wylogowuje u?ytkownika i zamyka po??czenie, ale nie zwalnia pami?.
+ * Wiadomo?ci mi?dzy klientami przesy?a si? np. w celu wywo?ania zwrotnego
+ * po??czenia bezpo?redniego. Funkcja zwraca losowy numer sekwencyjny,
+ * kt?ry mo?na zignorowa? albo wykorzysta? do potwierdzenia.
  *
- *  - sess - opis sesji
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomo?ci
+ * \param recipient Numer adresata
+ * \param message Tre?? wiadomo?ci
+ * \param message_len D?ugo?? wiadomo?ci
+ *
+ * \return Numer sekwencyjny wiadomo?ci lub -1 w przypadku b??du.
+ *
+ * \ingroup messages
  */
-void gg_logoff(struct gg_session *sess)
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
 {
-	if (!sess)
-		return;
+	struct gg_send_msg s;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
 
-	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;
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
 	}
-#elif defined _WIN32
-	if (sess->resolver) {
-		HANDLE h = sess->resolver;
-		TerminateThread(h, 0);
-		CloseHandle(h);
-		sess->resolver = NULL;
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
 	}
-#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;
-	}
+
+	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_image_request()
+/**
+ * Wysy?a ??danie obrazka o podanych parametrach.
  *
- * wysy?a ??danie wys?ania obrazka o podanych parametrach.
+ * Wiadomo?ci obrazkowe nie zawieraj? samych obrazk?w, a tylko ich rozmiary
+ * i sumy kontrolne. Odbiorca najpierw szuka obrazk?w w swojej pami?ci
+ * podr?cznej i dopiero gdy ich nie znajdzie, wysy?a ??danie do nadawcy.
+ * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY.
  *
- *  - sess - opis sesji
- *  - recipient - numer adresata
- *  - size - rozmiar obrazka
- *  - crc32 - suma kontrolna obrazka
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param size Rozmiar obrazka w bajtach
+ * \param crc32 Suma kontrola obrazka
  *
- * 0/-1
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup messages
  */
 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
 {
@@ -1383,13 +1691,13 @@ int gg_image_request(struct gg_session *
 	char dummy = 0;
 	int res;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);
+	gg_debug_session(sess, 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;
@@ -1407,7 +1715,7 @@ int gg_image_request(struct gg_session *
 	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) {
@@ -1415,14 +1723,14 @@ int gg_image_request(struct gg_session *
 		char *buf;
 
 		if (!q) {
-			gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
+			gg_debug_session(sess, 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");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
 			free(q);
 			return -1;
 		}
@@ -1449,20 +1757,20 @@ int gg_image_request(struct gg_session *
 	return res;
 }
 
-/*
- * gg_image_reply()
+/**
+ * Wysy?a ??dany obrazek.
  *
- * wysy?a ??dany obrazek.
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param filename Nazwa pliku
+ * \param image Bufor z obrazkiem
+ * \param size Rozmiar obrazka
  *
- *  - sess - opis sesji
- *  - recipient - numer adresata
- *  - filename - nazwa pliku
- *  - image - bufor z obrazkiem
- *  - size - rozmiar obrazka
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  *
- * 0/-1
+ * \ingroup messages
  */
-int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size)
+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;
@@ -1470,7 +1778,7 @@ int gg_image_reply(struct gg_session *se
 	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);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);
 
 	if (!sess || !filename || !image) {
 		errno = EFAULT;
@@ -1487,7 +1795,7 @@ int gg_image_reply(struct gg_session *se
 		return -1;
 	}
 
-	/* wytnij ?cie?ki, zostaw tylko nazw?liku */
+	/* wytnij ?cie?ki, zostaw tylko nazw? pliku */
 	while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
 		filename = tmp + 1;
 
@@ -1495,7 +1803,7 @@ int gg_image_reply(struct gg_session *se
 		errno = EINVAL;
 		return -1;
 	}
-	
+
 	s.recipient = gg_fix32(recipient);
 	s.seq = gg_fix32(0);
 	s.msgclass = gg_fix32(GG_CLASS_MSG);
@@ -1505,26 +1813,26 @@ int gg_image_reply(struct gg_session *se
 
 	r->flag = 0x05;
 	r->size = gg_fix32(size);
-	r->crc32 = gg_fix32(gg_crc32(0, image, size));
+	r->crc32 = gg_fix32(gg_crc32(0, (unsigned char*) image, size));
 
 	while (size > 0) {
-		size_t buflen, chunklen;
-		
+		int buflen, chunklen;
+
 		/* \0 + struct gg_msg_image_reply */
 		buflen = sizeof(struct gg_msg_image_reply) + 1;
 
-		/* w pierwszym kawa?ku jest nazwa pliku */
+		/* w pierwszym kawa?ku jest nazwa pliku */
 		if (r->flag == 0x05) {
 			strcpy(buf + buflen, filename);
 			buflen += strlen(filename) + 1;
 		}
 
-		chunklen = ((size_t)size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t)size;
+		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)
@@ -1536,249 +1844,26 @@ int gg_image_reply(struct gg_session *se
 	return res;
 }
 
-/*
- * gg_send_message_ctcp()
+/**
+ * Wysy?a do serwera list? kontakt?w.
  *
- * wysy?a wiadomo??o innego u?ytkownika. zwraca losowy numer
- * sekwencyjny, kt?mo?na zignorowa?lbo wykorzysta?o potwierdzenia.
+ * Funkcja informuje serwer o li?cie kontakt?w, kt?rych statusy b?d?
+ * obserwowane lub kontakt?w, kt?re bed? blokowane. Dla ka?dego z \c count
+ * kontakt?w tablica \c userlist zawiera numer, a tablica \c types rodzaj
+ * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED).
  *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomo?ci
- *  - recipient - numer adresata
- *  - message - tre??iadomo?ci
- *  - message_len - d?ugo?? * List? kontakt?w nale?y \b zawsze wysy?a? po po??czeniu, nawet je?li
+ * jest pusta.
  *
- * numer sekwencyjny wiadomo?ci lub -1 w przypadku b??.
- */
-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()
+ * \param sess Struktura sesji
+ * \param userlist Wska?nik do tablicy numer?w kontakt?w
+ * \param types Wska?nik do tablicy rodzaj?w kontakt?w
+ * \param count Liczba kontakt?w
  *
- * wysy?a wiadomo??o innego u?ytkownika. zwraca losowy numer
- * sekwencyjny, kt?mo?na zignorowa?lbo wykorzysta?o potwierdzenia.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomo?ci
- *  - recipient - numer adresata
- *  - message - tre??iadomo?ci
- *
- * numer sekwencyjny wiadomo?ci lub -1 w przypadku b??.
+ * \ingroup contacts
  */
-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??o innego u?ytkownika. zwraca losowy numer
- * sekwencyjny, kt?mo?na zignorowa?lbo wykorzysta?o potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomo?ci
- *  - recipient - numer adresata
- *  - message - tre??iadomo?ci
- *  - format - informacje o formatowaniu
- *  - formatlen - d?ugo??nformacji o formatowaniu
- *
- * numer sekwencyjny wiadomo?ci lub -1 w przypadku b??.
- */
-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((const char *)message) + 1, format, formatlen, NULL) == -1)
-		return -1;
-
-	return gg_fix32(s.seq);
-}
-
-/*
- * gg_send_message_confer()
- *
- * wysy?a wiadomo??o kilku u?ytkownikow (konferencja). zwraca losowy numer
- * sekwencyjny, kt?mo?na zignorowa?lbo wykorzysta?o potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomo?ci
- *  - recipients_count - ilo??dresat? *  - recipients - numerki adresat? *  - message - tre??iadomo?ci
- *
- * numer sekwencyjny wiadomo?ci lub -1 w przypadku b??.
- */
-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??o kilku u?ytkownikow (konferencja). zwraca
- * losowy numer sekwencyjny, kt?mo?na zignorowa?lbo wykorzysta?o
- * potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomo?ci
- *  - recipients_count - ilo??dresat? *  - recipients - numerki adresat? *  - message - tre??iadomo?ci
- *  - format - informacje o formatowaniu
- *  - formatlen - d?ugo??nformacji o formatowaniu
- *
- * numer sekwencyjny wiadomo?ci lub -1 w przypadku b??.
- */
-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((const char *)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?ontakt?wraz z odpowiadaj?cymi im typami user?
- * dzi? czemu wie, czyj stan nas interesuje.
- *
- *  - sess - opis sesji
- *  - userlist - wska?nik do tablicy numer? *  - types - wska?nik do tablicy typ??ytkownik? *  - count - ilo??umerk? *
- * 0, -1.
- */
 int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
 {
 	struct gg_notify *n;
@@ -1786,13 +1871,13 @@ int gg_notify_ex(struct gg_session *sess
 	char *t;
 	int i, res = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
-	
+	gg_debug_session(sess, 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;
@@ -1800,10 +1885,10 @@ int gg_notify_ex(struct gg_session *sess
 
 	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;
@@ -1814,12 +1899,12 @@ int gg_notify_ex(struct gg_session *sess
 
 		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++) { 
+
+		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;
@@ -1836,17 +1921,19 @@ int gg_notify_ex(struct gg_session *sess
 	return res;
 }
 
-/*
- * gg_notify()
+/**
+ * Wysy?a do serwera list? kontakt?w.
  *
- * wysy?a serwerowi list?ontakt?dzi? czemu wie, czyj stan nas
- * interesuje.
+ * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
+ * s? rodzaju \c GG_USER_NORMAL.
  *
- *  - sess - opis sesji
- *  - userlist - wska?nik do tablicy numer? *  - count - ilo??umerk? * \param sess Struktura sesji
+ * \param userlist Wska?nik do tablicy numer?w kontakt?w
+ * \param count Liczba kontakt?w
  *
- * 0, -1.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup contacts
  */
 int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
 {
@@ -1854,13 +1941,13 @@ int gg_notify(struct gg_session *sess, u
 	uin_t *u;
 	int i, res = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count);
-	
+	gg_debug_session(sess, 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;
@@ -1868,10 +1955,10 @@ int gg_notify(struct gg_session *sess, u
 
 	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;
@@ -1879,15 +1966,15 @@ int gg_notify(struct gg_session *sess, u
 			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++) { 
+
+		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);
@@ -1903,24 +1990,27 @@ int gg_notify(struct gg_session *sess, u
 	return res;
 }
 
-/*
- * gg_add_notify_ex()
+/**
+ * Dodaje kontakt.
  *
- * dodaje do listy kontakt?any numer w trakcie po??czenia.
- * dodawanemu u?ytkownikowi okre?lamy jego typ (patrz protocol.html)
+ * Dodaje do listy kontakt?w dany numer w trakcie po??czenia. Aby zmieni?
+ * rodzaj kontaktu (np. z normalnego na zablokowany), nale?y najpierw usun??
+ * poprzedni rodzaj, poniewa? serwer operuje na maskach bitowych.
  *
- *  - sess - opis sesji
- *  - uin - numer
- *  - type - typ
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
  *
- * 0, -1.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup contacts
  */
 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);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
@@ -1930,46 +2020,50 @@ int gg_add_notify_ex(struct gg_session *
 		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 kontakt.
  *
- * dodaje do listy kontakt?any numer w trakcie po??czenia.
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontakt?w to \c GG_USER_NORMAL.
  *
- *  - sess - opis sesji
- *  - uin - numer
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
  *
- * 0, -1.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup contacts
  */
 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 kontakt.
  *
- * usuwa z listy kontakt? trakcie po??czenia.
- * usuwanemu u?ytkownikowi okre?lamy jego typ (patrz protocol.html)
+ * Usuwa z listy kontakt?w dany numer w trakcie po??czenia.
  *
- *  - sess - opis sesji
- *  - uin - numer
- *  - type - typ
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
  *
- * 0, -1.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup contacts
  */
 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);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
@@ -1982,35 +2076,48 @@ int gg_remove_notify_ex(struct gg_sessio
 
 	a.uin = gg_fix32(uin);
 	a.dunno1 = type;
-	
+
 	return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
 }
 
-/*
- * gg_remove_notify()
+/**
+ * Usuwa kontakt.
  *
- * usuwa z listy kontakt? trakcie po??czenia.
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontakt?w to \c GG_USER_NORMAL.
  *
- *  - sess - opis sesji
- *  - uin - numer
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
  *
- * 0, -1.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup contacts
  */
 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 do serwera zapytanie dotycz?ce listy kontakt?w.
  *
- * wysy?a ??danie/zapytanie listy kontakt?a serwerze.
+ * Funkcja s?u?y do importu lub eksportu listy kontakt?w do serwera.
+ * W odr??nieniu od funkcji \c gg_notify(), ta lista kontakt?w jest przez
+ * serwer jedynie przechowywana i nie ma wp?ywu na po??czenie. Format
+ * listy kontakt?w jest ignorowany przez serwer, ale ze wzgl?du na
+ * kompatybilno?? z innymi klientami, nale?y przechowywa? dane w tym samym
+ * formacie co oryginalny klient Gadu-Gadu.
  *
- *  - sess - opis sesji
- *  - type - rodzaj zapytania/??dania
- *  - request - tre??apytania/??dania (mo?e by?ULL)
+ * Program nie musi si? przejmowa? fragmentacj? listy kontakt?w wynikaj?c?
+ * z protoko?u -- wysy?a i odbiera kompletn? list?.
  *
- * 0, -1
+ * \param sess Struktura sesji
+ * \param type Rodzaj zapytania
+ * \param request Tre?? zapytania (mo?e by? r?wne NULL)
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup importexport
  */
 int gg_userlist_request(struct gg_session *sess, char type, const char *request)
 {
@@ -2020,7 +2127,7 @@ int gg_userlist_request(struct gg_sessio
 		errno = EFAULT;
 		return -1;
 	}
-	
+
 	if (sess->state != GG_STATE_CONNECTED) {
 		errno = ENOTCONN;
 		return -1;
@@ -2030,7 +2137,7 @@ int gg_userlist_request(struct gg_sessio
 		sess->userlist_blocks = 1;
 		return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
 	}
-	
+
 	len = strlen(request);
 
 	sess->userlist_blocks = 0;
@@ -2053,6 +2160,8 @@ int gg_userlist_request(struct gg_sessio
 	return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
 }
 
+/* @} */
+
 /*
  * Local variables:
  * c-indentation-style: k&r
============================================================
--- libpurple/protocols/gg/lib/libgadu.h	d6bdbb792a340333b9ed9049448efb6e4d5e1531
+++ libpurple/protocols/gg/lib/libgadu.h	46fc0a49b72bc5496a96bc77c409d8db0e4fda88
@@ -1,12 +1,13 @@
-/* $Id: libgadu.h 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: libgadu.h.in 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka at irc.pl>
- *                          Robert J. Wo?ny <speedy at ziew.org>
- *                          Arkadiusz Mi?kiewicz <arekm at pld-linux.org>
- *                          Tomasz Chili? <chilek at chilan.com>
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka at irc.pl>
+ *                          Robert J. Wo?ny <speedy at ziew.org>
+ *                          Arkadiusz Mi?kiewicz <arekm at pld-linux.org>
+ *                          Tomasz Chili?ski <chilek at chilan.com>
  *                          Piotr Wysocki <wysek at linux.bydg.org>
  *                          Dawid Jarosz <dawjar at poczta.onet.pl>
+ *                          Jakub Zawadzki <darkjames at darkjames.ath.cx>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -19,338 +20,567 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file libgadu.h
+ *
+ * \brief G??wny plik nag??wkowy biblioteki
+ */
+
 #ifndef __GG_LIBGADU_H
 #define __GG_LIBGADU_H
 
 #ifdef __cplusplus
-#ifdef _MSC_VER
+#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
+/** \cond ignore */
+
+/* Defined if libgadu was compiled for bigendian machine. */
+#undef GG_CONFIG_BIGENDIAN
+
+/* Defined if this machine has gethostbyname_r(). */
+#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+
+/* Defined if libgadu was compiled and linked with pthread support. */
+#undef GG_CONFIG_HAVE_PTHREAD
+
+/* Defined if pthread resolver is the default one. */
+#undef GG_CONFIG_PTHREAD_DEFAULT 
+
+/* Defined if this machine has C99-compiliant vsnprintf(). */
+#undef GG_CONFIG_HAVE_C99_VSNPRINTF
+
+/* Defined if this machine has va_copy(). */
+#undef GG_CONFIG_HAVE_VA_COPY
+
+/* Defined if this machine has __va_copy(). */
+#undef GG_CONFIG_HAVE___VA_COPY
+
+/* Defined if this machine supports long long. */
+#undef GG_CONFIG_HAVE_LONG_LONG
+
+/* Defined if libgadu was compiled and linked with TLS support. */
+#undef GG_CONFIG_HAVE_OPENSSL
+
+/* Defined if uintX_t types are defined in <stdint.h>. */
+#undef GG_CONFIG_HAVE_STDINT_H
+
+/* Defined if uintX_t types are defined in <inttypes.h>. */
+#undef GG_CONFIG_HAVE_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/inttypes.h>. */
+#undef GG_CONFIG_HAVE_SYS_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/int_types.h>. */
+#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+
+/* Defined if uintX_t types are defined in <sys/types.h>. */
+#undef GG_CONFIG_HAVE_SYS_TYPES_H
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #include <openssl/ssl.h>
 #endif
 
-/*
- * typedef uin_t
- *
- * typ reprezentuj?cy numer osoby.
+#ifdef GG_CONFIG_HAVE_STDINT_H
+#include <stdint.h>
+#else
+#  ifdef GG_CONFIG_HAVE_INTTYPES_H
+#  include <inttypes.h>
+#  else
+#    ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H
+#    include <sys/inttypes.h>
+#    else
+#      ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+#      include <sys/int_types.h>
+#      else
+#        ifdef GG_CONFIG_HAVE_SYS_TYPES_H
+#        include <sys/types.h>
+#        else
+
+#ifndef __AC_STDINT_H
+#define __AC_STDINT_H
+
+/* ISO C 9X: 7.18 Integer types <stdint.h> */
+
+typedef unsigned char   uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int   uint32_t;
+
+#ifndef __CYGWIN__
+#define __int8_t_defined
+typedef   signed char    int8_t;
+typedef   signed short  int16_t;
+typedef   signed int    int32_t;
+#endif
+
+#endif /* __AC_STDINT_H */
+
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/** \endcond */
+
+/**
+ * Numer Gadu-Gadu.
  */
 typedef uint32_t uin_t;
 
-/*
- * og? struktura opisuj?ca r? sesje. przydatna w klientach.
+/**
+ * Identyfikator po??czenia bezpo?redniego Gadu-Gadu 7.x.
  */
+typedef struct {
+	uint8_t id[8];
+} gg_dcc7_id_t;
+
+/**
+ * Makro deklaruj?ce pola wsp?lne dla struktur sesji.
+ */
 #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?? 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 */
+	int fd;			/**< Obserwowany deskryptor */ \
+	int check;		/**< Informacja o ??daniu odczytu/zapisu (patrz \ref gg_check_t) */ \
+	int state;		/**< Aktualny stan po??czenia (patrz \ref gg_state_t) */ \
+	int error;		/**< Kod b??du dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \
+	int type;		/**< Rodzaj sesji (patrz \ref gg_session_t) */ \
+	int id;			/**< Identyfikator sesji */ \
+	int timeout;		/**< Czas pozosta?y do zako?czenia stanu */ \
+	int (*callback)(x*); 	/**< Funkcja zwrotna */ \
+	void (*destroy)(x*); 	/**< Funkcja zwalniania zasob?w */
 
+/**
+ * Struktura wsp?lna dla wszystkich sesji i po??cze?. Pozwala na proste
+ * rzutowanie niezale?ne od rodzaju po??czenia.
+ */
 struct gg_common {
 	gg_common_head(struct gg_common)
 };
 
 struct gg_image_queue;
 
-/*
- * struct gg_session
+struct gg_dcc7;
+
+/**
+ * Spos?b rozwi?zywania nazw serwer?w.
+ */
+typedef enum {
+	GG_RESOLVER_DEFAULT = 0,	/**< Domy?lny spos?b rozwi?zywania nazw (jeden z poni?szych) */
+	GG_RESOLVER_FORK,		/**< Rozwi?zywanie nazw bazuj?ce na procesach */
+	GG_RESOLVER_PTHREAD,		/**< Rozwi?zywanie nazw bazuj?ce na w?tkach */
+	GG_RESOLVER_CUSTOM,		/**< Funkcje rozwi?zywania nazw dostarczone przed aplikacj? */
+	GG_RESOLVER_INVALID = -1	/**< Nieprawid?owy spos?b rozwi?zywania nazw (wynik \c gg_session_get_resolver) */
+} gg_resolver_t;
+
+/**
+ * Rodzaj kodowania znak?w.
+ */
+typedef enum {
+	GG_ENCODING_CP1250 = 0,		/**< Kodowanie CP1250 */
+	GG_ENCODING_UTF8,		/**< Kodowanie UTF-8 */
+	GG_ENCODING_INVALID = -1	/**< Nieprawid?owe kodowanie */
+} gg_encoding_t;
+
+/**
+ * Sesja Gadu-Gadu.
  *
- * struktura opisuj?ca dan? sesj?tworzona przez gg_login(), zwalniana
- * przez gg_free_session().
+ * Tworzona przez funkcj? \c gg_login(), zwalniana przez \c gg_free_session().
+ *
+ * \ingroup login
  */
 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? si??czymy */
-	int seq;        	/* numer sekwencyjny ostatniej wiadomo?ci */
-	int last_pong;  	/* czas otrzymania ostatniego ping/pong */
-	int last_event;		/* czas otrzymania ostatniego pakietu */
+	int async;      	/**< Flaga po??czenia asynchronicznego */
+	int pid;        	/**< Numer procesu rozwi?zuj?cego nazw? serwera */
+	int port;       	/**< Port serwera */
+	int seq;        	/**< Numer sekwencyjny ostatniej wiadomo?ci */
+	int last_pong;  	/**< Czas otrzymania ostatniej ramki utrzymaniowej */
+	int last_event;		/**< Czas otrzymania ostatniego pakietu */
 
-	struct gg_event *event;	/* zdarzenie po ->callback() */
+	struct gg_event *event;	/**< Zdarzenie po wywo?aniu \c callback */
 
-	uint32_t proxy_addr;	/* adres proxy, keszowany */
-	uint16_t proxy_port;	/* port proxy */
+	uint32_t proxy_addr;	/**< Adres serwera po?rednicz?cego */
+	uint16_t proxy_port;	/**< Port serwera po?rednicz?cego */
 
-	uint32_t hub_addr;	/* adres huba po resolvni?u */
-	uint32_t server_addr;	/* adres serwera, od huba */
+	uint32_t hub_addr;	/**< Adres huba po rozwi?zaniu nazwy */
+	uint32_t server_addr;	/**< Adres serwera otrzymany od huba */
 
-	uint32_t client_addr;	/* adres klienta */
-	uint16_t client_port;	/* port, na kt? klient s?ucha */
+	uint32_t client_addr;	/**< Adres gniazda dla po??cze? bezpo?rednich do wersji Gadu-Gadu 6.x */
+	uint16_t client_port;	/**< Port gniazda dla po??cze? bezpo?rednich do wersji Gadu-Gadu 6.x */
 
-	uint32_t external_addr;	/* adres zewnetrzny klienta */
-	uint16_t external_port;	/* port zewnetrzny klienta */
+	uint32_t external_addr;	/**< Publiczny adres dla po??cze? bezpo?rednich do wersji Gadu-Gadu 6.x */
+	uint16_t external_port;	/**< Publiczny port dla po??cze? bezpo?rednich do wersji Gadu-Gadu 6.x */
 
-	uin_t uin;		/* numerek klienta */
-	char *password;		/* i jego has?o. zwalniane automagicznie */
+	uin_t uin;		/**< W?asny numer Gadu-Gadu */
+	char *password;		/**< Has?o (zwalniane po u?yciu) */
 
-	int initial_status;	/* pocz?tkowy stan klienta */
-	int status;		/* aktualny stan klienta */
+	int initial_status;	/**< Pocz?tkowy status */
+	int status;		/**< Aktualny status */
 
-	char *recv_buf;		/* bufor na otrzymywane pakiety */
-	int recv_done;		/* ile ju? wczytano do bufora */
-	int recv_left;		/* i ile jeszcze trzeba wczyta?/
+	char *recv_buf;		/**< Bufor na odbierany pakiety */
+	int recv_done;		/**< Liczba wczytanych bajt?w pakietu */
+	int recv_left;		/**< Liczba pozosta?ych do wczytania bajt?w pakietu */
 
-	int protocol_version;	/* wersja u?ywanego protoko?u */
-	char *client_version;	/* wersja u?ywanego klienta */
-	int last_sysmsg;	/* ostatnia wiadomo??ystemowa */
+	int protocol_version;	/**< Wersja protoko?u (bez flag) */
+	char *client_version;	/**< Wersja klienta */
+	int last_sysmsg;	/**< Numer ostatniej wiadomo?ci systemowej */
 
-	char *initial_descr;	/* pocz?tkowy opis stanu klienta */
+	char *initial_descr;	/**< Pocz?tkowy opis statusu */
 
-	void *resolver;		/* wska?nik na informacje resolvera */
+	void *resolver;		/**< Dane prywatne procesu lub w?tku rozwi?zuj?cego nazw? serwera */
 
-	char *header_buf;	/* bufor na pocz?tek nag?? */
-	unsigned int header_done;/* ile ju? mamy */
+	char *header_buf;	/**< Bufor na pocz?tek nag??wka pakietu */
+	unsigned int header_done;	/**< Liczba wczytanych bajt?w nag??wka pakietu */
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-	SSL *ssl;		/* sesja TLS */
-	SSL_CTX *ssl_ctx;	/* kontekst sesji? */
+#ifdef GG_CONFIG_HAVE_OPENSSL
+	SSL *ssl;		/**< Struktura TLS */
+	SSL_CTX *ssl_ctx;	/**< Kontekst sesji TLS */
 #else
-	void *ssl;		/* zachowujemy ABI */
-	void *ssl_ctx;
+	void *ssl;		/**< Struktura TLS */
+	void *ssl_ctx;		/**< Kontekst sesji TLS */
 #endif
 
-	int image_size;		/* maksymalny rozmiar obrazk? KiB */
+	int image_size;		/**< Maksymalny rozmiar obs?ugiwanych obrazk?w w KiB */
 
-	char *userlist_reply;	/* fragment odpowiedzi listy kontakt?/
+	char *userlist_reply;	/**< Bufor z odbieran? list? kontakt?w */
 
-	int userlist_blocks;	/* na ile kawa?k?odzielono list?ontakt?/
+	int userlist_blocks;	/**< Liczba cz??ci listy kontakt?w */
 
-	struct gg_image_queue *images;	/* aktualnie wczytywane obrazki */
+	struct gg_image_queue *images;	/**< Lista wczytywanych obrazk?w */
+
+	int hash_type;		/**< Rodzaj funkcji skr?tu has?a (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
+
+	char *send_buf;		/**< Bufor z danymi do wys?ania */
+	int send_left;		/**< Liczba bajt?w do wys?ania */
+
+	struct gg_dcc7 *dcc7_list;	/**< Lista po??cze? bezpo?rednich skojarzonych z sesj? */
+	
+	int soft_timeout;	/**< Flaga m?wi?ca, ?e po przekroczeniu \c timeout nale?y wywo?a? \c gg_watch_fd() */
+
+	int protocol_flags;	/**< Flagi protoko?u */
+
+	gg_encoding_t encoding;		/**< Rodzaj kodowania znak?w */
+
+	gg_resolver_t resolver_type;	/**< Spos?b rozwi?zywania nazw serwer?w */
+	int (*resolver_start)(int *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynaj?ca rozwi?zywanie nazwy */
+	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniaj?ca zasoby po rozwi?zaniu nazwy */
+
+	int protocol_features;	/**< Opcje protoko?u */
 };
 
-/*
- * struct gg_http
+/**
+ * Po??czenie HTTP.
  *
- * og? struktura opisuj?ca stan wszystkich operacji HTTP. tworzona
- * przez gg_http_connect(), zwalniana przez gg_http_free().
+ * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free().
+ *
+ * \ingroup http
  */
 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? si??czymy */
+	int async;              /**< Flaga po??czenia asynchronicznego */
+	int pid;                /**< Identyfikator procesu rozwi?zuj?cego nazw? serwera */
+	int port;               /**< Port */
 
-	char *query;            /* bufor zapytania http */
-	char *header;           /* bufor nag?? */
-	int header_size;        /* rozmiar wczytanego nag?? */
-	char *body;             /* bufor otrzymanych informacji */
-	unsigned int body_size; /* oczekiwana ilo??nformacji */
+	char *query;            /**< Zapytanie HTTP */
+	char *header;           /**< Odebrany nag??wek */
+	int header_size;        /**< Rozmiar wczytanego nag??wka */
+	char *body;             /**< Odebrana strona */
+	unsigned int body_size; /**< Rozmiar strony */
 
-	void *data;             /* dane danej operacji http */
+	void *data;             /**< Dane prywatne us?ugi HTTP */
 
-	char *user_data;	/* dane u?ytkownika, nie s? zwalniane przez gg_http_free() */
+	char *user_data;	/**< Dane prywatne u?ytkownika (nie s? zwalniane) */
 
-	void *resolver;		/* wska?nik na informacje resolvera */
+	void *resolver;		/**< Dane prywatne procesu lub w?tku rozwi?zuj?cego nazw? */
 
-	unsigned int body_done;	/* ile ju? tre?ci odebrano? */
+	unsigned int body_done;	/**< Liczba odebranych bajt?w strony */
+
+	gg_resolver_t resolver_type;	/**< Spos?b rozwi?zywania nazw serwer?w */
+	int (*resolver_start)(int *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynaj?ca rozwi?zywanie nazwy */
+	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniaj?ca zasoby po rozwi?zaniu nazwy */
 };
 
+/** \cond ignore */
+
 #ifdef __GNUC__
 #define GG_PACKED __attribute__ ((packed))
+#ifndef GG_IGNORE_DEPRECATED
+#define GG_DEPRECATED __attribute__ ((deprecated))
 #else
+#define GG_DEPRECATED
+#endif
+#else
 #define GG_PACKED
+#define GG_DEPRECATED
 #endif
 
-#define GG_MAX_PATH 276
+/** \endcond */
 
-/*
- * struct gg_file_info
+#define GG_MAX_PATH 276		/**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
+
+/**
+ * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
  *
- * odpowiednik windowsowej struktury WIN32_FIND_DATA niezb?ej przy
- * wysy?aniu plik?+ * Wykorzystywana przy po??czeniach bezpo?rednich do wersji Gadu-Gadu 6.x.
  */
 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;
+	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 */
+} /** \cond ignore */ GG_PACKED /** \endcond */;
 
-/*
- * struct gg_dcc
+/**
+ * Po??czenie bezpo?rednie do wersji Gadu-Gadu 6.x.
  *
- * struktura opisuj?ca nas?uchuj?ce gniazdo po??cze??y klientami.
- * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free().
+ * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(),
+ * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez
+ * \c gg_dcc_free().
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc {
 	gg_common_head(struct gg_dcc)
 
-	struct gg_event *event;	/* opis zdarzenia */
+	struct gg_event *event;	/**< Zdarzenie po wywo?aniu \c callback */
 
-	int active;		/* czy to my si??czymy? */
-	int port;		/* port, na kt? 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 */
+	int active;		/**< Flaga po??czenia aktywnego (nieu?ywana) */
+	int port;		/**< Port gniazda nas?uchuj?cego */
+	uin_t uin;		/**< W?asny numer Gadu-Gadu */
+	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony po??czenia */
+	int file_fd;		/**< deskryptor pliku */
+	unsigned int offset;	/**< Po?o?enie w pliku */
+	unsigned int chunk_size;
+				/**< Rozmiar kawa?ka pliku */
+	unsigned int chunk_offset;
+				/**< Po?o?enie w aktualnym kawa?ku pliku */
 	struct gg_file_info file_info;
-				/* informacje o pliku */
-	int established;	/* po??czenie ustanowione */
-	uint8_t *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 */
+				/**< Informacje o pliku */
+	int established;	/**< Flaga ustanowienia po??czenia */
+	char *voice_buf;	/**< Bufor na pakiet po??czenia g?osowego */
+	int incoming;		/**< Flaga po??czenia przychodz?cego */
+	char *chunk_buf;	/**< Bufor na fragment danych */
+	uint32_t remote_addr;	/**< Adres drugiej strony */
+	uint16_t remote_port;	/**< Port drugiej strony */
 };
 
-/*
- * enum gg_session_t
+#define GG_DCC7_HASH_LEN	20	/**< Maksymalny rozmiar skr?tu pliku w po??czeniach bezpo?renich */
+#define GG_DCC7_FILENAME_LEN	255	/**< Maksymalny rozmiar nazwy pliku w po??czeniach bezpo?rednich */
+#define GG_DCC7_INFO_LEN	64	/**< Maksymalny rozmiar informacji o po??czeniach bezpo?rednich */
+
+/**
+ * Po??czenie bezpo?rednie od wersji Gadu-Gadu 7.x.
  *
- * rodzaje sesji.
+ * \ingroup dcc7
  */
+struct gg_dcc7 {
+	gg_common_head(struct gg_dcc7)
+
+	gg_dcc7_id_t cid;	/**< Identyfikator po??czenia */
+
+	struct gg_event *event;	/**< Struktura zdarzenia */
+
+	uin_t uin;		/**< W?asny numer Gadu-Gadu */
+	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony po??czenia */
+
+	int file_fd;		/**< Deskryptor przesy?anego pliku */
+	unsigned int offset;	/**< Aktualne po?o?enie w przesy?anym pliku */
+	unsigned int size;	/**< Rozmiar przesy?anego pliku */
+	unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
+				/**< Nazwa przesy?anego pliku */
+	unsigned char hash[GG_DCC7_HASH_LEN];
+				/**< Skr?t SHA1 przesy?anego pliku */
+
+	int dcc_type;		/**< Rodzaj po??czenia bezpo?redniego */
+	int established;	/**< Flaga ustanowienia po??czenia */
+	int incoming;		/**< Flaga po??czenia przychodz?cego */
+	int reverse;		/**< Flaga po??czenia zwrotnego */
+
+	uint32_t local_addr;	/**< Adres lokalny */
+	uint16_t local_port;	/**< Port lokalny */
+
+	uint32_t remote_addr;	/**< Adres drugiej strony */
+	uint16_t remote_port;	/**< Port drugiej strony */
+
+	struct gg_session *sess;
+				/**< Sesja do kt?rej przypisano po??czenie */
+	struct gg_dcc7 *next;	/**< Nast?pne po??czenie w li?cie */
+
+	int soft_timeout;	/**< Flaga m?wi?ca, ?e po przekroczeniu \c timeout nale?y wywo?a? \c gg_dcc7_watch_fd() */
+	int seek;		/**< Flaga m?wi?ca, ?e mo?na zmienia? po?o?enie w wysy?anym pliku */
+};
+
+/**
+ * Rodzaj sesji.
+ */
 enum gg_session_t {
-	GG_SESSION_GG = 1,	/* po??czenie z serwerem gg */
-	GG_SESSION_HTTP,	/* og? 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? 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_GG = 1,	/**< Po??czenie z serwerem Gadu-Gadu */
+	GG_SESSION_HTTP,	/**< Po??czenie HTTP */
+	GG_SESSION_SEARCH,	/**< Wyszukiwanie w katalogu publicznym (nieaktualne) */
+	GG_SESSION_REGISTER,	/**< Rejestracja nowego konta */
+	GG_SESSION_REMIND,	/**< Przypominanie has?a */
+	GG_SESSION_PASSWD,	/**< Zmiana has?a */
+	GG_SESSION_CHANGE,	/**< Zmiana informacji w katalogu publicznym (nieaktualne) */
+	GG_SESSION_DCC,		/**< Po??czenie bezpo?rednie (do wersji 6.x) */
+	GG_SESSION_DCC_SOCKET,	/**< Gniazdo nas?uchuj?ce (do wersji 6.x) */
+	GG_SESSION_DCC_SEND,	/**< Wysy?anie pliku (do wersji 6.x) */
+	GG_SESSION_DCC_GET,	/**< Odbieranie pliku (do wersji 6.x) */
+	GG_SESSION_DCC_VOICE,	/**< Rozmowa g?osowa (do wersji 6.x) */
+	GG_SESSION_USERLIST_GET,	/**< Import listy kontakt?w z serwera (nieaktualne) */
+	GG_SESSION_USERLIST_PUT,	/**< Eksport listy kontakt?w do serwera (nieaktualne) */
+	GG_SESSION_UNREGISTER,	/**< Usuwanie konta */
+	GG_SESSION_USERLIST_REMOVE,	/**< Usuwanie listy kontakt?w z serwera (nieaktualne) */
+	GG_SESSION_TOKEN,	/**< Pobieranie tokenu */
+	GG_SESSION_DCC7_SOCKET,	/**< Gniazdo nas?uchuj?ce (od wersji 7.x) */
+	GG_SESSION_DCC7_SEND,	/**< Wysy?anie pliku (od wersji 7.x) */
+	GG_SESSION_DCC7_GET,	/**< Odbieranie pliku (od wersji 7.x) */
+	GG_SESSION_DCC7_VOICE,	/**< Rozmowa g?osowa (od wersji 7.x) */
 
-	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. */
+	GG_SESSION_USER0 = 256,	/**< Rodzaj zadeklarowany dla u?ytkownika */
+	GG_SESSION_USER1,	/**< Rodzaj zadeklarowany dla u?ytkownika */
+	GG_SESSION_USER2,	/**< Rodzaj zadeklarowany dla u?ytkownika */
+	GG_SESSION_USER3,	/**< Rodzaj zadeklarowany dla u?ytkownika */
+	GG_SESSION_USER4,	/**< Rodzaj zadeklarowany dla u?ytkownika */
+	GG_SESSION_USER5,	/**< Rodzaj zadeklarowany dla u?ytkownika */
+	GG_SESSION_USER6,	/**< Rodzaj zadeklarowany dla u?ytkownika */
+	GG_SESSION_USER7	/**< Rodzaj zadeklarowany dla u?ytkownika */
 };
 
-/*
- * enum gg_state_t
- *
- * opisuje stan asynchronicznej maszyny.
+/**
+ * Aktualny stan sesji.
  */
 enum gg_state_t {
-		/* wsp? */
-	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 */
+	/* wsp?lne */
+	GG_STATE_IDLE = 0,		/**< Nie dzieje si? nic */
+	GG_STATE_RESOLVING,             /**< Oczekiwanie na rozwi?zanie nazwy serwera */
+	GG_STATE_CONNECTING,            /**< Oczekiwanie na po??czenie */
+	GG_STATE_READING_DATA,		/**< Oczekiwanie na dane */
+	GG_STATE_ERROR,			/**< Kod b??du w polu \c 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_session */
+	GG_STATE_CONNECTING_HUB,	/**< Oczekiwanie na po??czenie z hubem */
+	GG_STATE_CONNECTING_GG,         /**< Oczekiwanie na po??czenie z serwerem */
+	GG_STATE_READING_KEY,           /**< Oczekiwanie na klucz */
+	GG_STATE_READING_REPLY,         /**< Oczekiwanie na odpowied? serwera */
+	GG_STATE_CONNECTED,             /**< Po??czono z serwerem */
 
-		/* gg_http */
-	GG_STATE_SENDING_QUERY,		/* wysy?a zapytanie http */
-	GG_STATE_READING_HEADER,	/* czeka na nag?? http */
-	GG_STATE_PARSING,               /* przetwarza dane */
-	GG_STATE_DONE,                  /* sko?? */
+	/* gg_http */
+	GG_STATE_SENDING_QUERY,		/**< Wys?ano zapytanie HTTP */
+	GG_STATE_READING_HEADER,	/**< Oczekiwanie na nag??wek HTTP */
+	GG_STATE_PARSING,               /**< Przetwarzanie danych */
+	GG_STATE_DONE,                  /**< Po??czenie zako?czone */
 
-		/* gg_dcc */
-	GG_STATE_LISTENING,		/* czeka na po??czenia */
+	/* 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?in */
-	GG_STATE_SENDING_ACK,		/* wysy?a potwierdzenie dcc */
+	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_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_SENDING_FILE_ACK,	/* wysy?a potwierdzenie pliku */
 	GG_STATE_READING_FILE_ACK,	/* czeka na potwierdzenie pliku */
-	GG_STATE_SENDING_FILE_HEADER,	/* wysy?a nag?? pliku */
-	GG_STATE_READING_FILE_HEADER,	/* czeka na nag?? */
+	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_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 */
+	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 */
+	GG_STATE_TLS_NEGOTIATION,	/**< Negocjacja po??czenia szyfrowanego */
+
+	GG_STATE_REQUESTING_ID,		/**< Oczekiwanie na nadanie identyfikatora po??czenia bezpo?redniego */
+	GG_STATE_WAITING_FOR_ACCEPT,	/**< Oczekiwanie na potwierdzenie lub odrzucenie po??czenia bezpo?redniego */
+	GG_STATE_WAITING_FOR_INFO,	/**< Oczekiwanie na informacje o po??czeniu bezpo?rednim */
+
+	GG_STATE_READING_ID,		/**< Odebranie identyfikatora po??czenia bezpo?redniego */
+	GG_STATE_SENDING_ID		/**< Wys?ano identyfikatora po??czenia bezpo?redniego */
 };
 
-/*
- * enum gg_check_t
+/**
+ * Informacja o tym, czy biblioteka chce zapisywa? i/lub czyta?
+ * z deskryptora. Maska bitowa.
  *
- * informuje, co proces klienta powinien sprawdzi?a deskryptorze danego
- * po??czenia.
+ * \ingroup events
  */
 enum gg_check_t {
-	GG_CHECK_NONE = 0,		/* nic. nie powinno wyst?pi?/
-	GG_CHECK_WRITE = 1,		/* sprawdzamy mo?liwo??apisu */
-	GG_CHECK_READ = 2		/* sprawdzamy mo?liwo??dczytu */
+	GG_CHECK_NONE = 0,		/**< Nie sprawdzaj niczego */
+	GG_CHECK_WRITE = 1,		/**< Sprawd? mo?liwo?? zapisu */
+	GG_CHECK_READ = 2		/**< Sprawd? mo?liwo?? odczytu */
 };
 
-/*
- * struct gg_login_params
+/**
+ * Parametry po??czenia z serwerem Gadu-Gadu. Parametry zosta?y przeniesione
+ * do struktury, by unikn?? zmian API po rozszerzeniu protoko?u i dodaniu
+ * kolejnych opcji po??czenia. Cz??? parametr?w, kt?re nie s? ju? aktualne
+ * lub nie maj? znaczenia, zosta?a usuni?ta z dokumentacji.
  *
- * parametry gg_login(). przeniesiono do struktury, ?eby unikn??roblem? * z ci?g?ymi zmianami API, gdy dodano co? nowego do protoko?u.
+ * \ingroup login
  */
 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? */
-	int last_sysmsg;		/* ostatnia wiadomo??ystemowa */
-	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?lienta era omnix? */
+	uin_t uin;			/**< Numer Gadu-Gadu */
+	char *password;			/**< Has?o */
+	int async;			/**< Flaga asynchronicznego po??czenia (domy?lnie nie) */
+	int status;			/**< Pocz?tkowy status u?ytkownika (domy?lnie \c GG_STATUS_AVAIL) */
+	char *status_descr;		/**< Pocz?tkowy opis u?ytkownika (domy?lnie brak) */
+	uint32_t server_addr;		/**< Adres serwera Gadu-Gadu (domy?lnie pobierany automatycznie) */
+	uint16_t server_port;		/**< Port serwera Gadu-Gadu (domy?lnie pobierany automatycznie) */
+#ifndef DOXYGEN
+	uint32_t client_addr;		/**< Adres po??cze? bezpo?rednich (nieaktualne) */
+	uint16_t client_port;		/**< Port po??cze? bezpo?rednich (nieaktualne) */
+#endif
+	int protocol_version;		/**< Wersja protoko?u wysy?ana do serwera (domy?lnie najnowsza obs?ugiwana) */
+	char *client_version;		/**< Wersja klienta wysy?ana do serwera (domy?lnie najnowsza znana) */
+	int has_audio;			/**< Flaga obs?ugi po??cze? g?osowych */
+	int last_sysmsg;		/**< Numer ostatnio odebranej wiadomo?ci systemowej */
+	uint32_t external_addr;		/**< Adres publiczny dla po??cze? bezpo?rednich (6.x) */
+	uint16_t external_port;		/**< Port publiczny dla po??cze? bezpo?rednich (6.x) */
+#ifndef DOXYGEN
+	int tls;			/**< Flaga po??czenia szyfrowanego (nieaktualna) */
+#endif
+	int image_size;			/**< Maksymalny rozmiar obs?ugiwanych obrazk?w w kilobajtach */
+#ifndef DOXYGEN
+	int era_omnix;			/**< Flaga udawania klienta Era Omnix (nieaktualna) */
+#endif
+	int hash_type;			/**< Rodzaj skr?tu has?a (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domy?lnie SHA1) */
+	gg_encoding_t encoding;		/**< Rodzaj kodowania u?ywanego w sesji (domy?lnie CP1250) */
+	gg_resolver_t resolver;		/**< Spos?b rozwi?zywania nazw (patrz \ref build-resolver) */
+	int protocol_features;		/**< Opcje protoko?u (flagi GG_FEATURE_*). */
 
-	char dummy[6 * sizeof(int)];	/* miejsce na kolejnych 6 zmiennych,
-					 * ?eby z dodaniem parametru nie
-					 * zmienia? si?ozmiar struktury */
+#ifndef DOXYGEN
+	char dummy[2 * sizeof(int)];	/**< \internal Miejsce na kilka kolejnych
+					  parametr?w, ?eby wraz z dodawaniem kolejnych
+					  parametr?w nie zmienia? si? rozmiar struktury */
+#endif
+
 };
 
 struct gg_session *gg_login(const struct gg_login_params *p);
@@ -367,232 +597,337 @@ int gg_image_request(struct gg_session *
 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 unsigned char *image, int size);
+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 */
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type);
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs);
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
 
-	struct gg_image_queue *next;	/* nast?y na li?cie */
-};
+int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type);
+gg_resolver_t gg_http_get_resolver(struct gg_http *gh);
+int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
 
-/*
- * enum gg_event_t
+int gg_global_set_resolver(gg_resolver_t type);
+gg_resolver_t gg_global_get_resolver(void);
+int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
+/**
+ * Rodzaj zdarzenia.
  *
- * rodzaje zdarze? * \ingroup events
  */
 enum gg_event_t {
-	GG_EVENT_NONE = 0,		/* nic si?ie wydarzy?o */
-	GG_EVENT_MSG,			/* otrzymano wiadomo??/
-	GG_EVENT_NOTIFY,		/* kto? si?ojawi? */
-	GG_EVENT_NOTIFY_DESCR,		/* kto? si?ojawi? 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?ie uda?o */
-	GG_EVENT_CONN_SUCCESS,		/* po??czenie si?owiod?o */
-	GG_EVENT_DISCONNECT,		/* serwer zrywa po??czenie */
+	GG_EVENT_NONE = 0,		/**< Nie wydarzy?o si? nic wartego uwagi */
+	GG_EVENT_MSG,			/**< \brief Otrzymano wiadomo??. Przekazuje r?wnie? wiadomo?ci systemowe od numeru 0. */
+	GG_EVENT_NOTIFY,		/**< \brief Informacja o statusach os?b z listy kontakt?w (przed 6.0). Zdarzenie nale?y obs?ugiwa?, je?li planuje si? u?ywa? protoko?u w wersji starszej ni? domy?lna. */
+	GG_EVENT_NOTIFY_DESCR,		/**< \brief Informacja o statusie opisowym osoby z listy kontakt?w (przed 6.0). Zdarzenie nale?y obs?ugiwa?, je?li planuje si? u?ywa? protoko?u w wersji starszej ni? domy?lna. */
+	GG_EVENT_STATUS,		/**< \brief Zmiana statusu osoby z listy kontakt?w (przed 6.0). Zdarzenie nale?y obs?ugiwa?, je?li planuje si? u?ywa? protoko?u w wersji starszej ni? domy?lna. */
+	GG_EVENT_ACK,			/**< Potwierdzenie dor?czenia wiadomo?ci */
+	GG_EVENT_PONG,			/**< \brief Utrzymanie po??czenia. Obecnie serwer nie wysy?a ju? do klienta ramek utrzymania po??czenia, polega wy??cznie na wysy?aniu ramek przez klienta. */
+	GG_EVENT_CONN_FAILED,		/**< \brief Nie uda?o si? po??czy? */
+	GG_EVENT_CONN_SUCCESS,		/**< \brief Po??czono z serwerem. Pierwsz? rzecz?, jak? nale?y zrobi? jest wys?anie listy kontakt?w. */
+	GG_EVENT_DISCONNECT,		/**< \brief Serwer zrywa po??czenie. Zdarza si?, gdy r?wnolegle do serwera pod??czy si? druga sesja i trzeba zerwa? po??czenie z pierwsz?. */
 
-	GG_EVENT_DCC_NEW,		/* nowe po??czenie mi?y klientami */
-	GG_EVENT_DCC_ERROR,		/* b??d po??czenia mi?y klientami */
-	GG_EVENT_DCC_DONE,		/* zako?no po??czenie */
-	GG_EVENT_DCC_CLIENT_ACCEPT,	/* moment akceptacji klienta */
-	GG_EVENT_DCC_CALLBACK,		/* klient si?o??czy? na ??danie */
-	GG_EVENT_DCC_NEED_FILE_INFO,	/* nale?y wype?ni?ile_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_DCC_NEW,		/**< Nowe po??czenie bezpo?rednie (6.x) */
+	GG_EVENT_DCC_ERROR,		/**< B??d po??czenia bezpo?redniego (6.x) */
+	GG_EVENT_DCC_DONE,		/**< Zako?czono po??czenie bezpo?rednie (6.x) */
+	GG_EVENT_DCC_CLIENT_ACCEPT,	/**< Moment akceptacji klienta w po??czeniu bezpo?rednim (6.x) */
+	GG_EVENT_DCC_CALLBACK,		/**< Zwrotne po??czenie bezpo?rednie (6.x) */
+	GG_EVENT_DCC_NEED_FILE_INFO,	/**< Nale?y wype?ni? \c file_info dla po??czenia bezpo?redniego (6.x) */
+	GG_EVENT_DCC_NEED_FILE_ACK,	/**< Czeka na potwierdzenie pliku w po??czeniu bezpo?rednim (6.x) */
+	GG_EVENT_DCC_NEED_VOICE_ACK,	/**< Czeka na potwierdzenie rozmowy w po??czeniu bezpo?rednim (6.x) */
+	GG_EVENT_DCC_VOICE_DATA, 	/**< Dane bezpo?redniego po??czenia g?osowego (6.x) */
 
-	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_PUBDIR50_SEARCH_REPLY,	/**< Odpowied? katalogu publicznego */
+	GG_EVENT_PUBDIR50_READ,		/**< Odczytano w?asne dane z katalogu publicznego */
+	GG_EVENT_PUBDIR50_WRITE,	/**< Zmieniono w?asne dane w katalogu publicznym */
 
-	GG_EVENT_STATUS60,		/* kto? zmieni? stan w GG 6.0 */
-	GG_EVENT_NOTIFY60,		/* kto? si?ojawi? w GG 6.0 */
-	GG_EVENT_USERLIST,		/* odpowied? listy kontakt? 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 */
+	GG_EVENT_STATUS60,		/**< Zmiana statusu osoby z listy kontakt?w */
+	GG_EVENT_NOTIFY60,		/**< Informacja o statusach os?b z listy kontakt?w */
+	GG_EVENT_USERLIST,		/**< Wynik importu lub eksportu listy kontakt?w */
+	GG_EVENT_IMAGE_REQUEST,		/**< ??danie przes?ania obrazka z wiadommo?ci */
+	GG_EVENT_IMAGE_REPLY,		/**< Przys?ano obrazek z wiadomo?ci */
+	GG_EVENT_DCC_ACK,		/**< Potwierdzenie transmisji w po??czeniu bezpo?rednim (6.x) */
+
+	GG_EVENT_DCC7_NEW,		/**< Nowe po??czenie bezpo?rednie (7.x) */
+	GG_EVENT_DCC7_ACCEPT,		/**< Zaakceptowano po??czenie bezpo?rednie (7.x), nowy deskryptor */
+	GG_EVENT_DCC7_REJECT,		/**< Odrzucono po??czenie bezpo?rednie (7.x) */
+	GG_EVENT_DCC7_CONNECTED,	/**< Zestawiono po??czenie bezpo?rednie (7.x), nowy deskryptor */
+	GG_EVENT_DCC7_ERROR,		/**< B??d po??czenia bezpo?redniego (7.x) */
+	GG_EVENT_DCC7_DONE,		/**< Zako?czono po??czenie bezpo?rednie (7.x) */
+	GG_EVENT_DCC7_PENDING,		/**< Trwa pr?ba po??czenia bezpo?redniego (7.x), nowy deskryptor */
+
+	GG_EVENT_XML_EVENT,		/**< Otrzymano komunikat systemowy (7.7) */
+	GG_EVENT_DISCONNECT_ACK,	/**< \brief Potwierdzenie zako?czenia sesji. Informuje o tym, ?e zmiana stanu na niedost?pny z opisem dotar?a do serwera i mo?na zako?czy? po??czenie TCP. */
 };
 
 #define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
 
-/*
- * enum gg_failure_t
- *
- * okre?la pow?ieudanego po??czenia.
+/**
+ * Pow?d nieudanego po??czenia.
  */
 enum gg_failure_t {
-	GG_FAILURE_RESOLVING = 1,	/* nie znaleziono serwera */
-	GG_FAILURE_CONNECTING,		/* nie mo?na si?o??czy?/
-	GG_FAILURE_INVALID,		/* serwer zwr? 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?maila */
+	GG_FAILURE_RESOLVING = 1,	/**< Nie znaleziono serwera */
+	GG_FAILURE_CONNECTING,		/**< B??d po??czenia */
+	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, 		/**< Nieu?ywane */
+	GG_FAILURE_TLS,			/**< B??d negocjacji szyfrowanego po??czenia */
+	GG_FAILURE_NEED_EMAIL, 		/**< Serwer roz??czy? nas z pro?b? o zmian? adresu e-mail */
+	GG_FAILURE_INTRUDER,		/**< Zbyt wiele pr?b po??czenia z nieprawid?owym has?em */
+	GG_FAILURE_UNAVAILABLE		/**< Serwery s? wy??czone */
 };
 
-/*
- * enum gg_error_t
+/**
+ * Kod b??du danej operacji.
  *
- * okre?la rodzaj b?? wywo?anego przez dan? operacj?nie zawiera
- * przesadnie szczeg?ych informacji o powodzie b??, by nie komplikowa? * obs?ugi b??w. je?li wymagana jest wi?za dok?adno??nale?y sprawdzi? * zawarto??miennej errno.
+ * Nie zawiera przesadnie szczeg??owych informacji o powodach b??d?w, by nie
+ * komplikowa? ich obs?ugi. Je?li wymagana jest wi?ksza dok?adno??, nale?y
+ * sprawdzi? zawarto?? zmiennej systemowej \c 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_RESOLVING = 1,		/**< Nie znaleziono hosta */
+	GG_ERROR_CONNECTING,		/**< B??d po??czenia */
+	GG_ERROR_READING,		/**< B??d odczytu/odbierania */
+	GG_ERROR_WRITING,		/**< B??d zapisu/wysy?ania */
 
-	GG_ERROR_DCC_HANDSHAKE,		/* b??d negocjacji */
-	GG_ERROR_DCC_FILE,		/* b??d odczytu/zapisu pliku */
-	GG_ERROR_DCC_EOF,		/* plik si?ko??? */
-	GG_ERROR_DCC_NET,		/* b??d wysy?ania/odbierania */
-	GG_ERROR_DCC_REFUSED 		/* po??czenie odrzucone przez usera */
+	GG_ERROR_DCC_HANDSHAKE,		/**< B??d negocjacji */
+	GG_ERROR_DCC_FILE,		/**< B??d odczytu/zapisu pliku */
+	GG_ERROR_DCC_EOF,		/**< Przedwczesny koniec pliku */
+	GG_ERROR_DCC_NET,		/**< B??d wysy?ania/odbierania */
+	GG_ERROR_DCC_REFUSED, 		/**< Po??czenie odrzucone */
+
+	GG_ERROR_DCC7_HANDSHAKE,	/**< B??d negocjacji */
+	GG_ERROR_DCC7_FILE,		/**< B??d odczytu/zapisu pliku */
+	GG_ERROR_DCC7_EOF,		/**< Przedwczesny koniec pliku */
+	GG_ERROR_DCC7_NET,		/**< B??d wysy?ania/odbierania */
+	GG_ERROR_DCC7_REFUSED 		/**< Po??czenie odrzucone */
 };
 
-/*
- * struktury dotycz?ce wyszukiwania w GG 5.0. NIE NALE?Y SI?DO NICH
- * ODWO?YWA?BEZPO?REDNIO! do dost? do nich s?u?? funkcje gg_pubdir50_*()
+/**
+ * Pole zapytania lub odpowiedzi katalogu publicznego.
  */
 struct gg_pubdir50_entry {
-	int num;
-	char *field;
-	char *value;
-};
+	int num;	/**< Numer wyniku */
+	char *field;	/**< Nazwa pola */
+	char *value;	/**< Warto?? pola */
+} /* GG_DEPRECATED */;
 
+/**
+ * Zapytanie lub odpowied? katalogu publicznego.
+ *
+ * Patrz \c gg_pubdir50_t.
+ */
 struct gg_pubdir50_s {
-	int count;
-	uin_t next;
-	int type;
-	uint32_t seq;
-	struct gg_pubdir50_entry *entries;
-	int entries_count;
-};
+	int count;	/**< Liczba wynik?w odpowiedzi */
+	uin_t next;	/**< Numer pocz?tkowy nast?pnego zapytania */
+	int type;	/**< Rodzaj zapytania */
+	uint32_t seq;	/**< Numer sekwencyjny */
+	struct gg_pubdir50_entry *entries;	/**< Pola zapytania lub odpowiedzi */
+	int entries_count;	/**< Liczba p?l */
+} /* GG_DEPRECATED */;
 
-/*
- * typedef gg_pubdir_50_t
+/**
+ * Zapytanie lub odpowied? katalogu publicznego.
  *
- * typ opisuj?cy zapytanie lub wynik zapytania katalogu publicznego
- * z protoko?u GG 5.0. nie nale?y si?dwo?ywa?ezpo?rednio do jego
- * p?- s?u?? do tego funkcje gg_pubdir50_*()
+ * Do p?l nie nale?y si? odwo?ywa? bezpo?rednio -- wszystkie niezb?dne
+ * informacje s? dost?pne za pomoc? funkcji \c 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()
+/**
+ * Opis zdarzenia \c GG_EVENT_MSG.
  */
-struct gg_event {
-	int type;	/* rodzaj zdarzenia -- gg_event_t */
-	union {		/* @event */
-		struct gg_notify_reply *notify;	/* informacje o li?cie kontakt?- GG_EVENT_NOTIFY */
+struct gg_event_msg {
+	uin_t sender;		/**< Numer nadawcy */
+	int msgclass;		/**< Klasa wiadomo?ci */
+	time_t time;		/**< Czas nadania */
+	unsigned char *message;	/**< Tre?? wiadomo?ci */
 
-		enum gg_failure_t failure;	/* b??d po??czenia -- GG_EVENT_FAILURE */
+	int recipients_count;	/**< Liczba odbiorc?w konferencji */
+	uin_t *recipients;	/**< Odbiorcy konferencji */
 
-		struct gg_dcc *dcc_new;		/* nowe po??czenie bezpo?rednie -- GG_EVENT_DCC_NEW */
+	int formats_length;	/**< D?ugo?? informacji o formatowaniu tekstu */
+	void *formats;		/**< Informacje o formatowaniu tekstu */
+	uint32_t seq;		/**< Numer sekwencyjny wiadomo?ci */
 
-		int dcc_error;			/* b??d po??czenia bezpo?redniego -- GG_EVENT_DCC_ERROR */
+	char *xhtml_message;	/**< Tre?? wiadomo?ci w formacie XHTML (mo?e by? r?wne \c NULL, je?li wiadomo?? nie zawiera tre?ci XHTML) */
+};
 
-		gg_pubdir50_t pubdir50;		/* wynik operacji zwi?zanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR.
+ */
+struct gg_event_notify_descr {
+	struct gg_notify_reply *notify;	/**< Informacje o li?cie kontakt?w */
+	char *descr;		/**< Opis status */
+};
 
-		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??iadomo?ci */
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS.
+ */
+struct gg_event_status {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	uint32_t status;	/**< Nowy status */
+	char *descr;		/**< Opis */
+};
 
-			int recipients_count;	/* ilo??dbiorc?onferencji */
-			uin_t *recipients;	/* odbiorcy konferencji */
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS60.
+ */
+struct gg_event_status60 {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	int status;		/**< Nowy status */
+	uint32_t remote_ip;	/**< Adres IP dla po??cze? bezpo?rednich */
+	uint16_t remote_port;	/**< Port dla po??cze? bezpo?rednich */
+	int version;		/**< Wersja protoko?u */
+	int image_size;		/**< Maksymalny rozmiar obs?ugiwanych obrazk?w w KiB */
+	char *descr;		/**< Opis statusu */
+	time_t time;		/**< Czas powrotu */
+};
 
-			int formats_length;	/* d?ugo??nformacji o formatowaniu tekstu */
-			void *formats;		/* informacje o formatowaniu tekstu */
-		} msg;
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60.
+ */
+struct gg_event_notify60 {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	int status;		/**< Nowy status */
+	uint32_t remote_ip;	/**< Adres IP dla po??cze? bezpo?rednich */
+	uint16_t remote_port;	/**< Port dla po??cze? bezpo?rednich */
+	int version;		/**< Wersja protoko?u */
+	int image_size;		/**< Maksymalny rozmiar obs?ugiwanych obrazk?w w KiB */
+	char *descr;		/**< Opis statusu */
+	time_t time;		/**< Czas powrotu */
+};
 
-		struct {			/* @notify_descr informacje o li?cie kontakt? opisami stanu -- GG_EVENT_NOTIFY_DESCR */
-			struct gg_notify_reply *notify;	/* informacje o li?cie kontakt?/
-			char *descr;		/* opis stanu */
-		} notify_descr;
+/**
+ * Opis zdarzenia \c GG_EVENT_ACK.
+ */
+struct gg_event_ack {
+	uin_t recipient;	/**< Numer odbiorcy */
+	int status;		/**< Status dor?czenia */
+	int seq;		/**< Numer sekwencyjny wiadomo?ci */
+};
 
-		struct {			/* @status zmiana stanu -- GG_EVENT_STATUS */
-			uin_t uin;		/* numer */
-			uint32_t status;	/* nowy stan */
-			char *descr;		/* opis stanu */
-		} status;
+/**
+ * Opis zdarzenia \c GG_EVENT_USERLIST.
+ */
+struct gg_event_userlist {
+	char type;		/**< Rodzaj odpowiedzi */
+	char *reply;		/**< Tre?? odpowiedzi */
+};
 
-		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;
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC_VOICE_DATA.
+ */
+struct gg_event_dcc_voice_data {
+	uint8_t *data;		/**< Dane d?wi?kowe */
+	int length;		/**< Rozmiar danych d?wi?kowych */
+};
 
-		struct {			/* @notify60 informacja o li?cie kontakt?- 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;
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST.
+ */
+struct gg_event_image_request {
+	uin_t sender;		/**< Nadawca ??dania */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 */
+};
 
-		struct {			/* @ack potwierdzenie wiadomo?ci -- GG_EVENT_ACK */
-			uin_t recipient;	/* numer odbiorcy */
-			int status;		/* stan dor?enia wiadomo?ci */
-			int seq;		/* numer sekwencyjny wiadomo?ci */
-		} ack;
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REPLY.
+ */
+struct gg_event_image_reply {
+	uin_t sender;		/**< Nadawca obrazka */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 */
+	char *filename;		/**< Nazwa pliku */
+	char *image;		/**< Bufor z obrazkiem */
+};
 
-		struct {			/* @dcc_voice_data otrzymano dane d?wi?we -- GG_EVENT_DCC_VOICE_DATA */
-			uint8_t *data;		/* dane d?wi?we */
-			int length;		/* ilo??anych d?wi?wych */
-		} dcc_voice_data;
+/**
+ * Opis zdarzenia \c GG_EVENT_XML_EVENT.
+ */
+struct gg_event_xml_event {
+	char *data;		/**< Bufor z komunikatem */
+};
 
-		struct {			/* @userlist odpowied? listy kontakt?erwera */
-			char type;		/* rodzaj odpowiedzi */
-			char *reply;		/* tre??dpowiedzi */
-		} userlist;
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
+ */
+struct gg_event_dcc7_connected {
+	struct gg_dcc7 *dcc7;	/**< Struktura po??czenia */
+	// XXX czy co? si? przyda?
+};
 
-		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;
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_REJECT.
+ */
+struct gg_event_dcc7_reject {
+	struct gg_dcc7 *dcc7;	/**< Struktura po??czenia */
+	int reason;		/**< pow?d odrzucenia */
+};
 
-		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;
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT.
+ */
+struct gg_event_dcc7_accept {
+	struct gg_dcc7 *dcc7;	/**< Struktura po??czenia */
+	int type;		/**< Spos?b po??czenia (P2P, przez serwer) */
+	uint32_t remote_ip;	/**< Adres zdalnego klienta */
+	uint16_t remote_port;	/**< Port zdalnego klienta */
 };
 
+/**
+ * Unia wszystkich zdarze? zwracanych przez funkcje \c gg_watch_fd(), 
+ * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd().
+ *
+ * \ingroup events
+ */
+union gg_event_union {
+	enum gg_failure_t failure;	/**< B??d po??czenia (\c GG_EVENT_CONN_FAILED) */
+	struct gg_notify_reply *notify;	/**< Zmiana statusu kontakt?w (\c GG_EVENT_NOTIFY) */
+	struct gg_event_notify_descr notify_descr;	/**< Zmiana statusu kontakt?w (\c GG_EVENT_NOTIFY_DESCR) */
+	struct gg_event_status status;	/**< Zmiana statusu kontakt?w (\c GG_EVENT_STATUS) */
+	struct gg_event_status60 status60;	/**< Zmiana statusu kontakt?w (\c GG_EVENT_STATUS60) */
+	struct gg_event_notify60 *notify60;	/**< Zmiana statusu kontakt?w (\c GG_EVENT_NOTIFY60) */
+	struct gg_event_msg msg;	/**< Otrzymano wiadomo?? (\c GG_EVENT_MSG) */
+	struct gg_event_ack ack;	/**< Potwierdzenie wiadomo?ci (\c GG_EVENT_ACK) */
+	struct gg_event_image_request image_request;	/**< ??danie wys?ania obrazka (\c GG_EVENT_IMAGE_REQUEST) */
+	struct gg_event_image_reply image_reply;	/**< Odpowied? z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */
+	struct gg_event_userlist userlist;	/**< Odpowied? listy kontakt?w (\c GG_EVENT_USERLIST) */
+	gg_pubdir50_t pubdir50;	/**< Odpowied? katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */
+	struct gg_event_xml_event xml_event;	/**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */
+	struct gg_dcc *dcc_new;	/**< Nowe po??czenie bezpo?rednie (\c GG_EVENT_DCC_NEW) */
+	enum gg_error_t dcc_error;	/**< B??d po??czenia bezpo?redniego (\c GG_EVENT_DCC_ERROR) */
+	struct gg_event_dcc_voice_data dcc_voice_data;	/**< Dane po??czenia g?osowego (\c GG_EVENT_DCC_VOICE_DATA) */
+	struct gg_dcc7 *dcc7_new;	/**< Nowe po??czenie bezpo?rednie (\c GG_EVENT_DCC7_NEW) */
+	enum gg_error_t dcc7_error;	/**< B??d po??czenia bezpo?redniego (\c GG_EVENT_DCC7_ERROR) */
+	struct gg_event_dcc7_connected dcc7_connected;	/**< Informacja o zestawieniu po??czenia bezpo?redniego (\c GG_EVENT_DCC7_CONNECTED) */
+	struct gg_event_dcc7_reject dcc7_reject;	/**< Odrzucono po??czenia bezpo?redniego (\c GG_EVENT_DCC7_REJECT) */
+	struct gg_event_dcc7_accept dcc7_accept;	/**< Zaakceptowano po??czenie bezpo?rednie (\c GG_EVENT_DCC7_ACCEPT) */
+};
+
+/**
+ * Opis zdarzenia.
+ *
+ * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd()
+ * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu nale?y zwolni?
+ * za pomoc? \c gg_event_free().
+ *
+ * \ingroup events
+ */
+struct gg_event {
+	int type;			/**< Rodzaj zdarzenia */
+	union gg_event_union event;	/**< Informacja o zdarzeniu */
+};
+
 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?- */
 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);
@@ -600,76 +935,11 @@ int gg_remove_notify(struct gg_session *
 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??mian ABI przy zmianach
- * w protokole.
- *
- * NIE NALE?Y SI?ODWO?YWA?DO P? 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);
@@ -681,6 +951,8 @@ void gg_pubdir50_free(gg_pubdir50_t res)
 uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
 void gg_pubdir50_free(gg_pubdir50_t res);
 
+#ifndef DOXYGEN
+
 #define GG_PUBDIR50_UIN "FmNumber"
 #define GG_PUBDIR50_STATUS "FmStatus"
 #define GG_PUBDIR50_FIRSTNAME "firstname"
@@ -699,111 +971,115 @@ void gg_pubdir50_free(gg_pubdir50_t res)
 #define GG_PUBDIR50_FAMILYNAME "familyname"
 #define GG_PUBDIR50_FAMILYCITY "familycity"
 
-int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length);
+#else
 
-/*
- * struct gg_pubdir
+/** 
+ * \ingroup pubdir50
  *
- * operacje na katalogu publicznym.
+ * Rodzaj pola zapytania.
  */
+enum {
+	GG_PUBDIR50_UIN,	/**< Numer Gadu-Gadu */
+	GG_PUBDIR50_STATUS,	/**< Status (tylko wynik wyszukiwania) */
+	GG_PUBDIR50_FIRSTNAME,	/**< Imi? */
+	GG_PUBDIR50_LASTNAME,	/**< Nazwisko */
+	GG_PUBDIR50_NICKNAME,	/**< Pseudonim */
+	GG_PUBDIR50_BIRTHYEAR,	/**< Rok urodzenia lub przedzia? lat oddzielony spacj? */
+	GG_PUBDIR50_CITY,	/**< Miejscowo?? */
+	GG_PUBDIR50_GENDER,	/**< P?e? */
+	GG_PUBDIR50_ACTIVE,	/**< Osoba dost?pna (tylko wyszukiwanie) */
+	GG_PUBDIR50_START,	/**< Numer pocz?tkowy wyszukiwania (tylko wyszukiwanie) */
+	GG_PUBDIR50_FAMILYNAME,	/**< Nazwisko rodowe (tylko wysy?anie informacji o sobie) */
+	GG_PUBDIR50_FAMILYCITY,	/**< Miejscowo?? pochodzenia (tylko wysy?anie informacji o sobie) */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Warto?? pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowoln? p?e?.
+ */
+enum {
+	GG_PUBDIR50_GENDER_FEMALE,	/**< Kobieta */
+	GG_PUBDIR50_GENDER_MALE,	/**< M??czyzna */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Warto?? pola GG_PUBDIR50_GENDER przy wysy?aniu informacji o sobie.
+ */
+enum {
+	GG_PUBDIR50_GENDER_SET_FEMALE,	/**< Kobieta */
+	GG_PUBDIR50_GENDER_SET_MALE,	/**< M??czyzna */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Warto?? pola GG_PUBDIR50_ACTIVE.
+ */
+enum {
+	GG_PUBDIR50_ACTIVE_TRUE,	/**< Wyszukaj tylko osoby dost?pne */
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Wynik operacji na katalogu publicznym.
+ *
+ * \ingroup http
+ */
 struct gg_pubdir {
-	int success;		/* czy si?da?o */
-	uin_t uin;		/* otrzymany numerek. 0 je?li b??d */
+	int success;		/**< Flaga powodzenia operacji */
+	uin_t uin;		/**< Otrzymany numer lub 0 w przypadku b??du */
 };
 
-/* og? funkcje, nie powinny by??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
 
+/**
+ * Token autoryzacji niekt?rych operacji HTTP.
+ * 
+ * \ingroup token
+ */
 struct gg_token {
-	int width;		/* szeroko??brazka */
-	int height;		/* wysoko??brazka */
-	int length;		/* ilo??nak? tokenie */
-	char *tokenid;		/* id tokenu */
+	int width;		/**< Szeroko?? obrazka */
+	int height;		/**< Wysoko?? obrazka */
+	int length;		/**< Liczba znak?w w tokenie */
+	char *tokenid;		/**< Identyfikator tokenu */
 };
 
-/* funkcje dotycz?ce token?/
 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);
+#ifndef DOXYGEN
 #define gg_register_watch_fd gg_pubdir_watch_fd
 #define gg_register_free gg_pubdir_free
-#define gg_free_register gg_pubdir_free
+#endif
 
-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);
+#ifndef DOXYGEN
 #define gg_unregister_watch_fd gg_pubdir_watch_fd
 #define gg_unregister_free gg_pubdir_free
+#endif
 
-/* 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);
+#ifndef DOXYGEN
 #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
+#endif
 
-/* 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);
+#ifndef DOXYGEN
+#define gg_change_passwd_watch_fd gg_pubdir_watch_fd
 #define gg_change_passwd_free gg_pubdir_free
-#define gg_free_change_passwd gg_pubdir_free
+#endif
 
-/*
- * 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 */
-};
+extern int gg_dcc_port;
+extern unsigned long gg_dcc_ip;
 
-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?a 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?y klientami.
- */
-extern int gg_dcc_port;			/* port, na kt? nas?uchuje klient */
-extern unsigned long gg_dcc_ip;		/* adres, na kt? 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);
@@ -814,120 +1090,249 @@ int gg_dcc_voice_send(struct gg_dcc *d, 
 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
+#define GG_DCC_VOICE_FRAME_LENGTH 195		/**< Rozmiar pakietu g?osowego przed wersj? Gadu-Gadu 5.0.5 */
+#define GG_DCC_VOICE_FRAME_LENGTH_505 326	/**< Rozmiar pakietu g?osowego od wersji Gadu-Gadu 5.0.5 */
 
 struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
-#define gg_dcc_socket_free gg_free_dcc
+#ifndef DOXYGEN
+#define gg_dcc_socket_free gg_dcc_free
 #define gg_dcc_socket_watch_fd gg_dcc_watch_fd
+#endif
 
 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?rzybywania wpis?gg_debug(...)' nie chcia?o mi
- * si?stawia?dpowiednich leveli, wi?wi?zo??z?a do _MISC.
- */
-extern int gg_debug_level;	/* poziom debugowania. mapa bitowa sta?ych GG_DEBUG_* */
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash);
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash);
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);
+void gg_dcc7_free(struct gg_dcc7 *d);
 
-/*
- * mo?na poda?ska?nik do funkcji obs?uguj?cej wywo?ania gg_debug().
- * nieoficjalne, nieudokumentowane, mo?e si?mieni?je?li kto? jest
- * zainteresowany, niech da zna?a ekg-devel.
- */
+extern int gg_debug_level;
+
 extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
+extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap);
 
-/*
- * mo?na poda?lik, do kt?o b? 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
+/**
+ * \ingroup debug
+ * @{
+ */
+#define GG_DEBUG_NET 1		/**< Rejestracja zdarze? zwi?zanych z sieci? */
+#define GG_DEBUG_TRAFFIC 2	/**< Rejestracja ruchu sieciowego */
+#define GG_DEBUG_DUMP 4		/**< Rejestracja zawarto?ci pakiet?w */
+#define GG_DEBUG_FUNCTION 8	/**< Rejestracja wywo?a? funkcji */
+#define GG_DEBUG_MISC 16	/**< Rejestracja r??nych informacji */
+/** @} */
 
 #ifdef GG_DEBUG_DISABLE
 #define gg_debug(x, y...) do { } while(0)
+#define gg_debug_session(z, x, y...) do { } while(0)
 #else
 void gg_debug(int level, const char *format, ...);
+void gg_debug_session(struct gg_session *sess, int level, const char *format, ...);
 #endif
 
 const char *gg_libgadu_version(void);
 
-/*
- * konfiguracja http proxy.
+extern int gg_proxy_enabled;
+extern char *gg_proxy_host;
+extern int gg_proxy_port;
+extern char *gg_proxy_username;
+extern char *gg_proxy_password;
+extern int gg_proxy_http_only;
+
+extern unsigned long gg_local_ip;
+
+#define GG_LOGIN_HASH_GG32 0x01	/**< Algorytm Gadu-Gadu */
+#define GG_LOGIN_HASH_SHA1 0x02	/**< Algorytm SHA1 */
+
+#ifndef DOXYGEN
+
+#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
+
+#else
+
+/**
+ * \ingroup pubdir50
+ * 
+ * Rodzaj zapytania lub odpowiedzi katalogu publicznego.
  */
-extern int gg_proxy_enabled;		/* w??cza obs?ug?roxy */
-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??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?roxy wy??cznie dla us?ug HTTP */
+enum {
+	GG_PUBDIR50_WRITE,	/**< Wys?anie do serwera informacji o sobie */
+	GG_PUBDIR50_READ,	/**< Pobranie z serwera informacji o sobie */
+	GG_PUBDIR50_SEARCH,	/**< Wyszukiwanie w katalogu publicznym */
+	GG_PUBDIR50_SEARCH_REPLY,	/**< Wynik wyszukiwania w katalogu publicznym */
+};
 
+#endif	/* DOXYGEN */
 
-/*
- * adres, z kt?o ?lemy pakiety (np ??czymy si? serwerem)
- * u?ywany przy gg_connect()
- */
-extern unsigned long gg_local_ip;
-/*
- * -------------------------------------------------------------------------
- * poni?ej znajduj? si?ewn?zne sprawy biblioteki. zwyk?y klient nie
- * powinien ich w og?rusza?bo i nie ma po co. wszystko mo?na za?atwi? * procedurami wy?szego poziomu, kt?h definicje znajduj? si?a pocz?tku
- * tego pliku.
- * -------------------------------------------------------------------------
- */
+/** \cond obsolete */
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
-#elif defined _WIN32
-int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname);
-#endif
+#define gg_free_event gg_event_free
+#define gg_free_http gg_http_free
+#define gg_free_pubdir gg_pubdir_free
+#define gg_free_register gg_pubdir_free
+#define gg_free_remind_passwd gg_pubdir_free
+#define gg_free_dcc gg_dcc_free
+#define gg_free_change_passwd gg_pubdir_free
 
-#ifdef _WIN32
-int gg_thread_socket(int thread_id, int socket);
-#endif
+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;
+} /* GG_DEPRECATED */;
 
-int gg_resolve(int *fd, int *pid, const char *hostname);
+struct gg_search {
+	int count;
+	struct gg_search_result *results;
+} GG_DEPRECATED;
 
-#if defined __GNUC__ && !defined _WIN32
-char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+struct gg_search_result {
+	uin_t uin;
+	char *first_name;
+	char *last_name;
+	char *nickname;
+	int born;
+	int gender;
+	char *city;
+	int active;
+} GG_DEPRECATED;
+
+#define GG_GENDER_NONE 0
+#define GG_GENDER_FEMALE 1
+#define GG_GENDER_MALE 2
+
+struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED;
+int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_free_search(struct gg_http *f) GG_DEPRECATED;
+#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) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED;
+void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED;
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED;
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED;
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED;
+
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_change_info_request {
+	char *first_name;
+	char *last_name;
+	char *nickname;
+	char *email;
+	int born;
+	int gender;
+	char *city;
+} /* GG_DEPRECATED */;
+
+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) GG_DEPRECATED;
+void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED;
+#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
+
+struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED;
+int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED;
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED;
+
+/** \endcond */
+
+int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED;
+
+#ifdef __GNUC__
+char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED;
 #else
-char *gg_saprintf(const char *format, ...);
+char *gg_saprintf(const char *format, ...) GG_DEPRECATED;
 #endif
 
-char *gg_vsaprintf(const char *format, va_list ap);
+char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
 
 #define gg_alloc_sprintf gg_saprintf
 
-char *gg_get_line(char **ptr);
+char *gg_get_line(char **ptr) GG_DEPRECATED;
 
-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);
+int gg_connect(void *addr, int port, int async) GG_DEPRECATED;
+struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
+char *gg_read_line(int sock, char *buf, int length) GG_DEPRECATED;
+void gg_chomp(char *line) GG_DEPRECATED;
+char *gg_urlencode(const char *str) GG_DEPRECATED;
+int gg_http_hash(const char *format, ...) GG_DEPRECATED;
+void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED;
+int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED;
+int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED;
+void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED;
+int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED;
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED;
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED;
+uint32_t gg_fix32(uint32_t x) GG_DEPRECATED;
+uint16_t gg_fix16(uint16_t x) GG_DEPRECATED;
 #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);
+char *gg_proxy_auth(void) GG_DEPRECATED;
+char *gg_base64_encode(const char *buf) GG_DEPRECATED;
+char *gg_base64_decode(const char *buf) GG_DEPRECATED;
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED;
 
+/**
+ * Kolejka odbieranych obrazk?w.
+ */
+struct gg_image_queue {
+	uin_t sender;			/**< Nadawca obrazka */
+	uint32_t size;			/**< Rozmiar obrazka */
+	uint32_t crc32;			/**< Suma kontrolna CRC32 */
+	char *filename;			/**< Nazwa pliku */
+	char *image;			/**< Bufor z odebranymi danymi */
+	uint32_t done;			/**< Rozmiar odebranych danych */
+
+	struct gg_image_queue *next;	/**< Kolejny element listy */
+} GG_DEPRECATED;
+
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+
 #define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
 #define GG_APPMSG_PORT 80
 #define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
@@ -941,137 +1346,250 @@ int gg_image_queue_remove(struct gg_sess
 #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_CLIENT_VERSION "8.0.0.7669"
+#define GG_DEFAULT_PROTOCOL_VERSION 0x2e
 #define GG_DEFAULT_TIMEOUT 30
 #define GG_HAS_AUDIO_MASK 0x40000000
+#define GG_HAS_AUDIO7_MASK 0x20000000
 #define GG_ERA_OMNIX_MASK 0x04000000
-#define GG_LIBGADU_VERSION "1.5.20050718"
+#undef GG_LIBGADU_VERSION
 
+#ifndef DOXYGEN
+
+#define GG_FEATURE_MSG77		0x01
+#define GG_FEATURE_STATUS77		0x02
+#define GG_FEATURE_DND_FFC		0x10
+#define GG_FEATURE_IMAGE_DESCR		0x20
+
+/* Poni?sze makra zosta?y zachowane dla zgodno?ci API */
+#define GG_FEATURE_MSG80		0
+#define GG_FEATURE_STATUS80		0
+#define GG_FEATURE_STATUS80BETA		0
+#define GG_FEATURE_ALL			(GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR)
+
+#else
+
+/** 
+ * \ingroup login
+ *
+ * Flagi opcji protoko?u.
+ */
+enum {
+	GG_FEATURE_MSG77,	/**< Klient ?yczy sobie otrzymywa? wiadomo?ci zgodnie z protoko?em 7.7 */
+	GG_FEATURE_STATUS77,	/**< Klient ?yczy sobie otrzymywa? zmiany stanu zgodnie z protoko?em 7.7 */
+	GG_FEATURE_DND_FFC,	/**< Klient obs?uguje statusy "nie przeszkadza?" i "poGGadaj ze mn?" */
+	GG_FEATURE_IMAGE_DESCR,	/**< Klient obs?uguje opisy graficzne oraz flag? \c GG_STATUS80_DESCR_MASK */
+};
+
+
+#endif
+
 #define GG_DEFAULT_DCC_PORT 1550
 
 struct gg_header {
 	uint32_t type;			/* typ pakietu */
-	uint32_t length;		/* d?ugo??eszty 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 */
+	uint32_t key;			/* klucz szyfrowania has?a */
 } GG_PACKED;
 
 #define GG_LOGIN 0x000c
 
 struct gg_login {
-	uint32_t uin;			/* m?umerek */
-	uint32_t hash;			/* hash has?a */
-	uint32_t status;		/* status na dzie?bry */
+	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?dres ip */
-	uint16_t local_port;		/* port, na kt? s?ucham */
+	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?umerek */
-	uint32_t hash;			/* hash has?a */
-	uint32_t status;		/* status na dzie?bry */
+	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?dres ip */
-	uint16_t local_port;		/* port, na kt? s?ucham */
-	uint32_t external_ip;		/* zewn?zny adres ip */
-	uint16_t external_port;		/* zewn?zny port */
+	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?umerek */
-	uint32_t hash;			/* hash has?a */
-	uint32_t status;		/* status na dzie?bry */
+	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?dres ip */
-	uint16_t local_port;		/* port, na kt? s?ucham */
-	uint32_t external_ip;		/* zewn?zny adres ip */
-	uint16_t external_port;		/* zewn?zny port */
+	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_LOGIN70 0x0019
+
+struct gg_login70 {
+	uint32_t uin;			/* m?j numerek */
+	uint8_t hash_type;		/* rodzaj hashowania has?a */
+	uint8_t hash[64];		/* hash has?a dope?niony zerami */
+	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 */
+	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 */
+	uint32_t seq;			/* czas wys?ania zapytania */
 } GG_PACKED;
 
 #define GG_NEW_STATUS 0x0002
 
-#define GG_STATUS_NOT_AVAIL 0x0001		/* niedost?y */
-#define GG_STATUS_NOT_AVAIL_DESCR 0x0015	/* niedost?y z opisem (4.8) */
-#define GG_STATUS_AVAIL 0x0002			/* dost?y */
-#define GG_STATUS_AVAIL_DESCR 0x0004		/* dost?y z opisem (4.9) */
-#define GG_STATUS_BUSY 0x0003			/* zaj? */
-#define GG_STATUS_BUSY_DESCR 0x0005		/* zaj? 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 */
+#ifndef DOXYGEN
 
-#define GG_STATUS_FRIENDS_MASK 0x8000		/* tylko dla znajomych (4.6) */
+#define GG_STATUS_NOT_AVAIL 0x0001
+#define GG_STATUS_NOT_AVAIL_DESCR 0x0015
+#define GG_STATUS_FFC 0x0017
+#define GG_STATUS_FFC_DESCR 0x0018
+#define GG_STATUS_AVAIL 0x0002
+#define GG_STATUS_AVAIL_DESCR 0x0004
+#define GG_STATUS_BUSY 0x0003
+#define GG_STATUS_BUSY_DESCR 0x0005
+#define GG_STATUS_DND 0x0021
+#define GG_STATUS_DND_DESCR 0x0022
+#define GG_STATUS_INVISIBLE 0x0014
+#define GG_STATUS_INVISIBLE_DESCR 0x0016
+#define GG_STATUS_BLOCKED 0x0006
 
-#define GG_STATUS_DESCR_MAXSIZE 70
+#define GG_STATUS_IMAGE_MASK 0x0100
+#define GG_STATUS_DESCR_MASK 0x4000
+#define GG_STATUS_FRIENDS_MASK 0x8000
 
-/*
- * makra do ?atwego i szybkiego sprawdzania stanu.
+#else
+
+/**
+ * Rodzaje status?w u?ytkownika.
+ *
+ * \ingroup status
  */
+enum {
+	GG_STATUS_NOT_AVAIL,		/**< Niedost?pny */
+	GG_STATUS_NOT_AVAIL_DESCR,	/**< Niedost?pny z opisem */
+	GG_STATUS_FFC,			/**< PoGGadaj ze mn? */
+	GG_STATUS_FFC_DESCR,		/**< PoGGadaj ze mn? z opisem */
+	GG_STATUS_AVAIL,		/**< Dost?pny */
+	GG_STATUS_AVAIL_DESCR,		/**< Dost?pny z opisem */
+	GG_STATUS_BUSY,			/**< Zaj?ty */
+	GG_STATUS_BUSY_DESCR,		/**< Zaj?ty z opisem */
+	GG_STATUS_DND,			/**< Nie przeszkadza? */
+	GG_STATUS_DND_DESCR,		/**< Nie przeszakdza? z opisem */
+	GG_STATUS_INVISIBLE,		/**< Niewidoczny (tylko w?asny status) */
+	GG_STATUS_INVISIBLE_DESCR,	/**< Niewidoczny z opisem (tylko w?asny status) */
+	GG_STATUS_BLOCKED,		/**< Zablokowany (tylko status innych) */
+	GG_STATUS_IMAGE_MASK,		/**< Flaga bitowa oznaczaj?ca opis graficzny (tylko je?li wybrano \c GG_FEATURE_IMAGE_DESCR) */
+	GG_STATUS_DESCR_MASK,		/**< Flaga bitowa oznaczaj?ca status z opisem (tylko je?li wybrano \c GG_FEATURE_IMAGE_DESCR) */
+	GG_STATUS_FRIENDS_MASK,		/**< Flaga bitowa dost?pno?ci tylko dla znajomych */
+};
 
+#endif	/* DOXYGEN */
+
+/**
+ * \ingroup status
+ *
+ * Flaga bitowa dostepnosci informujaca ze mozemy voipowac
+ */
+
+#define GG_STATUS_VOICE_MASK 0x20000	/**< czy ma wlaczone audio (7.7) */
+
+/**
+ * \ingroup status
+ *
+ * Maksymalna d?ugo?c opisu.
+ */
+#define GG_STATUS_DESCR_MAXSIZE 255
+#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70
+
+#define GG_STATUS_MASK 0xff
+
 /* GG_S_F() tryb tylko dla znajomych */
 #define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
 
-/* GG_S() stan bez uwzgl?ienia trybu tylko dla znajomych */
-#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
+/* GG_S() stan bez uwzgl?dnienia dodatkowych flag */
+#define GG_S(x) ((x) & GG_STATUS_MASK)
 
-/* GG_S_A() dost?y */
-#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
 
-/* GG_S_NA() niedost?y */
+/* GG_S_FF() ch?tny do rozmowy */
+#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR)
+
+/* GG_S_AV() dost?pny */
+#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_AW() zaraz wracam */
+#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
+
+/* GG_S_DD() nie przeszkadza? */
+#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_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? */
-#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_A() dost?pny lub ch?tny do rozmowy */
+#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x))
+
+/* GG_S_B() zaj?ty lub nie przeszkadza? */
+#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x))
+
+
 /* 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)
+#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \
+		   GG_S(x) == GG_STATUS_FFC_DESCR || \
+		   GG_S(x) == GG_STATUS_AVAIL_DESCR || \
+		   GG_S(x) == GG_STATUS_BUSY_DESCR || \
+		   GG_S(x) == GG_STATUS_DND_DESCR || \
+		   GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
 
-/* GG_S_BL() blokowany lub blokuj?cy */
+/* GG_S_BL() blokowany lub blokuj?cy */
 #define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
 
+/**
+ * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA)
+ */
 struct gg_new_status {
-	uint32_t status;			/* na jaki zmieni?*/
+	uint32_t status;			/**< Nowy status */
 } GG_PACKED;
 
 #define GG_NOTIFY_FIRST 0x000f
@@ -1081,13 +1599,30 @@ struct gg_notify {
 
 struct gg_notify {
 	uint32_t uin;				/* numerek danej osoby */
-	uint8_t dunno1;				/* rodzaj wpisu w li?cie */
+	uint8_t dunno1;				/* rodzaj wpisu w li?cie */
 } GG_PACKED;
 
-#define GG_USER_OFFLINE 0x01	/* b?iemy niewidoczni dla u?ytkownika */
-#define GG_USER_NORMAL 0x03	/* zwyk?y u?ytkownik */
-#define GG_USER_BLOCKED 0x04	/* zablokowany u?ytkownik */
+#ifndef DOXYGEN
 
+#define GG_USER_OFFLINE 0x01
+#define GG_USER_NORMAL 0x03
+#define GG_USER_BLOCKED 0x04
+
+#else
+
+/**
+ * \ingroup contacts
+ *
+ * Rodzaj kontaktu.
+ */
+enum {
+	GG_USER_NORMAL,		/**< Zwyk?y kontakt */
+	GG_USER_BLOCKED,	/**< Zablokowany */
+	GG_USER_OFFLINE,	/**< Niewidoczny dla kontaktu */
+};
+
+#endif	/* DOXYGEN */
+
 #define GG_LIST_EMPTY 0x0012
 
 #define GG_NOTIFY_REPLY 0x000c	/* tak, to samo co GG_LOGIN */
@@ -1096,7 +1631,7 @@ 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? s?ucha klient */
+	uint16_t remote_port;		/* port, na kt?rym s?ucha klient */
 	uint32_t version;		/* wersja klienta */
 	uint16_t dunno2;		/* znowu port? */
 } GG_PACKED;
@@ -1107,7 +1642,7 @@ 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? s?ucha klient */
+	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 */
@@ -1119,12 +1654,38 @@ 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? s?ucha klient */
+	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_NOTIFY_REPLY77 0x0018
+
+struct gg_notify_reply77 {
+	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 */
+	uint32_t dunno2;		/* ? */
+} GG_PACKED;
+
+#define GG_STATUS77 0x0017
+
+struct gg_status77 {
+	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 */
+	uint32_t dunno2;		/* ? */
+} GG_PACKED;
+
 #define GG_ADD_NOTIFY 0x000d
 #define GG_REMOVE_NOTIFY 0x000e
 
@@ -1142,16 +1703,42 @@ struct gg_status {
 
 #define GG_SEND_MSG 0x000b
 
+#ifndef DOXYGEN
+
 #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??stecz */
+#define GG_CLASS_EXT GG_CLASS_ACK	/**< Dla kompatybilno?ci wstecz */
 
-#define GG_MSG_MAXSIZE 2000
+#else
 
+/**
+ * Klasy wiadomo?ci. Warto?ci s? maskami bitowymi, kt?re w wi?kszo?ci
+ * przypadk?w mo?na ??czy? (po??czenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT
+ * nie ma sensu).
+ *
+ * \ingroup messages
+ */
+enum {
+	GG_CLASS_MSG,		/**< Wiadomo?? ma pojawi? si? w osobnym oknie */
+	GG_CLASS_CHAT,		/**< Wiadomo?? ma pojawi? si? w oknie rozmowy */
+	GG_CLASS_CTCP,		/**< Wiadomo?? przeznaczona dla klienta Gadu-Gadu */
+	GG_CLASS_ACK,		/**< Klient nie ?yczy sobie potwierdzenia */
+	GG_CLASS_QUEUED,	/**< Wiadomo?? zakolejkowana na serwerze (tylko przy odbieraniu) */
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Maksymalna d?ugo?? wiadomo?ci.
+ *
+ * \ingroup messages
+ */
+#define GG_MSG_MAXSIZE 1989
+
 struct gg_send_msg {
 	uint32_t recipient;
 	uint32_t seq;
@@ -1163,16 +1750,19 @@ struct gg_msg_richtext {
 	uint16_t length;
 } GG_PACKED;
 
+/**
+ * Struktura opisuj?ca formatowanie tekstu. W zale?no?ci od warto?ci pola
+ * \c font, zaraz za t? struktur? mo?e wyst?pi? \c gg_msg_richtext_color
+ * lub \c gg_msg_richtext_image.
+ *
+ * \ingroup messages
+ */
 struct gg_msg_richtext_format {
-	uint16_t position;
-	uint8_t font;
+	uint16_t position;	/**< Pocz?tkowy znak formatowania (liczony od 0) */
+	uint8_t font;		/**< Atrybuty formatowania */
 } GG_PACKED;
 
-struct gg_msg_richtext_image {
-	uint16_t unknown1;
-	uint32_t size;
-	uint32_t crc32;
-} GG_PACKED;
+#ifndef DOXYGEN
 
 #define GG_FONT_BOLD 0x01
 #define GG_FONT_ITALIC 0x02
@@ -1180,12 +1770,46 @@ struct gg_msg_richtext_image {
 #define GG_FONT_COLOR 0x08
 #define GG_FONT_IMAGE 0x80
 
+#else
+
+/**
+ * Atrybuty formatowania wiadomo?ci.
+ *
+ * \ingroup messages
+ */
+enum {
+	GG_FONT_BOLD,
+	GG_FONT_ITALIC,
+	GG_FONT_UNDERLINE,
+	GG_FONT_COLOR,
+	GG_FONT_IMAGE
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Struktura opisuj?c? kolor tekstu dla atrybutu \c GG_FONT_COLOR.
+ *
+ * \ingroup messages
+ */
 struct gg_msg_richtext_color {
-	uint8_t red;
-	uint8_t green;
-	uint8_t blue;
+	uint8_t red;		/**< Sk?adowa czerwona koloru */
+	uint8_t green;		/**< Sk?adowa zielona koloru */
+	uint8_t blue;		/**< Sk?adowa niebieska koloru */
 } GG_PACKED;
 
+/**
+ * Strukturya opisuj?ca obrazek wstawiony do wiadomo?ci dla atrubutu
+ * \c GG_FONT_IMAGE.
+ *
+ * \ingroup messages
+ */
+struct gg_msg_richtext_image {
+	uint16_t unknown1;	/**< Nieznane pole o warto?ci 0x0109 */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 obrazka */
+} GG_PACKED;
+
 struct gg_msg_recipients {
 	uint8_t flag;
 	uint32_t count;
@@ -1207,12 +1831,32 @@ struct gg_msg_image_reply {
 
 #define GG_SEND_MSG_ACK 0x0005
 
+#ifndef DOXYGEN
+
 #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
 
+#else
+
+/**
+ * Status dor?czenia wiadomo?ci.
+ *
+ * \ingroup messages
+ */
+enum
+{
+	GG_ACK_DELIVERED,	/**< Wiadomo?? dostarczono. */
+	GG_ACK_QUEUED,		/**< Wiadomo?? zakolejkowano z powodu niedost?pno?ci odbiorcy. */
+	GG_ACK_BLOCKED,		/**< Wiadomo?? zablokowana przez serwer (spam, ?wi?teczne ograniczenia itd.) */
+	GG_ACK_MBOXFULL,	/**< Wiadomo?ci nie dostarczono z powodu zape?nionej kolejki wiadomo?ci odbiorcy. */
+	GG_ACK_NOT_DELIVERED	/**< Wiadomo?ci nie dostarczono (tylko dla \c GG_CLASS_CTCP). */
+};
+
+#endif	/* DOXYGEN */
+
 struct gg_send_msg_ack {
 	uint32_t status;
 	uint32_t recipient;
@@ -1236,29 +1880,59 @@ struct gg_recv_msg {
 
 #define GG_USERLIST_REQUEST 0x0016
 
+#define GG_XML_EVENT 0x0027
+
+#ifndef DOXYGEN
+
 #define GG_USERLIST_PUT 0x00
 #define GG_USERLIST_PUT_MORE 0x01
 #define GG_USERLIST_GET 0x02
 
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj zapytania.
+ */
+enum {
+	GG_USERLIST_PUT,	/**< Eksport listy kontakt?w. */
+	GG_USERLIST_GET,	/**< Import listy kontakt?w. */
+};
+
+#endif	/* DOXYGEN */
+
 struct gg_userlist_request {
 	uint8_t type;
 } GG_PACKED;
 
 #define GG_USERLIST_REPLY 0x0010
 
+#ifndef DOXYGEN
+
 #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
 
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj odpowiedzi.
+ */
+enum {
+	GG_USERLIST_PUT_REPLY,	/**< Wyeksportowano listy kontakt?w. */
+	GG_USERLIST_GET_REPLY,	/**< Zaimportowano list? kontakt?w. */
+};
+
+#endif	/* DOXYGEN */
+
 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;
@@ -1274,14 +1948,14 @@ struct gg_dcc_big_packet {
 } GG_PACKED;
 
 /*
- * p?co, nie znamy dok?adnie protoko?u. nie wiemy, co czemu odpowiada.
- * nazwy s? niepowa?ne i tymczasowe.
+ * 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?mu damy */
+#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_CATCH_FILE 0x0002	/* wysy?amy plik */
 
 #define GG_DCC_FILEATTR_READONLY 0x0020
 
@@ -1290,11 +1964,88 @@ struct gg_dcc_big_packet {
 #define GG_DCC_TIMEOUT_FILE_ACK 300	/* 5 minut */
 #define GG_DCC_TIMEOUT_VOICE_ACK 300	/* 5 minut */
 
+#define GG_DCC7_INFO 0x1f
+
+struct gg_dcc7_info {
+	uint32_t uin;			/* numer nadawcy */
+	uint32_t type;			/* spos?b po??czenia */
+	gg_dcc7_id_t id;		/* identyfikator po??czenia */
+	char info[GG_DCC7_INFO_LEN];	/* informacje o po??czeniu "ip port" */
+} GG_PACKED;
+
+#define GG_DCC7_NEW 0x20
+
+struct gg_dcc7_new {
+	gg_dcc7_id_t id;		/* identyfikator po??czenia */
+	uint32_t uin_from;		/* numer nadawcy */
+	uint32_t uin_to;		/* numer odbiorcy */
+	uint32_t type;			/* rodzaj transmisji */
+	unsigned char filename[GG_DCC7_FILENAME_LEN];	/* nazwa pliku */
+	uint32_t size;			/* rozmiar pliku */
+	uint32_t size_hi;		/* rozmiar pliku (starsze bajty) */
+	unsigned char hash[GG_DCC7_HASH_LEN];	/* hash SHA1 */
+} GG_PACKED;
+
+#define GG_DCC7_ACCEPT 0x21
+
+struct gg_dcc7_accept {
+	uint32_t uin;			/* numer przyjmuj?cego po??czenie */
+	gg_dcc7_id_t id;		/* identyfikator po??czenia */
+	uint32_t offset;		/* offset przy wznawianiu transmisji */
+	uint32_t dunno1;		/* 0x00000000 */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_P2P 0x00000001	/**< Po??czenie bezpo?rednie */
+#define GG_DCC7_TYPE_SERVER 0x00000002	/**< Po??czenie przez serwer */
+
+#define GG_DCC7_REJECT 0x22
+
+struct gg_dcc7_reject {
+	uint32_t uin;			/**< Numer odrzucaj?cego po??czenie */
+	gg_dcc7_id_t id;		/**< Identyfikator po??czenia */
+	uint32_t reason;		/**< Pow?d roz??czenia */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_REJECT_BUSY 0x00000001	/**< Po??czenie bezpo?rednie ju? trwa, nie umiem obs?u?y? wi?cej */
+#define GG_DCC7_REJECT_USER 0x00000002	/**< U?ytkownik odrzuci? po??czenie */
+#define GG_DCC7_REJECT_VERSION 0x00000006	/**< Druga strona ma wersj? klienta nieobs?uguj?c? po??cze? bezpo?rednich tego typu */
+
+#define GG_DCC7_ID_REQUEST 0x23
+
+struct gg_dcc7_id_request {
+	uint32_t type;			/**< Rodzaj tranmisji */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_VOICE 0x00000001	/**< Transmisja g?osu */
+#define GG_DCC7_TYPE_FILE 0x00000004	/**< transmisja pliku */
+
+#define GG_DCC7_ID_REPLY 0x23
+
+struct gg_dcc7_id_reply {
+	uint32_t type;			/** Rodzaj transmisji */
+	gg_dcc7_id_t id;		/** Przyznany identyfikator */
+} GG_PACKED;
+
+#define GG_DCC7_DUNNO1 0x24
+
+struct gg_dcc7_dunno1 {
+	// XXX
+} GG_PACKED;
+
+#define GG_DCC7_TIMEOUT_CONNECT 10	/* 10 sekund */
+#define GG_DCC7_TIMEOUT_SEND 1800	/* 30 minut */
+#define GG_DCC7_TIMEOUT_GET 1800	/* 30 minut */
+#define GG_DCC7_TIMEOUT_FILE_ACK 300	/* 5 minut */
+#define GG_DCC7_TIMEOUT_VOICE_ACK 300	/* 5 minut */
+
 #ifdef __cplusplus
-#ifdef _MSC_VER
+}
+#ifdef _WIN32
 #pragma pack(pop)
 #endif
-}
 #endif
 
 #endif /* __GG_LIBGADU_H */
============================================================
--- libpurple/protocols/gg/lib/obsolete.c	9299df4788c6713310cf033502f528ae3ce2e137
+++ libpurple/protocols/gg/lib/obsolete.c	a94b3936091bd78372f57b02fc07f93274a5151e
@@ -1,4 +1,4 @@
-/* $Id: obsolete.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka at irc.pl>
@@ -14,16 +14,23 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-/*
- * Plik zawiera deklaracje funkcji, kt?s? ju? nieaktualne ze wzgl?
- * na zmiany w protokole, ale s? wymagane przez aplikacje linkowane ze
- * starszymi wersjami bibliotek.
+/**
+ * \file obsolete.c
+ *
+ * \brief Nieaktualne funkcje
+ *
+ * Plik zawiera definicje funkcji, kt?re s? ju? nieaktualne ze wzgl?du
+ * na zmiany w protokole. Programy konsolidowane ze starszych wersjami
+ * bibliotek powinny nadal mie? mo?liwo?? dzia?ania, mimo ograniczonej
+ * funkcjonalno?ci.
  */
 
+/** \cond obsolete */
+
 #include <errno.h>
 
 #include "libgadu.h"
@@ -205,3 +212,25 @@ void gg_change_info_request_free(struct 
 {
 
 }
+
+int gg_resolve(int *fd, int *pid, const char *hostname)
+{
+	return -1;
+}
+
+void gg_resolve_pthread_cleanup(void *arg, int kill)
+{
+
+}
+
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
+{
+	return -1;
+}
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+{
+	return -1;
+}
+
+/** \endcond */
============================================================
--- libpurple/protocols/gg/lib/pubdir.c	4dbeb06586d39247a8d3e81a992d65339d574a8d
+++ libpurple/protocols/gg/lib/pubdir.c	28a76bb68aa2380804d02e89c6c83485eb23d487
@@ -1,8 +1,9 @@
-/* $Id: pubdir.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: pubdir.c 502 2008-01-10 23:25:17Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka at irc.pl>
+ *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka at irc.pl>
  *                          Dawid Jarosz <dawjar at poczta.onet.pl>
+ *                          Adam Wysocki <gophi at ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,11 +16,15 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/**
+ * \file pubdir.c
+ *
+ * \brief Obs?uga katalogu publicznego
+ */
 
 #include <ctype.h>
 #include <errno.h>
@@ -29,20 +34,22 @@
 #include <string.h>
 #include <unistd.h>
 
-/*
- * gg_register3()
+#include "libgadu.h"
+
+/**
+ * Rejestruje nowego u?ytkownika.
  *
- * rozpoczyna rejestracj??ytkownika protoko?em GG 6.0. wymaga wcze?niejszego
- * pobrania tokenu za pomoc? funkcji gg_token().
+ * Wymaga wcze?niejszego pobrania tokenu za pomoc? \c gg_token().
  *
- *  - email - adres e-mail klienta
- *  - password - has?o klienta
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto??okenu
- *  - async - po??czenie asynchroniczne
+ * \param email Adres e-mail
+ * \param password Has?o
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawarto?? tokenu
+ * \param async Flaga po??czenia asynchronicznego
  *
- * zaalokowana struct gg_http, kt?po?niej nale?y zwolni? * funkcj? gg_register_free(), albo NULL je?li wyst?pi? b??d.
+ * \return Struktura \c gg_http lub \c NULL w przypadku b??du
+ *
+ * \ingroup register
  */
 struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async)
 {
@@ -121,20 +128,60 @@ struct gg_http *gg_register3(const char 
 	return h;
 }
 
-/*
- * gg_unregister3()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
  *
- * usuwa konto u?ytkownika z serwera protoko?em GG 6.0
+ * Operacja b?dzie zako?czona, gdy pole \c state b?dzie r?wne \c GG_STATE_DONE.
+ * Je?li wyst?pi b??d, \c state b?dzie r?wne \c GG_STATE_ERROR, a kod b??du
+ * znajdzie si? w polu \c error.
  *
- *  - uin - numerek GG
- *  - password - has?o klienta
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto??okenu
- *  - async - po??czenie asynchroniczne
+ * \note W rzeczywisto?ci funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * zaalokowana struct gg_http, kt?po?niej nale?y zwolni? * funkcj? gg_unregister_free(), albo NULL je?li wyst?pi? b??d.
+ * \param h Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup register
  */
+int gg_register_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywisto?ci funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura po??czenia
+ *
+ * \ingroup register
+ */
+void gg_register_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Usuwa u?ytkownika.
+ *
+ * Wymaga wcze?niejszego pobrania tokenu za pomoc? \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param password Has?o
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawarto?? tokenu
+ * \param async Flaga po??czenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku b??du
+ *
+ * \ingroup unregister
+ */
 struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async)
 {
 	struct gg_http *h;
@@ -145,7 +192,7 @@ struct gg_http *gg_unregister3(uin_t uin
 		errno = EFAULT;
 		return NULL;
 	}
-
+    
 	__pwd = gg_saprintf("%ld", random());
 	__fmpwd = gg_urlencode(password);
 	__tokenid = gg_urlencode(tokenid);
@@ -210,23 +257,62 @@ struct gg_http *gg_unregister3(uin_t uin
 	return h;
 }
 
-/*
- * gg_change_passwd4()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
  *
- * wysy?a ??danie zmiany has?a zgodnie z protoko?em GG 6.0. wymaga
- * wcze?niejszego pobrania tokenu za pomoc? funkcji gg_token().
+ * Operacja b?dzie zako?czona, gdy pole \c state b?dzie r?wne \c GG_STATE_DONE.
+ * Je?li wyst?pi b??d, \c state b?dzie r?wne \c GG_STATE_ERROR, a kod b??du
+ * znajdzie si? w polu \c error.
  *
- *  - uin - numer
- *  - email - adres e-mail
- *  - passwd - stare has?o
- *  - newpasswd - nowe has?o
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto??okenu
- *  - async - po??czenie asynchroniczne
+ * \note W rzeczywisto?ci funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * zaalokowana struct gg_http, kt?po?niej nale?y zwolni? * funkcj? gg_change_passwd_free(), albo NULL je?li wyst?pi? b??d.
+ * \param h Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup unregister
  */
+int gg_unregister_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywisto?ci funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura po??czenia
+ *
+ * \ingroup unregister
+ */
+void gg_unregister_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia has?o u?ytkownika.
+ *
+ * Wymaga wcze?niejszego pobrania tokenu za pomoc? \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail
+ * \param passwd Obecne has?o
+ * \param newpasswd Nowe has?o
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawarto?? tokenu
+ * \param async Flaga po??czenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku b??du
+ *
+ * \ingroup passwd
+ */
 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;
@@ -309,20 +395,60 @@ struct gg_http *gg_change_passwd4(uin_t 
 	return h;
 }
 
-/*
- * gg_remind_passwd3()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
  *
- * wysy?a ??danie przypomnienia has?a e-mailem.
+ * Operacja b?dzie zako?czona, gdy pole \c state b?dzie r?wne \c GG_STATE_DONE.
+ * Je?li wyst?pi b??d, \c state b?dzie r?wne \c GG_STATE_ERROR, a kod b??du
+ * znajdzie si? w polu \c error.
  *
- *  - uin - numer
- *  - email - adres e-mail taki, jak ten zapisany na serwerze
- *  - async - po??czenie asynchroniczne
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto??okenu
+ * \note W rzeczywisto?ci funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * zaalokowana struct gg_http, kt?po?niej nale?y zwolni? * funkcj? gg_remind_passwd_free(), albo NULL je?li wyst?pi? b??d.
+ * \param h Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup passwd
  */
+int gg_change_passwd_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywisto?ci funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura po??czenia
+ *
+ * \ingroup passwd
+ */
+void gg_change_passwd_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Wysy?a has?o u?ytkownika na e-mail.
+ *
+ * Wymaga wcze?niejszego pobrania tokenu za pomoc? \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail (podany przy rejestracji)
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawarto?? tokenu
+ * \param async Flaga po??czenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku b??du
+ *
+ * \ingroup remind
+ */
 struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async)
 {
 	struct gg_http *h;
@@ -396,18 +522,56 @@ struct gg_http *gg_remind_passwd3(uin_t 
 	return h;
 }
 
-/*
- * gg_pubdir_watch_fd()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
  *
- * przy asynchronicznych operacjach na katalogu publicznym nale?y wywo?ywa? * t?unkcj?rzy zmianach na obserwowanym deskryptorze.
+ * Operacja b?dzie zako?czona, gdy pole \c state b?dzie r?wne \c GG_STATE_DONE.
+ * Je?li wyst?pi b??d, \c state b?dzie r?wne \c GG_STATE_ERROR, a kod b??du
+ * znajdzie si? w polu \c error.
  *
- *  - h - struktura opisuj?ca po??czenie
+ * \note W rzeczywisto?ci funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * je?li wszystko posz?o dobrze to 0, inaczej -1. operacja b?ie
- * zako?na, je?li h->state == GG_STATE_DONE. je?li wyst?pi jaki?
- * b??d, to b?ie tam GG_STATE_ERROR i odpowiedni kod b?? w h->error.
+ * \param h Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup remind
  */
+int gg_remind_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywisto?ci funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura po??czenia
+ *
+ * \ingroup remind
+ */
+void gg_remind_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
+ *
+ * Operacja b?dzie zako?czona, gdy pole \c state b?dzie r?wne \c GG_STATE_DONE.
+ * Je?li wyst?pi b??d, \c state b?dzie r?wne \c GG_STATE_ERROR, a kod b??du
+ * znajdzie si? w polu \c error.
+ *
+ * \param h Struktura po??czenia
+ *
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ */
 int gg_pubdir_watch_fd(struct gg_http *h)
 {
 	struct gg_pubdir *p;
@@ -447,8 +611,12 @@ int gg_pubdir_watch_fd(struct gg_http *h
 	
 	gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body);
 
-	if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
+	if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) {
 		p->success = 1;
+		p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0);
+		gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin);
+	} else 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);
@@ -458,12 +626,10 @@ int gg_pubdir_watch_fd(struct gg_http *h
 	return 0;
 }
 
-/*
- * gg_pubdir_free()
+/**
+ * Zwalnia zasoby po operacji na katalogu publicznym.
  *
- * zwalnia pami?po efektach operacji na katalogu publicznym.
- *
- *  - h - zwalniana struktura
+ * \param h Struktura po??czenia
  */
 void gg_pubdir_free(struct gg_http *h)
 {
@@ -474,14 +640,17 @@ void gg_pubdir_free(struct gg_http *h)
 	gg_http_free(h);
 }
 
-/*
- * gg_token()
+/**
+ * Pobiera token do autoryzacji operacji na katalogu publicznym.
  *
- * pobiera z serwera token do autoryzacji zak?adania konta, usuwania
- * konta i zmiany has?a.
+ * Token jest niezb?dny do tworzenia nowego i usuwania u?ytkownika,
+ * zmiany has?a itd.
  *
- * zaalokowana struct gg_http, kt?po?niej nale?y zwolni? * funkcj? gg_token_free(), albo NULL je?li wyst?pi? b??d.
+ * \param async Flaga po??czenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku b??du
+ *
+ * \ingroup token
  */
 struct gg_http *gg_token(int async)
 {
@@ -511,17 +680,18 @@ struct gg_http *gg_token(int async)
 	return h;
 }
 
-/*
- * gg_token_watch_fd()
+/**
+ * Funkcja wywo?ywana po zaobserwowaniu zmian na deskryptorze po??czenia.
  *
- * przy asynchronicznych operacjach zwi?zanych z tokenem nale?y wywo?ywa? * t?unkcj?rzy zmianach na obserwowanym deskryptorze.
+ * Operacja b?dzie zako?czona, gdy pole \c state b?dzie r?wne \c GG_STATE_DONE.
+ * Je?li wyst?pi b??d, \c state b?dzie r?wne \c GG_STATE_ERROR, a kod b??du
+ * znajdzie si? w polu \c error.
  *
- *  - h - struktura opisuj?ca po??czenie
+ * \param h Struktura po??czenia
  *
- * je?li wszystko posz?o dobrze to 0, inaczej -1. operacja b?ie
- * zako?na, je?li h->state == GG_STATE_DONE. je?li wyst?pi jaki?
- * b??d, to b?ie tam GG_STATE_ERROR i odpowiedni kod b?? w h->error.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
+ *
+ * \ingroup token
  */
 int gg_token_watch_fd(struct gg_http *h)
 {
@@ -547,8 +717,8 @@ int gg_token_watch_fd(struct gg_http *h)
 	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
+	/* 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;
@@ -573,8 +743,8 @@ int gg_token_watch_fd(struct gg_http *h)
 			return -1;
 		}
 		
-		/* dostali?my tokenid i wszystkie niezb?e informacje,
-		 * wi?pobierzmy obrazek z tokenem */
+		/* 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);
@@ -623,6 +793,8 @@ int gg_token_watch_fd(struct gg_http *h)
 		free(path);
 		free(url);
 
+		gg_http_free_fields(h);
+
 		memcpy(h, h2, sizeof(struct gg_http));
 		free(h2);
 
@@ -652,12 +824,12 @@ int gg_token_watch_fd(struct gg_http *h)
 	return 0;
 }
 
-/*
- * gg_token_free()
+/**
+ * Zwalnia zasoby po operacji pobierania tokenu.
  *
- * zwalnia pami?po efektach pobierania tokenu.
+ * \param h Struktura po??czenia
  *
- *  - h - zwalniana struktura
+ * \ingroup token
  */
 void gg_token_free(struct gg_http *h)
 {
============================================================
--- libpurple/protocols/gg/lib/pubdir50.c	88881a94e1fe2f108bc6959201f33a8661d55bee
+++ libpurple/protocols/gg/lib/pubdir50.c	7da71632ba0136ccb182307b01d6fa772ff261c2
@@ -1,4 +1,4 @@
-/* $Id: pubdir50.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: pubdir50.c 854 2009-10-12 21:06:28Z wojtekka $ */
 
 /*
  *  (C) Copyright 2003 Wojtek Kaniewski <wojtekka at irc.pl>
@@ -14,23 +14,32 @@
  *
  *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/**
+ * \file pubdir50.c
+ *
+ * \brief Obs?uga katalogu publicznego od wersji Gadu-Gadu 5.x
+ */
 
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
-/*
- * gg_pubdir50_new()
+#include "libgadu.h"
+#include "libgadu-internal.h"
+
+/**
+ * Tworzy nowe zapytanie katalogu publicznego.
  *
- * tworzy now? zmienn? typu gg_pubdir50_t.
+ * \param type Rodzaj zapytania
  *
- * zaalokowana zmienna lub NULL w przypadku braku pami?.
+ * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku b??du.
+ *
+ * \ingroup pubdir50
  */
 gg_pubdir50_t gg_pubdir50_new(int type)
 {
@@ -50,17 +59,16 @@ gg_pubdir50_t gg_pubdir50_new(int type)
 	return res;
 }
 
-/*
- * gg_pubdir50_add_n()  // funkcja wewn?zna
+/**
+ * \internal Dodaje lub zast?puje pole zapytania lub odpowiedzi katalogu
+ * publicznego.
  *
- * funkcja dodaje lub zast?je istniej?ce pole do zapytania lub odpowiedzi.
+ * \param req Zapytanie lub odpowied?
+ * \param num Numer wyniku odpowiedzi (0 dla zapytania)
+ * \param field Nazwa pola
+ * \param value Warto?? pola
  *
- *  - req - wska?nik opisu zapytania,
- *  - num - numer wyniku (0 dla zapytania),
- *  - field - nazwa pola,
- *  - value - warto??ola,
- *
- * 0/-1
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  */
 static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
 {
@@ -110,31 +118,31 @@ static int gg_pubdir50_add_n(gg_pubdir50
 	return 0;
 }
 
-/*
- * gg_pubdir50_add()
+/**
+ * Dodaje pole zapytania.
  *
- * funkcja dodaje pole do zapytania.
+ * \param req Zapytanie
+ * \param field Nazwa pola
+ * \param value Warto?? pola
  *
- *  - req - wska?nik opisu zapytania,
- *  - field - nazwa pola,
- *  - value - warto??ola,
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  *
- * 0/-1
+ * \ingroup pubdir50
  */
 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.
  *
- * ustawia numer sekwencyjny zapytania.
+ * \param req Zapytanie
+ * \param seq Numer sekwencyjny
  *
- *  - req - zapytanie,
- *  - seq - nowy numer sekwencyjny.
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  *
- * 0/-1.
+ * \ingroup pubdir50
  */
 int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
 {
@@ -151,12 +159,12 @@ int gg_pubdir50_seq_set(gg_pubdir50_t re
 	return 0;
 }
 
-/*
- * gg_pubdir50_free()
+/**
+ * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego.
  *
- * zwalnia pami?po zapytaniu lub rezultacie szukania u?ytkownika.
+ * \param s Zapytanie lub odpowied?
  *
- *  - s - zwalniana zmienna,
+ * \ingroup pubdir50
  */
 void gg_pubdir50_free(gg_pubdir50_t s)
 {
@@ -174,15 +182,15 @@ void gg_pubdir50_free(gg_pubdir50_t s)
 	free(s);
 }
 
-/*
- * gg_pubdir50()
+/**
+ * Wysy?a zapytanie katalogu publicznego do serwera.
  *
- * wysy?a zapytanie katalogu publicznego do serwera.
+ * \param sess Struktura sesji
+ * \param req Zapytanie
  *
- *  - sess - sesja,
- *  - req - zapytanie.
+ * \return Numer sekwencyjny zapytania lub 0 w przypadku b??du
  *
- * numer sekwencyjny wyszukiwania lub 0 w przypadku b??.
+ * \ingroup pubdir50
  */
 uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
 {
@@ -191,16 +199,16 @@ uint32_t gg_pubdir50(struct gg_session *
 	char *buf, *p;
 	struct gg_pubdir50_request *r;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
 	
 	if (!sess || !req) {
-		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
+		gg_debug_session(sess, 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");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
 		errno = ENOTCONN;
 		return 0;
 	}
@@ -210,30 +218,81 @@ uint32_t gg_pubdir50(struct gg_session *
 		if (req->entries[i].num)
 			continue;
 		
-		size += strlen(req->entries[i].field) + 1;
-		size += strlen(req->entries[i].value) + 1;
+		if (sess->encoding == GG_ENCODING_CP1250) {
+			size += strlen(req->entries[i].field) + 1;
+			size += strlen(req->entries[i].value) + 1;
+		} else {
+			char *tmp;
+
+			tmp = gg_utf8_to_cp(req->entries[i].field);
+
+			if (tmp == NULL)
+				return -1;
+
+			size += strlen(tmp) + 1;
+
+			free(tmp);
+
+			tmp = gg_utf8_to_cp(req->entries[i].value);
+
+			if (tmp == NULL)
+				return -1;
+
+			size += strlen(tmp) + 1;
+
+			free(tmp);
+		}
 	}
 
 	if (!(buf = malloc(size))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
 		return 0;
 	}
 
+	if (!req->seq)
+		req->seq = time(NULL);
+
+	res = req->seq;
+
 	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);
+	r->seq = gg_fix32(req->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;
+		if (sess->encoding == GG_ENCODING_CP1250) {
+			strcpy(p, req->entries[i].field);
+			p += strlen(p) + 1;
 
-		strcpy(p, req->entries[i].value);
-		p += strlen(p) + 1;
+			strcpy(p, req->entries[i].value);
+			p += strlen(p) + 1;
+		} else {
+			char *tmp;
+
+			tmp = gg_utf8_to_cp(req->entries[i].field);
+
+			if (tmp == NULL) {
+				free(buf);
+				return -1;
+			}
+
+			strcpy(p, tmp);
+			p += strlen(tmp) + 1;
+			free(tmp);
+
+			tmp = gg_utf8_to_cp(req->entries[i].value);
+
+			if (tmp == NULL) {
+				free(buf);
+				return -1;
+			}
+
+			strcpy(p, tmp);
+			p += strlen(tmp) + 1;
+			free(tmp);
+		}
 	}
 
 	if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
@@ -245,26 +304,26 @@ uint32_t gg_pubdir50(struct gg_session *
 }
 
 /*
- * gg_pubdir50_handle_reply()  // funkcja wewn?zna
+ * \internal Analizuje przychodz?cy pakiet odpowiedzi i zapisuje wynik
+ * w strukturze \c gg_event.
  *
- * analizuje przychodz?cy pakiet odpowiedzi i zapisuje wynik w struct gg_event.
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param packet Pakiet odpowiedzi
+ * \param length D?ugo?? pakietu odpowiedzi
  *
- *  - e - opis zdarzenia
- *  - packet - zawarto??akietu odpowiedzi
- *  - length - d?ugo??akietu odpowiedzi
- *
- * 0/-1
+ * \return 0 je?li si? powiod?o, -1 w przypadku b??du
  */
-int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, 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);
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length);
 
-	if (!e || !packet) {
+	if (!sess || !e || !packet) {
 		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
 		errno = EFAULT;
 		return -1;
@@ -299,11 +358,11 @@ int gg_pubdir50_handle_reply(struct gg_e
 			break;
 	}
 
-	/* brak wynik?*/
+	/* brak wynik?w? */
 	if (length == 5)
 		return 0;
 
-	/* pomi?cz?tek odpowiedzi */
+	/* pomi? pocz?tek odpowiedzi */
 	p = packet + 5;
 
 	while (p < end) {
@@ -311,7 +370,7 @@ int gg_pubdir50_handle_reply(struct gg_e
 
 		field = p;
 
-		/* sprawd?, czy nie mamy podzia?u na kolejne pole */
+		/* sprawd?, czy nie mamy podzia?u na kolejne pole */
 		if (!*field) {
 			num++;
 			field++;
@@ -320,22 +379,22 @@ int gg_pubdir50_handle_reply(struct gg_e
 		value = NULL;
 		
 		for (p = field; p < end; p++) {
-			/* je?li mamy koniec tekstu... */
+			/* je?li mamy koniec tekstu... */
 			if (!*p) {
-				/* ...i jeszcze nie mieli?my warto?ci pola to
-				 * wiemy, ?e po tym zerze jest warto??. */
+				/* ...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?i */
+					 * warto?ci i mo?emy wychodzi?
+					 * grzecznie z p?tli */
 					break;
 			}
 		}
 		
-		/* sprawd?my, czy pole nie wychodzi poza pakiet, ?eby nie
-		 * mie?egfault?je?li serwer przestanie zaka??akiet?		/* 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) {
@@ -345,14 +404,30 @@ int gg_pubdir50_handle_reply(struct gg_e
 
 		p++;
 
-		/* je?li dostali?my namier na nast?e wyniki, to znaczy ?e
-		 * mamy koniec wynik? nie jest to kolejna osoba. */
+		/* 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;
+			if (sess->encoding == GG_ENCODING_CP1250) {
+				if (gg_pubdir50_add_n(res, num, field, value) == -1)
+					goto failure;
+			} else {
+				char *tmp;
+
+				tmp = gg_cp_to_utf8(value);
+
+				if (tmp == NULL)
+					goto failure;
+
+				if (gg_pubdir50_add_n(res, num, field, tmp) == -1) {
+					free(tmp);
+					goto failure;
+				}
+
+				free(tmp);
+			}
 		}
 	}	
 
@@ -365,16 +440,16 @@ failure:
 	return -1;
 }
 
-/*
- * gg_pubdir50_get()
+/**
+ * Pobiera pole z odpowiedzi katalogu publicznego.
  *
- * pobiera informacj? rezultatu wyszukiwania.
+ * \param res Odpowied?
+ * \param num Numer wyniku odpowiedzi
+ * \param field Nazwa pola (wielko?? liter nie ma znaczenia)
  *
- *  - res - rezultat wyszukiwania,
- *  - num - numer odpowiedzi,
- *  - field - nazwa pola (wielko??iter nie ma znaczenia).
+ * \return Warto?? pola lub \c NULL je?li nie znaleziono
  *
- * warto??ola lub NULL, je?li nie znaleziono.
+ * \ingroup pubdir50
  */
 const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
 {
@@ -399,57 +474,61 @@ const char *gg_pubdir50_get(gg_pubdir50_
 	return value;
 }
 
-/*
- * gg_pubdir50_count()
+/**
+ * Zwraca liczb? wynik?w odpowiedzi.
  *
- * zwraca ilo??ynik?anego zapytania.
+ * \param res Odpowied?
  *
- *  - res - odpowied?
+ * \return Liczba wynik?w lub -1 w przypadku b??du
  *
- * ilo??ub -1 w przypadku b??.
+ * \ingroup pubdir50
  */
 int gg_pubdir50_count(gg_pubdir50_t res)
 {
 	return (!res) ? -1 : res->count;
 }
 
-/*
- * gg_pubdir50_type()
+/**
+ * Zwraca rodzaj zapytania lub odpowiedzi.
  *
- * zwraca rodzaj zapytania lub odpowiedzi.
+ * \param res Zapytanie lub odpowied?
  *
- *  - res - zapytanie lub odpowied?
+ * \return Rodzaj lub -1 w przypadku b??du
  *
- * ilo??ub -1 w przypadku b??.
+ * \ingroup pubdir50
  */
 int gg_pubdir50_type(gg_pubdir50_t res)
 {
 	return (!res) ? -1 : res->type;
 }
 
-/*
- * gg_pubdir50_next()
+/**
+ * Zwraca numer, od kt?rego nale?y rozpocz?c kolejne wyszukiwanie.
  *
- * zwraca numer, od kt?o nale?y rozpocz??olejne wyszukiwanie, je?li
- * zale?y nam na kolejnych wynikach.
+ * D?u?sze odpowiedzi katalogu publicznego s? wysy?ane przez serwer
+ * w mniejszych paczkach. Po otrzymaniu odpowiedzi, je?li numer kolejnego
+ * wyszukiwania jest wi?kszy od zera, dalsze wyniki mo?na otrzyma? przez
+ * wywo?anie kolejnego zapytania z okre?lonym numerem pocz?tkowym.
  *
- *  - res - odpowied?
+ * \param res Odpowied?
  *
- * numer lub -1 w przypadku b??.
+ * \return Numer lub -1 w przypadku b??du
+ *
+ * \ingroup pubdir50
  */
 uin_t gg_pubdir50_next(gg_pubdir50_t res)
 {
 	return (!res) ? (unsigned) -1 : res->next;
 }
 
-/*
- * gg_pubdir50_seq()
+/**
+ * Zwraca numer sekwencyjny zapytania lub odpowiedzi.
  *
- * zwraca numer sekwencyjny zapytania lub odpowiedzi.
+ * \param res Zapytanie lub odpowied?
  *
- *  - res - zapytanie lub odpowied?
+ * \return Numer sekwencyjny lub -1 w przypadku b??du
  *
- * numer lub -1 w przypadku b??.
+ * \ingroup pubdir50
  */
 uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
 {


More information about the Commits mailing list