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