Mercurial > pidgin
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 |