comparison libpurple/protocols/gg/lib/dcc7.c @ 29538:6359fde67f4c

Update our internal libgadu to 1.9.0-rc2. This does not yet build on Windows. Refs #10542. The Windows build errors are the only reason this isn't on `im.pidgin.pidgin` already.
author John Bailey <rekkanoryo@rekkanoryo.org>
date Sun, 21 Feb 2010 16:52:42 +0000
parents
children db6735e579f8
comparison
equal deleted inserted replaced
29474:551253814063 29538:6359fde67f4c
1 /* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */
2
3 /*
4 * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
5 * Tomasz Chiliński <chilek@chilan.com>
6 * Adam Wysocki <gophi@ekg.chmurka.net>
7 *
8 * Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License Version
12 * 2.1 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110,
22 * USA.
23 */
24
25 /**
26 * \file dcc7.c
27 *
28 * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
29 */
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #ifdef sun
38 # include <sys/filio.h>
39 #endif
40 #include <time.h>
41
42 #include <ctype.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdarg.h>
46 #include <string.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50
51 #include "compat.h"
52 #include "libgadu.h"
53
54 #define gg_debug_dcc(dcc, fmt...) \
55 gg_debug_session((dcc) ? (dcc)->sess : NULL, fmt)
56
57 /**
58 * \internal Dodaje połączenie bezpośrednie do sesji.
59 *
60 * \param sess Struktura sesji
61 * \param dcc Struktura połączenia
62 *
63 * \return 0 jeśli się powiodło, -1 w przypadku błędu
64 */
65 static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
66 {
67 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
68
69 if (!sess || !dcc || dcc->next) {
70 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
71 errno = EINVAL;
72 return -1;
73 }
74
75 dcc->next = sess->dcc7_list;
76 sess->dcc7_list = dcc;
77
78 return 0;
79 }
80
81 /**
82 * \internal Usuwa połączenie bezpośrednie z sesji.
83 *
84 * \param sess Struktura sesji
85 * \param dcc Struktura połączenia
86 *
87 * \return 0 jeśli się powiodło, -1 w przypadku błędu
88 */
89 static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
90 {
91 struct gg_dcc7 *tmp;
92
93 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
94
95 if (!sess || !dcc) {
96 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
97 errno = EINVAL;
98 return -1;
99 }
100
101 if (sess->dcc7_list == dcc) {
102 sess->dcc7_list = dcc->next;
103 dcc->next = NULL;
104 return 0;
105 }
106
107 for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
108 if (tmp->next == dcc) {
109 tmp = dcc->next;
110 dcc->next = NULL;
111 return 0;
112 }
113 }
114
115 errno = ENOENT;
116 return -1;
117 }
118
119 /**
120 * \internal Zwraca strukturę połączenia o danym identyfikatorze.
121 *
122 * \param sess Struktura sesji
123 * \param id Identyfikator połączenia
124 * \param uin Numer nadawcy lub odbiorcy
125 *
126 * \return Struktura połączenia lub \c NULL jeśli nie znaleziono
127 */
128 static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
129 {
130 struct gg_dcc7 *tmp;
131 int empty;
132
133 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
134
135 empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
136
137 for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
138 if (empty) {
139 if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
140 return tmp;
141 } else {
142 if (!memcmp(&tmp->cid, &id, sizeof(id)))
143 return tmp;
144 }
145 }
146
147 return NULL;
148 }
149
150 /**
151 * \internal Nawiązuje połączenie bezpośrednie
152 *
153 * \param sess Struktura sesji
154 * \param dcc Struktura połączenia
155 *
156 * \return 0 jeśli się powiodło, -1 w przypadku błędu
157 */
158 static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc)
159 {
160 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p, %p)\n", sess, dcc);
161
162 if (!sess || !dcc) {
163 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
164 errno = EINVAL;
165 return -1;
166 }
167
168 if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
169 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
170 return -1;
171 }
172
173 dcc->state = GG_STATE_CONNECTING;
174 dcc->check = GG_CHECK_WRITE;
175 dcc->timeout = GG_DCC7_TIMEOUT_CONNECT;
176 dcc->soft_timeout = 1;
177
178 return 0;
179 }
180
181 /**
182 * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego
183 *
184 * \param dcc Struktura połączenia
185 * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
186 *
187 * \return 0 jeśli się powiodło, -1 w przypadku błędu
188 */
189 static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port)
190 {
191 struct sockaddr_in sin;
192 int fd;
193
194 gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
195
196 if (!dcc) {
197 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
198 errno = EINVAL;
199 return -1;
200 }
201
202 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
203 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
204 return -1;
205 }
206
207 // XXX losować porty?
208
209 if (!port)
210 port = GG_DEFAULT_DCC_PORT;
211
212 while (1) {
213 sin.sin_family = AF_INET;
214 sin.sin_addr.s_addr = INADDR_ANY;
215 sin.sin_port = htons(port);
216
217 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port);
218
219 if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin)))
220 break;
221
222 if (port++ == 65535) {
223 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n");
224 close(fd);
225 errno = ENOENT;
226 return -1;
227 }
228 }
229
230 if (listen(fd, 1)) {
231 int errsv = errno;
232 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
233 close(fd);
234 errno = errsv;
235 return -1;
236 }
237
238 dcc->fd = fd;
239 dcc->local_port = port;
240
241 dcc->state = GG_STATE_LISTENING;
242 dcc->check = GG_CHECK_READ;
243 dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
244
245 return 0;
246 }
247
248 /**
249 * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry
250 *
251 * \param dcc Struktura połączenia
252 *
253 * \return 0 jeśli się powiodło, -1 w przypadku błędu
254 */
255 static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
256 {
257 struct gg_dcc7_info pkt;
258
259 gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
260
261 // XXX dać możliwość konfiguracji?
262
263 dcc->local_addr = dcc->sess->client_addr;
264
265 if (gg_dcc7_listen(dcc, 0) == -1)
266 return -1;
267
268 memset(&pkt, 0, sizeof(pkt));
269 pkt.uin = gg_fix32(dcc->peer_uin);
270 pkt.type = GG_DCC7_TYPE_P2P;
271 pkt.id = dcc->cid;
272 snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), dcc->local_port);
273
274 return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
275 }
276
277 /**
278 * \internal Odwraca połączenie po nieudanym connect()
279 *
280 * \param dcc Struktura połączenia
281 *
282 * \return 0 jeśli się powiodło, -1 w przypadku błędu
283 */
284 static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
285 {
286 gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
287
288 if (dcc->reverse) {
289 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
290 return -1;
291 }
292
293 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
294 close(dcc->fd);
295 dcc->fd = -1;
296 dcc->reverse = 1;
297
298 return gg_dcc7_listen_and_send_info(dcc);
299 }
300
301 /**
302 * \internal Wysyła do serwera żądanie nadania identyfikatora sesji
303 *
304 * \param sess Struktura sesji
305 * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE)
306 *
307 * \return 0 jeśli się powiodło, -1 w przypadku błędu
308 */
309 static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
310 {
311 struct gg_dcc7_id_request pkt;
312
313 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type);
314
315 if (!sess) {
316 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n");
317 errno = EFAULT;
318 return -1;
319 }
320
321 if (sess->state != GG_STATE_CONNECTED) {
322 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n");
323 errno = ENOTCONN;
324 return -1;
325 }
326
327 if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
328 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type);
329 errno = EINVAL;
330 return -1;
331 }
332
333 memset(&pkt, 0, sizeof(pkt));
334 pkt.type = gg_fix32(type);
335
336 return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL);
337 }
338
339 /**
340 * \internal Rozpoczyna wysyłanie pliku.
341 *
342 * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz
343 * \c gg_dcc_send_file_fd().
344 *
345 * \param sess Struktura sesji
346 * \param rcpt Numer odbiorcy
347 * \param fd Deskryptor pliku
348 * \param size Rozmiar pliku
349 * \param filename1250 Nazwa pliku w kodowaniu CP-1250
350 * \param hash Skrót SHA-1 pliku
351 * \param seek Flaga mówiąca, czy można używać lseek()
352 *
353 * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
354 *
355 * \ingroup dcc7
356 */
357 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)
358 {
359 struct gg_dcc7 *dcc = NULL;
360
361 if (!sess || !rcpt || !filename1250 || !hash || fd == -1) {
362 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n");
363 errno = EINVAL;
364 goto fail;
365 }
366
367 if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
368 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n");
369 goto fail;
370 }
371
372 if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
373 goto fail;
374
375 memset(dcc, 0, sizeof(struct gg_dcc7));
376 dcc->type = GG_SESSION_DCC7_SEND;
377 dcc->dcc_type = GG_DCC7_TYPE_FILE;
378 dcc->state = GG_STATE_REQUESTING_ID;
379 dcc->timeout = GG_DEFAULT_TIMEOUT;
380 dcc->sess = sess;
381 dcc->fd = -1;
382 dcc->uin = sess->uin;
383 dcc->peer_uin = rcpt;
384 dcc->file_fd = fd;
385 dcc->size = size;
386 dcc->seek = seek;
387
388 strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1);
389 dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
390
391 memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
392
393 if (gg_dcc7_session_add(sess, dcc) == -1)
394 goto fail;
395
396 return dcc;
397
398 fail:
399 free(dcc);
400 return NULL;
401 }
402
403 /**
404 * Rozpoczyna wysyłanie pliku o danej nazwie.
405 *
406 * \param sess Struktura sesji
407 * \param rcpt Numer odbiorcy
408 * \param filename Nazwa pliku w lokalnym systemie plików
409 * \param filename1250 Nazwa pliku w kodowaniu CP-1250
410 * \param hash Skrót SHA-1 pliku (lub \c NULL jeśli ma być wyznaczony)
411 *
412 * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
413 *
414 * \ingroup dcc7
415 */
416 struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash)
417 {
418 struct gg_dcc7 *dcc = NULL;
419 const char *tmp;
420 char hash_buf[GG_DCC7_HASH_LEN];
421 struct stat st;
422 int fd = -1;
423
424 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p)\n", sess, rcpt, filename, hash);
425
426 if (!sess || !rcpt || !filename) {
427 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n");
428 errno = EINVAL;
429 goto fail;
430 }
431
432 if (!filename1250)
433 filename1250 = filename;
434
435 if (stat(filename, &st) == -1) {
436 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno));
437 goto fail;
438 }
439
440 if ((st.st_mode & S_IFDIR)) {
441 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
442 errno = EINVAL;
443 goto fail;
444 }
445
446 if ((fd = open(filename, O_RDONLY)) == -1) {
447 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
448 goto fail;
449 }
450
451 if (!hash) {
452 if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
453 goto fail;
454
455 hash = hash_buf;
456 }
457
458 if ((tmp = strrchr(filename1250, '/')))
459 filename1250 = tmp + 1;
460
461 if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1)))
462 goto fail;
463
464 return dcc;
465
466 fail:
467 if (fd != -1) {
468 int errsv = errno;
469 close(fd);
470 errno = errsv;
471 }
472
473 free(dcc);
474 return NULL;
475 }
476
477 /**
478 * \internal Rozpoczyna wysyłanie pliku o danym deskryptorze.
479 *
480 * \note Wysyłanie pliku nie będzie działać poprawnie, jeśli deskryptor
481 * źródłowy jest w trybie nieblokującym i w pewnym momencie zabraknie danych.
482 *
483 * \param sess Struktura sesji
484 * \param rcpt Numer odbiorcy
485 * \param fd Deskryptor pliku
486 * \param size Rozmiar pliku
487 * \param filename1250 Nazwa pliku w kodowaniu CP-1250
488 * \param hash Skrót SHA-1 pliku
489 *
490 * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
491 *
492 * \ingroup dcc7
493 */
494 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)
495 {
496 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, %d, %d, %u, \"%s\", %p)\n", sess, rcpt, fd, size, filename1250, hash);
497
498 return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0);
499 }
500
501
502 /**
503 * Potwierdza chęć odebrania pliku.
504 *
505 * \param dcc Struktura połączenia
506 * \param offset Początkowy offset przy wznawianiu przesyłania pliku
507 *
508 * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset
509 * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub
510 * podobną.
511 *
512 * \return 0 jeśli się powiodło, -1 w przypadku błędu
513 *
514 * \ingroup dcc7
515 */
516 int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
517 {
518 struct gg_dcc7_accept pkt;
519
520 gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
521
522 if (!dcc || !dcc->sess) {
523 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
524 errno = EFAULT;
525 return -1;
526 }
527
528 memset(&pkt, 0, sizeof(pkt));
529 pkt.uin = gg_fix32(dcc->peer_uin);
530 pkt.id = dcc->cid;
531 pkt.offset = gg_fix32(offset);
532
533 if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1)
534 return -1;
535
536 dcc->offset = offset;
537
538 return gg_dcc7_listen_and_send_info(dcc);
539 }
540
541 /**
542 * Odrzuca próbę przesłania pliku.
543 *
544 * \param dcc Struktura połączenia
545 * \param reason Powód odrzucenia
546 *
547 * \return 0 jeśli się powiodło, -1 w przypadku błędu
548 *
549 * \ingroup dcc7
550 */
551 int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
552 {
553 struct gg_dcc7_reject pkt;
554
555 gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
556
557 if (!dcc || !dcc->sess) {
558 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
559 errno = EFAULT;
560 return -1;
561 }
562
563 memset(&pkt, 0, sizeof(pkt));
564 pkt.uin = gg_fix32(dcc->peer_uin);
565 pkt.id = dcc->cid;
566 pkt.reason = gg_fix32(reason);
567
568 return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL);
569 }
570
571 /**
572 * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego.
573 *
574 * \param sess Struktura sesji
575 * \param e Struktura zdarzenia
576 * \param payload Treść pakietu
577 * \param len Długość pakietu
578 *
579 * \return 0 jeśli się powiodło, -1 w przypadku błędu
580 */
581 int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len)
582 {
583 struct gg_dcc7_id_reply *p = payload;
584 struct gg_dcc7 *tmp;
585
586 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len);
587
588 for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
589 gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
590
591 if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != gg_fix32(p->type))
592 continue;
593
594 tmp->cid = p->id;
595
596 switch (tmp->dcc_type) {
597 case GG_DCC7_TYPE_FILE:
598 {
599 struct gg_dcc7_new s;
600
601 memset(&s, 0, sizeof(s));
602 s.id = tmp->cid;
603 s.type = gg_fix32(GG_DCC7_TYPE_FILE);
604 s.uin_from = gg_fix32(tmp->uin);
605 s.uin_to = gg_fix32(tmp->peer_uin);
606 s.size = gg_fix32(tmp->size);
607
608 strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN);
609
610 tmp->state = GG_STATE_WAITING_FOR_ACCEPT;
611 tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
612
613 return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL);
614 }
615 }
616 }
617
618 return 0;
619 }
620
621 /**
622 * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego.
623 *
624 * \param sess Struktura sesji
625 * \param e Struktura zdarzenia
626 * \param payload Treść pakietu
627 * \param len Długość pakietu
628 *
629 * \return 0 jeśli się powiodło, -1 w przypadku błędu
630 */
631 int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len)
632 {
633 struct gg_dcc7_accept *p = payload;
634 struct gg_dcc7 *dcc;
635
636 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len);
637
638 if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
639 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n");
640 // XXX wysłać reject?
641 e->type = GG_EVENT_DCC7_ERROR;
642 e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
643 return 0;
644 }
645
646 if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
647 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n");
648 e->type = GG_EVENT_DCC7_ERROR;
649 e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
650 return 0;
651 }
652
653 // XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT?
654
655 dcc->offset = gg_fix32(p->offset);
656 dcc->state = GG_STATE_WAITING_FOR_INFO;
657
658 return 0;
659 }
660
661 /**
662 * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim.
663 *
664 * \param sess Struktura sesji
665 * \param e Struktura zdarzenia
666 * \param payload Treść pakietu
667 * \param len Długość pakietu
668 *
669 * \return 0 jeśli się powiodło, -1 w przypadku błędu
670 */
671 int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len)
672 {
673 struct gg_dcc7_info *p = payload;
674 struct gg_dcc7 *dcc;
675 char *tmp;
676
677 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
678
679 if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
680 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
681 return 0;
682 }
683
684 if (p->type != GG_DCC7_TYPE_P2P) {
685 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
686 e->type = GG_EVENT_DCC7_ERROR;
687 e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
688 return 0;
689 }
690
691 if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
692 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
693 e->type = GG_EVENT_DCC7_ERROR;
694 e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
695 return 0;
696 }
697
698 if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
699 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n");
700 e->type = GG_EVENT_DCC7_ERROR;
701 e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
702 return 0;
703 }
704
705 // jeśli nadal czekamy na połączenie przychodzące, a druga strona nie
706 // daje rady i oferuje namiary na siebie, bierzemy co dają.
707
708 if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
709 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
710 e->type = GG_EVENT_DCC7_ERROR;
711 e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
712 return 0;
713 }
714
715 if (dcc->state == GG_STATE_LISTENING) {
716 close(dcc->fd);
717 dcc->fd = -1;
718 dcc->reverse = 1;
719 }
720
721 if (dcc->type == GG_SESSION_DCC7_SEND) {
722 e->type = GG_EVENT_DCC7_ACCEPT;
723 e->event.dcc7_accept.dcc7 = dcc;
724 e->event.dcc7_accept.type = gg_fix32(p->type);
725 e->event.dcc7_accept.remote_ip = dcc->remote_addr;
726 e->event.dcc7_accept.remote_port = dcc->remote_port;
727 } else {
728 e->type = GG_EVENT_DCC7_PENDING;
729 }
730
731 if (gg_dcc7_connect(sess, dcc) == -1) {
732 if (gg_dcc7_reverse_connect(dcc) == -1) {
733 e->type = GG_EVENT_DCC7_ERROR;
734 e->event.dcc7_error = GG_ERROR_DCC7_NET;
735 return 0;
736 }
737 }
738
739 return 0;
740 }
741
742 /**
743 * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego.
744 *
745 * \param sess Struktura sesji
746 * \param e Struktura zdarzenia
747 * \param payload Treść pakietu
748 * \param len Długość pakietu
749 *
750 * \return 0 jeśli się powiodło, -1 w przypadku błędu
751 */
752 int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len)
753 {
754 struct gg_dcc7_reject *p = payload;
755 struct gg_dcc7 *dcc;
756
757 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len);
758
759 if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
760 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n");
761 return 0;
762 }
763
764 if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
765 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n");
766 e->type = GG_EVENT_DCC7_ERROR;
767 e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
768 return 0;
769 }
770
771 e->type = GG_EVENT_DCC7_REJECT;
772 e->event.dcc7_reject.dcc7 = dcc;
773 e->event.dcc7_reject.reason = gg_fix32(p->reason);
774
775 // XXX ustawić state na rejected?
776
777 return 0;
778 }
779
780 /**
781 * \internal Obsługuje pakiet nowego połączenia bezpośredniego.
782 *
783 * \param sess Struktura sesji
784 * \param e Struktura zdarzenia
785 * \param payload Treść pakietu
786 * \param len Długość pakietu
787 *
788 * \return 0 jeśli się powiodło, -1 w przypadku błędu
789 */
790 int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len)
791 {
792 struct gg_dcc7_new *p = payload;
793 struct gg_dcc7 *dcc;
794
795 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len);
796
797 switch (gg_fix32(p->type)) {
798 case GG_DCC7_TYPE_FILE:
799 if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
800 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n");
801 return -1;
802 }
803
804 memset(dcc, 0, sizeof(struct gg_dcc7));
805 dcc->type = GG_SESSION_DCC7_GET;
806 dcc->dcc_type = GG_DCC7_TYPE_FILE;
807 dcc->fd = -1;
808 dcc->file_fd = -1;
809 dcc->uin = sess->uin;
810 dcc->peer_uin = gg_fix32(p->uin_from);
811 dcc->cid = p->id;
812 dcc->sess = sess;
813
814 if (gg_dcc7_session_add(sess, dcc) == -1) {
815 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
816 gg_dcc7_free(dcc);
817 return -1;
818 }
819
820 dcc->size = gg_fix32(p->size);
821 strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1);
822 dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
823 memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN);
824
825 e->type = GG_EVENT_DCC7_NEW;
826 e->event.dcc7_new = dcc;
827
828 break;
829
830 case GG_DCC7_TYPE_VOICE:
831 if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
832 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n");
833 return -1;
834 }
835
836 memset(dcc, 0, sizeof(struct gg_dcc7));
837
838 dcc->type = GG_SESSION_DCC7_VOICE;
839 dcc->dcc_type = GG_DCC7_TYPE_VOICE;
840 dcc->fd = -1;
841 dcc->file_fd = -1;
842 dcc->uin = sess->uin;
843 dcc->peer_uin = gg_fix32(p->uin_from);
844 dcc->cid = p->id;
845 dcc->sess = sess;
846
847 if (gg_dcc7_session_add(sess, dcc) == -1) {
848 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
849 gg_dcc7_free(dcc);
850 return -1;
851 }
852
853 e->type = GG_EVENT_DCC7_NEW;
854 e->event.dcc7_new = dcc;
855
856 break;
857
858 default:
859 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from));
860
861 break;
862 }
863
864 return 0;
865 }
866
867 /**
868 * \internal Ustawia odpowiednie stany wewnętrzne w zależności od rodzaju
869 * połączenia.
870 *
871 * \param dcc Struktura połączenia
872 *
873 * \return 0 jeśli się powiodło, -1 w przypadku błędu.
874 */
875 static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
876 {
877 gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
878
879 if (!dcc) {
880 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
881 errno = EINVAL;
882 return -1;
883 }
884
885 switch (dcc->type) {
886 case GG_SESSION_DCC7_GET:
887 dcc->state = GG_STATE_GETTING_FILE;
888 dcc->check = GG_CHECK_READ;
889 return 0;
890
891 case GG_SESSION_DCC7_SEND:
892 dcc->state = GG_STATE_SENDING_FILE;
893 dcc->check = GG_CHECK_WRITE;
894 return 0;
895
896 case GG_SESSION_DCC7_VOICE:
897 dcc->state = GG_STATE_READING_VOICE_DATA;
898 dcc->check = GG_CHECK_READ;
899 return 0;
900 }
901
902 errno = EINVAL;
903
904 return -1;
905 }
906
907 /**
908 * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
909 *
910 * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
911 * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
912 * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
913 *
914 * \param dcc Struktura połączenia
915 *
916 * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
917 *
918 * \ingroup dcc7
919 */
920 struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
921 {
922 struct gg_event *e;
923
924 gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
925
926 if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) {
927 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
928 errno = EINVAL;
929 return NULL;
930 }
931
932 if (!(e = malloc(sizeof(struct gg_event)))) {
933 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
934 return NULL;
935 }
936
937 memset(e, 0, sizeof(struct gg_event));
938 e->type = GG_EVENT_NONE;
939
940 switch (dcc->state) {
941 case GG_STATE_LISTENING:
942 {
943 struct sockaddr_in sin;
944 int fd, one = 1;
945 unsigned int sin_len = sizeof(sin);
946
947 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
948
949 if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
950 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
951 return e;
952 }
953
954 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));
955
956 #ifdef FIONBIO
957 if (ioctl(fd, FIONBIO, &one) == -1) {
958 #else
959 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
960 #endif
961 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
962 close(fd);
963 e->type = GG_EVENT_DCC7_ERROR;
964 e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
965 return e;
966 }
967
968 close(dcc->fd);
969 dcc->fd = fd;
970
971 dcc->state = GG_STATE_READING_ID;
972 dcc->check = GG_CHECK_READ;
973 dcc->timeout = GG_DEFAULT_TIMEOUT;
974 dcc->incoming = 1;
975
976 dcc->remote_port = ntohs(sin.sin_port);
977 dcc->remote_addr = sin.sin_addr.s_addr;
978
979 e->type = GG_EVENT_DCC7_CONNECTED;
980 e->event.dcc7_connected.dcc7 = dcc;
981
982 return e;
983 }
984
985 case GG_STATE_CONNECTING:
986 {
987 int res = 0, error = 0;
988 unsigned int error_size = sizeof(error);
989
990 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
991
992 dcc->soft_timeout = 0;
993
994 if (dcc->timeout == 0)
995 error = ETIMEDOUT;
996
997 if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
998 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
999
1000 if (gg_dcc7_reverse_connect(dcc) != -1) {
1001 e->type = GG_EVENT_DCC7_PENDING;
1002 } else {
1003 e->type = GG_EVENT_DCC7_ERROR;
1004 e->event.dcc_error = GG_ERROR_DCC7_NET;
1005 }
1006
1007 return e;
1008 }
1009
1010 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
1011
1012 dcc->state = GG_STATE_SENDING_ID;
1013 dcc->check = GG_CHECK_WRITE;
1014 dcc->timeout = GG_DEFAULT_TIMEOUT;
1015 dcc->incoming = 0;
1016
1017 return e;
1018 }
1019
1020 case GG_STATE_READING_ID:
1021 {
1022 gg_dcc7_id_t id;
1023 int res;
1024
1025 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
1026
1027 if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) {
1028 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
1029 e->type = GG_EVENT_DCC7_ERROR;
1030 e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
1031 return e;
1032 }
1033
1034 if (memcmp(&id, &dcc->cid, sizeof(id))) {
1035 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
1036 e->type = GG_EVENT_DCC7_ERROR;
1037 e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
1038 return e;
1039 }
1040
1041 if (dcc->incoming) {
1042 dcc->state = GG_STATE_SENDING_ID;
1043 dcc->check = GG_CHECK_WRITE;
1044 dcc->timeout = GG_DEFAULT_TIMEOUT;
1045 } else {
1046 gg_dcc7_postauth_fixup(dcc);
1047 dcc->timeout = GG_DEFAULT_TIMEOUT;
1048 }
1049
1050 return e;
1051 }
1052
1053 case GG_STATE_SENDING_ID:
1054 {
1055 int res;
1056
1057 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
1058
1059 if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) {
1060 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno));
1061 e->type = GG_EVENT_DCC7_ERROR;
1062 e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
1063 return e;
1064 }
1065
1066 if (dcc->incoming) {
1067 gg_dcc7_postauth_fixup(dcc);
1068 dcc->timeout = GG_DEFAULT_TIMEOUT;
1069 } else {
1070 dcc->state = GG_STATE_READING_ID;
1071 dcc->check = GG_CHECK_READ;
1072 dcc->timeout = GG_DEFAULT_TIMEOUT;
1073 }
1074
1075 return e;
1076 }
1077
1078 case GG_STATE_SENDING_FILE:
1079 {
1080 char buf[1024];
1081 int chunk, res;
1082
1083 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
1084
1085 if (dcc->offset >= dcc->size) {
1086 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
1087 e->type = GG_EVENT_DCC7_DONE;
1088 return e;
1089 }
1090
1091 if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
1092 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
1093 e->type = GG_EVENT_DCC7_ERROR;
1094 e->event.dcc_error = GG_ERROR_DCC7_FILE;
1095 return e;
1096 }
1097
1098 if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
1099 chunk = sizeof(buf);
1100
1101 if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
1102 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
1103 e->type = GG_EVENT_DCC7_ERROR;
1104 e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
1105 return e;
1106 }
1107
1108 if ((res = write(dcc->fd, buf, res)) == -1) {
1109 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
1110 e->type = GG_EVENT_DCC7_ERROR;
1111 e->event.dcc_error = GG_ERROR_DCC7_NET;
1112 return e;
1113 }
1114
1115 dcc->offset += res;
1116
1117 if (dcc->offset >= dcc->size) {
1118 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
1119 e->type = GG_EVENT_DCC7_DONE;
1120 return e;
1121 }
1122
1123 dcc->state = GG_STATE_SENDING_FILE;
1124 dcc->check = GG_CHECK_WRITE;
1125 dcc->timeout = GG_DCC7_TIMEOUT_SEND;
1126
1127 return e;
1128 }
1129
1130 case GG_STATE_GETTING_FILE:
1131 {
1132 char buf[1024];
1133 int res, wres;
1134
1135 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
1136
1137 if (dcc->offset >= dcc->size) {
1138 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
1139 e->type = GG_EVENT_DCC7_DONE;
1140 return e;
1141 }
1142
1143 if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) {
1144 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));
1145 e->type = GG_EVENT_DCC7_ERROR;
1146 e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
1147 return e;
1148 }
1149
1150 // XXX zapisywać do skutku?
1151
1152 if ((wres = write(dcc->file_fd, buf, res)) < res) {
1153 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));
1154 e->type = GG_EVENT_DCC7_ERROR;
1155 e->event.dcc_error = GG_ERROR_DCC7_FILE;
1156 return e;
1157 }
1158
1159 dcc->offset += res;
1160
1161 if (dcc->offset >= dcc->size) {
1162 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
1163 e->type = GG_EVENT_DCC7_DONE;
1164 return e;
1165 }
1166
1167 dcc->state = GG_STATE_GETTING_FILE;
1168 dcc->check = GG_CHECK_READ;
1169 dcc->timeout = GG_DCC7_TIMEOUT_GET;
1170
1171 return e;
1172 }
1173
1174 default:
1175 {
1176 gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
1177 e->type = GG_EVENT_DCC7_ERROR;
1178 e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
1179
1180 return e;
1181 }
1182 }
1183
1184 return e;
1185 }
1186
1187 /**
1188 * Zwalnia zasoby używane przez połączenie bezpośrednie.
1189 *
1190 * \param dcc Struktura połączenia
1191 *
1192 * \ingroup dcc7
1193 */
1194 void gg_dcc7_free(struct gg_dcc7 *dcc)
1195 {
1196 gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
1197
1198 if (!dcc)
1199 return;
1200
1201 if (dcc->fd != -1)
1202 close(dcc->fd);
1203
1204 if (dcc->file_fd != -1)
1205 close(dcc->file_fd);
1206
1207 if (dcc->sess)
1208 gg_dcc7_session_remove(dcc->sess, dcc);
1209
1210 free(dcc);
1211 }
1212