diff libpurple/protocols/gg/lib/events.c @ 31860:93b08d43f684

matekm and kkszysiu collaborated on this patch to update our internal libgadu to version 1.10.1.
author John Bailey <rekkanoryo@rekkanoryo.org>
date Thu, 24 Mar 2011 20:53:13 +0000
parents a8cc50c2279f
children 3a90a59ddea2
line wrap: on
line diff
--- a/libpurple/protocols/gg/lib/events.c	Thu Mar 24 15:18:14 2011 +0000
+++ b/libpurple/protocols/gg/lib/events.c	Thu Mar 24 20:53:13 2011 +0000
@@ -1,4 +1,4 @@
-/* $Id: events.c 855 2009-10-12 21:42:51Z wojtekka $ */
+/* $Id: events.c 1062 2011-03-13 18:10:24Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -29,6 +29,7 @@
 
 #include "libgadu.h"
 #include "libgadu-internal.h"
+#include "libgadu-debug.h"
 
 #include <sys/types.h>
 
@@ -41,6 +42,8 @@
 
 #include "compat.h"
 #include "protocol.h"
+#include "encoding.h"
+#include "session.h"
 
 #include <errno.h>
 #include <stdio.h>
@@ -49,6 +52,10 @@
 #include <time.h>
 #include <unistd.h>
 #include <ctype.h>
+#ifdef GG_CONFIG_HAVE_GNUTLS
+#  include <gnutls/gnutls.h>
+#  include <gnutls/x509.h>
+#endif
 #ifdef GG_CONFIG_HAVE_OPENSSL
 #  include <openssl/err.h>
 #  include <openssl/x509.h>
@@ -73,9 +80,11 @@
 
 	switch (e->type) {
 		case GG_EVENT_MSG:
+		case GG_EVENT_MULTILOGON_MSG:
 			free(e->event.msg.message);
 			free(e->event.msg.formats);
 			free(e->event.msg.recipients);
+			free(e->event.msg.xhtml_message);
 			break;
 
 		case GG_EVENT_NOTIFY:
@@ -129,6 +138,36 @@
 		case GG_EVENT_XML_EVENT:
 			free(e->event.xml_event.data);
 			break;
+
+		case GG_EVENT_USER_DATA:
+		{
+			int i, j;
+
+			for (i = 0; i < e->event.user_data.user_count; i++) {
+				for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
+					free(e->event.user_data.users[i].attrs[j].key);
+					free(e->event.user_data.users[i].attrs[j].value);
+				}
+
+				free(e->event.user_data.users[i].attrs);
+			}
+
+			free(e->event.user_data.users);
+
+			break;
+		}
+
+		case GG_EVENT_MULTILOGON_INFO:
+		{
+			int i;
+
+			for (i = 0; i < e->event.multilogon_info.count; i++)
+				free(e->event.multilogon_info.sessions[i].name);
+
+			free(e->event.multilogon_info.sessions);
+
+			break;
+		}
 	}
 
 	free(e);
@@ -174,1236 +213,6 @@
 	return 0;
 }
 
-/**
- * \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
- */
-static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender)
-{
-	struct gg_msg_image_reply *i = (void*) p;
-	struct gg_image_queue *q, *qq;
-
-	if (!p || !sess || !e) {
-		errno = EFAULT;
-		return;
-	}
-
-	/* znajdź dany obrazek w kolejce danej sesji */
-
-	for (qq = sess->images, q = NULL; qq; qq = qq->next) {
-		if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) {
-			q = qq;
-			break;
-		}
-	}
-
-	if (!q) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
-		return;
-	}
-
-	if (p[0] == 0x05) {
-		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_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
-			return;
-		}
-
-		if (!(q->filename = strdup(p))) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
-			return;
-		}
-
-		len -= strlen(p) + 1;
-		p += strlen(p) + 1;
-	} else {
-		len -= sizeof(struct gg_msg_image_reply);
-		p += sizeof(struct gg_msg_image_reply);
-	}
-
-	if (q->done + len > q->size)
-		len = q->size - q->done;
-
-	memcpy(q->image + q->done, p, len);
-	q->done += len;
-
-	/* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
-
-	if (q->done >= q->size) {
-		e->type = GG_EVENT_IMAGE_REPLY;
-		e->event.image_reply.sender = sender;
-		e->event.image_reply.size = q->size;
-		e->event.image_reply.crc32 = q->crc32;
-		e->event.image_reply.filename = q->filename;
-		e->event.image_reply.image = q->image;
-
-		gg_image_queue_remove(sess, q, 0);
-
-		free(q);
-	}
-}
-
-/**
- * \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.
- *
- * \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, char *p, char *packet_end)
-{
-	while (p < packet_end) {
-		switch (*p) {
-			case 0x01:		/* konferencja */
-			{
-				struct gg_msg_recipients *m = (void*) p;
-				uint32_t i, count;
-
-				p += sizeof(*m);
-
-				if (p > packet_end) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
-					goto malformed;
-				}
-
-				count = gg_fix32(m->count);
-
-				if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
-					goto malformed;
-				}
-
-				if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
-					goto fail;
-				}
-
-				for (i = 0; i < count; i++, p += sizeof(uint32_t)) {
-					uint32_t u;
-					memcpy(&u, p, sizeof(uint32_t));
-					e->event.msg.recipients[i] = gg_fix32(u);
-				}
-
-				e->event.msg.recipients_count = count;
-
-				break;
-			}
-
-			case 0x02:		/* richtext */
-			{
-				uint16_t len;
-				char *buf;
-
-				if (p + 3 > packet_end) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
-					goto malformed;
-				}
-
-				memcpy(&len, p + 1, sizeof(uint16_t));
-				len = gg_fix16(len);
-
-				if (!(buf = malloc(len))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
-					goto fail;
-				}
-
-				p += 3;
-
-				if (p + len > packet_end) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
-					free(buf);
-					goto malformed;
-				}
-
-				memcpy(buf, p, len);
-
-				e->event.msg.formats = buf;
-				e->event.msg.formats_length = len;
-
-				p += len;
-
-				break;
-			}
-
-			case 0x04:		/* image_request */
-			{
-				struct gg_msg_image_request *i = (void*) p;
-
-				if (p + sizeof(*i) > packet_end) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\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 0x05:		/* image_reply */
-			case 0x06:
-			{
-				struct gg_msg_image_reply *rep = (void*) p;
-
-				if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
-
-					/* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
-
-					e->type = GG_EVENT_IMAGE_REPLY;
-					e->event.image_reply.sender = 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);
-
-				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 Analizuje przychodzący pakiet z wiadomością.
- *
- * Rozbija pakiet na poszczególne składniki -- tekst, informacje
- * o konferencjach, formatowani itd.
- *
- * \param h Wskaźnik do odebranego pakietu
- * \param e Struktura zdarzenia
- * \param sess Struktura sesji
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
-{
-	struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
-	char *p, *packet_end = (char*) r + h->length;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
-
-	if (!r->seq && !r->msgclass) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
-		e->type = GG_EVENT_NONE;
-		return 0;
-	}
-
-	/* znajdź \0 */
-	for (p = (char*) r + sizeof(*r); ; p++) {
-		if (p >= packet_end) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
-			goto malformed;
-		}
-
-		if (*p == 0x02 && p == packet_end - 1) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
-			break;
-		}
-
-		if (!*p)
-			break;
-	}
-
-	p++;
-
-	switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) {
-		case -1:	// handled
-			return 0;
-
-		case -2:	// failed
-			goto fail;
-
-		case -3:	// malformed
-			goto malformed;
-	}
-
-	e->type = GG_EVENT_MSG;
-	e->event.msg.msgclass = gg_fix32(r->msgclass);
-	e->event.msg.sender = gg_fix32(r->sender);
-	e->event.msg.time = gg_fix32(r->time);
-	e->event.msg.seq = gg_fix32(r->seq);
-	e->event.msg.message = (unsigned char*) strdup((char*) r + sizeof(*r));
-
-	return 0;
-
-malformed:
-	e->type = GG_EVENT_NONE;
-	free(e->event.msg.message);
-	free(e->event.msg.recipients);
-	free(e->event.msg.formats);
-
-	return 0;
-
-fail:
-	free(e->event.msg.message);
-	free(e->event.msg.recipients);
-	free(e->event.msg.formats);
-	return -1;
-}
-
-/**
- * \internal Zamienia tekst w formacie HTML na czysty tekst.
- *
- * \param dst Bufor wynikowy (może być \c NULL)
- * \param html Tekst źródłowy
- *
- * \note Dokleja \c \\0 na końcu bufora wynikowego.
- *
- * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
- */
-static int gg_convert_from_html(char *dst, const char *html)
-{
-	const char *src, *entity, *tag;
-	int len, in_tag, in_entity;
-
-	len = 0;
-	in_tag = 0;
-	tag = NULL;
-	in_entity = 0;
-	entity = NULL;
-
-	for (src = html; *src != 0; src++) {
-		if (*src == '<') {
-			tag = src;
-			in_tag = 1;
-			continue;
-		}
-
-		if (in_tag && (*src == '>')) {
-			if (strncmp(tag, "<br", 3) == 0) {
-				if (dst != NULL)
-					dst[len] = '\n';
-				len++;
-			}
-			in_tag = 0;
-			continue;
-		}
-
-		if (in_tag)
-			continue;
-
-		if (*src == '&') {
-			in_entity = 1;
-			entity = src;
-			continue;
-		}
-
-		if (in_entity && *src == ';') {
-			in_entity = 0;
-			if (dst != NULL) {
-				if (strncmp(entity, "&lt;", 4) == 0)
-					dst[len] = '<';
-				else if (strncmp(entity, "&gt;", 4) == 0)
-					dst[len] = '>';
-				else if (strncmp(entity, "&quot;", 6) == 0)
-					dst[len] = '"';
-				else if (strncmp(entity, "&apos;", 6) == 0)
-					dst[len] = '\'';
-				else if (strncmp(entity, "&amp;", 5) == 0)
-					dst[len] = '&';
-				else
-					dst[len] = '?';
-			}
-			len++;
-			continue;
-		}
-
-		if (in_entity && !(isalnum(*src) || *src == '#'))
-			in_entity = 0;
-
-		if (in_entity)
-			continue;
-
-		if (dst != NULL)
-			dst[len] = *src;
-
-		len++;
-	}
-
-	if (dst != NULL)
-		dst[len] = 0;
-
-	return len;
-}
-
-/**
- * \internal Analizuje przychodzący pakiet z wiadomością protokołu Gadu-Gadu 8.0.
- *
- * Rozbija pakiet na poszczególne składniki -- tekst, informacje
- * o konferencjach, formatowani itd.
- *
- * \param h Wskaźnik do odebranego pakietu
- * \param e Struktura zdarzenia
- * \param sess Struktura sesji
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
-{
-	char *packet = (char*) h + sizeof(struct gg_header);
-	struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet;
-	uint32_t offset_plain;
-	uint32_t offset_attr;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e);
-
-	if (!r->seq && !r->msgclass) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n");
-		goto malformed;
-	}
-
-	offset_plain = gg_fix32(r->offset_plain);
-	offset_attr  = gg_fix32(r->offset_attr);
-
-	if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n");
-		goto malformed;
-	}
-
-	if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n");
-		offset_attr = 0;	/* nie parsuj attr. */
-		/* goto ignore; */
-	}
-
-	/* Normalna sytuacja, więc nie podpada pod powyższy warunek. */
-	if (offset_attr == h->length)
-		offset_attr = 0;
-
-	if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n");
-		goto malformed;
-	}
-
-	if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n");
-		goto malformed;
-	}
-
-	e->type = GG_EVENT_MSG;
-	e->event.msg.msgclass = gg_fix32(r->msgclass);
-	e->event.msg.sender = gg_fix32(r->sender);
-	e->event.msg.time = gg_fix32(r->time);
-	e->event.msg.seq = gg_fix32(r->seq);
-
-	if (sess->encoding == GG_ENCODING_CP1250) {
-		e->event.msg.message = (unsigned char*) strdup(packet + offset_plain);
-	} else {
-		if (offset_plain > sizeof(struct gg_recv_msg80)) {
-			int len;
-
-			len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80));
-
-			e->event.msg.message = malloc(len + 1);
-
-			if (e->event.msg.message == NULL)
-				goto fail;
-
-			gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80));
-		} else {
-			e->event.msg.message = (unsigned char*) gg_cp_to_utf8(packet + offset_plain);
-		}
-	}
-
-	if (offset_plain > sizeof(struct gg_recv_msg80)) {
-		if (sess->encoding == GG_ENCODING_UTF8)
-			e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80));
-		else
-			e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80));
-	} else {
-		e->event.msg.xhtml_message = NULL;
-	}
-
-	if (offset_attr != 0) {
-		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) {
-			case -1:	// handled
-				return 0;
-
-			case -2:	// failed
-				goto fail;
-
-			case -3:	// malformed
-				goto malformed;
-		}
-	}
-
-	return 0;
-
-fail:
-	free(e->event.msg.message);
-	free(e->event.msg.xhtml_message);
-	free(e->event.msg.recipients);
-	free(e->event.msg.formats);
-	return -1;
-
-malformed:
-	e->type = GG_EVENT_NONE;
-	free(e->event.msg.message);
-	free(e->event.msg.xhtml_message);
-	free(e->event.msg.recipients);
-	free(e->event.msg.formats);
-	return 0;
-}
-
-/**
- * \internal Odbiera pakiet od serwera.
- *
- * Analizuje pakiet i wypełnia strukturę zdarzenia.
- *
- * \param sess Struktura sesji
- * \param e Struktura zdarzenia
- *
- * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
- */
-static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
-{
-	struct gg_header *h = NULL;
-	char *p;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (!(h = gg_recv_packet(sess))) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
-		goto fail;
-	}
-
-	p = (char*) h + sizeof(struct gg_header);
-
-	switch (h->type) {
-		case GG_RECV_MSG:
-		{
-			if (h->length >= sizeof(struct gg_recv_msg))
-				if (gg_handle_recv_msg(h, e, sess))
-					goto fail;
-
-			break;
-		}
-
-		case GG_RECV_MSG80:
-		{
-			if (h->length >= sizeof(struct gg_recv_msg80))
-				if (gg_handle_recv_msg80(h, e, sess))
-					goto fail;
-
-			break;
-		}
-
-
-		case GG_NOTIFY_REPLY:
-		{
-			struct gg_notify_reply *n = (void*) p;
-			unsigned int count, i;
-			char *tmp;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-			if (h->length < sizeof(*n)) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
-				errno = EINVAL;
-				goto fail;
-			}
-
-			if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) {
-				e->type = GG_EVENT_NOTIFY_DESCR;
-
-				if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-					goto fail;
-				}
-				e->event.notify_descr.notify[1].uin = 0;
-				memcpy(e->event.notify_descr.notify, p, sizeof(*n));
-				e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin);
-				e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status);
-				e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port);
-				e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version);
-
-				count = h->length - sizeof(*n);
-				if (!(tmp = malloc(count + 1))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-					goto fail;
-				}
-				memcpy(tmp, p + sizeof(*n), count);
-				tmp[count] = 0;
-				e->event.notify_descr.descr = tmp;
-
-			} else {
-				e->type = GG_EVENT_NOTIFY;
-
-				if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-					goto fail;
-				}
-
-				memcpy(e->event.notify, p, h->length);
-				count = h->length / sizeof(*n);
-				e->event.notify[count].uin = 0;
-
-				for (i = 0; i < count; i++) {
-					e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin);
-					e->event.notify[i].status = gg_fix32(e->event.notify[i].status);
-					e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port);
-					e->event.notify[i].version = gg_fix32(e->event.notify[i].version);
-				}
-			}
-
-			break;
-		}
-
-		case GG_STATUS:
-		{
-			struct gg_status *s = (void*) p;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
-
-			if (h->length >= sizeof(*s)) {
-				e->type = GG_EVENT_STATUS;
-				memcpy(&e->event.status, p, sizeof(*s));
-				e->event.status.uin = gg_fix32(e->event.status.uin);
-				e->event.status.status = gg_fix32(e->event.status.status);
-				if (h->length > sizeof(*s)) {
-					int len = h->length - sizeof(*s);
-					char *buf = malloc(len + 1);
-					if (buf) {
-						memcpy(buf, p + sizeof(*s), len);
-						buf[len] = 0;
-					}
-					e->event.status.descr = buf;
-				} else
-					e->event.status.descr = NULL;
-			}
-
-			break;
-		}
-
-		case GG_NOTIFY_REPLY77:
-		case GG_NOTIFY_REPLY80BETA:
-		{
-			struct gg_notify_reply77 *n = (void*) p;
-			unsigned int length = h->length, i = 0;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-			e->type = GG_EVENT_NOTIFY60;
-			e->event.notify60 = malloc(sizeof(*e->event.notify60));
-
-			if (!e->event.notify60) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-				goto fail;
-			}
-
-			e->event.notify60[0].uin = 0;
-
-			while (length >= sizeof(struct gg_notify_reply77)) {
-				uin_t uin = gg_fix32(n->uin);
-				char *tmp;
-
-				e->event.notify60[i].uin = uin & 0x00ffffff;
-				e->event.notify60[i].status = n->status;
-				e->event.notify60[i].remote_ip = n->remote_ip;
-				e->event.notify60[i].remote_port = gg_fix16(n->remote_port);
-				e->event.notify60[i].version = n->version;
-				e->event.notify60[i].image_size = n->image_size;
-				e->event.notify60[i].descr = NULL;
-				e->event.notify60[i].time = 0;
-
-				if (uin & 0x40000000)
-					e->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
-				if (uin & 0x20000000)
-					e->event.notify60[i].version |= GG_HAS_AUDIO7_MASK;
-				if (uin & 0x08000000)
-					e->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
-
-				if (GG_S_D(n->status)) {
-					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77));
-
-					if (sizeof(struct gg_notify_reply77) + descr_len <= length) {
-						char *descr;
-
-						if (!(descr = malloc(descr_len + 1))) {
-							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-							goto fail;
-						}
-
-						memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len);
-						descr[descr_len] = 0;
-
-						if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) {
-							char *cp_descr = gg_utf8_to_cp(descr);
-
-							if (!cp_descr) {
-								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-								free(descr);
-								goto fail;
-							}
-
-							free(descr);
-							descr = cp_descr;
-						}
-
-						e->event.notify60[i].descr = descr;
-
-						/* XXX czas */
-
-						length -= sizeof(struct gg_notify_reply77) + descr_len + 1;
-						n = (void*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1);
-					} else {
-						length = 0;
-					}
-
-				} else {
-					length -= sizeof(struct gg_notify_reply77);
-					n = (void*) ((char*) n + sizeof(struct gg_notify_reply77));
-				}
-
-				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-					free(e->event.notify60);
-					goto fail;
-				}
-
-				e->event.notify60 = (void*) tmp;
-				e->event.notify60[++i].uin = 0;
-			}
-
-			break;
-		}
-
-		case GG_STATUS77:
-		case GG_STATUS80BETA:
-		{
-			struct gg_status77 *s = (void*) p;
-			uint32_t uin;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
-
-			if (h->length < sizeof(*s))
-				break;
-
-			uin = gg_fix32(s->uin);
-
-			e->type = GG_EVENT_STATUS60;
-			e->event.status60.uin = uin & 0x00ffffff;
-			e->event.status60.status = s->status;
-			e->event.status60.remote_ip = s->remote_ip;
-			e->event.status60.remote_port = gg_fix16(s->remote_port);
-			e->event.status60.version = s->version;
-			e->event.status60.image_size = s->image_size;
-			e->event.status60.descr = NULL;
-			e->event.status60.time = 0;
-
-			if (uin & 0x40000000)
-				e->event.status60.version |= GG_HAS_AUDIO_MASK;
-			if (uin & 0x20000000)
-				e->event.status60.version |= GG_HAS_AUDIO7_MASK;
-			if (uin & 0x08000000)
-				e->event.status60.version |= GG_ERA_OMNIX_MASK;
-
-			if (h->length > sizeof(*s)) {
-				int len = h->length - sizeof(*s);
-				char *buf = malloc(len + 1);
-
-				/* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ?
-				 * 	- goto fail; (?)
-				 */
-				if (buf) {
-					memcpy(buf, (char*) p + sizeof(*s), len);
-					buf[len] = 0;
-
-					if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) {
-						char *cp_buf = gg_utf8_to_cp(buf);
-						free(buf);
-						buf = cp_buf;
-					}
-				}
-
-				e->event.status60.descr = buf;
-
-				if (len > 4 && p[h->length - 5] == 0) {
-					uint32_t t;
-					memcpy(&t, p + h->length - 4, sizeof(uint32_t));
-					e->event.status60.time = gg_fix32(t);
-				}
-			}
-
-			break;
-		}
-
-		case GG_NOTIFY_REPLY60:
-		{
-			struct gg_notify_reply60 *n = (void*) p;
-			unsigned int length = h->length, i = 0;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-			e->type = GG_EVENT_NOTIFY60;
-			e->event.notify60 = malloc(sizeof(*e->event.notify60));
-
-			if (!e->event.notify60) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-				goto fail;
-			}
-
-			e->event.notify60[0].uin = 0;
-
-			while (length >= sizeof(struct gg_notify_reply60)) {
-				uin_t uin = gg_fix32(n->uin);
-				char *tmp;
-
-				e->event.notify60[i].uin = uin & 0x00ffffff;
-				e->event.notify60[i].status = n->status;
-				e->event.notify60[i].remote_ip = n->remote_ip;
-				e->event.notify60[i].remote_port = gg_fix16(n->remote_port);
-				e->event.notify60[i].version = n->version;
-				e->event.notify60[i].image_size = n->image_size;
-				e->event.notify60[i].descr = NULL;
-				e->event.notify60[i].time = 0;
-
-				if (uin & 0x40000000)
-					e->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
-				if (uin & 0x08000000)
-					e->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
-
-				if (GG_S_D(n->status)) {
-					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
-
-					if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
-						if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) {
-							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-							goto fail;
-						}
-
-						memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len);
-						e->event.notify60[i].descr[descr_len] = 0;
-
-						/* XXX czas */
-
-						length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
-						n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
-					} else {
-						length = 0;
-					}
-
-				} else {
-					length -= sizeof(struct gg_notify_reply60);
-					n = (void*) ((char*) n + sizeof(struct gg_notify_reply60));
-				}
-
-				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-					free(e->event.notify60);
-					goto fail;
-				}
-
-				e->event.notify60 = (void*) tmp;
-				e->event.notify60[++i].uin = 0;
-			}
-
-			break;
-		}
-
-		case GG_STATUS60:
-		{
-			struct gg_status60 *s = (void*) p;
-			uint32_t uin;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
-
-			if (h->length < sizeof(*s))
-				break;
-
-			uin = gg_fix32(s->uin);
-
-			e->type = GG_EVENT_STATUS60;
-			e->event.status60.uin = uin & 0x00ffffff;
-			e->event.status60.status = s->status;
-			e->event.status60.remote_ip = s->remote_ip;
-			e->event.status60.remote_port = gg_fix16(s->remote_port);
-			e->event.status60.version = s->version;
-			e->event.status60.image_size = s->image_size;
-			e->event.status60.descr = NULL;
-			e->event.status60.time = 0;
-
-			if (uin & 0x40000000)
-				e->event.status60.version |= GG_HAS_AUDIO_MASK;
-			if (uin & 0x08000000)
-				e->event.status60.version |= GG_ERA_OMNIX_MASK;
-
-			if (h->length > sizeof(*s)) {
-				int len = h->length - sizeof(*s);
-				char *buf = malloc(len + 1);
-
-				if (buf) {
-					memcpy(buf, (char*) p + sizeof(*s), len);
-					buf[len] = 0;
-				}
-
-				e->event.status60.descr = buf;
-
-				if (len > 4 && p[h->length - 5] == 0) {
-					uint32_t t;
-					memcpy(&t, p + h->length - 4, sizeof(uint32_t));
-					e->event.status60.time = gg_fix32(t);
-				}
-			}
-
-			break;
-		}
-
-		case GG_STATUS80:
-		{
-			struct gg_notify_reply80 *s = (void*) p;
-			uint32_t descr_len;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
-
-			if (h->length < sizeof(*s))
-				break;
-
-			e->type = GG_EVENT_STATUS60;
-			e->event.status60.uin		= gg_fix32(s->uin);
-			e->event.status60.status	= gg_fix32(s->status);
-			e->event.status60.remote_ip	= s->remote_ip;
-			e->event.status60.remote_port	= gg_fix16(s->remote_port);
-			e->event.status60.image_size	= s->image_size;
-			e->event.status60.descr		= NULL;
-			e->event.status60.version	= 0x00;	/* not-supported */
-			e->event.status60.time		= 0;	/* not-supported */
-
-			descr_len = gg_fix32(s->descr_len);
-
-			if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) {
-				char *buf = malloc(descr_len + 1);
-
-				if (buf) {
-					memcpy(buf, (char*) p + sizeof(*s), descr_len);
-					buf[descr_len] = 0;
-
-					if (sess->encoding != GG_ENCODING_UTF8) {
-						char *cp_buf = gg_utf8_to_cp(buf);
-						free(buf);
-						buf = cp_buf;
-					}
-				}
-
-				e->event.status60.descr = buf;
-			}
-			break;
-		}
-
-		case GG_NOTIFY_REPLY80:
-		{
-			struct gg_notify_reply80 *n = (void*) p;
-			unsigned int length = h->length, i = 0;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
-			e->type = GG_EVENT_NOTIFY60;
-			e->event.notify60 = malloc(sizeof(*e->event.notify60));
-
-			if (!e->event.notify60) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-				goto fail;
-			}
-
-			e->event.notify60[0].uin = 0;
-
-			while (length >= sizeof(struct gg_notify_reply80)) {
-				uint32_t descr_len;
-				char *tmp;
-
-				e->event.notify60[i].uin	= gg_fix32(n->uin);
-				e->event.notify60[i].status	= gg_fix32(n->status);
-				e->event.notify60[i].remote_ip	= n->remote_ip;
-				e->event.notify60[i].remote_port= gg_fix16(n->remote_port);
-				e->event.notify60[i].image_size	= n->image_size;
-				e->event.notify60[i].descr	= NULL;
-				e->event.notify60[i].version	= 0x00;	/* not-supported */
-				e->event.notify60[i].time	= 0;	/* not-supported */
-
-				descr_len = gg_fix32(n->descr_len);
-
-				length -= sizeof(struct gg_notify_reply80);
-				n = (void*) ((char*) n + sizeof(struct gg_notify_reply80));
-
-				if (descr_len) {
-					if (length >= descr_len) {
-						/* XXX, GG_S_D(n->status) */
-						char *descr;
-
-						if (!(descr = malloc(descr_len + 1))) {
-							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-							goto fail;
-						}
-
-						memcpy(descr, n, descr_len);
-						descr[descr_len] = 0;
-
-						if (sess->encoding != GG_ENCODING_UTF8) {
-							char *cp_descr = gg_utf8_to_cp(descr);
-
-							if (!cp_descr) {
-								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-								free(descr);
-								goto fail;
-							}
-
-							free(descr);
-							descr = cp_descr;
-						}
-						e->event.notify60[i].descr = descr;
-
-						length -= descr_len;
-						n = (void*) ((char*) n + descr_len);
-					} else
-						length = 0;
-				}
-
-				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
-					free(e->event.notify60);
-					goto fail;
-				}
-
-				e->event.notify60 = (void*) tmp;
-				e->event.notify60[++i].uin = 0;
-			}
-			break;
-		}
-
-		case GG_SEND_MSG_ACK:
-		{
-			struct gg_send_msg_ack *s = (void*) p;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
-
-			if (h->length < sizeof(*s))
-				break;
-
-			e->type = GG_EVENT_ACK;
-			e->event.ack.status = gg_fix32(s->status);
-			e->event.ack.recipient = gg_fix32(s->recipient);
-			e->event.ack.seq = gg_fix32(s->seq);
-
-			break;
-		}
-
-		case GG_PONG:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
-
-			e->type = GG_EVENT_PONG;
-			sess->last_pong = time(NULL);
-
-			break;
-		}
-
-		case GG_DISCONNECTING:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
-			e->type = GG_EVENT_DISCONNECT;
-			break;
-		}
-
-		case GG_DISCONNECT_ACK:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n");
-			e->type = GG_EVENT_DISCONNECT_ACK;
-			break;
-		}
-
-		case GG_XML_EVENT:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n");
-			e->type = GG_EVENT_XML_EVENT;
-			if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n");
-				goto fail;
-			}
-			memcpy(e->event.xml_event.data, p, h->length);
-			e->event.xml_event.data[h->length] = 0;
-			break;
-		}
-
-		case GG_PUBDIR50_REPLY:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
-			if (gg_pubdir50_handle_reply_sess(sess, e, p, h->length) == -1)
-				goto fail;
-			break;
-		}
-
-		case GG_USERLIST_REPLY:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
-
-			if (h->length < 1)
-				break;
-
-			/* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
-			 * gdy otrzymano wszystkie odpowiedzi */
-			if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) {
-				if (--sess->userlist_blocks)
-					break;
-
-				p[0] = GG_USERLIST_PUT_REPLY;
-			}
-
-			if (h->length > 1) {
-				char *tmp;
-				unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0;
-
-				gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
-
-				if (!(tmp = realloc(sess->userlist_reply, len + h->length))) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
-					free(sess->userlist_reply);
-					sess->userlist_reply = NULL;
-					goto fail;
-				}
-
-				sess->userlist_reply = tmp;
-				sess->userlist_reply[len + h->length - 1] = 0;
-				memcpy(sess->userlist_reply + len, p + 1, h->length - 1);
-			}
-
-			if (p[0] == GG_USERLIST_GET_MORE_REPLY)
-				break;
-
-			e->type = GG_EVENT_USERLIST;
-			e->event.userlist.type = p[0];
-			e->event.userlist.reply = sess->userlist_reply;
-			sess->userlist_reply = NULL;
-
-			break;
-		}
-
-		case GG_DCC7_ID_REPLY:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
-
-			if (h->length < sizeof(struct gg_dcc7_id_reply))
-				break;
-
-			if (gg_dcc7_handle_id(sess, e, p, h->length) == -1)
-				goto fail;
-
-			break;
-		}
-
-		case GG_DCC7_ACCEPT:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
-
-			if (h->length < sizeof(struct gg_dcc7_accept))
-				break;
-
-			if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1)
-				goto fail;
-
-			break;
-		}
-
-		case GG_DCC7_NEW:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
-
-			if (h->length < sizeof(struct gg_dcc7_new))
-				break;
-
-			if (gg_dcc7_handle_new(sess, e, p, h->length) == -1)
-				goto fail;
-
-			break;
-		}
-
-		case GG_DCC7_REJECT:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
-
-			if (h->length < sizeof(struct gg_dcc7_reject))
-				break;
-
-			if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1)
-				goto fail;
-
-			break;
-		}
-
-		case GG_DCC7_INFO:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
-
-			if (h->length < sizeof(struct gg_dcc7_info))
-				break;
-
-			if (gg_dcc7_handle_info(sess, e, p, h->length) == -1)
-				goto fail;
-
-			break;
-		}
-
-		default:
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
-	}
-
-	free(h);
-	return 0;
-
-fail:
-	free(h);
-	return -1;
-}
-
 /** \endcond */
 
 /**
@@ -1465,6 +274,8 @@
 			memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
 			sess->send_left -= res;
 		}
+
+		res = 0;
 	}
 
 	switch (sess->state) {
@@ -1568,15 +379,14 @@
 
 			auth = gg_proxy_auth();
 
-#ifdef GG_CONFIG_HAVE_OPENSSL
-			if (sess->ssl) {
+#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
+			if (sess->ssl != NULL) {
 				snprintf(buf, sizeof(buf) - 1,
-					"GET %s/appsvc/appmsg3.asp?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
+					"GET %s/appsvc/appmsg_ver10.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
+					"Connection: close\r\n"
 					"Host: " GG_APPMSG_HOST "\r\n"
-					"User-Agent: " GG_HTTP_USERAGENT "\r\n"
-					"Pragma: no-cache\r\n"
 					"%s"
-					"\r\n", host, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
+					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
 			} else
 #endif
 			{
@@ -1590,12 +400,6 @@
 			free(auth);
 			free(client);
 
-			/* zwolnij pamięć po wersji klienta. */
-			if (sess->client_version) {
-				free(sess->client_version);
-				sess->client_version = NULL;
-			}
-
 			gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
 
 			/* zapytanie jest krótkie, więc zawsze zmieści się
@@ -1681,15 +485,10 @@
 			/* analizujemy otrzymane dane. */
 			tmp = buf;
 
