libpurple/protocols/gg/lib/libgadu.c

changeset 38882
bea4cc95b40f
parent 38881
25cb836b9cec
parent 38182
783878958371
child 38883
90462fef3dd8
--- a/libpurple/protocols/gg/lib/libgadu.c	Wed Oct 26 10:17:10 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3082 +0,0 @@
-/* $Id$ */
-
-/*
- *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Woźny <speedy@ziew.org>
- *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
- *                          Tomasz Chiliński <chilek@chilan.com>
- *                          Adam Wysocki <gophi@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 libgadu.c
- *
- * \brief Główny moduł biblioteki
- */
-
-#include "strman.h"
-#include "network.h"
-#include "fileio.h"
-
-#include "libgadu.h"
-#include "protocol.h"
-#include "resolver.h"
-#include "internal.h"
-#include "encoding.h"
-#include "debug.h"
-#include "session.h"
-#include "message.h"
-#include "deflate.h"
-#include "tvbuilder.h"
-#include "protobuf.h"
-#include "packets.pb-c.h"
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#ifdef GG_CONFIG_HAVE_GNUTLS
-#  include <gnutls/gnutls.h>
-#endif
-#ifdef GG_CONFIG_HAVE_OPENSSL
-#  include <openssl/err.h>
-#  include <openssl/rand.h>
-#endif
-
-/**
- * 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;
-
-/**
- * Flaga włączenia połączeń przez serwer pośredniczący.
- *
- * \ingroup proxy
- */
-int gg_proxy_enabled = 0;
-
-/**
- * Adres serwera pośredniczącego.
- *
- * \ingroup proxy
- */
-char *gg_proxy_host = NULL;
-
-/**
- * Port serwera pośredniczącego.
- *
- * \ingroup proxy
- */
-int gg_proxy_port = 0;
-
-/**
- * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
- *
- * \ingroup proxy
- */
-int gg_proxy_http_only = 0;
-
-/**
- * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
- *
- * \ingroup proxy
- */
-char *gg_proxy_username = NULL;
-
-/**
- * Hasło użytkownika do autoryzacji serwera pośredniczącego.
- *
- * \ingroup proxy
- */
-char *gg_proxy_password = NULL;
-
-#ifndef DOXYGEN
-
-#ifndef lint
-static char rcsid[] GG_UNUSED = "$Id$";
-#endif
-
-#endif /* DOXYGEN */
-
-static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients);
-static void gg_compat_message_cleanup(struct gg_session *sess);
-
-#ifdef GG_CONFIG_IS_GPL_COMPLIANT
-/**
- * Symbol zdefiniowany tylko dla libgadu zgodnego z licencją GPL.
- *
- * Zwracana wartość nie jest istotna, a ponadto może się zmienić w przyszłych
- * wersjach biblioteki. Istotne jest tylko wywołanie tej funkcji w kodzie, który
- * ma być zgodny z GPL, aby wymusić jej istnienie.
- *
- * \return Wartość 1.
- *
- * \ingroup version
- */
-int gg_is_gpl_compliant(void)
-{
-	return 1;
-}
-#endif
-
-/**
- * Zwraca wersję biblioteki.
- *
- * \return Wskaźnik na statyczny bufor z wersją biblioteki.
- *
- * \ingroup version
- */
-const char *gg_libgadu_version(void)
-{
-	return GG_LIBGADU_VERSION;
-}
-
-void * gg_new0(size_t size)
-{
-	void *ptr;
-
-	ptr = malloc(size);
-	if (ptr == NULL) {
-		gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR,
-			"//gg_new0(%" GG_SIZE_FMT
-			") not enough memory\n", size);
-		return NULL;
-	}
-
-	memset(ptr, 0, size);
-	return ptr;
-}
-
-int
-gg_required_proto(struct gg_session *gs, int protocol_version)
-{
-	if (gs->protocol_version >= protocol_version)
-		return 1;
-
-	gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_ERROR, "// requested "
-		"feature requires protocol %#02x, but %#02x is selected\n",
-		protocol_version, gs->protocol_version);
-	return 0;
-}
-
-int gg_get_dummy_fd(struct gg_session *sess)
-{
-	struct gg_session_private *p = sess->private_data;
-
-	if (p->dummyfds_created)
-		return p->dummyfds[0];
-
-	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, p->dummyfds) == -1) {
-		gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_get_dummy_fd() "
-			"unable to create pipes (errno=%d, %s)\n",
-			errno, strerror(errno));
-		return -1;
-	}
-
-	p->dummyfds_created = 1;
-	return p->dummyfds[0];
-}
-
-/**
- * \internal Liczy skrót z hasła i ziarna.
- *
- * \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)
-{
-	unsigned int x, y, z;
-
-	y = seed;
-
-	for (x = 0; *password; password++) {
-		x = (x & 0xffffff00) | *password;
-		y ^= x;
-		y += x;
-		x <<= 8;
-		y ^= x;
-		x <<= 8;
-		y -= x;
-		x <<= 8;
-		y ^= x;
-
-		z = y & 0x1F;
-		y = (y << z) | (y >> (32 - z));
-	}
-
-	return y;
-}
-
-/**
- * \internal Odbiera od serwera dane binarne.
- *
- * Funkcja odbiera dane od serwera zajmując się SSL/TLS w razie konieczności.
- * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
- * wywołaniami systemowymi.
- *
- * \param sess Struktura sesji
- * \param buf Bufor na danymi
- * \param length Długość bufora
- *
- * \return To samo co funkcja systemowa \c read
- */
-int gg_read(struct gg_session *sess, char *buf, int length)
-{
-	struct gg_session_private *p = sess->private_data;
-	int res;
-
-#ifdef GG_CONFIG_HAVE_GNUTLS
-	if (sess->ssl != NULL) {
-		for (;;) {
-			res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length);
-
-			if (res < 0) {
-				if (res == GNUTLS_E_AGAIN)
-					errno = EAGAIN;
-				else if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
-					continue;
-				else
-					errno = EINVAL;
-
-				return -1;
-			}
-
-			return res;
-		}
-	}
-#endif
-
-#ifdef GG_CONFIG_HAVE_OPENSSL
-	if (sess->ssl != NULL) {
-		for (;;) {
-			int err;
-
-			res = SSL_read(sess->ssl, buf, length);
-
-			if (res < 0) {
-				err = SSL_get_error(sess->ssl, res);
-
-				if (err == SSL_ERROR_SYSCALL && errno == EINTR)
-					continue;
-
-				if (err == SSL_ERROR_WANT_READ)
-					errno = EAGAIN;
-				else if (err != SSL_ERROR_SYSCALL)
-					errno = EINVAL;
-
-				return -1;
-			}
-
-			return res;
-		}
-	}
-#endif
-
-	if (p->socket_handle != NULL) {
-		if (p->socket_manager.read_cb == NULL) {
-			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-				"// gg_read() socket_manager.read callback is "
-				"empty\n");
-			errno = EINVAL;
-			return -1;
-		}
-
-		do {
-			res = p->socket_manager.read_cb(
-				p->socket_manager.cb_data, p->socket_handle,
-				(unsigned char*)buf, length);
-		} while (res < 0 && errno == EINTR);
-
-		if (res < 0) {
-			if (errno == EAGAIN)
-				return -1;
-			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-				"// gg_read() unexpected errno=%d\n", errno);
-			errno = EINVAL;
-		}
-		return res;
-	}
-
-	for (;;) {
-		res = recv(sess->fd, buf, length, 0);
-
-		if (res == -1 && errno == EINTR)
-			continue;
-
-		return res;
-	}
-}
-
-/**
- * \internal Wysyła do serwera dane binarne.
- *
- * Funkcja wysyła dane do serwera zajmując się SSL/TLS w razie konieczności.
- * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
- * wywołaniami systemowymi.
- *
- * \note Funkcja nie zajmuje się buforowaniem wysyłanych danych (patrz
- * gg_write()).
- *
- * \param sess Struktura sesji
- * \param buf Bufor z danymi
- * \param length Długość bufora
- *
- * \return To samo co funkcja systemowa \c write
- */
-static int gg_write_common(struct gg_session *sess, const char *buf, int length)
-{
-	struct gg_session_private *p = sess->private_data;
-	int res;
-
-#ifdef GG_CONFIG_HAVE_GNUTLS
-	if (sess->ssl != NULL) {
-		for (;;) {
-			res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length);
-
-			if (res < 0) {
-				if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
-					continue;
-
-				if (res == GNUTLS_E_AGAIN)
-					errno = EAGAIN;
-				else
-					errno = EINVAL;
-
-				return -1;
-			}
-
-			return res;
-		}
-	}
-#endif
-
-#ifdef GG_CONFIG_HAVE_OPENSSL
-	if (sess->ssl != NULL) {
-		for (;;) {
-			int err;
-
-			res = SSL_write(sess->ssl, buf, length);
-
-			if (res < 0) {
-				err = SSL_get_error(sess->ssl, res);
-
-				if (err == SSL_ERROR_SYSCALL && errno == EINTR)
-					continue;
-
-				if (err == SSL_ERROR_WANT_WRITE)
-					errno = EAGAIN;
-				else if (err != SSL_ERROR_SYSCALL)
-					errno = EINVAL;
-
-				return -1;
-			}
-
-			return res;
-		}
-	}
-#endif
-
-	if (p->socket_handle != NULL) {
-		if (p->socket_manager.write_cb == NULL) {
-			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-				"// gg_write_common() socket_manager.write "
-				"callback is empty\n");
-			errno = EINVAL;
-			return -1;
-		}
-
-		do {
-			res = p->socket_manager.write_cb(
-				p->socket_manager.cb_data, p->socket_handle,
-				(const unsigned char*)buf, length);
-		} while (res < 0 && errno == EINTR);
-
-		if (res < 0) {
-			if (errno == EAGAIN)
-				return -1;
-			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-				"// gg_read() unexpected errno=%d\n", errno);
-			errno = EINVAL;
-		}
-
-		return res;
-	}
-
-	for (;;) {
-		res = send(sess->fd, buf, length, 0);
-
-		if (res == -1 && errno == EINTR)
-			continue;
-
-		return res;
-	}
-}
-
-/**
- * \internal Wysyła do serwera dane binarne.
- *
- * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności.
- *
- * \param sess Struktura sesji
- * \param buf Bufor z danymi
- * \param length Długość bufora
- *
- * \return To samo co funkcja systemowa \c write
- */
-int gg_write(struct gg_session *sess, const char *buf, int length)
-{
-	int res = 0;
-
-	if (!sess->async) {
-		int written = 0;
-
-		while (written < length) {
-			res = gg_write_common(sess, buf + written, length - written);
-
-			if (res == -1)
-				return -1;
-
-			written += res;
-			res = written;
-		}
-	} else {
-		if (sess->send_buf == NULL) {
-			res = gg_write_common(sess, buf, length);
-
-			if (res == -1 && errno == EAGAIN)
-				res = 0;
-			if (res == -1)
-				return -1;
-		}
-
-		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;
-}
-
-void gg_close(struct gg_session *sess)
-{
-	struct gg_session_private *p = sess->private_data;
-	int errno_copy;
-
-	errno_copy = errno;
-
-	if (!p->socket_is_external) {
-		if (sess->fd != -1)
-			close(sess->fd);
-	} else {
-		assert(p->socket_manager_type !=
-			GG_SOCKET_MANAGER_TYPE_INTERNAL);
-		if (p->socket_handle != NULL) {
-			p->socket_manager.close_cb(p->socket_manager.cb_data,
-				p->socket_handle);
-		}
-		p->socket_is_external = 0;
-	}
-	sess->fd = -1;
-	p->socket_handle = NULL;
-
-	while (p->event_queue) {
-		gg_eventqueue_t *next = p->event_queue->next;
-		gg_event_free(p->event_queue->event);
-		free(p->event_queue);
-		p->event_queue = next;
-	}
-
-	while (p->imgout_queue) {
-		gg_imgout_queue_t *next = p->imgout_queue->next;
-		free(p->imgout_queue);
-		p->imgout_queue = next;
-	}
-
-	if (p->dummyfds_created) {
-		close(p->dummyfds[0]);
-		close(p->dummyfds[1]);
-		p->dummyfds_created = 0;
-	}
-
-	gg_compat_message_cleanup(sess);
-
-	errno = errno_copy;
-}
-
-/**
- * \internal Odbiera pakiet od serwera.
- *
- * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca
- * w zaalokowanym buforze.
- *
- * Przy połączeniach asynchronicznych, funkcja może nie być w stanie
- * skompletować całego pakietu -- w takim przypadku zwróci \c NULL, a kodem błędu
- * będzie \c EAGAIN.
- *
- * \param sess Struktura sesji
- *
- * \return Wskaźnik do zaalokowanego bufora
- */
-void *gg_recv_packet(struct gg_session *sess)
-{
-	struct gg_header *gh;
-	char *packet;
-	int res;
-	size_t len;
-	uint32_t ghlen = 0;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
-
-	if (sess == NULL) {
-		errno = EFAULT;
-		return NULL;
-	}
-
-	for (;;) {
-		if (sess->recv_buf == NULL && sess->recv_done == 0) {
-			sess->recv_buf = malloc(sizeof(struct gg_header) + 1);
-
-			if (sess->recv_buf == NULL) {
-				gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
-				return NULL;
-			}
-		}
-
-		gh = (struct gg_header*) sess->recv_buf;
-
-		if ((size_t) sess->recv_done < sizeof(struct gg_header)) {
-			len = sizeof(struct gg_header) - sess->recv_done;
-			gg_debug_session(sess, GG_DEBUG_NET,
-				"// gg_recv_packet() header: %d done, "
-				"%" GG_SIZE_FMT " to go\n",
-				sess->recv_done, len);
-		} else {
-			ghlen = gh ? gg_fix32(gh->length) : 0;
-
-			if (ghlen > 65535) {
-				gg_debug_session(sess, GG_DEBUG_ERROR,
-					"// gg_recv_packet() invalid packet "
-					"length (%d)\n", ghlen);
-				errno = ERANGE;
-				goto fail;
-			}
-
-			if ((size_t) sess->recv_done >= sizeof(struct gg_header) + ghlen) {
-				gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() and that's it\n");
-				break;
-			}
-
-			len = sizeof(struct gg_header) + ghlen - sess->recv_done;
-
-			gg_debug_session(sess, GG_DEBUG_NET,
-				"// gg_recv_packet() payload: %d done, "
-				"%u length, %" GG_SIZE_FMT " to go\n",
-				sess->recv_done, ghlen, len);
-		}
-
-		res = gg_read(sess, sess->recv_buf + sess->recv_done, len);
-
-		if (res == 0) {
-			errno = ECONNRESET;
-			gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() connection broken\n");
-			goto fail;
-		}
-
-		if (res == -1 && errno == EAGAIN) {
-			gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() resource temporarily unavailable\n");
-			goto eagain;
-		}
-
-		if (res == -1) {
-			gg_debug_session(sess, GG_DEBUG_ERROR,
-				"// gg_recv_packet() read failed: errno=%d, "
-				"%s\n", errno, strerror(errno));
-			goto fail;
-		}
-
-		gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() read %d bytes\n", res);
-
-		if (sess->recv_done + res == sizeof(struct gg_header)) {
-			char *tmp;
-			ghlen = gh ? gg_fix32(gh->length) : 0;
-
-			gg_debug_session(sess, GG_DEBUG_NET,
-				"// gg_recv_packet() header complete, "
-				"payload %d bytes\n", ghlen);
-
-			if (ghlen == 0)
-				break;
-
-			if (ghlen > 65535) {
-				gg_debug_session(sess, GG_DEBUG_ERROR,
-					"// gg_recv_packet() invalid packet "
-					"length (%d)\n", ghlen);
-				errno = ERANGE;
-				goto fail;
-			}
-
-			tmp = realloc(sess->recv_buf, sizeof(struct gg_header) + ghlen + 1);
-
-			if (tmp == NULL) {
-				gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
-				goto fail;
-			}
-
-			sess->recv_buf = tmp;
-		}
-
-		sess->recv_done += res;
-	}
-
-	packet = sess->recv_buf;
-	sess->recv_buf = NULL;
-	sess->recv_done = 0;
-
-	if (gh == NULL)
-		goto fail;
-
-	/* Czasami zakładamy, że teksty w pakietach są zakończone zerem */
-	packet[sizeof(struct gg_header) + ghlen] = 0;
-
-	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet(type=0x%.2x, "
-		"length=%d)\n", gg_fix32(gh->type), ghlen);
-	gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(struct gg_header) + ghlen);
-
-	gh->type = gg_fix32(gh->type);
-	gh->length = ghlen;
-
-	return packet;
-
-fail:
-	free(sess->recv_buf);
-	sess->recv_buf = NULL;
-	sess->recv_done = 0;
-
-eagain:
-	return NULL;
-}
-
-/**
- * \internal Wysyła pakiet 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.
- *
- * \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
- *
- * \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;
-	unsigned int tmp_length;
-	void *payload;
-	unsigned int payload_length;
-	va_list ap;
-	int res;
-
-	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_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for packet header\n");
-		return -1;
-	}
-
-	va_start(ap, type);
-
-	payload = va_arg(ap, void *);
-
-	while (payload) {
-		char *tmp2;
-
-		payload_length = va_arg(ap, unsigned int);
-
-		if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
-			gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for payload\n");
-			free(tmp);
-			va_end(ap);
-			return -1;
-		}
-
-		tmp = tmp2;
-
-		memcpy(tmp + tmp_length, payload, payload_length);
-		tmp_length += payload_length;
-
-		payload = va_arg(ap, void *);
-	}
-
-	va_end(ap);
-
-	h = (struct gg_header*) tmp;
-	h->type = gg_fix32(type);
-	h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
-
-	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet(type=0x%.2x, "
-		"length=%d)\n", gg_fix32(h->type), gg_fix32(h->length));
-	gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length);
-
-	res = gg_write(sess, tmp, tmp_length);
-
-	free(tmp);
-
-	if (res == -1) {
-		gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() "
-			"write() failed. res = %d, errno = %d (%s)\n",
-			res, errno, strerror(errno));
-		return -1;
-	}
-
-	if (sess->async) {
-		gg_debug_session(sess, GG_DEBUG_NET, "// 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;
-}
-
-/**
- * \internal Funkcja zwrotna sesji.
- *
- * 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 *sess)
-{
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
-}
-
-/**
- * Łączy się z serwerem Gadu-Gadu.
- *
- * 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.
- *
- * 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.
- *
- * \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().
- *
- * \note Funkcja zwróci błąd ENOSYS jeśli połączenie SSL było wymagane, ale
- *       obsługa SSL nie jest wkompilowana.
- *
- * \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)
-{
-	struct gg_session *sess = NULL;
-	struct gg_session_private *sess_private = NULL;
-
-	if (p == NULL) {
-		gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
-		errno = EFAULT;
-		return NULL;
-	}
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);
-
-	sess = malloc(sizeof(struct gg_session));
-
-	if (sess == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
-		goto fail;
-	}
-
-	memset(sess, 0, sizeof(struct gg_session));
-	sess->fd = -1;
-
-	sess_private = malloc(sizeof(struct gg_session_private));
-
-	if (sess_private == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session private data\n");
-		goto fail;
-	}
-
-	memset(sess_private, 0, sizeof(struct gg_session_private));
-	sess->private_data = sess_private;
-
-	if (p->password == NULL || p->uin == 0) {
-		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
-		errno = EFAULT;
-		goto fail;
-	}
-
-	if (!(sess->password = strdup(p->password))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
-		goto fail;
-	}
-
-	if (p->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;
-	}
-
-	sess->uin = p->uin;
-	sess->state = GG_STATE_RESOLVING;
-	sess->check = GG_CHECK_READ;
-	sess->timeout = GG_DEFAULT_TIMEOUT;
-	sess->async = p->async;
-	sess->type = GG_SESSION_GG;
-	sess->initial_status = p->status;
-	sess->callback = gg_session_callback;
-	sess->destroy = gg_free_session;
-	sess->port = p->server_port;
-	sess->server_addr = p->server_addr;
-	sess->external_port = p->external_port;
-	sess->external_addr = p->external_addr;
-	sess->client_addr = p->client_addr;
-	sess->client_port = p->client_port;
-
-	if (GG_LOGIN_PARAMS_HAS_FIELD(p, compatibility))
-		sess_private->compatibility = p->compatibility;
-
-	if (GG_LOGIN_PARAMS_HAS_FIELD(p, connect_host) && p->connect_host != NULL) {
-		int port = 0;
-		char *colon;
-
-		sess->connect_host = strdup(p->connect_host);
-		if (sess->connect_host == NULL)
-			goto fail;
-
-		colon = strchr(sess->connect_host, ':');
-		if (colon != NULL) {
-			colon[0] = '\0';
-			port = atoi(colon + 1);
-		}
-		if (port > 0)
-			sess->port = port;
-	}
-
-	if (GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager_type) &&
-		GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager) &&
-		p->socket_manager_type != GG_SOCKET_MANAGER_TYPE_INTERNAL)
-	{
-		if ((unsigned int)p->socket_manager_type >
-			GG_SOCKET_MANAGER_TYPE_TLS)
-		{
-			gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_login()"
-				" invalid arguments. unknown socket manager "
-				"type (%d)\n", p->socket_manager_type);
-			errno = EFAULT;
-			goto fail;
-		} else {
-			sess_private->socket_manager_type =
-				p->socket_manager_type;
-			memcpy(&sess_private->socket_manager,
-				&p->socket_manager,
-				sizeof(gg_socket_manager_t));
-		}
-	} else {
-		sess_private->socket_manager_type =
-			GG_SOCKET_MANAGER_TYPE_INTERNAL;
-	}
-
-	if (GG_LOGIN_PARAMS_HAS_FIELD(p, host_white_list) &&
-		p->host_white_list != NULL)
-	{
-		sess_private->host_white_list =
-			gg_strarr_dup(p->host_white_list);
-		if (sess_private->host_white_list == NULL)
-			goto fail;
-	}
-
-	if (p->protocol_features == 0) {
-		sess->protocol_features = GG_FEATURE_MSG80 |
-			GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC |
-			GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 |
-			GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK |
-			GG_FEATURE_TYPING_NOTIFICATION;
-	} else {
-		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;
-	}
-
-	if (!(sess->status_flags = p->status_flags))
-		sess->status_flags = GG_STATUS_FLAG_UNKNOWN | GG_STATUS_FLAG_SPAM;
-
-	if (!p->protocol_version)
-		sess->protocol_version = GG_DEFAULT_PROTOCOL_VERSION;
-	else if (p->protocol_version < 0x2e) {
-		gg_debug(GG_DEBUG_MISC, "// gg_login() libgadu no longer support protocol < 0x2e\n");
-		sess->protocol_version = 0x2e;
-	} else
-		sess->protocol_version = p->protocol_version;
-
-	if (p->client_version && strcmp(p->client_version, "-") != 0)
-		sess->client_version = strdup(p->client_version);
-	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) {
-		sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1);
-
-		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) > GG_STATUS_DESCR_MAXSIZE)
-			sess->initial_descr[GG_STATUS_DESCR_MAXSIZE] = 0;
-	}
-
-	if (p->tls != GG_SSL_DISABLED) {
-#if !defined(GG_CONFIG_HAVE_GNUTLS) && !defined(GG_CONFIG_HAVE_OPENSSL)
-		gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
-
-		if (p->tls == GG_SSL_REQUIRED) {
-			errno = ENOSYS;
-			goto fail;
-		}
-#else
-		sess->ssl_flag = p->tls;
-#endif
-	}
-
-	if (p->hash_type)
-		sess->hash_type = p->hash_type;
-	else
-		sess->hash_type = GG_LOGIN_HASH_SHA1;
-
-	if (sess->server_addr == 0 && sess->connect_host == NULL) {
-		if (gg_proxy_enabled) {
-			sess->resolver_host = gg_proxy_host;
-			sess->proxy_port = gg_proxy_port;
-			sess->state = (sess->async) ?
-				GG_STATE_RESOLVE_PROXY_HUB_ASYNC :
-				GG_STATE_RESOLVE_PROXY_HUB_SYNC;
-		} else {
-			sess->resolver_host = GG_APPMSG_HOST;
-			sess->proxy_port = 0;
-			sess->state = (sess->async) ? GG_STATE_RESOLVE_HUB_ASYNC : GG_STATE_RESOLVE_HUB_SYNC;
-		}
-	} else {
-		if (sess->connect_host != NULL)
-			sess->server_addr = 0;
-		else {
-			/* XXX inet_ntoa i wielowątkowość */
-			sess->connect_host = strdup(inet_ntoa(*(struct in_addr*) &sess->server_addr));
-			if (sess->connect_host == NULL)
-				goto fail;
-		}
-		sess->connect_index = 0;
-
-		if (gg_proxy_enabled) {
-			sess->resolver_host = gg_proxy_host;
-			sess->proxy_port = gg_proxy_port;
-			if (sess->port == 0)
-				sess->connect_port[0] = GG_HTTPS_PORT;
-			else
-				sess->connect_port[0] = sess->port;
-			sess->connect_port[1] = 0;
-			sess->state = (sess->async) ? GG_STATE_RESOLVE_PROXY_GG_ASYNC : GG_STATE_RESOLVE_PROXY_GG_SYNC;
-		} else {
-			sess->resolver_host = sess->connect_host;
-			if (sess->port == 0) {
-				if (sess->ssl_flag == GG_SSL_DISABLED) {
-					sess->connect_port[0] = GG_DEFAULT_PORT;
-					sess->connect_port[1] = GG_HTTPS_PORT;
-				} else {
-					sess->connect_port[0] = GG_HTTPS_PORT;
-					sess->connect_port[1] = 0;
-				}
-			} else {
-				sess->connect_port[0] = sess->port;
-				sess->connect_port[1] = 0;
-			}
-			sess->state = (sess->async) ? GG_STATE_RESOLVE_GG_ASYNC : GG_STATE_RESOLVE_GG_SYNC;
-		}
-	}
-
-	/* XXX inaczej gg_watch_fd() wyjdzie z timeoutem */
-	sess->timeout = GG_DEFAULT_TIMEOUT;
-
-	if (!sess->async) {
-		while (!GG_SESSION_IS_CONNECTED(sess)) {
-			struct gg_event *ge;
-
-			ge = gg_watch_fd(sess);
-
-			if (ge == NULL) {
-				gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
-				goto fail;
-			}
-
-			if (ge->type == GG_EVENT_CONN_FAILED) {
-				errno = EACCES;
-				gg_debug(GG_DEBUG_MISC, "// gg_session_connect() could not login\n");
-				gg_event_free(ge);
-				goto fail;
-			}
-
-			gg_event_free(ge);
-		}
-	} else {
-		struct gg_event *ge;
-
-		ge = gg_watch_fd(sess);
-
-		if (ge == NULL) {
-			gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
-			goto fail;
-		}
-
-		gg_event_free(ge);
-	}
-
-	return sess;
-
-fail:
-	gg_free_session(sess);
-
-	return NULL;
-}
-
-/**
- * Wysyła do serwera pakiet utrzymania połączenia.
- *
- * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia,
- * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie
- * połączenie.
- *
- * \param sess Struktura sesji
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup login
- */
-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. Aby mieć pewność, że opis statusu
- * zostanie zachowany, należy ustawić stan \c GG_STATUS_NOT_AVAIL_DESCR
- * za pomocą funkcji \c gg_change_status_descr() i poczekać na zdarzenie
- * \c GG_EVENT_DISCONNECT_ACK.
- *
- * \param sess Struktura sesji
- *
- * \ingroup login
- */
-void gg_logoff(struct gg_session *sess)
-{
-	if (!sess)
-		return;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
-
-#ifdef GG_CONFIG_HAVE_GNUTLS
-	if (sess->ssl != NULL)
-		gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR);
-#endif
-
-#ifdef GG_CONFIG_HAVE_OPENSSL
-	if (sess->ssl != NULL)
-		SSL_shutdown(sess->ssl);
-#endif
-
-	sess->resolver_cleanup(&sess->resolver, 1);
-
-	gg_close(sess);
-
-	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;
-	gg_chat_list_t *chat;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_free_session(%p);\n", sess);
-
-	if (sess == NULL)
-		return;
-
-	/* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
-
-	free(sess->resolver_result);
-	free(sess->connect_host);
-	free(sess->password);
-	free(sess->initial_descr);
-	free(sess->client_version);
-	free(sess->header_buf);
-	free(sess->recv_buf);
-
-#ifdef GG_CONFIG_HAVE_GNUTLS
-	if (sess->ssl != NULL) {
-		gg_session_gnutls_t *tmp;
-
-		tmp = (gg_session_gnutls_t*) sess->ssl;
-		gnutls_deinit(tmp->session);
-		gnutls_certificate_free_credentials(tmp->xcred);
-		gnutls_global_deinit();
-		free(sess->ssl);
-	}
-#endif
-
-#ifdef GG_CONFIG_HAVE_OPENSSL
-	if (sess->ssl)
-		SSL_free(sess->ssl);
-
-	if (sess->ssl_ctx)
-		SSL_CTX_free(sess->ssl_ctx);
-#endif
-
-	if (sess->resolver_cleanup != NULL)
-		sess->resolver_cleanup(&sess->resolver, 1);
-
-	gg_close(sess);
-
-	while (sess->images) {
-		struct gg_image_queue *next = sess->images->next;
-
-		gg_image_queue_remove(sess, sess->images, 1);
-
-		/* a fix for false-positive NULL-dereference */
-		sess->images = next;
-	}
-
-	free(sess->send_buf);
-
-	for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
-		dcc->sess = NULL;
-
-	chat = sess->private_data->chat_list;
-	while (chat != NULL) {
-		gg_chat_list_t *next = chat->next;
-		free(chat->participants);
-		free(chat);
-		chat = next;
-	}
-
-	gg_strarr_free(sess->private_data->host_white_list);
-
-	free(sess->private_data);
-
-	free(sess);
-}
-
-/**
- * Zmienia status użytkownika.
- *
- * \param sess Struktura sesji
- * \param status Nowy status użytkownika
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \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_descr(sess, status, NULL);
-}
-
-/**
- * Zmienia status użytkownika na status opisowy.
- *
- * \param sess Struktura sesji
- * \param status Nowy status użytkownika
- * \param descr Opis statusu użytkownika (lub \c NULL)
- *
- * \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_status80 p;
-	char *gen_descr = NULL;
-	int descr_len = 0;
-	int descr_null_len = 0;
-	int res;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	sess->status = status;
-
-	if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
-		descr = gen_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1);
-
-		if (!gen_descr)
-			return -1;
-	}
-
-	if (descr) {
-		descr_len = strlen(descr);
-
-		if (descr_len > GG_STATUS_DESCR_MAXSIZE)
-			descr_len = GG_STATUS_DESCR_MAXSIZE;
-
-		/* XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8 */
-	} else {
-		descr = "";
-	}
-
-	p.status		= gg_fix32(status);
-	p.flags			= gg_fix32(sess->status_flags);
-	p.description_size	= gg_fix32(descr_len);
-
-	if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
-		p.flags = gg_fix32(0x00000014);
-		descr_null_len = 1;
-	}
-
-	res = gg_send_packet(sess, GG_NEW_STATUS80,
-		&p, sizeof(p), descr, descr_len,
-		"\x00", descr_null_len, NULL);
-
-	free(gen_descr);
-
-	if (GG_S_NA(status)) {
-		sess->state = GG_STATE_DISCONNECTING;
-		sess->timeout = GG_TIMEOUT_DISCONNECT;
-	}
-
-	return res;
-}
-
-/**
- * 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 ts 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 ts)
-{
-	gg_debug_session(sess, GG_DEBUG_FUNCTION,
-		"** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n",
-		sess, status, descr, ts);
-
-	return gg_change_status_descr(sess, status, descr);
-}
-
-/**
- * Funkcja zmieniająca flagi statusu.
- *
- * \param sess Struktura sesji
- * \param flags Nowe flagi statusu
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \note Aby zmiany weszły w życie, należy ponownie ustawić status za pomocą
- * funkcji z rodziny \c gg_change_status().
- *
- * \ingroup status
- */
-int gg_change_status_flags(struct gg_session *sess, int flags)
-{
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_flags(%p, 0x%08x);\n", sess, flags);
-
-	if (sess == NULL) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	sess->status_flags = flags;
-
-	return 0;
-}
-
-#ifndef DOXYGEN
-
-static int gg_send_message_110(struct gg_session *sess,
-	uin_t recipient, uint64_t chat_id,
-	const char *message, int is_html)
-{
-	GG110SendMessage msg = GG110_SEND_MESSAGE__INIT;
-	int packet_type = recipient ? GG_SEND_MSG110 : GG_CHAT_SEND_MSG;
-	int seq;
-	char *html_message_gen = NULL, *plain_message_gen = NULL;
-	const char *html_message, *plain_message;
-	int succ = 1;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION,
-		"** gg_send_message_110(%p, %u, %" PRIu64 ", %p, %d);\n",
-		sess, recipient, chat_id, message, is_html);
-
-	if (message == NULL)
-		return -1;
-
-	if ((recipient == 0) == (chat_id == 0))
-		return -1;
-
-	if (is_html) {
-		html_message = message;
-
-		if (sess->encoding != GG_ENCODING_UTF8) {
-			html_message = html_message_gen = gg_encoding_convert(
-				html_message, sess->encoding, GG_ENCODING_UTF8,
-				-1, -1);
-			if (html_message_gen == NULL)
-				return -1;
-		}
-
-		plain_message = plain_message_gen =
-			gg_message_html_to_text_110(html_message);
-		if (plain_message_gen == NULL) {
-			free(html_message_gen);
-			return -1;
-		}
-	} else {
-		plain_message = message;
-
-		if (sess->encoding != GG_ENCODING_UTF8) {
-			plain_message = plain_message_gen = gg_encoding_convert(
-				plain_message, sess->encoding, GG_ENCODING_UTF8,
-				-1, -1);
-			if (plain_message_gen == NULL)
-				return -1;
-		}
-
-		html_message = html_message_gen =
-			gg_message_text_to_html_110(plain_message, -1);
-		if (html_message_gen == NULL) {
-			free(plain_message_gen);
-			return -1;
-		}
-	}
-
-	seq = ++sess->seq;
-
-	if (recipient) {
-		msg.has_recipient = 1;
-		gg_protobuf_set_uin(&msg.recipient, recipient, NULL);
-	}
-
-	msg.seq = seq;
-
-	/* rzutujemy z const, ale msg i tak nie będzie modyfikowany */
-	msg.msg_plain = (char*)plain_message;
-	msg.msg_xhtml = (char*)html_message;
-
-	if (chat_id) {
-		msg.dummy3 = "";
-		msg.has_chat_id = 1;
-		msg.chat_id = chat_id;
-	}
-
-	if (!GG_PROTOBUF_SEND(sess, NULL, packet_type, gg110_send_message, msg))
-		succ = 0;
-
-	free(html_message_gen);
-	free(plain_message_gen);
-
-	return succ ? seq : -1;
-}
-
-static char *
-gg_message_legacy_text_to_html(const char *src, gg_encoding_t encoding,
-	const unsigned char *format, size_t format_len)
-{
-	size_t len;
-	char *dst;
-
-	if (format == NULL || format_len <= 3) {
-		format = NULL;
-		format_len = 0;
-	} else {
-		format += 3;
-		format_len -= 3;
-	}
-
-	len = gg_message_text_to_html(NULL, src, encoding, format, format_len);
-
-	dst = malloc(len + 1);
-	if (dst == NULL)
-		return NULL;
-
-	gg_message_text_to_html(dst, src, encoding, format, format_len);
-
-	return dst;
-}
-
-/**
- * \internal Wysyła wiadomość.
- *
- * 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
- * \param format Informacje o formatowaniu
- * \param formatlen Długość informacji o formatowaniu
- * \param html_message Treść wiadomości HTML
- *
- * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
- *
- * \ingroup messages
- */
-static int gg_send_message_common(struct gg_session *sess, int msgclass,
-	int recipients_count, uin_t *recipients, const unsigned char *message,
-	const unsigned char *format, int formatlen,
-	const unsigned char *html_message)
-{
-	struct gg_send_msg80 s80;
-	const char *cp_msg = NULL, *utf_html_msg = NULL;
-	char *recoded_msg = NULL, *recoded_html_msg = NULL;
-	unsigned char *generated_format = NULL;
-	int seq_no = -1;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_common("
-		"%p, %d, %d, %p, %p, %p, %d, %p);\n", sess, msgclass,
-		recipients_count, recipients, message, format,
-		formatlen, html_message);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	if ((message == NULL && html_message == NULL) ||
-		recipients_count <= 0 || recipients_count > 0xffff ||
-		recipients == NULL || (format == NULL && formatlen != 0))
-	{
-		errno = EINVAL;
-		return -1;
-	}
-
-	if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 &&
-		recipients_count == 1)
-	{
-		int is_html = (html_message != NULL);
-		char *formatted_msg = NULL;
-
-		if (formatlen > 3 && !is_html) {
-			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_WARNING,
-				"// gg_send_message_common() using legacy "
-				"formatting with new protocol\n");
-			formatted_msg = gg_message_legacy_text_to_html(
-				(const char *)message, sess->encoding,
-				format, formatlen);
-			if (formatted_msg == NULL)
-				goto cleanup;
-			html_message = (unsigned char*)formatted_msg;
-			is_html = 1;
-		}
-
-		seq_no = gg_send_message_110(sess, recipients[0], 0,
-			(const char*)(is_html ? html_message : message),
-			is_html);
-		goto cleanup;
-	}
-
-	if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 &&
-		!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_LEGACY_CONFER))
-	{
-		gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-			"// gg_send_message_common() legacy conferences disabled\n");
-		errno = EINVAL;
-		return -1;
-	}
-
-	if (message == NULL) {
-		char *tmp_msg;
-		size_t len, fmt_len;
-		uint16_t fixed_fmt_len;
-
-		len = gg_message_html_to_text(NULL, NULL, &fmt_len, (const char*) html_message, sess->encoding);
-
-		tmp_msg = malloc(len + 1);
-
-		if (tmp_msg == NULL)
-			goto cleanup;
-
-		if (fmt_len != 0) {
-			generated_format = malloc(fmt_len + 3);
-
-			if (generated_format == NULL) {
-				free(tmp_msg);
-				goto cleanup;
-			}
-
-			generated_format[0] = '\x02';
-			fixed_fmt_len = gg_fix16(fmt_len);
-			memcpy(generated_format + 1, &fixed_fmt_len, sizeof(fixed_fmt_len));
-			gg_message_html_to_text(tmp_msg, generated_format + 3,
-				NULL, (const char*)html_message, sess->encoding);
-
-			format = generated_format;
-			formatlen = fmt_len + 3;
-		} else {
-			gg_message_html_to_text(tmp_msg, NULL, NULL, (const char*) html_message, sess->encoding);
-
-			format = NULL;
-			formatlen = 0;
-		}
-
-		if (sess->encoding != GG_ENCODING_CP1250) {
-			cp_msg = recoded_msg = gg_encoding_convert(tmp_msg, sess->encoding, GG_ENCODING_CP1250, -1, -1);
-			free(tmp_msg);
-
-			if (cp_msg == NULL)
-				goto cleanup;
-		} else {
-			cp_msg = recoded_msg = tmp_msg;
-		}
-	} else {
-		if (sess->encoding != GG_ENCODING_CP1250) {
-			cp_msg = recoded_msg = gg_encoding_convert(
-				(const char*)message, sess->encoding,
-				GG_ENCODING_CP1250, -1, -1);
-
-			if (cp_msg == NULL)
-				goto cleanup;
-		} else {
-			cp_msg = (const char*) message;
-		}
-	}
-
-	if (html_message == NULL) {
-		char *formatted_msg;
-
-		formatted_msg = gg_message_legacy_text_to_html(
-			(const char*)message, sess->encoding, format, formatlen);
-		if (formatted_msg == NULL)
-			goto cleanup;
-
-		if (sess->encoding == GG_ENCODING_UTF8) {
-			utf_html_msg = recoded_html_msg = formatted_msg;
-		} else {
-			utf_html_msg = recoded_html_msg = gg_encoding_convert(
-				formatted_msg, sess->encoding,
-				GG_ENCODING_UTF8, -1, -1);
-			free(formatted_msg);
-
-			if (utf_html_msg == NULL)
-				goto cleanup;
-		}
-	} else {
-		if (sess->encoding == GG_ENCODING_UTF8) {
-			utf_html_msg = (const char*) html_message;
-		} else {
-			utf_html_msg = recoded_html_msg = gg_encoding_convert(
-				(const char*)html_message, sess->encoding,
-				GG_ENCODING_UTF8, -1, -1);
-
-			if (utf_html_msg == NULL)
-				goto cleanup;
-		}
-	}
-
-	/* 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;
-
-	s80.seq = gg_fix32(seq_no);
-	s80.msgclass = gg_fix32(msgclass);
-	s80.offset_plain = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1);
-	s80.offset_attr = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1 + strlen(cp_msg) + 1);
-
-	if (recipients_count > 1) {
-		struct gg_msg_recipients r;
-		int i, j, k;
-		uin_t *recps;
-
-		r.flag = GG_MSG_OPTION_CONFERENCE;
-		r.count = gg_fix32(recipients_count - 1);
-
-		recps = malloc(sizeof(uin_t) * (recipients_count - 1));
-
-		if (!recps) {
-			seq_no = -1;
-			goto cleanup;
-		}
-
-		for (i = 0; i < recipients_count; i++) {
-			for (j = 0, k = 0; j < recipients_count; j++) {
-				if (j != i) {
-					recps[k] = gg_fix32(recipients[j]);
-					k++;
-				}
-			}
-
-			s80.recipient = gg_fix32(recipients[i]);
-
-			if (gg_send_packet(sess, GG_SEND_MSG80, &s80,
-				sizeof(s80), utf_html_msg,
-				strlen(utf_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 {
-		s80.recipient = gg_fix32(recipients[0]);
-
-		if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80),
-			utf_html_msg, strlen(utf_html_msg) + 1, cp_msg,
-			strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
-		{
-			seq_no = -1;
-		}
-	}
-
-cleanup:
-	free(recoded_msg);
-	free(recoded_html_msg);
-	free(generated_format);
-
-	if (seq_no >= 0)
-		gg_compat_message_sent(sess, seq_no, recipients_count, recipients);
-
-	return seq_no;
-}
-
-#endif /* DOXYGEN */
-
-/**
- * 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);
-
-	if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
-		int seq_no;
-
-		seq_no = gg_send_message_110(sess, recipient, 0, (const char*)message, 0);
-
-		if (seq_no >= 0)
-			gg_compat_message_sent(sess, seq_no, 1, &recipient);
-
-		return seq_no;
-	}
-
-	return gg_send_message_common(sess, msgclass, 1, &recipient, message,
-		(const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
-		9, NULL);
-}
-
-/**
- * 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_common(sess, msgclass, 1, &recipient, message, format, formatlen, NULL);
-}
-
-/**
- * Wysyła formatowaną wiadomość HTML.
- *
- * 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 html_message Treść wiadomości HTML
- *
- * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
- *
- * \ingroup messages
- */
-int gg_send_message_html(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *html_message)
-{
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_html(%p, "
-		"%d, %u, %p);\n", sess, msgclass, recipient, html_message);
-
-	return gg_send_message_common(sess, msgclass, 1, &recipient, NULL, NULL, 0, html_message);
-}
-
-/**
- * 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_common(sess, msgclass, recipients_count,
-		recipients, message,
-		(const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
-		9, NULL);
-}
-
-/**
- * Wysyła wiadomość formatowaną 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
- * \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_confer_richtext(struct gg_session *sess, int msgclass,
-	int recipients_count, uin_t *recipients, const unsigned char *message,
-	const unsigned char *format, int formatlen)
-{
-	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);
-
-	return gg_send_message_common(sess, msgclass, recipients_count, recipients, message, format, formatlen, NULL);
-}
-
-/**
- * Wysyła formatowaną wiadomość HTML 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 html_message Treść wiadomości HTML
- *
- * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
- *
- * \ingroup messages
- */
-int gg_send_message_confer_html(struct gg_session *sess, int msgclass,
-	int recipients_count, uin_t *recipients,
-	const unsigned char *html_message)
-{
-	gg_debug_session(sess, GG_DEBUG_FUNCTION,
-		"** gg_send_message_confer_html(%p, %d, %d, %p, %p);\n", sess,
-		msgclass, recipients_count, recipients, html_message);
-
-	return gg_send_message_common(sess, msgclass, recipients_count, recipients, NULL, NULL, 0, html_message);
-}
-
-/**
- * Wysyła wiadomość binarną przeznaczoną dla klienta.
- *
- * 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.
- *
- * \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
- */
-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_session(sess, 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);
-}
-
-/**
- * Wysyła żądanie 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.
- *
- * \param sess Struktura sesji
- * \param recipient Numer adresata
- * \param size Rozmiar obrazka w bajtach
- * \param crc32 Suma kontrola obrazka
- *
- * \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)
-{
-	struct gg_send_msg s;
-	struct gg_msg_image_request r;
-	char dummy = 0;
-	int res;
-
-	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;
-	}
-
-	if (size < 0) {
-		errno = EINVAL;
-		return -1;
-	}
-
-	s.recipient = gg_fix32(recipient);
-	s.seq = gg_fix32(0);
-	s.msgclass = gg_fix32(GG_CLASS_MSG);
-
-	r.flag = GG_MSG_OPTION_IMAGE_REQUEST;
-	r.size = gg_fix32(size);
-	r.crc32 = gg_fix32(crc32);
-
-	res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);
-
-	if (!res) {
-		struct gg_image_queue *q = malloc(sizeof(*q));
-		char *buf;
-
-		if (!q) {
-			gg_debug_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_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
-			free(q);
-			return -1;
-		}
-
-		memset(q, 0, sizeof(*q));
-
-		q->sender = recipient;
-		q->size = size;
-		q->crc32 = crc32;
-		q->image = buf;
-
-		if (!sess->images)
-			sess->images = q;
-		else {
-			struct gg_image_queue *qq;
-
-			for (qq = sess->images; qq->next; qq = qq->next);
-
-			qq->next = q;
-		}
-	}
-
-	return res;
-}
-
-/**
- * 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
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup messages
- */
-int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
-{
-	struct gg_session_private *p;
-	struct gg_msg_image_reply *r;
-	struct gg_send_msg s;
-	const char *tmp;
-	char buf[1910];
-	gg_imgout_queue_t *queue = NULL, *queue_end = NULL;
-
-	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;
-		return -1;
-	}
-
-	p = sess->private_data;
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	if (size < 0) {
-		errno = EINVAL;
-		return -1;
-	}
-
-	/* wytnij ścieżki, zostaw tylko nazwę pliku */
-	while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
-		filename = tmp + 1;
-
-	if (strlen(filename) < 1 || strlen(filename) > 1024) {
-		errno = EINVAL;
-		return -1;
-	}
-
-	s.recipient = gg_fix32(recipient);
-	s.seq = gg_fix32(0);
-	s.msgclass = gg_fix32(GG_CLASS_MSG);
-
-	buf[0] = 0;
-	r = (void*) &buf[1];
-
-	r->flag = GG_MSG_OPTION_IMAGE_REPLY;
-	r->size = gg_fix32(size);
-	r->crc32 = gg_fix32(gg_crc32(0, (const unsigned char*) image, size));
-
-	while (size > 0) {
-		gg_imgout_queue_t *it;
-		size_t buflen, chunklen;
-
-		/* \0 + struct gg_msg_image_reply */
-		buflen = sizeof(struct gg_msg_image_reply) + 1;
-
-		/* w pierwszym kawałku jest nazwa pliku */
-		if (r->flag == GG_MSG_OPTION_IMAGE_REPLY) {
-			strcpy(buf + buflen, filename);
-			buflen += strlen(filename) + 1;
-		}
-
-		chunklen = ((size_t) size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t) size;
-
-		memcpy(buf + buflen, image, chunklen);
-		size -= chunklen;
-		image += chunklen;
-
-		it = gg_new0(sizeof(gg_imgout_queue_t));
-		if (!it)
-			break;
-		if (queue_end) {
-			queue_end->next = it;
-			queue_end = it;
-		} else {
-			queue = queue_end = it;
-		}
-
-		memcpy(&it->msg_hdr, &s, sizeof(s));
-		memcpy(it->buf, buf, buflen + chunklen);
-		it->buf_len = buflen + chunklen;
-
-		r->flag = GG_MSG_OPTION_IMAGE_REPLY_MORE;
-	}
-
-	if (p->imgout_queue) {
-		queue_end = p->imgout_queue;
-		while (queue_end->next)
-			queue_end = queue_end->next;
-		queue_end->next = queue;
-	} else {
-		p->imgout_queue = queue;
-	}
-	gg_image_sendout(sess);
-
-	return 0;
-}
-
-void gg_image_sendout(struct gg_session *sess)
-{
-	struct gg_session_private *p = sess->private_data;
-
-	while (p->imgout_waiting_ack < GG_IMGOUT_WAITING_MAX && p->imgout_queue) {
-		gg_imgout_queue_t *it = p->imgout_queue;
-		int res;
-
-		p->imgout_queue = p->imgout_queue->next;
-		p->imgout_waiting_ack++;
-
-		res = gg_send_packet(sess, GG_SEND_MSG,
-			&it->msg_hdr, sizeof(it->msg_hdr),
-			it->buf, it->buf_len,
-			NULL);
-
-		free(it);
-
-		if (res == -1)
-			break;
-	}
-}
-
-static int gg_notify105_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
-{
-	int i = 0;
-
-	if (!userlist || !count)
-		return gg_send_packet(sess, GG_NOTIFY105_LIST_EMPTY, NULL);
-
-	while (i < count) {
-		gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
-		gg_tvbuilder_expected_size(tvb, 2100);
-
-		while (i < count) {
-			size_t prev_size = gg_tvbuilder_get_size(tvb);
-			gg_tvbuilder_write_uin(tvb, userlist[i]);
-			gg_tvbuilder_write_uint8(tvb,
-				(types == NULL) ? GG_USER_NORMAL : types[i]);
-
-			/* Oryginalny klient wysyła maksymalnie 2048 bajtów
-			 * danych w każdym pakiecie tego typu.
-			 */
-			if (gg_tvbuilder_get_size(tvb) > 2048) {
-				gg_tvbuilder_strip(tvb, prev_size);
-				break;
-			}
-			i++;
-		}
-
-		if (!gg_tvbuilder_send(tvb, (i < count) ?
-			GG_NOTIFY105_FIRST : GG_NOTIFY105_LAST))
-		{
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * Wysyła do serwera listę kontaktów.
- *
- * 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).
- *
- * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli
- * jest pusta.
- *
- * \param sess Struktura sesji
- * \param userlist Wskaźnik do tablicy numerów kontaktów
- * \param types    Wskaźnik do tablicy rodzajów kontaktów. Jeżeli NULL, wszystkie kontakty są typu GG_USER_NORMAL.
- * \param count Liczba kontaktów
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup contacts
- */
-int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
-{
-	struct gg_notify *n;
-	int i, res = 0;
-
-	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;
-	}
-
-	if (sess->protocol_version >= GG_PROTOCOL_VERSION_110)
-		return gg_notify105_ex(sess, userlist, types, count);
-
-	if (!userlist || !count)
-		return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
-
-	while (count > 0) {
-		int part_count, packet_type;
-
-		if (count > 400) {
-			part_count = 400;
-			packet_type = GG_NOTIFY_FIRST;
-		} else {
-			part_count = count;
-			packet_type = GG_NOTIFY_LAST;
-		}
-
-		if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
-			return -1;
-
-		for (i = 0; i < part_count; i++) {
-			n[i].uin = gg_fix32(userlist[i]);
-			if (types == NULL)
-				n[i].dunno1 = GG_USER_NORMAL;
-			else
-				n[i].dunno1 = types[i];
-		}
-
-		if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
-			free(n);
-			res = -1;
-			break;
-		}
-
-		count -= part_count;
-		userlist += part_count;
-		if (types != NULL)
-			types += part_count;
-
-		free(n);
-	}
-
-	return res;
-}
-
-/**
- * Wysyła do serwera listę kontaktów.
- *
- * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
- * są rodzaju \c GG_USER_NORMAL.
- *
- * \param sess Struktura sesji
- * \param userlist Wskaźnik do tablicy numerów kontaktów
- * \param count Liczba kontaktów
- *
- * \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)
-{
-	return gg_notify_ex(sess, userlist, NULL, count);
-}
-
-/**
- * Dodaje kontakt.
- *
- * 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.
- *
- * \param sess Struktura sesji
- * \param uin Numer kontaktu
- * \param type Rodzaj kontaktu
- *
- * \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)
-{
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
-		gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
-		gg_tvbuilder_expected_size(tvb, 16);
-
-		gg_tvbuilder_write_uin(tvb, uin);
-		gg_tvbuilder_write_uint8(tvb, type);
-
-		if (!gg_tvbuilder_send(tvb, GG_ADD_NOTIFY105))
-			return -1;
-		return 0;
-	} else {
-		struct gg_add_remove a;
-
-		a.uin = gg_fix32(uin);
-		a.dunno1 = type;
-
-		return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
-	}
-}
-
-/**
- * Dodaje kontakt.
- *
- * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
- * kontaktów to \c GG_USER_NORMAL.
- *
- * \param sess Struktura sesji
- * \param uin Numer kontaktu
- *
- * \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);
-}
-
-/**
- * Usuwa kontakt.
- *
- * Usuwa z listy kontaktów dany numer w trakcie połączenia.
- *
- * \param sess Struktura sesji
- * \param uin Numer kontaktu
- * \param type Rodzaj kontaktu
- *
- * \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)
-{
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
-		gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
-		gg_tvbuilder_expected_size(tvb, 16);
-
-		gg_tvbuilder_write_uin(tvb, uin);
-		gg_tvbuilder_write_uint8(tvb, type);
-
-		if (!gg_tvbuilder_send(tvb, GG_REMOVE_NOTIFY105))
-			return -1;
-		return 0;
-	} else {
-		struct gg_add_remove a;
-
-		a.uin = gg_fix32(uin);
-		a.dunno1 = type;
-
-		return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
-	}
-}
-
-/**
- * Usuwa kontakt.
- *
- * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
- * kontaktów to \c GG_USER_NORMAL.
- *
- * \param sess Struktura sesji
- * \param uin Numer kontaktu
- *
- * \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);
-}
-
-/**
- * Wysyła do serwera zapytanie dotyczące listy kontaktów.
- *
- * 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.
- *
- * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą
- * z protokołu -- wysyła i odbiera kompletną listę.
- *
- * \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)
-{
-	int len;
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	if (!request) {
-		sess->userlist_blocks = 1;
-		return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
-	}
-
-	len = strlen(request);
-
-	sess->userlist_blocks = 0;
-
-	while (len > 2047) {
-		sess->userlist_blocks++;
-
-		if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
-			return -1;
-
-		if (type == GG_USERLIST_PUT)
-			type = GG_USERLIST_PUT_MORE;
-
-		request += 2047;
-		len -= 2047;
-	}
-
-	sess->userlist_blocks++;
-
-	return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
-}
-
-/**
- * Wysyła do serwera zapytanie dotyczące listy kontaktów (10.0).
- *
- * 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 jednak weryfikowany przez serwer, który stara się
- * synchronizować listę kontaktów zapisaną w formatach GG 7.0 oraz GG 10.0.
- * Serwer przyjmuje listy kontaktów przysłane w formacie niezgodnym z podanym
- * jako \c format_type, ale nie zachowuje ich, a przesłanie takiej listy jest
- * równoznaczne z usunięciem listy kontaktów.
- *
- * Program nie musi się przejmować kompresją listy kontaktów zgodną
- * z protokołem -- wysyła i odbiera kompletną listę zapisaną czystym tekstem.
- *
- * \param sess Struktura sesji
- * \param type Rodzaj zapytania
- * \param version Numer ostatniej znanej programowi wersji listy kontaktów lub 0
- * \param format_type Typ formatu listy kontaktów
- * \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_userlist100_request(struct gg_session *sess, char type,
-	unsigned int version, char format_type, const char *request)
-{
-	struct gg_userlist100_request pkt;
-	unsigned char *zrequest;
-	size_t zrequest_len;
-	int ret;
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	pkt.type = type;
-	pkt.version = gg_fix32(version);
-	pkt.format_type = format_type;
-	pkt.unknown1 = 0x01;
-
-	if (request == NULL)
-		return gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), NULL);
-
-	zrequest = gg_deflate(request, &zrequest_len);
-
-	if (zrequest == NULL) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_userlist100_request() gg_deflate() failed\n");
-		return -1;
-	}
-
-	ret = gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), zrequest, zrequest_len, NULL);
-
-	free(zrequest);
-
-	return ret;
-}
-
-/**
- * Informuje rozmówcę o pisaniu wiadomości.
- *
- * \param sess Struktura sesji
- * \param recipient Numer adresata
- * \param length Długość wiadomości lub 0 jeśli jest pusta
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup messages
- */
-int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length){
-	struct gg_typing_notification pkt;
-	uin_t uin;
-
-	pkt.length = gg_fix16(length);
-	uin = gg_fix32(recipient);
-	memcpy(&pkt.uin, &uin, sizeof(uin_t));
-
-	return gg_send_packet(sess, GG_TYPING_NOTIFICATION, &pkt, sizeof(pkt), NULL);
-}
-
-/**
- * Rozłącza inną sesję multilogowania.
- *
- * \param gs Struktura sesji
- * \param conn_id Sesja do rozłączenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup login
- */
-int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id)
-{
-	struct gg_multilogon_disconnect pkt;
-
-	pkt.conn_id = conn_id;
-
-	return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL);
-}
-
-/**
- * Tworzy nową konferencję (11.0).
- *
- * \param gs Struktura sesji
- *
- * \return Numer sekwencyjny (ten sam, co w \c gg_event_chat_created), lub -1
- *         w przypadku błędu
- *
- * \ingroup chat
- */
-int gg_chat_create(struct gg_session *gs)
-{
-	struct gg_chat_create pkt;
-	int seq;
-
-	if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
-		return -1;
-
-	seq = ++gs->seq;
-
-	pkt.seq = gg_fix32(seq);
-	pkt.dummy = 0;
-
-	if (gg_send_packet(gs, GG_CHAT_CREATE, &pkt, sizeof(pkt), NULL) == -1)
-		return -1;
-
-	return seq;
-}
-
-/**
- * Zaprasza nowych użytkowników do konferencji (11.0).
- *
- * \param gs                 Struktura sesji
- * \param id                 Identyfikator konferencji
- * \param participants       Lista użytkowników do zaproszenia
- * \param participants_count Liczba użytkowników
- *
- * \return Numer sekwencyjny w przypadku powodzenia (ten sam, co w
- *         \c gg_event_chat_invite_ack), lub -1 w przypadku błędu
- *
- * \ingroup chat
- */
-int gg_chat_invite(struct gg_session *gs, uint64_t id, uin_t *participants,
-	unsigned int participants_count)
-{
-	struct gg_chat_invite pkt;
-	int seq, ret;
-	unsigned int i;
-	struct gg_chat_participant
-	{
-		uint32_t uin;
-		uint32_t dummy;
-	} GG_PACKED;
-	struct gg_chat_participant *participants_list;
-	size_t participants_list_size;
-
-	if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
-		return -1;
-
-	if (participants_count == 0 || participants_count >=
-		~(unsigned int)0 / sizeof(struct gg_chat_participant))
-	{
-		return -1;
-	}
-
-	participants_list_size = sizeof(struct gg_chat_participant) *
-		participants_count;
-	participants_list = malloc(participants_list_size);
-	if (participants_list == NULL)
-		return -1;
-
-	seq = ++gs->seq;
-	pkt.id = gg_fix64(id);
-	pkt.seq = gg_fix32(seq);
-	pkt.participants_count = gg_fix32(participants_count);
-
-	for (i = 0; i < participants_count; i++) {
-		participants_list[i].uin = gg_fix32(participants[i]);
-		participants_list[i].dummy = gg_fix32(0x1e);
-	}
-
-	ret = gg_send_packet(gs, GG_CHAT_INVITE,
-		&pkt, sizeof(pkt),
-		participants_list, participants_list_size,
-		NULL);
-	free(participants_list);
-
-	if (ret == -1)
-		return -1;
-	return seq;
-}
-
-/**
- * Opuszcza konferencję (11.0).
- *
- * \param gs Struktura sesji
- * \param id Identyfikator konferencji
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup chat
- */
-int gg_chat_leave(struct gg_session *gs, uint64_t id)
-{
-	struct gg_chat_leave pkt;
-	int seq;
-
-	if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
-		return -1;
-
-	seq = ++gs->seq;
-	pkt.id = gg_fix64(id);
-	pkt.seq = gg_fix32(seq);
-
-	if (gg_send_packet(gs, GG_CHAT_LEAVE, &pkt, sizeof(pkt), NULL) == -1)
-		return -1;
-
-	return seq;
-}
-
-/**
- * Wysyła wiadomość w ramach konferencji (11.0).
- *
- * \param gs      Struktura sesji
- * \param id      Identyfikator konferencji
- * \param message Wiadomość
- * \param is_html 1, jeżeli wiadomość jest zapisana jako HTML, 0 w p.p.
- *
- * \return Numer sekwencyjny (taki sam, jak w \c gg_event_chat_send_msg_ack)
- *         jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup chat
- */
-int gg_chat_send_message(struct gg_session *gs, uint64_t id, const char *message, int is_html)
-{
-	if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
-		return -1;
-
-	return gg_send_message_110(gs, 0, id, message, is_html);
-}
-
-/* @} */
-
-/**
- * Sprawdza czy biblioteka obsługuje daną funkcję.
- *
- * \param feature Identyfikator funkcji.
- *
- * \return Wartość niezerowa jeśli funkcja jest obsłgiwana.
- *
- * \ingroup version
- */
-int gg_libgadu_check_feature(gg_libgadu_feature_t feature)
-{
-	switch (feature)
-	{
-	case GG_LIBGADU_FEATURE_SSL:
-#if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS)
-		return 1;
-#else
-		return 0;
-#endif
-
-	case GG_LIBGADU_FEATURE_PTHREAD:
-#ifdef GG_CONFIG_HAVE_PTHREAD
-		return 1;
-#else
-		return 0;
-#endif
-
-	case GG_LIBGADU_FEATURE_USERLIST100:
-#ifdef GG_CONFIG_HAVE_ZLIB
-		return 1;
-#else
-		return 0;
-#endif
-
-	/* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */
-
-	}
-
-	return 0;
-}
-
-static void gg_socket_manager_error(struct gg_session *sess, enum gg_failure_t failure)
-{
-	int pipes[2];
-	uint8_t dummy = 0;
-	struct gg_session_private *p = sess->private_data;
-
-	p->socket_failure = failure;
-
-	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to"
-			" create pipes (errno=%d, %s)\n", errno,
-			strerror(errno));
-		return;
-	}
-
-	p->socket_is_external = 0;
-	sess->fd = pipes[1];
-	sess->check = GG_CHECK_READ;
-	sess->state = GG_STATE_ERROR;
-	if (send(pipes[0], &dummy, sizeof(dummy), 0) != sizeof(dummy)) {
-		gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to"
-			" send via pipe (errno=%d, %s)\n", errno,
-			strerror(errno));
-		return;
-	}
-	close(pipes[0]);
-}
-
-int gg_compat_feature_is_enabled(struct gg_session *sess, gg_compat_feature_t feature)
-{
-	gg_compat_t level;
-
-	if (sess == NULL)
-		return 0;
-
-	level = sess->private_data->compatibility;
-
-	switch (feature) {
-		case GG_COMPAT_FEATURE_ACK_EVENT:
-		case GG_COMPAT_FEATURE_LEGACY_CONFER:
-			return (level < GG_COMPAT_1_12_0);
-	}
-
-	return 0;
-}
-
-static gg_msg_list_t * gg_compat_find_sent_message(struct gg_session *sess, int seq, int remove)
-{
-	struct gg_session_private *p = sess->private_data;
-	gg_msg_list_t *it, *previous = NULL;
-
-	for (it = p->sent_messages; it; it = it->next) {
-		if (it->seq == seq)
-			break;
-		else
-			previous = it;
-	}
-
-	if (remove && it) {
-		if (previous == NULL)
-			p->sent_messages = it->next;
-		else
-			previous->next = it->next;
-	}
-
-	return it;
-}
-
-static void gg_compat_message_cleanup(struct gg_session *sess)
-{
-	struct gg_session_private *p = sess->private_data;
-
-	while (p->sent_messages) {
-		gg_msg_list_t *next = p->sent_messages->next;
-		free(p->sent_messages->recipients);
-		free(p->sent_messages);
-		p->sent_messages = next;
-	}
-}
-
-static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients)
-{
-	struct gg_session_private *p = sess->private_data;
-	gg_msg_list_t *sm;
-	uin_t *new_recipients;
-	size_t old_count, i;
-
-	if (sess->protocol_version < GG_PROTOCOL_VERSION_110)
-		return;
-
-	if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT))
-		return;
-
-	sm = gg_compat_find_sent_message(sess, seq, 0);
-	if (!sm) {
-		sm = gg_new0(sizeof(gg_msg_list_t));
-		if (!sm)
-			return;
-		sm->next = p->sent_messages;
-		p->sent_messages = sm;
-	}
-
-	sm->seq = seq;
-	old_count = sm->recipients_count;
-	sm->recipients_count += recipients_count;
-
-	new_recipients = realloc(sm->recipients, sizeof(uin_t) * sm->recipients_count);
-	if (new_recipients == NULL) {
-		gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-			"// gg_compat_message_sent() not enough memory\n");
-		return;
-	}
-	sm->recipients = new_recipients;
-
-	for (i = 0; i < recipients_count; i++)
-		sm->recipients[old_count + i] = recipients[i];
-}
-
-void gg_compat_message_ack(struct gg_session *sess, int seq)
-{
-	gg_msg_list_t *sm;
-	size_t i;
-
-	if (sess->protocol_version < GG_PROTOCOL_VERSION_110)
-		return;
-
-	if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT))
-		return;
-
-	sm = gg_compat_find_sent_message(sess, seq, 1);
-	if (!sm)
-		return;
-
-	for (i = 0; i < sm->recipients_count; i++) {
-		struct gg_event *qev;
-
-		qev = gg_eventqueue_add(sess);
-
-		qev->type = GG_EVENT_ACK;
-		qev->event.ack.status = GG_ACK_DELIVERED;
-		qev->event.ack.recipient = sm->recipients[i];
-		qev->event.ack.seq = seq;
-	}
-
-	free(sm->recipients);
-	free(sm);
-}
-
-/**
- * Odbiera nowo utworzone gniazdo TCP/TLS.
- *
- * Po wywołaniu tej funkcji należy zacząć obserwować deskryptor sesji (nawet
- * w przypadku niepowodzenia).
- *
- * Jeżeli gniazdo nie zostanie obsłużone, należy je zniszczyć.
- *
- * \param handle Uchwyt gniazda
- * \param priv Dane prywatne biblioteki libgadu
- * \param fd Deskryptor nowo utworzonego gniazda, lub -1 w przypadku błędu
- *
- * \return Wartość różna od zera, jeżeli gniazdo zostało obsłużone, 0 w przeciwnym przypadku
- *
- * \ingroup socketmanager
- */
-int gg_socket_manager_connected(void *handle, void *priv, int fd)
-{
-	struct gg_session *sess = priv;
-	struct gg_session_private *p = sess->private_data;
-
-	if (p->socket_handle != handle) {
-		gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-			"// gg_socket_manager_connected() invalid handle\n");
-		return 0;
-	}
-
-	sess->fd = -1;
-
-	if (fd < 0) {
-		gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-			"// gg_socket_manager_connected() connection error\n");
-		p->socket_handle = NULL;
-		gg_socket_manager_error(sess, GG_FAILURE_CONNECTING);
-		return 0;
-	}
-
-	if (p->socket_next_state == GG_STATE_TLS_NEGOTIATION) {
-		if (gg_session_init_ssl(sess) == -1) {
-			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
-				"// gg_socket_manager_connected() couldn't "
-				"initialize ssl\n");
-			p->socket_handle = NULL;
-			gg_socket_manager_error(sess, GG_FAILURE_TLS);
-			return 0;
-		}
-	}
-
-	p->socket_is_external = 1;
-	sess->fd = fd;
-	sess->timeout = GG_DEFAULT_TIMEOUT;
-	sess->state = p->socket_next_state;
-
-	gg_debug_session(sess, GG_DEBUG_MISC, "// next state=%s\n",
-		gg_debug_state(p->socket_next_state));
-
-	if (p->socket_next_state == GG_STATE_READING_KEY)
-		sess->check = GG_CHECK_READ;
-	else
-		sess->check = GG_CHECK_WRITE;
-
-	return 1;
-}
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */

mercurial