Na podstawie informacji i kodu od Jakuba Zawadzkiego, udało mi się
sklecić obsługę połączeń bezpośrednich. W końcu.

Tak jak pisałem w ostatnim mailu, API jest w miarę proste. Doszła nowa
struktura gg_dcc7, analogiczna do gg_dcc. Jeśli chcemy wysłać plik,
wywołujemy gg_dcc7_send_file() i obserwujemy zdarzenia zarówno z
gg_session, jak i gg_dcc7. Parametry funkcji to sesja, odbiorca, nazwa
pliku, nazwa pliku w cp1250 (jeśli NULL to brana jest oryginalna nazwa)
i SHA1 pliku (jeśli NULL to biblioteka liczy, blokując działanie
aplikacji). Wysyłanie jest praktycznie bezobsługowe, ale wypadałoby
zareagować na GG_EVENT_DCC7_DONE i GG_EVENT_DCC7_ERROR w gg_dcc7 oraz
GG_EVENT_DCC7_REJECTED w gg_session, żeby wiedzieć, kiedy wywołać
gg_dcc7_free().

Jeśli ktoś do nas wysyła plik, dostajemy GG_EVENT_DCC7_NEW w gg_session.
Należy przygotować deskryptor, do którego biblioteka może zapisywać
nadchodzące dane i wywołać gg_dcc7_accept() albo od razu wywołać
gg_dcc7_reject(), jeśli nie chcemy połączenia. Tak samo jak przy
wysyłaniu, wystarczy obsłużyć GG_EVENT_DCC7_DONE i GG_EVENT_DCC7_ERROR w
gg_dcc7.

To wszystko. Proste, prawda? W drugim mailu wyślę łatkę na ekg, chociaż
niestety ze względu na organizację listy połączeń, nie wygląda tak
banalnie -- trzeba w wielu miejscach sprawdzać czy chodzi o dcc czy dcc7
i w zależności od tego wyciągać informacje z danej struktury. Gdyby
pozbyć się obsługi starych połączeń bezpośrednich, kod byłby znacznie
bardziej przejrzysty.

Brakuje:

- rozmów głosowych -- muszę jeszcze posniffować, bo oczywiście podczas
  gościnnej sesji z XP nie pomyślałem, że sniffer nie złapie ruchu
  między dwoma lokalnymi klientami, gdy ja nasłuchuję na sieciówce,

- połączeń przez serwer -- enigmatyczne informacje o rodzaju połączenia
  niestety nic mi jeszcze nie mówią,

- timera po stronie libgadu do zestawiania połączenia w drugą stronę,
  gdy nie możemy się połączyć z drugą stroną -- póki co umiemy tylko
  zaakceptować namiary przesłane od oryginalnego klienta, gdy ten nie
  jest w stanie się połączyć z naszym gniazdem nasłuchującym,

- obsługi tajemniczych ramek 0x24,

- dokumentacji kodu -- wiadomo, najtrudniejsze ;)

Na koniec szybki opis zmian w API:

1. Funkcje (bez opisów, bo powinny być oczywiste):

   struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess,
       uin_t rcpt, const char *filename, const char *filename1250,
       const char *hash);

   int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);

   int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);

   struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);

   void gg_dcc7_free(struct gg_dcc7 *d);

2. Zdarzenia w gg_session:

   GG_EVENT_DCC7_NEW - nowe połączenie bezpośrednie:

     struct gg_event {
       union {
         struct gg_dcc7 *dcc7_new;
       } event;
     };

   GG_EVENT_DCC7_ACCEPT - zaakceptowano połączenie:

     struct gg_event {
       union {
         struct {
           struct gg_dcc7 *dcc7;
           int type; /* sposób połączenia (P2P, przez serwer) */
           uint32_t remote_ip;
           uint16_t remote_port;
         } dcc7_accept;
       } event;
     };

   GG_EVENT_DCC7_REJECT - odrzucono połączenie:

     struct gg_event {
       union {
         struct {
           struct gg_dcc7 *dcc7;
           int reason;
         } dcc7_reject;
       } event;
     };

2. Zdarzenia w gg_dcc7:

   GG_EVENT_DCC7_ERROR - błąd połączenia:

     struct gg_event {
       union {
         int dcc7_error;
       } event;
     };

   GG_EVENT_DCC7_DONE - zakończono połączenie.

     // bez informacji