-#ifdef GG_CONFIG_HAVE_OPENSSL
-			if (!sess->ssl)
-#endif
-			{
-				while (*tmp && *tmp != ' ')
-					tmp++;
-				while (*tmp && *tmp == ' ')
-					tmp++;
-			}
+			while (*tmp && *tmp != ' ')
+				tmp++;
+			while (*tmp && *tmp == ' ')
+				tmp++;
 			while (*tmp && *tmp != ' ')
 				tmp++;
 			while (*tmp && *tmp == ' ')
@@ -1730,6 +529,67 @@
 
 			sess->port = port;
 
+			/* Jeśli podano nazwę, nie adres serwera... */
+			if (sess->server_addr == INADDR_NONE) {
+				if (sess->resolver_start(&sess->fd, &sess->resolver, host) == -1) {
+					gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
+					goto fail_resolving;
+				}
+
+				sess->state = GG_STATE_RESOLVING_GG;
+				sess->check = GG_CHECK_READ;
+				sess->timeout = GG_DEFAULT_TIMEOUT;
+				break;
+			}
+
+			/* łączymy się z właściwym serwerem. */
+			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+
+				sess->port = GG_HTTPS_PORT;
+
+				/* nie wyszło? próbujemy portu 443. */
+				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
+					/* ostatnia deska ratunku zawiodła?
+					 * w takim razie zwijamy manatki. */
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					goto fail_connecting;
+				}
+			}
+
+			sess->state = GG_STATE_CONNECTING_GG;
+			sess->check = GG_CHECK_WRITE;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+			sess->soft_timeout = 1;
+
+			break;
+		}
+
+		case GG_STATE_RESOLVING_GG:
+		{
+			struct in_addr addr;
+			int failed = 0;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING_GG\n");
+
+			if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+				failed = 1;
+				errno2 = errno;
+			}
+
+			close(sess->fd);
+			sess->fd = -1;
+
+			sess->resolver_cleanup(&sess->resolver, 0);
+
+			if (failed) {
+				errno = errno2;
+				goto fail_resolving;
+			}
+
+			sess->server_addr = addr.s_addr;
+
 			/* łączymy się z właściwym serwerem. */
 			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
 				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
