Bug 964828 - Update libpurple up to 2.10.11. rs=aleth
authorPatrick Cloke <clokep@gmail.com>
Fri, 20 Mar 2015 00:16:03 -0400
changeset 776 2b62405a4ed14ad05eb3d645cdad9b380e635d39
parent 775 9e65893ce648d58eab6d0472225a4d5e9f1c917b
child 777 21c486b08ddaadd49469324fca986cdb84b6aca5
push id52
push userclokep@gmail.com
push dateMon, 15 Jun 2015 19:48:37 +0000
reviewersaleth
bugs964828
Bug 964828 - Update libpurple up to 2.10.11. rs=aleth
config/version.txt
libpurple/account.c
libpurple/blist.c
libpurple/cipher.c
libpurple/circbuffer.c
libpurple/core.c
libpurple/dnsquery.c
libpurple/dnssrv.c
libpurple/ft.c
libpurple/internal.h
libpurple/network.c
libpurple/pluginpref.c
libpurple/protocols/bonjour/bonjour_ft.c
libpurple/protocols/bonjour/jabber.c
libpurple/protocols/gg/gg.c
libpurple/protocols/gg/gg.h
libpurple/protocols/gg/lib/common.c
libpurple/protocols/gg/lib/compat.h
libpurple/protocols/gg/lib/config.h
libpurple/protocols/gg/lib/dcc.c
libpurple/protocols/gg/lib/dcc7.c
libpurple/protocols/gg/lib/debug.c
libpurple/protocols/gg/lib/debug.h
libpurple/protocols/gg/lib/deflate.c
libpurple/protocols/gg/lib/deflate.h
libpurple/protocols/gg/lib/encoding.c
libpurple/protocols/gg/lib/encoding.h
libpurple/protocols/gg/lib/endian.c
libpurple/protocols/gg/lib/events.c
libpurple/protocols/gg/lib/fileio.h
libpurple/protocols/gg/lib/handlers.c
libpurple/protocols/gg/lib/http.c
libpurple/protocols/gg/lib/internal.h
libpurple/protocols/gg/lib/libgadu-config.h
libpurple/protocols/gg/lib/libgadu-debug.h
libpurple/protocols/gg/lib/libgadu-internal.h
libpurple/protocols/gg/lib/libgadu.c
libpurple/protocols/gg/lib/libgadu.h
libpurple/protocols/gg/lib/message.c
libpurple/protocols/gg/lib/message.h
libpurple/protocols/gg/lib/network.c
libpurple/protocols/gg/lib/network.h
libpurple/protocols/gg/lib/obsolete.c
libpurple/protocols/gg/lib/packets.pb-c.c
libpurple/protocols/gg/lib/packets.pb-c.h
libpurple/protocols/gg/lib/protobuf-c.c
libpurple/protocols/gg/lib/protobuf-c.h
libpurple/protocols/gg/lib/protobuf.c
libpurple/protocols/gg/lib/protobuf.h
libpurple/protocols/gg/lib/protocol.h
libpurple/protocols/gg/lib/pubdir.c
libpurple/protocols/gg/lib/pubdir50.c
libpurple/protocols/gg/lib/resolver.c
libpurple/protocols/gg/lib/resolver.h
libpurple/protocols/gg/lib/session.h
libpurple/protocols/gg/lib/sha1.c
libpurple/protocols/gg/lib/strman.h
libpurple/protocols/gg/lib/tvbuff.c
libpurple/protocols/gg/lib/tvbuff.h
libpurple/protocols/gg/lib/tvbuilder.c
libpurple/protocols/gg/lib/tvbuilder.h
libpurple/protocols/gg/moz.build
libpurple/protocols/jabber/auth_scram.c
libpurple/protocols/jabber/bosh.c
libpurple/protocols/jabber/chat.c
libpurple/protocols/jabber/facebook_roster.c
libpurple/protocols/jabber/facebook_roster.h
libpurple/protocols/jabber/google/google_roster.c
libpurple/protocols/jabber/iq.c
libpurple/protocols/jabber/jabber.c
libpurple/protocols/jabber/jabber.h
libpurple/protocols/jabber/jutil.c
libpurple/protocols/jabber/moz.build
libpurple/protocols/jabber/oob.c
libpurple/protocols/jabber/presence.c
libpurple/protocols/jabber/roster.c
libpurple/protocols/jabber/roster.h
libpurple/protocols/jabber/si.c
libpurple/protocols/msn/cmdproc.c
libpurple/protocols/msn/cmdproc.h
libpurple/protocols/msn/command.c
libpurple/protocols/msn/command.h
libpurple/protocols/msn/contact.c
libpurple/protocols/msn/contact.h
libpurple/protocols/msn/directconn.c
libpurple/protocols/msn/history.c
libpurple/protocols/msn/httpconn.c
libpurple/protocols/msn/msg.c
libpurple/protocols/msn/msg.h
libpurple/protocols/msn/msn.c
libpurple/protocols/msn/msnutils.c
libpurple/protocols/msn/nexus.c
libpurple/protocols/msn/nexus.h
libpurple/protocols/msn/notification.c
libpurple/protocols/msn/p2p.c
libpurple/protocols/msn/p2p.h
libpurple/protocols/msn/servconn.c
libpurple/protocols/msn/session.c
libpurple/protocols/msn/slp.c
libpurple/protocols/msn/slpcall.c
libpurple/protocols/msn/slpcall.h
libpurple/protocols/msn/slplink.c
libpurple/protocols/msn/slplink.h
libpurple/protocols/msn/slpmsg.c
libpurple/protocols/msn/slpmsg.h
libpurple/protocols/msn/slpmsg_part.c
libpurple/protocols/msn/slpmsg_part.h
libpurple/protocols/msn/switchboard.c
libpurple/protocols/msn/switchboard.h
libpurple/protocols/msn/tlv.c
libpurple/protocols/msn/tlv.h
libpurple/protocols/msn/transaction.c
libpurple/protocols/msn/transaction.h
libpurple/protocols/myspace/myspace.c
libpurple/protocols/myspace/user.c
libpurple/protocols/myspace/zap.c
libpurple/protocols/novell/nmevent.c
libpurple/protocols/novell/nmrtf.c
libpurple/protocols/oscar/bstream.c
libpurple/protocols/oscar/family_auth.c
libpurple/protocols/oscar/family_bart.c
libpurple/protocols/oscar/family_chatnav.c
libpurple/protocols/oscar/family_feedbag.c
libpurple/protocols/oscar/family_icbm.c
libpurple/protocols/oscar/family_locate.c
libpurple/protocols/oscar/family_oservice.c
libpurple/protocols/oscar/oscar.c
libpurple/protocols/oscar/oscar.h
libpurple/protocols/oscar/peer.c
libpurple/protocols/oscar/tlv.c
libpurple/protocols/oscar/userinfo.c
libpurple/protocols/oscar/util.c
libpurple/protocols/sametime/sametime.c
libpurple/protocols/simple/simple.c
libpurple/protocols/yahoo/libymsg.c
libpurple/protocols/yahoo/libymsg.h
libpurple/protocols/yahoo/util.c
libpurple/protocols/yahoo/yahoo_filexfer.c
libpurple/protocols/yahoo/yahoo_packet.c
libpurple/protocols/yahoo/yahoo_packet.h
libpurple/protocols/yahoo/yahoo_picture.c
libpurple/protocols/yahoo/yahoo_profile.c
libpurple/protocols/yahoo/yahoochat.c
libpurple/protocols/yahoo/ycht.c
libpurple/proxy.c
libpurple/prpl.c
libpurple/prpl.h
libpurple/ssl-nss.c
libpurple/stun.c
libpurple/upnp.c
libpurple/util.c
libpurple/win32/wpurpleerror.h
libpurple/xmlnode.c
--- a/config/version.txt
+++ b/config/version.txt
@@ -1,1 +1,1 @@
-2.10.9
+2.10.11
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -40,16 +40,21 @@
 #include "util.h"
 #include "xmlnode.h"
 
 #define schedule_accounts_save()
 
 typedef struct
 {
 	PurpleConnectionErrorInfo *current_error;
+
+	/* libpurple 3.0.0 compatibility */
+	char *password_keyring;
+	char *password_mode;
+	char *password_ciphertext;
 } PurpleAccountPrivate;
 
 #define PURPLE_ACCOUNT_GET_PRIVATE(account) \
 	((PurpleAccountPrivate *) (account->priv))
 
 /* TODO: Should use PurpleValue instead of this?  What about "ui"? */
 typedef struct
 {
@@ -85,16 +90,25 @@ static GList   *accounts = NULL;
 static guint    save_timer = 0;
 static gboolean accounts_loaded = FALSE;
 
 static GList *handles = NULL;
 
 static void set_current_error(PurpleAccount *account,
 	PurpleConnectionErrorInfo *new_err);
 
+static void
+_purple_account_set_encrypted_password(PurpleAccount *account, const char *keyring,
+	const char *mode, const char *ciphertext);
+static gboolean
+_purple_account_get_encrypted_password(PurpleAccount *account, const char **keyring,
+	const char **mode, const char **ciphertext);
+static gboolean
+_purple_account_is_password_encrypted(PurpleAccount *account);
+
 #if 0
 /*********************************************************************
  * Writing to disk                                                   *
  *********************************************************************/
 
 static void
 setting_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
 {
@@ -378,16 +392,36 @@ account_to_xmlnode(PurpleAccount *accoun
 	child = xmlnode_new_child(node, "name");
 	xmlnode_insert_data(child, purple_account_get_username(account), -1);
 
 	if (purple_account_get_remember_password(account) &&
 		((tmp = purple_account_get_password(account)) != NULL))
 	{
 		child = xmlnode_new_child(node, "password");
 		xmlnode_insert_data(child, tmp, -1);
+	} else if (_purple_account_is_password_encrypted(account)) {
+		const char *keyring = NULL;
+		const char *mode = NULL;
+		const char *ciphertext = NULL;
+		gboolean success;
+
+		purple_debug_warning("account", "saving libpurple3-compatible "
+			"encrypted password\n");
+
+		success = _purple_account_get_encrypted_password(account,
+			&keyring, &mode, &ciphertext);
+		g_warn_if_fail(success);
+
+		child = xmlnode_new_child(node, "password");
+		if (keyring != NULL)
+			xmlnode_set_attrib(child, "keyring_id", keyring);
+		if (mode != NULL)
+			xmlnode_set_attrib(child, "mode", mode);
+		if (ciphertext != NULL)
+			xmlnode_insert_data(child, ciphertext, -1);
 	}
 
 	if ((tmp = purple_account_get_alias(account)) != NULL)
 	{
 		child = xmlnode_new_child(node, "alias");
 		xmlnode_insert_data(child, tmp, -1);
 	}
 
@@ -877,20 +911,40 @@ parse_account(xmlnode *node)
 	}
 
 	ret = purple_account_new(name, _purple_oscar_convert(name, protocol_id)); /* XXX: */
 	g_free(name);
 	g_free(protocol_id);
 
 	/* Read the password */
 	child = xmlnode_get_child(node, "password");
-	if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
-	{
-		purple_account_set_remember_password(ret, TRUE);
-		purple_account_set_password(ret, data);
+	if (child != NULL) {
+		const char *keyring_id = xmlnode_get_attrib(child, "keyring_id");
+		const char *mode = xmlnode_get_attrib(child, "mode");
+		gboolean is_plaintext;
+
+		data = xmlnode_get_data(child);
+
+		if (keyring_id == NULL || keyring_id[0] == '\0')
+			is_plaintext = TRUE;
+		else if (g_strcmp0(keyring_id, "keyring-internal") != 0)
+			is_plaintext = FALSE;
+		else if (mode == NULL || mode[0] == '\0' || g_strcmp0(mode, "cleartext") == 0)
+			is_plaintext = TRUE;
+		else
+			is_plaintext = FALSE;
+
+		if (is_plaintext) {
+			purple_account_set_remember_password(ret, TRUE);
+			purple_account_set_password(ret, data);
+		} else {
+			purple_debug_warning("account", "found encrypted password, "
+				"but it's not supported in 2.x.y\n");
+			_purple_account_set_encrypted_password(ret, keyring_id, mode, data);
+		}
 		g_free(data);
 	}
 
 	/* Read the alias */
 	child = xmlnode_get_child(node, "alias");
 	if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
 	{
 		if (*data != '\0')
@@ -1118,16 +1172,21 @@ purple_account_destroy(PurpleAccount *ac
 	}
 
 	priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
 	PURPLE_DBUS_UNREGISTER_POINTER(priv->current_error);
 	if (priv->current_error) {
 		g_free(priv->current_error->description);
 		g_free(priv->current_error);
 	}
+
+	g_free(priv->password_keyring);
+	g_free(priv->password_mode);
+	g_free(priv->password_ciphertext);
+
 	g_free(priv);
 
 	PURPLE_DBUS_UNREGISTER_POINTER(account);
 	g_free(account);
 }
 
 void
 purple_account_set_register_callback(PurpleAccount *account, PurpleAccountRegistrationCb cb, void *user_data)
@@ -3008,17 +3067,18 @@ purple_accounts_delete(PurpleAccount *ac
 
 void
 purple_accounts_reorder(PurpleAccount *account, gint new_index)
 {
 	gint index;
 	GList *l;
 
 	g_return_if_fail(account != NULL);
-	g_return_if_fail(new_index <= g_list_length(accounts));
+	g_return_if_fail(new_index >= 0);
+	g_return_if_fail((guint)new_index <= g_list_length(accounts));
 
 	index = g_list_index(accounts, account);
 
 	if (index == -1) {
 		purple_debug_error("account",
 				   "Unregistered account (%s) discovered during reorder!\n",
 				   purple_account_get_username(account));
 		return;
@@ -3284,8 +3344,53 @@ purple_accounts_uninit(void)
 	}
 #endif
 	for (; accounts; accounts = g_list_delete_link(accounts, accounts))
 		purple_account_destroy(accounts->data);
 
 	purple_signals_disconnect_by_handle(handle);
 	purple_signals_unregister_by_instance(handle);
 }
+
+/* libpurple 3.0.0 compatibility */
+
+static void
+_purple_account_set_encrypted_password(PurpleAccount *account, const char *keyring,
+	const char *mode, const char *ciphertext)
+{
+	PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+	g_free(priv->password_keyring);
+	g_free(priv->password_mode);
+	g_free(priv->password_ciphertext);
+
+	priv->password_keyring = g_strdup(keyring);
+	priv->password_mode = g_strdup(mode);
+	priv->password_ciphertext = g_strdup(ciphertext);
+}
+
+static gboolean
+_purple_account_get_encrypted_password(PurpleAccount *account, const char **keyring,
+	const char **mode, const char **ciphertext)
+{
+	PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+	g_return_val_if_fail(keyring != NULL, FALSE);
+	g_return_val_if_fail(mode != NULL, FALSE);
+	g_return_val_if_fail(ciphertext != NULL, FALSE);
+
+	if (!_purple_account_is_password_encrypted(account))
+		return FALSE;
+
+	*keyring = priv->password_keyring;
+	*mode = priv->password_mode;
+	*ciphertext = priv->password_ciphertext;
+
+	return TRUE;
+}
+
+static gboolean
+_purple_account_is_password_encrypted(PurpleAccount *account)
+{
+	PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
+
+	return (priv->password_keyring != NULL);
+}
--- a/libpurple/blist.c
+++ b/libpurple/blist.c
@@ -274,17 +274,18 @@ group_to_xmlnode(PurpleBlistNode *gnode)
 {
 	xmlnode *node, *child;
 	PurpleGroup *group;
 	PurpleBlistNode *cnode;
 
 	group = (PurpleGroup *)gnode;
 
 	node = xmlnode_new("group");
-	xmlnode_set_attrib(node, "name", group->name);
+	if (g_strcmp0(group->name, _("Buddies")) != 0)
+		xmlnode_set_attrib(node, "name", group->name);
 
 	/* Write settings */
 	g_hash_table_foreach(group->node.settings, value_to_xmlnode, node);
 
 	/* Write contacts and chats */
 	for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
 	{
 		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(cnode))
--- a/libpurple/cipher.c
+++ b/libpurple/cipher.c
@@ -439,18 +439,17 @@ purple_cipher_context_digest(PurpleCiphe
 }
 
 gboolean
 purple_cipher_context_digest_to_str(PurpleCipherContext *context, size_t in_len,
 								   gchar digest_s[], size_t *out_len)
 {
 	/* 8k is a bit excessive, will tweak later. */
 	guchar digest[BUF_LEN * 4];
-	gint n = 0;
-	size_t dlen = 0;
+	size_t n, dlen = 0;
 
 	g_return_val_if_fail(context, FALSE);
 	g_return_val_if_fail(digest_s, FALSE);
 
 	if(!purple_cipher_context_digest(context, sizeof(digest), digest, &dlen))
 		return FALSE;
 
 	/* in_len must be greater than dlen * 2 so we have room for the NUL. */
--- a/libpurple/circbuffer.c
+++ b/libpurple/circbuffer.c
@@ -37,18 +37,18 @@ purple_circ_buffer_new(gsize growsize) {
 void purple_circ_buffer_destroy(PurpleCircBuffer *buf) {
 	g_return_if_fail(buf != NULL);
 
 	g_free(buf->buffer);
 	g_free(buf);
 }
 
 static void grow_circ_buffer(PurpleCircBuffer *buf, gsize len) {
-	int in_offset = 0, out_offset = 0;
-	int start_buflen;
+	gsize in_offset = 0, out_offset = 0;
+	gsize start_buflen;
 
 	g_return_if_fail(buf != NULL);
 
 	start_buflen = buf->buflen;
 
 	while ((buf->buflen - buf->bufused) < len)
 		buf->buflen += buf->growsize;
 
@@ -65,20 +65,18 @@ static void grow_circ_buffer(PurpleCircB
 		buf->inptr = buf->buffer + in_offset;
 		buf->outptr = buf->buffer + out_offset;
 	}
 
 	/* If the fill pointer is wrapped to before the remove
 	 * pointer, we need to shift the data */
 	if (in_offset < out_offset
 			|| (in_offset == out_offset && buf->bufused > 0)) {
-		int shift_n = MIN(buf->buflen - start_buflen,
-			in_offset);
-		memcpy(buf->buffer + start_buflen, buf->buffer,
-			shift_n);
+		gsize shift_n = MIN(buf->buflen - start_buflen, in_offset);
+		memcpy(buf->buffer + start_buflen, buf->buffer, shift_n);
 
 		/* If we couldn't fit the wrapped read buffer
 		 * at the end */
 		if (shift_n < in_offset) {
 			memmove(buf->buffer,
 				buf->buffer + shift_n,
 				in_offset - shift_n);
 			buf->inptr = buf->buffer +
@@ -87,31 +85,30 @@ static void grow_circ_buffer(PurpleCircB
 			buf->inptr = buf->buffer +
 				start_buflen + in_offset;
 		}
 	}
 }
 
 void purple_circ_buffer_append(PurpleCircBuffer *buf, gconstpointer src, gsize len) {
 
-	int len_stored;
+	gsize len_stored;
 
 	g_return_if_fail(buf != NULL);
 
 	/* Grow the buffer, if necessary */
 	if ((buf->buflen - buf->bufused) < len)
 		grow_circ_buffer(buf, len);
 
 	/* If there is not enough room to copy all of src before hitting
 	 * the end of the buffer then we will need to do two copies.
 	 * One copy from inptr to the end of the buffer, and the
 	 * second copy from the start of the buffer to the end of src. */
 	if (buf->inptr >= buf->outptr)
-		len_stored = MIN(len, buf->buflen
-			- (buf->inptr - buf->buffer));
+		len_stored = MIN(len, buf->buflen - (buf->inptr - buf->buffer));
 	else
 		len_stored = len;
 
 	if (len_stored > 0)
 		memcpy(buf->inptr, src, len_stored);
 
 	if (len_stored < len) {
 		memcpy(buf->buffer, (char*)src + len_stored, len - len_stored);
@@ -140,14 +137,14 @@ gsize purple_circ_buffer_get_max_read(co
 
 gboolean purple_circ_buffer_mark_read(PurpleCircBuffer *buf, gsize len) {
 	g_return_val_if_fail(buf != NULL, FALSE);
 	g_return_val_if_fail(purple_circ_buffer_get_max_read(buf) >= len, FALSE);
 
 	buf->outptr += len;
 	buf->bufused -= len;
 	/* wrap to the start if we're at the end */
-	if ((buf->outptr - buf->buffer) == buf->buflen)
+	if ((gsize)(buf->outptr - buf->buffer) == buf->buflen)
 		buf->outptr = buf->buffer;
 
 	return TRUE;
 }
 
--- a/libpurple/core.c
+++ b/libpurple/core.c
@@ -84,17 +84,20 @@ purple_core_init(const char *ui)
 
 #ifdef ENABLE_NLS
 	bindtextdomain(PACKAGE, LOCALEDIR);
 #endif
 #ifdef _WIN32
 	wpurple_init();
 #endif
 
+#if !GLIB_CHECK_VERSION(2, 36, 0)
+	/* GLib type system is automaticaly initialized since 2.36. */
 	g_type_init();
+#endif
 
 	_core = core = g_new0(PurpleCore, 1);
 	core->ui = g_strdup(ui);
 	core->reserved = NULL;
 
 	ops = purple_core_get_ui_ops();
 
 	/* The signals subsystem is important and should be first. */
--- a/libpurple/dnsquery.c
+++ b/libpurple/dnsquery.c
@@ -33,16 +33,18 @@
 #include "notify.h"
 #include "prefs.h"
 #include "util.h"
 
 #ifndef _WIN32
 #include <resolv.h>
 #endif
 
+#define MAX_ADDR_RESPONSE_LEN 1048576
+
 /**************************************************************************
  * DNS query API
  **************************************************************************/
 
 static PurpleDnsQueryUiOps *dns_query_ui_ops = NULL;
 
 typedef struct _PurpleDnsQueryResolverProcess PurpleDnsQueryResolverProcess;
 
@@ -235,17 +237,17 @@ trap_gdb_bug(int sig)
 #endif
 
 static void
 write_to_parent(int fd, const void *buf, size_t count)
 {
 	ssize_t written;
 
 	written = write(fd, buf, count);
-	if (written != count) {
+	if (written < 0 || (gsize)written != count) {
 		if (written < 0)
 			fprintf(stderr, "dns[%d]: Error writing data to "
 					"parent: %s\n", getpid(), strerror(errno));
 		else
 			fprintf(stderr, "dns[%d]: Error: Tried to write %"
 					G_GSIZE_FORMAT " bytes to parent but instead "
 					"wrote %" G_GSIZE_FORMAT " bytes\n",
 					getpid(), count, written);
@@ -411,17 +413,17 @@ cope_with_gdb_brokenness(void)
 		return;
 	already_done = TRUE;
 	ppid = getppid();
 	g_snprintf(s, sizeof(s), "/proc/%d/exe", ppid);
 	n = readlink(s, e, sizeof(e));
 	if(n < 0)
 		return;
 
-	e[MIN(n,sizeof(e)-1)] = '\0';
+	e[MIN((gsize)n,sizeof(e)-1)] = '\0';
 
 	if(strstr(e,"gdb")) {
 		purple_debug_info("dns",
 				   "Debugger detected, performing useless query...\n");
 		gethostbyname("x.x.x.x.x");
 	}
 #endif
 }
@@ -544,17 +546,17 @@ send_dns_request_to_child(PurpleDnsQuery
 	/* Send the data structure to the child */
 	rc = write(resolver->fd_in, &dns_params, sizeof(dns_params));
 	if (rc < 0) {
 		purple_debug_error("dns", "Unable to write to DNS child %d: %s\n",
 				resolver->dns_pid, g_strerror(errno));
 		purple_dnsquery_resolver_destroy(resolver);
 		return FALSE;
 	}
-	if (rc < sizeof(dns_params)) {
+	if ((gsize)rc < sizeof(dns_params)) {
 		purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT
 				" bytes to child but only wrote %" G_GSSIZE_FORMAT "\n",
 				sizeof(dns_params), rc);
 		purple_dnsquery_resolver_destroy(resolver);
 		return FALSE;
 	}
 
 	purple_debug_info("dns",
@@ -657,17 +659,17 @@ host_resolved(gpointer data, gint source
 		/* Re-read resolv.conf and friends in case DNS servers have changed */
 		res_init();
 
 		purple_dnsquery_failed(query_data, message);
 	} else if (rc > 0) {
 		/* Success! */
 		while (rc > 0) {
 			rc = read(query_data->resolver->fd_out, &addrlen, sizeof(addrlen));
-			if (rc > 0 && addrlen > 0) {
+			if (rc > 0 && addrlen > 0 && addrlen < MAX_ADDR_RESPONSE_LEN) {
 				addr = g_malloc(addrlen);
 				rc = read(query_data->resolver->fd_out, addr, addrlen);
 				hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen));
 				hosts = g_slist_append(hosts, addr);
 			} else {
 				break;
 			}
 		}
--- a/libpurple/dnssrv.c
+++ b/libpurple/dnssrv.c
@@ -44,16 +44,18 @@
 
 #ifndef T_SRV
 #define T_SRV	PurpleDnsTypeSrv
 #endif
 #ifndef T_TXT
 #define T_TXT	PurpleDnsTypeTxt
 #endif
 
+#define MAX_ADDR_RESPONSE_LEN 1048576
+
 #include "debug.h"
 #include "dnssrv.h"
 #include "eventloop.h"
 #include "network.h"
 
 static PurpleSrvTxtQueryUiOps *srv_txt_query_ui_ops = NULL;
 
 #ifndef _WIN32
@@ -476,16 +478,20 @@ resolved(gpointer data, gint source, Pur
 	int size;
 	int type;
 	PurpleSrvTxtQueryData *query_data = (PurpleSrvTxtQueryData*)data;
 	int i;
 	int status;
 
 	if (read(source, &type, sizeof(type)) == sizeof(type)) {
 		if (read(source, &size, sizeof(size)) == sizeof(size)) {
+			if (size < -1 || size > MAX_ADDR_RESPONSE_LEN) {
+				purple_debug_warning("dnssrv", "res_query returned invalid number\n");
+				size = 0;
+			}
 			if (size == -1 || size == 0) {
 				if (size == -1) {
 					purple_debug_warning("dnssrv", "res_query returned an error\n");
 					/* Re-read resolv.conf and friends in case DNS servers have changed */
 					res_init();
 				} else
 					purple_debug_info("dnssrv", "Found 0 entries, errno is %i\n", errno);
 
@@ -533,22 +539,30 @@ resolved(gpointer data, gint source, Pur
 							purple_debug_error("dnssrv","unable to read txt "
 									"response length: %s\n", g_strerror(errno));
 							size = 0;
 							g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
 							g_list_free(responses);
 							responses = NULL;
 							break;
 						}
+						if (len > MAX_ADDR_RESPONSE_LEN) {
+							purple_debug_error("dnssrv", "we've read invalid number\n");
+							size = 0;
+							g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
+							g_list_free(responses);
+							responses = NULL;
+							break;
+						}
 
 						res = g_new0(PurpleTxtResponse, 1);
 						res->content = g_new0(gchar, len);
 
 						red = read(source, res->content, len);
-						if (red != len) {
+						if (red < 0 || (gsize)red != len) {
 							purple_debug_error("dnssrv","unable to read txt "
 									"response: %s\n", g_strerror(errno));
 							size = 0;
 							purple_txt_response_destroy(res);
 							g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
 							g_list_free(responses);
 							responses = NULL;
 							break;
@@ -746,17 +760,17 @@ purple_srv_resolve_account(PurpleAccount
 		purple_debug_error("dnssrv", "Wrong arguments\n");
 		cb(NULL, 0, extradata);
 		g_return_val_if_reached(NULL);
 	}
 
 	proxy_type = purple_proxy_info_get_type(
 		purple_proxy_get_setup(account));
 	if (proxy_type == PURPLE_PROXY_TOR) {
-		purple_debug_info("dnssrv", "Aborting SRV lookup in Tor Proxy mode.");
+		purple_debug_info("dnssrv", "Aborting SRV lookup in Tor Proxy mode.\n");
 		cb(NULL, 0, extradata);
 		return NULL;
 	}
 
 #ifdef USE_IDN
 	if (!dns_str_is_ascii(domain)) {
 		int ret = purple_network_convert_idn_to_ascii(domain, &hostname);
 		if (ret != 0) {
@@ -864,17 +878,17 @@ PurpleSrvTxtQueryData *purple_txt_resolv
 	int pid;
 #else
 	GError* err = NULL;
 #endif
 
 	proxy_type = purple_proxy_info_get_type(
 		purple_proxy_get_setup(account));
 	if (proxy_type == PURPLE_PROXY_TOR) {
-		purple_debug_info("dnssrv", "Aborting TXT lookup in Tor Proxy mode.");
+		purple_debug_info("dnssrv", "Aborting TXT lookup in Tor Proxy mode.\n");
 		cb(NULL, extradata);
 		return NULL;
 	}
 
 #ifdef USE_IDN
 	if (!dns_str_is_ascii(domain)) {
 		int ret = purple_network_convert_idn_to_ascii(domain, &hostname);
 		if (ret != 0) {
--- a/libpurple/ft.c
+++ b/libpurple/ft.c
@@ -93,17 +93,17 @@ purple_xfer_status_type_to_string(Purple
 		{ PURPLE_XFER_STATUS_UNKNOWN, "unknown" },
 		{ PURPLE_XFER_STATUS_NOT_STARTED, "not started" },
 		{ PURPLE_XFER_STATUS_ACCEPTED, "accepted" },
 		{ PURPLE_XFER_STATUS_STARTED, "started" },
 		{ PURPLE_XFER_STATUS_DONE, "done" },
 		{ PURPLE_XFER_STATUS_CANCEL_LOCAL, "cancelled locally" },
 		{ PURPLE_XFER_STATUS_CANCEL_REMOTE, "cancelled remotely" }
 	};
-	int i;
+	gsize i;
 
 	for (i = 0; i < G_N_ELEMENTS(type_names); ++i)
 		if (type_names[i].type == type)
 			return type_names[i].name;
 
 	return "invalid state";
 }
 
@@ -635,16 +635,18 @@ purple_xfer_request_accepted(PurpleXfer 
 	account = purple_xfer_get_account(xfer);
 
 	purple_debug_misc("xfer", "request accepted for %p\n", xfer);
 
 	if (!filename && type == PURPLE_XFER_RECEIVE) {
 		xfer->status = PURPLE_XFER_STATUS_ACCEPTED;
 		xfer->ops.init(xfer);
 		return;
+	} else {
+		g_return_if_fail(filename != NULL);
 	}
 
 	buddy = purple_find_buddy(account, xfer->who);
 
 	if (type == PURPLE_XFER_SEND) {
 		/* Sending a file */
 		/* Check the filename. */
 		PurpleXferUiOps *ui_ops;
@@ -1066,17 +1068,17 @@ purple_xfer_read(PurpleXfer *xfer, gucha
 		if (r < 0 && errno == EAGAIN)
 			r = 0;
 		else if (r < 0)
 			r = -1;
 		else if (r == 0)
 			r = -1;
 	}
 
-	if (r == xfer->current_buffer_size)
+	if (r >= 0 && (gsize)r == xfer->current_buffer_size)
 		/*
 		 * We managed to read the entire buffer.  This means our this
 		 * network is fast and our buffer is too small, so make it
 		 * bigger.
 		 */
 		purple_xfer_increase_buffer_size(xfer);
 
 	return r;
@@ -1121,17 +1123,17 @@ do_transfer(PurpleXfer *xfer)
 		r = purple_xfer_read(xfer, &buffer);
 		if (r > 0) {
 			size_t wc;
 			if (ui_ops && ui_ops->ui_write)
 				wc = ui_ops->ui_write(xfer, buffer, r);
 			else
 				wc = fwrite(buffer, 1, r, xfer->dest_fp);
 
-			if (wc != r) {
+			if (wc != (gsize)r) {
 				purple_debug_error("filetransfer", "Unable to write whole buffer.\n");
 				purple_xfer_cancel_local(xfer);
 				g_free(buffer);
 				return;
 			}
 
 			if ((purple_xfer_get_size(xfer) > 0) &&
 				((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
@@ -1214,17 +1216,17 @@ do_transfer(PurpleXfer *xfer)
 
 		if (r == -1) {
 			purple_xfer_cancel_remote(xfer);
 			if (!priv->buffer)
 				/* We don't free buffer if priv->buffer is set, because in
 				   that case buffer doesn't belong to us. */
 				g_free(buffer);
 			return;
-		} else if (r == result) {
+		} else if (r >= 0 && (gsize)r == result) {
 			/*
 			 * We managed to write the entire buffer.  This means our
 			 * network is fast and our buffer is too small, so make it
 			 * bigger.
 			 */
 			purple_xfer_increase_buffer_size(xfer);
 		} else {
 			if (ui_ops && ui_ops->data_not_sent)
@@ -1302,17 +1304,22 @@ begin_transfer(PurpleXfer *xfer, PurpleI
 		                        type == PURPLE_XFER_RECEIVE ? "wb" : "rb");
 
 		if (xfer->dest_fp == NULL) {
 			purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
 			purple_xfer_cancel_local(xfer);
 			return;
 		}
 
-		fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET);
+		if (fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET) != 0) {
+			purple_debug_error("xfer", "couldn't seek\n");
+			purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
+			purple_xfer_cancel_local(xfer);
+			return;
+		}
 	}
 
 	if (xfer->fd != -1)
 		xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer);
 
 	xfer->start_time = time(NULL);
 
 	if (xfer->ops.start != NULL)
--- a/libpurple/internal.h
+++ b/libpurple/internal.h
@@ -143,22 +143,58 @@
 #	define PURPLE_TIME_T_MODIFIER "zu"
 #else
 #error Unknown size of time_t
 #endif
 #endif
 
 #include <glib-object.h>
 
+#if !GLIB_CHECK_VERSION(2, 32, 0)
+
+#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#define G_GNUC_END_IGNORE_DEPRECATIONS
+
+#endif /* 2.32.0 */
+
+#ifdef __clang__
+
+#undef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+	_Pragma ("clang diagnostic push") \
+	_Pragma ("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+
+#undef G_GNUC_END_IGNORE_DEPRECATIONS
+#define G_GNUC_END_IGNORE_DEPRECATIONS \
+	_Pragma ("clang diagnostic pop")
+
+#endif /* __clang__ */
+
+#ifdef __COVERITY__
+
+/* avoid TAINTED_SCALAR warning */
+#undef g_utf8_next_char
+#define g_utf8_next_char(p) (char *)((p) + 1)
+
+#endif
+
 /* Safer ways to work with static buffers. When using non-static
  * buffers, either use g_strdup_* functions (preferred) or use
  * g_strlcpy/g_strlcpy directly. */
 #define purple_strlcpy(dest, src) g_strlcpy(dest, src, sizeof(dest))
 #define purple_strlcat(dest, src) g_strlcat(dest, src, sizeof(dest))
 
+typedef union
+{
+	struct sockaddr sa;
+	struct sockaddr_in in;
+	struct sockaddr_in6 in6;
+	struct sockaddr_storage storage;
+} common_sockaddr_t;
+
 #define PURPLE_WEBSITE "http://pidgin.im/"
 #define PURPLE_DEVEL_WEBSITE "http://developer.pidgin.im/"
 
 
 /* INTERNAL FUNCTIONS */
 
 #include "account.h"
 #include "connection.h"
@@ -218,9 +254,19 @@ void _purple_connection_new_unregister(P
  * @note This function should only be called by purple_account_disconnect()
  *        in account.c.  If you're trying to sign off an account, use that
  *        function instead.
  *
  * @param gc The purple connection to destroy.
  */
 void _purple_connection_destroy(PurpleConnection *gc);
 
+/**
+ * Sets most commonly used socket flags: O_NONBLOCK and FD_CLOEXEC.
+ *
+ * @param fd The file descriptor for the socket.
+ *
+ * @return TRUE if succeeded, FALSE otherwise.
+ */
+gboolean
+_purple_network_set_common_socket_flags(int fd);
+
 #endif /* _PURPLE_INTERNAL_H_ */
--- a/libpurple/network.c
+++ b/libpurple/network.c
@@ -179,17 +179,17 @@ purple_network_get_local_system_ip(int f
 
 	if (fd < 0)
 		source = socket(PF_INET,SOCK_STREAM, 0);
 
 	ifc.ifc_len = sizeof(buffer);
 	ifc.ifc_req = (struct ifreq *)buffer;
 	ioctl(source, SIOCGIFCONF, &ifc);
 
-	if (fd < 0)
+	if (fd < 0 && source >= 0)
 		close(source);
 
 	tmp = buffer;
 	while (tmp < buffer + ifc.ifc_len)
 	{
 		ifr = (struct ifreq *)tmp;
 		tmp += HX_SIZE_OF_IFREQ(*ifr);
 
@@ -401,17 +401,16 @@ void purple_network_listen_map_external(
 {
 	listen_map_external = map_external;
 }
 
 static PurpleNetworkListenData *
 purple_network_do_listen(unsigned short port, int socket_family, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
 {
 	int listenfd = -1;
-	int flags;
 	const int on = 1;
 	PurpleNetworkListenData *listen_data;
 	unsigned short actual_port;
 #ifdef HAVE_GETADDRINFO
 	int errnum;
 	struct addrinfo hints, *res, *next;
 	char serv[6];
 
@@ -486,21 +485,17 @@ purple_network_do_listen(unsigned short 
 	}
 #endif
 
 	if (socket_type == SOCK_STREAM && listen(listenfd, 4) != 0) {
 		purple_debug_warning("network", "listen: %s\n", g_strerror(errno));
 		close(listenfd);
 		return NULL;
 	}
-	flags = fcntl(listenfd, F_GETFL);
-	fcntl(listenfd, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
-	fcntl(listenfd, F_SETFD, FD_CLOEXEC);
-#endif
+	_purple_network_set_common_socket_flags(listenfd);
 	actual_port = purple_network_get_port_from_fd(listenfd);
 
 	purple_debug_info("network", "Listening on port: %hu\n", actual_port);
 
 	listen_data = g_new0(PurpleNetworkListenData, 1);
 	listen_data->listenfd = listenfd;
 	listen_data->adding = TRUE;
 	listen_data->retry = TRUE;
@@ -1137,16 +1132,43 @@ int purple_network_convert_idn_to_ascii(
 #else
 	g_return_val_if_fail(out != NULL, -1);
 
 	*out = g_strdup(in);
 	return 0;
 #endif
 }
 
+gboolean
+_purple_network_set_common_socket_flags(int fd)
+{
+	int flags;
+	gboolean succ = TRUE;
+
+	g_return_val_if_fail(fd >= 0, FALSE);
+
+	flags = fcntl(fd, F_GETFL);
+
+	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
+		purple_debug_warning("network",
+			"Couldn't set O_NONBLOCK flag\n");
+		succ = FALSE;
+	}
+
+#ifndef _WIN32
+	if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+		purple_debug_warning("network",
+			"Couldn't set FD_CLOEXEC flag\n");
+		succ = FALSE;
+	}
+#endif
+
+	return succ;
+}
+
 void
 purple_network_init(void)
 {
 #ifdef HAVE_NETWORKMANAGER
 	GError *error = NULL;
 #endif
 #if 0
 	GError *err = NULL;
--- a/libpurple/pluginpref.c
+++ b/libpurple/pluginpref.c
@@ -137,21 +137,28 @@ purple_plugin_pref_new_with_name_and_lab
 	pref->label = g_strdup(label);
 
 	return pref;
 }
 
 void
 purple_plugin_pref_destroy(PurplePluginPref *pref)
 {
+	GList *tmp;
 	g_return_if_fail(pref != NULL);
 
 	g_free(pref->name);
 	g_free(pref->label);
-	g_list_free(pref->choices);
+	tmp = pref->choices;
+	while(tmp) {
+		g_free(tmp->data);
+		/* Remove the string, and the data entries */
+		tmp = g_list_delete_link(tmp, tmp);
+		tmp = g_list_delete_link(tmp, tmp);
+	}
 	g_free(pref);
 }
 
 void
 purple_plugin_pref_set_name(PurplePluginPref *pref, const char *name)
 {
 	g_return_if_fail(pref != NULL);
 	g_return_if_fail(name != NULL);
@@ -248,17 +255,17 @@ purple_plugin_pref_get_type(PurplePlugin
 
 void
 purple_plugin_pref_add_choice(PurplePluginPref *pref, const char *label, gpointer choice)
 {
 	g_return_if_fail(pref  != NULL);
 	g_return_if_fail(label != NULL);
 	g_return_if_fail(choice || purple_prefs_get_type(pref->name) == PURPLE_PREF_INT);
 
-	pref->choices = g_list_append(pref->choices, (gpointer)label);
+	pref->choices = g_list_append(pref->choices, g_strdup(label));
 	pref->choices = g_list_append(pref->choices, choice);
 }
 
 GList *
 purple_plugin_pref_get_choices(PurplePluginPref *pref)
 {
 	g_return_val_if_fail(pref != NULL, NULL);
 
--- a/libpurple/protocols/bonjour/bonjour_ft.c
+++ b/libpurple/protocols/bonjour/bonjour_ft.c
@@ -803,26 +803,19 @@ bonjour_sock5_request_cb(gpointer data, 
 			purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno);
 
 			purple_input_remove(xfer->watcher);
 			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
 		} else {
-			int flags;
-
 			purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d\n", acceptfd);
 
-			flags = fcntl(acceptfd, F_GETFL);
-			fcntl(acceptfd, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
-			fcntl(acceptfd, F_SETFD, FD_CLOEXEC);
-#endif
-
+			_purple_network_set_common_socket_flags(acceptfd);
 			purple_input_remove(xfer->watcher);
 			close(source);
 			xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
 							 bonjour_sock5_request_cb, xfer);
 			xf->sock5_req_state++;
 			xf->rxlen = 0;
 		}
 		break;
@@ -940,17 +933,17 @@ bonjour_bytestreams_listen(int sock, gpo
 	xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
 	xmlnode_set_attrib(query, "sid", xf->sid);
 	xmlnode_set_attrib(query, "mode", "tcp");
 
 	xfer->local_port = purple_network_get_port_from_fd(sock);
 
 	local_ips = bonjour_jabber_get_local_ips(sock);
 
-	port = g_strdup_printf("%hu", xfer->local_port);
+	port = g_strdup_printf("%hu", (guint16)purple_xfer_get_local_port(xfer));
 	while(local_ips) {
 		streamhost = xmlnode_new_child(query, "streamhost");
 		xmlnode_set_attrib(streamhost, "jid", xf->sid);
 		xmlnode_set_attrib(streamhost, "host", local_ips->data);
 		xmlnode_set_attrib(streamhost, "port", port);
 		g_free(local_ips->data);
 		local_ips = g_slist_delete_link(local_ips, local_ips);
 	}
--- a/libpurple/protocols/bonjour/jabber.c
+++ b/libpurple/protocols/bonjour/jabber.c
@@ -144,30 +144,31 @@ get_xmlnode_contents(xmlnode *node)
 }
 
 static void
 _jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb)
 {
 	xmlnode *body_node, *html_node, *events_node;
 	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(pb));
 	gchar *body = NULL;
-	gboolean composing_event = FALSE;
 
 	body_node = xmlnode_get_child(message_node, "body");
 	html_node = xmlnode_get_child(message_node, "html");
 
 	if (body_node == NULL && html_node == NULL) {
 		purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
 		return;
 	}
 
 	events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
 	if (events_node != NULL) {
+#if 0
 		if (xmlnode_get_child(events_node, "composing") != NULL)
 			composing_event = TRUE;
+#endif
 		if (xmlnode_get_child(events_node, "id") != NULL) {
 			/* The user is just typing */
 			/* TODO: Deal with typing notification */
 			return;
 		}
 	}
 
 	if (html_node != NULL) {
@@ -367,22 +368,24 @@ static gint
 	return ret;
 }
 
 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) {
 
 	g_return_if_fail(packet != NULL);
 	g_return_if_fail(pb != NULL);
 
-	if (!strcmp(packet->name, "message"))
+	if (g_strcmp0(packet->name, "message") == 0)
 		_jabber_parse_and_write_message_to_ui(packet, pb);
-	else if(!strcmp(packet->name, "iq"))
+	else if (g_strcmp0(packet->name, "iq") == 0)
 		xep_iq_parse(packet, pb);
-	else
-		purple_debug_warning("bonjour", "Unknown packet: %s\n", packet->name ? packet->name : "(null)");
+	else {
+		purple_debug_warning("bonjour", "Unknown packet: %s\n",
+			packet->name ? packet->name : "(null)");
+	}
 }
 
 static void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
 
 	/* Inform the user that the conversation has been closed */
 	BonjourBuddy *bb = NULL;
 	const gchar *name = bconv->pb ? purple_buddy_get_name(bconv->pb) : "(unknown)";
 
@@ -626,57 +629,50 @@ void bonjour_jabber_stream_started(Bonjo
 #ifndef INET6_ADDRSTRLEN
 #define INET6_ADDRSTRLEN 46
 #endif
 
 static void
 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
 {
 	BonjourJabber *jdata = data;
-	struct sockaddr_storage their_addr; /* connector's address information */
-	socklen_t sin_size = sizeof(struct sockaddr_storage);
+	common_sockaddr_t their_addr; /* connector's address information */
+	socklen_t sin_size = sizeof(common_sockaddr_t);
 	int client_socket;
-	int flags;
 #ifdef HAVE_INET_NTOP
 	char addrstr[INET6_ADDRSTRLEN];
 #endif
 	const char *address_text;
 	struct _match_buddies_by_address_t *mbba;
 	BonjourJabberConversation *bconv;
 	GSList *buddies;
 
 	/* Check that it is a read condition */
 	if (condition != PURPLE_INPUT_READ)
 		return;
 
 	memset(&their_addr, 0, sin_size);
 
-	if ((client_socket = accept(server_socket, (struct sockaddr*)&their_addr, &sin_size)) == -1)
+	if ((client_socket = accept(server_socket, &their_addr.sa, &sin_size)) == -1)
 		return;
-
-	flags = fcntl(client_socket, F_GETFL);
-	fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
-	fcntl(client_socket, F_SETFD, FD_CLOEXEC);
-#endif
+	_purple_network_set_common_socket_flags(client_socket);
 
 	/* Look for the buddy that has opened the conversation and fill information */
 #ifdef HAVE_INET_NTOP
-	if (their_addr.ss_family == AF_INET6) {
-		address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in6 *)&their_addr)->sin6_addr,
-			addrstr, sizeof(addrstr));
+	if (their_addr.sa.sa_family == AF_INET6) {
+		address_text = inet_ntop(their_addr.sa.sa_family,
+			&their_addr.in6.sin6_addr, addrstr, sizeof(addrstr));
 
-		append_iface_if_linklocal(addrstr,
-			((struct sockaddr_in6 *)&their_addr)->sin6_scope_id);
+		append_iface_if_linklocal(addrstr, their_addr.in6.sin6_scope_id);
+	} else {
+		address_text = inet_ntop(their_addr.sa.sa_family,
+			&their_addr.in.sin_addr, addrstr, sizeof(addrstr));
 	}
-	else
-		address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in *)&their_addr)->sin_addr,
-			addrstr, sizeof(addrstr));
 #else
-	address_text = inet_ntoa(((struct sockaddr_in *)&their_addr)->sin_addr);
+	address_text = inet_ntoa(their_addr.in.sin_addr);
 #endif
 	purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
 	mbba = g_new0(struct _match_buddies_by_address_t, 1);
 	mbba->address = address_text;
 
 	buddies = purple_find_buddies(jdata->account, NULL);
 	g_slist_foreach(buddies, _match_buddies_by_address, mbba);
 	g_slist_free(buddies);
@@ -770,17 +766,20 @@ bonjour_jabber_start(BonjourJabber *jdat
 		return -1;
 	}
 
 #ifdef PF_INET6
 	if (jdata->socket6 != -1) {
 		struct sockaddr_in6 addr6;
 #ifdef IPV6_V6ONLY
 		int on = 1;
-		setsockopt(jdata->socket6, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+		if (setsockopt(jdata->socket6, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) != 0) {
+			purple_debug_error("bonjour", "couldn't force IPv6\n");
+			return -1;
+		}
 #endif
 	        memset(&addr6, 0, sizeof(addr6));
 		addr6.sin6_family = AF_INET6;
 		addr6.sin6_port = htons(jdata->port);
       		addr6.sin6_addr = in6addr_any;
 		ipv6_port = start_serversocket_listening(jdata->port, jdata->socket6, (struct sockaddr *) &addr6, sizeof(addr6), TRUE, TRUE);
 		/* Open a watcher in the socket we have just opened */
 		if (ipv6_port > 0) {
@@ -1163,18 +1162,24 @@ bonjour_jabber_close_conversation(Bonjou
 				}
 				xfers = tmp_next;
 			}
 		}
 
 		/* Close the socket and remove the watcher */
 		if (bconv->socket >= 0) {
 			/* Send the end of the stream to the other end of the conversation */
-			if (bconv->sent_stream_start == FULLY_SENT)
-				send(bconv->socket, STREAM_END, strlen(STREAM_END), 0);
+			if (bconv->sent_stream_start == FULLY_SENT) {
+				size_t len = strlen(STREAM_END);
+				if (send(bconv->socket, STREAM_END, len, 0) != (gssize)len) {
+					purple_debug_error("bonjour",
+						"bonjour_jabber_close_conversation: "
+						"couldn't send data\n");
+				}
+			}
 			/* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
 			close(bconv->socket);
 		}
 		if (bconv->rx_handler != 0)
 			purple_input_remove(bconv->rx_handler);
 		if (bconv->tx_handler > 0)
 			purple_input_remove(bconv->tx_handler);
 
--- a/libpurple/protocols/gg/gg.c
+++ b/libpurple/protocols/gg/gg.c
@@ -1669,44 +1669,42 @@ static GList *ggp_chat_info(PurpleConnec
 	pce->label = _("Chat _name:");
 	pce->identifier = "name";
 	pce->required = TRUE;
 	m = g_list_append(m, pce);
 
 	return m;
 }
 
-static void ggp_login(PurpleAccount *account)
+static void ggp_login_to(PurpleAccount *account, uint32_t server)
 {
 	PurpleConnection *gc;
 	PurplePresence *presence;
 	PurpleStatus *status;
 	struct gg_login_params *glp;
 	GGPInfo *info;
-	const char *address;
 	const gchar *encryption_type;
 
 	if (ggp_setup_proxy(account) == -1)
 		return;
 
 	gc = purple_account_get_connection(account);
 	glp = g_new0(struct gg_login_params, 1);
-	info = g_new0(GGPInfo, 1);
+	info = gc->proto_data;
+	g_return_if_fail(info);
 
 	/* Probably this should be moved to *_new() function. */
 	info->session = NULL;
 	info->chats = NULL;
 	info->chats_count = 0;
 	info->token = NULL;
 	info->searches = ggp_search_new();
 	info->pending_richtext_messages = NULL;
 	info->pending_images = g_hash_table_new(g_direct_hash, g_direct_equal);
 	info->status_broadcasting = purple_account_get_bool(account, "status_broadcasting", TRUE);
-	
-	gc->proto_data = info;
 
 	glp->uin = ggp_get_uin(account);
 	glp->password = (char *)purple_account_get_password(account);
 	glp->image_size = 255;
 
 	presence = purple_account_get_presence(account);
 	status = purple_presence_get_active_status(presence);
 
@@ -1722,67 +1720,109 @@ static void ggp_login(PurpleAccount *acc
 	if (strcmp(encryption_type, "opportunistic_tls") == 0)
 		glp->tls = 1;
 	else
 		glp->tls = 0;
 	purple_debug_info("gg", "TLS enabled: %d\n", glp->tls);
 
 	if (!info->status_broadcasting)
 		glp->status = glp->status|GG_STATUS_FRIENDS_MASK;
-	
-	address = purple_account_get_string(account, "gg_server", "");
-	if (address && *address) {
-		/* TODO: Make this non-blocking */
-		struct in_addr *addr = gg_gethostbyname(address);
-
-		purple_debug_info("gg", "Using gg server given by user (%s)\n", address);
-
-		if (addr == NULL) {
-			gchar *tmp = g_strdup_printf(_("Unable to resolve hostname '%s': %s"),
-					address, g_strerror(errno));
-			purple_connection_error_reason(gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, /* should this be a settings error? */
-				tmp);
-			g_free(tmp);
-			return;
-		}
-
-		glp->server_addr = inet_addr(inet_ntoa(*addr));
-		glp->server_port = 8074;
-		free(addr);
-	} else
-		purple_debug_info("gg", "Trying to retrieve address from gg appmsg service\n");
+	glp->server_addr = server;
 
 	info->session = gg_login(glp);
 	purple_connection_update_progress(gc, _("Connecting"), 0, 2);
 	if (info->session == NULL) {
 		purple_connection_error_reason (gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Connection failed"));
 		g_free(glp);
 		return;
 	}
 	gc->inpa = purple_input_add(info->session->fd,
 		ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
 		ggp_async_login_handler, gc);
 }
 
+static void
+ggp_login_resolved(GSList *hosts, gpointer _account, const char *error_message)
+{
+	PurpleAccount *account = _account;
+	PurpleConnection *gc;
+	GGPInfo *info;
+	uint32_t server_addr = 0;
+
+	gc = purple_account_get_connection(account);
+	info = gc->proto_data;
+	g_return_if_fail(info);
+	info->dns_query = NULL;
+
+	while (hosts && (hosts = g_slist_delete_link(hosts, hosts))) {
+		struct sockaddr *addr = hosts->data;
+
+		if (addr->sa_family == AF_INET && server_addr == 0) {
+			struct sockaddr_in *addrv4 = (struct sockaddr_in *)addr;
+
+			server_addr = addrv4->sin_addr.s_addr;
+		}
+
+		g_free(hosts->data);
+		hosts = g_slist_delete_link(hosts, hosts);
+	}
+
+	if (server_addr == 0) {
+		gchar *tmp = g_strdup_printf(
+			_("Unable to resolve hostname: %s"), error_message);
+		purple_connection_error_reason(gc,
+			/* should this be a settings error? */
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+		g_free(tmp);
+		return;
+	}
+
+	ggp_login_to(account, server_addr);
+}
+
+static void
+ggp_login(PurpleAccount *account)
+{
+	PurpleConnection *gc;
+	GGPInfo *info;
+	const char *address;
+
+	gc = purple_account_get_connection(account);
+	info = g_new0(GGPInfo, 1);
+	gc->proto_data = info;
+
+	address = purple_account_get_string(account, "gg_server", "");
+	if (address == NULL || address[0] == '\0') {
+		purple_debug_info("gg", "Trying to retrieve address from gg appmsg service\n");
+		ggp_login_to(account, 0);
+		return;
+	}
+
+	purple_debug_info("gg", "Using gg server given by user (%s)\n", address);
+	info->dns_query = purple_dnsquery_a_account(account, address, 8074,
+		ggp_login_resolved, account);
+}
+
 static void ggp_close(PurpleConnection *gc)
 {
-
 	if (gc == NULL) {
 		purple_debug_info("gg", "gc == NULL\n");
 		return;
 	}
 
 	if (gc->proto_data) {
 		PurpleAccount *account = purple_connection_get_account(gc);
 		PurpleStatus *status;
 		GGPInfo *info = gc->proto_data;
 
+		if (info->dns_query)
+			purple_dnsquery_destroy(info->dns_query);
+
 		status = purple_account_get_active_status(account);
 
 		if (info->session != NULL) {
 			ggp_set_status(account, status);
 			gg_logoff(info->session);
 			gg_free_session(info->session);
 		}
 
@@ -2197,16 +2237,26 @@ static GList *ggp_actions(PurplePlugin *
 	return m;
 }
 
 static gboolean ggp_offline_message(const PurpleBuddy *buddy)
 {
 	return TRUE;
 }
 
+static gboolean ggp_load(PurplePlugin *plugin)
+{
+	purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with "
+		"libgadu %s...\n", gg_libgadu_version());
+
+	gg_is_gpl_compliant();
+
+	return TRUE;
+}
+
 static PurplePluginProtocolInfo prpl_info =
 {
 	OPT_PROTO_IM_IMAGE,
 	NULL,				/* user_splits */
 	NULL,				/* protocol_options */
 	{"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY},	/* icon_spec */
 	ggp_list_icon,			/* list_icon */
 	NULL,				/* list_emblem */
@@ -2294,17 +2344,17 @@ static PurplePluginInfo info = {
 	"Gadu-Gadu",				/* name */
 	DISPLAY_VERSION,			/* version */
 
 	N_("Gadu-Gadu Protocol Plugin"),	/* summary */
 	N_("Polish popular IM"),		/* description */
 	"boler@sourceforge.net",		/* author */
 	PURPLE_WEBSITE,				/* homepage */
 
-	NULL,					/* load */
+	ggp_load,				/* load */
 	NULL,					/* unload */
 	NULL,					/* destroy */
 
 	NULL,					/* ui_info */
 	&prpl_info,				/* extra_info */
 	NULL,					/* prefs_info */
 	ggp_actions,				/* actions */
 
@@ -2314,16 +2364,24 @@ static PurplePluginInfo info = {
 	NULL,
 	NULL
 };
 
 static void purple_gg_debug_handler(int level, const char * format, va_list args) {
 	PurpleDebugLevel purple_level;
 	char *msg = g_strdup_vprintf(format, args);
 
+	if (!msg) {
+		purple_debug_fatal("gg",
+			"failed to vprintf the following message: %s",
+			format ? format : "(null)\n");
+
+		return;
+	}
+
 	/* This is pretty pointless since the GG_DEBUG levels don't correspond to
 	 * the purple ones */
 	switch (level) {
 		case GG_DEBUG_FUNCTION:
 			purple_level = PURPLE_DEBUG_INFO;
 			break;
 		case GG_DEBUG_MISC:
 		case GG_DEBUG_NET:
--- a/libpurple/protocols/gg/gg.h
+++ b/libpurple/protocols/gg/gg.h
@@ -21,16 +21,17 @@
  */
 
 
 #ifndef _PURPLE_GG_H
 #define _PURPLE_GG_H
 
 #include <libgadu.h>
 #include "internal.h"
+#include "dnsquery.h"
 #include "search.h"
 #include "connection.h"
 
 
 #define PUBDIR_RESULTS_MAX 20
 
 
 typedef struct
@@ -60,13 +61,14 @@ typedef struct {
 	struct gg_session *session;
 	GGPToken *token;
 	GList *chats;
 	GGPSearches *searches;
 	int chats_count;
 	GList *pending_richtext_messages;
 	GHashTable *pending_images;
 	gboolean status_broadcasting; //When TRUE status is visible to all, when FALSE status is visible only to friends.
+	PurpleDnsQueryData *dns_query;
 } GGPInfo;
 
 #endif /* _PURPLE_GG_H */
 
 /* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/lib/common.c
+++ b/libpurple/protocols/gg/lib/common.c
@@ -1,9 +1,9 @@
-/* $Id: common.c 1101 2011-05-05 21:17:28Z wojtekka $ */
+/* $Id$ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
  *                          Robert J. Woźny <speedy@ziew.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
  *  2.1 as published by the Free Software Foundation.
@@ -19,110 +19,106 @@
  *  USA.
  */
 
 /**
  * \file common.c
  *
  * \brief Funkcje wykorzystywane przez różne moduły biblioteki
  */
-#ifndef _WIN32
-#  include <sys/types.h>
-#  include <sys/ioctl.h>
-#  include <sys/socket.h>
-#  include <netinet/in.h>
-#  include <arpa/inet.h>
-#  ifdef sun
-#    include <sys/filio.h>
-#  endif
-#endif
+
+#include "network.h"
+#include "strman.h"
+#include "fileio.h"
 
 #include <errno.h>
-#include <fcntl.h>
-#ifndef _WIN32
-#  include <netdb.h>
-#endif
 #include <stdarg.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#ifndef _MSC_VER
-#  include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "config.h"
+#include "libgadu.h"
+#include "internal.h"
+
+#ifndef GG_CONFIG_HAVE_VA_COPY
+#  ifdef GG_CONFIG_HAVE___VA_COPY
+#    define va_copy(dest, src) __va_copy((dest), (src))
+#  else
+/* Taka wersja va_copy() działa poprawnie tylko na platformach, które
+ * va_copy() de facto wcale nie potrzebują, np. MSVC. Definicja tylko dla
+ * przejrzystości kodu. */
+#    define va_copy(dest, src) (dest) = (src)
+#  endif
 #endif
 
-#include "libgadu.h"
-
 /**
  * \internal Odpowiednik funkcji \c vsprintf alokujący miejsce na wynik.
  *
  * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
  * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
  *
  * \param format Format wiadomości (zgodny z \c printf)
  * \param ap Lista argumentów (zgodna z \c printf)
  *
  * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
  *
  * \ingroup helper
  */
 char *gg_vsaprintf(const char *format, va_list ap)
 {
-	int size = 0;
+	int size;
 	char *buf = NULL;
-
-#ifdef GG_CONFIG_HAVE_VA_COPY
 	va_list aq;
 
-	va_copy(aq, ap);
-#else
-#  ifdef GG_CONFIG_HAVE___VA_COPY
-	va_list aq;
-
-	__va_copy(aq, ap);
-#  endif
-#endif
-
-#ifndef GG_CONFIG_HAVE_C99_VSNPRINTF
+#if !defined(GG_CONFIG_HAVE_C99_VSNPRINTF) && !defined(HAVE__VSCPRINTF)
 	{
-		int res;
+		int res = 0;
 		char *tmp;
 
 		size = 128;
 		do {
-			size *= 2;
-			if (!(tmp = realloc(buf, size + 1))) {
+			if (res > size) {
+				/* Jednak zachowanie zgodne z C99. */
+				size = res + 1;
+			} else {
+				size *= 2;
+			}
+
+			if (!(tmp = realloc(buf, size))) {
 				free(buf);
 				return NULL;
 			}
+
 			buf = tmp;
-			res = vsnprintf(buf, size, format, ap);
-		} while (res == size - 1 || res == -1);
+			va_copy(aq, ap);
+			res = vsnprintf(buf, size, format, aq);
+			va_end(aq);
+		} while (res >= size || res < 0);
 	}
 #else
+	va_copy(aq, ap);
+
+#  ifdef HAVE__VSCPRINTF
+	size = _vscprintf(format, aq) + 1;
+#  else
 	{
 		char tmp[2];
 
 		/* libce Solarisa przy buforze NULL zawsze zwracają -1, więc
 		 * musimy podać coś istniejącego jako cel printf()owania. */
-		size = vsnprintf(tmp, sizeof(tmp), format, ap);
-		if (!(buf = malloc(size + 1)))
-			return NULL;
+		size = vsnprintf(tmp, sizeof(tmp), format, aq) + 1;
 	}
-#endif
+#  endif
+	va_end(aq);
+	if (!(buf = malloc(size)))
+		return NULL;
 
-#ifdef GG_CONFIG_HAVE_VA_COPY
-	vsnprintf(buf, size + 1, format, aq);
-	va_end(aq);
-#else
-#  ifdef GG_CONFIG_HAVE___VA_COPY
-	vsnprintf(buf, size + 1, format, aq);
-	va_end(aq);
-#  else
-	vsnprintf(buf, size + 1, format, ap);
-#  endif
+	vsnprintf(buf, size, format, ap);
 #endif
 
 	return buf;
 }
 
 /**
  * \internal Odpowiednik funkcji \c sprintf alokujący miejsce na wynik.
  *
@@ -151,16 +147,18 @@ char *gg_saprintf(const char *format, ..
  * \internal Pobiera linię tekstu z bufora.
  *
  * Funkcja niszczy bufor źródłowy bezpowrotnie, dzieląc go na kolejne ciągi
  * znaków i obcina znaki końca linii.
  *
  * \param ptr Wskaźnik do zmiennej, która przechowuje aktualne położenie
  *            w analizowanym buforze
  *
+ * \note Funkcja nie jest już używana. Pozostała dla zachowania ABI.
+ *
  * \return Wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec
  *         bufora.
  */
 char *gg_get_line(char **ptr)
 {
 	char *foo, *res;
 
 	if (!ptr || !*ptr || !strcmp(*ptr, ""))
@@ -185,37 +183,46 @@ char *gg_get_line(char **ptr)
 }
 
 /**
  * \internal Czyta linię tekstu z gniazda.
  *
  * Funkcja czyta tekst znak po znaku, więc nie jest efektywna, ale dzięki
  * brakowi buforowania, nie koliduje z innymi funkcjami odczytu.
  *
+ * \note W przypadku zakończenia połączenia przez drugą stronę, ostatnia
+ * linia nie jest zwracana.
+ *
  * \param sock Deskryptor gniazda
  * \param buf Wskaźnik do bufora
  * \param length Długość bufora
  *
- * \return Zwraca \c buf jeśli się powiodło, lub \c NULL w przypadku błędu.
+ * \return Zwraca wskaźnik na koniec odebranej linii jeśli się powiodło,
+ * lub \c NULL w przypadku błędu.
  */
 char *gg_read_line(int sock, char *buf, int length)
 {
 	int ret;
 
 	if (!buf || length < 0)
 		return NULL;
 
 	for (; length > 1; buf++, length--) {
 		do {
-			if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR && errno != EAGAIN) {
-				gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno));
+			if ((ret = recv(sock, buf, 1, 0)) == -1 &&
+				errno != EINTR && errno != EAGAIN)
+			{
+				gg_debug(GG_DEBUG_MISC, "// gg_read_line() "
+					"error on read (errno=%d, %s)\n",
+					errno, strerror(errno));
 				*buf = 0;
 				return NULL;
 			} else if (ret == 0) {
-				gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n");
+				gg_debug(GG_DEBUG_MISC, "// gg_read_line() "
+					"eof reached\n");
 				*buf = 0;
 				return NULL;
 			}
 		} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
 
 		if (*buf == '\n') {
 			buf++;
 			break;
@@ -234,69 +241,73 @@ char *gg_read_line(int sock, char *buf, 
  * \param async Flaga asynchronicznego połączenia
  *
  * \return Deskryptor gniazda lub -1 w przypadku błędu
  *
  * \ingroup helper
  */
 int gg_connect(void *addr, int port, int async)
 {
-	int sock, one = 1, errno2;
+	int sock, errno2;
 	struct sockaddr_in sin;
 	struct in_addr *a = addr;
 	struct sockaddr_in myaddr;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n",
+		inet_ntoa(*a), port, async);
 
 	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno));
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed "
+			"(errno=%d, %s)\n", errno, strerror(errno));
 		return -1;
 	}
 
 	memset(&myaddr, 0, sizeof(myaddr));
 	myaddr.sin_family = AF_INET;
 
 	myaddr.sin_addr.s_addr = gg_local_ip;
 
 	if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno));
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed "
+			"(errno=%d, %s)\n", errno, strerror(errno));
 		errno2 = errno;
 		close(sock);
 		errno = errno2;
 		return -1;
 	}
 
 	if (async) {
-#ifdef FIONBIO
-		if (ioctl(sock, FIONBIO, &one) == -1) {
-#else
-		if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
-#endif
-			gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno));
+		if (!gg_fd_set_nonblocking(sock)) {
+			gg_debug(GG_DEBUG_MISC, "// gg_connect() can't set "
+				"nonblocking (errno=%d, %s)\n",
+				errno, strerror(errno));
 			errno2 = errno;
 			close(sock);
 			errno = errno2;
 			return -1;
 		}
 	}
 
 	memset(&sin, 0, sizeof(sin));
 	sin.sin_port = htons(port);
 	sin.sin_family = AF_INET;
 	sin.sin_addr.s_addr = a->s_addr;
 
 	if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
 		if (errno && (!async || errno != EINPROGRESS)) {
-			gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno));
+			gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() "
+				"failed (errno=%d, %s)\n",
+				errno, strerror(errno));
 			errno2 = errno;
 			close(sock);
 			errno = errno2;
 			return -1;
 		}
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
+		gg_debug(GG_DEBUG_MISC,
+			"// gg_connect() connect() in progress\n");
 	}
 
 	return sock;
 }
 
 /**
  * \internal Usuwa znaki końca linii.
  *
@@ -322,17 +333,17 @@ void gg_chomp(char *line)
 }
 
 /**
  * \internal Koduje ciąg znaków do postacji adresu HTTP.
  *
  * Zamienia znaki niedrukowalne, spoza ASCII i mające specjalne znaczenie
  * dla protokołu HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkową
  * wartością znaku.
- * 
+ *
  * \param str Ciąg znaków do zakodowania
  *
  * \return Zaalokowany bufor lub \c NULL w przypadku błędu.
  *
  * \ingroup helper
  */
 char *gg_urlencode(const char *str)
 {
@@ -340,27 +351,34 @@ char *gg_urlencode(const char *str)
 	const char hex[] = "0123456789abcdef";
 	const char *p;
 	unsigned int size = 0;
 
 	if (!str)
 		str = "";
 
 	for (p = str; *p; p++, size++) {
-		if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') || (*p == '.') || (*p == '-'))
+		if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
+			(*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') ||
+			(*p == '.') || (*p == '-'))
+		{
 			size += 2;
+		}
 	}
 
 	if (!(buf = malloc(size + 1)))
 		return NULL;
 
 	for (p = str, q = buf; *p; p++, q++) {
-		if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || (*p == '@') || (*p == '.') || (*p == '-'))
+		if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
+			(*p >= '0' && *p <= '9') || (*p == '@') ||
+			(*p == '.') || (*p == '-'))
+		{
 			*q = *p;
-		else {
+		} else {
 			if (*p == ' ')
 				*q = '+';
 			else {
 				*q++ = '%';
 				*q++ = hex[*p >> 4 & 15];
 				*q = hex[*p & 15];
 			}
 		}
@@ -389,17 +407,18 @@ int gg_http_hash(const char *format, ...
 {
 	unsigned int a, c, i, j;
 	va_list ap;
 	int b = -1;
 
 	va_start(ap, format);
 
 	for (j = 0; j < strlen(format); j++) {
-		char *arg, buf[16];
+		const char *arg;
+		char buf[16];
 
 		if (format[j] == 'u') {
 			snprintf(buf, sizeof(buf), "%d", va_arg(ap, uin_t));
 			arg = buf;
 		} else {
 			if (!(arg = va_arg(ap, char*)))
 				arg = "";
 		}
@@ -491,55 +510,55 @@ char *gg_base64_encode(const char *buf)
  * \return Zaalokowany bufor ze zdekodowanymi danymi
  *
  * \ingroup helper
  */
 char *gg_base64_decode(const char *buf)
 {
 	char *res, *save, *foo, val;
 	const char *end;
-	unsigned int index = 0;
+	unsigned int idx = 0;
 
 	if (!buf)
 		return NULL;
 
 	save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
 
 	if (!save)
 		return NULL;
 
 	end = buf + strlen(buf);
 
 	while (*buf && buf < end) {
 		if (*buf == '\r' || *buf == '\n') {
 			buf++;
 			continue;
 		}
-		if (!(foo = strchr(gg_base64_charset, *buf)))
+		if (!(foo = memchr(gg_base64_charset, *buf, sizeof(gg_base64_charset))))
 			foo = gg_base64_charset;
 		val = (int)(foo - gg_base64_charset);
 		buf++;
-		switch (index) {
+		switch (idx) {
 			case 0:
 				*res |= val << 2;
 				break;
 			case 1:
 				*res++ |= val >> 4;
 				*res |= val << 4;
 				break;
 			case 2:
 				*res++ |= val >> 2;
 				*res |= val << 6;
 				break;
 			case 3:
 				*res++ |= val;
 				break;
 		}
-		index++;
-		index %= 4;
+		idx++;
+		idx %= 4;
 	}
 	*res = 0;
 
 	return save;
 }
 
 /**
  * \internal Tworzy nagłówek autoryzacji serwera pośredniczącego.
@@ -553,108 +572,112 @@ char *gg_base64_decode(const char *buf)
 char *gg_proxy_auth(void)
 {
 	char *tmp, *enc, *out;
 	unsigned int tmp_size;
 
 	if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password)
 		return NULL;
 
-	if (!(tmp = malloc((tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2))))
+	tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2;
+	tmp = malloc(tmp_size);
+	if (!tmp)
 		return NULL;
 
 	snprintf(tmp, tmp_size, "%s:%s", gg_proxy_username, gg_proxy_password);
 
-	if (!(enc = gg_base64_encode(tmp))) {
+	enc = gg_base64_encode(tmp);
+	if (!enc) {
 		free(tmp);
 		return NULL;
 	}
 
 	free(tmp);
 
-	if (!(out = malloc(strlen(enc) + 40))) {
+	out = malloc(strlen(enc) + 40);
+	if (!out) {
 		free(enc);
 		return NULL;
 	}
 
 	snprintf(out, strlen(enc) + 40,  "Proxy-Authorization: Basic %s\r\n", enc);
 
 	free(enc);
 
 	return out;
 }
 
 /**
  * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej.
  */
 static const uint32_t gg_crc32_table[256] =
 {
-	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 
-	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 
-	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 
-	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 
-	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 
-	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 
-	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 
-	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 
-	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 
-	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 
-	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 
-	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 
-	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 
-	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 
-	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 
-	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 
-	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 
-	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 
-	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 
-	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 
-	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 
-	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 
-	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 
-	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 
-	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 
-	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 
-	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 
-	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 
-	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 
-	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 
-	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 
-	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 
-	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 
-	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 
-	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 
-	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 
-	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 
-	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 
-	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 
-	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 
-	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 
-	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 
-	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 
-	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 
-	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 
-	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 
-	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 
-	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 
-	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 
-	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 
-	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 
-	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 
-	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 
-	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 
-	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 
-	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 
-	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 
-	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 
-	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 
-	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 
-	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 
-	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 
-	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 
+	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
 };
 
 /**
  * Wyznacza sumę kontrolną CRC32.
  *
  * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeśli liczona
  *            jest suma kontrolna pierwszego bloku
@@ -671,16 +694,188 @@ uint32_t gg_crc32(uint32_t crc, const un
 	crc ^= 0xffffffffL;
 
 	while (len--)
 		crc = (crc >> 8) ^ gg_crc32_table[(crc ^ *buf++) & 0xff];
 
 	return crc ^ 0xffffffffL;
 }
 
+/**
+ * \internal Parsuje identyfikator użytkownika.
+ *
+ * \param str Ciąg tekstowy, zawierający identyfikator
+ * \param len Długość identyfikatora
+ *
+ * \return Identyfikator, lub 0, jeżeli nie udało się odczytać
+ */
+uin_t gg_str_to_uin(const char *str, int len)
+{
+	char buff[11];
+	char *endptr;
+	uin_t uin;
+
+	if (len < 0)
+		len = strlen(str);
+	if (len > 10)
+		return 0;
+	memcpy(buff, str, len);
+	buff[len] = '\0';
+
+	errno = 0;
+	uin = strtoul(buff, &endptr, 10);
+	if (errno == ERANGE || endptr[0] != '\0')
+		return 0;
+
+	return uin;
+}
+
+/**
+ * Szuka informacji o konferencji o podanym identyfikatorze.
+ *
+ * \param sess Struktura sesji
+ * \param id   Identyfikator konferencji
+ *
+ * \return Struktura z informacjami o konferencji
+ */
+gg_chat_list_t *gg_chat_find(struct gg_session *sess, uint64_t id)
+{
+	gg_chat_list_t *chat_list = sess->private_data->chat_list;
+
+	while (chat_list != NULL) {
+		if (chat_list->id == id)
+			return chat_list;
+		chat_list = chat_list->next;
+	}
+
+	return NULL;
+}
+
+/**
+ * \internal Aktualizuje informacje o konferencji.
+ *
+ * \param sess               Struktura sesji
+ * \param id                 Identyfikator konferencji
+ * \param version            Wersja informacji o konferencji
+ * \param participants       Lista uczestników konferencji
+ * \param participants_count Ilość uczestników konferencji
+ *
+ * \return Wartość równa 0, jeżeli zakończono powodzeniem
+ */
+int gg_chat_update(struct gg_session *sess, uint64_t id, uint32_t version,
+	const uin_t *participants, unsigned int participants_count)
+{
+	gg_chat_list_t *chat;
+	uin_t *participants_new;
+
+	if (participants_count >= ~(unsigned int)0 / sizeof(uin_t))
+		return -1;
+
+	chat = gg_chat_find(sess, id);
+
+	if (!chat) {
+		chat = malloc(sizeof(gg_chat_list_t));
+
+		if (!chat)
+			return -1;
+
+		memset(chat, 0, sizeof(gg_chat_list_t));
+		chat->id = id;
+		chat->next = sess->private_data->chat_list;
+		sess->private_data->chat_list = chat;
+	}
+
+	participants_new = realloc(chat->participants,
+		sizeof(uin_t) * participants_count);
+
+	if (participants_new == NULL)
+		return -1;
+
+	chat->version = version;
+	chat->participants = participants_new;
+	chat->participants_count = participants_count;
+	memcpy(chat->participants, participants,
+		sizeof(uin_t) * participants_count);
+
+	return 0;
+}
+
+void gg_connection_failure(struct gg_session *gs, struct gg_event *ge,
+	enum gg_failure_t failure)
+{
+	gg_close(gs);
+
+	if (ge != NULL) {
+		ge->type = GG_EVENT_CONN_FAILED;
+		ge->event.failure = failure;
+	}
+	gs->state = GG_STATE_IDLE;
+}
+
+time_t gg_server_time(struct gg_session *gs)
+{
+	time_t now = time(NULL);
+
+	if (gs == NULL || gs->private_data == NULL) {
+		gg_debug_session(gs, GG_DEBUG_ERROR, "time diff data is not "
+			"accessible\n");
+		return now;
+	}
+
+	return now + gs->private_data->time_diff;
+}
+
+void gg_strarr_free(char **strarr)
+{
+	char **it;
+
+	if (strarr == NULL)
+		return;
+
+	for (it = strarr; *it != NULL; it++)
+		free(*it);
+	free(strarr);
+}
+
+char ** gg_strarr_dup(char **strarr)
+{
+	size_t i, len, size;
+	char **it, **out;
+
+	if (strarr == NULL)
+		return NULL;
+
+	len = 0;
+	for (it = strarr; *it != NULL; it++)
+		len++;
+
+	size = (len + 1) * sizeof(char*);
+	out = malloc(size);
+
+	if (out == NULL) {
+		gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_strarr_dup() "
+			"not enough memory for the array\n");
+		return NULL;
+	}
+	memset(out, 0, size);
+
+	for (i = 0; i < len; i++) {
+		out[i] = strdup(strarr[i]);
+		if (out[i] == NULL) {
+			gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR,
+				"// gg_strarr_dup() "
+				"not enough memory for the array element\n");
+			gg_strarr_free(out);
+			return NULL;
+		}
+	}
+
+	return out;
+}
+
 /*
  * Local variables:
  * c-indentation-style: k&r
  * c-basic-offset: 8
  * indent-tabs-mode: notnil
  * End:
  *
  * vim: shiftwidth=8:
deleted file mode 100644
--- a/libpurple/protocols/gg/lib/compat.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* $Id: compat.h 506 2008-01-14 22:15:05Z wojtekka $ */
-
-/*
- *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Woźny <speedy@ziew.org>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU Lesser General Public License Version
- *  2.1 as published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
- *  USA.
- */
-
-/**
- * \file compat.h
- *
- * \brief Makra zapewniające kompatybilność API na różnych systemach
- */
-
-#ifndef __COMPAT_H
-#define __COMPAT_H
-
-#ifdef sun
-#  define INADDR_NONE   ((in_addr_t) 0xffffffff)
-#endif
-
-#endif
rename from libpurple/protocols/gg/lib/libgadu-config.h
rename to libpurple/protocols/gg/lib/config.h
--- a/libpurple/protocols/gg/lib/libgadu-config.h
+++ b/libpurple/protocols/gg/lib/config.h
@@ -1,75 +1,93 @@
-/* Local libgadu configuration. */
+/* Local libgadu configuration file. */
+
+#undef printf
 
-/*#include "config.h" */
+/* libpurple's config */
+#include <config.h>
 
-#ifndef __GG_LIBGADU_CONFIG_H
-#define __GG_LIBGADU_CONFIG_H
+#define GG_LIBGADU_VERSION "1.12.0"
 
 /* Defined if libgadu was compiled for bigendian machine. */
-#undef __GG_LIBGADU_BIGENDIAN
+#undef GG_CONFIG_BIGENDIAN
 #ifdef WORDS_BIGENDIAN
-#define __GG_LIBGADU_BIGENDIAN
-#endif /* WORDS_BIGENDIAN */
-
-#ifndef _WIN32
-/* Defined if this machine has va_copy(). */
-#define __GG_LIBGADU_HAVE_VA_COPY
-
-/* Defined if this machine has __va_copy(). */
-#define __GG_LIBGADU_HAVE___VA_COPY
+#  define GG_CONFIG_BIGENDIAN
 #endif
 
-/* Defined if this machine supports long long. */
-#undef __GG_LIBGADU_HAVE_LONG_LONG
-#ifdef HAVE_LONG_LONG
-#define __GG_LIBGADU_HAVE_LONG_LONG
-#endif /* HAVE_LONG_LONG */
+/* Defined if this machine has gethostbyname_r(). */
+#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+
+/* Define to 1 if you have the `_exit' function. */
+#define HAVE__EXIT 1
+
+/* Defined if libgadu was compiled and linked with fork support. */
+#undef GG_CONFIG_HAVE_FORK
+#ifndef _WIN32
+#  define GG_CONFIG_HAVE_FORK
+#endif
 
 /* Defined if libgadu was compiled and linked with pthread support. */
-/* We don't like pthreads. */
-#undef __GG_LIBGADU_HAVE_PTHREAD
+/* We don't use pthreads - they may not be safe. */
+#undef GG_CONFIG_HAVE_PTHREAD
 
-/* Defined if libgadu was compiled and linked with GnuTLS encryption support. */
-#ifdef HAVE_GNUTLS
-#  define GG_CONFIG_HAVE_GNUTLS
-#else
-#  undef GG_CONFIG_HAVE_GNUTLS
+/* Defined if this machine has C99-compiliant vsnprintf(). */
+#undef HAVE_C99_VSNPRINTF
+#ifndef _WIN32
+#  define HAVE_C99_VSNPRINTF
 #endif
 
-/* Defined if libgadu was compiled and linked with TLS support. */
-/* Always undefined in Purple. */
-#undef __GG_LIBGADU_HAVE_OPENSSL
+/* Defined if this machine has va_copy(). */
+#define GG_CONFIG_HAVE_VA_COPY
 
-/* Include file containing uintXX_t declarations. */
-#if HAVE_STDINT_H
-#include <stdint.h>
+/* Defined if this machine has __va_copy(). */
+#define GG_CONFIG_HAVE___VA_COPY
+
+/* Defined if this machine supports long long. */
+#undef GG_CONFIG_HAVE_LONG_LONG
+#ifdef HAVE_LONG_LONG
+#  define GG_CONFIG_HAVE_LONG_LONG
 #endif
 
-/* Defined if this machine has C99-compiliant vsnprintf(). */
-#ifndef _WIN32
-#define __GG_LIBGADU_HAVE_C99_VSNPRINTF
-#else
-#undef __GG_LIBGADU_HAVE_C99_VSNPRINTF
+/* Defined if libgadu was compiled and linked with GnuTLS support. */
+#undef GG_CONFIG_HAVE_GNUTLS
+#if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_2_10)
+#  define GG_CONFIG_HAVE_GNUTLS
 #endif
 
-#define vnsprintf g_vnsprintf
+/* Defined if libgadu was compiled and linked with OpenSSL support. */
+/* OpenSSL cannot be used with libpurple due to licence type. */
+#undef GG_CONFIG_HAVE_OPENSSL
+
+/* Defined if libgadu was compiled and linked with zlib support. */
+#define GG_CONFIG_HAVE_ZLIB
 
-#ifdef _WIN32
-#define random (long) rand
+/* Defined if uintX_t types are defined in <stdint.h>. */
+#undef GG_CONFIG_HAVE_STDINT_H
+#ifdef HAVE_STDINT_H
+#  define GG_CONFIG_HAVE_STDINT_H
+#endif
+
+/* Defined if uintX_t types are defined in <inttypes.h>. */
+#undef GG_CONFIG_HAVE_INTTYPES_H
+#ifdef HAVE_INTTYPES_H
+#  define GG_CONFIG_HAVE_INTTYPES_H
 #endif
 
-#ifdef _MSC_VER
-typedef unsigned __int32 uint32_t;
-typedef unsigned __int16 uint16_t;
-typedef unsigned __int8  uint8_t;
-#define snprintf _snprintf
-#include <win32dep.h>
-#define EINPROGRESS WSAEINPROGRESS
-#define ECONNRESET WSAECONNRESET
-#define ENOTCONN WSAENOTCONN
-#define strncasecmp strnicmp
-#define strcasecmp stricmp
-#define ssize_t SSIZE_T
+/* Defined if uintX_t types are defined in <sys/types.h>. */
+#undef GG_CONFIG_HAVE_SYS_TYPES_H
+#ifdef HAVE_SYS_TYPES_H
+#  define GG_CONFIG_HAVE_SYS_TYPES_H
 #endif
 
-#endif /* __GG_LIBGADU_CONFIG_H */
+/* Defined if this machine has uint64_t. */
+#define GG_CONFIG_HAVE_UINT64_T
+
+/* Defined if libgadu is GPL compliant (was not linked with OpenSSL or any
+ * other non-GPL compliant library support). */
+#define GG_CONFIG_IS_GPL_COMPLIANT
+
+/* Defined if libgadu uses system defalt trusted CAs. */
+#define GG_CONFIG_SSL_SYSTEM_TRUST
+
+/* Defined if libgadu is GPL compliant (was not linked with OpenSSL or any
+   other non-GPL compliant library support). */
+#define GG_CONFIG_IS_GPL_COMPLIANT
--- a/libpurple/protocols/gg/lib/dcc.c
+++ b/libpurple/protocols/gg/lib/dcc.c
@@ -1,9 +1,9 @@
-/* $Id: dcc.c 1023 2010-11-16 18:27:35Z wojtekka $ */
+/* $Id$ */
 
 /*
  *  (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
  *                          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
@@ -21,64 +21,42 @@
  */
 
 /**
  * \file dcc.c
  *
  * \brief Obsługa połączeń bezpośrednich do wersji Gadu-Gadu 6.x
  */
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifndef _WIN32
-#  include <sys/ioctl.h>
-#  include <sys/socket.h>
-#  include <netinet/in.h>
-#  include <arpa/inet.h>
-#  ifdef sun
-#    include <sys/filio.h>
-#  endif
-#endif
+#include "fileio.h"
+#include "network.h"
 
 #include <ctype.h>
 #include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
 #include <string.h>
-#include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 
-#include "compat.h"
 #include "libgadu.h"
-
-#ifndef GG_DEBUG_DISABLE
+#include "debug.h"
+#include "internal.h"
 
 /**
  * \internal Przekazuje zawartość pakietu do odpluskwiania.
  *
  * \param prefix Prefiks informacji
  * \param fd Deskryptor gniazda
  * \param buf Bufor z danumi
  * \param size Rozmiar bufora z danymi
  */
 static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size)
 {
-	unsigned int i;
-
 	gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size);
-
-	for (i = 0; i < size; i++)
-		gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]);
-
+	gg_debug_dump(NULL, GG_DEBUG_DUMP, buf, size);
 	gg_debug(GG_DEBUG_MISC, "\n");
 }
-#else
-#define gg_dcc_debug_data(a,b,c,d) do { } while (0)
-#endif
 
 /**
  * Wysyła żądanie zwrotnego połączenia bezpośredniego.
  *
  * Funkcję wykorzystuje się, jeśli nie ma możliwości połączenia się z odbiorcą
  * pliku lub rozmowy głosowej. Po otrzymaniu żądania druga strona spróbuje
  * nawiązać zwrotne połączenie bezpośrednie z nadawcą.
  * gg_dcc_request()
@@ -87,46 +65,39 @@ static void gg_dcc_debug_data(const char
  * \param uin Numer odbiorcy
  *
  * \return Patrz \c gg_send_message_ctcp()
  *
  * \ingroup dcc6
  */
 int gg_dcc_request(struct gg_session *sess, uin_t uin)
 {
-	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (unsigned char*) "\002", 1);
+	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char*) "\002", 1);
 }
 
 /**
  * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32.
  *
  * \note Funkcja działa jedynie gdy kompilator obsługuje typ danych
  * \c long \c long.
  *
  * \param ut Czas w postaci uniksowej
  * \param ft Czas w postaci API WIN32
  */
 static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
 {
-#ifdef GG_CONFIG_HAVE_LONG_LONG
-	unsigned long long tmp;
+	uint64_t tmp;
 
 	tmp = ut;
 	tmp += 11644473600LL;
 	tmp *= 10000000LL;
 
-#ifndef GG_CONFIG_BIGENDIAN
-	ft[0] = (uint32_t) tmp;
-	ft[1] = (uint32_t) (tmp >> 32);
-#else
-	ft[0] = gg_fix32((uint32_t) (tmp >> 32));
-	ft[1] = gg_fix32((uint32_t) tmp);
-#endif
+	tmp = gg_fix64(tmp);
 
-#endif
+	memcpy(ft, &tmp, sizeof(tmp));
 }
 
 /**
  * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
  *
  * \note Większą funkcjonalność zapewnia funkcja \c gg_dcc_fill_file_info2().
  *
  * \param d Struktura połączenia
@@ -162,29 +133,34 @@ int gg_dcc_fill_file_info2(struct gg_dcc
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename);
 
 	if (!d || d->type != GG_SESSION_DCC_SEND) {
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n");
 		errno = EINVAL;
 		return -1;
 	}
 
-	if (stat(local_filename, &st) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno));
+	if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno));
+		return -1;
+	}
+
+	if (fstat(d->file_fd, &st) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() "
+			"fstat() failed (%s)\n", strerror(errno));
+		close(d->file_fd);
+		d->file_fd = -1;
 		return -1;
 	}
 
 	if ((st.st_mode & S_IFDIR)) {
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n");
 		errno = EINVAL;
-		return -1;
-	}
-
-	if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno));
+		close(d->file_fd);
+		d->file_fd = -1;
 		return -1;
 	}
 
 	memset(&d->file_info, 0, sizeof(d->file_info));
 
 	if (!(st.st_mode & S_IWUSR))
 		d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY);
 
@@ -233,17 +209,18 @@ int gg_dcc_fill_file_info2(struct gg_dcc
 			*q = 140;
 		} else if (*q == 159) {
 			*q = 143;
 		} else if (*q == 191) {
 			*q = 175;
 		}
 	}
 
-	gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename);
+	gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\","
+		" dos name \"%s\"\n", name, d->file_info.short_filename);
 	strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
 
 	return 0;
 }
 
 /**
  * \internal Rozpoczyna połączenie bezpośrednie z danym klientem.
  *
@@ -257,17 +234,19 @@ int gg_dcc_fill_file_info2(struct gg_dcc
  */
 static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
 {
 	struct gg_dcc *d = NULL;
 	struct in_addr addr;
 
 	addr.s_addr = ip;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET");
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %u, %u, "
+		"%s);\n", inet_ntoa(addr), port, my_uin, peer_uin,
+		(type == GG_SESSION_DCC_SEND) ? "SEND" : "GET");
 
 	if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) {
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n");
 		errno = EINVAL;
 		return NULL;
 	}
 
 	if (!(d = (void*) calloc(1, sizeof(*d)))) {
@@ -367,19 +346,19 @@ void gg_dcc_set_type(struct gg_dcc *d, i
 	d->type = type;
 	d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST;
 }
 
 /**
  * \internal Funkcja zwrotna połączenia bezpośredniego.
  *
  * Pole \c callback struktury \c gg_dcc zawiera wskaźnik do tej funkcji.
- * Wywołuje ona \c gg_watch_fd() i zachowuje wynik w polu \c event.
+ * Wywołuje ona \c gg_dcc_watch_fd() i zachowuje wynik w polu \c event.
  *
- * \note Funkcjonalność funkcjo zwrotnej nie jest już wspierana.
+ * \note Funkcjonalność funkcji zwrotnej nie jest już wspierana.
  *
  * \param d Struktura połączenia
  *
  * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 static int gg_dcc_callback(struct gg_dcc *d)
 {
 	struct gg_event *e = gg_dcc_watch_fd(d);
@@ -392,43 +371,47 @@ static int gg_dcc_callback(struct gg_dcc
 /**
  * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich.
  *
  * Funkcja przywiązuje gniazdo do pierwszego wolnego portu TCP.
  *
  * \param uin Własny numer
  * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
  *
+ * \note Ze względu na możliwość podania wartości -1 do parametru będącego
+ *       16-bitową liczbą bez znaku, port 65535 nie jest dostępny.
+ *
  * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
  *
  * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
 {
 	struct gg_dcc *c;
-	struct sockaddr_in sin;
 	int sock, bound = 0, errno2;
 
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port);
 
 	if (!uin) {
 		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n");
 		errno = EINVAL;
 		return NULL;
 	}
 
 	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
 		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno));
 		return NULL;
 	}
 
-	if (port == 0 || port == (uint16_t)-1) /* XXX: port is unsigned */
+	if (port == 0 || port == (uint16_t)-1)
 		port = GG_DEFAULT_DCC_PORT;
 
 	while (!bound) {
+		struct sockaddr_in sin;
+
 		memset(&sin, 0, sizeof(sin));
 		sin.sin_family = AF_INET;
 		sin.sin_addr.s_addr = INADDR_ANY;
 		sin.sin_port = htons(port);
 
 		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port);
 		if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin)))
 			bound = 1;
@@ -455,16 +438,17 @@ struct gg_dcc *gg_dcc_socket_create(uin_
 		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n");
 		close(sock);
 		return NULL;
 	}
 	memset(c, 0, sizeof(*c));
 
 	c->port = c->id = port;
 	c->fd = sock;
+	c->file_fd = -1;
 	c->type = GG_SESSION_DCC_SOCKET;
 	c->uin = uin;
 	c->timeout = -1;
 	c->state = GG_STATE_LISTENING;
 	c->check = GG_CHECK_READ;
 	c->callback = gg_dcc_callback;
 	c->destroy = gg_dcc_free;
 
@@ -495,49 +479,53 @@ int gg_dcc_voice_send(struct gg_dcc *d, 
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n");
 		errno = EINVAL;
 		return -1;
 	}
 
 	packet.type = 0x03; /* XXX */
 	packet.length = gg_fix32(length);
 
-	if (write(d->fd, &packet, sizeof(packet)) < (signed)sizeof(packet)) {
-		gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n");
+	if (send(d->fd, &packet, sizeof(packet), 0) < (signed)sizeof(packet)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() send() failed\n");
 		return -1;
 	}
 	gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet));
 
-	if (write(d->fd, buf, length) < length) {
-		gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n");
+	if (send(d->fd, buf, length, 0) < length) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() send() failed\n");
 		return -1;
 	}
 	gg_dcc_debug_data("write", d->fd, buf, length);
 
 	return 0;
 }
 
 /**
  * \internal Odbiera dane z połączenia bezpośredniego z obsługą błędów.
  *
  * \param fd Deskryptor gniazda
  * \param buf Bufor na dane
  * \param size Rozmiar bufora na dane
  */
 #define gg_dcc_read(fd, buf, size) \
 { \
-	int tmp = read(fd, buf, size); \
+	int _tmp = recv(fd, buf, size, 0); \
 	\
-	if (tmp < (int) size) { \
-		if (tmp == -1) { \
-			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); \
-		} else if (tmp == 0) { \
-			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); \
+	if (_tmp < (int) size) { \
+		if (_tmp == -1) { \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed " \
+				"(errno=%d, %s)\n", errno, strerror(errno)); \
+		} else if (_tmp == 0) { \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed, " \
+				"connection broken\n"); \
 		} else { \
-			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (%d bytes, %d needed)\n", tmp, size); \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed " \
+				"(%d bytes, %" GG_SIZE_FMT " needed)\n", \
+				_tmp, size); \
 		} \
 		e->type = GG_EVENT_DCC_ERROR; \
 		e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
 		return e; \
 	} \
 	gg_dcc_debug_data("read", fd, buf, size); \
 }
 
@@ -545,24 +533,27 @@ int gg_dcc_voice_send(struct gg_dcc *d, 
  * \internal Wysyła dane do połączenia bezpośredniego z obsługą błędów.
  *
  * \param fd Deskryptor gniazda
  * \param buf Bufor z danymi
  * \param size Rozmiar bufora z danymi
  */
 #define gg_dcc_write(fd, buf, size) \
 { \
-	int tmp; \
+	int write_res; \
 	gg_dcc_debug_data("write", fd, buf, size); \
-	tmp = write(fd, buf, size); \
-	if (tmp < (int) size) { \
-		if (tmp == -1) { \
-			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); \
+	write_res = send(fd, buf, size, 0); \
+	if (write_res < (int) size) { \
+		if (write_res == -1) { \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() " \
+				"failed (errno=%d, %s)\n", errno, strerror(errno)); \
 		} else { \
-			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d needed, %d done)\n", size, tmp); \
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() " \
+				"failed (%" GG_SIZE_FMT " needed, %d done)\n", \
+				size, write_res); \
 		} \
 		e->type = GG_EVENT_DCC_ERROR; \
 		e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
 		return e; \
 	} \
 }
 
 /**
@@ -580,48 +571,55 @@ int gg_dcc_voice_send(struct gg_dcc *d, 
  */
 struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
 {
 	struct gg_event *e;
 	int foo;
 
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h);
 
-	if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) {
+	if (!h || (h->type != GG_SESSION_DCC &&
+		h->type != GG_SESSION_DCC_SOCKET &&
+		h->type != GG_SESSION_DCC_SEND &&
+		h->type != GG_SESSION_DCC_GET &&
+		h->type != GG_SESSION_DCC_VOICE))
+	{
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n");
 		errno = EINVAL;
 		return NULL;
 	}
 
 	if (!(e = (void*) calloc(1, sizeof(*e)))) {
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n");
 		return NULL;
 	}
 
 	e->type = GG_EVENT_NONE;
 
 	if (h->type == GG_SESSION_DCC_SOCKET) {
 		struct sockaddr_in sin;
 		struct gg_dcc *c;
-		int fd, one = 1;
-		unsigned int sin_len = sizeof(sin);
+		int fd;
+		socklen_t sin_len = sizeof(sin);
 
 		if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
-			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno));
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't "
+				"accept() new connection (errno=%d, %s)\n",
+				errno, strerror(errno));
 			return e;
 		}
 
-		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct "
+			"connection from %s:%d\n", inet_ntoa(sin.sin_addr),
+			htons(sin.sin_port));
 
-#ifdef FIONBIO
-		if (ioctl(fd, FIONBIO, &one) == -1) {
-#else
-		if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
-#endif
-			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno));
+		if (!gg_fd_set_nonblocking(fd)) {
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set"
+				" nonblocking (errno=%d, %s)\n",
+				errno, strerror(errno));
 			close(fd);
 			e->type = GG_EVENT_DCC_ERROR;
 			e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 			return e;
 		}
 
 		if (!(c = (void*) calloc(1, sizeof(*c)))) {
 			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n");
@@ -640,36 +638,39 @@ struct gg_event *gg_dcc_watch_fd(struct 
 		c->remote_addr = sin.sin_addr.s_addr;
 		c->remote_port = ntohs(sin.sin_port);
 
 		e->type = GG_EVENT_DCC_NEW;
 		e->event.dcc_new = c;
 
 		return e;
 	} else {
-		struct gg_dcc_tiny_packet tiny;
-		struct gg_dcc_small_packet small;
-		struct gg_dcc_big_packet big;
+		struct gg_dcc_tiny_packet tiny_pkt;
+		struct gg_dcc_small_packet small_pkt;
+		struct gg_dcc_big_packet big_pkt;
 		int size, tmp, res;
-		unsigned int utmp, res_size = sizeof(res);
+		unsigned int utmp;
+		socklen_t res_size = sizeof(res);
 		char buf[1024], ack[] = "UDAG";
 
 		struct gg_dcc_file_info_packet {
 			struct gg_dcc_big_packet big;
 			struct gg_file_info file_info;
 		} GG_PACKED;
 		struct gg_dcc_file_info_packet file_info_packet;
 
 		switch (h->state) {
 			case GG_STATE_READING_UIN_1:
 			case GG_STATE_READING_UIN_2:
 			{
 				uin_t uin;
 
-				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
+				gg_debug(GG_DEBUG_MISC,
+					"// gg_dcc_watch_fd() GG_READING_UIN_%d\n",
+					(h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
 
 				gg_dcc_read(h->fd, &uin, sizeof(uin));
 
 				if (h->state == GG_STATE_READING_UIN_1) {
 					h->state = GG_STATE_READING_UIN_2;
 					h->check = GG_CHECK_READ;
 					h->timeout = GG_DEFAULT_TIMEOUT;
 					h->peer_uin = gg_fix32(uin);
@@ -682,32 +683,32 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				}
 
 				return e;
 			}
 
 			case GG_STATE_SENDING_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
 
-				gg_dcc_write(h->fd, ack, 4);
+				gg_dcc_write(h->fd, ack, (size_t)4);
 
 				h->state = GG_STATE_READING_TYPE;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				return e;
 
 			case GG_STATE_READING_TYPE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n");
 
-				gg_dcc_read(h->fd, &small, sizeof(small));
+				gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt));
 
-				small.type = gg_fix32(small.type);
+				small_pkt.type = gg_fix32(small_pkt.type);
 
-				switch (small.type) {
+				switch (small_pkt.type) {
 					case 0x0003:	/* XXX */
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n");
 						h->type = GG_SESSION_DCC_SEND;
 						h->state = GG_STATE_SENDING_FILE_INFO;
 						h->check = GG_CHECK_WRITE;
 						h->timeout = GG_DEFAULT_TIMEOUT;
 
 						e->type = GG_EVENT_DCC_CALLBACK;
@@ -720,31 +721,32 @@ struct gg_event *gg_dcc_watch_fd(struct 
 						h->state = GG_STATE_READING_REQUEST;
 						h->check = GG_CHECK_READ;
 						h->timeout = GG_DEFAULT_TIMEOUT;
 						h->incoming = 1;
 
 						break;
 
 					default:
-						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %ld\n", small.type, h->peer_uin);
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type "
+							"(%.4x) from %u\n", small_pkt.type, h->peer_uin);
 						e->type = GG_EVENT_DCC_ERROR;
 						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 				}
 
 				return e;
 
 			case GG_STATE_READING_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n");
 
-				gg_dcc_read(h->fd, &small, sizeof(small));
+				gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt));
 
-				small.type = gg_fix32(small.type);
+				small_pkt.type = gg_fix32(small_pkt.type);
 
-				switch (small.type) {
+				switch (small_pkt.type) {
 					case 0x0001:	/* XXX */
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n");
 						h->state = GG_STATE_READING_FILE_INFO;
 						h->check = GG_CHECK_READ;
 						h->timeout = GG_DEFAULT_TIMEOUT;
 						break;
 
 					case 0x0003:	/* XXX */
@@ -753,17 +755,19 @@ struct gg_event *gg_dcc_watch_fd(struct 
 						h->check = GG_CHECK_WRITE;
 						h->timeout = GG_DCC_TIMEOUT_VOICE_ACK;
 						h->type = GG_SESSION_DCC_VOICE;
 						e->type = GG_EVENT_DCC_NEED_VOICE_ACK;
 
 						break;
 
 					default:
-						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin);
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown "
+							"dcc request (%.4x) from %u\n",
+							small_pkt.type, h->peer_uin);
 						e->type = GG_EVENT_DCC_ERROR;
 						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 				}
 
 				return e;
 
 			case GG_STATE_READING_FILE_INFO:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n");
@@ -781,78 +785,81 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 				e->type = GG_EVENT_DCC_NEED_FILE_ACK;
 
 				return e;
 
 			case GG_STATE_SENDING_FILE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n");
 
-				big.type = gg_fix32(0x0006);	/* XXX */
-				big.dunno1 = gg_fix32(h->offset);
-				big.dunno2 = 0;
+				big_pkt.type = gg_fix32(0x0006);	/* XXX */
+				big_pkt.dunno1 = gg_fix32(h->offset);
+				big_pkt.dunno2 = 0;
 
-				gg_dcc_write(h->fd, &big, sizeof(big));
+				gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt));
 
 				h->state = GG_STATE_READING_FILE_HEADER;
-				h->chunk_size = sizeof(big);
+				h->chunk_size = sizeof(big_pkt);
 				h->chunk_offset = 0;
-				if (!(h->chunk_buf = malloc(sizeof(big)))) {
+				if (!(h->chunk_buf = malloc(sizeof(big_pkt)))) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
 					free(e);
 					return NULL;
 				}
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				return e;
 
 			case GG_STATE_SENDING_VOICE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n");
 
-				tiny.type = 0x01;	/* XXX */
+				tiny_pkt.type = 0x01;	/* XXX */
 
-				gg_dcc_write(h->fd, &tiny, sizeof(tiny));
+				gg_dcc_write(h->fd, &tiny_pkt, sizeof(tiny_pkt));
 
 				h->state = GG_STATE_READING_VOICE_HEADER;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				h->offset = 0;
 
 				return e;
 
 			case GG_STATE_READING_FILE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n");
 
-				tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+				tmp = recv(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0);
 
 				if (tmp == -1) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno));
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() "
+						"failed (errno=%d, %s)\n", errno, strerror(errno));
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
 					return e;
 				}
 
-				gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+				gg_dcc_debug_data("read", h->fd,
+					h->chunk_buf + h->chunk_offset,
+					h->chunk_size - h->chunk_offset);
 
 				h->chunk_offset += tmp;
 
 				if (h->chunk_offset < h->chunk_size)
 					return e;
 
-				memcpy(&big, h->chunk_buf, sizeof(big));
+				memcpy(&big_pkt, h->chunk_buf, sizeof(big_pkt));
 				free(h->chunk_buf);
 				h->chunk_buf = NULL;
 
-				big.type = gg_fix32(big.type);
-				h->chunk_size = gg_fix32(big.dunno1);
+				big_pkt.type = gg_fix32(big_pkt.type);
+				h->chunk_size = gg_fix32(big_pkt.dunno1);
 				h->chunk_offset = 0;
 
-				if (big.type == 0x0005)	{ /* XXX */
+				if (big_pkt.type == 0x0005)	{ /* XXX */
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n");
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_REFUSED;
 					return e;
 				}
 
 				if (h->chunk_size == 0) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n");
@@ -865,52 +872,55 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				h->timeout = GG_DEFAULT_TIMEOUT;
 				h->established = 1;
 
 				return e;
 
 			case GG_STATE_READING_VOICE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n");
 
-				gg_dcc_read(h->fd, &tiny, sizeof(tiny));
+				gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt));
 
-				switch (tiny.type) {
+				switch (tiny_pkt.type) {
 					case 0x03:	/* XXX */
 						h->state = GG_STATE_READING_VOICE_SIZE;
 						h->check = GG_CHECK_READ;
 						h->timeout = GG_DEFAULT_TIMEOUT;
 						h->established = 1;
 						break;
 					case 0x04:	/* XXX */
-						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n");
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
+							"peer breaking connection\n");
 						/* XXX zwracać odpowiedni event */
 					default:
-						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type);
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
+							"unknown request (%.2x)\n", tiny_pkt.type);
 						e->type = GG_EVENT_DCC_ERROR;
 						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 				}
 
 				return e;
 
 			case GG_STATE_READING_VOICE_SIZE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n");
 
-				gg_dcc_read(h->fd, &small, sizeof(small));
+				gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt));
+
+				small_pkt.type = gg_fix32(small_pkt.type);
 
-				small.type = gg_fix32(small.type);
-
-				if (small.type < 16 || small.type > sizeof(buf)) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type);
+				if (small_pkt.type < 16 || small_pkt.type > sizeof(buf)) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
+						"invalid voice frame size (%d)\n", small_pkt.type);
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
 
 					return e;
 				}
 
-				h->chunk_size = small.type;
+				h->chunk_size = small_pkt.type;
 				h->chunk_offset = 0;
 
 				if (!(h->voice_buf = malloc(h->chunk_size))) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
 					free(e);
 					return NULL;
 				}
 
@@ -918,22 +928,25 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				return e;
 
 			case GG_STATE_READING_VOICE_DATA:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n");
 
-				tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+				tmp = recv(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0);
 				if (tmp < 1) {
 					if (tmp == -1) {
-						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno));
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
+							"recv() failed (errno=%d, %s)\n",
+							errno, strerror(errno));
 					} else {
-						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n");
+						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
+							"recv() failed, connection broken\n");
 					}
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
 					return e;
 				}
 
 				gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp);
 