3. Typy sesji:

   GG_SESSION_DCC7_SEND - wysyłanie pliku

   GG_SESSION_DCC7_GET - odbieranie pliku

Rzeczy jeszcze niezaimplementowanych i takich, które na pewno się
zmienią, nie wymieniałem, więc np. na GG_EVENT_DCC7_CONNECTED trzeba
będzie poczekać.

w.
Index: include/libgadu.h.in
===================================================================
RCS file: /home/cvs/libgadu/include/libgadu.h.in,v
retrieving revision 1.7
diff -u -r1.7 libgadu.h.in
--- include/libgadu.h.in	16 May 2007 17:24:02 -0000	1.7
+++ include/libgadu.h.in	3 Jun 2007 09:22:28 -0000
@@ -128,6 +128,15 @@
 typedef uint32_t uin_t;
 
 /*
+ * typedef gg_dcc7_id_t
+ *
+ * typ reprezentuj±cy identyfikator po³±czenia DCC 7.x.
+ */
+typedef struct {
+	uint8_t id[8];
+} gg_dcc7_id_t;
+
+/*
  * ogólna struktura opisuj±ca ró¿ne sesje. przydatna w klientach.
  */
 #define gg_common_head(x) \
@@ -146,6 +155,7 @@
 };
 
 struct gg_image_queue;
+struct gg_dcc7;
 
 /*
  * struct gg_session
@@ -218,6 +228,8 @@
 
 	char *send_buf;		/* bufor z danymi do wys³ania */
 	int send_left;		/* ile jeszcze trzeba wys³aæ */
+
+	struct gg_dcc7 *dcc7_list;	/* lista po³±czeñ bezpo¶rednich skojarzonych z sesj± */
 };
 
 /*
@@ -304,6 +316,43 @@
 	uint16_t remote_port;	/* port drugiej strony */
 };
 
+#define GG_DCC7_HASH_LEN	20
+#define GG_DCC7_FILENAME_LEN	255
+#define GG_DCC7_INFO_LEN	64
+
+struct gg_dcc7 {
+	gg_common_head(struct gg_dcc7)
+
+	gg_dcc7_id_t cid;	/* identyfikator po³±czenia */
+
+	struct gg_event *event;	/* opis zdarzenia */
+
+	uin_t uin;		/* uin klienta */
+	uin_t peer_uin;		/* uin drugiej strony */
+
+	int file_fd;		/* deskryptor pliku */
+	unsigned int offset;	/* offset w pliku */
+	unsigned int size;	/* rozmiar pliku */
+	unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
+				/* nazwa pliku */
+	unsigned char hash[GG_DCC7_HASH_LEN];
+				/* hash pliku */
+
+	int dcc_type;		/* rodzaj po³±czenia bezpo¶redniego */
+	int established;	/* po³±czenie ustanowione */
+	int incoming;		/* po³±czenie przychodz±ce */
+
+	uint32_t local_addr;	/* adres lokalny */
+	uint16_t local_port;	/* port lokalny */
+
+	uint32_t remote_addr;	/* adres drugiej strony */
+	uint16_t remote_port;	/* port drugiej strony */
+
+	struct gg_session *sess;
+				/* sesja do której przypisano po³±czenie */
+	struct gg_dcc7 *next;	/* nastêpne po³±czenie w li¶cie */
+};
+
 /*
  * enum gg_session_t
  *
@@ -327,6 +376,10 @@
 	GG_SESSION_UNREGISTER,	/* usuwanie konta */
 	GG_SESSION_USERLIST_REMOVE,	/* usuwanie userlisty */
 	GG_SESSION_TOKEN,	/* pobieranie tokenu */
+	GG_SESSION_DCC7_SOCKET,	/* nas³uchuj±ce gniazdo */
+	GG_SESSION_DCC7_SEND,	/* wysy³anie pliku */
+	GG_SESSION_DCC7_GET,	/* odbieranie pliku */
+	GG_SESSION_DCC7_VOICE,	/* rozmowa g³osowa */
 
 	GG_SESSION_USER0 = 256,	/* zdefiniowana dla u¿ytkownika */
 	GG_SESSION_USER1,	/* j.w. */
@@ -390,7 +443,14 @@
 	GG_STATE_READING_TYPE,		/* czeka na typ po³±czenia */
 
 	/* nowe. bez sensu jest to API. */