@@ -1779,7 +639,7 @@
 					errno = ETIMEDOUT;
 #endif
 
-#ifdef GG_CONFIG_HAVE_OPENSSL
+#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
 				/* jeśli logujemy się po TLS, nie próbujemy
 				 * się łączyć już z niczym innym w przypadku
 				 * błędu. nie dość, że nie ma sensu, to i
@@ -1858,9 +718,14 @@
 				}
 			}
 
+#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
+			if (sess->ssl != NULL) {
+#ifdef GG_CONFIG_HAVE_GNUTLS
+				gnutls_transport_set_ptr(GG_SESSION_GNUTLS(sess), (gnutls_transport_ptr_t) sess->fd);
+#endif
 #ifdef GG_CONFIG_HAVE_OPENSSL
-			if (sess->ssl) {
 				SSL_set_fd(sess->ssl, sess->fd);
+#endif
 
 				sess->state = GG_STATE_TLS_NEGOTIATION;
 				sess->check = GG_CHECK_WRITE;
@@ -1877,6 +742,83 @@
 			break;
 		}
 
+#ifdef GG_CONFIG_HAVE_GNUTLS
+		case GG_STATE_TLS_NEGOTIATION:
+		{
+			int res;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+
+gnutls_handshake_repeat:
+			res = gnutls_handshake(GG_SESSION_GNUTLS(sess));
+
+			if (res == GNUTLS_E_AGAIN) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_AGAIN\n");
+
+				sess->state = GG_STATE_TLS_NEGOTIATION;
+				if (gnutls_record_get_direction(GG_SESSION_GNUTLS(sess)) == 0)
+					sess->check = GG_CHECK_READ;
+				else
+					sess->check = GG_CHECK_WRITE;
+				sess->timeout = GG_DEFAULT_TIMEOUT;
+				break;
+			}
+
+			if (res == GNUTLS_E_INTERRUPTED) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_INTERRUPTED\n");
+				goto gnutls_handshake_repeat;
+			}
+
+			if (res != 0) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake error %d\n", res);
+				e->type = GG_EVENT_CONN_FAILED;
+				e->event.failure = GG_FAILURE_TLS;
+				sess->state = GG_STATE_IDLE;
+				close(sess->fd);
+				sess->fd = -1;
+				break;
+			}
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "//   cipher: VERS-%s:%s:%s:%s:COMP-%s\n",
+				gnutls_protocol_get_name(gnutls_protocol_get_version(GG_SESSION_GNUTLS(sess))),
+				gnutls_cipher_get_name(gnutls_cipher_get(GG_SESSION_GNUTLS(sess))),
+				gnutls_kx_get_name(gnutls_kx_get(GG_SESSION_GNUTLS(sess))),
+				gnutls_mac_get_name(gnutls_mac_get(GG_SESSION_GNUTLS(sess))),
+				gnutls_compression_get_name(gnutls_compression_get(GG_SESSION_GNUTLS(sess))));
+
+			if (gnutls_certificate_type_get(GG_SESSION_GNUTLS(sess)) == GNUTLS_CRT_X509) {
+				unsigned int peer_count;
+				const gnutls_datum_t *peers;
+				gnutls_x509_crt_t cert;
+
+				if (gnutls_x509_crt_init(&cert) >= 0) {
+					peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count);
+
+					if (peers != NULL) {
+						char buf[256];
+						size_t size;
+
+						if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) >= 0) {
+							size = sizeof(buf);
+							gnutls_x509_crt_get_dn(cert, buf, &size);
+							gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
+							size = sizeof(buf);
+							gnutls_x509_crt_get_issuer_dn(cert, buf, &size);
+							gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
+						}
+					}
+				}
+			}
+
+			sess->state = GG_STATE_READING_KEY;
+			sess->check = GG_CHECK_READ;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+
+			break;
+		}
+#endif
+
 #ifdef GG_CONFIG_HAVE_OPENSSL
 		case GG_STATE_TLS_NEGOTIATION:
 		{
@@ -1916,7 +858,7 @@
 
 					break;
 				} else {
-					char buf[1024];
+					char buf[256];
 
 					ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
 
@@ -1938,7 +880,7 @@
 			if (!peer)
 				gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
 			else {
-				char buf[1024];
+				char buf[256];
 
 				X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
 				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
@@ -1956,20 +898,17 @@
 #endif
 
 		case GG_STATE_READING_KEY:
+		case GG_STATE_READING_REPLY:
+		case GG_STATE_CONNECTED:
+		case GG_STATE_DISCONNECTING:
 		{
-			struct gg_header *h;
-			struct gg_welcome *w;
-			unsigned char *password = (unsigned char*) sess->password;
-			int ret;
-			uint8_t login_hash[64];
+			struct gg_header *gh;
 
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
-
-			memset(login_hash, 0, sizeof(login_hash));
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
 
 			/* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie
 			 * się tekstu wrzucanego przez proxy. */