@@ -955,17 +968,20 @@ struct gg_event *gg_dcc_watch_fd(struct 
 			case GG_STATE_CONNECTING:
 			{
 				uin_t uins[2];
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n");
 
 				res = 0;
 				if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res));
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() connection failed "
+						"(fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n",
+						h->fd, errno, strerror(errno), foo, res, strerror(res));
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 					return e;
 				}
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n");
 
 				uins[0] = gg_fix32(h->uin);
@@ -978,17 +994,17 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				return e;
 			}
 
 			case GG_STATE_READING_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n");
 
-				gg_dcc_read(h->fd, buf, 4);
+				gg_dcc_read(h->fd, buf, (size_t)4);
 
 				if (strncmp(buf, ack, 4)) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
 
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 					return e;
 				}
@@ -997,32 +1013,33 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				h->timeout = GG_DEFAULT_TIMEOUT;
 				h->state = GG_STATE_SENDING_REQUEST;
 
 				return e;
 
 			case GG_STATE_SENDING_VOICE_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n");
 
-				small.type = gg_fix32(0x0003);
+				small_pkt.type = gg_fix32(0x0003);
 
-				gg_dcc_write(h->fd, &small, sizeof(small));
+				gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt));
 
 				h->state = GG_STATE_READING_VOICE_ACK;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				return e;
 
 			case GG_STATE_SENDING_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n");
 
