Mercurial > pidgin
annotate src/protocols/gg/lib/dcc.c @ 13967:99b9b58b19dd
[gaim-migrate @ 16523]
Fix a crazy MSN crash. Basically it's possible to have more than one
slplink associated with a given switchboard, but our code did not
allow for that. I think it happens when you're in a multi-user
chat and you do stuff with multiple users that involves slplinks.
Like maybe file transfer and buddy icon related stuff.
Tracking this down took an ungodly amount of time, but thanks to
Meebo for letting me do it :-)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 20 Jul 2006 07:31:15 +0000 |
parents | fc464a0abccc |
children |
rev | line source |
---|---|
12323
fc464a0abccc
[gaim-migrate @ 14627]
Richard Laager <rlaager@wiktel.com>
parents:
12218
diff
changeset
|
1 /* $Id: dcc.c 14627 2005-12-04 18:19:56Z rlaager $ */ |
11360 | 2 |
3 /* | |
4 * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> | |
5 * Tomasz Chiliński <chilek@chilan.com> | |
6 * | |
7 * This program is free software; you can redistribute it and/or modify | |
8 * it under the terms of the GNU Lesser General Public License Version | |
9 * 2.1 as published by the Free Software Foundation. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU Lesser General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU Lesser General Public | |
17 * License along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, | |
19 * USA. | |
20 */ | |
21 | |
22 #include <sys/types.h> | |
23 #include <sys/stat.h> | |
11546
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
24 #ifndef _WIN32 |
11360 | 25 #include <sys/ioctl.h> |
26 #include <sys/socket.h> | |
27 #include <netinet/in.h> | |
28 #include <arpa/inet.h> | |
29 #ifdef sun | |
30 # include <sys/filio.h> | |
31 #endif | |
11546
3c536224f0d0
[gaim-migrate @ 13801]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
11360
diff
changeset
|
32 #endif |
11360 | 33 |
34 #include <ctype.h> | |
35 #include <errno.h> | |
36 #include <fcntl.h> | |
37 #include <stdarg.h> | |
38 #include <string.h> | |
39 #include <stdio.h> | |
40 #include <stdlib.h> | |
41 #include <unistd.h> | |
42 | |
43 #include "compat.h" | |
44 #include "libgadu.h" | |
45 | |
46 #ifndef GG_DEBUG_DISABLE | |
47 /* | |
48 * gg_dcc_debug_data() // funkcja wewnętrzna | |
49 * | |
50 * wyświetla zrzut pakietu w hexie. | |
51 * | |
52 * - prefix - prefiks zrzutu pakietu | |
53 * - fd - deskryptor gniazda | |
54 * - buf - bufor z danymi | |
55 * - size - rozmiar danych | |
56 */ | |
57 static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size) | |
58 { | |
59 unsigned int i; | |
60 | |
61 gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size); | |
62 | |
63 for (i = 0; i < size; i++) | |
64 gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]); | |
65 | |
66 gg_debug(GG_DEBUG_MISC, "\n"); | |
67 } | |
68 #else | |
69 #define gg_dcc_debug_data(a,b,c,d) do { } while (0) | |
70 #endif | |
71 | |
72 /* | |
73 * gg_dcc_request() | |
74 * | |
75 * wysyła informację o tym, że dany klient powinien się z nami połączyć. | |
76 * wykorzystywane, kiedy druga strona, której chcemy coś wysłać jest za | |
77 * maskaradą. | |
78 * | |
79 * - sess - struktura opisująca sesję GG | |
80 * - uin - numerek odbiorcy | |
81 * | |
82 * patrz gg_send_message_ctcp(). | |
83 */ | |
84 int gg_dcc_request(struct gg_session *sess, uin_t uin) | |
85 { | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
86 return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char *)"\002", 1); |
11360 | 87 } |
88 | |
89 /* | |
90 * gg_dcc_fill_filetime() // funkcja wewnętrzna | |
91 * | |
92 * zamienia czas w postaci unixowej na windowsowy. | |
93 * | |
94 * - unix - czas w postaci unixowej | |
95 * - filetime - czas w postaci windowsowej | |
96 */ | |
12323
fc464a0abccc
[gaim-migrate @ 14627]
Richard Laager <rlaager@wiktel.com>
parents:
12218
diff
changeset
|
97 static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft) |
11360 | 98 { |
99 #ifdef __GG_LIBGADU_HAVE_LONG_LONG | |
100 unsigned long long tmp; | |
101 | |
102 tmp = ut; | |
103 tmp += 11644473600LL; | |
104 tmp *= 10000000LL; | |
105 | |
106 #ifndef __GG_LIBGADU_BIGENDIAN | |
107 ft[0] = (uint32_t) tmp; | |
108 ft[1] = (uint32_t) (tmp >> 32); | |
109 #else | |
110 ft[0] = gg_fix32((uint32_t) (tmp >> 32)); | |
111 ft[1] = gg_fix32((uint32_t) tmp); | |
112 #endif | |
113 | |
114 #endif | |
115 } | |
116 | |
117 /* | |
118 * gg_dcc_fill_file_info() | |
119 * | |
120 * wypełnia pola struct gg_dcc niezbędne do wysłania pliku. | |
121 * | |
122 * - d - struktura opisująca połączenie DCC | |
123 * - filename - nazwa pliku | |
124 * | |
125 * 0, -1. | |
126 */ | |
127 int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename) | |
128 { | |
129 return gg_dcc_fill_file_info2(d, filename, filename); | |
130 } | |
131 | |
132 /* | |
133 * gg_dcc_fill_file_info2() | |
134 * | |
135 * wypełnia pola struct gg_dcc niezbędne do wysłania pliku. | |
136 * | |
137 * - d - struktura opisująca połączenie DCC | |
138 * - filename - nazwa pliku | |
139 * - local_filename - nazwa na lokalnym systemie plików | |
140 * | |
141 * 0, -1. | |
142 */ | |
143 int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename) | |
144 { | |
145 struct stat st; | |
146 const char *name, *ext, *p; | |
147 unsigned char *q; | |
148 int i, j; | |
149 | |
150 gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename); | |
151 | |
152 if (!d || d->type != GG_SESSION_DCC_SEND) { | |
153 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n"); | |
154 errno = EINVAL; | |
155 return -1; | |
156 } | |
157 | |
158 if (stat(local_filename, &st) == -1) { | |
159 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno)); | |
160 return -1; | |
161 } | |
162 | |
163 if ((st.st_mode & S_IFDIR)) { | |
164 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n"); | |
165 errno = EINVAL; | |
166 return -1; | |
167 } | |
168 | |
169 if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) { | |
170 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno)); | |
171 return -1; | |
172 } | |
173 | |
174 memset(&d->file_info, 0, sizeof(d->file_info)); | |
175 | |
176 if (!(st.st_mode & S_IWUSR)) | |
177 d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY); | |
178 | |
179 gg_dcc_fill_filetime(st.st_atime, d->file_info.atime); | |
180 gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime); | |
181 gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime); | |
182 | |
183 d->file_info.size = gg_fix32(st.st_size); | |
184 d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */ | |
185 | |
186 if (!(name = strrchr(filename, '/'))) | |
187 name = filename; | |
188 else | |
189 name++; | |
190 | |
191 if (!(ext = strrchr(name, '.'))) | |
192 ext = name + strlen(name); | |
193 | |
194 for (i = 0, p = name; i < 8 && p < ext; i++, p++) | |
195 d->file_info.short_filename[i] = toupper(name[i]); | |
196 | |
197 if (i == 8 && p < ext) { | |
198 d->file_info.short_filename[6] = '~'; | |
199 d->file_info.short_filename[7] = '1'; | |
200 } | |
201 | |
202 if (strlen(ext) > 0) { | |
203 for (j = 0; *ext && j < 4; j++, p++) | |
204 d->file_info.short_filename[i + j] = toupper(ext[j]); | |
205 } | |
206 | |
207 for (q = d->file_info.short_filename; *q; q++) { | |
208 if (*q == 185) { | |
209 *q = 165; | |
210 } else if (*q == 230) { | |
211 *q = 198; | |
212 } else if (*q == 234) { | |
213 *q = 202; | |
214 } else if (*q == 179) { | |
215 *q = 163; | |
216 } else if (*q == 241) { | |
217 *q = 209; | |
218 } else if (*q == 243) { | |
219 *q = 211; | |
220 } else if (*q == 156) { | |
221 *q = 140; | |
222 } else if (*q == 159) { | |
223 *q = 143; | |
224 } else if (*q == 191) { | |
225 *q = 175; | |
226 } | |
227 } | |
228 | |
229 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename); | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
230 strncpy((char *)d->file_info.filename, name, sizeof(d->file_info.filename) - 1); |
11360 | 231 |
232 return 0; | |
233 } | |
234 | |
235 /* | |
236 * gg_dcc_transfer() // funkcja wewnętrzna | |
237 * | |
238 * inicjuje proces wymiany pliku z danym klientem. | |
239 * | |
240 * - ip - adres ip odbiorcy | |
241 * - port - port odbiorcy | |
242 * - my_uin - własny numer | |
243 * - peer_uin - numer obiorcy | |
244 * - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET) | |
245 * | |
246 * zaalokowana struct gg_dcc lub NULL jeśli wystąpił błąd. | |
247 */ | |
248 static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type) | |
249 { | |
250 struct gg_dcc *d = NULL; | |
251 struct in_addr addr; | |
252 | |
253 addr.s_addr = ip; | |
254 | |
255 gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET"); | |
256 | |
257 if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) { | |
258 gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n"); | |
259 errno = EINVAL; | |
260 return NULL; | |
261 } | |
262 | |
263 if (!(d = (void*) calloc(1, sizeof(*d)))) { | |
264 gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n"); | |
265 return NULL; | |
266 } | |
267 | |
268 d->check = GG_CHECK_WRITE; | |
269 d->state = GG_STATE_CONNECTING; | |
270 d->type = type; | |
271 d->timeout = GG_DEFAULT_TIMEOUT; | |
272 d->file_fd = -1; | |
273 d->active = 1; | |
274 d->fd = -1; | |
275 d->uin = my_uin; | |
276 d->peer_uin = peer_uin; | |
277 | |
278 if ((d->fd = gg_connect(&addr, port, 1)) == -1) { | |
279 gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n"); | |
280 free(d); | |
281 return NULL; | |
282 } | |
283 | |
284 return d; | |
285 } | |
286 | |
287 /* | |
288 * gg_dcc_get_file() | |
289 * | |
290 * inicjuje proces odbierania pliku od danego klienta, gdy ten wysłał do | |
291 * nas żądanie połączenia. | |
292 * | |
293 * - ip - adres ip odbiorcy | |
294 * - port - port odbiorcy | |
295 * - my_uin - własny numer | |
296 * - peer_uin - numer obiorcy | |
297 * | |
298 * zaalokowana struct gg_dcc lub NULL jeśli wystąpił błąd. | |
299 */ | |
300 struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) | |
301 { | |
302 gg_debug(GG_DEBUG_MISC, "// gg_dcc_get_file() handing over to gg_dcc_transfer()\n"); | |
303 | |
304 return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET); | |
305 } | |
306 | |
307 /* | |
308 * gg_dcc_send_file() | |
309 * | |
310 * inicjuje proces wysyłania pliku do danego klienta. | |
311 * | |
312 * - ip - adres ip odbiorcy | |
313 * - port - port odbiorcy | |
314 * - my_uin - własny numer | |
315 * - peer_uin - numer obiorcy | |
316 * | |
317 * zaalokowana struct gg_dcc lub NULL jeśli wystąpił błąd. | |
318 */ | |
319 struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) | |
320 { | |
321 gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n"); | |
322 | |
323 return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND); | |
324 } | |
325 | |
326 /* | |
327 * gg_dcc_voice_chat() | |
328 * | |
329 * próbuje nawiązać połączenie głosowe. | |
330 * | |
331 * - ip - adres ip odbiorcy | |
332 * - port - port odbiorcy | |
333 * - my_uin - własny numer | |
334 * - peer_uin - numer obiorcy | |
335 * | |
336 * zaalokowana struct gg_dcc lub NULL jeśli wystąpił błąd. | |
337 */ | |
338 struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) | |
339 { | |
340 gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n"); | |
341 | |
342 return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE); | |
343 } | |
344 | |
345 /* | |
346 * gg_dcc_set_type() | |
347 * | |
348 * po zdarzeniu GG_EVENT_DCC_CALLBACK należy ustawić typ połączenia za | |
349 * pomocą tej funkcji. | |
350 * | |
351 * - d - struktura opisująca połączenie | |
352 * - type - typ połączenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE) | |
353 */ | |
354 void gg_dcc_set_type(struct gg_dcc *d, int type) | |
355 { | |
356 d->type = type; | |
357 d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST; | |
358 } | |
359 | |
360 /* | |
361 * gg_dcc_callback() // funkcja wewnętrzna | |
362 * | |
363 * wywoływana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza | |
364 * rezultat w struct gg_dcc->event. | |
365 * | |
366 * - d - structura opisująca połączenie | |
367 * | |
368 * 0, -1. | |
369 */ | |
370 static int gg_dcc_callback(struct gg_dcc *d) | |
371 { | |
372 struct gg_event *e = gg_dcc_watch_fd(d); | |
373 | |
374 d->event = e; | |
375 | |
376 return (e != NULL) ? 0 : -1; | |
377 } | |
378 | |
379 /* | |
380 * gg_dcc_socket_create() | |
381 * | |
382 * tworzy gniazdo dla bezpośredniej komunikacji między klientami. | |
383 * | |
384 * - uin - własny numer | |
385 * - port - preferowany port, jeśli równy 0 lub -1, próbuje domyślnego | |
386 * | |
387 * zaalokowana struct gg_dcc, którą poźniej należy zwolnić funkcją | |
388 * gg_dcc_free(), albo NULL jeśli wystąpił błąd. | |
389 */ | |
390 struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port) | |
391 { | |
392 struct gg_dcc *c; | |
393 struct sockaddr_in sin; | |
394 int sock, bound = 0, errno2; | |
395 | |
396 gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port); | |
397 | |
398 if (!uin) { | |
399 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n"); | |
400 errno = EINVAL; | |
401 return NULL; | |
402 } | |
403 | |
404 if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { | |
405 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno)); | |
406 return NULL; | |
407 } | |
408 | |
409 if (!port) | |
410 port = GG_DEFAULT_DCC_PORT; | |
411 | |
412 while (!bound) { | |
413 sin.sin_family = AF_INET; | |
414 sin.sin_addr.s_addr = INADDR_ANY; | |
415 sin.sin_port = htons(port); | |
416 | |
417 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port); | |
418 if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin))) | |
419 bound = 1; | |
420 else { | |
421 if (++port == 65535) { | |
422 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n"); | |
423 close(sock); | |
424 return NULL; | |
425 } | |
426 } | |
427 } | |
428 | |
429 if (listen(sock, 10)) { | |
430 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno)); | |
431 errno2 = errno; | |
432 close(sock); | |
433 errno = errno2; | |
434 return NULL; | |
435 } | |
436 | |
437 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port); | |
438 | |
439 if (!(c = malloc(sizeof(*c)))) { | |
440 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n"); | |
441 close(sock); | |
442 return NULL; | |
443 } | |
444 memset(c, 0, sizeof(*c)); | |
445 | |
446 c->port = c->id = port; | |
447 c->fd = sock; | |
448 c->type = GG_SESSION_DCC_SOCKET; | |
449 c->uin = uin; | |
450 c->timeout = -1; | |
451 c->state = GG_STATE_LISTENING; | |
452 c->check = GG_CHECK_READ; | |
453 c->callback = gg_dcc_callback; | |
454 c->destroy = gg_dcc_free; | |
455 | |
456 return c; | |
457 } | |
458 | |
459 /* | |
460 * gg_dcc_voice_send() | |
461 * | |
462 * wysyła ramkę danych dla rozmowy głosowej. | |
463 * | |
464 * - d - struktura opisująca połączenie dcc | |
465 * - buf - bufor z danymi | |
466 * - length - rozmiar ramki | |
467 * | |
468 * 0, -1. | |
469 */ | |
470 int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length) | |
471 { | |
472 struct packet_s { | |
473 uint8_t type; | |
474 uint32_t length; | |
475 } GG_PACKED; | |
476 struct packet_s packet; | |
477 | |
478 gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length); | |
479 if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) { | |
480 gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n"); | |
481 errno = EINVAL; | |
482 return -1; | |
483 } | |
484 | |
485 packet.type = 0x03; /* XXX */ | |
486 packet.length = gg_fix32(length); | |
487 | |
488 if (write(d->fd, &packet, sizeof(packet)) < (signed)sizeof(packet)) { | |
489 gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n"); | |
490 return -1; | |
491 } | |
492 gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet)); | |
493 | |
494 if (write(d->fd, buf, length) < length) { | |
495 gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n"); | |
496 return -1; | |
497 } | |
498 gg_dcc_debug_data("write", d->fd, buf, length); | |
499 | |
500 return 0; | |
501 } | |
502 | |
503 #define gg_read(fd, buf, size) \ | |
504 { \ | |
505 int tmp = read(fd, buf, size); \ | |
506 \ | |
507 if (tmp < (int) size) { \ | |
508 if (tmp == -1) { \ | |
509 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); \ | |
510 } else if (tmp == 0) { \ | |
511 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); \ | |
512 } else { \ | |
513 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (%d bytes, %d needed)\n", tmp, size); \ | |
514 } \ | |
515 e->type = GG_EVENT_DCC_ERROR; \ | |
516 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \ | |
517 return e; \ | |
518 } \ | |
519 gg_dcc_debug_data("read", fd, buf, size); \ | |
520 } | |
521 | |
522 #define gg_write(fd, buf, size) \ | |
523 { \ | |
524 int tmp; \ | |
525 gg_dcc_debug_data("write", fd, buf, size); \ | |
526 tmp = write(fd, buf, size); \ | |
527 if (tmp < (int) size) { \ | |
528 if (tmp == -1) { \ | |
529 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); \ | |
530 } else { \ | |
531 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d needed, %d done)\n", size, tmp); \ | |
532 } \ | |
533 e->type = GG_EVENT_DCC_ERROR; \ | |
534 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \ | |
535 return e; \ | |
536 } \ | |
537 } | |
538 | |
539 /* | |
540 * gg_dcc_watch_fd() | |
541 * | |
542 * funkcja, którą należy wywołać, gdy coś się zmieni na gg_dcc->fd. | |
543 * | |
544 * - h - struktura zwrócona przez gg_create_dcc_socket() | |
545 * | |
546 * zaalokowana struct gg_event lub NULL, jeśli zabrakło pamięci na nią. | |
547 */ | |
548 struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h) | |
549 { | |
550 struct gg_event *e; | |
551 int foo; | |
552 | |
553 gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h); | |
554 | |
555 if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) { | |
556 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n"); | |
557 errno = EINVAL; | |
558 return NULL; | |
559 } | |
560 | |
561 if (!(e = (void*) calloc(1, sizeof(*e)))) { | |
562 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n"); | |
563 return NULL; | |
564 } | |
565 | |
566 e->type = GG_EVENT_NONE; | |
567 | |
568 if (h->type == GG_SESSION_DCC_SOCKET) { | |
569 struct sockaddr_in sin; | |
570 struct gg_dcc *c; | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
571 int fd; |
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
572 socklen_t sin_len = sizeof(sin); |
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
573 int one = 1; |
11360 | 574 |
575 if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { | |
576 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno)); | |
577 return e; | |
578 } | |
579 | |
580 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); | |
581 | |
582 #ifdef FIONBIO | |
583 if (ioctl(fd, FIONBIO, &one) == -1) { | |
584 #else | |
585 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { | |
586 #endif | |
587 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno)); | |
588 close(fd); | |
589 e->type = GG_EVENT_DCC_ERROR; | |
590 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; | |
591 return e; | |
592 } | |
593 | |
594 if (!(c = (void*) calloc(1, sizeof(*c)))) { | |
595 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n"); | |
596 | |
597 free(e); | |
598 close(fd); | |
599 return NULL; | |
600 } | |
601 | |
602 c->fd = fd; | |
603 c->check = GG_CHECK_READ; | |
604 c->state = GG_STATE_READING_UIN_1; | |
605 c->type = GG_SESSION_DCC; | |
606 c->timeout = GG_DEFAULT_TIMEOUT; | |
607 c->file_fd = -1; | |
608 c->remote_addr = sin.sin_addr.s_addr; | |
609 c->remote_port = ntohs(sin.sin_port); | |
610 | |
611 e->type = GG_EVENT_DCC_NEW; | |
612 e->event.dcc_new = c; | |
613 | |
614 return e; | |
615 } else { | |
616 struct gg_dcc_tiny_packet tiny; | |
617 struct gg_dcc_small_packet small; | |
618 struct gg_dcc_big_packet big; | |
12218
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
619 int size, tmp, res; |
9cbc5967fbfd
[gaim-migrate @ 14520]
Richard Laager <rlaager@wiktel.com>
parents:
11546
diff
changeset
|
620 socklen_t res_size = sizeof(res); |
11360 | 621 unsigned int utmp; |
622 char buf[1024], ack[] = "UDAG"; | |
623 | |
624 struct gg_dcc_file_info_packet { | |
625 struct gg_dcc_big_packet big; | |
626 struct gg_file_info file_info; | |
627 } GG_PACKED; | |
628 struct gg_dcc_file_info_packet file_info_packet; | |
629 | |
630 switch (h->state) { | |
631 case GG_STATE_READING_UIN_1: | |
632 case GG_STATE_READING_UIN_2: | |
633 { | |
634 uin_t uin; | |
635 | |
636 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2); | |
637 | |
638 gg_read(h->fd, &uin, sizeof(uin)); | |
639 | |
640 if (h->state == GG_STATE_READING_UIN_1) { | |
641 h->state = GG_STATE_READING_UIN_2; | |
642 h->check = GG_CHECK_READ; | |
643 h->timeout = GG_DEFAULT_TIMEOUT; | |
644 h->peer_uin = gg_fix32(uin); | |
645 } else { | |
646 h->state = GG_STATE_SENDING_ACK; | |
647 h->check = GG_CHECK_WRITE; | |
648 h->timeout = GG_DEFAULT_TIMEOUT; | |
649 h->uin = gg_fix32(uin); | |
650 e->type = GG_EVENT_DCC_CLIENT_ACCEPT; | |
651 } | |
652 | |
653 return e; | |
654 } | |
655 | |
656 case GG_STATE_SENDING_ACK: | |
657 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n"); | |
658 | |
659 gg_write(h->fd, ack, 4); | |
660 | |
661 h->state = GG_STATE_READING_TYPE; | |
662 h->check = GG_CHECK_READ; | |
663 h->timeout = GG_DEFAULT_TIMEOUT; | |
664 | |
665 return e; | |
666 | |
667 case GG_STATE_READING_TYPE: | |
668 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n"); | |
669 | |
670 gg_read(h->fd, &small, sizeof(small)); | |
671 | |
672 small.type = gg_fix32(small.type); | |
673 | |
674 switch (small.type) { | |
675 case 0x0003: /* XXX */ | |
676 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n"); | |
677 h->type = GG_SESSION_DCC_SEND; | |
678 h->state = GG_STATE_SENDING_FILE_INFO; | |
679 h->check = GG_CHECK_WRITE; | |
680 h->timeout = GG_DEFAULT_TIMEOUT; | |
681 | |
682 e->type = GG_EVENT_DCC_CALLBACK; | |
683 | |
684 break; | |
685 | |
686 case 0x0002: /* XXX */ | |
687 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n"); | |
688 h->type = GG_SESSION_DCC_GET; | |
689 h->state = GG_STATE_READING_REQUEST; | |
690 h->check = GG_CHECK_READ; | |
691 h->timeout = GG_DEFAULT_TIMEOUT; | |
692 h->incoming = 1; | |
693 | |
694 break; | |
695 | |
696 default: | |
697 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %ld\n", small.type, h->peer_uin); | |
698 e->type = GG_EVENT_DCC_ERROR; | |
699 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; | |
700 } | |
701 | |
702 return e; | |
703 | |
704 case GG_STATE_READING_REQUEST: | |
705 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n"); | |
706 | |
707 gg_read(h->fd, &small, sizeof(small)); | |
708 | |
709 small.type = gg_fix32(small.type); | |
710 | |
711 switch (small.type) { | |
712 case 0x0001: /* XXX */ | |
713 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n"); | |
714 h->state = GG_STATE_READING_FILE_INFO; | |
715 h->check = GG_CHECK_READ; | |
716 h->timeout = GG_DEFAULT_TIMEOUT; | |
717 break; | |
718 | |
719 case 0x0003: /* XXX */ | |
720 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n"); | |
721 h->state = GG_STATE_SENDING_VOICE_ACK; | |
722 h->check = GG_CHECK_WRITE; | |
723 h->timeout = GG_DCC_TIMEOUT_VOICE_ACK; | |
724 h->type = GG_SESSION_DCC_VOICE; | |
725 e->type = GG_EVENT_DCC_NEED_VOICE_ACK; | |
726 | |
727 break; | |
728 | |
729 default: | |
730 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin); | |
731 e->type = GG_EVENT_DCC_ERROR; | |
732 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; | |
733 } | |
734 | |
735 return e; | |
736 | |
737 case GG_STATE_READING_FILE_INFO: | |
738 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n"); | |
739 | |
740 gg_read(h->fd, &file_info_packet, sizeof(file_info_packet)); | |
741 | |
742 memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info)); | |
743 | |
744 h->file_info.mode = gg_fix32(h->file_info.mode); | |
745 h->file_info.size = gg_fix32(h->file_info.size); | |
746 | |
747 h->state = GG_STATE_SENDING_FILE_ACK; | |
748 h->check = GG_CHECK_WRITE; | |
749 h->timeout = GG_DCC_TIMEOUT_FILE_ACK; | |
750 | |
751 e->type = GG_EVENT_DCC_NEED_FILE_ACK; | |
752 | |
753 return e; | |
754 | |
755 case GG_STATE_SENDING_FILE_ACK: | |
756 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n"); | |
757 | |
758 big.type = gg_fix32(0x0006); /* XXX */ | |
759 big.dunno1 = gg_fix32(h->offset); | |
760 big.dunno2 = 0; | |
761 | |
762 gg_write(h->fd, &big, sizeof(big)); | |
763 | |
764 h->state = GG_STATE_READING_FILE_HEADER; | |
765 h->chunk_size = sizeof(big); | |
766 h->chunk_offset = 0; | |
767 if (!(h->chunk_buf = malloc(sizeof(big)))) { | |
768 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); | |
769 free(e); | |
770 return NULL; | |
771 } | |
772 h->check = GG_CHECK_READ; | |
773 h->timeout = GG_DEFAULT_TIMEOUT; | |
774 | |
775 return e; | |
776 | |
777 case GG_STATE_SENDING_VOICE_ACK: | |
778 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n"); | |
779 | |
780 tiny.type = 0x01; /* XXX */ | |
781 | |
782 gg_write(h->fd, &tiny, sizeof(tiny)); | |
783 | |
784 h->state = GG_STATE_READING_VOICE_HEADER; | |
785 h->check = GG_CHECK_READ; | |
786 h->timeout = GG_DEFAULT_TIMEOUT; | |
787 | |
788 h->offset = 0; | |
789 | |
790 return e; | |
791 | |
792 case GG_STATE_READING_FILE_HEADER: | |
793 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n"); | |
794 | |
795 tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); | |
796 | |
797 if (tmp == -1) { | |
798 gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); | |
799 e->type = GG_EVENT_DCC_ERROR; | |
800 e->event.dcc_error = GG_ERROR_DCC_NET; | |
801 return e; | |
802 } | |
803 | |
804 gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); | |
805 | |
806 h->chunk_offset += tmp; | |
807 | |
808 if (h->chunk_offset < h->chunk_size) | |
809 return e; | |
810 | |
811 memcpy(&big, h->chunk_buf, sizeof(big)); | |
812 free(h->chunk_buf); | |
813 h->chunk_buf = NULL; | |
814 | |
815 big.type = gg_fix32(big.type); | |
816 h->chunk_size = gg_fix32(big.dunno1); | |
817 h->chunk_offset = 0; | |
818 | |
819 if (big.type == 0x0005) { /* XXX */ | |
820 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n"); | |
821 e->type = GG_EVENT_DCC_ERROR; | |
822 e->event.dcc_error = GG_ERROR_DCC_REFUSED; | |
823 return e; | |
824 } | |
825 | |
826 if (h->chunk_size == 0) { | |
827 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n"); | |
828 e->type = GG_EVENT_DCC_DONE; | |
829 return e; | |
830 } | |
831 | |
832 h->state = GG_STATE_GETTING_FILE; | |
833 h->check = GG_CHECK_READ; | |
834 h->timeout = GG_DEFAULT_TIMEOUT; | |
835 h->established = 1; | |
836 | |
837 return e; | |
838 | |
839 case GG_STATE_READING_VOICE_HEADER: | |
840 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n"); | |
841 | |
842 gg_read(h->fd, &tiny, sizeof(tiny)); | |
843 | |
844 switch (tiny.type) { | |
845 case 0x03: /* XXX */ | |
846 h->state = GG_STATE_READING_VOICE_SIZE; | |
847 h->check = GG_CHECK_READ; | |
848 h->timeout = GG_DEFAULT_TIMEOUT; | |
849 h->established = 1; | |
850 break; | |
851 case 0x04: /* XXX */ | |
852 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n"); | |
853 /* XXX zwracać odpowiedni event */ | |
854 default: | |
855 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type); | |
856 e->type = GG_EVENT_DCC_ERROR; | |
857 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; | |
858 } | |
859 | |
860 return e; | |
861 | |
862 case GG_STATE_READING_VOICE_SIZE: | |
863 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n"); | |
864 | |
865 gg_read(h->fd, &small, sizeof(small)); | |
866 | |
867 small.type = gg_fix32(small.type); | |
868 | |
869 if (small.type < 16 || small.type > sizeof(buf)) { | |
870 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type); | |
871 e->type = GG_EVENT_DCC_ERROR; | |
872 e->event.dcc_error = GG_ERROR_DCC_NET; | |
873 | |
874 return e; | |
875 } | |
876 | |
877 h->chunk_size = small.type; | |
878 h->chunk_offset = 0; | |
879 | |
880 if (!(h->voice_buf = malloc(h->chunk_size))) { | |
881 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n"); | |
882 return NULL; | |
883 } | |
884 | |
885 h->state = GG_STATE_READING_VOICE_DATA; | |
886 h->check = GG_CHECK_READ; | |
887 h->timeout = GG_DEFAULT_TIMEOUT; | |
888 | |
889 return e; | |
890 | |
891 case GG_STATE_READING_VOICE_DATA: | |
892 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n"); | |
893 | |
894 tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); | |
895 if (tmp < 1) { | |
896 if (tmp == -1) { | |
897 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); | |
898 } else { | |
899 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); | |
900 } | |
901 e->type = GG_EVENT_DCC_ERROR; | |
902 e->event.dcc_error = GG_ERROR_DCC_NET; | |
903 return e; | |
904 } | |
905 | |
906 gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp); | |
907 | |
908 h->chunk_offset += tmp; | |
909 | |
910 if (h->chunk_offset >= h->chunk_size) { | |
911 e->type = GG_EVENT_DCC_VOICE_DATA; | |
912 e->event.dcc_voice_data.data = h->voice_buf; | |
913 e->event.dcc_voice_data.length = h->chunk_size; | |
914 h->state = GG_STATE_READING_VOICE_HEADER; | |
915 h->voice_buf = NULL; | |
916 } | |
917 | |
918 h->check = GG_CHECK_READ; | |
919 h->timeout = GG_DEFAULT_TIMEOUT; | |
920 | |
921 return e; | |
922 | |
923 case GG_STATE_CONNECTING: | |
924 { | |
925 uin_t uins[2]; | |
926 | |
927 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n"); | |
928 | |
929 res = 0; | |
930 if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) { | |
931 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res)); | |
932 e->type = GG_EVENT_DCC_ERROR; | |
933 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; | |
934 return e; | |
935 } | |
936 | |
937 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n"); | |
938 | |
939 uins[0] = gg_fix32(h->uin); | |
940 uins[1] = gg_fix32(h->peer_uin); | |
941 | |
942 gg_write(h->fd, uins, sizeof(uins)); | |
943 | |
944 h->state = GG_STATE_READING_ACK; | |
945 h->check = GG_CHECK_READ; | |
946 h->timeout = GG_DEFAULT_TIMEOUT; | |
947 | |
948 return e; | |
949 } | |
950 | |
951 case GG_STATE_READING_ACK: | |
952 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n"); | |
953 | |
954 gg_read(h->fd, buf, 4); | |
955 | |
956 if (strncmp(buf, ack, 4)) { | |
957 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n"); | |
958 | |
959 e->type = GG_EVENT_DCC_ERROR; | |
960 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; | |
961 return e; | |
962 } | |
963 | |
964 h->check = GG_CHECK_WRITE; | |
965 h->timeout = GG_DEFAULT_TIMEOUT; | |
966 h->state = GG_STATE_SENDING_REQUEST; | |
967 | |
968 return e; | |
969 | |
970 case GG_STATE_SENDING_VOICE_REQUEST: | |
971 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n"); | |
972 | |
973 small.type = gg_fix32(0x0003); | |
974 | |
975 gg_write(h->fd, &small, sizeof(small)); | |
976 | |
977 h->state = GG_STATE_READING_VOICE_ACK; | |
978 h->check = GG_CHECK_READ; | |
979 h->timeout = GG_DEFAULT_TIMEOUT; | |
980 | |
981 return e; | |
982 | |
983 case GG_STATE_SENDING_REQUEST: | |
984 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n"); | |
985 | |
986 small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */ | |
987 | |
988 gg_write(h->fd, &small, sizeof(small)); | |
989 | |
990 switch (h->type) { | |
991 case GG_SESSION_DCC_GET: | |
992 h->state = GG_STATE_READING_REQUEST; | |
993 h->check = GG_CHECK_READ; | |
994 h->timeout = GG_DEFAULT_TIMEOUT; | |
995 break; | |
996 | |
997 case GG_SESSION_DCC_SEND: | |
998 h->state = GG_STATE_SENDING_FILE_INFO; | |
999 h->check = GG_CHECK_WRITE; | |
1000 h->timeout = GG_DEFAULT_TIMEOUT; | |
1001 | |
1002 if (h->file_fd == -1) | |
1003 e->type = GG_EVENT_DCC_NEED_FILE_INFO; | |
1004 break; | |
1005 | |
1006 case GG_SESSION_DCC_VOICE: | |
1007 h->state = GG_STATE_SENDING_VOICE_REQUEST; | |
1008 h->check = GG_CHECK_WRITE; | |
1009 h->timeout = GG_DEFAULT_TIMEOUT; | |
1010 break; | |
1011 } | |
1012 | |
1013 return e; | |
1014 | |
1015 case GG_STATE_SENDING_FILE_INFO: | |
1016 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n"); | |
1017 | |
1018 if (h->file_fd == -1) { | |
1019 e->type = GG_EVENT_DCC_NEED_FILE_INFO; | |
1020 return e; | |
1021 } | |
1022 | |
1023 small.type = gg_fix32(0x0001); /* XXX */ | |
1024 | |
1025 gg_write(h->fd, &small, sizeof(small)); | |
1026 | |
1027 file_info_packet.big.type = gg_fix32(0x0003); /* XXX */ | |
1028 file_info_packet.big.dunno1 = 0; | |
1029 file_info_packet.big.dunno2 = 0; | |
1030 | |
1031 memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info)); | |
1032 | |
1033 /* zostają teraz u nas, więc odwracamy z powrotem */ | |
1034 h->file_info.size = gg_fix32(h->file_info.size); | |
1035 h->file_info.mode = gg_fix32(h->file_info.mode); | |
1036 | |
1037 gg_write(h->fd, &file_info_packet, sizeof(file_info_packet)); | |
1038 | |
1039 h->state = GG_STATE_READING_FILE_ACK; | |
1040 h->check = GG_CHECK_READ; | |
1041 h->timeout = GG_DCC_TIMEOUT_FILE_ACK; | |
1042 | |
1043 return e; | |
1044 | |
1045 case GG_STATE_READING_FILE_ACK: | |
1046 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n"); | |
1047 | |
1048 gg_read(h->fd, &big, sizeof(big)); | |
1049 | |
1050 /* XXX sprawdzać wynik */ | |
1051 h->offset = gg_fix32(big.dunno1); | |
1052 | |
1053 h->state = GG_STATE_SENDING_FILE_HEADER; | |
1054 h->check = GG_CHECK_WRITE; | |
1055 h->timeout = GG_DEFAULT_TIMEOUT; | |
1056 | |
1057 e->type = GG_EVENT_DCC_ACK; | |
1058 | |
1059 return e; | |
1060 | |
1061 case GG_STATE_READING_VOICE_ACK: | |
1062 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n"); | |
1063 | |
1064 gg_read(h->fd, &tiny, sizeof(tiny)); | |
1065 | |
1066 if (tiny.type != 0x01) { | |
1067 gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type); | |
1068 e->type = GG_EVENT_DCC_ERROR; | |
1069 e->event.dcc_error = GG_ERROR_DCC_REFUSED; | |
1070 return e; | |
1071 } | |
1072 | |
1073 h->state = GG_STATE_READING_VOICE_HEADER; | |
1074 h->check = GG_CHECK_READ; | |
1075 h->timeout = GG_DEFAULT_TIMEOUT; | |
1076 | |
1077 e->type = GG_EVENT_DCC_ACK; | |
1078 | |
1079 return e; | |
1080 | |
1081 case GG_STATE_SENDING_FILE_HEADER: | |
1082 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n"); | |
1083 | |
1084 h->chunk_offset = 0; | |
1085 | |
1086 if ((h->chunk_size = h->file_info.size - h->offset) > 4096) { | |
1087 h->chunk_size = 4096; | |
1088 big.type = gg_fix32(0x0003); /* XXX */ | |
1089 } else | |
1090 big.type = gg_fix32(0x0002); /* XXX */ | |
1091 | |
1092 big.dunno1 = gg_fix32(h->chunk_size); | |
1093 big.dunno2 = 0; | |
1094 | |
1095 gg_write(h->fd, &big, sizeof(big)); | |
1096 | |
1097 h->state = GG_STATE_SENDING_FILE; | |
1098 h->check = GG_CHECK_WRITE; | |
1099 h->timeout = GG_DEFAULT_TIMEOUT; | |
1100 h->established = 1; | |
1101 | |
1102 return e; | |
1103 | |
1104 case GG_STATE_SENDING_FILE: | |
1105 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n"); | |
1106 | |
1107 if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) | |
1108 utmp = sizeof(buf); | |
1109 | |
1110 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size); | |
1111 | |
1112 /* koniec pliku? */ | |
1113 if (h->file_info.size == 0) { | |
1114 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n"); | |
1115 e->type = GG_EVENT_DCC_DONE; | |
1116 | |
1117 return e; | |
1118 } | |
1119 | |
1120 lseek(h->file_fd, h->offset, SEEK_SET); | |
1121 | |
1122 size = read(h->file_fd, buf, utmp); | |
1123 | |
1124 /* błąd */ | |
1125 if (size == -1) { | |
1126 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); | |
1127 | |
1128 e->type = GG_EVENT_DCC_ERROR; | |
1129 e->event.dcc_error = GG_ERROR_DCC_FILE; | |
1130 | |
1131 return e; | |
1132 } | |
1133 | |
1134 /* koniec pliku? */ | |
1135 if (size == 0) { | |
1136 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); | |
1137 e->type = GG_EVENT_DCC_ERROR; | |
1138 e->event.dcc_error = GG_ERROR_DCC_EOF; | |
1139 | |
1140 return e; | |
1141 } | |
1142 | |
1143 /* jeśli wczytaliśmy więcej, utnijmy. */ | |
1144 if (h->offset + size > h->file_info.size) { | |
1145 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size); | |
1146 size = h->file_info.size - h->offset; | |
1147 | |
1148 if (size < 1) { | |
1149 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n"); | |
1150 e->type = GG_EVENT_DCC_DONE; | |
1151 return e; | |
1152 } | |
1153 } | |
1154 | |
1155 tmp = write(h->fd, buf, size); | |
1156 | |
1157 if (tmp == -1) { | |
1158 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%s)\n", strerror(errno)); | |
1159 e->type = GG_EVENT_DCC_ERROR; | |
1160 e->event.dcc_error = GG_ERROR_DCC_NET; | |
1161 return e; | |
1162 } | |
1163 | |
1164 h->offset += size; | |
1165 | |
1166 if (h->offset >= h->file_info.size) { | |
1167 e->type = GG_EVENT_DCC_DONE; | |
1168 return e; | |
1169 } | |
1170 | |
1171 h->chunk_offset += size; | |
1172 | |
1173 if (h->chunk_offset >= h->chunk_size) { | |
1174 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); | |
1175 h->state = GG_STATE_SENDING_FILE_HEADER; | |
1176 h->timeout = GG_DEFAULT_TIMEOUT; | |
1177 } else { | |
1178 h->state = GG_STATE_SENDING_FILE; | |
1179 h->timeout = GG_DCC_TIMEOUT_SEND; | |
1180 } | |
1181 | |
1182 h->check = GG_CHECK_WRITE; | |
1183 | |
1184 return e; | |
1185 | |
1186 case GG_STATE_GETTING_FILE: | |
1187 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n"); | |
1188 | |
1189 if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) | |
1190 utmp = sizeof(buf); | |
1191 | |
1192 size = read(h->fd, buf, utmp); | |
1193 | |
1194 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size); | |
1195 | |
1196 /* błąd */ | |
1197 if (size == -1) { | |
1198 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); | |
1199 | |
1200 e->type = GG_EVENT_DCC_ERROR; | |
1201 e->event.dcc_error = GG_ERROR_DCC_NET; | |
1202 | |
1203 return e; | |
1204 } | |
1205 | |
1206 /* koniec? */ | |
1207 if (size == 0) { | |
1208 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); | |
1209 e->type = GG_EVENT_DCC_ERROR; | |
1210 e->event.dcc_error = GG_ERROR_DCC_EOF; | |
1211 | |
1212 return e; | |
1213 } | |
1214 | |
1215 tmp = write(h->file_fd, buf, size); | |
1216 | |
1217 if (tmp == -1 || tmp < size) { | |
1218 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno)); | |
1219 e->type = GG_EVENT_DCC_ERROR; | |
1220 e->event.dcc_error = GG_ERROR_DCC_NET; | |
1221 return e; | |
1222 } | |
1223 | |
1224 h->offset += size; | |
1225 | |
1226 if (h->offset >= h->file_info.size) { | |
1227 e->type = GG_EVENT_DCC_DONE; | |
1228 return e; | |
1229 } | |
1230 | |
1231 h->chunk_offset += size; | |
1232 | |
1233 if (h->chunk_offset >= h->chunk_size) { | |
1234 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); | |
1235 h->state = GG_STATE_READING_FILE_HEADER; | |
1236 h->timeout = GG_DEFAULT_TIMEOUT; | |
1237 h->chunk_offset = 0; | |
1238 h->chunk_size = sizeof(big); | |
1239 if (!(h->chunk_buf = malloc(sizeof(big)))) { | |
1240 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); | |
1241 free(e); | |
1242 return NULL; | |
1243 } | |
1244 } else { | |
1245 h->state = GG_STATE_GETTING_FILE; | |
1246 h->timeout = GG_DCC_TIMEOUT_GET; | |
1247 } | |
1248 | |
1249 h->check = GG_CHECK_READ; | |
1250 | |
1251 return e; | |
1252 | |
1253 default: | |
1254 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n"); | |
1255 e->type = GG_EVENT_DCC_ERROR; | |
1256 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; | |
1257 | |
1258 return e; | |
1259 } | |
1260 } | |
1261 | |
1262 return e; | |
1263 } | |
1264 | |
1265 #undef gg_read | |
1266 #undef gg_write | |
1267 | |
1268 /* | |
1269 * gg_dcc_free() | |
1270 * | |
1271 * zwalnia pamięć po strukturze połączenia dcc. | |
1272 * | |
1273 * - d - zwalniana struktura | |
1274 */ | |
1275 void gg_dcc_free(struct gg_dcc *d) | |
1276 { | |
1277 gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d); | |
1278 | |
1279 if (!d) | |
1280 return; | |
1281 | |
1282 if (d->fd != -1) | |
1283 close(d->fd); | |
1284 | |
1285 if (d->chunk_buf) { | |
1286 free(d->chunk_buf); | |
1287 d->chunk_buf = NULL; | |
1288 } | |
1289 | |
1290 free(d); | |
1291 } | |
1292 | |
1293 /* | |
1294 * Local variables: | |
1295 * c-indentation-style: k&r | |
1296 * c-basic-offset: 8 | |
1297 * indent-tabs-mode: notnil | |
1298 * End: | |
1299 * | |
1300 * vim: shiftwidth=8: | |
1301 */ |