-			if (sess->proxy_addr && sess->proxy_port) {
+			if (sess->state == GG_STATE_READING_KEY && sess->proxy_addr && sess->proxy_port) {
 				char buf[100];
 
 				strcpy(buf, "");
@@ -1991,249 +930,30 @@
 				break;
 			}
 
-			/* czytaj pierwszy pakiet. */
-			if (!(h = gg_recv_packet(sess))) {
-				if (errno == EAGAIN) {
-					sess->check = GG_CHECK_READ;
-					break;
-				}
-
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
-
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_READING;
-				sess->state = GG_STATE_IDLE;
-				errno2 = errno;
-				close(sess->fd);
-				errno = errno2;
-				sess->fd = -1;
-				break;
-			}
-
-			if (h->type != GG_WELCOME) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
-				free(h);
-				close(sess->fd);
-				sess->fd = -1;
-				errno = EINVAL;
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_INVALID;
-				sess->state = GG_STATE_IDLE;
-				break;
-			}
-
-			w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header));
-			w->key = gg_fix32(w->key);
-
-			switch (sess->hash_type) {
-				case GG_LOGIN_HASH_GG32:
-				{
-					unsigned int hash;
-
-					hash = gg_fix32(gg_login_hash(password, w->key));
-					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash);
-					memcpy(login_hash, &hash, sizeof(hash));
-
-					break;
-				}
-
-				case GG_LOGIN_HASH_SHA1:
-				{
-					char tmp[41];
-					int i;
-
-					gg_login_hash_sha1((char*) password, w->key, login_hash);
-					for (i = 0; i < 40; i += 2)
-						snprintf(tmp + i, sizeof(tmp) - i, "%02x", login_hash[i / 2]);
-
-					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp);
-
-					break;
-				}
-			}
-
-			free(h);
-			free(sess->password);
-			sess->password = NULL;
-
-			if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
-				struct sockaddr_in sin;
-				socklen_t sin_len = sizeof(sin);
-
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
-
-				if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
-					sess->client_addr = sin.sin_addr.s_addr;
-				} else {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
-					sess->client_addr = 0;
-				}
-			} else
-				sess->client_addr = gg_dcc_ip;
-
-			if (sess->protocol_version >= 0x2e) {
-				struct gg_login80 l;
-
-				uint32_t tmp_version_len	= gg_fix32(strlen(GG8_VERSION));
-				uint32_t tmp_descr_len		= gg_fix32((sess->initial_descr) ? strlen(sess->initial_descr) : 0);
-
-				memset(&l, 0, sizeof(l));
-				l.uin           = gg_fix32(sess->uin);
-				memcpy(l.language, GG8_LANG, sizeof(l.language));
-				l.hash_type     = sess->hash_type;
-				memcpy(l.hash, login_hash, sizeof(login_hash));
-				l.status        = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
-				l.flags		= gg_fix32(0x00800001);
-				l.features	= gg_fix32(sess->protocol_features);
-				l.image_size    = sess->image_size;
-				l.dunno2        = 0x64;
-
-				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n");
-				ret = gg_send_packet(sess, GG_LOGIN80,
-						&l, sizeof(l),
-						&tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION),
-						&tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
-						NULL);
-
-			} else if (sess->protocol_version == 0x2d) {
-				struct gg_login70 l;
-
-				memset(&l, 0, sizeof(l));
-				l.uin		= gg_fix32(sess->uin);
-				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr;
-				l.local_port	= gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
-				l.hash_type	= sess->hash_type;
-				memcpy(l.hash, login_hash, sizeof(login_hash));
-				l.image_size	= sess->image_size;
-				l.dunno2 	= 0x64;
-				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
-				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags);
-
-				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n");
-				ret = gg_send_packet(sess, GG_LOGIN80BETA,
-						&l, sizeof(l),
-						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
-						(sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0,
-						NULL);
-			} else {
-				struct gg_login70 l;
-
-				memset(&l, 0, sizeof(l));
-				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr;
-				l.uin		= gg_fix32(sess->uin);
-				l.local_port	= gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
-				l.hash_type	= sess->hash_type;
-				memcpy(l.hash, login_hash, sizeof(login_hash));
-				l.image_size	= sess->image_size;
-				l.dunno2	= 0xbe;
-				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
-				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags);
-
-				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n");
-				ret = gg_send_packet(sess, GG_LOGIN70,
-						&l, sizeof(l),
-						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
-						NULL);
-			}
-
-			free(sess->initial_descr);
-			sess->initial_descr = NULL;
-
-			if (ret == -1) {
-				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
-				errno2 = errno;
-				close(sess->fd);
-				errno = errno2;
-				sess->fd = -1;
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_WRITING;
-				sess->state = GG_STATE_IDLE;
-				break;
-			}
-
-			sess->state = GG_STATE_READING_REPLY;
-			sess->check = GG_CHECK_READ;
-
-			break;
-		}
-
-		case GG_STATE_READING_REPLY:
-		{
-			struct gg_header *h;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
-
-			if (!(h = gg_recv_packet(sess))) {
-				if (errno == EAGAIN) {
-					sess->check = GG_CHECK_READ;
-					break;
-				}
-
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
-				e->type = GG_EVENT_CONN_FAILED;
-				e->event.failure = GG_FAILURE_READING;
-				sess->state = GG_STATE_IDLE;
-				errno2 = errno;
-				close(sess->fd);
-				errno = errno2;
-				sess->fd = -1;
-				break;
-			}
-
-			if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
-				e->type = GG_EVENT_CONN_SUCCESS;
-				sess->state = GG_STATE_CONNECTED;
-				sess->check = GG_CHECK_READ;
-				sess->timeout = -1;
-				sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL;
-				free(h);
-				break;
-			}
-
-			if (h->type == GG_LOGIN_FAILED) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
-				e->event.failure = GG_FAILURE_PASSWORD;
-				errno = EACCES;
-			} else if (h->type == GG_DISCONNECTING) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
-				e->event.failure = GG_FAILURE_INTRUDER;
-				errno = EACCES;
-			} else {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
-				e->event.failure = GG_FAILURE_INVALID;
-				errno = EINVAL;
-			}
-
-			e->type = GG_EVENT_CONN_FAILED;
-			sess->state = GG_STATE_IDLE;
-			errno2 = errno;
-			close(sess->fd);
-			errno = errno2;
-			sess->fd = -1;
-			free(h);
-
-			break;
-		}
-
-		case GG_STATE_CONNECTED:
-		{
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
-
 			sess->last_event = time(NULL);
 
-			if ((res = gg_watch_fd_connected(sess, e)) == -1) {
+			gh = gg_recv_packet(sess);
 
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
-
+			if (gh == NULL) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
  				if (errno == EAGAIN) {
 					e->type = GG_EVENT_NONE;
 					res = 0;
-				} else
+				} else {
 					res = -1;
+				}
+
+				goto done;
 			}
 
+			if (gg_session_handle_packet(sess, gh->type, (const char *) gh + sizeof(struct gg_header), gh->length, e) == -1) {
+				free(gh);
+				res = -1;
+				goto done;
+			}
+
+			free(gh);
+
 			sess->check = GG_CHECK_READ;
 
 			break;