-				small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002);	/* XXX */
+				small_pkt.type = (h->type == GG_SESSION_DCC_GET) ?
+					gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */
 
-				gg_dcc_write(h->fd, &small, sizeof(small));
+				gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt));
 
 				switch (h->type) {
 					case GG_SESSION_DCC_GET:
 						h->state = GG_STATE_READING_REQUEST;
 						h->check = GG_CHECK_READ;
 						h->timeout = GG_DEFAULT_TIMEOUT;
 						break;
 
@@ -1047,19 +1064,19 @@ struct gg_event *gg_dcc_watch_fd(struct 
 			case GG_STATE_SENDING_FILE_INFO:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
 
 				if (h->file_fd == -1) {
 					e->type = GG_EVENT_DCC_NEED_FILE_INFO;
 					return e;
 				}
 
-				small.type = gg_fix32(0x0001);	/* XXX */
+				small_pkt.type = gg_fix32(0x0001);	/* XXX */
 
-				gg_dcc_write(h->fd, &small, sizeof(small));
+				gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt));
 
 				file_info_packet.big.type = gg_fix32(0x0003);	/* XXX */
 				file_info_packet.big.dunno1 = 0;
 				file_info_packet.big.dunno2 = 0;
 
 				memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info));
 
 				/* zostają teraz u nas, więc odwracamy z powrotem */