-	GG_STATE_TLS_NEGOTIATION	/* negocjuje po³±czenie TLS */
+	GG_STATE_TLS_NEGOTIATION,	/* negocjuje po³±czenie TLS */
+
+	GG_STATE_REQUESTING_ID,		/* czeka na nadanie identyfikatora */
+	GG_STATE_WAITING_FOR_ACCEPT,	/* czeka na potwierdzenie albo odrzucenie */
+	GG_STATE_WAITING_FOR_INFO,	/* czeka na informacje o po³±czeniu */
+
+	GG_STATE_READING_ID,		/* odbiera identyfikator po³±czenia bezpo¶redniego */
+	GG_STATE_SENDING_ID		/* wysy³a identyfikator po³±czenia bezpo¶redniego */
 };
 
 /*
@@ -502,7 +562,14 @@
 	GG_EVENT_USERLIST,		/* odpowied¼ listy kontaktów w GG 6.0 */
 	GG_EVENT_IMAGE_REQUEST,		/* pro¶ba o wys³anie obrazka GG 6.0 */
 	GG_EVENT_IMAGE_REPLY,		/* podes³any obrazek GG 6.0 */
-	GG_EVENT_DCC_ACK		/* potwierdzenie transmisji */
+	GG_EVENT_DCC_ACK,		/* potwierdzenie transmisji */
+
+	GG_EVENT_DCC7_NEW,		/* nowe po³±czenie bezpo¶rednie 7.x */
+	GG_EVENT_DCC7_ACCEPT,		/* zaakceptowano po³±czenie 7.x */
+	GG_EVENT_DCC7_REJECT,		/* odrzucono po³±czenie 7.x */
+	GG_EVENT_DCC7_CONNECTED,	/* zestawiono po³±czenie 7.x */
+	GG_EVENT_DCC7_ERROR,		/* b³±d po³±czenia 7.x */
+	GG_EVENT_DCC7_DONE		/* zakoñczono po³±czenie 7.x */
 };
 
 #define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
@@ -544,7 +611,13 @@
 	GG_ERROR_DCC_FILE,		/* b³±d odczytu/zapisu pliku */
 	GG_ERROR_DCC_EOF,		/* plik siê skoñczy³? */
 	GG_ERROR_DCC_NET,		/* b³±d wysy³ania/odbierania */
-	GG_ERROR_DCC_REFUSED 		/* po³±czenie odrzucone przez usera */
+	GG_ERROR_DCC_REFUSED, 		/* po³±czenie odrzucone przez usera */
+
+	GG_ERROR_DCC7_HANDSHAKE,	/* b³±d negocjacji */
+	GG_ERROR_DCC7_FILE,		/* b³±d odczytu/zapisu pliku */
+	GG_ERROR_DCC7_EOF,		/* plik siê skoñczy³? */
+	GG_ERROR_DCC7_NET,		/* b³±d wysy³ania/odbierania */
+	GG_ERROR_DCC7_REFUSED 		/* po³±czenie odrzucone przez usera */
 };
 
 /*
@@ -669,6 +742,27 @@
 			char *filename;		/* nazwa pliku */
 			char *image;		/* bufor z obrazkiem */
 		} image_reply;
+
+		struct gg_dcc7 *dcc7_new;	/* nowe po³±czenie bezpo¶rednie -- GG_EVENT_DCC7_NEW */
+
+		int dcc7_error;			/* b³±d po³±czenia bezpo¶redniego 7.x -- GG_EVENT_DCC7_ERROR */
+
+		struct {			/* @dcc7_connected informacja o zestawieniu po³±czenia -- GG_EVENT_DCC7_CONNECTED */
+			struct gg_dcc7 *dcc7;	/* struktura po³±czenia */
+			// XXX czy co¶ siê przyda?
+		} dcc7_connected;
+
+		struct {			/* @dcc7_reject odrzucono po³±czenie bezpo¶rednie -- GG_EVENT_DCC7_REJECT */
+			struct gg_dcc7 *dcc7;	/* struktura po³±czenia */
+			int reason;		/* powód roz³±czenia */
+		} dcc7_reject;
+
+		struct {			/* @dcc7_accept zaakceptowano po³±czenie bezpo¶rednie -- GG_EVENT_DCC7_ACCEPT */
+			struct gg_dcc7 *dcc7;	/* struktura po³±czenia */
+			int type;		/* sposób po³±czenia (P2P, przez serwer) */
+			uint32_t remote_ip;	/* adres zdalnego klienta */
+			uint16_t remote_port;	/* port zdalnego klienta */
+		} dcc7_accept;
 	} event;
 };
 
