libpurple/protocols/gg/lib/events.c

changeset 38882
bea4cc95b40f
parent 38881
25cb836b9cec
parent 38182
783878958371
child 38883
90462fef3dd8
--- a/libpurple/protocols/gg/lib/events.c	Wed Oct 26 10:17:10 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1901 +0,0 @@
-/* $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
- *  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 events.c
- *
- * \brief Obsługa zdarzeń
- *
- * \todo Poprawna obsługa gg_proxy_http_only
- */
-
-#include "strman.h"
-#include "network.h"
-
-#include "libgadu.h"
-#include "protocol.h"
-#include "internal.h"
-#include "encoding.h"
-#include "debug.h"
-#include "session.h"
-#include "resolver.h"
-#include "config.h"
-
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-#include <ctype.h>
-#ifdef GG_CONFIG_HAVE_GNUTLS
-#  include <gnutls/gnutls.h>
-#  include <gnutls/x509.h>
-#endif
-#ifdef GG_CONFIG_HAVE_OPENSSL
-#  include <openssl/err.h>
-#  include <openssl/x509.h>
-#  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.
- *
- * \param e Struktura zdarzenia
- *
- * \ingroup events
- */
-void gg_event_free(struct gg_event *e)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
-
-	if (!e)
-		return;
-
-	switch (e->type) {
-		case GG_EVENT_MSG:
-		case GG_EVENT_MULTILOGON_MSG:
-			free(e->event.msg.message);
-			free(e->event.msg.formats);
-			free(e->event.msg.recipients);
-			free(e->event.msg.xhtml_message);
-			break;
-
-		case GG_EVENT_NOTIFY:
-			free(e->event.notify);
-			break;
-
-		case GG_EVENT_NOTIFY60:
-		{
-			int i;
-
-			for (i = 0; e->event.notify60[i].uin; i++)
-				free(e->event.notify60[i].descr);
-
-			free(e->event.notify60);
-
-			break;
-		}
-
-		case GG_EVENT_STATUS60:
-			free(e->event.status60.descr);
-			break;
-
-		case GG_EVENT_STATUS:
-			free(e->event.status.descr);
-			break;
-
-		case GG_EVENT_NOTIFY_DESCR:
-			free(e->event.notify_descr.notify);
-			free(e->event.notify_descr.descr);
-			break;
-
-		case GG_EVENT_DCC_VOICE_DATA:
-			free(e->event.dcc_voice_data.data);
-			break;
-
-		case GG_EVENT_PUBDIR50_SEARCH_REPLY:
-		case GG_EVENT_PUBDIR50_READ:
-		case GG_EVENT_PUBDIR50_WRITE:
-			gg_pubdir50_free(e->event.pubdir50);
-			break;
-
-		case GG_EVENT_USERLIST:
-			free(e->event.userlist.reply);
-			break;
-
-		case GG_EVENT_IMAGE_REPLY:
-			free(e->event.image_reply.filename);
-			free(e->event.image_reply.image);
-			break;
-
-		case GG_EVENT_XML_EVENT:
-			free(e->event.xml_event.data);
-			break;
-
-		case GG_EVENT_JSON_EVENT:
-			free(e->event.json_event.data);
-			free(e->event.json_event.type);
-			break;
-
-		case GG_EVENT_USER_DATA:
-		{
-			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 */
-
-/**
- * \internal Usuwa obrazek z kolejki do wysłania.
- *
- * \param s Struktura sesji
- * \param q Struktura obrazka
- * \param freeq Flaga zwolnienia elementu kolejki
- *
- * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
- */
-int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
-{
-	if (!s || !q) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (s->images == q)
-		s->images = q->next;
-	else {
-		struct gg_image_queue *qq;
-
-		for (qq = s->images; qq; qq = qq->next) {
-			if (qq->next == q) {
-				qq->next = q->next;
-				break;
-			}
-		}
-	}
-
-	if (freeq) {
-		free(q->image);
-		free(q->filename);
-		free(q);
-	}
-
-	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[129];
-	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 *ge;
-	struct gg_session_private *priv;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
-
-	if (sess == NULL) {
-		errno = EFAULT;
-		return NULL;
-	}
-
-	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;
-	}
-
-	memset(ge, 0, sizeof(struct gg_event));
-
-	ge->type = GG_EVENT_NONE;
-
-	for (;;) {
-		unsigned int i, found = 0;
-		gg_action_t res;
-
-		res = GG_ACTION_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 (!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;
-		}
-
-		if (!sess->async && ge->type == GG_EVENT_NONE && res == GG_ACTION_WAIT)
-			res = GG_ACTION_NEXT;
-
-		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;
-				}
-				return ge;
-
-			case GG_ACTION_NEXT:
-				continue;
-
-			case GG_ACTION_FAIL:
-				sess->state = GG_STATE_IDLE;
-
-				gg_close(sess);
-
-				if (ge->event.failure != 0) {
-					ge->type = GG_EVENT_CONN_FAILED;
-				} else {
-					free(ge);
-					ge = NULL;
-				}
-
-				return ge;
-
-			/* Celowo nie ma default */
-		}
-	}
-}
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */

mercurial