@@ -1072,36 +1089,38 @@ struct gg_event *gg_dcc_watch_fd(struct 
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
 
 				return e;
 
 			case GG_STATE_READING_FILE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n");
 
-				gg_dcc_read(h->fd, &big, sizeof(big));
+				gg_dcc_read(h->fd, &big_pkt, sizeof(big_pkt));
 
 				/* XXX sprawdzać wynik */
-				h->offset = gg_fix32(big.dunno1);
+				h->offset = gg_fix32(big_pkt.dunno1);
 
 				h->state = GG_STATE_SENDING_FILE_HEADER;
 				h->check = GG_CHECK_WRITE;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				e->type = GG_EVENT_DCC_ACK;
 
 				return e;
 
 			case GG_STATE_READING_VOICE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n");
 
-				gg_dcc_read(h->fd, &tiny, sizeof(tiny));
+				gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt));
 
-				if (tiny.type != 0x01) {
-					gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type);
+				if (tiny_pkt.type != 0x01) {
+					gg_debug(GG_DEBUG_MISC, "// invalid "
+						"reply (%.2x), connection "
+						"refused\n", tiny_pkt.type);
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_REFUSED;
 					return e;
 				}
 
 				h->state = GG_STATE_READING_VOICE_HEADER;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
@@ -1112,61 +1131,78 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 			case GG_STATE_SENDING_FILE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n");
 
 				h->chunk_offset = 0;
 
 				if ((h->chunk_size = h->file_info.size - h->offset) > 4096) {
 					h->chunk_size = 4096;
-					big.type = gg_fix32(0x0003);  /* XXX */
+					big_pkt.type = gg_fix32(0x0003);  /* XXX */
 				} else
-					big.type = gg_fix32(0x0002);  /* XXX */
+					big_pkt.type = gg_fix32(0x0002);  /* XXX */
 
-				big.dunno1 = gg_fix32(h->chunk_size);
-				big.dunno2 = 0;
+				big_pkt.dunno1 = gg_fix32(h->chunk_size);
+				big_pkt.dunno2 = 0;
 
-				gg_dcc_write(h->fd, &big, sizeof(big));
+				gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt));
 
 				h->state = GG_STATE_SENDING_FILE;
 				h->check = GG_CHECK_WRITE;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 				h->established = 1;
 
 				return e;
 
 			case GG_STATE_SENDING_FILE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n");
 
 				if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
 					utmp = sizeof(buf);
 
-				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size);
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
+					"offset=%d, size=%d\n",
+					h->offset, h->file_info.size);
 
 				/* koniec pliku? */
 				if (h->file_info.size == 0) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n");
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() read()"
+						"reached eof on empty file\n");
 					e->type = GG_EVENT_DCC_DONE;
 
 					return e;
 				}
 
 				if (h->offset >= h->file_info.size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
 				}
 
-				lseek(h->file_fd, h->offset, SEEK_SET);
+				if (lseek(h->file_fd, h->offset, SEEK_SET) != (off_t)h->offset) {
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() lseek() "
+						"failed. (errno=%d, %s)\n",
+						errno, strerror(errno));
+
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_FILE;
+
+					return e;
+				}
 
 				size = read(h->file_fd, buf, utmp);
 
 				/* błąd */
 				if (size == -1) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() read() "
+						"failed. (errno=%d, %s)\n",
+						errno, strerror(errno));
 
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_FILE;
 
 					return e;
 				}
 
 				/* koniec pliku? */
@@ -1175,37 +1211,47 @@ struct gg_event *gg_dcc_watch_fd(struct 
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_EOF;
 
 					return e;
 				}
 
 				/* jeśli wczytaliśmy więcej, utnijmy. */
 				if (h->offset + size > h->file_info.size) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size);
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() read() "
+						"too much (read=%d, ofs=%d, "
+						"size=%d)\n", size, h->offset,
+						h->file_info.size);
 					size = h->file_info.size - h->offset;
 
 					if (size < 1) {
-						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n");
+						gg_debug(GG_DEBUG_MISC,
+							"// gg_dcc_watch_fd() "
+							"reached EOF after cutting\n");
 						e->type = GG_EVENT_DCC_DONE;
 						return e;
 					}
 				}
 
-				tmp = write(h->fd, buf, size);
+				tmp = send(h->fd, buf, size, 0);
 
 				if (tmp == -1) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%s)\n", strerror(errno));
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() send() "
+						"failed (%s)\n", strerror(errno));
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
 					return e;
 				}
 
 				if (tmp == 0) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (connection reset)\n");
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() send() "
+						"failed (connection reset)\n");
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
 					return e;
 				}
 
 				h->offset += tmp;
 
 				if (h->offset >= h->file_info.size) {
@@ -1235,43 +1281,52 @@ struct gg_event *gg_dcc_watch_fd(struct 
 					utmp = sizeof(buf);
 
 				if (h->offset >= h->file_info.size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
 				}
 
-				size = read(h->fd, buf, utmp);
+				size = recv(h->fd, buf, utmp, 0);
 
-				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size);
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
+					"ofs=%d, size=%d, recv()=%d\n",
+					h->offset, h->file_info.size, size);
 
 				/* błąd */
 				if (size == -1) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() recv() "
+						"failed. (errno=%d, %s)\n",
+						errno, strerror(errno));
 
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
 
 					return e;
 				}
 
 				/* koniec? */
 				if (size == 0) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n");
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() reached eof\n");
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_EOF;
 
 					return e;
 				}
 
 				tmp = write(h->file_fd, buf, size);
 
 				if (tmp == -1 || tmp < size) {
-					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno));
+					gg_debug(GG_DEBUG_MISC,
+						"// gg_dcc_watch_fd() write() "
+						"failed (%d:fd=%d:res=%d:%s)\n",
+						tmp, h->file_fd, size,
+						strerror(errno));
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
 					return e;
 				}
 
 				h->offset += size;
 
 				if (h->offset >= h->file_info.size) {
@@ -1281,18 +1336,18 @@ struct gg_event *gg_dcc_watch_fd(struct 
 
 				h->chunk_offset += size;
 
 				if (h->chunk_offset >= h->chunk_size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
 					h->state = GG_STATE_READING_FILE_HEADER;
 					h->timeout = GG_DEFAULT_TIMEOUT;
 					h->chunk_offset = 0;
-					h->chunk_size = sizeof(big);
-					if (!(h->chunk_buf = malloc(sizeof(big)))) {
+					h->chunk_size = sizeof(big_pkt);
+					if (!(h->chunk_buf = malloc(sizeof(big_pkt)))) {
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
 						free(e);
 						return NULL;
 					}
 				} else {
 					h->state = GG_STATE_GETTING_FILE;
 					h->timeout = GG_DCC_TIMEOUT_GET;
 				}
@@ -1325,16 +1380,19 @@ void gg_dcc_free(struct gg_dcc *d)
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d);
 
 	if (!d)
 		return;
 
 	if (d->fd != -1)
 		close(d->fd);
 
+	if (d->file_fd != -1)
+		gg_file_close(d->file_fd);
+
 	free(d->chunk_buf);
 	free(d);
 }
 
 /*
  * Local variables:
  * c-indentation-style: k&r
  * c-basic-offset: 8
--- a/libpurple/protocols/gg/lib/dcc7.c
+++ b/libpurple/protocols/gg/lib/dcc7.c
@@ -1,16 +1,16 @@
-/* $Id: dcc7.c 1087 2011-04-14 20:53:25Z wojtekka $ */
+/* $Id$ */
 
 /*
  *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
  *                          Tomasz Chiliński <chilek@chilan.com>
  *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *                          Bartłomiej Zimoń <uzi18@o2.pl>
- *  
+ *
  *  Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
  *  2.1 as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -24,49 +24,39 @@
  */
 
 /**
  * \file dcc7.c
  *
  * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
  */
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifndef _WIN32
-#  include <sys/ioctl.h>
-#  include <sys/socket.h>
-#  include <netinet/in.h>
-#  include <arpa/inet.h>
-#  ifdef sun
-#    include <sys/filio.h>
-#  endif
-#endif
-#include <time.h>
+#include "fileio.h"
+#include "network.h"
+#include "strman.h"
 
 #include <ctype.h>
 #include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
 #include <string.h>
-#include <stdio.h>
 #include <stdlib.h>
-#ifndef _MSC_VER
-#  include <unistd.h>
-#endif
+#include <time.h>
 
-#include "compat.h"
 #include "libgadu.h"
 #include "protocol.h"
 #include "resolver.h"
-#include "libgadu-internal.h"
-#include "libgadu-debug.h"
+#include "internal.h"
+#include "debug.h"
 
-#define gg_debug_dcc(dcc, level, ...) \
-	gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, __VA_ARGS__)
+#ifdef _MSC_VER
+#  define gg_debug_dcc(dcc, level, fmt, ...) \
+	gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt, __VA_ARGS__)
+#else
+#  define gg_debug_dcc(dcc, level, fmt...) \
+	gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt)
+#endif
 
 #define gg_debug_dump_dcc(dcc, level, buf, len) \
 	gg_debug_dump(((dcc) != NULL) ? (dcc)->sess : NULL, level, buf, len)
 
 /**
  * \internal Dodaje połączenie bezpośrednie do sesji.
  *
  * \param sess Struktura sesji
@@ -143,17 +133,17 @@ static struct gg_dcc7 *gg_dcc7_session_f
 	int empty;
 
 	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
 
 	empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
 
 	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
 		if (empty) {
-			if (tmp->peer_uin == uin /*&& tmp->state != GG_STATE_WAITING_FOR_ACCEPT*/)
+			if (tmp->peer_uin == uin && tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
 				return tmp;
 		} else {
 			if (!memcmp(&tmp->cid, &id, sizeof(id)))
 				return tmp;
 		}
 	}
 
 	return NULL;
@@ -172,17 +162,19 @@ static int gg_dcc7_get_relay_addr(struct
 
 	if (dcc == NULL || dcc->sess == NULL) {
 		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n");
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (dcc->sess->resolver_start(&dcc->fd, &dcc->resolver, GG_RELAY_HOST) == -1) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() "
+			"resolving failed (errno=%d, %s)\n",
+			errno, strerror(errno));
 		return -1;
 	}
 
 	dcc->state = GG_STATE_RESOLVING_RELAY;
 	dcc->check = GG_CHECK_READ;
 	dcc->timeout = GG_DEFAULT_TIMEOUT;
 
 	return 0;
@@ -248,34 +240,35 @@ static int gg_dcc7_listen(struct gg_dcc7
 	}
 
 	memset(&sin, 0, sizeof(sin));
 	sin.sin_family = AF_INET;
 	sin.sin_addr.s_addr = addr;
 	sin.sin_port = htons(port);
 
 	if (bind(fd, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to %s:%d\n", inet_ntoa(sin.sin_addr), port);
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to"
+			" bind to %s:%d\n", inet_ntoa(sin.sin_addr), port);
 		goto fail;
 	}
 
 	if (port == 0 && getsockname(fd, (struct sockaddr*) &sin, &sin_len) == -1) {
 		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to port %d\n", port);
 		goto fail;
 	}
 
 	if (listen(fd, 1)) {
 		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
 		goto fail;
 	}
 
 	dcc->fd = fd;
 	dcc->local_addr = sin.sin_addr.s_addr;
 	dcc->local_port = ntohs(sin.sin_port);
-	
+
 	dcc->state = GG_STATE_LISTENING;
 	dcc->check = GG_CHECK_READ;
 	dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
 
 	return 0;
 
 fail:
 	errsv = errno;
@@ -297,30 +290,32 @@ static int gg_dcc7_listen_and_send_info(
 	uint16_t external_port;
 	uint32_t external_addr;
 	struct in_addr addr;
 
 	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
 
 	if (gg_dcc7_listen(dcc, dcc->sess->client_addr, dcc->sess->client_port) == -1)
 		return -1;
-	
+
 	if (dcc->sess->external_port != 0)
 		external_port = dcc->sess->external_port;
 	else
 		external_port = dcc->local_port;
 
 	if (dcc->sess->external_addr != 0)
 		external_addr = dcc->sess->external_addr;
-	else 
+	else
 		external_addr = dcc->local_addr;
 
 	addr.s_addr = external_addr;
 
-	gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(addr), external_port);
+	gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() "
+		"sending IP address %s and port %d\n",
+		inet_ntoa(addr), external_port);
 
 	memset(&pkt, 0, sizeof(pkt));
 	pkt.uin = gg_fix32(dcc->peer_uin);
 	pkt.type = GG_DCC7_TYPE_P2P;
 	pkt.id = dcc->cid;
 	snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(addr), external_port);
 	snprintf((char*) pkt.hash, sizeof(pkt.hash), "%u", external_addr + external_port * rand());
 
@@ -377,17 +372,17 @@ static int gg_dcc7_request_id(struct gg_
 		return -1;
 	}
 
 	if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type);
 		errno = EINVAL;
 		return -1;
 	}
-	
+
 	memset(&pkt, 0, sizeof(pkt));
 	pkt.type = gg_fix32(type);
 
 	return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL);
 }
 
 /**
  * \internal Rozpoczyna wysyłanie pliku.
@@ -402,17 +397,19 @@ static int gg_dcc7_request_id(struct gg_
  * \param filename1250 Nazwa pliku w kodowaniu CP-1250
  * \param hash Skrót SHA-1 pliku
  * \param seek Flaga mówiąca, czy można używać lseek()
  *
  * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
  *
  * \ingroup dcc7
  */
-static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash, int seek)
+static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess,
+	uin_t rcpt, int fd, size_t size, const char *filename1250,
+	const char *hash, int seek)
 {
 	struct gg_dcc7 *dcc = NULL;
 
 	if (!sess || !rcpt || !filename1250 || !hash || fd == -1) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n");
 		errno = EINVAL;
 		goto fail;
 	}
@@ -433,17 +430,17 @@ static struct gg_dcc7 *gg_dcc7_send_file
 	dcc->sess = sess;
 	dcc->fd = -1;
 	dcc->uin = sess->uin;
 	dcc->peer_uin = rcpt;
 	dcc->file_fd = fd;
 	dcc->size = size;
 	dcc->seek = seek;
 
-	strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1);
+	strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN);
 	dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
 
 	memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
 
 	if (gg_dcc7_session_add(sess, dcc) == -1)
 		goto fail;
 
 	return dcc;
