libpurple/protocols/gg/lib/handlers.c

changeset 38882
bea4cc95b40f
parent 38881
25cb836b9cec
parent 38182
783878958371
child 38883
90462fef3dd8
--- a/libpurple/protocols/gg/lib/handlers.c	Wed Oct 26 10:17:10 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2881 +0,0 @@
-/*
- *  (C) Copyright 2001-2011 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 handlers.c
- *
- * \brief Funkcje obsługi przychodzących pakietów
- */
-
-#include <ctype.h>
-
-#include "fileio.h"
-#include "network.h"
-#include "strman.h"
-#include "libgadu.h"
-#include "resolver.h"
-#include "session.h"
-#include "protocol.h"
-#include "encoding.h"
-#include "message.h"
-#include "internal.h"
-#include "deflate.h"
-#include "tvbuff.h"
-#include "protobuf.h"
-#include "packets.pb-c.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-/* Ograniczenie długości listy kontaktów
- * z pakietów GG_USERLIST_REPLY do 10MB. */
-#define GG_USERLIST_REPLY_MAX_LENGTH 10485760
-
-/**
- * \internal Struktura opisująca funkcję obsługi pakietu.
- */
-typedef struct {
-	/* Typ pakietu */
-	uint32_t type;
-	/* Stan w którym pakiet jest obsługiwany */
-	enum gg_state_t state;
-	/* Minimalny rozmiar danych pakietu */
-	size_t min_length;
-	/* Funkcja obsługująca pakiet. Patrz gg_session_handle_packet(). */
-	int (*handler)(struct gg_session *, uint32_t, const char *, size_t, struct gg_event *);
-} gg_packet_handler_t;
-
-static int gg_ack_110(struct gg_session *gs, GG110Ack__Type type, uint32_t seq, struct gg_event *ge)
-{
-	GG110Ack msg = GG110_ACK__INIT;
-
-	msg.type = type;
-	msg.seq = seq;
-
-	if (!GG_PROTOBUF_SEND(gs, ge, GG_ACK110, gg110_ack, msg))
-		return -1;
-	return 0;
-}
-
-static void gg_sync_time(struct gg_session *gs, time_t server_time)
-{
-	time_t local_time = time(NULL);
-	int time_diff = server_time - local_time;
-
-	if (gs->private_data->time_diff == time_diff)
-		return;
-
-	gs->private_data->time_diff = time_diff;
-	gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_VERBOSE,
-		"// time synchronized (diff = %d)\n", time_diff);
-}
-
-static int gg_session_handle_welcome_110(struct gg_session *gs, uint32_t seed,
-	struct gg_event *ge)
-{
-	GG105Login msg = GG105_LOGIN__INIT;
-	char client_str[1000];
-	uint8_t hash[64];
-	const char *client_name = GG11_VERSION;
-	const char *client_version = GG_DEFAULT_CLIENT_VERSION_110;
-	const char *client_target = GG11_TARGET;
-	uint8_t dummy4[4] = {0, 0, 0, 0};
-
-	if (gs->hash_type != GG_LOGIN_HASH_SHA1) {
-		gg_debug_session(gs, GG_DEBUG_ERROR, "// Unsupported hash type "
-			"for this protocol version\n");
-		gg_connection_failure(gs, ge, GG_FAILURE_INTERNAL);
-		return -1;
-	}
-
-	if (gg_login_hash_sha1_2(gs->password, seed, hash) == -1) {
-		gg_debug_session(gs, GG_DEBUG_ERROR, "// gg_watch_fd() "
-			"gg_login_hash_sha1_2() failed, "
-			"probably out of memory\n");
-		gg_connection_failure(gs, ge, GG_FAILURE_INTERNAL);
-		return -1;
-	}
-
-	if (gs->client_version != NULL && !isdigit(gs->client_version[0])) {
-		client_name = "";
-		client_target = "";
-	}
-	if (gs->client_version != NULL)
-		client_version = gs->client_version;
-	snprintf(client_str, sizeof(client_str), "%s%s%s",
-		client_name, client_version, client_target);
-	client_str[sizeof(client_str) - 1] = '\0';
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() "
-		"sending GG_LOGIN105 packet\n");
-
-	msg.lang = GG8_LANG;
-	gg_protobuf_set_uin(&msg.uin, gs->uin, NULL);
-	msg.hash.len = 20;
-	msg.hash.data = hash;
-	msg.client = client_str;
-
-	/* flagi gg8 są różne od tych dla gg11 */
-	msg.initial_status = gs->initial_status ?
-		(gs->initial_status & 0xFF) : GG_STATUS_AVAIL;
-
-	if (gs->initial_descr != NULL) {
-		msg.initial_descr = gs->initial_descr;
-	}
-
-	/* GG11.0
-	msg.supported_features = "avatar,StatusComments,gg_account_sdp,"
-		"edisc,bot,fanpage,pubdir,botCaps"; */
-	/* GG11.2 */
-	msg.supported_features = "avatar,StatusComments,ggaccount,edisc,"
-		"music_shared,bot,fanpage,pubdir,botCaps,gifts,Gift";
-
-	msg.dummy4.len = sizeof(dummy4);
-	msg.dummy4.data = dummy4;
-
-	msg.has_dummy7 = 1;
-	msg.has_dummy8 = 1;
-	msg.has_dummy10 = 1;
-
-	if (!GG_PROTOBUF_SEND(gs, ge, GG_LOGIN105, gg105_login, msg))
-		return -1;
-
-	gs->state = GG_STATE_READING_REPLY;
-	gs->check = GG_CHECK_READ;
-	return 0;
-}
-
-static int gg_session_handle_login110_ok(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110LoginOK *msg = gg110_login_ok__unpack(NULL, len, (uint8_t*)ptr);
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110LoginOK", msg))
-		return -1;
-
-	gg_protobuf_expected(gs, "GG110LoginOK.dummy1", msg->dummy1, 1);
-	gg_sync_time(gs, msg->server_time);
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// login110_ok: "
-		"uin=%u, dummyhash=%s\n", msg->uin, msg->dummyhash);
-
-	gg110_login_ok__free_unpacked(msg, NULL);
-
-	ge->type = GG_EVENT_CONN_SUCCESS;
-	gs->state = GG_STATE_CONNECTED;
-	gs->check = GG_CHECK_READ;
-	gs->timeout = -1;
-	gs->status = (gs->initial_status) ? gs->initial_status : GG_STATUS_AVAIL;
-#if 0
-	free(gs->status_descr);
-	gs->status_descr = gs->initial_descr;
-#else
-	free(gs->initial_descr);
-#endif
-	gs->initial_descr = NULL;
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_WELCOME.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_welcome(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_welcome *w;
-	int ret;
-	uint8_t hash_buf[64];
-	uint32_t local_ip;
-	struct sockaddr_in sin;
-	socklen_t sin_len = sizeof(sin);
-	uint32_t seed;
-
-	struct gg_login80 l80;
-	const char *client_name, *version, *descr;
-	uint32_t client_name_len, version_len, descr_len;
-
-	if (len < sizeof(struct gg_welcome)) {
-		ge->type = GG_EVENT_CONN_FAILED;
-		ge->event.failure = GG_FAILURE_INVALID;
-		gs->state = GG_STATE_IDLE;
-		gg_close(gs);
-		return 0;
-	}
-
-	w = (const struct gg_welcome*) ptr;
-	seed = gg_fix32(w->key);
-
-	if (gs->protocol_version >= GG_PROTOCOL_VERSION_110)
-		return gg_session_handle_welcome_110(gs, seed, ge);
-
-	memset(hash_buf, 0, sizeof(hash_buf));
-
-	switch (gs->hash_type) {
-		case GG_LOGIN_HASH_GG32:
-		{
-			uint32_t hash;
-
-			hash = gg_fix32(gg_login_hash((unsigned char*) gs->password, seed));
-			gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() "
-				"challenge %.4x --> GG32 hash %.8x\n",
-				seed, hash);
-			memcpy(hash_buf, &hash, sizeof(hash));
-
-			break;
-		}
-
-		case GG_LOGIN_HASH_SHA1:
-		{
-#ifndef GG_DEBUG_DISABLE
-			char tmp[41];
-			int i;
-#endif
-
-			if (gg_login_hash_sha1_2(gs->password, seed, hash_buf) == -1) {
-				gg_debug_session(gs, GG_DEBUG_MISC,
-					"// gg_watch_fd() gg_login_hash_sha1_2()"
-					" failed, probably out of memory\n");
-				gg_close(gs);
-				ge->type = GG_EVENT_CONN_FAILED;
-				ge->event.failure = GG_FAILURE_INTERNAL;
-				gs->state = GG_STATE_IDLE;
-				return -1;
-			}
-
-#ifndef GG_DEBUG_DISABLE
-			for (i = 0; i < 40; i += 2)
-				snprintf(tmp + i, sizeof(tmp) - i, "%02x", hash_buf[i / 2]);
-
-			gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() "
-				"challenge %.4x --> SHA1 hash: %s\n",
-				seed, tmp);
-#endif
-
-			break;
-		}
-
-		default:
-			break;
-	}
-
-#if 0
-	if (gs->password != NULL && (gs->flags & (1 << GG_SESSION_FLAG_CLEAR_PASSWORD))) {
-		memset(gs->password, 0, strlen(gs->password));
-		free(gs->password);
-		gs->password = NULL;
-	}
-#endif
-
-	if (!getsockname(gs->fd, (struct sockaddr*) &sin, &sin_len)) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() "
-			"detected address to %s\n", inet_ntoa(sin.sin_addr));
-		local_ip = sin.sin_addr.s_addr;
-	} else {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
-		local_ip = 0;
-	}
-
-	if (gs->external_addr == 0)
-		gs->external_addr = local_ip;
-
-	memset(&l80, 0, sizeof(l80));
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending GG_LOGIN80 packet\n");
-	l80.uin = gg_fix32(gs->uin);
-	memcpy(l80.language, GG8_LANG, sizeof(l80.language));
-	l80.hash_type = gs->hash_type;
-	memcpy(l80.hash, hash_buf, sizeof(l80.hash));
-	l80.status = gg_fix32(gs->initial_status ? gs->initial_status : GG_STATUS_AVAIL);
-	l80.flags = gg_fix32(gs->status_flags);
-	l80.features = gg_fix32(gs->protocol_features);
-	l80.image_size = gs->image_size;
-	l80.dunno2 = 0x64;
-
-	if (gs->client_version != NULL && !isdigit(gs->client_version[0])) {
-		client_name = "";
-		client_name_len = 0;
-	} else {
-		client_name = GG8_VERSION;
-		client_name_len = strlen(GG8_VERSION);
-	}
-
-	version = (gs->client_version != NULL) ? gs->client_version : GG_DEFAULT_CLIENT_VERSION_100;
-	version_len = gg_fix32(client_name_len + strlen(version));
-
-	descr = (gs->initial_descr != NULL) ? gs->initial_descr : "";
-	descr_len = (gs->initial_descr != NULL) ? gg_fix32(strlen(gs->initial_descr)) : 0;
-
-	ret = gg_send_packet(gs,
-			GG_LOGIN80,
-			&l80, sizeof(l80),
-			&version_len, sizeof(version_len),
-			client_name, client_name_len,
-			version, strlen(version),
-			&descr_len, sizeof(descr_len),
-			descr, strlen(descr),
-			NULL);
-
-	if (ret == -1) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() "
-			"sending packet failed. (errno=%d, %s)\n",
-			errno, strerror(errno));
-		gg_close(gs);
-		ge->type = GG_EVENT_CONN_FAILED;
-		ge->event.failure = GG_FAILURE_WRITING;
-		gs->state = GG_STATE_IDLE;
-		return -1;
-	}
-
-	gs->state = GG_STATE_READING_REPLY;
-	gs->check = GG_CHECK_READ;
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_LOGIN_OK.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_login_ok(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
-	ge->type = GG_EVENT_CONN_SUCCESS;
-	gs->state = GG_STATE_CONNECTED;
-	gs->check = GG_CHECK_READ;
-	gs->timeout = -1;
-	gs->status = (gs->initial_status) ? gs->initial_status : GG_STATUS_AVAIL;
-#if 0
-	free(gs->status_descr);
-	gs->status_descr = gs->initial_descr;
-#else
-	free(gs->initial_descr);
-#endif
-	gs->initial_descr = NULL;
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_LOGIN_FAILED.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_login_failed(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	if (type != GG_DISCONNECTING)
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
-	else
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
-	ge->type = GG_EVENT_CONN_FAILED;
-	ge->event.failure = (type != GG_DISCONNECTING) ? GG_FAILURE_PASSWORD : GG_FAILURE_INTRUDER;
-	gs->state = GG_STATE_IDLE;
-	gg_close(gs);
-	errno = EACCES;
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_SEND_MSG_ACK.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_send_msg_ack(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	struct gg_session_private *p = gs->private_data;
-	const struct gg_send_msg_ack *s = (const struct gg_send_msg_ack*) ptr;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
-
-	ge->type = GG_EVENT_ACK;
-	ge->event.ack.status = gg_fix32(s->status);
-	ge->event.ack.recipient = gg_fix32(s->recipient);
-	ge->event.ack.seq = gg_fix32(s->seq);
-
-	if (ge->event.ack.seq == 0 && p->imgout_waiting_ack > 0)
-		p->imgout_waiting_ack--;
-	gg_image_sendout(gs);
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_SEND_MSG_ACK110.
- */
-static int gg_session_handle_send_msg_ack_110(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	struct gg_session_private *p = gs->private_data;
-	GG110MessageAck *msg = gg110_message_ack__unpack(NULL, len, (uint8_t*)ptr);
-	size_t i;
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110MessageAck", msg))
-		return -1;
-
-	if (msg->dummy1 == 0x4000) {
-		/* zaobserwowane w EKG rev2856, po wywołaniu check_conn, czyli
-		 * gg_image_request(sess, uin, 0, time(NULL));
-		 */
-		gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_WARNING,
-			"// gg_session_handle_send_msg_ack_110() magic dummy1 "
-			"value 0x4000\n");
-	} else if (msg->dummy1 != 0) {
-		gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_WARNING,
-			"// gg_session_handle_send_msg_ack_110() unknown dummy1 "
-			"value: %x\n", msg->dummy1);
-	}
-
-	gg_debug_session(gs, GG_DEBUG_VERBOSE,
-		"// gg_session_handle_send_msg_ack_110() "
-		"%s=%016" PRIx64 " %s=%016" PRIx64 "\n",
-		msg->has_msg_id ? "msg_id" : "0", msg->msg_id,
-		msg->has_conv_id ? "conv_id" : "0", msg->conv_id);
-
-	for (i = 0; i < msg->n_links; i++) {
-		GG110MessageAckLink *link = msg->links[i];
-		if (!GG_PROTOBUF_VALID(gs, "GG110MessageAckLink", link))
-			continue;
-		gg_debug_session(gs, GG_DEBUG_MISC,
-			"// gg_session_handle_send_msg_ack_110() "
-			"got link (id=%" PRIx64 ") \"%s\"\n", link->id, link->url);
-	}
-
-	ge->type = GG_EVENT_ACK110;
-	ge->event.ack110.msg_type = msg->msg_type;
-	ge->event.ack110.seq = msg->seq;
-	ge->event.ack110.time = msg->time;
-
-	gg_compat_message_ack(gs, msg->seq);
-
-	gg110_message_ack__free_unpacked(msg, NULL);
-
-	if (msg->seq == 0 && p->imgout_waiting_ack > 0)
-		p->imgout_waiting_ack--;
-	gg_image_sendout(gs);
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_PONG.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_pong(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
-
-	ge->type = GG_EVENT_PONG;
-
-	gs->last_pong = time(NULL);
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_DISCONNECTING.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_disconnecting(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
-
-	ge->type = GG_EVENT_DISCONNECT;
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_DISCONNECT_ACK.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_disconnect_ack(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received logoff acknowledge\n");
-
-	ge->type = GG_EVENT_DISCONNECT_ACK;
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiety GG_XML_EVENT i GG_XML_ACTION.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_xml_event(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n");
-
-	ge->type = GG_EVENT_XML_EVENT;
-	ge->event.xml_event.data = malloc(len + 1);
-
-	if (ge->event.xml_event.data == NULL) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-		return -1;
-	}
-
-	memcpy(ge->event.xml_event.data, ptr, len);
-	ge->event.xml_event.data[len] = 0;
-
-	return 0;
-}
-
-static int gg_session_handle_event_110(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110Event *msg = gg110_event__unpack(NULL, len, (uint8_t*)ptr);
-	int succ = 1;
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110Event", msg))
-		return -1;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_event_110: "
-		"received GG11 event (type=%d, id=%" PRIx64 ")\n", msg->type, msg->id);
-
-	if (msg->type == GG110_EVENT__TYPE__XML) {
-		ge->type = GG_EVENT_XML_EVENT;
-		ge->event.xml_event.data = strdup(msg->data);
-		succ = succ && (ge->event.xml_event.data != NULL);
-	} else if (msg->type == GG110_EVENT__TYPE__JSON) {
-		ge->type = GG_EVENT_JSON_EVENT;
-		ge->event.json_event.data = strdup(msg->data);
-		succ = succ && (ge->event.json_event.data != NULL);
-		ge->event.json_event.type = strdup(msg->subtype);
-		succ = succ && (ge->event.json_event.type != NULL);
-	} else {
-		gg_debug_session(gs, GG_DEBUG_WARNING,
-			"// gg_session_handle_event_110: "
-			"unsupported GG11 event type: %d\n", msg->type);
-		succ = 0;
-	}
-
-	if (gg_ack_110(gs, GG110_ACK__TYPE__MPA, msg->seq, ge) != 0) {
-		succ = 0;
-	}
-
-	gg110_event__free_unpacked(msg, NULL);
-
-	return succ ? 0 : -1;
-}
-
-/**
- * \internal Obsługuje pakiet GG_PUBDIR50_REPLY.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_pubdir50_reply(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
-
-	return gg_pubdir50_handle_reply_sess(gs, ge, ptr, len);
-}
-
-/**
- * \internal Obsługuje pakiet GG_USERLIST_REPLY.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_userlist_reply(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	char reply_type;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
-
-	reply_type = ptr[0];
-
-	/* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
-	 * gdy otrzymano wszystkie odpowiedzi */
-	if (reply_type == GG_USERLIST_PUT_REPLY || reply_type == GG_USERLIST_PUT_MORE_REPLY) {
-		if (--gs->userlist_blocks)
-			return 0;
-
-		reply_type = GG_USERLIST_PUT_REPLY;
-	}
-
-	if (len > 1) {
-		unsigned int reply_len = (gs->userlist_reply != NULL) ? strlen(gs->userlist_reply) : 0;
-		char *tmp;
-
-		gg_debug_session(gs, GG_DEBUG_MISC, "userlist_reply=%p, len=%"
-			GG_SIZE_FMT "\n", gs->userlist_reply, len);
-
-		if (reply_len + len > GG_USERLIST_REPLY_MAX_LENGTH) {
-			gg_debug_session(gs, GG_DEBUG_MISC,
-				"// gg_session_handle_userlist_reply() "
-				"too many userlist replies\n");
-			return -1;
-		}
-
-		tmp = realloc(gs->userlist_reply, reply_len + len);
-
-		if (tmp == NULL) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			return -1;
-		}
-
-		gs->userlist_reply = tmp;
-		memcpy(gs->userlist_reply + reply_len, ptr + 1, len - 1);
-		gs->userlist_reply[reply_len + len - 1] = 0;
-	}
-
-	if (reply_type == GG_USERLIST_GET_MORE_REPLY)
-		return 0;
-
-	ge->type = GG_EVENT_USERLIST;
-	ge->event.userlist.type = reply_type;
-	ge->event.userlist.reply = gs->userlist_reply;
-
-	gs->userlist_reply = NULL;
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_DCC7_ID_REPLY.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_dcc7_id_reply(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
-
-	return gg_dcc7_handle_id(gs, ge, ptr, len);
-}
-
-/**
- * \internal Obsługuje pakiet GG_DCC7_ACCEPT.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_dcc7_accept(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
-
-	return gg_dcc7_handle_accept(gs, ge, ptr, len);
-}
-
-/**
- * \internal Obsługuje pakiet GG_DCC7_NEW.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_dcc7_new(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
-
-	return gg_dcc7_handle_new(gs, ge, ptr, len);
-}
-
-/**
- * \internal Obsługuje pakiet GG_DCC7_REJECT.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_dcc7_reject(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
-
-	return gg_dcc7_handle_reject(gs, ge, ptr, len);
-}
-
-/**
- * \internal Obsługuje pakiet GG_DCC7_INFO.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_dcc7_info(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
-
-	return gg_dcc7_handle_info(gs, ge, ptr, len);
-}
-
-/**
- * \internal Analizuje przychodzący pakiet z obrazkiem.
- *
- * \param e Struktura zdarzenia
- * \param p Bufor z danymi
- * \param len Długość bufora
- * \param sess Struktura sesji
- * \param sender Numer nadawcy
- * \param type Typ pakietu (NIE typ GG_MSG_OPTION_IMAGE_*)
- */
-static void gg_image_queue_parse(struct gg_event *e, const char *p,
-	unsigned int len, struct gg_session *sess, uin_t sender,
-	uint32_t type)
-{
-	const struct gg_msg_image_reply *i = (const void*) p;
-	struct gg_image_queue *q, *qq;
-
-	gg_debug_session(sess, GG_DEBUG_VERBOSE,
-		"// gg_image_queue_parse(%p, %p, %d, %p, %u, %d)\n",
-		e, p, len, sess, sender, type);
-
-	if (!p || !sess || !e) {
-		errno = EFAULT;
-		return;
-	}
-
-	if (i->flag == GG_MSG_OPTION_IMAGE_REQUEST) {
-		e->type = GG_EVENT_IMAGE_REQUEST;
-		e->event.image_request.sender = sender;
-		e->event.image_reply.size = i->size;
-		e->event.image_request.crc32 = i->crc32;
-		return;
-	}
-
-	/* znajdź dany obrazek w kolejce danej sesji */
-
-	for (qq = sess->images, q = NULL; qq; qq = qq->next) {
-		if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) {
-			q = qq;
-			break;
-		}
-	}
-
-	if (!q) {
-		gg_debug_session(sess, GG_DEBUG_WARNING,
-			"// gg_image_queue_parse() unknown image from %d, "
-			"size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
-		return;
-	}
-
-	if (q->packet_type == 0)
-		q->packet_type = type;
-	if (q->packet_type != type)
-		return;
-
-	if (i->flag == GG_MSG_OPTION_IMAGE_REPLY) {
-		q->done = 0;
-
-		len -= sizeof(struct gg_msg_image_reply);
-		p += sizeof(struct gg_msg_image_reply);
-
-		if (memchr(p, 0, len) == NULL) {
-			gg_debug_session(sess, GG_DEBUG_ERROR,
-				"// gg_image_queue_parse() malformed packet "
-				"from %d, unlimited filename\n", sender);
-			return;
-		}
-
-		if (!(q->filename = strdup(p))) {
-			gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_image_queue_parse() out of memory\n");
-			return;
-		}
-
-		len -= strlen(p) + 1;
-		p += strlen(p) + 1;
-	} else if (i->flag == GG_MSG_OPTION_IMAGE_REPLY_MORE) {
-		len -= sizeof(struct gg_msg_image_reply);
-		p += sizeof(struct gg_msg_image_reply);
-	} else {
-		gg_debug_session(sess, GG_DEBUG_WARNING, "// gg_image_queue_parse() unexpected flag\n");
-		return;
-	}
-
-	if (q->done + len > q->size) {
-		gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_image_queue_parse() got too much\n");
-		len = q->size - q->done;
-	}
-
-	memcpy(q->image + q->done, p, len);
-	q->done += len;
-
-	gg_debug_session(sess, GG_DEBUG_VERBOSE,
-		"// gg_image_queue_parse() got image part (done: %d of %d)\n",
-		q->done, q->size);
-
-	/* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
-
-	if (q->done >= q->size) {
-		gg_debug_session(sess, GG_DEBUG_VERBOSE,
-			"// gg_image_queue_parse() image ready\n");
-
-		e->type = GG_EVENT_IMAGE_REPLY;
-		e->event.image_reply.sender = sender;
-		e->event.image_reply.size = q->size;
-		e->event.image_reply.crc32 = q->crc32;
-		e->event.image_reply.filename = q->filename;
-		e->event.image_reply.image = q->image;
-
-		gg_image_queue_remove(sess, q, 0);
-
-		free(q);
-	}
-}
-
-/**
- * \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.
- * \param packet_type Typ pakietu, w którym otrzymaliśmy wiadomość.
- *
- * \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_options(struct gg_session *sess,
-	struct gg_event *e, uin_t sender, const char *p, const char *packet_end,
-	uint32_t packet_type)
-{
-	while (p < packet_end) {
-		switch (*p) {
-			case GG_MSG_OPTION_CONFERENCE:
-			{
-				const struct gg_msg_recipients *m = (const void*) p;
-				uint32_t i, count;
-
-				p += sizeof(*m);
-
-				if (p > packet_end) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" 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_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" packet out of bounds (1.5)\n");
-					goto malformed;
-				}
-
-				if (e->event.msg.recipients != NULL) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" e->event.msg.recipients already exist\n");
-					goto malformed;
-				}
-
-				e->event.msg.recipients = malloc(count * sizeof(uin_t));
-
-				if (e->event.msg.recipients == NULL) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" not enough memory for recipients data\n");
-					goto fail;
-				}
-
-				memcpy(e->event.msg.recipients, p, count * sizeof(uin_t));
-				p += count * sizeof(uin_t);
-
-				for (i = 0; i < count; i++)
-					e->event.msg.recipients[i] = gg_fix32(e->event.msg.recipients[i]);
-
-				e->event.msg.recipients_count = count;
-
-				break;
-			}
-
-			case GG_MSG_OPTION_ATTRIBUTES:
-			{
-				uint16_t len;
-				char *buf;
-
-				if (p + 3 > packet_end) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" packet out of bounds (2)\n");
-					goto malformed;
-				}
-
-				memcpy(&len, p + 1, sizeof(uint16_t));
-				len = gg_fix16(len);
-
-				if (e->event.msg.formats != NULL) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" e->event.msg.formats already exist\n");
-					goto malformed;
-				}
-
-				buf = malloc(len);
-
-				if (buf == NULL) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" not enough memory for richtext data\n");
-					goto fail;
-				}
-
-				p += 3;
-
-				if (p + len > packet_end) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" packet out of bounds (3)\n");
-					free(buf);
-					goto malformed;
-				}
-
-				memcpy(buf, p, len);
-
-				e->event.msg.formats = buf;
-				e->event.msg.formats_length = len;
-
-				p += len;
-
-				break;
-			}
-
-			case GG_MSG_OPTION_IMAGE_REQUEST:
-			{
-				const struct gg_msg_image_request *i = (const void*) p;
-
-				if (p + sizeof(*i) > packet_end) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg() "
-						"packet out of bounds (3)\n");
-					goto malformed;
-				}
-
-				if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options()"
-						" mixed options (1)\n");
-					goto malformed;
-				}
-
-				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;
-
-				goto handled;
-			}
-
-			case GG_MSG_OPTION_IMAGE_REPLY:
-			case GG_MSG_OPTION_IMAGE_REPLY_MORE:
-			{
-				struct gg_msg_image_reply *rep = (void*) p;
-
-				if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
-					gg_debug_session(sess, GG_DEBUG_MISC,
-						"// gg_handle_recv_msg_options() "
-						"mixed options (2)\n");
-					goto malformed;
-				}
-
-				if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
-
-					/* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
-
-					e->type = GG_EVENT_IMAGE_REPLY;
-					e->event.image_reply.sender = 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;
-					goto handled;
-
-				} else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
-
-					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, sender, packet_type);
-
-				goto handled;
-			}
-
-			default:
-			{
-				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 Wysyła potwierdzenie odebrania wiadomości.
- *
- * \param gs Struktura sesji
- * \param seq Numer sekwencyjny odebranej wiadomości
- *
- * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
- */
-static int gg_session_send_msg_ack(struct gg_session *gs, uint32_t seq)
-{
-	struct gg_recv_msg_ack pkt;
-
-	gg_debug_session(gs, GG_DEBUG_FUNCTION, "** gg_session_send_msg_ack(%p);\n", gs);
-
-	if ((gs->protocol_features & GG_FEATURE_MSG_ACK) == 0)
-		return 0;
-
-	/* Kiedyś zdawało nam się, że mamy wysyłać liczbę odebranych
-	 * wiadomości, ale okazało się, że numer sekwencyjny. */
-	gs->recv_msg_count++;
-
-	pkt.seq = gg_fix32(seq);
-
-	return gg_send_packet(gs, GG_RECV_MSG_ACK, &pkt, sizeof(pkt), NULL);
-}
-
-/**
- * \internal Obsługuje pakiet GG_RECV_MSG.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_recv_msg(struct gg_session *sess, uint32_t type,
-	const char *packet, size_t length, struct gg_event *e)
-{
-	const struct gg_recv_msg *r = (const struct gg_recv_msg*) packet;
-	const char *payload = packet + sizeof(struct gg_recv_msg);
-	const char *payload_end = packet + length;
-	size_t len;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %"
-		GG_SIZE_FMT ", %p);\n", packet, length, e);
-
-	if (sess == NULL)
-		goto fail;
-
-	if ((r->seq == 0) && (r->msgclass == 0)) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
-		goto malformed;
-	}
-
-	/* jednobajtowa wiadomość o treści \x02 to żądanie połączenia DCC */
-	if (*payload == GG_MSG_CALLBACK && payload == payload_end - 1) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
-		length = 1;
-	} else {
-		const char *options;
-
-		options = memchr(payload, 0, (size_t) (payload_end - payload));
-
-		if (options == NULL) {
-			gg_debug_session(sess, GG_DEBUG_MISC,
-				"// gg_handle_recv_msg() malformed packet, "
-				"message out of bounds (0)\n");
-			goto malformed;
-		}
-
-		length = (size_t) (options - payload);
-
-		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), options + 1, payload_end, type)) {
-			case -1:	/* handled */
-				gg_session_send_msg_ack(sess, gg_fix32(r->seq));
-				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.seq = gg_fix32(r->seq);
-
-	e->event.msg.message = (unsigned char*)gg_encoding_convert(payload,
-		GG_ENCODING_CP1250, sess->encoding, length, -1);
-	if (e->event.msg.message == NULL) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg() out of memory\n");
-		goto fail;
-	}
-
-	len = gg_message_text_to_html(NULL, (char*)e->event.msg.message,
-		sess->encoding, e->event.msg.formats,
-		e->event.msg.formats_length);
-	e->event.msg.xhtml_message = malloc(len + 1);
-
-	if (e->event.msg.xhtml_message == NULL) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg() out of memory\n");
-		goto fail;
-	}
-
-	gg_message_text_to_html(e->event.msg.xhtml_message,
-		(char*)e->event.msg.message, sess->encoding,
-		e->event.msg.formats, e->event.msg.formats_length);
-
-	gg_session_send_msg_ack(sess, gg_fix32(r->seq));
-	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);
-	gg_session_send_msg_ack(sess, gg_fix32(r->seq));
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_RECV_MSG80.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_recv_msg_80(struct gg_session *sess, uint32_t type,
-	const char *packet, size_t length, struct gg_event *e)
-{
-	const struct gg_recv_msg80 *r = (const 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, %" GG_SIZE_FMT ", %p);\n",
-		packet, length, e);
-
-	if (sess == NULL)
-		goto fail;
-
-	if (r->seq == 0 && r->msgclass == 0) {
-		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 >= 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 > 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. */
-	}
-
-	/* Normalna sytuacja, więc nie podpada pod powyższy warunek. */
-	if (offset_attr == length)
-		offset_attr = 0;
-
-	if (memchr(packet + offset_plain, 0, 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 = (type != GG_RECV_OWN_MSG) ? GG_EVENT_MSG : GG_EVENT_MULTILOGON_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 (offset_attr != 0) {
-		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender),
-			packet + offset_attr, packet + length, type))
-		{
-			case -1:	/* handled */
-				gg_session_send_msg_ack(sess, gg_fix32(r->seq));
-				return 0;
-
-			case -2:	/* failed */
-				goto fail;
-
-			case -3:	/* malformed */
-				goto malformed;
-		}
-	}
-
-	if (sess->encoding == GG_ENCODING_CP1250) {
-		e->event.msg.message = (unsigned char*) strdup(packet + offset_plain);
-
-		if (e->event.msg.message == NULL) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg_80() out of memory\n");
-			goto fail;
-		}
-	} else {
-		if (offset_plain > sizeof(struct gg_recv_msg80)) {
-			size_t len, fmt_len;
-
-			len = gg_message_html_to_text(NULL, NULL, &fmt_len,
-				packet + sizeof(struct gg_recv_msg80),
-				GG_ENCODING_UTF8);
-			e->event.msg.message = malloc(len + 1);
-
-			if (e->event.msg.message == NULL) {
-				gg_debug_session(sess, GG_DEBUG_MISC,
-					"// gg_session_handle_recv_msg_80() "
-					"out of memory\n");
-				goto fail;
-			}
-
-			free(e->event.msg.formats);
-			e->event.msg.formats_length = fmt_len;
-			e->event.msg.formats = malloc(fmt_len);
-
-			if (e->event.msg.formats == NULL) {
-				gg_debug_session(sess, GG_DEBUG_MISC,
-					"// gg_session_handle_recv_msg_80() "
-					"out of memory\n");
-				goto fail;
-			}
-
-			gg_message_html_to_text((char*)e->event.msg.message,
-				e->event.msg.formats, NULL,
-				packet + sizeof(struct gg_recv_msg80),
-				GG_ENCODING_UTF8);
-		} else {
-			e->event.msg.message = (unsigned char*)gg_encoding_convert(
-				packet + offset_plain, GG_ENCODING_CP1250,
-				sess->encoding, -1, -1);
-
-			if (e->event.msg.message == NULL) {
-				gg_debug_session(sess, GG_DEBUG_MISC,
-					"// gg_session_handle_recv_msg_80() "
-					"out of memory\n");
-				goto fail;
-			}
-		}
-	}
-
-	if (offset_plain > sizeof(struct gg_recv_msg80)) {
-		e->event.msg.xhtml_message = gg_encoding_convert(
-			packet + sizeof(struct gg_recv_msg80), GG_ENCODING_UTF8,
-			sess->encoding, -1, -1);
-
-		if (e->event.msg.xhtml_message == NULL) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg_80() out of memory\n");
-			goto fail;
-		}
-	} else {
-		size_t len;
-
-		len = gg_message_text_to_html(NULL,
-			(char*)e->event.msg.message, sess->encoding,
-			e->event.msg.formats, e->event.msg.formats_length);
-		e->event.msg.xhtml_message = malloc(len + 1);
-
-		if (e->event.msg.xhtml_message == NULL) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg_80() out of memory\n");
-			goto fail;
-		}
-
-		gg_message_text_to_html(e->event.msg.xhtml_message,
-			(char*)e->event.msg.message, sess->encoding,
-			e->event.msg.formats, e->event.msg.formats_length);
-	}
-
-	gg_session_send_msg_ack(sess, gg_fix32(r->seq));
-	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);
-	gg_session_send_msg_ack(sess, gg_fix32(r->seq));
-	return 0;
-}
-
-static int gg_session_handle_recv_msg_110(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110RecvMessage *msg = gg110_recv_message__unpack(NULL, len, (uint8_t*)ptr);
-	uint8_t ack_type;
-	uin_t sender = 0;
-	uint32_t seq;
-	int succ = 1;
-	struct gg_event_msg *ev = &ge->event.msg;
-
-	gg_debug_session(gs, GG_DEBUG_FUNCTION,
-		"** gg_session_handle_recv_msg_110(%p, %" GG_SIZE_FMT
-		", %p);\n", ptr, len, ge);
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110RecvMessage", msg))
-		return -1;
-
-	seq = msg->seq;
-	if (type == GG_CHAT_RECV_MSG || type == GG_CHAT_RECV_OWN_MSG)
-		ack_type = GG110_ACK__TYPE__CHAT;
-	else
-		ack_type = GG110_ACK__TYPE__MSG;
-
-	if (msg->has_msg_id || msg->has_conv_id) {
-		msg->msg_id = msg->has_msg_id ? msg->msg_id : 0;
-		msg->conv_id = msg->has_conv_id ? msg->conv_id : 0;
-		gg_debug_session(gs, GG_DEBUG_VERBOSE,
-			"// gg_session_handle_recv_msg_110() "
-			"msg_id=%016" PRIx64 " conv_id=%016" PRIx64 "\n",
-			msg->msg_id, msg->conv_id);
-	}
-
-	if (msg->has_sender)
-		sender = gg_protobuf_get_uin(msg->sender);
-	else if (type == GG_CHAT_RECV_OWN_MSG)
-		sender = gs->uin;
-
-	if (msg->has_data && msg->msg_plain[0] == '\0') {
-		if (msg->data.len < sizeof(struct gg_msg_image_reply)) {
-			gg_debug_session(gs, GG_DEBUG_ERROR,
-				"// gg_session_handle_recv_msg_110() "
-				"packet too small (%" GG_SIZE_FMT " < %"
-				GG_SIZE_FMT ")\n", msg->data.len,
-				sizeof(struct gg_msg_image_reply));
-		} else {
-			gg_image_queue_parse(ge, (char *)msg->data.data,
-				msg->data.len, gs, sender, type);
-		}
-		gg110_recv_message__free_unpacked(msg, NULL);
-		return gg_ack_110(gs, GG110_ACK__TYPE__MSG, seq, ge);
-	}
-
-	if (type == GG_RECV_OWN_MSG110 || type == GG_CHAT_RECV_OWN_MSG)
-		ge->type = GG_EVENT_MULTILOGON_MSG;
-	else
-		ge->type = GG_EVENT_MSG;
-	ev->msgclass = GG_CLASS_CHAT;
-	ev->seq = seq;
-	ev->sender = sender;
-	ev->flags = msg->flags;
-	ev->seq = seq;
-	ev->time = msg->time;
-
-	if (abs(msg->time - gg_server_time(gs)) > 2)
-		ev->msgclass |= GG_CLASS_QUEUED;
-
-	ev->message = NULL;
-	if (msg->msg_plain[0] != '\0') {
-		ev->message = (unsigned char*)gg_encoding_convert(
-			msg->msg_plain, GG_ENCODING_UTF8, gs->encoding, -1, -1);
-		succ = succ && (ev->message != NULL);
-	}
-	ev->xhtml_message = NULL;
-	if (msg->msg_xhtml != NULL) {
-		ev->xhtml_message = gg_encoding_convert(
-			msg->msg_xhtml, GG_ENCODING_UTF8, gs->encoding, -1, -1);
-		succ = succ && (ev->xhtml_message != NULL);
-	}
-
-	/* wiadomości wysłane z mobilnego gg nie posiadają wersji xhtml */
-	if (ev->message == NULL && ev->xhtml_message == NULL) {
-		ev->message = (unsigned char*)strdup("");
-		succ = succ && (ev->message != NULL);
-	} else if (ev->message == NULL) {
-		ev->message = (unsigned char*)gg_message_html_to_text_110(
-			ev->xhtml_message);
-		succ = succ && (ev->message != NULL);
-	} else if (ev->xhtml_message == NULL) {
-		ev->xhtml_message = gg_message_text_to_html_110(
-			(char*)ev->message, -1);
-		succ = succ && (ev->xhtml_message != NULL);
-	}
-
-	/* otrzymywane tylko od gg <= 10.5 */
-	ev->formats = NULL;
-	ev->formats_length = 0;
-	if (msg->has_data && succ) {
-		ev->formats_length = msg->data.len;
-		ev->formats = malloc(msg->data.len);
-		if (ev->formats == NULL)
-			succ = 0;
-		else
-			memcpy(ev->formats, msg->data.data, msg->data.len);
-	}
-
-	if (msg->has_chat_id && succ) {
-		gg_chat_list_t *chat;
-
-		ev->chat_id = msg->chat_id;
-
-		chat = gg_chat_find(gs, msg->chat_id);
-		if (chat) {
-			size_t rcpt_size = chat->participants_count *
-				sizeof(uin_t);
-			ev->recipients = malloc(rcpt_size);
-			ev->recipients_count = chat->participants_count;
-			if (ev->recipients == NULL)
-				succ = 0;
-			else {
-				memcpy(ev->recipients, chat->participants,
-					rcpt_size);
-			}
-		}
-	}
-
-	gg110_recv_message__free_unpacked(msg, NULL);
-
-	if (gg_ack_110(gs, ack_type, seq, ge) != 0)
-		succ = 0;
-
-	if (succ)
-		return 0;
-	else {
-		free(ev->message);
-		free(ev->xhtml_message);
-		free(ev->formats);
-		free(ev->recipients);
-		return -1;
-	}
-}
-
-/**
- * \internal Obsługuje pakiet GG_STATUS.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_status(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_status *s = (const void*) ptr;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
-
-	ge->type = GG_EVENT_STATUS;
-	ge->event.status.uin = gg_fix32(s->uin);
-	ge->event.status.status = gg_fix32(s->status);
-	ge->event.status.descr = NULL;
-
-	if (len > sizeof(*s)) {
-		ge->event.status.descr = gg_encoding_convert(ptr + sizeof(*s),
-			GG_ENCODING_CP1250, gs->encoding, len - sizeof(*s), -1);
-
-		if (ge->event.status.descr == NULL) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiety GG_STATUS60, GG_STATUS77 i GG_STATUS80BETA.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_status_60_77_80beta(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_status60 *s60 = (const void*) ptr;
-	const struct gg_status77 *s77 = (const void*) ptr;
-	size_t struct_len;
-	uint32_t uin;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
-
-	ge->type = GG_EVENT_STATUS60;
-	ge->event.status60.descr = NULL;
-	ge->event.status60.time = 0;
-
-	if (type == GG_STATUS60) {
-		uin = gg_fix32(s60->uin);
-		ge->event.status60.status = s60->status;
-		ge->event.status60.remote_ip = s60->remote_ip;
-		ge->event.status60.remote_port = gg_fix16(s60->remote_port);
-		ge->event.status60.version = s60->version;
-		ge->event.status60.image_size = s60->image_size;
-		struct_len = sizeof(*s60);
-	} else {
-		uin = gg_fix32(s77->uin);
-		ge->event.status60.status = s77->status;
-		ge->event.status60.remote_ip = s77->remote_ip;
-		ge->event.status60.remote_port = gg_fix16(s77->remote_port);
-		ge->event.status60.version = s77->version;
-		ge->event.status60.image_size = s77->image_size;
-		struct_len = sizeof(*s77);
-	}
-
-	ge->event.status60.uin = uin & 0x00ffffff;
-
-	if (uin & 0x40000000)
-		ge->event.status60.version |= GG_HAS_AUDIO_MASK;
-	if (uin & 0x20000000)
-		ge->event.status60.version |= GG_HAS_AUDIO7_MASK;
-	if (uin & 0x08000000)
-		ge->event.status60.version |= GG_ERA_OMNIX_MASK;
-
-	if (len > struct_len) {
-		size_t descr_len;
-
-		descr_len = len - struct_len;
-
-		ge->event.status60.descr = gg_encoding_convert(ptr + struct_len,
-			(type == GG_STATUS80BETA) ? GG_ENCODING_UTF8 : GG_ENCODING_CP1250,
-			gs->encoding, descr_len, -1);
-
-		if (ge->event.status60.descr == NULL) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			return -1;
-		}
-
-		if (descr_len > 4 && ptr[len - 5] == 0) {
-			uint32_t t;
-			memcpy(&t, ptr + len - 4, sizeof(uint32_t));
-			ge->event.status60.time = gg_fix32(t);
-		}
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_NOTIFY_REPLY.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_notify_reply(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_notify_reply *n = (const void*) ptr;
-	char *descr;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-	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)
-	{
-		size_t descr_len;
-
-		ge->type = GG_EVENT_NOTIFY_DESCR;
-
-		if (!(ge->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			return -1;
-		}
-		ge->event.notify_descr.notify[1].uin = 0;
-		memcpy(ge->event.notify_descr.notify, ptr, sizeof(*n));
-		ge->event.notify_descr.notify[0].uin = gg_fix32(ge->event.notify_descr.notify[0].uin);
-		ge->event.notify_descr.notify[0].status = gg_fix32(ge->event.notify_descr.notify[0].status);
-		ge->event.notify_descr.notify[0].remote_port = gg_fix16(ge->event.notify_descr.notify[0].remote_port);
-		ge->event.notify_descr.notify[0].version = gg_fix32(ge->event.notify_descr.notify[0].version);
-
-		descr_len = len - sizeof(*n);
-
-		descr = gg_encoding_convert(ptr + sizeof(*n), GG_ENCODING_CP1250, gs->encoding, descr_len, -1);
-
-		if (descr == NULL) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			return -1;
-		}
-
-		ge->event.notify_descr.descr = descr;
-
-	} else {
-		unsigned int i, count;
-
-		ge->type = GG_EVENT_NOTIFY;
-
-		if (!(ge->event.notify = (void*) malloc(len + 2 * sizeof(*n)))) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			return -1;
-		}
-
-		memcpy(ge->event.notify, ptr, len);
-		count = len / sizeof(*n);
-		ge->event.notify[count].uin = 0;
-
-		for (i = 0; i < count; i++) {
-			ge->event.notify[i].uin = gg_fix32(ge->event.notify[i].uin);
-			ge->event.notify[i].status = gg_fix32(ge->event.notify[i].status);
-			ge->event.notify[i].remote_port = gg_fix16(ge->event.notify[i].remote_port);
-			ge->event.notify[i].version = gg_fix32(ge->event.notify[i].version);
-		}
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_STATUS80.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_status_80(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_notify_reply80 *n = (const void*) ptr;
-	size_t descr_len;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
-
-	ge->type = GG_EVENT_STATUS60;
-	ge->event.status60.uin = gg_fix32(n->uin);
-	ge->event.status60.status = gg_fix32(n->status);
-	ge->event.status60.remote_ip = n->remote_ip;
-	ge->event.status60.remote_port = gg_fix16(n->remote_port);
-	ge->event.status60.version = 0;
-	ge->event.status60.image_size = n->image_size;
-	ge->event.status60.descr = NULL;
-	ge->event.status60.time = 0;
-
-	descr_len = gg_fix32(n->descr_len);
-
-	if (descr_len != 0 && sizeof(struct gg_notify_reply80) + descr_len <= len) {
-		ge->event.status60.descr = gg_encoding_convert(
-			(const char*) n + sizeof(struct gg_notify_reply80),
-			GG_ENCODING_UTF8, gs->encoding, descr_len, -1);
-
-		if (ge->event.status60.descr == NULL) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			return -1;
-		}
-
-		/* XXX czas */
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_NOTIFY_REPLY80.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_notify_reply_80(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_notify_reply80 *n = (const void*) ptr;
-	unsigned int length = len, i = 0;
-
-	/* TODO: najpierw przeanalizować strukturę i określić
-	 * liczbę rekordów, żeby obyć się bez realloc()
-	 */
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-	ge->type = GG_EVENT_NOTIFY60;
-	ge->event.notify60 = malloc(sizeof(*ge->event.notify60));
-
-	if (!ge->event.notify60) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-		return -1;
-	}
-
-	ge->event.notify60[0].uin = 0;
-
-	while (length >= sizeof(struct gg_notify_reply80)) {
-		uin_t uin = gg_fix32(n->uin);
-		int descr_len;
-		void *tmp;
-
-		ge->event.notify60[i].uin = uin;
-		ge->event.notify60[i].status = gg_fix32(n->status);
-		ge->event.notify60[i].remote_ip = n->remote_ip;
-		ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
-		ge->event.notify60[i].version = 0;
-		ge->event.notify60[i].image_size = n->image_size;
-		ge->event.notify60[i].descr = NULL;
-		ge->event.notify60[i].time = 0;
-
-		descr_len = gg_fix32(n->descr_len);
-
-		if (descr_len != 0) {
-			if (sizeof(struct gg_notify_reply80) + descr_len <= length) {
-				ge->event.notify60[i].descr = gg_encoding_convert(
-					(const char*) n + sizeof(struct gg_notify_reply80),
-					GG_ENCODING_UTF8, gs->encoding, descr_len, -1);
-
-				if (ge->event.notify60[i].descr == NULL) {
-					gg_debug_session(gs, GG_DEBUG_MISC,
-						"// gg_watch_fd_connected() "
-						"out of memory\n");
-					return -1;
-				}
-
-				/* XXX czas */
-
-				length -= sizeof(struct gg_notify_reply80) + descr_len;
-				n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply80) + descr_len);
-			} else {
-				length = 0;
-			}
-
-		} else {
-			length -= sizeof(struct gg_notify_reply80);
-			n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply80));
-		}
-
-		if (!(tmp = realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			free(ge->event.notify60);
-			return -1;
-		}
-
-		ge->event.notify60 = tmp;
-		ge->event.notify60[++i].uin = 0;
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiety GG_NOTIFY_REPLY77 i GG_NOTIFY_REPLY80BETA.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_notify_reply_77_80beta(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_notify_reply77 *n = (const void*) ptr;
-	unsigned int length = len, i = 0;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-	ge->type = GG_EVENT_NOTIFY60;
-	ge->event.notify60 = malloc(sizeof(*ge->event.notify60));
-
-	if (ge->event.notify60 == NULL) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-		return -1;
-	}
-
-	ge->event.notify60[0].uin = 0;
-
-	while (length >= sizeof(struct gg_notify_reply77)) {
-		uin_t uin = gg_fix32(n->uin);
-		void *tmp;
-
-		ge->event.notify60[i].uin = uin & 0x00ffffff;
-		ge->event.notify60[i].status = n->status;
-		ge->event.notify60[i].remote_ip = n->remote_ip;
-		ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
-		ge->event.notify60[i].version = n->version;
-		ge->event.notify60[i].image_size = n->image_size;
-		ge->event.notify60[i].descr = NULL;
-		ge->event.notify60[i].time = 0;
-
-		if (uin & 0x40000000)
-			ge->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
-		if (uin & 0x20000000)
-			ge->event.notify60[i].version |= GG_HAS_AUDIO7_MASK;
-		if (uin & 0x08000000)
-			ge->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
-
-		if (GG_S_D(n->status)) {
-			unsigned char descr_len = *((const char*) n + sizeof(struct gg_notify_reply77));
-
-			if (sizeof(struct gg_notify_reply77) + descr_len <= length) {
-				ge->event.notify60[i].descr = gg_encoding_convert(
-					(const char*) n + sizeof(struct gg_notify_reply77) + 1,
-					(type == GG_NOTIFY_REPLY80BETA) ? GG_ENCODING_UTF8 : GG_ENCODING_CP1250,
-					gs->encoding, descr_len, -1);
-
-				if (ge->event.notify60[i].descr == NULL) {
-					gg_debug_session(gs, GG_DEBUG_MISC,
-						"// gg_watch_fd_connected() "
-						"out of memory\n");
-					return -1;
-				}
-
-				/* XXX czas */
-
-				length -= sizeof(struct gg_notify_reply77) + descr_len + 1;
-				n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1);
-			} else {
-				length = 0;
-			}
-
-		} else {
-			length -= sizeof(struct gg_notify_reply77);
-			n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply77));
-		}
-
-		if (!(tmp = realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			free(ge->event.notify60);
-			return -1;
-		}
-
-		ge->event.notify60 = tmp;
-		ge->event.notify60[++i].uin = 0;
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_NOTIFY_REPLY60.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_notify_reply_60(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_notify_reply60 *n = (const void*) ptr;
-	unsigned int length = len, i = 0;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-	ge->type = GG_EVENT_NOTIFY60;
-	ge->event.notify60 = malloc(sizeof(*ge->event.notify60));
-
-	if (ge->event.notify60 == NULL) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-		return -1;
-	}
-
-	ge->event.notify60[0].uin = 0;
-
-	while (length >= sizeof(struct gg_notify_reply60)) {
-		uin_t uin = gg_fix32(n->uin);
-		void *tmp;
-
-		ge->event.notify60[i].uin = uin & 0x00ffffff;
-		ge->event.notify60[i].status = n->status;
-		ge->event.notify60[i].remote_ip = n->remote_ip;
-		ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
-		ge->event.notify60[i].version = n->version;
-		ge->event.notify60[i].image_size = n->image_size;
-		ge->event.notify60[i].descr = NULL;
-		ge->event.notify60[i].time = 0;
-
-		if (uin & 0x40000000)
-			ge->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
-		if (uin & 0x08000000)
-			ge->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
-
-		if (GG_S_D(n->status)) {
-			unsigned char descr_len = *((const char*) n + sizeof(struct gg_notify_reply60));
-
-			if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
-				char *descr;
-
-				descr = gg_encoding_convert((const char*) n +
-					sizeof(struct gg_notify_reply60) + 1,
-					GG_ENCODING_CP1250, gs->encoding,
-					descr_len, -1);
-
-				if (descr == NULL) {
-					gg_debug_session(gs, GG_DEBUG_MISC,
-						"// gg_watch_fd_connected() "
-						"out of memory\n");
-					return -1;
-				}
-
-				ge->event.notify60[i].descr = descr;
-
-				/* XXX czas */
-
-				length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
-				n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
-			} else {
-				length = 0;
-			}
-
-		} else {
-			length -= sizeof(struct gg_notify_reply60);
-			n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply60));
-		}
-
-		if (!(tmp = realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
-			free(ge->event.notify60);
-			return -1;
-		}
-
-		ge->event.notify60 = tmp;
-		ge->event.notify60[++i].uin = 0;
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_USER_DATA.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_user_data(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	struct gg_user_data d;
-	const char *p = (const char*) ptr;
-	const char *packet_end = (const char*) ptr + len;
-	struct gg_event_user_data_user *users;
-	unsigned int i, j;
-	int res = 0;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received user data\n");
-
-	ge->event.user_data.user_count = 0;
-	ge->event.user_data.users = NULL;
-
-	if (ptr + sizeof(d) > packet_end)
-		goto malformed;
-
-	memcpy(&d, p, sizeof(d));
-	p += sizeof(d);
-
-	d.type = gg_fix32(d.type);
-	d.user_count = gg_fix32(d.user_count);
-
-	if (d.user_count > 0xffff) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (1)\n");
-		goto malformed;
-	}
-
-	if (d.user_count > 0) {
-		users = calloc(d.user_count, sizeof(struct gg_event_user_data_user));
-
-		if (users == NULL) {
-			gg_debug_session(gs, GG_DEBUG_MISC,
-				"// gg_session_handle_user_data() out of memory"
-				" (%d*%" GG_SIZE_FMT ")\n", d.user_count,
-				sizeof(struct gg_event_user_data_user));
-			goto fail;
-		}
-	} else {
-		users = NULL;
-	}
-
-	ge->type = GG_EVENT_USER_DATA;
-	ge->event.user_data.type = d.type;
-	ge->event.user_data.user_count = d.user_count;
-	ge->event.user_data.users = users;
-
-	gg_debug_session(gs, GG_DEBUG_DUMP, "type=%d, count=%d\n", d.type, d.user_count);
-
-	for (i = 0; i < d.user_count; i++) {
-		struct gg_user_data_user u;
-		struct gg_event_user_data_attr *attrs;
-
-		if (p + sizeof(u) > packet_end) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (2)\n");
-			goto malformed;
-		}
-
-		memcpy(&u, p, sizeof(u));
-		p += sizeof(u);
-
-		u.uin = gg_fix32(u.uin);
-		u.attr_count = gg_fix32(u.attr_count);
-
-		if (u.attr_count > 0xffff) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (2)\n");
-			goto malformed;
-		}
-
-		if (u.attr_count > 0) {
-			attrs = calloc(u.attr_count, sizeof(struct gg_event_user_data_attr));
-
-			if (attrs == NULL) {
-				gg_debug_session(gs, GG_DEBUG_MISC,
-					"// gg_session_handle_user_data() "
-					"out of memory (%d*%" GG_SIZE_FMT
-					")\n", u.attr_count,
-					sizeof(struct gg_event_user_data_attr));
-				goto fail;
-			}
-		} else {
-			attrs = NULL;
-		}
-
-		users[i].uin = u.uin;
-		users[i].attr_count = u.attr_count;
-		users[i].attrs = attrs;
-
-		gg_debug_session(gs, GG_DEBUG_DUMP, "    uin=%d, count=%d\n", u.uin, u.attr_count);
-
-		for (j = 0; j < u.attr_count; j++) {
-			uint32_t key_size;
-			uint32_t attr_type;
-			uint32_t value_size;
-			char *key;
-			char *value;
-
-			if (p + sizeof(key_size) > packet_end) {
-				gg_debug_session(gs, GG_DEBUG_MISC,
-					"// gg_session_handle_user_data()"
-					"malformed packet (3)\n");
-				goto malformed;
-			}
-
-			memcpy(&key_size, p, sizeof(key_size));
-			p += sizeof(key_size);
-
-			key_size = gg_fix32(key_size);
-
-			if (key_size > 0xffff || p + key_size > packet_end) {
-				gg_debug_session(gs, GG_DEBUG_MISC,
-					"// gg_session_handle_user_data() "
-					"malformed packet (3)\n");
-				goto malformed;
-			}
-
-			key = malloc(key_size + 1);
-
-			if (key == NULL) {
-				gg_debug_session(gs, GG_DEBUG_MISC,
-					"// gg_session_handle_user_data() "
-					"out of memory (%d)\n", key_size + 1);
-				goto fail;
-			}
-
-			memcpy(key, p, key_size);
-			p += key_size;
-
-			key[key_size] = 0;
-
-			attrs[j].key = key;
-
-			if (p + sizeof(attr_type) + sizeof(value_size) > packet_end) {
-				gg_debug_session(gs, GG_DEBUG_MISC,
-					"// gg_session_handle_user_data() "
-					"malformed packet (4)\n");
-				goto malformed;
-			}
-
-			memcpy(&attr_type, p, sizeof(attr_type));
-			p += sizeof(attr_type);
-			memcpy(&value_size, p, sizeof(value_size));
-			p += sizeof(value_size);
-
-			attrs[j].type = gg_fix32(attr_type);
-			value_size = gg_fix32(value_size);
-
-			if (value_size > 0xffff || p + value_size > packet_end) {
-				gg_debug_session(gs, GG_DEBUG_MISC,
-					"// gg_session_handle_user_data() "
-					"malformed packet (5)\n");
-				goto malformed;
-			}
-
-			value = malloc(value_size + 1);
-
-			if (value == NULL) {
-				gg_debug_session(gs, GG_DEBUG_MISC,
-					"// gg_session_handle_user_data() "
-					"out of memory (%d)\n", value_size + 1);
-				goto fail;
-			}
-
-			memcpy(value, p, value_size);
-			p += value_size;
-
-			value[value_size] = 0;
-
-			attrs[j].value = value;
-
-			gg_debug_session(gs, GG_DEBUG_DUMP, "	key=\"%s\", "
-				"type=%d, value=\"%s\"\n", key, attr_type, value);
-		}
-	}
-
-	return 0;
-
-fail:
-	res = -1;
-
-malformed:
-	ge->type = GG_EVENT_NONE;
-
-	for (i = 0; i < ge->event.user_data.user_count; i++) {
-		for (j = 0; j < ge->event.user_data.users[i].attr_count; j++) {
-			free(ge->event.user_data.users[i].attrs[j].key);
-			free(ge->event.user_data.users[i].attrs[j].value);
-		}
-
-		free(ge->event.user_data.users[i].attrs);
-	}
-
-	free(ge->event.user_data.users);
-
-	return res;
-}
-
-/**
- * \internal Obsługuje pakiet GG_TYPING_NOTIFICATION.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_typing_notification(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_typing_notification *n = (const void*) ptr;
-	uin_t uin;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received typing notification\n");
-
-	memcpy(&uin, &n->uin, sizeof(uin_t));
-
-	ge->type = GG_EVENT_TYPING_NOTIFICATION;
-	ge->event.typing_notification.uin = gg_fix32(uin);
-	ge->event.typing_notification.length = gg_fix16(n->length);
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_MULTILOGON_INFO.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_multilogon_info(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const char *packet_end = (const char*) ptr + len;
-	const struct gg_multilogon_info *info = (const struct gg_multilogon_info*) ptr;
-	const char *p = (const char*) ptr + sizeof(*info);
-	struct gg_multilogon_session *sessions = NULL;
-	size_t count;
-	size_t i;
-	int res = 0;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received multilogon info\n");
-
-	count = gg_fix32(info->count);
-
-	if (count > 0xffff) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (1)\n");
-		goto malformed;
-	}
-
-	sessions = calloc(count, sizeof(struct gg_multilogon_session));
-
-	if (sessions == NULL) {
-		gg_debug_session(gs, GG_DEBUG_MISC, "// "
-			"gg_handle_multilogon_info() out of memory (%"
-			GG_SIZE_FMT "*%" GG_SIZE_FMT ")\n",
-			count, sizeof(struct gg_multilogon_session));
-		return -1;
-	}
-
-	ge->type = GG_EVENT_MULTILOGON_INFO;
-	ge->event.multilogon_info.count = count;
-	ge->event.multilogon_info.sessions = sessions;
-
-	for (i = 0; i < count; i++) {
-		struct gg_multilogon_info_item item;
-		size_t name_size;
-
-		if (p + sizeof(item) > packet_end) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (2)\n");
-			goto malformed;
-		}
-
-		memcpy(&item, p, sizeof(item));
-
-		sessions[i].id = item.conn_id;
-		sessions[i].remote_addr = item.addr;
-		sessions[i].status_flags = gg_fix32(item.flags);
-		sessions[i].protocol_features = gg_fix32(item.features);
-		sessions[i].logon_time = gg_fix32(item.logon_time);
-
-		p += sizeof(item);
-
-		name_size = gg_fix32(item.name_size);
-
-		if (name_size > 0xffff || p + name_size > packet_end) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (3)\n");
-			goto malformed;
-		}
-
-		sessions[i].name = malloc(name_size + 1);
-
-		if (sessions[i].name == NULL) {
-			gg_debug_session(gs, GG_DEBUG_MISC,
-				"// gg_handle_multilogon_info() out of "
-				"memory (%" GG_SIZE_FMT ")\n", name_size);
-			goto fail;
-		}
-
-		memcpy(sessions[i].name, p, name_size);
-		sessions[i].name[name_size] = 0;
-
-		p += name_size;
-	}
-
-	return 0;
-
-fail:
-	res = -1;
-
-malformed:
-	ge->type = GG_EVENT_NONE;
-
-	for (i = 0; (int) i < ge->event.multilogon_info.count; i++)
-		free(ge->event.multilogon_info.sessions[i].name);
-
-	free(ge->event.multilogon_info.sessions);
-
-	return res;
-}
-
-/**
- * \internal Obsługuje pakiet GG_USERLIST100_VERSION.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_userlist_100_version(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_userlist100_version *version = (const struct gg_userlist100_version*) ptr;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist 100 version\n");
-
-	ge->type = GG_EVENT_USERLIST100_VERSION;
-	ge->event.userlist100_version.version = gg_fix32(version->version);
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet GG_USERLIST100_REPLY.
- *
- * Patrz gg_packet_handler_t
- */
-static int gg_session_handle_userlist_100_reply(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_userlist100_reply *reply = (const struct gg_userlist100_reply*) ptr;
-	char *data = NULL;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist 100 reply\n");
-
-	if (len > sizeof(*reply)) {
-		data = gg_inflate((const unsigned char*) ptr + sizeof(*reply), len - sizeof(*reply));
-
-		if (data == NULL) {
-			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_userlist_100_reply() gg_inflate() failed\n");
-			return -1;
-		}
-	}
-
-	ge->type = GG_EVENT_USERLIST100_REPLY;
-	ge->event.userlist100_reply.type = reply->type;
-	ge->event.userlist100_reply.version = gg_fix32(reply->version);
-	ge->event.userlist100_reply.format_type = reply->format_type;
-	ge->event.userlist100_reply.reply = data;
-
-	return 0;
-}
-
-static int gg_session_handle_imtoken(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110Imtoken *msg = gg110_imtoken__unpack(NULL, len, (uint8_t*)ptr);
-	char *imtoken = NULL;
-	int succ = 1;
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110Imtoken", msg))
-		return -1;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() "
-		"received imtoken\n");
-
-	if (msg->imtoken[0] != '\0') {
-		imtoken = strdup(msg->imtoken);
-		succ = succ && (imtoken != NULL);
-	}
-
-	gg110_imtoken__free_unpacked(msg, NULL);
-
-	ge->type = GG_EVENT_IMTOKEN;
-	ge->event.imtoken.imtoken = imtoken;
-
-	return succ ? 0 : -1;
-}
-
-static int gg_session_handle_pong_110(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110Pong *msg = gg110_pong__unpack(NULL, len, (uint8_t*)ptr);
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110Pong", msg))
-		return -1;
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() "
-		"received pong110\n");
-
-	ge->type = GG_EVENT_PONG110;
-	ge->event.pong110.time = msg->server_time;
-
-	gg_sync_time(gs, msg->server_time);
-
-	gg110_pong__free_unpacked(msg, NULL);
-
-	return 0;
-}
-
-static int gg_session_handle_chat_info(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_tvbuff_t *tvb;
-	uint32_t i;
-
-	uint64_t id;
-	uint32_t version;
-	uint32_t dummy1;
-	uint32_t participants_count;
-	uin_t *participants = NULL;
-
-	tvb = gg_tvbuff_new(ptr, len);
-
-	id = gg_tvbuff_read_uint64(tvb);
-	gg_tvbuff_expected_uint32(tvb, 0); /* unknown */
-	version = gg_tvbuff_read_uint32(tvb);
-	dummy1 = gg_tvbuff_read_uint32(tvb);
-	if (gg_tvbuff_is_valid(tvb) && dummy1 == 1) {
-		uint32_t name_length;
-
-		name_length = gg_tvbuff_read_uint32(tvb);
-		gg_tvbuff_skip(tvb, name_length);
-
-		gg_tvbuff_expected_uint32(tvb, 0); /* unknown */
-		gg_tvbuff_expected_uint32(tvb, 2); /* unknown */
-	}
-	participants_count = gg_tvbuff_read_uint32(tvb);
-	if (id == 0 && participants_count > 0) {
-		gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_WARNING,
-			"// gg_session_handle_chat_info() terminating packet "
-			"shouldn't contain participants\n");
-		participants_count = 0;
-	}
-
-	if (participants_count > 0) {
-		participants = malloc(sizeof(uin_t) * participants_count);
-		if (participants == NULL) {
-			gg_tvbuff_close(tvb);
-			return -1;
-		}
-	}
-
-	for (i = 0; i < participants_count && gg_tvbuff_is_valid(tvb); i++) {
-		participants[i] = gg_tvbuff_read_uint32(tvb);
-		gg_tvbuff_read_uint32(tvb); /* 0x1e lub 0x18 */
-	}
-
-	if (!gg_tvbuff_close(tvb)) {
-		free(participants);
-		return -1;
-	}
-
-	if (id == 0) {
-		ge->type = GG_EVENT_CHAT_INFO_GOT_ALL;
-		return 0;
-	}
-
-	if (0 != gg_chat_update(gs, id, version, participants,
-		participants_count))
-	{
-		free(participants);
-		return -1;
-	}
-
-	ge->type = GG_EVENT_CHAT_INFO;
-	ge->event.chat_info.id = id;
-	ge->event.chat_info.version = version;
-	ge->event.chat_info.participants_count = participants_count;
-	ge->event.chat_info.participants = participants;
-
-	return 0;
-}
-
-static int gg_session_handle_chat_info_update(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110ChatInfoUpdate *msg = gg110_chat_info_update__unpack(NULL, len, (uint8_t*)ptr);
-	gg_chat_list_t *chat;
-	uin_t participant;
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110ChatInfoUpdate", msg))
-		return -1;
-
-	gg_debug_session(gs, GG_DEBUG_VERBOSE,
-		"// gg_session_handle_chat_info_update() "
-		"msg_id=%016" PRIx64 " conv_id=%016" PRIx64 "\n",
-		msg->msg_id, msg->conv_id);
-
-	ge->type = GG_EVENT_CHAT_INFO_UPDATE;
-	ge->event.chat_info_update.id = msg->chat_id;
-	ge->event.chat_info_update.type = msg->update_type;
-	ge->event.chat_info_update.participant = participant = gg_protobuf_get_uin(msg->participant);
-	ge->event.chat_info_update.inviter = gg_protobuf_get_uin(msg->inviter);
-	ge->event.chat_info_update.version = msg->version;
-	ge->event.chat_info_update.time = msg->time;
-
-	chat = gg_chat_find(gs, msg->chat_id);
-	if (!chat) {
-		gg110_chat_info_update__free_unpacked(msg, NULL);
-		return 0;
-	}
-
-	chat->version = msg->version;
-	if (msg->update_type == GG_CHAT_INFO_UPDATE_ENTERED) {
-		uin_t *old_part = chat->participants;
-		chat->participants = realloc(chat->participants,
-			sizeof(uin_t) * chat->participants_count);
-		if (chat->participants == NULL) {
-			chat->participants = old_part;
-			gg_debug_session(gs, GG_DEBUG_ERROR,
-				"// gg_session_handle_chat_info_update() "
-				"out of memory (count=%u)\n",
-				chat->participants_count);
-			return -1;
-		}
-		chat->participants_count++;
-		chat->participants[chat->participants_count - 1] = participant;
-	} else if (msg->update_type == GG_CHAT_INFO_UPDATE_EXITED) {
-		uint32_t idx;
-		for (idx = 0; idx < chat->participants_count; idx++)
-			if (chat->participants[idx] == participant)
-				break;
-		if (chat->participants_count > 1 &&
-			idx < chat->participants_count)
-			chat->participants[idx] = chat->participants[chat->participants_count - 1];
-		if (idx < chat->participants_count) {
-			chat->participants_count--;
-			if (chat->participants_count == 0) {
-				free(chat->participants);
-				chat->participants = NULL;
-			} else {
-				chat->participants = realloc(chat->participants,
-					sizeof(uin_t)*chat->participants_count);
-			}
-		}
-	}
-
-	gg110_chat_info_update__free_unpacked(msg, NULL);
-	return 0;
-}
-
-static int gg_session_handle_chat_created(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_chat_created *p = (const struct gg_chat_created *)ptr;
-
-	if (0 != gg_chat_update(gs, gg_fix64(p->id), 0, &gs->uin, 1))
-		return -1;
-
-	ge->type = GG_EVENT_CHAT_CREATED;
-	ge->event.chat_created.id = gg_fix64(p->id);
-	ge->event.chat_created.seq = gg_fix32(p->seq);
-	return 0;
-}
-
-static int gg_session_handle_chat_invite_ack(struct gg_session *gs,
-	uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_chat_invite_ack *p =
-		(const struct gg_chat_invite_ack *)ptr;
-
-	ge->type = GG_EVENT_CHAT_INVITE_ACK;
-	ge->event.chat_invite_ack.id = gg_fix64(p->id);
-	ge->event.chat_invite_ack.seq = gg_fix32(p->seq);
-
-	return 0;
-}
-
-static int gg_session_handle_chat_left(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	const struct gg_chat_left *p = (const struct gg_chat_left *)ptr;
-
-	ge->type = GG_EVENT_CHAT_INFO_UPDATE;
-	ge->event.chat_info_update.id = gg_fix64(p->id);
-	ge->event.chat_info_update.type = GG_CHAT_INFO_UPDATE_EXITED;
-	/* Właściwie, to nie wiadomo, czy to jest "osoba wychodząca", czy
-	 * "osoba wyrzucająca nas" z konferencji. */
-	ge->event.chat_info_update.participant = gg_fix32(p->uin);
-	ge->event.chat_info_update.inviter = gg_fix32(p->uin);
-	ge->event.chat_info_update.version = 0;
-	ge->event.chat_info_update.time = time(NULL);
-
-	return 0;
-}
-
-static int gg_session_handle_options(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110Options *msg = gg110_options__unpack(NULL, len, (uint8_t*)ptr);
-	size_t i;
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110Options", msg))
-		return -1;
-
-	gg_protobuf_expected(gs, "GG110Options.dummy1", msg->dummy1, 0);
-
-	for (i = 0; i < msg->n_options; i++) {
-		ProtobufKVP *kvp = msg->options[i];
-		if (!GG_PROTOBUF_VALID(gs, "ProtobufKVP", kvp))
-			continue;
-		gg_debug_session(gs, GG_DEBUG_MISC,
-			"// gg_session_handle_options[%s] = \"%s\"\n",
-			kvp->key, kvp->value);
-	}
-
-	gg110_options__free_unpacked(msg, NULL);
-
-	return 0;
-}
-
-static int gg_session_handle_access_info(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110AccessInfo *msg = gg110_access_info__unpack(NULL, len, (uint8_t*)ptr);
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110AccessInfo", msg))
-		return -1;
-
-	gg_debug_session(gs, GG_DEBUG_MISC,
-		"// gg_session_handle_access_info: dummy[%02x, %02x], "
-		"last[message=%u, file_transfer=%u, conference_ch=%u]\n",
-		msg->dummy1, msg->dummy2, msg->last_message,
-		msg->last_file_transfer, msg->last_conference_ch);
-
-	gg110_access_info__free_unpacked(msg, NULL);
-
-	return 0;
-}
-
-/* ten pakiet jest odbierany tylko, jeżeli przy logowaniu użyliśmy identyfikatora typu 0x01 */
-static int gg_session_handle_uin_info(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	gg_tvbuff_t *tvb;
-	char *uin1 = NULL, *uin2 = NULL;
-
-	tvb = gg_tvbuff_new(ptr, len);
-
-	gg_tvbuff_expected_uint32(tvb, 1); /* unknown */
-	gg_tvbuff_expected_uint32(tvb, 2); /* unknown */
-
-	/* podstawowy identyfikator (numer GG) */
-	gg_tvbuff_expected_uint8(tvb, 0);
-	gg_tvbuff_read_str_dup(tvb, &uin1);
-
-	/* identyfikator użyty przy logowaniu (numer GG lub email) */
-	gg_tvbuff_expected_uint8(tvb, 1);
-	gg_tvbuff_read_str_dup(tvb, &uin2);
-
-	if (!gg_tvbuff_close(tvb)) {
-		free(uin1);
-		free(uin2);
-		return -1;
-	}
-
-	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_uin_info: "
-		"uin1=\"%s\", uin2=\"%s\"\n", uin1, uin2);
-
-	free(uin1);
-	free(uin2);
-
-	return 0;
-}
-
-static int gg_session_handle_transfer_info(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG112TransferInfo *msg = gg112_transfer_info__unpack(NULL, len, (uint8_t*)ptr);
-	int succ = 1;
-	size_t i;
-	uin_t peer = 0, sender = 0;
-
-	if (!GG_PROTOBUF_VALID(gs, "GG112TransferInfo", msg))
-		return -1;
-
-	/* see packets.proto */
-	if (msg->dummy1 != 5 && msg->dummy1 != 6) {
-		gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_WARNING,
-			"// gg_session_handle_transfer_info: "
-			"unknown dummy1 value: %d\n", msg->dummy1);
-	}
-
-	if (GG_PROTOBUF_VALID(gs, "GG112TransferInfoUin", msg->peer)) {
-		gg_protobuf_expected(gs, "GG112TransferInfoUin.dummy1",
-			msg->peer->dummy1, 1);
-		peer = gg_protobuf_get_uin(msg->peer->uin);
-	}
-	if (GG_PROTOBUF_VALID(gs, "GG112TransferInfoUin", msg->sender)) {
-		gg_protobuf_expected(gs, "GG112TransferInfoUin.dummy1",
-			msg->sender->dummy1, 1);
-		sender = gg_protobuf_get_uin(msg->sender->uin);
-	}
-
-	gg_debug_session(gs, GG_DEBUG_MISC,
-		"// gg_session_handle_transfer_info: dummy1=%#x, time=%u, "
-			"sender=%u, peer=%u, msg_id=%#016" PRIx64 ", "
-			"conv_id=%#016" PRIx64 "\n",
-		msg->dummy1, msg->time, sender, peer, msg->msg_id,
-		msg->conv_id);
-
-	for (i = 0; i < msg->n_data; i++) {
-		ProtobufKVP *kvp = msg->data[i];
-		if (!GG_PROTOBUF_VALID(gs, "ProtobufKVP", kvp))
-			continue;
-		gg_debug_session(gs, GG_DEBUG_MISC,
-			"// gg_session_handle_transfer_info[%s] = \"%s\"\n",
-			kvp->key, kvp->value);
-	}
-
-	if (msg->file && GG_PROTOBUF_VALID(gs, "GG112TransferInfoFile", msg->file)) {
-		GG112TransferInfoFile *file = msg->file;
-		gg_debug_session(gs, GG_DEBUG_MISC,
-			"// gg_session_handle_transfer_info file: "
-			"type=\"%s\", content_type=\"%s\", filename=\"%s\", "
-			"filesize=%u, msg_id=%#016" PRIx64 " url=\"%s\"\n",
-			file->type, file->content_type, file->filename,
-			file->filesize, file->msg_id, file->url);
-	}
-
-	succ = (gg_ack_110(gs, GG110_ACK__TYPE__TRANSFER_INFO,
-		msg->seq, ge) == 0);
-
-	gg112_transfer_info__free_unpacked(msg, NULL);
-
-	return succ ? 0 : -1;
-}
-
-static int gg_session_handle_magic_notification(struct gg_session *gs, uint32_t type,
-	const char *ptr, size_t len, struct gg_event *ge)
-{
-	GG110MagicNotification *msg = gg110_magic_notification__unpack(NULL, len, (uint8_t*)ptr);
-	int succ = 1;
-
-	if (!GG_PROTOBUF_VALID(gs, "GG110MagicNotification", msg))
-		return -1;
-
-	gg_debug_session(gs, GG_DEBUG_MISC,
-		"// gg_session_handle_magic_notification \n");
-
-	gg_protobuf_expected(gs, "GG110MagicNotification.dummy1", msg->dummy1, 2);
-	gg_protobuf_expected(gs, "GG110MagicNotification.dummy2", msg->dummy2, 1);
-	gg_protobuf_expected(gs, "GG110MagicNotification.dummy3", msg->dummy3, 1);
-
-	succ = (gg_ack_110(gs, GG110_ACK__TYPE__MAGIC_NOTIFICATION, msg->seq, ge) == 0);
-
-	gg110_magic_notification__free_unpacked(msg, NULL);
-
-	return succ ? 0 : -1;
-}
-
-/**
- * \internal Tablica obsługiwanych pakietów
- */
-static const gg_packet_handler_t handlers[] =
-{
-	/* style:maxlinelength:start-ignore */
-	{ GG_WELCOME, GG_STATE_READING_KEY, 0, gg_session_handle_welcome },
-	{ GG_LOGIN_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
-	{ GG_LOGIN80_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
-	{ GG_LOGIN110_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login110_ok },
-	{ GG_NEED_EMAIL, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
-	{ GG_LOGIN_FAILED, GG_STATE_READING_REPLY, 0, gg_session_handle_login_failed },
-	{ GG_LOGIN80_FAILED, GG_STATE_READING_REPLY, 0, gg_session_handle_login_failed },
-	{ GG_SEND_MSG_ACK, GG_STATE_CONNECTED, sizeof(struct gg_send_msg_ack), gg_session_handle_send_msg_ack },
-	{ GG_SEND_MSG_ACK110, GG_STATE_CONNECTED, 0, gg_session_handle_send_msg_ack_110 },
-	{ GG_PONG, GG_STATE_CONNECTED, 0, gg_session_handle_pong },
-	{ GG_DISCONNECTING, GG_STATE_CONNECTED, 0, gg_session_handle_disconnecting },
-	{ GG_DISCONNECT_ACK, GG_STATE_DISCONNECTING, 0, gg_session_handle_disconnect_ack },
-	{ GG_XML_EVENT, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event },
-	{ GG_EVENT110, GG_STATE_CONNECTED, 0, gg_session_handle_event_110 },
-	{ GG_PUBDIR50_REPLY, GG_STATE_CONNECTED, 0, gg_session_handle_pubdir50_reply },
-	{ GG_USERLIST_REPLY, GG_STATE_CONNECTED, sizeof(char), gg_session_handle_userlist_reply },
-	{ GG_DCC7_ID_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_id_reply), gg_session_handle_dcc7_id_reply },
-	{ GG_DCC7_ACCEPT, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_accept), gg_session_handle_dcc7_accept },
-	{ GG_DCC7_NEW, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_new), gg_session_handle_dcc7_new },
-	{ GG_DCC7_REJECT, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_reject), gg_session_handle_dcc7_reject },
-	{ GG_DCC7_INFO, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_info), gg_session_handle_dcc7_info },
-	{ GG_RECV_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg), gg_session_handle_recv_msg },
-	{ GG_RECV_MSG80, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 },
-	{ GG_RECV_MSG110, GG_STATE_CONNECTED, 0, gg_session_handle_recv_msg_110 },
-	{ GG_RECV_OWN_MSG110, GG_STATE_CONNECTED, 0, gg_session_handle_recv_msg_110 },
-	{ GG_STATUS, GG_STATE_CONNECTED, sizeof(struct gg_status), gg_session_handle_status },
-	{ GG_STATUS60, GG_STATE_CONNECTED, sizeof(struct gg_status60), gg_session_handle_status_60_77_80beta },
-	{ GG_STATUS77, GG_STATE_CONNECTED, sizeof(struct gg_status77), gg_session_handle_status_60_77_80beta },
-	{ GG_STATUS80BETA, GG_STATE_CONNECTED, sizeof(struct gg_status77), gg_session_handle_status_60_77_80beta },
-	{ GG_STATUS80, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply80), gg_session_handle_status_80 },
-	{ GG_NOTIFY_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply), gg_session_handle_notify_reply },
-	{ GG_NOTIFY_REPLY60, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply60), gg_session_handle_notify_reply_60 },
-	{ GG_NOTIFY_REPLY77, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply77), gg_session_handle_notify_reply_77_80beta },
-	{ GG_NOTIFY_REPLY80BETA, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply77), gg_session_handle_notify_reply_77_80beta },
-	{ GG_NOTIFY_REPLY80, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply80), gg_session_handle_notify_reply_80 },
-	{ GG_USER_DATA, GG_STATE_CONNECTED, sizeof(struct gg_user_data), gg_session_handle_user_data },
-	{ GG_TYPING_NOTIFICATION, GG_STATE_CONNECTED, sizeof(struct gg_typing_notification), gg_session_handle_typing_notification },
-	{ GG_MULTILOGON_INFO, GG_STATE_CONNECTED, sizeof(struct gg_multilogon_info), gg_session_handle_multilogon_info },
-	{ GG_XML_ACTION, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event },
-	{ GG_RECV_OWN_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 },
-	{ GG_USERLIST100_VERSION, GG_STATE_CONNECTED, sizeof(struct gg_userlist100_version), gg_session_handle_userlist_100_version },
-	{ GG_USERLIST100_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_userlist100_reply), gg_session_handle_userlist_100_reply },
-	{ GG_IMTOKEN, GG_STATE_CONNECTED, 0, gg_session_handle_imtoken },
-	{ GG_PONG110, GG_STATE_CONNECTED, 0, gg_session_handle_pong_110 },
-	{ GG_CHAT_INFO, GG_STATE_CONNECTED, 0, gg_session_handle_chat_info },
-	{ GG_CHAT_INFO_UPDATE, GG_STATE_CONNECTED, 0, gg_session_handle_chat_info_update },
-	{ GG_CHAT_CREATED, GG_STATE_CONNECTED, sizeof(struct gg_chat_created), gg_session_handle_chat_created },
-	{ GG_CHAT_INVITE_ACK, GG_STATE_CONNECTED, sizeof(struct gg_chat_invite_ack), gg_session_handle_chat_invite_ack },
-	{ GG_CHAT_RECV_MSG, GG_STATE_CONNECTED, 0, gg_session_handle_recv_msg_110 },
-	{ GG_CHAT_RECV_OWN_MSG, GG_STATE_CONNECTED, 0, gg_session_handle_recv_msg_110 },
-	{ GG_CHAT_LEFT, GG_STATE_CONNECTED, sizeof(struct gg_chat_left), gg_session_handle_chat_left },
-	{ GG_OPTIONS, GG_STATE_CONNECTED, 0, gg_session_handle_options },
-	{ GG_ACCESS_INFO, GG_STATE_CONNECTED, 0, gg_session_handle_access_info },
-	{ GG_UIN_INFO, GG_STATE_CONNECTED, 0, gg_session_handle_uin_info },
-	{ GG_TRANSFER_INFO, GG_STATE_CONNECTED, 0, gg_session_handle_transfer_info },
-	{ GG_MAGIC_NOTIFICATION, GG_STATE_CONNECTED, 0, gg_session_handle_magic_notification }
-	/* style:maxlinelength:end-ignore */
-};
-
-/**
- * \internal Obsługuje przychodzący pakiet danych.
- *
- * \param gs Struktura sesji
- * \param type Typ pakietu
- * \param ptr Wskaźnik do bufora pakietu
- * \param len Długość bufora pakietu
- * \param[out] ge Struktura zdarzenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-int gg_session_handle_packet(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
-{
-	unsigned int i;
-
-	gg_debug_session(gs, GG_DEBUG_FUNCTION,
-		"// gg_session_handle_packet(%d, %p, %" GG_SIZE_FMT ")\n",
-		type, ptr, len);
-
-	gs->last_event = time(NULL);
-
-#if 0
-	if ((gs->flags & (1 << GG_SESSION_FLAG_RAW_PACKET)) != 0) {
-		char *tmp;
-
-		tmp = malloc(len);
-
-		if (tmp == NULL) {
-			gg_debug_session(gs, GG_DEBUG_ERROR,
-				"// gg_session_handle_packet() out of memory "
-				"(%d bytes)\n", len);
-			return -1;
-		}
-
-		memcpy(tmp, ptr, len);
-
-		ge->type = GG_EVENT_RAW_PACKET;
-		ge->event.raw_packet.type = type;
-		ge->event.raw_packet.length = len;
-		ge->event.raw_packet.data = tmp;
-
-		return 0;
-	}
-#endif
-
-	for (i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) {
-		if (handlers[i].type != 0 && handlers[i].type != type)
-			continue;
-
-		if (handlers[i].state != 0 && handlers[i].state != (enum gg_state_t) gs->state) {
-			gg_debug_session(gs, GG_DEBUG_WARNING,
-				"// gg_session_handle_packet() packet 0x%02x "
-				"unexpected in state %d\n", type, gs->state);
-			continue;
-		}
-
-		if (len < handlers[i].min_length) {
-			gg_debug_session(gs, GG_DEBUG_ERROR,
-				"// gg_session_handle_packet() packet 0x%02x "
-				"too short (%" GG_SIZE_FMT " bytes)\n",
-				type, len);
-			continue;
-		}
-
-		return (*handlers[i].handler)(gs, type, ptr, len, ge);
-	}
-
-	gg_debug_session(gs, GG_DEBUG_WARNING, "// gg_session_handle_packet() "
-		"unhandled packet 0x%02x, len %" GG_SIZE_FMT ", state %d\n",
-		type, len, gs->state);
-
-	return 0;
-}

mercurial