@@ -912,6 +1006,12 @@
 void gg_dcc_free(struct gg_dcc *c);
 #define gg_free_dcc gg_dcc_free
 
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash);
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);
+void gg_dcc7_free(struct gg_dcc7 *d);
+
 /*
  * je¶li chcemy sobie podebugowaæ, wystarczy ustawiæ `gg_debug_level'.
  * niestety w miarê przybywania wpisów `gg_debug(...)' nie chcia³o mi
@@ -973,6 +1073,8 @@
  * -------------------------------------------------------------------------
  */
 
+int gg_file_hash_sha1(int fd, uint8_t *result);
+
 #ifdef GG_CONFIG_HAVE_PTHREAD
 int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
 void gg_resolve_pthread_cleanup(void *resolver, int kill);
@@ -1017,6 +1119,12 @@
 char *gg_base64_decode(const char *buf);
 int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq);
 
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len);
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len);
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len);
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len);
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len);
+
 #define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
 #define GG_APPMSG_PORT 80
 #define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
@@ -1426,6 +1534,84 @@
 #define GG_DCC_TIMEOUT_FILE_ACK 300	/* 5 minut */
 #define GG_DCC_TIMEOUT_VOICE_ACK 300	/* 5 minut */
 
+/*
+ * pakiety, sta³e, struktury dla DCC 7.x
+ */
+
+#define GG_DCC7_INFO 0x1f
+
+struct gg_dcc7_info {
+	uint32_t uin;			/* numer nadawcy */
+	uint32_t type;			/* sposób po³±czenia */
+	gg_dcc7_id_t id;		/* identyfikator po³±czenia */
+	char info[GG_DCC7_INFO_LEN];	/* informacje o po³±czeniu "ip port" */
+} GG_PACKED;
+
+#define GG_DCC7_NEW 0x20
+
+struct gg_dcc7_new {
+	gg_dcc7_id_t id;		/* identyfikator po³±czenia */
+	uint32_t uin_from;		/* numer nadawcy */
+	uint32_t uin_to;		/* numer odbiorcy */
+	uint32_t type;			/* rodzaj transmisji */
+	unsigned char filename[GG_DCC7_FILENAME_LEN];	/* nazwa pliku */
+	uint32_t size;			/* rozmiar pliku */
+	uint32_t dunno1;		/* 0x00000000 */
+	unsigned char hash[GG_DCC7_HASH_LEN];	/* hash SHA1 */
+} GG_PACKED;
+
+#define GG_DCC7_ACCEPT 0x21
+
+struct gg_dcc7_accept {
+	uint32_t uin;			/* numer przyjmuj±cego po³±czenie */
+	gg_dcc7_id_t id;		/* identyfikator po³±czenia */
+	uint32_t offset;		/* offset przy wznawianiu transmisji */
+	uint32_t dunno1;		/* 0x00000000 */
+} GG_PACKED;
+
+#define GG_DCC7_TYPE_P2P 0x00000001	/* po³±czenie bezpo¶rednie */
+#define GG_DCC7_TYPE_SERVER 0x00000002	/* po³±czenie przez serwer */
+
+#define GG_DCC7_REJECT 0x22
+
+struct gg_dcc7_reject {
+	uint32_t uin;			/* numer odrzucaj±cego po³±czenie */
+	gg_dcc7_id_t id;		/* identyfikator po³±czenia */
+	uint32_t reason;		/* powód roz³±czenia */
+} GG_PACKED;
+
+#define GG_DCC7_REJECT_BUSY 0x00000001	/* trwa po³±czenie bezpo¶rednie, nie umiem obs³u¿yæ wiêcej */
+#define GG_DCC7_REJECT_USER 0x00000002	/* u¿ytkownik odrzuci³ po³±czenie */
+#define GG_DCC7_REJECT_VERSION 0x00000006	/* druga strona ma wersjê klienta nieobs³uguj±c± po³±czeñ bezpo¶rednich tego typu */
+
+#define GG_DCC7_ID_REQUEST 0x23
+
+struct gg_dcc7_id_request {
+	uint32_t type;			/* rodzaj tranmisji */
+} GG_PACKED;
+
+#define GG_DCC7_TYPE_VOICE 0x00000001	/* transmisja g³osu */
+#define GG_DCC7_TYPE_FILE 0x00000004	/* transmisja pliku */
+
+#define GG_DCC7_ID_REPLY 0x23
+
+struct gg_dcc7_id_reply {
+	uint32_t type;			/* rodzaj transmisji */
+	gg_dcc7_id_t id;		/* przyznany identyfikator */
+} GG_PACKED;
+
+#define GG_DCC7_DUNNO1 0x24
+
+struct gg_dcc7_dunno1 {
+	// XXX
+} GG_PACKED;
+
+#define GG_DCC7_TIMEOUT_CONNECT 30	/* 30 sekund */
+#define GG_DCC7_TIMEOUT_SEND 1800	/* 30 minut */
+#define GG_DCC7_TIMEOUT_GET 1800	/* 30 minut */
+#define GG_DCC7_TIMEOUT_FILE_ACK 300	/* 5 minut */
+#define GG_DCC7_TIMEOUT_VOICE_ACK 300	/* 5 minut */
+
 #ifdef __cplusplus
 }
 #ifdef _WIN32