@@ -461,51 +458,54 @@ fail:
  * \param filename Nazwa pliku w lokalnym systemie plików
  * \param filename1250 Nazwa pliku w kodowaniu CP-1250
  * \param hash Skrót SHA-1 pliku (lub \c NULL jeśli ma być wyznaczony)
  *
  * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
  *
  * \ingroup dcc7
  */
-struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash)
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt,
+	const char *filename, const char *filename1250, const char *hash)
 {
 	struct gg_dcc7 *dcc = NULL;
 	const char *tmp;
 	char hash_buf[GG_DCC7_HASH_LEN];
 	struct stat st;
 	int fd = -1;
 
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p)\n", sess, rcpt, filename, hash);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d,"
+		" \"%s\", %p)\n", sess, rcpt, filename, hash);
 
 	if (!sess || !rcpt || !filename) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n");
 		errno = EINVAL;
 		goto fail;
 	}
 
 	if (!filename1250)
 		filename1250 = filename;
 
-	if (stat(filename, &st) == -1) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno));
+	if ((fd = open(filename, O_RDONLY)) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if (fstat(fd, &st) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() "
+			"fstat() failed (%s)\n", strerror(errno));
 		goto fail;
 	}
 
 	if ((st.st_mode & S_IFDIR)) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
 		errno = EINVAL;
 		goto fail;
 	}
 
-	if ((fd = open(filename, O_RDONLY)) == -1) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
-		goto fail;
-	}
-
 	if (!hash) {
 		if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
 			goto fail;
 
 		hash = hash_buf;
 	}
 
 	if ((tmp = strrchr(filename1250, '/')))
@@ -514,17 +514,17 @@ struct gg_dcc7 *gg_dcc7_send_file(struct
 	if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1)))
 		goto fail;
 
 	return dcc;
 
 fail:
 	if (fd != -1) {
 		int errsv = errno;
-		close(fd);
+		gg_file_close(fd);
 		errno = errsv;
 	}
 
 	free(dcc);
 	return NULL;
 }
 
 /**
@@ -539,19 +539,22 @@ fail:
  * \param size Rozmiar pliku
  * \param filename1250 Nazwa pliku w kodowaniu CP-1250
  * \param hash Skrót SHA-1 pliku
  *
  * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
  *
  * \ingroup dcc7
  */
-struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash)
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt,
+	int fd, size_t size, const char *filename1250, const char *hash)
 {
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, %d, %d, %u, \"%s\", %p)\n", sess, rcpt, fd, size, filename1250, hash);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, "
+		"%d, %d, %" GG_SIZE_FMT ", \"%s\", %p)\n",
+		sess, rcpt, fd, size, filename1250, hash);
 
 	return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0);
 }
 
 
 /**
  * Potwierdza chęć odebrania pliku.
  *
@@ -634,36 +637,42 @@ int gg_dcc7_reject(struct gg_dcc7 *dcc, 
 int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
 {
 	const struct gg_dcc7_id_reply *p = payload;
 	struct gg_dcc7 *tmp;
 
 	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len);
 
 	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, "
+			"state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
 
-		if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != gg_fix32(p->type))
+		if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != (int) gg_fix32(p->type))
 			continue;
-		
+
 		tmp->cid = p->id;
 
 		switch (tmp->dcc_type) {
 			case GG_DCC7_TYPE_FILE:
 			{
 				struct gg_dcc7_new s;
 
 				memset(&s, 0, sizeof(s));
 				s.id = tmp->cid;
 				s.type = gg_fix32(GG_DCC7_TYPE_FILE);
 				s.uin_from = gg_fix32(tmp->uin);
 				s.uin_to = gg_fix32(tmp->peer_uin);
 				s.size = gg_fix32(tmp->size);
 
-				memcpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN);
+				/* Uwaga: To nie jest ciąg kończony zerem.
+				 * Note: This is not a null-terminated string. */
+				GG_STATIC_ASSERT(
+					sizeof(s.filename) == sizeof(tmp->filename) - 1,
+					filename_sizes_does_not_match);
+				memcpy((char*)s.filename, (char*)tmp->filename, sizeof(s.filename));
 
 				tmp->state = GG_STATE_WAITING_FOR_ACCEPT;
 				tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
 
 				return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL);
 			}
 		}
 	}
@@ -685,31 +694,31 @@ int gg_dcc7_handle_accept(struct gg_sess
 {
 	const struct gg_dcc7_accept *p = payload;
 	struct gg_dcc7 *dcc;
 
 	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len);
 
 	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n");
-		// XXX wysłać reject?
+		/* XXX wysłać reject? */
 		e->type = GG_EVENT_DCC7_ERROR;
 		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 		return 0;
 	}
 
 	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n");
 		e->type = GG_EVENT_DCC7_ERROR;
 		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 		return 0;
 	}
-	
-	// XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT?
-	
+
+	/* XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT? */
+
 	dcc->offset = gg_fix32(p->offset);
 	dcc->state = GG_STATE_WAITING_FOR_INFO;
 
 	return 0;
 }
 
 /**
  * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim.
@@ -723,23 +732,24 @@ int gg_dcc7_handle_accept(struct gg_sess
  */
 int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
 {
 	const struct gg_dcc7_info *p = payload;
 	struct gg_dcc7 *dcc;
 	char *tmp;
 
 	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "// gg_dcc7_handle_info() received address: %s, hash: %s\n", p->info, p->hash);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "// gg_dcc7_handle_info() "
+		"received address: %s, hash: %s\n", p->info, p->hash);
 
 	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
 		return 0;
 	}
-	
+
 	if (dcc->state == GG_STATE_CONNECTED) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() state is already connected\n");
 		return 0;
 	}
 
 	switch (p->type)
 	{
 	case GG_DCC7_TYPE_P2P:
@@ -753,88 +763,102 @@ int gg_dcc7_handle_info(struct gg_sessio
 		if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
 			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n");
 			e->type = GG_EVENT_DCC7_ERROR;
 			e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 			return 0;
 		}
 
 		if (dcc->state == GG_STATE_WAITING_FOR_INFO) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() waiting for info so send one\n");
+			gg_debug_session(sess, GG_DEBUG_MISC,
+				"// gg_dcc7_handle_info() waiting for info "
+				"so send one\n");
 			gg_dcc7_listen_and_send_info(dcc);
 			e->type = GG_EVENT_DCC7_PENDING;
 			e->event.dcc7_pending.dcc7 = dcc;
 			return 0;
 		}
 
 		break;
 
 	case GG_DCC7_TYPE_SERVER:
 		if (!(tmp = strstr(p->info, "GG"))) {
 			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown info packet\n");
 			e->type = GG_EVENT_DCC7_ERROR;
 			e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 			return 0;
 		}
 
-#if defined(HAVE_UINT64_T) && defined(HAVE_STRTOULL)
+#if defined(HAVE__STRTOUI64) || defined(HAVE_STRTOULL)
 		{
 			uint64_t cid;
 
+#  ifdef HAVE__STRTOUI64
+			cid = _strtoui64(tmp + 2, NULL, 0);
+#  else
 			cid = strtoull(tmp + 2, NULL, 0);
+#  endif
 
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() info.str=%s, info.id=%llu, sess.id=%llu\n", tmp + 2, cid, *((unsigned long long*) &dcc->cid));
+			gg_debug_session(sess, GG_DEBUG_MISC,
+				"// gg_dcc7_handle_info() info.str=%s, "
+				"info.id=%llu, sess.id=%llu\n", tmp + 2, cid,
+				*((unsigned long long*) &dcc->cid));
 
 			cid = gg_fix64(cid);
 
 			if (memcmp(&dcc->cid, &cid, sizeof(cid)) != 0) {
 				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid session id\n");
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 				return 0;
 			}
 		}
+#else
+		(void)tmp;
 #endif
 
 		if (gg_dcc7_get_relay_addr(dcc) == -1) {
 			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n");
 			e->type = GG_EVENT_DCC7_ERROR;
 			e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
 			return 0;
 		}
 
-		// XXX wysyłać dopiero jeśli uda się połączyć z serwerem?
+		/* XXX wysyłać dopiero jeśli uda się połączyć z serwerem? */
 
 		gg_send_packet(dcc->sess, GG_DCC7_INFO, payload, len, NULL);
 
 		return 0;
 
 	default:
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info()"
+			" unhandled transfer type (%d)\n", p->type);
 		e->type = GG_EVENT_DCC7_ERROR;
 		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 		return 0;
 	}
 
-	// jeśli nadal czekamy na połączenie przychodzące, a druga strona nie
-	// daje rady i oferuje namiary na siebie, bierzemy co dają.
-
-// 	if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
-// 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
-// 		e->type = GG_EVENT_DCC7_ERROR;
-// 		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
-// 		return 0;
-// 	}
+#if 0
+	/* jeśli nadal czekamy na połączenie przychodzące, a druga strona nie
+	 * daje rady i oferuje namiary na siebie, bierzemy co dają.
+	 */
+	if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+#endif
 
 	if (dcc->state == GG_STATE_LISTENING) {
 		close(dcc->fd);
 		dcc->fd = -1;
 		dcc->reverse = 1;
 	}
-	
+
 	if (dcc->type == GG_SESSION_DCC7_SEND) {
 		e->type = GG_EVENT_DCC7_ACCEPT;
 		e->event.dcc7_accept.dcc7 = dcc;
 		e->event.dcc7_accept.type = gg_fix32(p->type);
 		e->event.dcc7_accept.remote_ip = dcc->remote_addr;
 		e->event.dcc7_accept.remote_port = dcc->remote_port;
 	} else {
 		e->type = GG_EVENT_DCC7_PENDING;
@@ -868,29 +892,29 @@ int gg_dcc7_handle_reject(struct gg_sess
 	struct gg_dcc7 *dcc;
 
 	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len);
 
 	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n");
 		return 0;
 	}
-	
+
 	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n");
 		e->type = GG_EVENT_DCC7_ERROR;
 		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
 		return 0;
 	}
 
 	e->type = GG_EVENT_DCC7_REJECT;
 	e->event.dcc7_reject.dcc7 = dcc;
 	e->event.dcc7_reject.reason = gg_fix32(p->reason);
 
-	// XXX ustawić state na rejected?
+	/* XXX ustawić state na rejected? */
 
 	return 0;
 }
 
 /**
  * \internal Obsługuje pakiet nowego połączenia bezpośredniego.
  *
  * \param sess Struktura sesji
@@ -908,84 +932,91 @@ int gg_dcc7_handle_new(struct gg_session
 	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len);
 
 	switch (gg_fix32(p->type)) {
 		case GG_DCC7_TYPE_FILE:
 			if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
 				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n");
 				return -1;
 			}
-			
+
 			memset(dcc, 0, sizeof(struct gg_dcc7));
 			dcc->type = GG_SESSION_DCC7_GET;
 			dcc->dcc_type = GG_DCC7_TYPE_FILE;
 			dcc->fd = -1;
 			dcc->file_fd = -1;
 			dcc->uin = sess->uin;
 			dcc->peer_uin = gg_fix32(p->uin_from);
 			dcc->cid = p->id;
 			dcc->sess = sess;
 
 			if (gg_dcc7_session_add(sess, dcc) == -1) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+				gg_debug_session(sess, GG_DEBUG_MISC,
+					"// gg_dcc7_handle_new() unable to "
+					"add to session\n");
 				gg_dcc7_free(dcc);
 				return -1;
 			}
 
 			dcc->size = gg_fix32(p->size);
-			strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1);
+			strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN);
 			dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
 			memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN);
 
 			e->type = GG_EVENT_DCC7_NEW;
 			e->event.dcc7_new = dcc;
 
 			break;
 
 		case GG_DCC7_TYPE_VOICE:
 			if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
 				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n");
 				return -1;
 			}
-			
+
 			memset(dcc, 0, sizeof(struct gg_dcc7));
 
 			dcc->type = GG_SESSION_DCC7_VOICE;
 			dcc->dcc_type = GG_DCC7_TYPE_VOICE;
 			dcc->fd = -1;
 			dcc->file_fd = -1;
 			dcc->uin = sess->uin;
 			dcc->peer_uin = gg_fix32(p->uin_from);
 			dcc->cid = p->id;
 			dcc->sess = sess;
 
 			if (gg_dcc7_session_add(sess, dcc) == -1) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+				gg_debug_session(sess, GG_DEBUG_MISC,
+					"// gg_dcc7_handle_new() unable to add "
+					"to session\n");
 				gg_dcc7_free(dcc);
 				return -1;
 			}
 
 			e->type = GG_EVENT_DCC7_NEW;
 			e->event.dcc7_new = dcc;
 
 			break;
 
 		default:
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from));
+			gg_debug_session(sess, GG_DEBUG_MISC,
+				"// gg_dcc7_handle_new() unknown dcc type (%d) "
+				"from %u\n", gg_fix32(p->type),
+				gg_fix32(p->uin_from));
 
 			break;
 	}
 
 	return 0;
 }
 
 /**
  * \internal Ustawia odpowiednie stany wewnętrzne w zależności od rodzaju
  * połączenia.
- * 
+ *
  * \param dcc Struktura połączenia
  *
  * \return 0 jeśli się powiodło, -1 w przypadku błędu.
  */
 static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
 {
 	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
 
@@ -1031,17 +1062,20 @@ static int gg_dcc7_postauth_fixup(struct
  * \ingroup dcc7
  */
 struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
 {
 	struct gg_event *e;
 
 	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
 
-	if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) {
+	if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND &&
+		dcc->type != GG_SESSION_DCC7_GET &&
+		dcc->type != GG_SESSION_DCC7_VOICE))
+	{
 		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
 		errno = EINVAL;
 		return NULL;
 	}
 
 	if (!(e = malloc(sizeof(struct gg_event)))) {
 		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
 		return NULL;
@@ -1049,34 +1083,36 @@ struct gg_event *gg_dcc7_watch_fd(struct
 
 	memset(e, 0, sizeof(struct gg_event));
 	e->type = GG_EVENT_NONE;
 
 	switch (dcc->state) {
 		case GG_STATE_LISTENING:
 		{
 			struct sockaddr_in sin;
-			int fd, one = 1;
+			int fd;
 			socklen_t sin_len = sizeof(sin);
 
 			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
 
 			if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() accept() failed "
+					"(%s)\n", strerror(errno));
 				return e;
 			}
 
-			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd()"
+				" connection from %s:%d\n",
+				inet_ntoa(sin.sin_addr), htons(sin.sin_port));
 
-#ifdef FIONBIO
-			if (ioctl(fd, FIONBIO, &one) == -1) {
-#else
-			if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
-#endif
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
+			if (!gg_fd_set_nonblocking(fd)) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() can't set "
+					"nonblocking (%s)\n", strerror(errno));
 				close(fd);
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 				return e;
 			}
 
 			close(dcc->fd);
 			dcc->fd = fd;
@@ -1093,39 +1129,50 @@ struct gg_event *gg_dcc7_watch_fd(struct
 			e->event.dcc7_connected.dcc7 = dcc;
 
 			return e;
 		}
 
 		case GG_STATE_CONNECTING:
 		{
 			int res = 0, error = 0;
-			unsigned int error_size = sizeof(error);
+			socklen_t error_size = sizeof(error);
 
 			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
 
 			dcc->soft_timeout = 0;
 
 			if (dcc->timeout == 0)
 				error = ETIMEDOUT;
 
-			if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
+			if (error || (res = getsockopt(dcc->fd, SOL_SOCKET,
+				SO_ERROR, &error, &error_size)) == -1 ||
+				error != 0)
+			{
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() connection "
+					"failed (%s)\n", (res == -1) ?
+					strerror(errno) : strerror(error));
 
 				if (dcc->relay) {
-					for (dcc->relay_index++; dcc->relay_index < dcc->relay_count; dcc->relay_index++) {
+					for (dcc->relay_index++;
+						dcc->relay_index < dcc->relay_count;
+						dcc->relay_index++)
+					{
 						dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr;
 						dcc->remote_port = dcc->relay_list[dcc->relay_index].port;
 
 						if (gg_dcc7_connect(dcc) == 0)
 							break;
 					}
 
 					if (dcc->relay_index >= dcc->relay_count) {
-						gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
+						gg_debug_dcc(dcc, GG_DEBUG_MISC,
+							"// gg_dcc7_watch_fd() "
+							"no relay available\n");
 						e->type = GG_EVENT_DCC7_ERROR;
 						e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 						return e;
 					}
 				} else {
 					if (gg_dcc7_reverse_connect(dcc) != -1) {
 						e->type = GG_EVENT_DCC7_PENDING;
 						e->event.dcc7_pending.dcc7 = dcc;
@@ -1153,36 +1200,42 @@ struct gg_event *gg_dcc7_watch_fd(struct
 			int res;
 
 			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
 
 			if (!dcc->relay) {
 				struct gg_dcc7_welcome_p2p welcome, welcome_ok;
 				welcome_ok.id = dcc->cid;
 
-				if ((res = read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
-					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+				if ((res = recv(dcc->fd, &welcome, sizeof(welcome), 0)) != sizeof(welcome)) {
+					gg_debug_dcc(dcc, GG_DEBUG_MISC,
+						"// gg_dcc7_watch_fd() recv() "
+						"failed (%d, %s)\n", res,
+						strerror(errno));
 					e->type = GG_EVENT_DCC7_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 					return e;
 				}
 
 				if (memcmp(&welcome, &welcome_ok, sizeof(welcome))) {
 					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
 					e->type = GG_EVENT_DCC7_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 					return e;
 				}
 			} else {
 				struct gg_dcc7_welcome_server welcome, welcome_ok;
 				welcome_ok.magic = GG_DCC7_WELCOME_SERVER;
 				welcome_ok.id = dcc->cid;
 
-				if ((res = read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
-					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+				if ((res = recv(dcc->fd, &welcome, sizeof(welcome), 0)) != sizeof(welcome)) {
+					gg_debug_dcc(dcc, GG_DEBUG_MISC,
+						"// gg_dcc7_watch_fd() recv() "
+						"failed (%d, %s)\n",
+						res, strerror(errno));
 					e->type = GG_EVENT_DCC7_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 					return e;
 				}
 
 				if (memcmp(&welcome, &welcome_ok, sizeof(welcome)) != 0) {
 					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
 					e->type = GG_EVENT_DCC7_ERROR;
@@ -1209,30 +1262,36 @@ struct gg_event *gg_dcc7_watch_fd(struct
 
 			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
 
 			if (!dcc->relay) {
 				struct gg_dcc7_welcome_p2p welcome;
 
 				welcome.id = dcc->cid;
 
-				if ((res = write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
-					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));
+				if ((res = send(dcc->fd, &welcome, sizeof(welcome), 0)) != sizeof(welcome)) {
+					gg_debug_dcc(dcc, GG_DEBUG_MISC,
+						"// gg_dcc7_watch_fd() send() "
+						"failed (%d, %s)\n",
+						res, strerror(errno));
 					e->type = GG_EVENT_DCC7_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 					return e;
 				}
 			} else {
 				struct gg_dcc7_welcome_server welcome;
 
 				welcome.magic = gg_fix32(GG_DCC7_WELCOME_SERVER);
 				welcome.id = dcc->cid;
 
-				if ((res = write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
-					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));
+				if ((res = send(dcc->fd, &welcome, sizeof(welcome), 0)) != sizeof(welcome)) {
+					gg_debug_dcc(dcc, GG_DEBUG_MISC,
+						"// gg_dcc7_watch_fd() send() "
+						"failed (%d, %s)\n", res,
+						strerror(errno));
 					e->type = GG_EVENT_DCC7_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
 					return e;
 				}
 			}
 
 			if (dcc->incoming) {
 				gg_dcc7_postauth_fixup(dcc);
@@ -1244,46 +1303,55 @@ struct gg_event *gg_dcc7_watch_fd(struct
 			}
 
 			return e;
 		}
 
 		case GG_STATE_SENDING_FILE:
 		{
 			char buf[1024];
-			int chunk, res;
+			size_t chunk;
+			int res;
 
-			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd()"
+				" GG_STATE_SENDING_FILE (offset=%d, size=%d)\n",
+				dcc->offset, dcc->size);
 
 			if (dcc->offset >= dcc->size) {
 				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
 				e->type = GG_EVENT_DCC7_DONE;
 				e->event.dcc7_done.dcc7 = dcc;
 				return e;
 			}
 
 			if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() lseek() failed "
+					"(%s)\n", strerror(errno));
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_FILE;
 				return e;
 			}
 
 			if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
 				chunk = sizeof(buf);
 
 			if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() read() failed "
+					"(res=%d, %s)\n", res, strerror(errno));
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
 				return e;
 			}
 
-			if ((res = write(dcc->fd, buf, res)) == -1) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
+			if ((res = send(dcc->fd, buf, res, 0)) == -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() send() failed "
+					"(%s)\n", strerror(errno));
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_NET;
 				return e;
 			}
 
 			dcc->offset += res;
 
 			if (dcc->offset >= dcc->size) {
@@ -1300,36 +1368,44 @@ struct gg_event *gg_dcc7_watch_fd(struct
 			return e;
 		}
 
 		case GG_STATE_GETTING_FILE:
 		{
 			char buf[1024];
 			int res, wres;
 
-			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd()"
+				" GG_STATE_GETTING_FILE (offset=%d, size=%d)\n",
+				dcc->offset, dcc->size);
 
 			if (dcc->offset >= dcc->size) {
 				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
 				e->type = GG_EVENT_DCC7_DONE;
 				e->event.dcc7_done.dcc7 = dcc;
 				return e;
 			}
 
-			if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));
+			if ((res = recv(dcc->fd, buf, sizeof(buf), 0)) < 1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() recv() failed "
+					"(fd=%d, res=%d, %s)\n", dcc->fd, res,
+					strerror(errno));
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
 				return e;
 			}
 
-			// XXX zapisywać do skutku?
+			/* XXX zapisywać do skutku? */
 
 			if ((wres = write(dcc->file_fd, buf, res)) < res) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno));
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() write() failed "
+					"(fd=%d, res=%d, %s)\n", dcc->file_fd,
+					wres, strerror(errno));
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_FILE;
 				return e;
 			}
 
 			dcc->offset += res;
 
 			if (dcc->offset >= dcc->size) {
@@ -1344,77 +1420,92 @@ struct gg_event *gg_dcc7_watch_fd(struct
 			dcc->timeout = GG_DCC7_TIMEOUT_GET;
 
 			return e;
 		}
 
 		case GG_STATE_RESOLVING_RELAY:
 		{
 			struct in_addr addr;
+			int res;
 
 			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n");
 
-			if (read(dcc->fd, &addr, sizeof(addr)) < sizeof(addr) || addr.s_addr == INADDR_NONE) {
+			do {
+				res = gg_resolver_recv(dcc->fd, &addr, sizeof(addr));
+			} while (res == -1 && errno == EINTR);
+
+			dcc->sess->resolver_cleanup(&dcc->resolver, 0);
+
+			if (res != sizeof(addr) || addr.s_addr == INADDR_NONE) {
 				int errno_save = errno;
 
 				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n");
 				close(dcc->fd);
 				dcc->fd = -1;
-				dcc->sess->resolver_cleanup(&dcc->resolver, 0);
 				errno = errno_save;
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 				return e;
 			}
 
-			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), GG_RELAY_PORT);
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd()"
+				" resolved, connecting to %s:%d\n",
+				inet_ntoa(addr), GG_RELAY_PORT);
 
 			if ((dcc->fd = gg_connect(&addr, GG_RELAY_PORT, 1)) == -1) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() connection "
+					"failed (errno=%d, %s), critical\n",
+					errno, strerror(errno));
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 				return e;
 			}
-			
+
 			dcc->state = GG_STATE_CONNECTING_RELAY;
 			dcc->check = GG_CHECK_WRITE;
 			dcc->timeout = GG_DEFAULT_TIMEOUT;
 
 			e->type = GG_EVENT_DCC7_PENDING;
 			e->event.dcc7_pending.dcc7 = dcc;
 
 			return e;
 		}
 
 		case GG_STATE_CONNECTING_RELAY:
 		{
 			int res;
-			unsigned int res_size = sizeof(res);
+			socklen_t res_size = sizeof(res);
 			struct gg_dcc7_relay_req pkt;
 
 			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n");
-			
+
 			if (getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) != 0 || res != 0) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() connection "
+					"failed (errno=%d, %s)\n",
+					res, strerror(res));
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 				return e;
 			}
 
 			memset(&pkt, 0, sizeof(pkt));
 			pkt.magic = gg_fix32(GG_DCC7_RELAY_REQUEST);
 			pkt.len = gg_fix32(sizeof(pkt));
 			pkt.id = dcc->cid;
 			pkt.type = gg_fix16(GG_DCC7_RELAY_TYPE_SERVER);
 			pkt.dunno1 = gg_fix16(GG_DCC7_RELAY_DUNNO1);
 
-			gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic));
+			gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_watch_fd()"
+				" send pkt(0x%.2x)\n", gg_fix32(pkt.magic));
 			gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, (const char*) &pkt, sizeof(pkt));
 
-			if ((res = write(dcc->fd, &pkt, sizeof(pkt))) != sizeof(pkt)) {
+			if ((res = send(dcc->fd, &pkt, sizeof(pkt), 0)) != sizeof(pkt)) {
 				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n");
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 				return e;
 			}
 
 			dcc->state = GG_STATE_READING_RELAY;
 			dcc->check = GG_CHECK_READ;
@@ -1423,80 +1514,104 @@ struct gg_event *gg_dcc7_watch_fd(struct
 			return e;
 		}
 
 		case GG_STATE_READING_RELAY:
 		{
 			char buf[256];
 			struct gg_dcc7_relay_reply *pkt;
 			struct gg_dcc7_relay_reply_server srv;
+			size_t max_relay_count = (sizeof(buf) - sizeof(*pkt)) / sizeof(srv);
 			int res;
 			int i;
 
 			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n");
 
-			if ((res = read(dcc->fd, buf, sizeof(buf))) < sizeof(*pkt)) {
+			if ((res = recv(dcc->fd, buf, sizeof(buf), 0)) < (int) sizeof(*pkt)) {
 				if (res == 0)
 					errno = ECONNRESET;
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() recv() failed "
+					"(%d, %s)\n", res, strerror(errno));
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 				return e;
 			}
 
 			pkt = (struct gg_dcc7_relay_reply*) buf;
 
-			if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY || gg_fix32(pkt->rcount) < 1 || gg_fix32(pkt->rcount > 256) || gg_fix32(pkt->len) < sizeof(*pkt) + gg_fix32(pkt->rcount) * sizeof(srv)) {
+			if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY ||
+				gg_fix32(pkt->rcount) < 1 ||
+				gg_fix32(pkt->rcount) > 256 ||
+				gg_fix32(pkt->len) < sizeof(*pkt) +
+				gg_fix32(pkt->rcount) * sizeof(srv))
+			{
 				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n");
 				errno = EINVAL;
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 				return e;
 			}
 
-			gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic));
+			gg_debug_dcc(dcc, GG_DEBUG_DUMP,
+				"// gg_dcc7_get_relay() read pkt(0x%.2x)\n",
+				gg_fix32(pkt->magic));
 			gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, buf, res);
 
 			free(dcc->relay_list);
 
 			dcc->relay_index = 0;
 			dcc->relay_count = gg_fix32(pkt->rcount);
+
+			if (dcc->relay_count > 0xffff ||
+				(size_t)dcc->relay_count > max_relay_count)
+			{
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"// gg_dcc7_watch_fd() relay_count out "
+					"of bounds (%d)\n", dcc->relay_count);
+				dcc->relay_count = 0;
+				free(e);
+				return NULL;
+			}
+
 			dcc->relay_list = malloc(dcc->relay_count * sizeof(gg_dcc7_relay_t));
 
 			if (dcc->relay_list == NULL) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory");
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
 				dcc->relay_count = 0;
 				free(e);
 				return NULL;
 			}
 
 			for (i = 0; i < dcc->relay_count; i++) {
 				struct in_addr addr;
 
 				memcpy(&srv, buf + sizeof(*pkt) + i * sizeof(srv), sizeof(srv));
 				dcc->relay_list[i].addr = srv.addr;
 				dcc->relay_list[i].port = gg_fix16(srv.port);
 				dcc->relay_list[i].family = srv.family;
 
 				addr.s_addr = srv.addr;
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "//    %s %d %d\n", inet_ntoa(addr), gg_fix16(srv.port), srv.family);
+				gg_debug_dcc(dcc, GG_DEBUG_MISC,
+					"//    %s %d %d\n", inet_ntoa(addr),
+					gg_fix16(srv.port), srv.family);
 			}
-			
+
 			dcc->relay = 1;
 
 			for (; dcc->relay_index < dcc->relay_count; dcc->relay_index++) {
 				dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr;
 				dcc->remote_port = dcc->relay_list[dcc->relay_index].port;
 
 				if (gg_dcc7_connect(dcc) == 0)
 					break;
 			}
 
 			if (dcc->relay_index >= dcc->relay_count) {
-				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available\n");
 				e->type = GG_EVENT_DCC7_ERROR;
 				e->event.dcc_error = GG_ERROR_DCC7_RELAY;
 				return e;
 			}
 
 			return e;
 		}
 
@@ -1526,18 +1641,17 @@ void gg_dcc7_free(struct gg_dcc7 *dcc)
 
 	if (!dcc)
 		return;
 
 	if (dcc->fd != -1)
 		close(dcc->fd);
 
 	if (dcc->file_fd != -1)
-		close(dcc->file_fd);
+		gg_file_close(dcc->file_fd);
 
 	if (dcc->sess)
 		gg_dcc7_session_remove(dcc->sess, dcc);
 
 	free(dcc->relay_list);
 
 	free(dcc);
 }
-
--- a/libpurple/protocols/gg/lib/debug.c
+++ b/libpurple/protocols/gg/lib/debug.c
@@ -27,17 +27,17 @@
  */
 #include <sys/types.h>
 #include <errno.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "libgadu.h"
-#include "libgadu-debug.h"
+#include "debug.h"
 
 /**
  * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową
  * składającą się ze stałych \c GG_DEBUG_...
  *
  * \ingroup debug
  */
 int gg_debug_level = 0;
