libpurple/protocols/gg/lib/dcc7.c

changeset 38882
bea4cc95b40f
parent 38881
25cb836b9cec
parent 38182
783878958371
child 38883
90462fef3dd8
--- a/libpurple/protocols/gg/lib/dcc7.c	Wed Oct 26 10:17:10 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1660 +0,0 @@
-/* $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
- *  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., 51 Franklin St, Fifth Floor, Boston, MA 02110,
- *  USA.
- */
-
-/**
- * \file dcc7.c
- *
- * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
- */
-
-#include "fileio.h"
-#include "network.h"
-#include "strman.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-
-#include "libgadu.h"
-#include "protocol.h"
-#include "resolver.h"
-#include "internal.h"
-#include "debug.h"
-
-#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
- * \param dcc Struktura połączenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
-{
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
-
-	if (!sess || !dcc || dcc->next) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_add() invalid parameters\n");
-		errno = EINVAL;
-		return -1;
-	}
-
-	dcc->next = sess->dcc7_list;
-	sess->dcc7_list = dcc;
-
-	return 0;
-}
-
-/**
- * \internal Usuwa połączenie bezpośrednie z sesji.
- *
- * \param sess Struktura sesji
- * \param dcc Struktura połączenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
-{
-	struct gg_dcc7 *tmp;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
-
-	if (sess == NULL || dcc == NULL) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
-		errno = EINVAL;
-		return -1;
-	}
-
-	if (sess->dcc7_list == dcc) {
-		sess->dcc7_list = dcc->next;
-		dcc->next = NULL;
-		return 0;
-	}
-
-	for (tmp = sess->dcc7_list; tmp != NULL; tmp = tmp->next) {
-		if (tmp->next == dcc) {
-			tmp->next = dcc->next;
-			dcc->next = NULL;
-			return 0;
-		}
-	}
-
-	errno = ENOENT;
-	return -1;
-}
-
-/**
- * \internal Zwraca strukturę połączenia o danym identyfikatorze.
- *
- * \param sess Struktura sesji
- * \param id Identyfikator połączenia
- * \param uin Numer nadawcy lub odbiorcy
- *
- * \return Struktura połączenia lub \c NULL jeśli nie znaleziono
- */
-static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
-{
-	struct gg_dcc7 *tmp;
-	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)
-				return tmp;
-		} else {
-			if (!memcmp(&tmp->cid, &id, sizeof(id)))
-				return tmp;
-		}
-	}
-
-	return NULL;
-}
-
-/**
- * \internal Rozpoczyna proces pobierania adresu
- *
- * \param dcc Struktura połączenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc)
-{
-	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc);
-
-	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));
-		return -1;
-	}
-
-	dcc->state = GG_STATE_RESOLVING_RELAY;
-	dcc->check = GG_CHECK_READ;
-	dcc->timeout = GG_DEFAULT_TIMEOUT;
-
-	return 0;
-}
-
-/**
- * \internal Nawiązuje połączenie bezpośrednie
- *
- * \param dcc Struktura połączenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_dcc7_connect(struct gg_dcc7 *dcc)
-{
-	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc);
-
-	if (dcc == NULL) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
-		errno = EINVAL;
-		return -1;
-	}
-
-	if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
-		return -1;
-	}
-
-	dcc->state = GG_STATE_CONNECTING;
-	dcc->check = GG_CHECK_WRITE;
-	dcc->timeout = GG_DCC7_TIMEOUT_CONNECT;
-	dcc->soft_timeout = 1;
-
-	return 0;
-}
-
-/**
- * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego
- *
- * \param dcc Struktura połączenia
- * \param addr Preferowany adres (jeśli równy 0, nasłuchujemy na wszystkich interfejsach)
- * \param port Preferowany port (jeśli równy 0, nasłuchujemy na losowym)
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint32_t addr, uint16_t port)
-{
-	struct sockaddr_in sin;
-	socklen_t sin_len = sizeof(sin);
-	int errsv;
-	int fd;
-
-	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
-
-	if (!dcc) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
-		errno = EINVAL;
-		return -1;
-	}
-
-	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
-		return -1;
-	}
-
-	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);
-		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;
-	close(fd);
-	errno = errsv;
-	return -1;
-}
-
-/**
- * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry
- *
- * \param dcc Struktura połączenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
-{
-	struct gg_dcc7_info pkt;
-	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
-		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);
-
-	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());
-
-	return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
-}
-
-/**
- * \internal Odwraca połączenie po nieudanym connect()
- *
- * \param dcc Struktura połączenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
-{
-	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
-
-	if (dcc == NULL)
-		return -1;
-
-	if (dcc->reverse) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
-		return -1;
-	}
-
-	gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
-	close(dcc->fd);
-	dcc->fd = -1;
-	dcc->reverse = 1;
-
-	return gg_dcc7_listen_and_send_info(dcc);
-}
-
-/**
- * \internal Wysyła do serwera żądanie nadania identyfikatora sesji
- *
- * \param sess Struktura sesji
- * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE)
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
-{
-	struct gg_dcc7_id_request pkt;
-
-	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type);
-
-	if (!sess) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n");
-		errno = ENOTCONN;
-		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.
- *
- * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz
- * \c gg_dcc_send_file_fd().
- *
- * \param sess Struktura sesji
- * \param rcpt Numer odbiorcy
- * \param fd Deskryptor pliku
- * \param size Rozmiar pliku
- * \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)
-{
-	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;
-	}
-
-	if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
-		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n");
-		goto fail;
-	}
-
-	if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
-		goto fail;
-
-	memset(dcc, 0, sizeof(struct gg_dcc7));
-	dcc->type = GG_SESSION_DCC7_SEND;
-	dcc->dcc_type = GG_DCC7_TYPE_FILE;
-	dcc->state = GG_STATE_REQUESTING_ID;
-	dcc->timeout = GG_DEFAULT_TIMEOUT;
-	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);
-	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;
-
-fail:
-	free(dcc);
-	return NULL;
-}
-
-/**
- * Rozpoczyna wysyłanie pliku o danej nazwie.
- *
- * \param sess Struktura sesji
- * \param rcpt Numer odbiorcy
- * \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 *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);
-
-	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 ((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 (!hash) {
-		if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
-			goto fail;
-
-		hash = hash_buf;
-	}
-
-	if ((tmp = strrchr(filename1250, '/')))
-		filename1250 = tmp + 1;
-
-	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;
-		gg_file_close(fd);
-		errno = errsv;
-	}
-
-	free(dcc);
-	return NULL;
-}
-
-/**
- * \internal Rozpoczyna wysyłanie pliku o danym deskryptorze.
- *
- * \note Wysyłanie pliku nie będzie działać poprawnie, jeśli deskryptor
- * źródłowy jest w trybie nieblokującym i w pewnym momencie zabraknie danych.
- *
- * \param sess Struktura sesji
- * \param rcpt Numer odbiorcy
- * \param fd Deskryptor pliku
- * \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)
-{
-	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.
- *
- * \param dcc Struktura połączenia
- * \param offset Początkowy offset przy wznawianiu przesyłania pliku
- *
- * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset
- * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub
- * podobną.
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup dcc7
- */
-int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
-{
-	struct gg_dcc7_accept pkt;
-
-	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
-
-	if (!dcc || !dcc->sess) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	memset(&pkt, 0, sizeof(pkt));
-	pkt.uin = gg_fix32(dcc->peer_uin);
-	pkt.id = dcc->cid;
-	pkt.offset = gg_fix32(offset);
-
-	if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1)
-		return -1;
-
-	dcc->offset = offset;
-
-	return gg_dcc7_listen_and_send_info(dcc);
-}
-
-/**
- * Odrzuca próbę przesłania pliku.
- *
- * \param dcc Struktura połączenia
- * \param reason Powód odrzucenia
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- *
- * \ingroup dcc7
- */
-int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
-{
-	struct gg_dcc7_reject pkt;
-
-	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
-
-	if (!dcc || !dcc->sess) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	memset(&pkt, 0, sizeof(pkt));
-	pkt.uin = gg_fix32(dcc->peer_uin);
-	pkt.id = dcc->cid;
-	pkt.reason = gg_fix32(reason);
-
-	return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL);
-}
-
-/**
- * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego.
- *
- * \param sess Struktura sesji
- * \param e Struktura zdarzenia
- * \param payload Treść pakietu
- * \param len Długość pakietu
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-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);
-
-		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);
-
-				/* 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);
-			}
-		}
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego.
- *
- * \param sess Struktura sesji
- * \param e Struktura zdarzenia
- * \param payload Treść pakietu
- * \param len Długość pakietu
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
-{
-	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? */
-		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? */
-
-	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.
- *
- * \param sess Struktura sesji
- * \param e Struktura zdarzenia
- * \param payload Treść pakietu
- * \param len Długość pakietu
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-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);
-
-	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:
-		if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
-			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
-			e->type = GG_EVENT_DCC7_ERROR;
-			e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
-			return 0;
-		}
-
-		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_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__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));
-
-			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? */
-
-		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);
-		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;
-		e->event.dcc7_pending.dcc7 = dcc;
-	}
-
-	if (gg_dcc7_connect(dcc) == -1) {
-		if (gg_dcc7_reverse_connect(dcc) == -1) {
-			e->type = GG_EVENT_DCC7_ERROR;
-			e->event.dcc7_error = GG_ERROR_DCC7_NET;
-			return 0;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego.
- *
- * \param sess Struktura sesji
- * \param e Struktura zdarzenia
- * \param payload Treść pakietu
- * \param len Długość pakietu
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
-{
-	const struct gg_dcc7_reject *p = payload;
-	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? */
-
-	return 0;
-}
-
-/**
- * \internal Obsługuje pakiet nowego połączenia bezpośredniego.
- *
- * \param sess Struktura sesji
- * \param e Struktura zdarzenia
- * \param payload Treść pakietu
- * \param len Długość pakietu
- *
- * \return 0 jeśli się powiodło, -1 w przypadku błędu
- */
-int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
-{
-	const struct gg_dcc7_new *p = payload;
-	struct gg_dcc7 *dcc;
-
-	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_dcc7_free(dcc);
-				return -1;
-			}
-
-			dcc->size = gg_fix32(p->size);
-			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_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 %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);
-
-	if (!dcc) {
-		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
-		errno = EINVAL;
-		return -1;
-	}
-
-	switch (dcc->type) {
-		case GG_SESSION_DCC7_GET:
-			dcc->state = GG_STATE_GETTING_FILE;
-			dcc->check = GG_CHECK_READ;
-			return 0;
-
-		case GG_SESSION_DCC7_SEND:
-			dcc->state = GG_STATE_SENDING_FILE;
-			dcc->check = GG_CHECK_WRITE;
-			return 0;
-
-		case GG_SESSION_DCC7_VOICE:
-			dcc->state = GG_STATE_READING_VOICE_DATA;
-			dcc->check = GG_CHECK_READ;
-			return 0;
-	}
-
-	errno = EINVAL;
-
-	return -1;
-}
-
-/**
- * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
- *
- * 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 dcc Struktura połączenia
- *
- * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
- *
- * \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))
-	{
-		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;
-	}
-
-	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;
-			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));
-				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));
-
-			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;
-
-			dcc->state = GG_STATE_READING_ID;
-			dcc->check = GG_CHECK_READ;
-			dcc->timeout = GG_DEFAULT_TIMEOUT;
-			dcc->incoming = 1;
-
-			dcc->remote_port = ntohs(sin.sin_port);
-			dcc->remote_addr = sin.sin_addr.s_addr;
-
-			e->type = GG_EVENT_DCC7_CONNECTED;
-			e->event.dcc7_connected.dcc7 = dcc;
-
-			return e;
-		}
-
-		case GG_STATE_CONNECTING:
-		{
-			int res = 0, error = 0;
-			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 (dcc->relay) {
-					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\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;
-					} else {
-						e->type = GG_EVENT_DCC7_ERROR;
-						e->event.dcc_error = GG_ERROR_DCC7_NET;
-					}
-
-					return e;
-				}
-			}
-
-			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
-
-			dcc->state = GG_STATE_SENDING_ID;
-			dcc->check = GG_CHECK_WRITE;
-			dcc->timeout = GG_DEFAULT_TIMEOUT;
-			dcc->incoming = 0;
-
-			return e;
-		}
-
-		case GG_STATE_READING_ID:
-		{
-			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 = 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 = 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;
-					e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
-					return e;
-				}
-			}
-
-			if (dcc->incoming) {
-				dcc->state = GG_STATE_SENDING_ID;
-				dcc->check = GG_CHECK_WRITE;
-				dcc->timeout = GG_DEFAULT_TIMEOUT;
-			} else {
-				gg_dcc7_postauth_fixup(dcc);
-				dcc->timeout = GG_DEFAULT_TIMEOUT;
-			}
-
-			return e;
-		}
-
-		case GG_STATE_SENDING_ID:
-		{
-			int res;
-
-			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 = 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 = 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);
-				dcc->timeout = GG_DEFAULT_TIMEOUT;
-			} else {
-				dcc->state = GG_STATE_READING_ID;
-				dcc->check = GG_CHECK_READ;
-				dcc->timeout = GG_DEFAULT_TIMEOUT;
-			}
-
-			return e;
-		}
-
-		case GG_STATE_SENDING_FILE:
-		{
-			char buf[1024];
-			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);
-
-			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));
-				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));
-				e->type = GG_EVENT_DCC7_ERROR;
-				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
-				return e;
-			}
-
-			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) {
-				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;
-			}
-
-			dcc->state = GG_STATE_SENDING_FILE;
-			dcc->check = GG_CHECK_WRITE;
-			dcc->timeout = GG_DCC7_TIMEOUT_SEND;
-
-			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);
-
-			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 = 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? */
-
-			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));
-				e->type = GG_EVENT_DCC7_ERROR;
-				e->event.dcc_error = GG_ERROR_DCC7_FILE;
-				return e;
-			}
-
-			dcc->offset += res;
-
-			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;
-			}
-
-			dcc->state = GG_STATE_GETTING_FILE;
-			dcc->check = GG_CHECK_READ;
-			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");
-
-			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;
-				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);
-
-			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));
-				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;
-			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));
-				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_dump_dcc(dcc, GG_DEBUG_DUMP, (const char*) &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;
-			dcc->timeout = GG_DEFAULT_TIMEOUT;
-
-			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 = 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() 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))
-			{
-				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_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\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);
-			}
-
-			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\n");
-				e->type = GG_EVENT_DCC7_ERROR;
-				e->event.dcc_error = GG_ERROR_DCC7_RELAY;
-				return e;
-			}
-
-			return e;
-		}
-
-		default:
-		{
-			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
-			e->type = GG_EVENT_DCC7_ERROR;
-			e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
-
-			return e;
-		}
-	}
-
-	return e;
-}
-
-/**
- * Zwalnia zasoby używane przez połączenie bezpośrednie.
- *
- * \param dcc Struktura połączenia
- *
- * \ingroup dcc7
- */
-void gg_dcc7_free(struct gg_dcc7 *dcc)
-{
-	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
-
-	if (!dcc)
-		return;
-
-	if (dcc->fd != -1)
-		close(dcc->fd);
-
-	if (dcc->file_fd != -1)
-		gg_file_close(dcc->file_fd);
-
-	if (dcc->sess)
-		gg_dcc7_session_remove(dcc->sess, dcc);
-
-	free(dcc->relay_list);
-
-	free(dcc);
-}

mercurial