Index: src/Makefile.am
===================================================================
RCS file: /home/cvs/libgadu/src/Makefile.am,v
retrieving revision 1.10
diff -u -r1.10 Makefile.am
--- src/Makefile.am	19 Mar 2007 22:56:11 -0000	1.10
+++ src/Makefile.am	3 Jun 2007 09:22:28 -0000
@@ -1,4 +1,4 @@
 lib_LTLIBRARIES = libgadu.la
-libgadu_la_SOURCES = common.c dcc.c events.c http.c obsolete.c pubdir.c pubdir50.c libgadu.c sha1.c
+libgadu_la_SOURCES = common.c dcc.c dcc7.c events.c http.c obsolete.c pubdir.c pubdir50.c libgadu.c sha1.c
 libgadu_la_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/include
 libgadu_la_LDFLAGS = -version-number 3:7
Index: src/events.c
===================================================================
RCS file: /home/cvs/libgadu/src/events.c,v
retrieving revision 1.115
diff -u -r1.115 events.c
--- src/events.c	17 May 2007 17:18:06 -0000	1.115
+++ src/events.c	3 Jun 2007 09:22:29 -0000
@@ -891,6 +891,71 @@
 			break;
 		}
 
+		case GG_DCC7_ID_REPLY:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
+
+			if (h->length < sizeof(struct gg_dcc7_id_reply))
+				break;
+
+			if (gg_dcc7_handle_id(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_ACCEPT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
+
+			if (h->length < sizeof(struct gg_dcc7_accept))
+				break;
+
+			if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_NEW:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
+
+			if (h->length < sizeof(struct gg_dcc7_new))
+				break;
+
+			if (gg_dcc7_handle_new(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_REJECT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
+
+			if (h->length < sizeof(struct gg_dcc7_reject))
+				break;
+
+			if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_INFO:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
+
+			if (h->length < sizeof(struct gg_dcc7_info))
+				break;
+
+			if (gg_dcc7_handle_info(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
 		default:
 			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
 	}
@@ -1589,12 +1654,6 @@
 			free(sess->password);
 			sess->password = NULL;
 
-			{
-				struct in_addr dcc_ip;
-				dcc_ip.s_addr = gg_dcc_ip;
-				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip));
-			}
-
 			if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
 				struct sockaddr_in sin;
 				unsigned int sin_len = sizeof(sin);
@@ -1611,6 +1670,8 @@
 			} else
 				l.local_ip = gg_dcc_ip;
 
+			sess->client_addr = l.local_ip;
+
 			l.uin = gg_fix32(sess->uin);
 			l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
 			l.version = gg_fix32(sess->protocol_version);
Index: src/libgadu.c
===================================================================
RCS file: /home/cvs/libgadu/src/libgadu.c,v
retrieving revision 1.160
diff -u -r1.160 libgadu.c
--- src/libgadu.c	17 May 2007 17:18:06 -0000	1.160
+++ src/libgadu.c	3 Jun 2007 09:22:29 -0000
@@ -993,6 +993,8 @@
  */
 void gg_free_session(struct gg_session *sess)
 {
+	struct gg_dcc7 *dcc;
+
 	if (!sess)
 		return;
 
@@ -1039,6 +1041,9 @@
 	if (sess->send_buf)
 		free(sess->send_buf);
 
+	for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
+		dcc->sess = NULL;
+
 	free(sess);
 }
 
@@ -1068,6 +1073,11 @@
 		return -1;
 	}
 
+	// dodaj flagê obs³ugi po³±czeñ g³osowych zgodn± z GG 7.x
+	
+	if ((sess->protocol_version & 0xff) >= 0x2a && (sess->protocol_version & GG_HAS_AUDIO_MASK) && !GG_S_I(status))
+		status |= 0x20000;
+
 	p.status = gg_fix32(status);
 
 	sess->status = status;
Index: src/sha1.c
===================================================================
RCS file: /home/cvs/libgadu/src/sha1.c,v
retrieving revision 1.2
diff -u -r1.2 sha1.c
--- src/sha1.c	19 Mar 2007 23:16:24 -0000	1.2
+++ src/sha1.c	3 Jun 2007 09:22:29 -0000
@@ -18,6 +18,9 @@
  *  USA.
  */
 
+#include <sys/types.h>
+#include <unistd.h>
+
 #include "libgadu.h"
 
 #ifdef GG_CONFIG_HAVE_OPENSSL
@@ -201,13 +204,11 @@
 #endif /* GG_CONFIG_HAVE_OPENSSL */
 
 /**
- * gg_login_hash_sha1()
- *
- * liczy hash z hasla i danego seeda, korzystajac z SHA1
+ * Liczy skrót SHA1 z ziarna i hasła.
  *
- *  - password - haslo do hashowania
- *  - seed - wartosc podana przez serwer
- *  - result - przynajmniej 20 znakowy bufor ktory otrzyma hash
+ * \param password Hasło
+ * \param seed Ziarno
+ * \param result Bufor na wynik funkcji skrótu (20 bajtów)
  */
 void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
 {
@@ -221,3 +222,37 @@
 	SHA1_Final(result, &ctx);
 }
 
+/**
+ * \brief Liczy skrót SHA1 z pliku.
+ *
+ * \param fd Deskryptor pliku
+ *
+ * \return 0 lub -1
+ */
+int gg_file_hash_sha1(int fd, uint8_t *result)
+{
+	unsigned char buf[4096];
+	SHA_CTX ctx;
+	off_t pos;
+	int res;
+
+	if ((pos = lseek(fd, 0, SEEK_CUR)) == (off_t) -1)
+		return -1;
+
+	SHA1_Init(&ctx);
+
+	while ((res = read(fd, buf, sizeof(buf))) > 0)
+		SHA1_Update(&ctx, buf, res);
+
+	if (res == -1)
+		return -1;
+
+	SHA1_Final(result, &ctx);
+
+	if (lseek(fd, pos, SEEK_SET) == (off_t) -1)
+		return -1;
+
+	return 0;
+
+}
+
--- /dev/null	2007-06-03 12:30:10.360305259 +0200
+++ src/dcc7.c	2007-06-03 11:16:06.000000000 +0200
@@ -0,0 +1,824 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2001-2007 Wojtek Kaniewski <[EMAIL PROTECTED]>
+ *                          Tomasz Chiliñski <[EMAIL PROTECTED]>
+ *                          Adam Wysocki <[EMAIL PROTECTED]>
+ *  
+ *  Thanks to Jakub Zawadzki <[EMAIL PROTECTED]>
+ *
+ *  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.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+#  include <sys/filio.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "libgadu.h"
+
+static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	if (!sess || !dcc || dcc->next) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	dcc->next = sess->dcc7_list;
+	sess->dcc7_list = dcc;
+
+	return 0;
+}
+
+static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	struct gg_dcc7 *tmp;
+
+	if (!sess || !dcc) {
+		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; tmp = tmp->next) {
+		if (tmp->next == dcc) {
+			tmp = dcc->next;
+			dcc->next = NULL;
+			return 0;
+		}
+	}
+
+	errno = ENOENT;
+	return -1;
+}
+
+static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id)
+{
+	struct gg_dcc7 *tmp;
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		if (!memcmp(&tmp->cid, &id, sizeof(id)))
+			return tmp;
+	}
+
+	return NULL;
+}
+
+static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+#ifdef SO_SNDTIMEO
+	struct timeval tv;
+#endif
+
+	if (!sess || !dcc) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
+		return -1;
+	}
+
+	dcc->state = GG_STATE_CONNECTING;
+	dcc->check = GG_CHECK_WRITE;
+	dcc->timeout = GG_DEFAULT_TIMEOUT;
+
+	// XXX to pewnie nie zadzia³a, zrobiæ software'owy timeout
+
+#ifdef SO_SNDTIMEO
+	tv.tv_sec = GG_DCC7_TIMEOUT_CONNECT;
+	tv.tv_usec = 0;
+
+	setsockopt(dcc->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+#endif
+
+	return 0;
+}
+
+static int gg_dcc7_socket(struct gg_dcc7 *dcc, uint16_t port)
+{
+	struct sockaddr_in sin;
+	int fd, bound = 0;
+
+	if (!dcc) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc7_socket() can't create socket (%s)\n", strerror(errno));
+		return -1;
+	}
+
+	// XXX losowaæ porty?
+	
+	if (!port)
+		port = GG_DEFAULT_DCC_PORT;
+
+	while (!bound) {
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = INADDR_ANY;
+		sin.sin_port = htons(port);
+
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc7_socket() trying port %d\n", port);
+		if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin)))
+			bound = 1;
+		else {
+			if (++port == 65535) {
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc7_socket() no free port found\n");
+				close(fd);
+				errno = ENOENT;
+				return -1;
+			}
+		}
+	}
+
+	if (listen(fd, 1)) {
+		int errsv = errno;
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc7_socket() unable to listen (%s)\n", strerror(errno));
+		close(fd);
+		errno = errsv;
+		return -1;
+	}
+
+	dcc->fd = fd;
+	dcc->local_port = port;
+	dcc->type = GG_SESSION_DCC7_SOCKET;
+	
+	dcc->state = GG_STATE_LISTENING;
+	dcc->check = GG_CHECK_READ;
+	dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+	return 0;
+}
+
+static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
+{
+	struct gg_dcc7_id_request pkt;
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
+		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);
+}
+
+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;
+	struct stat st;
+	int fd = -1;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p);\n", sess, rcpt, filename, hash);
+
+	if (!sess || !rcpt || !filename) {
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if (!filename1250)
+		filename1250 = filename;
+
+	if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc7_send_file() not enough memory\n");
+		goto fail;
+	}
+
+	if (stat(filename, &st) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if ((st.st_mode & S_IFDIR)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if ((fd = open(filename, O_RDONLY)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
+		goto fail;
+
+	if ((tmp = strrchr(filename1250, '/')))
+		filename1250 = tmp + 1;
+
+	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 = st.st_size;
+
+	strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1);
+	dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+
+	if (hash) {
+		memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
+	} else {
+		if (gg_file_hash_sha1(fd, dcc->hash) == -1)
+			goto fail;
+	}
+
+	if (gg_dcc7_session_add(sess, dcc) == -1)
+		goto fail;
+
+	return dcc;
+
+fail:
+	if (fd != -1) {
+		int errsv = errno;
+		close(fd);
+		errno = errsv;
+	}
+
+	free(dcc);
+
+	return NULL;
+}
+
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
+{
+	struct gg_dcc7_accept pkt1;
+	struct gg_dcc7_info pkt2;
+
+	if (!dcc->sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	// XXX daæ mo¿liwo¶æ konfiguracji?
+	
+	dcc->local_addr = dcc->sess->client_addr;
+
+	if (gg_dcc7_socket(dcc, 0) == -1)
+		return -1;
+
+	memset(&pkt1, 0, sizeof(pkt1));
+	pkt1.uin = gg_fix32(dcc->peer_uin);
+	pkt1.id = dcc->cid;
+	pkt1.offset = gg_fix32(offset);
+
+	if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt1, sizeof(pkt1), NULL) == -1)
+		return -1;
+
+	memset(&pkt2, 0, sizeof(pkt2));
+	pkt2.uin = gg_fix32(dcc->peer_uin);
+	pkt2.type = GG_DCC7_TYPE_P2P;
+	pkt2.id = dcc->cid;
+	snprintf((char*) pkt2.info, sizeof(pkt2.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), dcc->local_port);
+
+	return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt2, sizeof(pkt2), NULL);
+}
+
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
+{
+	struct gg_dcc7_reject pkt;
+
+	if (!dcc->sess) {
+		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);
+}
+
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_id_reply *p = payload;
+	struct gg_dcc7 *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_MISC, "// sess %p dcc_list %p\n", sess, sess->dcc7_list);
+
+	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 != 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);
+
+				strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN);
+
+				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;
+}
+
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_accept *p = payload;
+	struct gg_dcc7 *dcc;
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id))) {
+		// XXX wys³aæ reject?
+		return 0;
+	}
+
+	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+		// XXX poinformowaæ?
+		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;
+}
+
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_info *p = payload;
+	struct gg_dcc7 *dcc;
+	char *tmp;
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id)))
+		return 0;
+	
+	if (p->type != GG_DCC7_TYPE_P2P) {
+		// XXX poinformowaæ?
+		return 0;
+	}
+
+	if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
+		// XXX poinformowaæ?
+		return 0;
+	}
+
+	if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
+		// XXX poinformowaæ?
+		return 0;
+	}
+
+	// je¶li nadal czekamy na po³±czenie przychodz±ce, a druga strona nie
+	// daje rady i oferuje namiary na siebie, bierzemy co daj±.
+
+	if (dcc->state != GG_STATE_WAITING_FOR_INFO && dcc->state != GG_STATE_LISTENING) {
+		// XXX poinformowaæ?
+		return 0;
+	}
+
+	if (dcc->state == GG_STATE_LISTENING) {
+		close(dcc->fd);
+		dcc->fd = -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;
+	}
+
+	if (gg_dcc7_connect(sess, dcc) == -1) {
+		// XXX poinformowaæ?
+		return 0;
+	}
+
+	return 0;
+}
+
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_reject *p = payload;
+	struct gg_dcc7 *dcc;
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id)))
+		return 0;
+	
+	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+		// XXX poinformowaæ?
+		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;
+}
+
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_new *p = payload;
+	struct gg_dcc7 *dcc;
+
+	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 - 1);
+			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(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 (%04x) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from));
+
+			break;
+	}
+
+	return 0;
+}
+
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
+{
+	struct gg_event *e;
+
+//	gg_debug(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(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid argument\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!(e = malloc(sizeof(struct gg_event)))) {
+		gg_debug(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, one = 1;
+			unsigned int sin_len = sizeof(sin);
+
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
+
+			if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
+				return e;
+			}
+
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+
+#ifdef FIONBIO
+			if (ioctl(fd, FIONBIO, &one) == -1) {
+#else
+			if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+				gg_debug(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, error = 0;
+			unsigned int error_size = sizeof(error);
+
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
+
+			if ((res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			gg_debug(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:
+		{
+			gg_dcc7_id_t id;
+			int res;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
+
+			if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) {
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() 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(&id, &dcc->cid, sizeof(id))) {
+				gg_debug(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 {
+				dcc->state = GG_STATE_SENDING_FILE;
+				dcc->check = GG_CHECK_WRITE;
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			}
+
+			return e;
+		}
+
+		case GG_STATE_SENDING_ID:
+		{
+			int res;
+
+			gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
+
+			if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) {
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (dcc->incoming) {
+				dcc->state = GG_STATE_GETTING_FILE;
+				dcc->check = GG_CHECK_READ;
+				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];
+			int chunk, res;
+
+//			gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+			if (lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
+				gg_debug(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(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
+				return e;
+			}
+
+			if ((res = write(dcc->fd, buf, res)) == -1) {
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() 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(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				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(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+			if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) {
+				gg_debug(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() 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(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(GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			dcc->state = GG_STATE_GETTING_FILE;
+			dcc->check = GG_CHECK_READ;
+			dcc->timeout = GG_DCC7_TIMEOUT_GET;
+
+			return e;
+		}
+
+		default:
+		{
+			gg_debug(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;
+}
+
+void gg_dcc7_free(struct gg_dcc7 *dcc)
+{
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p);\n", dcc);
+
+	if (!dcc)
+		return;
+
+	if (dcc->fd != -1)
+		close(dcc->fd);
+
+	if (dcc->file_fd != -1)
+		close(dcc->file_fd);
+
+	if (dcc->sess)
+		gg_dcc7_session_remove(dcc->sess, dcc);
+
+	free(dcc);
+}
+
_______________________________________________
libgadu-devel mailing list
[email protected]
http://lists.ziew.org/mailman/listinfo/libgadu-devel

Reply via email to