@@ -157,17 +157,17 @@ void gg_debug_session(struct gg_session 
  * \param buf Bufor danych
  * \param len Długość bufora danych
  *
  * \ingroup debug
  */
 void gg_debug_dump(struct gg_session *gs, int level, const char *buf, size_t len)
 {
 	char line[80];
-	int i, j;
+	unsigned int i, j;
 
 	for (i = 0; i < len; i += 16) {
 		int ofs;
 
 		sprintf(line, "%.4x: ", i);
 		ofs = 6;
 
 		for (j = 0; j < 16; j++) {
@@ -260,20 +260,45 @@ const char *gg_debug_state(enum gg_state
 	GG_DEBUG_STATE(GG_STATE_WAITING_FOR_INFO)
 	GG_DEBUG_STATE(GG_STATE_READING_ID)
 	GG_DEBUG_STATE(GG_STATE_SENDING_ID)
 	GG_DEBUG_STATE(GG_STATE_RESOLVING_GG)
 	GG_DEBUG_STATE(GG_STATE_RESOLVING_RELAY)
 	GG_DEBUG_STATE(GG_STATE_CONNECTING_RELAY)
 	GG_DEBUG_STATE(GG_STATE_READING_RELAY)
 	GG_DEBUG_STATE(GG_STATE_DISCONNECTING)
+	GG_DEBUG_STATE(GG_STATE_CONNECT_HUB)
+	GG_DEBUG_STATE(GG_STATE_CONNECT_PROXY_HUB)
+	GG_DEBUG_STATE(GG_STATE_CONNECT_GG)
+	GG_DEBUG_STATE(GG_STATE_CONNECT_PROXY_GG)
+	GG_DEBUG_STATE(GG_STATE_CONNECTING_PROXY_HUB)
+	GG_DEBUG_STATE(GG_STATE_CONNECTING_PROXY_GG)
+	GG_DEBUG_STATE(GG_STATE_RESOLVE_HUB_SYNC)
+	GG_DEBUG_STATE(GG_STATE_RESOLVE_HUB_ASYNC)
+	GG_DEBUG_STATE(GG_STATE_RESOLVE_PROXY_HUB_SYNC)
+	GG_DEBUG_STATE(GG_STATE_RESOLVE_PROXY_HUB_ASYNC)
+	GG_DEBUG_STATE(GG_STATE_RESOLVE_PROXY_GG_SYNC)
+	GG_DEBUG_STATE(GG_STATE_RESOLVE_PROXY_GG_ASYNC)
+	GG_DEBUG_STATE(GG_STATE_RESOLVE_GG_SYNC)
+	GG_DEBUG_STATE(GG_STATE_RESOLVE_GG_ASYNC)
+	GG_DEBUG_STATE(GG_STATE_RESOLVING_HUB)
+	GG_DEBUG_STATE(GG_STATE_RESOLVING_PROXY_HUB)
+	GG_DEBUG_STATE(GG_STATE_RESOLVING_PROXY_GG)
+	GG_DEBUG_STATE(GG_STATE_SEND_HUB)
+	GG_DEBUG_STATE(GG_STATE_SEND_PROXY_HUB)
+	GG_DEBUG_STATE(GG_STATE_SEND_PROXY_GG)
+	GG_DEBUG_STATE(GG_STATE_SENDING_HUB)
+	GG_DEBUG_STATE(GG_STATE_SENDING_PROXY_HUB)
+	GG_DEBUG_STATE(GG_STATE_SENDING_PROXY_GG)
+	GG_DEBUG_STATE(GG_STATE_READING_HUB)
+	GG_DEBUG_STATE(GG_STATE_READING_PROXY_HUB)
+	GG_DEBUG_STATE(GG_STATE_READING_PROXY_GG)
 #undef GG_DEBUG_STATE
 
 	/* Celowo nie ma default, żeby kompilator wyłapał brakujące stany */
-	
 	}
 
 	return NULL;
 }
 
 /**
  * \internal Zwraca ciąg z nazwą podanego zdarzenia.
  *
@@ -318,27 +343,35 @@ const char *gg_debug_event(enum gg_event
 	GG_DEBUG_EVENT(GG_EVENT_DCC7_NEW)
 	GG_DEBUG_EVENT(GG_EVENT_DCC7_ACCEPT)
 	GG_DEBUG_EVENT(GG_EVENT_DCC7_REJECT)
 	GG_DEBUG_EVENT(GG_EVENT_DCC7_CONNECTED)
 	GG_DEBUG_EVENT(GG_EVENT_DCC7_ERROR)
 	GG_DEBUG_EVENT(GG_EVENT_DCC7_DONE)
 	GG_DEBUG_EVENT(GG_EVENT_DCC7_PENDING)
 	GG_DEBUG_EVENT(GG_EVENT_XML_EVENT)
+	GG_DEBUG_EVENT(GG_EVENT_JSON_EVENT)
+	GG_DEBUG_EVENT(GG_EVENT_ACK110)
 	GG_DEBUG_EVENT(GG_EVENT_DISCONNECT_ACK)
 	GG_DEBUG_EVENT(GG_EVENT_TYPING_NOTIFICATION)
 	GG_DEBUG_EVENT(GG_EVENT_USER_DATA)
 	GG_DEBUG_EVENT(GG_EVENT_MULTILOGON_MSG)
 	GG_DEBUG_EVENT(GG_EVENT_MULTILOGON_INFO)
 	GG_DEBUG_EVENT(GG_EVENT_USERLIST100_VERSION)
 	GG_DEBUG_EVENT(GG_EVENT_USERLIST100_REPLY)
+	GG_DEBUG_EVENT(GG_EVENT_IMTOKEN)
+	GG_DEBUG_EVENT(GG_EVENT_PONG110)
+	GG_DEBUG_EVENT(GG_EVENT_CHAT_INFO)
+	GG_DEBUG_EVENT(GG_EVENT_CHAT_INFO_GOT_ALL)
+	GG_DEBUG_EVENT(GG_EVENT_CHAT_INFO_UPDATE)
+	GG_DEBUG_EVENT(GG_EVENT_CHAT_CREATED)
+	GG_DEBUG_EVENT(GG_EVENT_CHAT_INVITE_ACK)
 #undef GG_DEBUG_EVENT
 
 	/* Celowo nie ma default, żeby kompilator wyłapał brakujące zdarzenia */
-	
 	}
 
 	return NULL;
 }
 
 #else
 
 #undef gg_debug_common
rename from libpurple/protocols/gg/lib/libgadu-debug.h
rename to libpurple/protocols/gg/lib/debug.h
--- a/libpurple/protocols/gg/lib/libgadu-debug.h
+++ b/libpurple/protocols/gg/lib/debug.h
@@ -16,14 +16,12 @@
  *  USA.
  */
 
 #ifndef LIBGADU_DEBUG_H
 #define LIBGADU_DEBUG_H
 
 #include "libgadu.h"
 
-const char *gg_debug_state(enum gg_state_t state);
-const char *gg_debug_event(enum gg_event_t event);
 void gg_debug_dump(struct gg_session *sess, int level, const char *buf, size_t len);
 void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap);
 
 #endif /* LIBGADU_DEBUG_H */
--- a/libpurple/protocols/gg/lib/deflate.c
+++ b/libpurple/protocols/gg/lib/deflate.c
@@ -23,26 +23,27 @@
  *
  * \brief Funkcje kompresji Deflate
  */
 
 #include <stdlib.h>
 #include <string.h>
 
 #include "libgadu.h"
+#include "internal.h"
 #include "deflate.h"
 
 #ifdef GG_CONFIG_HAVE_ZLIB
 #include <zlib.h>
 #endif
 
 /**
  * \internal Kompresuje dane wejściowe algorytmem Deflate z najwyższym
  * stopniem kompresji, tak samo jak oryginalny klient.
- * 
+ *
  * Wynik funkcji należy zwolnić za pomocą \c free.
  *
  * \param in Ciąg znaków do skompresowania, zakończony \c \\0
  * \param out_lenp Wskaźnik na zmienną, do której zostanie zapisana
  *                 długość bufora wynikowego
  *
  * \return Skompresowany ciąg znaków lub \c NULL w przypadku niepowodzenia.
  */
@@ -68,55 +69,62 @@ unsigned char *gg_deflate(const char *in
 		gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflateInit() failed (%d)\n", ret);
 		return NULL;
 	}
 
 	out_len = deflateBound(&strm, strm.avail_in);
 	out = malloc(out_len);
 
 	if (out == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len);
+		gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for "
+			"output data (%" GG_SIZE_FMT ")\n", out_len);
 		goto fail;
 	}
 
 	strm.avail_out = out_len;
 	strm.next_out = out;
 
 	for (;;) {
 		ret = deflate(&strm, Z_FINISH);
-		
+
 		if (ret == Z_STREAM_END)
 			break;
-		
+
 		/* raczej nie powinno się zdarzyć przy Z_FINISH i out_len == deflateBound(),
 		 * ale dokumentacja zlib nie wyklucza takiej możliwości */
 		if (ret == Z_OK) {
 			out_len *= 2;
 			out2 = realloc(out, out_len);
-			
+
 			if (out2 == NULL) {
-				gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len);
+				gg_debug(GG_DEBUG_MISC, "// gg_deflate() not "
+					"enough memory for output data (%"
+					GG_SIZE_FMT ")\n", out_len);
 				goto fail;
 			}
-			
+
 			out = out2;
-			
+
 			strm.avail_out = out_len / 2;
 			strm.next_out = out + out_len / 2;
 		} else {
-			gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflate() failed (ret=%d, msg=%s)\n", ret, strm.msg != NULL ? strm.msg : "no error message provided");
+			gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflate() "
+				"failed (ret=%d, msg=%s)\n", ret,
+				strm.msg != NULL ? strm.msg :
+				"no error message provided");
 			goto fail;
 		}
 	}
 
 	out_len = strm.total_out;
 	out2 = realloc(out, out_len);
 
 	if (out2 == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for output data (%d)\n", out_len);
+		gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for "
+			"output data (%" GG_SIZE_FMT ")\n", out_len);
 		goto fail;
 	}
 
 	*out_lenp = out_len;
 	deflateEnd(&strm);
 
 	return out2;
 
@@ -163,57 +171,61 @@ char *gg_inflate(const unsigned char *in
 	if (ret != Z_OK) {
 		gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflateInit() failed (%d)\n", ret);
 		return NULL;
 	}
 
 	do {
 		out_len *= 2;
 		out2 = realloc(out, out_len);
-		
+
 		if (out2 == NULL) {
-			gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough memory for output data (%d)\n", out_len);
+			gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough "
+				"memory for output data (%" GG_SIZE_FMT ")\n", out_len);
 			goto fail;
 		}
-		
+
 		out = out2;
-		
+
 		if (first) {
 			strm.avail_out = out_len;
 			strm.next_out = (unsigned char*) out;
 		} else {
 			strm.avail_out = out_len / 2;
 			strm.next_out = (unsigned char*) out + out_len / 2;
 		}
-		
+
 		ret = inflate(&strm, Z_NO_FLUSH);
-		
+
 		if (ret != Z_OK && ret != Z_STREAM_END) {
-			gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflate() failed (ret=%d, msg=%s)\n", ret, strm.msg != NULL ? strm.msg : "no error message provided");
+			gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflate() "
+				"failed (ret=%d, msg=%s)\n", ret,
+				strm.msg != NULL ? strm.msg :
+				"no error message provided");
 			goto fail;
 		}
-		
+
 		first = 0;
 	} while (ret != Z_STREAM_END);
 
 	/* rezerwujemy ostatni znak na NULL-a */
 	out_len = strm.total_out + 1;
 	out2 = realloc(out, out_len);
 
 	if (out2 == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough memory for output data (%d)\n", out_len);
+		gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough memory for "
+			"output data (%" GG_SIZE_FMT ")\n", out_len);
 		goto fail;
 	}
 
 	out = out2;
 	out[out_len - 1] = '\0';
 
 	inflateEnd(&strm);
 
 	return out;
 
 fail:
 	inflateEnd(&strm);
 	free(out);
 #endif
 	return NULL;
 }
-
--- a/libpurple/protocols/gg/lib/deflate.h
+++ b/libpurple/protocols/gg/lib/deflate.h
@@ -1,12 +1,12 @@
 /* $Id$ */
 
 /*
- *  (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *  (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
  *  2.1 as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- a/libpurple/protocols/gg/lib/encoding.c
+++ b/libpurple/protocols/gg/lib/encoding.c
@@ -12,50 +12,50 @@
  *  GNU Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+#include "strman.h"
 #include <stdlib.h>
-#include <string.h>
 #include <errno.h>
 
 #include "libgadu.h"
 #include "encoding.h"
 
 /**
  * \file encoding.c
  *
  * \brief Funkcje konwersji kodowania tekstu
  */
 
 /**
  * \internal Tablica konwersji CP1250 na Unikod.
  */
 static const uint16_t table_cp1250[] =
 {
-        0x20ac, '?',    0x201a, '?',    0x201e, 0x2026, 0x2020, 0x2021,
-        '?',    0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,
-        '?',    0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
-        '?',    0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,
-        0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,
-        0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,
-        0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
-        0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,
-        0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,
-        0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,
-        0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,
-        0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,
-        0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,
-        0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,
-        0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,
-        0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9,
+	0x20ac, '?',    0x201a, '?',    0x201e, 0x2026, 0x2020, 0x2021,
+	'?',    0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,
+	'?',    0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
+	'?',    0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,
+	0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,
+	0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,
+	0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+	0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,
+	0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,
+	0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,
+	0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,
+	0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,
+	0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,
+	0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,
+	0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,
+	0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9,
 };
 
 /**
  * \internal Zamienia tekst kodowany CP1250 na UTF-8.
  *
  * \param src Tekst źródłowy w CP1250.
  * \param src_length Długość ciągu źródłowego (nigdy ujemna).
  * \param dst_length Długość ciągu docelowego (jeśli -1, nieograniczona).
@@ -83,29 +83,29 @@ static char *gg_encoding_convert_cp1250_
 			len += 3;
 	}
 
 	if ((dst_length != -1) && (len > dst_length))
 		len = dst_length;
 
 	result = malloc(len + 1);
 
-	if (result == NULL) 
+	if (result == NULL)
 		return NULL;
 
 	for (i = 0, j = 0; (src[i] != 0) && (i < src_length) && (j < len); i++) {
 		uint16_t uc;
 
 		if ((unsigned char) src[i] < 0x80)
 			uc = (unsigned char) src[i];
 		else
 			uc = table_cp1250[(unsigned char) src[i] - 128];
 
-		if (uc < 0x80) 
-			result[j++] = uc;
+		if (uc < 0x80)
+			result[j++] = (char) uc;
 		else if (uc < 0x800) {
 			if (j + 1 > len)
 				break;
 			result[j++] = 0xc0 | ((uc >> 6) & 0x1f);
 			result[j++] = 0x80 | (uc & 0x3f);
 		} else {
 			if (j + 2 > len)
 				break;
@@ -131,52 +131,49 @@ static char *gg_encoding_convert_cp1250_
  */
 static char *gg_encoding_convert_utf8_cp1250(const char *src, int src_length, int dst_length)
 {
 	char *result;
 	int i, j, len, uc_left = 0;
 	uint32_t uc = 0, uc_min = 0;
 
 	for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) {
-		if ((src[i] & 0xc0) == 0xc0) {
+		if ((src[i] & 0xc0) != 0x80)
 			len++;
-		} else if ((src[i] & 0x80) == 0x00) {
-			len++;
-		}
 	}
 
 	if ((dst_length != -1) && (len > dst_length))
 		len = dst_length;
 
 	result = malloc(len + 1);
 
 	if (result == NULL)
 		return NULL;
 
 	for (i = 0, j = 0; (src[i] != 0) && (i < src_length) && (j < len); i++) {
 		if ((unsigned char) src[i] >= 0xf5) {
-			if (uc_left != 0) 
+			if (uc_left != 0)
 				result[j++] = '?';
 			/* Restricted sequences */
 			result[j++] = '?';
 			uc_left = 0;
 		} else if ((src[i] & 0xf8) == 0xf0) {
-			if (uc_left != 0) 
+			if (uc_left != 0)
 				result[j++] = '?';
 			uc = src[i] & 0x07;
 			uc_left = 3;
 			uc_min = 0x10000;
 		} else if ((src[i] & 0xf0) == 0xe0) {
-			if (uc_left != 0) 
+			if (uc_left != 0)
 				result[j++] = '?';
 			uc = src[i] & 0x0f;
 			uc_left = 2;
 			uc_min = 0x800;
 		} else if ((src[i] & 0xe0) == 0xc0) {
-			if (uc_left != 0) 
+			if (uc_left != 0)
 				result[j++] = '?';
 			uc = src[i] & 0x1f;
 			uc_left = 1;
 			uc_min = 0x80;
 		} else if ((src[i] & 0xc0) == 0x80) {
 			if (uc_left > 0) {
 				uc <<= 6;
 				uc |= src[i] & 0x3f;
@@ -218,31 +215,32 @@ static char *gg_encoding_convert_utf8_cp
 }
 
 /**
  * \internal Zamienia kodowanie tekstu.
  *
  * \param src Tekst źródłowy.
  * \param src_encoding Kodowanie tekstu źródłowego.
  * \param dst_encoding Kodowanie tekstu docelowego.
- * \param src_length Długość ciągu źródłowego w bajtach (nigdy ujemna).
+ * \param src_length Długość ciągu źródłowego w bajtach (jeśli -1, zostanie obliczona na podstawie zawartości \p src).
  * \param dst_length Długość ciągu docelowego w bajtach (jeśli -1, nieograniczona).
  *
  * \return Zaalokowany bufor z tekstem w kodowaniu docelowym.
  */
-char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding, gg_encoding_t dst_encoding, int src_length, int dst_length)
+char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding,
+	gg_encoding_t dst_encoding, int src_length, int dst_length)
 {
 	char *result;
 
 	if (src == NULL) {
 		errno = EINVAL;
 		return NULL;
 	}
 
-	// specjalny przypadek obsługiwany ekspresowo
+	/* specjalny przypadek obsługiwany ekspresowo */
 	if ((dst_encoding == src_encoding) && (dst_length == -1) && (src_length == -1))
 		return strdup(src);
 
 	if (src_length == -1)
 		src_length = strlen(src);
 
 	if (dst_encoding == src_encoding) {
 		int len;
@@ -267,9 +265,8 @@ char *gg_encoding_convert(const char *sr
 		return gg_encoding_convert_utf8_cp1250(src, src_length, dst_length);
 
 	if (dst_encoding == GG_ENCODING_UTF8 && src_encoding == GG_ENCODING_CP1250)
 		return gg_encoding_convert_cp1250_utf8(src, src_length, dst_length);
 
 	errno = EINVAL;
 	return NULL;
 }
-
--- a/libpurple/protocols/gg/lib/encoding.h
+++ b/libpurple/protocols/gg/lib/encoding.h
@@ -17,11 +17,12 @@
  *  USA.
  */
 
 #ifndef LIBGADU_ENCODING_H
 #define LIBGADU_ENCODING_H
 
 #include "libgadu.h"
 
-char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding, gg_encoding_t dst_encoding, int src_length, int dst_length);
+char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding,
+	gg_encoding_t dst_encoding, int src_length, int dst_length);
 
 #endif /* LIBGADU_SESSION_H */
new file mode 100644
--- /dev/null
+++ b/libpurple/protocols/gg/lib/endian.c
@@ -0,0 +1,109 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+/**
+ * \file endian.c
+ *
+ * \brief Konwersja między różnymi kolejnościami bajtów
+ */
+
+#include "libgadu.h"
+#include "internal.h"
+
+/**
+ * \internal Zamienia kolejność bajtów w 64-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejność bajtów w słowie.
+ *
+ * \param x Liczba do zamiany
+ *
+ * \return Liczba z odpowiednią kolejnością bajtów
+ *
+ * \ingroup helper
+ */
+uint64_t gg_fix64(uint64_t x)
+{
+#ifndef GG_CONFIG_BIGENDIAN
+	return x;
+#else
+	return (uint64_t)
+		(((x & (uint64_t) 0x00000000000000ffULL) << 56) |
+		((x & (uint64_t) 0x000000000000ff00ULL) << 40) |
+		((x & (uint64_t) 0x0000000000ff0000ULL) << 24) |
+		((x & (uint64_t) 0x00000000ff000000ULL) << 8) |
+		((x & (uint64_t) 0x000000ff00000000ULL) >> 8) |
+		((x & (uint64_t) 0x0000ff0000000000ULL) >> 24) |
+		((x & (uint64_t) 0x00ff000000000000ULL) >> 40) |
+		((x & (uint64_t) 0xff00000000000000ULL) >> 56));
+#endif
+}
+
+/**
+ * \internal Zamienia kolejność bajtów w 32-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejność bajtów w słowie.
+ *
+ * \param x Liczba do zamiany
+ *
+ * \return Liczba z odpowiednią kolejnością bajtów
+ *
+ * \ingroup helper
+ */
+uint32_t gg_fix32(uint32_t x)
+{
+#ifndef GG_CONFIG_BIGENDIAN
+	return x;
+#else
+	return (uint32_t)
+		(((x & (uint32_t) 0x000000ffU) << 24) |
+		((x & (uint32_t) 0x0000ff00U) << 8) |
+		((x & (uint32_t) 0x00ff0000U) >> 8) |
+		((x & (uint32_t) 0xff000000U) >> 24));
+#endif
+}
+
+/**
+ * \internal Zamienia kolejność bajtów w 16-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych zamienia kolejność bajtów w słowie.
+ *
+ * \param x Liczba do zamiany
+ *
+ * \return Liczba z odpowiednią kolejnością bajtów
+ *
+ * \ingroup helper
+ */
+uint16_t gg_fix16(uint16_t x)
+{
+#ifndef GG_CONFIG_BIGENDIAN
+	return x;
+#else
+	return (uint16_t)
+		(((x & (uint16_t) 0x00ffU) << 8) |
+		((x & (uint16_t) 0xff00U) >> 8));
+#endif
+}
--- a/libpurple/protocols/gg/lib/events.c
+++ b/libpurple/protocols/gg/lib/events.c
@@ -1,9 +1,9 @@
-/* $Id: events.c 1105 2011-05-25 21:34:50Z wojtekka $ */
+/* $Id$ */
 
 /*
  *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
  *                          Robert J. Woźny <speedy@ziew.org>
  *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
  *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -20,52 +20,45 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
 /**
  * \file events.c
  *
  * \brief Obsługa zdarzeń
+ *
+ * \todo Poprawna obsługa gg_proxy_http_only
  */
 
-#include <sys/types.h>
-#ifndef _WIN32
-#  include <sys/ioctl.h>
-#  include <sys/socket.h>
-#  include <netinet/in.h>
-#  include <arpa/inet.h>
-#endif
-#include <ctype.h>
+#include "strman.h"
+#include "network.h"
 
-#include "compat.h"
 #include "libgadu.h"
-#include "libgadu-config.h"
 #include "protocol.h"
-#include "libgadu-internal.h"
+#include "internal.h"
 #include "encoding.h"
-#include "libgadu-debug.h"
+#include "debug.h"
 #include "session.h"
+#include "resolver.h"
+#include "config.h"
 
 #include <errno.h>
-#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <time.h>
-#ifndef _MSC_VER
-#  include <unistd.h>
-#endif
 #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>
+#  include <openssl/rand.h>
 #endif
 
 /**
  * Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
  *
  * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci
  * strukturę \c gg_event.
  *
@@ -136,49 +129,62 @@ void gg_event_free(struct gg_event *e)
 			free(e->event.image_reply.filename);
 			free(e->event.image_reply.image);
 			break;
 
 		case GG_EVENT_XML_EVENT:
 			free(e->event.xml_event.data);
 			break;
 
+		case GG_EVENT_JSON_EVENT:
+			free(e->event.json_event.data);
+			free(e->event.json_event.type);
+			break;
+
 		case GG_EVENT_USER_DATA:
 		{
-			int i, j;
+			unsigned 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;
 		}
 
 		case GG_EVENT_USERLIST100_REPLY:
 			free(e->event.userlist100_reply.reply);
 			break;
+
+		case GG_EVENT_IMTOKEN:
+			free(e->event.imtoken.imtoken);
+			break;
+
+		case GG_EVENT_CHAT_INFO:
+			free(e->event.chat_info.participants);
+			break;
 	}
 
 	free(e);
 }
 
 /** \cond internal */
 
 /**
@@ -217,809 +223,1675 @@ int gg_image_queue_remove(struct gg_sess
 	}
 
 	return 0;
 }
 
 /** \endcond */
 
 /**
+ * \internal Inicjalizuje struktury SSL.
+ *
+ * \param gs Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
+ */
+int gg_session_init_ssl(struct gg_session *gs)
+{
+#ifdef GG_CONFIG_HAVE_GNUTLS
+	gg_session_gnutls_t *tmp;
+
+	tmp = (gg_session_gnutls_t*) gs->ssl;
+
+	if (tmp == NULL) {
+		tmp = malloc(sizeof(gg_session_gnutls_t));
+
+		if (tmp == NULL) {
+			gg_debug(GG_DEBUG_MISC, "// gg_session_connect() out of memory for GnuTLS session\n");
+			return -1;
+		}
+
+		memset(tmp, 0, sizeof(gg_session_gnutls_t));
+
+		gs->ssl = tmp;
+
+		gnutls_global_init();
+		gnutls_certificate_allocate_credentials(&tmp->xcred);
+#ifdef GG_CONFIG_SSL_SYSTEM_TRUST
+#ifdef HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST
+		gnutls_certificate_set_x509_system_trust(tmp->xcred);
+#else
+		gnutls_certificate_set_x509_trust_file(tmp->xcred,
+			GG_CONFIG_GNUTLS_SYSTEM_TRUST_STORE,
+			GNUTLS_X509_FMT_PEM);
+#endif
+#endif
+	} else {
+		gnutls_deinit(tmp->session);
+	}
+
+	gnutls_init(&tmp->session, GNUTLS_CLIENT);
+	gnutls_set_default_priority(tmp->session);
+	gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred);
+	gnutls_transport_set_ptr(tmp->session, (gnutls_transport_ptr_t) (intptr_t) gs->fd);
+#endif
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+	char buf[1024];
+
+	OpenSSL_add_ssl_algorithms();
+
+	if (!RAND_status()) {
+		char rdata[1024];
+		struct {
+			time_t time;
+			void *ptr;
+		} rstruct;
+
+		time(&rstruct.time);
+		rstruct.ptr = (void *) &rstruct;
+
+		RAND_seed((void *) rdata, sizeof(rdata));
+		RAND_seed((void *) &rstruct, sizeof(rstruct));
+	}
+
+	if (gs->ssl_ctx == NULL) {
+		gs->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
+
+		if (gs->ssl_ctx == NULL) {
+			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+			gg_debug(GG_DEBUG_MISC, "// gg_session_connect() SSL_CTX_new() failed: %s\n", buf);
+			return -1;
+		}
+
+		SSL_CTX_set_verify(gs->ssl_ctx, SSL_VERIFY_NONE, NULL);
+#ifdef GG_CONFIG_SSL_SYSTEM_TRUST
+		SSL_CTX_set_default_verify_paths(gs->ssl_ctx);
+#endif
+	}
+
+	if (gs->ssl != NULL)
+		SSL_free(gs->ssl);
+
+	gs->ssl = SSL_new(gs->ssl_ctx);
+
+	if (gs->ssl == NULL) {
+		ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+		gg_debug(GG_DEBUG_MISC, "// gg_session_connect() SSL_new() failed: %s\n", buf);
+		return -1;
+	}
+
+	SSL_set_fd(gs->ssl, gs->fd);
+#endif
+
+	return 0;
+}
+
+/**
+ * \internal Funkcja próbuje wysłać dane zakolejkowane do wysyłki.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
+ */
+static int gg_send_queued_data(struct gg_session *sess)
+{
+	int res;
+
+	if (sess->send_buf == NULL || sess->send_left == 0)
+		return 0;
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
+
+	res = send(sess->fd, sess->send_buf, sess->send_left, 0);
+
+	if (res == -1) {
+		if (errno == EAGAIN || errno == EINTR) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
+				" non-critical send error (errno=%d, %s)\n",
+				errno, strerror(errno));
+
+			return 0;
+		}
+
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() send() "
+			"failed (errno=%d, %s)\n", errno, strerror(errno));
+
+		return -1;
+	}
+
+	if (res == sess->send_left) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
+		free(sess->send_buf);
+		sess->send_buf = NULL;
+		sess->send_left = 0;
+	} else if (res > 0) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d"
+			" bytes of queued data, %d bytes left\n",
+			res, sess->send_left - res);
+
+		memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
+		sess->send_left -= res;
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Sprawdza wynik połączenia asynchronicznego.
+ * \param gs Struktura sesji
+ * \param res_ptr Wskaźnik na kod błędu
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
+ */
+static int gg_async_connect_failed(struct gg_session *gs, int *res_ptr)
+{
+	int res = 0;
+	socklen_t res_size = sizeof(res);
+
+	if (!gs->async)
+		return 0;
+
+	if (gs->timeout == 0) {
+		*res_ptr = ETIMEDOUT;
+		return 1;
+	}
+
+	if (getsockopt(gs->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) == -1) {
+		*res_ptr = errno;
+		return 1;
+	}
+
+	if (res != 0) {
+		*res_ptr = res;
+		return 1;
+	}
+
+	*res_ptr = 0;
+
+	return 0;
+}
+
+typedef enum
+{
+	GG_ACTION_WAIT,
+	GG_ACTION_NEXT,
+	GG_ACTION_FAIL
+} gg_action_t;
+
+typedef gg_action_t (*gg_state_handler_t)(struct gg_session *gs,
+	struct gg_event *ge, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state);
+
+typedef struct
+{
+	enum gg_state_t state;
+	gg_state_handler_t handler;
+	enum gg_state_t next_state;
+	enum gg_state_t alt_state;
+	enum gg_state_t alt2_state;
+} gg_state_transition_t;
+
+/* zwraca:
+ * -1 w przypadku błędu
+ * 0 jeżeli nie ma ustawionego specjalnego managera gniazdek
+ * 1 w przypadku powodzenia
+ */
+static int gg_handle_resolve_custom(struct gg_session *sess, enum gg_state_t next_state)
+{
+	struct gg_session_private *p = sess->private_data;
+	int is_tls = 0;
+	int port;
+
+	if (p->socket_manager_type == GG_SOCKET_MANAGER_TYPE_INTERNAL)
+		return 0;
+
+	if (p->socket_manager.connect_cb == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
+			"// gg_handle_resolve_custom() socket_manager.connect "
+			"callback is empty\n");
+		return -1;
+	}
+
+	if (p->socket_handle != NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
+			"// gg_handle_resolve_custom() socket_handle is not "
+			"NULL\n");
+		return -1;
+	}
+
+	port = sess->connect_port[sess->connect_index];
+	if (next_state == GG_STATE_SEND_HUB)
+		port = GG_APPMSG_PORT;
+
+	if (sess->ssl_flag != GG_SSL_DISABLED &&
+		next_state == GG_STATE_READING_KEY)
+	{
+		/* XXX: w tej chwili nie ma możliwości łączenia się do HUBa po
+		 * SSL, ale może będzie w przyszłości */
+		is_tls = 1;
+	}
+
+	if (is_tls && p->socket_manager_type == GG_SOCKET_MANAGER_TYPE_TCP) {
+		is_tls = 0;
+		next_state = GG_STATE_TLS_NEGOTIATION;
+	}
+
+	if (port <= 0) {
+		gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
+			"// gg_handle_resolve_custom() port <= 0\n");
+		return -1;
+	}
+
+	p->socket_failure = 0;
+	p->socket_next_state = next_state;
+	p->socket_handle = p->socket_manager.connect_cb(
+		p->socket_manager.cb_data, sess->resolver_host, port, is_tls,
+		sess->async, sess);
+
+	if (p->socket_failure != 0) {
+		if (p->socket_handle != NULL) {
+			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_WARNING,
+				"// gg_handle_resolve_custom() handle should be"
+				" empty on error\n");
+		}
+		return -1;
+	}
+
+	if (p->socket_handle == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
+			"// gg_handle_resolve_custom() returned empty "
+			"handle\n");
+		return -1;
+	}
+
+	return 1;
+}
+
+static gg_action_t gg_handle_resolve_sync(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	struct in_addr addr;
+	int res;
+
+	res = gg_handle_resolve_custom(sess, alt_state);
+	if (res == 1)
+		return GG_ACTION_NEXT;
+	else if (res == -1)
+		return GG_ACTION_FAIL;
+
+	addr.s_addr = inet_addr(sess->resolver_host);
+
+	if (addr.s_addr == INADDR_NONE) {
+		struct in_addr *addr_list = NULL;
+		unsigned int addr_count;
+
+		if (gg_gethostbyname_real(sess->resolver_host, &addr_list, &addr_count, 0) == -1) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
+				" host %s not found\n", sess->resolver_host);
+			e->event.failure = GG_FAILURE_RESOLVING;
+			free(addr_list);
+			return GG_ACTION_FAIL;
+		}
+
+		sess->resolver_result = addr_list;
+		sess->resolver_count = addr_count;
+		sess->resolver_index = 0;
+	} else {
+		sess->resolver_result = malloc(sizeof(struct in_addr));
+
+		if (sess->resolver_result == NULL) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
+			return GG_ACTION_FAIL;
+		}
+
+		sess->resolver_result[0].s_addr = addr.s_addr;
+		sess->resolver_count = 1;
+		sess->resolver_index = 0;
+	}
+
+	sess->state = next_state;
+
+	return GG_ACTION_NEXT;
+}
+
+static gg_action_t gg_handle_resolve_async(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	int res;
+
+	res = gg_handle_resolve_custom(sess, alt_state);
+	if (res == 1)
+		return GG_ACTION_WAIT;
+	else if (res == -1)
+		return GG_ACTION_FAIL;
+
+	if (sess->resolver_start(&sess->fd, &sess->resolver, sess->resolver_host) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"resolving failed (errno=%d, %s)\n",
+			errno, strerror(errno));
+		e->event.failure = GG_FAILURE_RESOLVING;
+		return GG_ACTION_FAIL;
+	}
+
+	sess->state = next_state;
+	sess->check = GG_CHECK_READ;
+	sess->timeout = GG_DEFAULT_TIMEOUT;
+
+	return GG_ACTION_WAIT;
+}
+
+static gg_action_t gg_handle_resolving(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	char buf[256];
+	int count = -1;
+	int res;
+	unsigned int i;
+	struct in_addr *addrs;
+
+	res = gg_resolver_recv(sess->fd, buf, sizeof(buf));
+
+	if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"non-critical error (errno=%d, %s)\n",
+			errno, strerror(errno));
+		return GG_ACTION_WAIT;
+	}
+
+	sess->resolver_cleanup(&sess->resolver, 0);
+
+	if (res == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() read "
+			"error (errno=%d, %s)\n", errno, strerror(errno));
+		e->event.failure = GG_FAILURE_RESOLVING;
+		return GG_ACTION_FAIL;
+	}
+
+	if (res > 0) {
+		char *tmp;
+
+		tmp = realloc(sess->recv_buf, sess->recv_done + res);
+
+		if (tmp == NULL)
+			return GG_ACTION_FAIL;
+
+		sess->recv_buf = tmp;
+		memcpy(sess->recv_buf + sess->recv_done, buf, res);
+		sess->recv_done += res;
+	}
+
+	/* Sprawdź, czy mamy listę zakończoną INADDR_NONE */
+
+	addrs = (struct in_addr *)(void *)sess->recv_buf;
+
+	for (i = 0; i < sess->recv_done / sizeof(struct in_addr); i++) {
+		if (addrs[i].s_addr == INADDR_NONE) {
+			count = i;
+			break;
+		}
+	}
+
+	/* Nie znaleziono hosta */
+
+	if (count == 0) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() host not found\n");
+		e->event.failure = GG_FAILURE_RESOLVING;
+		return GG_ACTION_FAIL;
+	}
+
+	/* Nie mamy pełnej listy, ale połączenie zerwane */
+
+	if (res == 0 && count == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection broken\n");
+		e->event.failure = GG_FAILURE_RESOLVING;
+		return GG_ACTION_FAIL;
+	}
+
+	/* Nie mamy pełnej listy, normalna sytuacja */
+
+	if (count == -1)
+		return GG_ACTION_WAIT;
+
+#ifndef GG_DEBUG_DISABLE
+	if ((gg_debug_level & GG_DEBUG_DUMP) && (count > 0)) {
+		char *list;
+		size_t len;
+
+		len = 0;
+
+		for (i = 0; i < (unsigned int) count; i++) {
+			if (i > 0)
+				len += 2;
+
+			len += strlen(inet_ntoa(addrs[i]));
+		}
+
+		list = malloc(len + 1);
+
+		if (list == NULL)
+			return GG_ACTION_FAIL;
+
+		list[0] = 0;
+
+		for (i = 0; i < (unsigned int) count; i++) {
+			if (i > 0)
+				strcat(list, ", ");
+
+			strcat(list, inet_ntoa(addrs[i]));
+		}
+
+		gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() resolved: %s\n", list);
+
+		free(list);
+	}
+#endif
+
+	gg_close(sess);
+
+	sess->state = next_state;
+	sess->resolver_result = addrs;
+	sess->resolver_count = count;
+	sess->resolver_index = 0;
+	sess->recv_buf = NULL;
+	sess->recv_done = 0;
+
+	return GG_ACTION_NEXT;
+}
+
+static gg_action_t gg_handle_connect(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	struct in_addr addr;
+	int port;
+
+	if (sess->resolver_index >= sess->resolver_count) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of addresses to connect to\n");
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	addr = sess->resolver_result[sess->resolver_index];
+
+	if (sess->state == GG_STATE_CONNECT_HUB) {
+		sess->hub_addr = addr.s_addr;
+		port = GG_APPMSG_PORT;
+	} else {
+		sess->proxy_addr = addr.s_addr;
+		port = sess->proxy_port;
+	}
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connecting to %s:%d\n", inet_ntoa(addr), port);
+
+	sess->fd = gg_connect(&addr, port, sess->async);
+
+	if (sess->fd == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"connection failed (errno=%d, %s)\n",
+			errno, strerror(errno));
+		sess->resolver_index++;
+		return GG_ACTION_NEXT;
+	}
+
+	sess->state = next_state;
+	sess->check = GG_CHECK_WRITE;
+	sess->timeout = GG_DEFAULT_TIMEOUT;
+	sess->soft_timeout = 1;
+
+	return GG_ACTION_WAIT;
+}
+
+static gg_action_t gg_handle_connecting(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	int res;
+
+	sess->soft_timeout = 0;
+
+	if (gg_async_connect_failed(sess, &res)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"connection failed (errno=%d, %s)\n",
+			res, strerror(res));
+		gg_close(sess);
+		sess->resolver_index++;
+		sess->state = alt_state;
+	} else {
+		/* Z proxy zwykle łączymy się dwa razy, więc nie zwalniamy
+		 * adresów IP po pierwszym połączeniu. */
+		if (sess->state != GG_STATE_CONNECTING_PROXY_HUB) {
+			free(sess->resolver_result);
+			sess->resolver_result = NULL;
+		}
+
+		sess->state = next_state;
+	}
+
+	return GG_ACTION_NEXT;
+}
+
+static gg_action_t gg_handle_connect_gg(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	struct in_addr addr;
+	uint16_t port;
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "resolver_index=%d, "
+		"connect_index=%d, connect_port={%d,%d}\n",
+		sess->resolver_index, sess->connect_index,
+		sess->connect_port[0], sess->connect_port[1]);
+
+	if ((unsigned int) sess->connect_index >=
+		sizeof(sess->connect_port) / sizeof(sess->connect_port[0]) ||
+		sess->connect_port[sess->connect_index] == 0)
+	{
+		sess->connect_index = 0;
+		sess->resolver_index++;
+		if (sess->resolver_index >= sess->resolver_count) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of addresses to connect to\n");
+			e->event.failure = GG_FAILURE_CONNECTING;
+			return GG_ACTION_FAIL;
+		}
+	}
+
+	addr = sess->resolver_result[sess->resolver_index];
+	port = sess->connect_port[sess->connect_index];
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connecting to %s:%d\n", inet_ntoa(addr), port);
+
+	sess->server_addr = addr.s_addr;
+	sess->fd = gg_connect(&addr, port, sess->async);
+
+	if (sess->fd == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"connection failed (errno=%d, %s)\n",
+			errno, strerror(errno));
+		sess->connect_index++;
+		return GG_ACTION_NEXT;
+	}
+
+	sess->state = next_state;
+	sess->check = GG_CHECK_WRITE;
+	sess->timeout = GG_DEFAULT_TIMEOUT;
+	sess->soft_timeout = 1;
+
+	return GG_ACTION_WAIT;
+}
+
+static gg_action_t gg_handle_connecting_gg(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	int res;
+
+	sess->soft_timeout = 0;
+
+	/* jeśli wystąpił błąd podczas łączenia się... */
+	if (gg_async_connect_failed(sess, &res)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"connection failed (errno=%d, %s)\n",
+			res, strerror(res));
+		gg_close(sess);
+		sess->connect_index++;
+		sess->state = alt_state;
+		return GG_ACTION_NEXT;
+	}
+
+	free(sess->resolver_result);
+	sess->resolver_result = NULL;
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+
+	if (sess->ssl_flag != GG_SSL_DISABLED) {
+		if (gg_session_init_ssl(sess) == -1) {
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+
+		sess->state = alt2_state;
+		sess->check = GG_CHECK_WRITE;
+		sess->timeout = GG_DEFAULT_TIMEOUT;
+
+		return GG_ACTION_NEXT;
+	} else {
+		sess->state = next_state;
+		sess->check = GG_CHECK_READ;
+		sess->timeout = GG_DEFAULT_TIMEOUT;
+
+		return GG_ACTION_WAIT;
+	}
+}
+
+static gg_action_t gg_handle_send_hub(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	char *req, *client, *auth;
+	const char *host;
+	int res;
+	int proxy;
+	size_t req_len;
+
+	if (sess->client_version != NULL && isdigit(sess->client_version[0]))
+		client = gg_urlencode(sess->client_version);
+	else if (sess->protocol_version <= GG_PROTOCOL_VERSION_100)
+		client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION_100);
+	else /* sess->protocol_version >= GG_PROTOCOL_VERSION_110 */
+		client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION_110);
+
+	if (client == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
+		return GG_ACTION_FAIL;
+	}
+
+	if (sess->proxy_addr && sess->proxy_port) {
+		host = "http://" GG_APPMSG_HOST;
+		proxy = 1;
+	} else {
+		host = "";
+		proxy = 0;
+	}
+
+	auth = gg_proxy_auth();
+
+	if (sess->ssl_flag != GG_SSL_DISABLED) {
+		req = gg_saprintf
+			("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"
+			"%s"
+			"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+	} else {
+		req = gg_saprintf
+			("GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
+			"Host: " GG_APPMSG_HOST "\r\n"
+			"%s"
+			"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+	}
+
+	free(auth);
+	free(client);
+
+	if (req == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
+		e->event.failure = GG_FAILURE_PROXY;
+		return GG_ACTION_FAIL;
+	}
+
+	req_len = strlen(req);
+
+	gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// sending http query:\n%s", req);
+
+	res = send(sess->fd, req, req_len, 0);
+
+	free(req);
+
+	if (res == -1 && errno != EINTR && errno != EAGAIN) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
+		e->event.failure = (!proxy) ? GG_FAILURE_HUB : GG_FAILURE_PROXY;
+		return GG_ACTION_FAIL;
+	}
+
+	if ((size_t) res < req_len) {
+		sess->state = alt_state;
+		sess->check = GG_CHECK_WRITE;
+		sess->timeout = GG_DEFAULT_TIMEOUT;
+	} else {
+		sess->state = next_state;
+		sess->check = GG_CHECK_READ;
+		sess->timeout = GG_DEFAULT_TIMEOUT;
+	}
+
+	return GG_ACTION_WAIT;
+}
+
+static gg_action_t gg_handle_sending_hub_proxy(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	if (gg_send_queued_data(sess) == -1) {
+		e->event.failure = GG_FAILURE_WRITING;
+		return GG_ACTION_FAIL;
+	}
+
+	if (sess->send_left > 0)
+		return GG_ACTION_WAIT;
+
+	sess->state = next_state;
+	sess->check = GG_CHECK_READ;
+	sess->timeout = GG_DEFAULT_TIMEOUT;
+
+	return GG_ACTION_WAIT;
+}
+
+static gg_action_t gg_handle_reading_hub_proxy(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	char buf[1024], *tmp, host[128];
+	int port = GG_DEFAULT_PORT;
+	int reply;
+	const char *body;
+	struct in_addr addr;
+	int res;
+	char **host_white;
+	char *host_white_default[] = GG_DEFAULT_HOST_WHITE_LIST;
+
+	res = recv(sess->fd, buf, sizeof(buf), 0);
+
+	if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"non-critical recv error (errno=%d, %s)\n",
+			errno, strerror(errno));
+		return GG_ACTION_WAIT;
+	}
+
+	if (res == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() recv "
+			"error (errno=%d, %s)\n", errno, strerror(errno));
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	if (res != 0) {
+		tmp = realloc(sess->recv_buf, sess->recv_done + res + 1);
+
+		if (tmp == NULL) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for http reply\n");
+			return GG_ACTION_FAIL;
+		}
+
+		sess->recv_buf = tmp;
+		memcpy(sess->recv_buf + sess->recv_done, buf, res);
+		sess->recv_done += res;
+		sess->recv_buf[sess->recv_done] = 0;
+	}
+
+	if (res == 0 && sess->recv_buf == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	if (res != 0)
+		return GG_ACTION_WAIT;
+
+	gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// received http reply:\n%s", sess->recv_buf);
+
+	res = sscanf(sess->recv_buf, "HTTP/1.%*d %3d ", &reply);
+
+	/* sprawdzamy, czy wszystko w porządku. */
+	if (res != 1 || reply != 200) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	/* szukamy początku treści */
+	body = strstr(sess->recv_buf, "\r\n\r\n");
+
+	if (body == NULL) {
+		body = strstr(sess->recv_buf, "\n\n");
+
+		if (body == NULL) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
+			e->event.failure = GG_FAILURE_CONNECTING;
+			return GG_ACTION_FAIL;
+		} else {
+			body += 2;
+		}
+	} else {
+		body += 4;
+	}
+
+	/* 17591 0 91.197.13.71:8074 91.197.13.71 */
+	res = sscanf(body, "%d %*d %128s", &reply, host);
+
+	if (res != 2) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid hub reply, connection failed\n");
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "reply=%d, host=\"%s\"\n", reply, host);
+
+	/* jeśli pierwsza liczba w linii nie jest równa zeru,
+	 * oznacza to, że mamy wiadomość systemową. */
+	if (reply != 0) {
+		tmp = strchr(body, '\n');
+
+		if (tmp != NULL) {
+			e->type = GG_EVENT_MSG;
+			e->event.msg.msgclass = reply;
+			e->event.msg.sender = 0;
+			e->event.msg.message = (unsigned char*) strdup(tmp + 1);
+
+			if (e->event.msg.message == NULL) {
+				gg_debug_session(sess, GG_DEBUG_MISC,
+					"// gg_watch_fd() not enough memory "
+					"for system message\n");
+				return GG_ACTION_FAIL;
+			}
+		}
+	}
+
+	gg_close(sess);
+
+	tmp = strchr(host, ':');
+
+	if (tmp != NULL) {
+		*tmp = 0;
+		port = atoi(tmp + 1);
+	}
+
+	if (strcmp(host, "notoperating") == 0) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n");
+		e->event.failure = GG_FAILURE_UNAVAILABLE;
+		return GG_ACTION_FAIL;
+	}
+
+	addr.s_addr = inet_addr(host);
+	if (addr.s_addr == INADDR_NONE)
+		addr.s_addr = 0;
+	sess->server_addr = addr.s_addr;
+
+	free(sess->recv_buf);
+	sess->recv_buf = NULL;
+	sess->recv_done = 0;
+
+	if (sess->state != GG_STATE_READING_PROXY_HUB) {
+		if (sess->port == 0) {
+			sess->connect_port[0] = port;
+			sess->connect_port[1] = (port != GG_HTTPS_PORT) ? GG_HTTPS_PORT : 0;
+		} else {
+			sess->connect_port[0] = sess->port;
+			sess->connect_port[1] = 0;
+		}
+	} else {
+		sess->connect_port[0] = (sess->port == 0) ? GG_HTTPS_PORT : sess->port;
+		sess->connect_port[1] = 0;
+	}
+
+	free(sess->connect_host);
+	sess->connect_host = strdup(host);
+
+	if (sess->connect_host == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory\n");
+		return GG_ACTION_FAIL;
+	}
+
+	host_white = sess->private_data->host_white_list;
+	if (!host_white)
+		host_white = host_white_default;
+
+	if (sess->ssl_flag == GG_SSL_REQUIRED && host_white[0] != NULL) {
+		int host_ok = 0;
+		char **it;
+		int host_len;
+
+		host_len = strlen(sess->connect_host);
+
+		for (it = host_white; *it != NULL; it++) {
+			const char *white = *it;
+			int white_len, dom_offset;
+
+			white_len = strlen(white);
+			if (white_len > host_len)
+				continue;
+
+			dom_offset = host_len - white_len;
+			if (strncasecmp(sess->connect_host + dom_offset, white,
+				white_len) != 0)
+			{
+				continue;
+			}
+
+			if (white_len < host_len) {
+				if (sess->connect_host[dom_offset - 1] != '.')
+					continue;
+			}
+
+			host_ok = 1;
+			break;
+		}
+
+		if (!host_ok) {
+			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
+				"// gg_watch_fd() the HUB server returned "
+				"a host that is not trusted (%s)\n",
+				sess->connect_host);
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+	}
+
+	if (sess->state == GG_STATE_READING_HUB)
+		sess->resolver_host = sess->connect_host;
+
+	/* Jeśli łączymy się przez proxy, zacznijmy od początku listy */
+	sess->resolver_index = 0;
+
+	sess->state = (sess->async) ? next_state : alt_state;
+
+	return GG_ACTION_NEXT;
+}
+
+static gg_action_t gg_handle_send_proxy_gg(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	char *req, *auth;
+	size_t req_len;
+	int res;
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() %s\n", gg_debug_state(sess->state));
+
+	if (sess->connect_index > 1 || sess->connect_port[sess->connect_index] == 0) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of connection candidates\n");
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	auth = gg_proxy_auth();
+
+	req = gg_saprintf("CONNECT %s:%d HTTP/1.0\r\n%s\r\n",
+		sess->connect_host, sess->connect_port[sess->connect_index],
+		(auth) ? auth : "");
+
+	free(auth);
+
+	sess->connect_index++;
+
+	if (req == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
+		e->event.failure = GG_FAILURE_PROXY;
+		return GG_ACTION_FAIL;
+	}
+
+	req_len = strlen(req);
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n%s", req);
+
+	res = send(sess->fd, req, req_len, 0);
+
+	free(req);
+
+	if (res == -1 && errno != EINTR && errno != EAGAIN) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
+		e->event.failure = GG_FAILURE_PROXY;
+		return GG_ACTION_FAIL;
+	}
+
+	if ((size_t) res < req_len) {
+		sess->state = alt_state;
+		sess->check = GG_CHECK_WRITE;
+		sess->timeout = GG_DEFAULT_TIMEOUT;
+	} else {
+		sess->state = next_state;
+		sess->check = GG_CHECK_READ;
+		sess->timeout = GG_DEFAULT_TIMEOUT;
+	}
+
+	return GG_ACTION_WAIT;
+}
+
+static gg_action_t gg_handle_tls_negotiation(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
+	int valid_hostname = 0;
+#endif
+
+#ifdef GG_CONFIG_HAVE_GNUTLS
+	unsigned int status;
+	int res;
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+
+	for (;;) {
+		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");
+
+			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;
+			return GG_ACTION_WAIT;
+		}
+
+		if (res == GNUTLS_E_INTERRUPTED) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_INTERRUPTED\n");
+			continue;
+		}
+
+		if (res != GNUTLS_E_SUCCESS) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
+				" TLS handshake error: %d, %s\n",
+				res, gnutls_strerror(res));
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+
+		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);
+
+					if (gnutls_x509_crt_check_hostname(cert, sess->connect_host) != 0)
+						valid_hostname = 1;
+				}
+			}
+
+			gnutls_x509_crt_deinit(cert);
+		}
+	}
+
+	res = gnutls_certificate_verify_peers2(GG_SESSION_GNUTLS(sess), &status);
+
+	if (res != 0 || status != 0) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING!  unable to"
+			" verify peer certificate: 0x%x, %d, %s\n", status, res,
+			gnutls_strerror(res));
+
+		if (sess->ssl_flag == GG_SSL_REQUIRED) {
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+	} else {
+		gg_debug_session(sess, GG_DEBUG_MISC, "//   verified peer certificate\n");
+	}
+
+
+#elif defined GG_CONFIG_HAVE_OPENSSL
+
+	X509 *peer;
+	int res;
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() %s\n", gg_debug_state(sess->state));
+
+	res = SSL_connect(GG_SESSION_OPENSSL(sess));
+
+	if (res <= 0) {
+		int err;
+
+		err = SSL_get_error(GG_SESSION_OPENSSL(sess), res);
+
+		if (res == 0) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+
+		if (err == SSL_ERROR_WANT_READ) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
+
+			sess->check = GG_CHECK_READ;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+			return GG_ACTION_WAIT;
+		} else if (err == SSL_ERROR_WANT_WRITE) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
+
+			sess->check = GG_CHECK_WRITE;
+			sess->timeout = GG_DEFAULT_TIMEOUT;
+			return GG_ACTION_WAIT;
+		} else {
+			char buf[256];
+
+			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
+
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+	}
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation"
+		" succeded:\n//   cipher: %s\n",
+		SSL_get_cipher_name(GG_SESSION_OPENSSL(sess)));
+
+	peer = SSL_get_peer_certificate(GG_SESSION_OPENSSL(sess));
+
+	if (peer == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
+
+		if (sess->ssl_flag == GG_SSL_REQUIRED) {
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+	} else {
+		char buf[256];
+		long res;
+
+		X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
+		gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
+
+		X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
+		gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
+
+		res = SSL_get_verify_result(GG_SESSION_OPENSSL(sess));
+
+		if (res != X509_V_OK) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING!  "
+				"unable to verify peer certificate! "
+				"res=%ld\n", res);
+
+			if (sess->ssl_flag == GG_SSL_REQUIRED) {
+				e->event.failure = GG_FAILURE_TLS;
+				return GG_ACTION_FAIL;
+			}
+		} else {
+			gg_debug_session(sess, GG_DEBUG_MISC, "//   verified peer certificate\n");
+		}
+
+		if (X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, buf, sizeof(buf)) == -1)
+			buf[0] = 0;
+
+		/* Obsługa certyfikatów z wieloznacznikiem */
+		if (strchr(buf, '*') == buf && strchr(buf + 1, '*') == NULL) {
+			char *tmp;
+
+			tmp = strchr(sess->connect_host, '.');
+
+			if (tmp != NULL)
+				valid_hostname = (strcasecmp(tmp, buf + 1) == 0);
+		} else {
+			valid_hostname = (strcasecmp(sess->connect_host, buf) == 0);
+		}
+	}
+
+#else
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() no SSL support\n");
+	e->event.failure = GG_FAILURE_TLS;
+	return GG_ACTION_FAIL;
+
+#endif
+
+#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
+	if (!valid_hostname) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING!  unable to verify hostname\n");
+
+		if (sess->ssl_flag == GG_SSL_REQUIRED) {
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+	}
+
+	sess->state = next_state;
+	sess->check = GG_CHECK_READ;
+	sess->timeout = GG_DEFAULT_TIMEOUT;
+
+	return GG_ACTION_WAIT;
+#endif
+}
+
+static gg_action_t gg_handle_reading_proxy_gg(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+	char buf[256];
+	int res;
+	int reply;
+	char *body;
+
+	res = recv(sess->fd, buf, sizeof(buf), 0);
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "recv() = %d\n", res);
+
+	if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"non-critical recv error (errno=%d, %s)\n",
+			errno, strerror(errno));
+		return GG_ACTION_WAIT;
+	}
+
+	if (res == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() recv "
+			"error (errno=%d, %s)\n", errno, strerror(errno));
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	if (res != 0) {
+		char *tmp;
+
+		tmp = realloc(sess->recv_buf, sess->recv_done + res + 1);
+
+		if (tmp == NULL) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for http reply\n");
+			return GG_ACTION_FAIL;
+		}
+
+		sess->recv_buf = tmp;
+		memcpy(sess->recv_buf + sess->recv_done, buf, res);
+		sess->recv_done += res;
+		sess->recv_buf[sess->recv_done] = 0;
+	}
+
+	if (res == 0 && sess->recv_buf == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	/* szukamy początku treści */
+	body = strstr(sess->recv_buf, "\r\n\r\n");
+
+	if (body == NULL) {
+		body = strstr(sess->recv_buf, "\n\n");
+
+		if (body == NULL) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
+			e->event.failure = GG_FAILURE_CONNECTING;
+			return GG_ACTION_FAIL;
+		} else {
+			body += 2;
+		}
+	} else {
+		body += 4;
+	}
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// found body!\n");
+
+	gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// received proxy reply:\n%s\n", sess->recv_buf);
+
+	res = sscanf(sess->recv_buf, "HTTP/1.%*d %3d ", &reply);
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "res = %d, reply = %d\n", res, reply);
+
+	/* sprawdzamy, czy wszystko w porządku. */
+	if (res != 1 || reply != 200) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
+		e->event.failure = GG_FAILURE_CONNECTING;
+		return GG_ACTION_FAIL;
+	}
+
+	if (sess->ssl_flag != GG_SSL_DISABLED) {
+		if (gg_session_init_ssl(sess) == -1) {
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+
+		/* Teoretycznie SSL jest inicjowany przez klienta, więc serwer
+		 * nie powinien niczego wysłać. */
+		if (sess->recv_buf + sess->recv_done > body) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unexpected SSL data\n");
+			e->event.failure = GG_FAILURE_TLS;
+			return GG_ACTION_FAIL;
+		}
+
+		free(sess->recv_buf);
+		sess->recv_buf = NULL;
+		sess->recv_done = 0;
+
+		sess->state = alt_state;
+		sess->check = GG_CHECK_WRITE;
+		sess->timeout = GG_DEFAULT_TIMEOUT;
+
+		return GG_ACTION_WAIT;
+	}
+
+	sess->state = next_state;
+	sess->check = GG_CHECK_READ;
+	sess->timeout = GG_DEFAULT_TIMEOUT;	/* Pierwszy pakiet musi przyjść */
+
+	/* Jeśli zbuforowaliśmy za dużo, przeanalizuj */
+
+	if (sess->recv_buf + sess->recv_done > body) {
+		sess->recv_done = sess->recv_done - (body - sess->recv_buf);
+		memmove(sess->recv_buf, body, sess->recv_done);
+		sess->state = alt2_state;
+		return GG_ACTION_NEXT;
+	} else {
+		free(sess->recv_buf);
+		sess->recv_buf = NULL;
+		sess->recv_done = 0;
+	}
+
+	return GG_ACTION_WAIT;
+}
+
+static gg_action_t gg_handle_connected(struct gg_session *sess,
+	struct gg_event *e, enum gg_state_t next_state,
+	enum gg_state_t alt_state, enum gg_state_t alt2_state)
+{
+#if 0
+	char buf[1024];
+	int res;
+
+	if (gg_send_queued_data(sess) == -1)
+		return GG_ACTION_FAIL;
+
+	res = gg_read(sess, buf, sizeof(buf));
+
+	if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
+			"non-critical read error (errno=%d, %s)\n",
+			errno, strerror(errno));
+		return GG_ACTION_WAIT;
+	}
+
+	if (res == -1 || res == 0) {
+		if (res == -1) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
+				" read error (errno=%d, %s)\n",
+				errno, strerror(errno));
+		} else {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
+				" connection closed\n");
+		}
+
+		if (sess->state == GG_STATE_DISCONNECTING && res == 0) {
+			e->type = GG_EVENT_DISCONNECT_ACK;
+		} else if (sess->state == GG_STATE_READING_KEY) {
+			e->event.failure = GG_FAILURE_INVALID;
+			return GG_ACTION_FAIL;
+		}
+
+		return GG_ACTION_FAIL;
+	}
+
+	gg_debug_dump(sess, GG_DEBUG_DUMP, buf, res);
+
+	if (gg_session_handle_data(sess, buf, res, e) == -1)
+		return GG_ACTION_FAIL;
+
+	if (sess->send_buf != NULL)
+		sess->check |= GG_CHECK_WRITE;
+
+	return GG_ACTION_WAIT;
+#else
+	struct gg_header *gh;
+
+	if (gg_send_queued_data(sess) == -1)
+		return GG_ACTION_FAIL;
+
+	gh = gg_recv_packet(sess);
+
+	if (gh == NULL) {
+		if (sess->state == GG_STATE_DISCONNECTING) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection broken expectedly\n");
+			e->type = GG_EVENT_DISCONNECT_ACK;
+			return GG_ACTION_WAIT;
+		}
+
+		if (errno != EAGAIN) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
+				" gg_recv_packet failed (errno=%d, %s)\n",
+				errno, strerror(errno));
+			return GG_ACTION_FAIL;
+		}
+	} else {
+		if (gg_session_handle_packet(sess, gh->type,
+			(const char *) gh + sizeof(struct gg_header),
+			gh->length, e) == -1)
+		{
+			free(gh);
+			return GG_ACTION_FAIL;
+		}
+
+		free(gh);
+	}
+
+	sess->check = GG_CHECK_READ;
+
+	if (sess->send_buf != NULL)
+		sess->check |= GG_CHECK_WRITE;
+
+	return GG_ACTION_WAIT;
+#endif
+}
+
+static gg_action_t gg_handle_error(struct gg_session *sess, struct gg_event *e,
+	enum gg_state_t next_state, enum gg_state_t alt_state,
+	enum gg_state_t alt2_state)
+{
+	struct gg_session_private *p = sess->private_data;
+
+	gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_handle_error() failure=%d\n", p->socket_failure);
+
+	e->event.failure = p->socket_failure;
+
+	return GG_ACTION_FAIL;
+}
+
+static const gg_state_transition_t handlers[] =
+{
+	/* style:maxlinelength:start-ignore */
+	{ GG_STATE_RESOLVE_HUB_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_HUB, GG_STATE_SEND_HUB, 0 },
+	{ GG_STATE_RESOLVE_GG_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_GG, GG_STATE_READING_KEY, 0 },
+	{ GG_STATE_RESOLVE_PROXY_HUB_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_PROXY_HUB, GG_STATE_SEND_PROXY_HUB, 0 },
+	{ GG_STATE_RESOLVE_PROXY_GG_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_PROXY_GG, GG_STATE_SEND_PROXY_GG, 0 },
+
+	{ GG_STATE_RESOLVE_HUB_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_HUB, GG_STATE_SEND_HUB, 0 },
+	{ GG_STATE_RESOLVE_GG_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_GG, GG_STATE_READING_KEY, 0 },
+	{ GG_STATE_RESOLVE_PROXY_HUB_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_PROXY_HUB, GG_STATE_SEND_PROXY_HUB, 0 },
+	{ GG_STATE_RESOLVE_PROXY_GG_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_PROXY_GG, GG_STATE_SEND_PROXY_GG, 0 },
+
+	{ GG_STATE_RESOLVING_HUB, gg_handle_resolving, GG_STATE_CONNECT_HUB, 0, 0 },
+	{ GG_STATE_RESOLVING_GG, gg_handle_resolving, GG_STATE_CONNECT_GG, 0, 0 },
+	{ GG_STATE_RESOLVING_PROXY_HUB, gg_handle_resolving, GG_STATE_CONNECT_PROXY_HUB, 0, 0 },
+	{ GG_STATE_RESOLVING_PROXY_GG, gg_handle_resolving, GG_STATE_CONNECT_PROXY_GG, 0, 0 },
+
+	{ GG_STATE_CONNECT_HUB, gg_handle_connect, GG_STATE_CONNECTING_HUB, 0, 0 },
+	{ GG_STATE_CONNECT_PROXY_HUB, gg_handle_connect, GG_STATE_CONNECTING_PROXY_HUB, 0, 0 },
+	{ GG_STATE_CONNECT_PROXY_GG, gg_handle_connect, GG_STATE_CONNECTING_PROXY_GG, 0, 0 },
+
+	{ GG_STATE_CONNECT_GG, gg_handle_connect_gg, GG_STATE_CONNECTING_GG, 0, 0 },
+
+	{ GG_STATE_CONNECTING_HUB, gg_handle_connecting, GG_STATE_SEND_HUB, GG_STATE_CONNECT_HUB, 0 },
+	{ GG_STATE_CONNECTING_PROXY_HUB, gg_handle_connecting, GG_STATE_SEND_PROXY_HUB, GG_STATE_CONNECT_PROXY_HUB, 0 },
+	{ GG_STATE_CONNECTING_PROXY_GG, gg_handle_connecting, GG_STATE_SEND_PROXY_GG, GG_STATE_CONNECT_PROXY_GG, 0 },
+
+	{ GG_STATE_CONNECTING_GG, gg_handle_connecting_gg, GG_STATE_READING_KEY, GG_STATE_CONNECT_GG, GG_STATE_TLS_NEGOTIATION },
+
+	{ GG_STATE_SEND_HUB, gg_handle_send_hub, GG_STATE_READING_HUB, GG_STATE_SENDING_HUB, 0 },
+	{ GG_STATE_SEND_PROXY_HUB, gg_handle_send_hub, GG_STATE_READING_PROXY_HUB, GG_STATE_SENDING_PROXY_HUB, 0 },
+
+	{ GG_STATE_SEND_PROXY_GG, gg_handle_send_proxy_gg, GG_STATE_READING_PROXY_GG, GG_STATE_SENDING_PROXY_GG, 0 },
+
+	{ GG_STATE_SENDING_HUB, gg_handle_sending_hub_proxy, GG_STATE_READING_HUB, 0, 0 },
+	{ GG_STATE_SENDING_PROXY_HUB, gg_handle_sending_hub_proxy, GG_STATE_READING_PROXY_HUB, 0, 0 },
+	{ GG_STATE_SENDING_PROXY_GG, gg_handle_sending_hub_proxy, GG_STATE_READING_PROXY_GG, 0, 0 },
+
+	{ GG_STATE_READING_HUB, gg_handle_reading_hub_proxy, GG_STATE_RESOLVE_GG_ASYNC, GG_STATE_RESOLVE_GG_SYNC, 0 },
+	{ GG_STATE_READING_PROXY_HUB, gg_handle_reading_hub_proxy, GG_STATE_CONNECT_PROXY_GG, GG_STATE_CONNECT_PROXY_GG, 0 },
+
+	{ GG_STATE_READING_PROXY_GG, gg_handle_reading_proxy_gg, GG_STATE_READING_KEY, GG_STATE_TLS_NEGOTIATION, GG_STATE_READING_KEY },
+
+	{ GG_STATE_TLS_NEGOTIATION, gg_handle_tls_negotiation, GG_STATE_READING_KEY, 0, 0 },
+
+	{ GG_STATE_READING_KEY, gg_handle_connected, 0, 0, 0 },
+	{ GG_STATE_READING_REPLY, gg_handle_connected, 0, 0, 0 },
+	{ GG_STATE_CONNECTED, gg_handle_connected, 0, 0, 0 },
+	{ GG_STATE_DISCONNECTING, gg_handle_connected, 0, 0, 0 },
+	{ GG_STATE_ERROR, gg_handle_error, 0, 0, 0 },
+	/* style:maxlinelength:end-ignore */
+};
+
+struct gg_event *gg_eventqueue_add(struct gg_session *sess)
+{
+	struct gg_event *ge;
+	gg_eventqueue_t *queue_el, *it;
+
+	queue_el = gg_new0(sizeof(gg_eventqueue_t));
+	ge = gg_new0(sizeof(struct gg_event));
+
+	if (queue_el == NULL || ge == NULL) {
+		free(queue_el);
+		free(ge);
+		return NULL;
+	}
+
+	ge->type = GG_EVENT_NONE;
+
+	queue_el->event = ge;
+	if (sess->private_data->event_queue == NULL)
+		sess->private_data->event_queue = queue_el;
+	else {
+		it = sess->private_data->event_queue;
+		while (it->next != NULL)
+			it = it->next;
+		it->next = queue_el;
+	}
+
+	return ge;
+}
+
+/**
  * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
  *
  * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
  * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
  * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
  *
  * \param sess Struktura sesji
  *
  * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
  *
  * \ingroup events
  */
 struct gg_event *gg_watch_fd(struct gg_session *sess)
 {
-	struct gg_event *e;
-	int res = 0;
-	int port = 0;
-	int errno2 = 0;
+	struct gg_event *ge;
+	struct gg_session_private *priv;
 
 	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
 
-	if (!sess) {
+	if (sess == NULL) {
 		errno = EFAULT;
 		return NULL;
 	}
 
-	if (!(e = (void*) calloc(1, sizeof(*e)))) {
+	priv = sess->private_data;
+
+	if (priv->event_queue != NULL) {
+		gg_eventqueue_t *next;
+
+		ge = priv->event_queue->event;
+		next = priv->event_queue->next;
+		free(priv->event_queue);
+		priv->event_queue = next;
+
+		if (next == NULL) {
+			sess->check = priv->check_after_queue;
+			sess->fd = priv->fd_after_queue;
+		}
+		return ge;
+	}
+
+	ge = malloc(sizeof(struct gg_event));
+
+	if (ge == NULL) {
 		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
 		return NULL;
 	}
 
-	e->type = GG_EVENT_NONE;
-
-	if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
-
-		res = write(sess->fd, sess->send_buf, sess->send_left);
-
-		if (res == -1 && errno != EAGAIN) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno));
-
-			if (sess->state == GG_STATE_READING_REPLY)
-				e->event.failure = GG_FAILURE_CONNECTING;
-
-			goto fail;
-		}
+	memset(ge, 0, sizeof(struct gg_event));
 
-		if (res == sess->send_left) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
-			free(sess->send_buf);
-			sess->send_buf = NULL;
-			sess->send_left = 0;
-		} else if (res > 0) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res);
-
-			memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
-			sess->send_left -= res;
-		}
-
-		res = 0;
-	}
-
-	switch (sess->state) {
-		case GG_STATE_RESOLVING:
-		{
-			struct in_addr addr;
-			int failed = 0;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
+	ge->type = GG_EVENT_NONE;
 
-			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_proxy_hub;
-			}
+	for (;;) {
+		unsigned int i, found = 0;
+		gg_action_t res;
 
-			/* jeśli jesteśmy w resolverze i mamy ustawiony port
-			 * proxy, znaczy, że resolvowaliśmy proxy. zatem
-			 * wpiszmy jego adres. */
-			if (sess->proxy_port)
-				sess->proxy_addr = addr.s_addr;
-
-			/* zapiszmy sobie adres huba i adres serwera (do
-			 * bezpośredniego połączenia, jeśli hub leży)
-			 * z resolvera. */
-			if (sess->proxy_addr && sess->proxy_port)
-				port = sess->proxy_port;
-			else {
-				sess->server_addr = sess->hub_addr = addr.s_addr;
-				port = GG_APPMSG_PORT;
-			}
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
-
-			/* łączymy się albo z hubem, albo z proxy, zależnie
-			 * od tego, co resolvowaliśmy. */
-			if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
-				/* jeśli w trybie asynchronicznym gg_connect()
-				 * zwróci błąd, nie ma sensu próbować dalej. */
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
-				goto fail_proxy_hub;
-			}
+		res = GG_ACTION_FAIL;
 
-			/* jeśli podano serwer i łączmy się przez proxy,
-			 * jest to bezpośrednie połączenie, inaczej jest
-			 * do huba. */
-
-			if (sess->proxy_addr && sess->proxy_port && sess->server_addr) {
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->soft_timeout = 1;
-			} else
-				sess->state = GG_STATE_CONNECTING_HUB;
-
-			sess->check = GG_CHECK_WRITE;
-			sess->timeout = GG_DEFAULT_TIMEOUT;
-
-			break;
-		}
-
-		case GG_STATE_CONNECTING_HUB:
-		{
-			char buf[1024], *client, *auth;
-			int res = 0;
-			socklen_t res_size = sizeof(res);
-			const char *host;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
-
-			/* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
-			 * przypadkiem jakiś błąd. */
-			if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to %s failed (errno=%d, %s)\n", (sess->proxy_addr && sess->proxy_port) ? "proxy" : "hub", res, strerror(res));
-				goto fail_proxy_hub;
-			}
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
-
-			if (sess->client_version != NULL && isdigit(sess->client_version[0]))
-				client = gg_urlencode(sess->client_version);
-			else
-				client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION);
-
-			if (client == NULL) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
-				goto fail;
+		for (i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) {
+			if (handlers[i].state == (enum gg_state_t) sess->state) {
+				gg_debug_session(sess, GG_DEBUG_MISC,
+					"// gg_watch_fd() %s\n",
+					gg_debug_state(sess->state));
+				res = (*handlers[i].handler)(sess, ge,
+					handlers[i].next_state,
+					handlers[i].alt_state,
+					handlers[i].alt2_state);
+				found = 1;
+				break;
 			}
-
-			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port)
-				host = "http://" GG_APPMSG_HOST;
-			else
-				host = "";
-
-			auth = gg_proxy_auth();
-
-#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
-			if (sess->ssl != NULL) {
-				snprintf(buf, sizeof(buf) - 1,
-					"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"
-					"%s"
-					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
-			} else
-#endif
-			{
-				snprintf(buf, sizeof(buf) - 1,
-					"GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
-					"Host: " GG_APPMSG_HOST "\r\n"
-					"%s"
-					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
-			}
-
-			free(auth);
-			free(client);
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
-
-			/* zapytanie jest krótkie, więc zawsze zmieści się
-			 * do bufora gniazda. jeśli write() zwróci mniej,
-			 * stało się coś złego. */
-			if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
-				goto fail_proxy_hub;
-			}
-
-			sess->state = GG_STATE_READING_DATA;
-			sess->check = GG_CHECK_READ;
-			sess->timeout = GG_DEFAULT_TIMEOUT;
-
-			break;
 		}
 
-		case GG_STATE_READING_DATA:
-		{
-			char buf[1024], *tmp, *host;
-			int port = GG_DEFAULT_PORT;
-			struct in_addr addr;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
-
-			/* czytamy linię z gniazda i obcinamy \r\n. */
-			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-			gg_chomp(buf);
-			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
-
-			/* sprawdzamy, czy wszystko w porządku. */
-			if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
-				goto fail_proxy_hub;
-			}
-
-			/* ignorujemy resztę nagłówka. */
-			while (strcmp(buf, "\r\n") && strcmp(buf, ""))
-				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-
-			/* czytamy pierwszą linię danych. */
-			if (gg_read_line(sess->fd, buf, sizeof(buf) - 1) == NULL) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() read error\n");
-				goto fail_proxy_hub;
-			}
-			gg_chomp(buf);
-
-			/* jeśli pierwsza liczba w linii nie jest równa zeru,
-			 * oznacza to, że mamy wiadomość systemową. */
-			if (atoi(buf)) {
-				char tmp[1024], *foo, *sysmsg_buf = NULL;
-				int len = 0;
-
-				while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) {
-					if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
-						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
-						break;
-					}
-
-					sysmsg_buf = foo;
-
-					if (!len)
-						strcpy(sysmsg_buf, tmp);
-					else
-						strcat(sysmsg_buf, tmp);
-
-					len += strlen(tmp);
-				}
-
-				e->type = GG_EVENT_MSG;
-				e->event.msg.msgclass = atoi(buf);
-				e->event.msg.sender = 0;
-				e->event.msg.message = (unsigned char*) sysmsg_buf;
-			}
-
-			close(sess->fd);
-			sess->fd = -1;
-
-			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
-
-			/* analizujemy otrzymane dane. */
-			tmp = buf;
-
-			while (*tmp && *tmp != ' ')
-				tmp++;
-			while (*tmp && *tmp == ' ')
-				tmp++;
-			while (*tmp && *tmp != ' ')
-				tmp++;
-			while (*tmp && *tmp == ' ')
-				tmp++;
-			host = tmp;
-			while (*tmp && *tmp != ' ')
-				tmp++;
-			*tmp = 0;
-
-			if ((tmp = strchr(host, ':'))) {
-				*tmp = 0;
-				port = atoi(tmp + 1);
-			}
-
-			if (strcmp(host, "") == 0) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid response\n");
-				e->event.failure = GG_FAILURE_HUB;
-				goto fail;
-			}
-
-			if (!strcmp(host, "notoperating")) {
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
-				e->event.failure = GG_FAILURE_UNAVAILABLE;
-				goto fail;
-			}
-
-			addr.s_addr = inet_addr(host);
-			sess->server_addr = addr.s_addr;
-
-			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) {
-				/* jeśli mamy proxy, łączymy się z nim. */
-				if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
-					/* nie wyszło? trudno. */
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
-					e->event.failure = GG_FAILURE_PROXY;
-					goto fail;
-				}
-
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->check = GG_CHECK_WRITE;
-				sess->timeout = GG_DEFAULT_TIMEOUT;
-				sess->soft_timeout = 1;
-				break;
-			}
-
-			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;
-				}
-
-				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));
-					e->event.failure = GG_FAILURE_CONNECTING;
-					goto fail;
-				}
-			}
-
-			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;
-				e->event.failure = GG_FAILURE_RESOLVING;
-				goto fail;
-			}
-
-			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));
-
-				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));
-					e->event.failure = GG_FAILURE_CONNECTING;
-					goto fail;
-				}
-			}
-
-			sess->state = GG_STATE_CONNECTING_GG;
-			sess->check = GG_CHECK_WRITE;
-			sess->timeout = GG_DEFAULT_TIMEOUT;
-			sess->soft_timeout = 1;
-
-			break;
+		if (!found) {
+			gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
+				"// gg_watch_fd() invalid state %s\n",
+				gg_debug_state(sess->state));
+			ge->event.failure = GG_FAILURE_INTERNAL;
 		}
 
-		case GG_STATE_CONNECTING_GG:
-		{
-			int res = 0;
-			socklen_t res_size = sizeof(res);
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
-
-			sess->soft_timeout = 0;
-
-			/* jeśli wystąpił błąd podczas łączenia się... */
-			if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-				/* jeśli nie udało się połączenie z proxy,
-				 * nie mamy czego próbować więcej. */
-				if (sess->proxy_addr && sess->proxy_port) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
-					e->event.failure = GG_FAILURE_PROXY;
-					goto fail;
-				}
-
-				close(sess->fd);
-				sess->fd = -1;
-
-#ifdef ETIMEDOUT
-				if (sess->timeout == 0)
-					errno = ETIMEDOUT;
-#endif
-
-#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
-				 * trzeba by się bawić w tworzenie na nowo
-				 * SSL i SSL_CTX. */
-
-				if (sess->ssl) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
-					e->event.failure = GG_FAILURE_CONNECTING;
-					goto fail;
-				}
-#endif
-
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
-
-				if (sess->port == GG_HTTPS_PORT) {
-					e->event.failure = GG_FAILURE_CONNECTING;
-					goto fail;
-				}
-
-				sess->port = GG_HTTPS_PORT;
+		if (!sess->async && ge->type == GG_EVENT_NONE && res == GG_ACTION_WAIT)
+			res = GG_ACTION_NEXT;
 
-				/* próbujemy na port 443. */
-				if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
-					e->event.failure = GG_FAILURE_CONNECTING;
-					goto fail;
+		switch (res) {
+			case GG_ACTION_WAIT:
+				if (priv->event_queue != NULL) {
+					priv->fd_after_queue = sess->fd;
+					priv->check_after_queue = sess->check;
+					/* wymuszamy ponowne wywołanie gg_watch_fd */
+					sess->fd = gg_get_dummy_fd(sess);
+					if (sess->fd < 0)
+						sess->fd = priv->fd_after_queue;
+					sess->check = GG_CHECK_READ | GG_CHECK_WRITE;
 				}
-
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->check = GG_CHECK_WRITE;
-				sess->timeout = GG_DEFAULT_TIMEOUT;
-				sess->soft_timeout = 1;
-
-				break;
-			}
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
-
-			if (gg_proxy_http_only)
-				sess->proxy_port = 0;
-
-			/* jeśli mamy proxy, wyślijmy zapytanie. */
-			if (sess->proxy_addr && sess->proxy_port) {
-				char buf[100], *auth = gg_proxy_auth();
-				struct in_addr addr;
+				return ge;
 
-				if (sess->server_addr)
-					addr.s_addr = sess->server_addr;
-				else
-					addr.s_addr = sess->hub_addr;
+			case GG_ACTION_NEXT:
+				continue;
 
-				snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
-
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf);
+			case GG_ACTION_FAIL:
+				sess->state = GG_STATE_IDLE;
 
-				/* wysyłamy zapytanie. jest ono na tyle krótkie,
-				 * że musi się zmieścić w buforze gniazda. jeśli
-				 * write() zawiedzie, stało się coś złego. */
-				if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
-					free(auth);
-					e->event.failure = GG_FAILURE_PROXY;
-					goto fail;
-				}
+				gg_close(sess);
 
-				if (auth) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "//   %s", auth);
-					if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) {
-						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
-						free(auth);
-						e->event.failure = GG_FAILURE_PROXY;
-						goto fail;
-					}
-
-					free(auth);
+				if (ge->event.failure != 0) {
+					ge->type = GG_EVENT_CONN_FAILED;
+				} else {
+					free(ge);
+					ge = NULL;
 				}
 
-				if (write(sess->fd, "\r\n", 2) < 2) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
-					e->event.failure = GG_FAILURE_PROXY;
-					goto fail;
-				}
-			}
-
-#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
-				SSL_set_fd(sess->ssl, sess->fd);
-#endif
-
-				sess->state = GG_STATE_TLS_NEGOTIATION;
-				sess->check = GG_CHECK_WRITE;
-				sess->timeout = GG_DEFAULT_TIMEOUT;
-
-				break;
-			}
-#endif
-
-			sess->state = GG_STATE_READING_KEY;
-			sess->check = GG_CHECK_READ;
-			sess->timeout = GG_DEFAULT_TIMEOUT;
-
-			break;
-		}
-
-#ifdef GG_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:
-		{
-			int res;
-			X509 *peer;
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
-
-			if ((res = SSL_connect(sess->ssl)) <= 0) {
-				int err = SSL_get_error(sess->ssl, res);
-
-				if (res == 0) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
-
-					e->type = GG_EVENT_CONN_FAILED;
-					e->event.failure = GG_FAILURE_TLS;
-					sess->state = GG_STATE_IDLE;
-					close(sess->fd);
-					sess->fd = -1;
-					break;
-				}
-
-				if (err == SSL_ERROR_WANT_READ) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
-
-					sess->state = GG_STATE_TLS_NEGOTIATION;
-					sess->check = GG_CHECK_READ;
-					sess->timeout = GG_DEFAULT_TIMEOUT;
-
-					break;
-				} else if (err == SSL_ERROR_WANT_WRITE) {
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
+				return ge;
 
-					sess->state = GG_STATE_TLS_NEGOTIATION;
-					sess->check = GG_CHECK_WRITE;
-					sess->timeout = GG_DEFAULT_TIMEOUT;
-
-					break;
-				} else {
-					char buf[256];
-
-					ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
-
-					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
-
-					e->type = GG_EVENT_CONN_FAILED;
-					e->event.failure = GG_FAILURE_TLS;
-					sess->state = GG_STATE_IDLE;
-					close(sess->fd);
-					sess->fd = -1;
-					break;
-				}
-			}
-
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl));
-
-			peer = SSL_get_peer_certificate(sess->ssl);
-
-			if (!peer)
-				gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
-			else {
-				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);
-
-				X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
-				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;
+			/* Celowo nie ma default */
 		}
-#endif
-
-		case GG_STATE_READING_KEY:
-		case GG_STATE_READING_REPLY:
-		case GG_STATE_CONNECTED:
-		case GG_STATE_DISCONNECTING:
-		{
-			struct gg_header *gh;
-
-			if (sess->state == GG_STATE_READING_KEY)
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
-			else if (sess->state == GG_STATE_READING_REPLY)
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
-			else if (sess->state == GG_STATE_CONNECTED)
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
-			else if (sess->state == GG_STATE_DISCONNECTING)
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_DISCONNECTING\n");
-
-			/* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie
-			 * się tekstu wrzucanego przez proxy. */
-			if (sess->state == GG_STATE_READING_KEY && sess->proxy_addr && sess->proxy_port) {
-				char buf[100];
-
-				strcpy(buf, "");
-				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-				gg_chomp(buf);
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n//   %s\n", buf);
-
-				while (strcmp(buf, "")) {
-					gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-					gg_chomp(buf);
-					if (strcmp(buf, ""))
-						gg_debug_session(sess, GG_DEBUG_MISC, "//   %s\n", buf);
-				}
-
-				/* XXX niech czeka jeszcze raz w tej samej
-				 * fazie. głupio, ale działa. */
-				sess->proxy_port = 0;
-
-				break;
-			}
-
-			sess->last_event = time(NULL);
-
-			gh = gg_recv_packet(sess);
-
-			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)
-					goto fail;
-			} else {
-				if (gg_session_handle_packet(sess, gh->type, (const char *) gh + sizeof(struct gg_header), gh->length, e) == -1) {
-					free(gh);
-					goto fail;
-				}
-
-				free(gh);
-			}
-
-			sess->check = GG_CHECK_READ;
-
-			break;
-		}
-	}
-
-	if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED))
-		sess->check |= GG_CHECK_WRITE;
-
-	return e;
-
-fail_proxy_hub:
-	if (sess->proxy_port)
-		e->event.failure = GG_FAILURE_PROXY;
-	else
-		e->event.failure = GG_FAILURE_HUB;
-
-fail:
-	sess->resolver_cleanup(&sess->resolver, 1);
-
-	sess->state = GG_STATE_IDLE;
-
-	if (sess->fd != -1) {
-		int errno2;
-
-		errno2 = errno;
-		close(sess->fd);
-		errno = errno2;
-		sess->fd = -1;
-	}
-
-	if (e->event.failure != 0) {
-		e->type = GG_EVENT_CONN_FAILED;
-		return e;
-	} else {
-		free(e);
-		return NULL;
 	}
 }
 
 /*
  * Local variables:
  * c-indentation-style: k&r
  * c-basic-offset: 8
  * indent-tabs-mode: notnil
new file mode 100644
--- /dev/null
+++ b/libpurple/protocols/gg/lib/fileio.h
@@ -0,0 +1,63 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+/**
+ * \file fileio.h
+ *
+ * \brief Makra zapewniające kompatybilność API do obsługi operacji na plikach na różnych systemach
+ */
+
+#ifndef LIBGADU_FILEIO_H
+#define LIBGADU_FILEIO_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#  include <io.h>
+#  define gg_file_close _close
+#  undef lseek
+#  define lseek _lseek
+#  undef open
+#  define open _open
+#  undef read
+#  define read _read
+#  undef stat
+#  define stat _stat
+#  undef fstat
+#  define fstat _fstat
+#  undef write
+#  define write _write
+#  define S_IRWXO 0
+#  define S_IRWXG 0
+#else
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
+#  include <unistd.h>
+#  define gg_file_close close
+#endif
+
+#ifndef S_IWUSR
+#  define S_IWUSR S_IWRITE
+#endif
+
+#endif /* LIBGADU_FILEIO_H */
--- a/libpurple/protocols/gg/lib/handlers.c
+++ b/libpurple/protocols/gg/lib/handlers.c
@@ -21,126 +21,259 @@
  */
 
 /**
  * \file handlers.c
  *
  * \brief Funkcje obsługi przychodzących pakietów
  */
 
-#include <sys/types.h>
-#ifndef _WIN32
-#  include <sys/socket.h>
-#  include <netinet/in.h>
-#  include <arpa/inet.h>
-#endif
 #include <ctype.h>
-#ifndef _WIN32
-#  ifdef sun
-#    include <sys/filio.h>
-#  endif
-#endif
-
-#include "compat.h"
+
+#include "fileio.h"
+#include "network.h"
+#include "strman.h"
 #include "libgadu.h"
-#include "libgadu-config.h"
 #include "resolver.h"
 #include "session.h"
 #include "protocol.h"
 #include "encoding.h"
 #include "message.h"
-#include "libgadu-internal.h"
+#include "internal.h"
 #include "deflate.h"
+#include "tvbuff.h"
+#include "protobuf.h"
+#include "packets.pb-c.h"
 
 #include <errno.h>
-#ifndef _WIN32
-#  include <netdb.h>
-#endif
-#include <stdarg.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <signal.h>
-#ifndef _MSC_VER
-#  include <unistd.h>
-#endif
 #include <time.h>
-#ifdef GG_CONFIG_HAVE_OPENSSL
-#  include <openssl/err.h>
-#  include <openssl/rand.h>
-#endif
 
 /**
  * \internal Struktura opisująca funkcję obsługi pakietu.
  */
 typedef struct {
 	/* Typ pakietu */
 	uint32_t type;
 	/* Stan w którym pakiet jest obsługiwany */
-	int state;
+	enum gg_state_t state;
 	/* Minimalny rozmiar danych pakietu */
-	int min_length;
+	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)
+static int gg_session_handle_welcome(struct gg_session *gs, uint32_t type,
+	const char *ptr, size_t len, struct gg_event *ge)
 {
-	struct gg_welcome *w;
+	const struct gg_welcome *w;
 	int ret;
 	uint8_t hash_buf[64];
 	uint32_t local_ip;
 	struct sockaddr_in sin;
-	unsigned int sin_len = sizeof(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;
-		close(gs->fd);
-		gs->fd = -1;
+		gg_close(gs);
 		return 0;
 	}
 
-	w = (struct gg_welcome*) ptr;
-	w->key = gg_fix32(w->key);
+	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, w->key));
-			gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, 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
 
-			gg_login_hash_sha1(gs->password, w->key, hash_buf);
+			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", w->key, tmp);
+			gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() "
+				"challenge %.4x --> SHA1 hash: %s\n",
+				seed, tmp);
 #endif
 
 			break;
 		}
 
 		default:
 			break;
 	}
@@ -149,103 +282,68 @@ static int gg_session_handle_welcome(str
 	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));
+		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 (GG_SESSION_IS_PROTOCOL_8_0(gs)) {
-		struct gg_login80 l80;
-		const char *client_name, *version, *descr;
-		uint32_t client_name_len, version_len, descr_len;
-
-		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;
-		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 (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 {
-		struct gg_login70 l70;
-
-		if (gg_dcc_ip != (unsigned long) inet_addr("255.255.255.255"))
-			local_ip = gg_dcc_ip;
-
-		gs->client_addr = local_ip;
-
-		memset(&l70, 0, sizeof(l70));
-		l70.uin = gg_fix32(gs->uin);
-		l70.hash_type = gs->hash_type;
-		memcpy(l70.hash, hash_buf, sizeof(l70.hash));
-		l70.status = gg_fix32(gs->initial_status ? gs->initial_status : GG_STATUS_AVAIL);
-		l70.version = gg_fix32(gs->protocol_version | gs->protocol_flags);
-		if (gs->external_addr && gs->external_port > 1023) {
-			l70.local_ip = gs->external_addr;
-			l70.local_port = gg_fix16(gs->external_port);
-		} else {
-			l70.local_ip = local_ip;
-			l70.local_port = gg_fix16(gg_dcc_port);
-		}
-
-		l70.image_size = gs->image_size;
-		l70.dunno2 = 0xbe;
-
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending GG_LOGIN70 packet\n");
-		ret = gg_send_packet(gs, GG_LOGIN70, &l70, sizeof(l70), gs->initial_descr, (gs->initial_descr) ? strlen(gs->initial_descr) : 0, NULL);
+		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) {
-		int errno_copy;
-
-		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
-		errno_copy = errno;
-		close(gs->fd);
-		errno = errno_copy;
-		gs->fd = -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;
@@ -253,17 +351,18 @@ static int gg_session_handle_welcome(str
 	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)
+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
@@ -277,101 +376,168 @@ static int gg_session_handle_login_ok(st
 	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)
+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;
-	close(gs->fd);
-	gs->fd = -1;
+	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)
+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_send_msg_ack *s = (struct gg_send_msg_ack*) ptr;
+	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)
+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)
+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)
+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)
+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");
@@ -379,34 +545,74 @@ static int gg_session_handle_xml_event(s
 	}
 
 	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)
+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)
+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
@@ -417,17 +623,18 @@ static int gg_session_handle_userlist_re
 
 		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=%d\n", gs->userlist_reply, len);
+		gg_debug_session(gs, GG_DEBUG_MISC, "userlist_reply=%p, len=%"
+			GG_SIZE_FMT "\n", gs->userlist_reply, len);
 
 		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;
 		}
 
@@ -448,194 +655,249 @@ static int gg_session_handle_userlist_re
 	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)
+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)
+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)
+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)
+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)
+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)
+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)
 {
-	struct gg_msg_image_reply *i = (void*) p;
+	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_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
+		gg_debug_session(sess, GG_DEBUG_WARNING,
+			"// gg_image_queue_parse() unknown image from %d, "
+			"size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
 		return;
 	}
 
-	if (p[0] == GG_MSG_OPTION_IMAGE_REPLY) {
+	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_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
+			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_MISC, "// gg_image_queue_parse() out of memory\n");
+			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 {
+	} 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)
+	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)
+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:
 			{
-				struct gg_msg_recipients *m = (void*) p;
+				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");
+					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");
+				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");
+					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");
+					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]);
@@ -646,64 +908,76 @@ static int gg_handle_recv_msg_options(st
 			}
 
 			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");
+					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");
+					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");
+					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");
+					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:
 			{
-				struct gg_msg_image_request *i = (void*) p;
+				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");
+					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");
+					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;
@@ -712,17 +986,19 @@ static int gg_handle_recv_msg_options(st
 			}
 
 			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");
+					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;
@@ -730,30 +1006,34 @@ static int gg_handle_recv_msg_options(st
 					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");
+					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);
+				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);
+				gg_debug_session(sess, GG_DEBUG_MISC,
+					"// gg_handle_recv_msg() "
+					"unknown payload 0x%.2x\n", *p);
 				p = packet_end;
 			}
 		}
 	}
 
 	return 0;
 
 handled:
@@ -789,179 +1069,95 @@ static int gg_session_send_msg_ack(struc
 
 	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)
+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;
-	char *tmp;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %d, %p);\n", packet, length, e);
+	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
+	/* 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");
+			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)) {
-			case -1:	// handled
+		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
+			case -2:	/* failed */
 				goto fail;
 
-			case -3:	// malformed
+			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);
 
-	tmp = gg_encoding_convert(payload, GG_ENCODING_CP1250, sess->encoding, length, -1);
-	if (tmp == NULL)
+	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;
-	e->event.msg.message = (unsigned char*) tmp;
-
-	gg_session_send_msg_ack(sess, gg_fix32(r->seq));
-	return 0;
-
-fail:
-	free(e->event.msg.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, %d, %p);\n", packet, length, e);
-
-	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;
+	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;
 	}
 
-	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)) {
-			case -1:	// handled
-				gg_session_send_msg_ack(sess, gg_fix32(r->seq));
-				return 0;