| 1 /* $Id$ */ |
|
| 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 * This program is free software; you can redistribute it and/or modify |
|
| 9 * it under the terms of the GNU Lesser General Public License Version |
|
| 10 * 2.1 as published by the Free Software Foundation. |
|
| 11 * |
|
| 12 * This program is distributed in the hope that it will be useful, |
|
| 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 15 * GNU Lesser General Public License for more details. |
|
| 16 * |
|
| 17 * You should have received a copy of the GNU Lesser General Public |
|
| 18 * License along with this program; if not, write to the Free Software |
|
| 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, |
|
| 20 * USA. |
|
| 21 */ |
|
| 22 |
|
| 23 /** |
|
| 24 * \file dcc.c |
|
| 25 * |
|
| 26 * \brief Obsługa połączeń bezpośrednich do wersji Gadu-Gadu 6.x |
|
| 27 */ |
|
| 28 |
|
| 29 #include "fileio.h" |
|
| 30 #include "network.h" |
|
| 31 |
|
| 32 #include <ctype.h> |
|
| 33 #include <errno.h> |
|
| 34 #include <string.h> |
|
| 35 #include <stdlib.h> |
|
| 36 |
|
| 37 #include "libgadu.h" |
|
| 38 #include "debug.h" |
|
| 39 #include "internal.h" |
|
| 40 |
|
| 41 /** |
|
| 42 * \internal Przekazuje zawartość pakietu do odpluskwiania. |
|
| 43 * |
|
| 44 * \param prefix Prefiks informacji |
|
| 45 * \param fd Deskryptor gniazda |
|
| 46 * \param buf Bufor z danumi |
|
| 47 * \param size Rozmiar bufora z danymi |
|
| 48 */ |
|
| 49 static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size) |
|
| 50 { |
|
| 51 gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size); |
|
| 52 gg_debug_dump(NULL, GG_DEBUG_DUMP, buf, size); |
|
| 53 gg_debug(GG_DEBUG_MISC, "\n"); |
|
| 54 } |
|
| 55 |
|
| 56 /** |
|
| 57 * Wysyła żądanie zwrotnego połączenia bezpośredniego. |
|
| 58 * |
|
| 59 * Funkcję wykorzystuje się, jeśli nie ma możliwości połączenia się z odbiorcą |
|
| 60 * pliku lub rozmowy głosowej. Po otrzymaniu żądania druga strona spróbuje |
|
| 61 * nawiązać zwrotne połączenie bezpośrednie z nadawcą. |
|
| 62 * gg_dcc_request() |
|
| 63 * |
|
| 64 * \param sess Struktura sesji |
|
| 65 * \param uin Numer odbiorcy |
|
| 66 * |
|
| 67 * \return Patrz \c gg_send_message_ctcp() |
|
| 68 * |
|
| 69 * \ingroup dcc6 |
|
| 70 */ |
|
| 71 int gg_dcc_request(struct gg_session *sess, uin_t uin) |
|
| 72 { |
|
| 73 return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char*) "\002", 1); |
|
| 74 } |
|
| 75 |
|
| 76 /** |
|
| 77 * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32. |
|
| 78 * |
|
| 79 * \note Funkcja działa jedynie gdy kompilator obsługuje typ danych |
|
| 80 * \c long \c long. |
|
| 81 * |
|
| 82 * \param ut Czas w postaci uniksowej |
|
| 83 * \param ft Czas w postaci API WIN32 |
|
| 84 */ |
|
| 85 static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft) |
|
| 86 { |
|
| 87 uint64_t tmp; |
|
| 88 |
|
| 89 tmp = ut; |
|
| 90 tmp += 11644473600LL; |
|
| 91 tmp *= 10000000LL; |
|
| 92 |
|
| 93 tmp = gg_fix64(tmp); |
|
| 94 |
|
| 95 memcpy(ft, &tmp, sizeof(tmp)); |
|
| 96 } |
|
| 97 |
|
| 98 /** |
|
| 99 * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku. |
|
| 100 * |
|
| 101 * \note Większą funkcjonalność zapewnia funkcja \c gg_dcc_fill_file_info2(). |
|
| 102 * |
|
| 103 * \param d Struktura połączenia |
|
| 104 * \param filename Nazwa pliku |
|
| 105 * |
|
| 106 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 107 * |
|
| 108 * \ingroup dcc6 |
|
| 109 */ |
|
| 110 int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename) |
|
| 111 { |
|
| 112 return gg_dcc_fill_file_info2(d, filename, filename); |
|
| 113 } |
|
| 114 |
|
| 115 /** |
|
| 116 * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku. |
|
| 117 * |
|
| 118 * \param d Struktura połączenia |
|
| 119 * \param filename Nazwa pliku zapisywana w strukturze |
|
| 120 * \param local_filename Nazwa pliku w lokalnym systemie plików |
|
| 121 * |
|
| 122 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 123 * |
|
| 124 * \ingroup dcc6 |
|
| 125 */ |
|
| 126 int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename) |
|
| 127 { |
|
| 128 struct stat st; |
|
| 129 const char *name, *ext, *p; |
|
| 130 unsigned char *q; |
|
| 131 int i, j; |
|
| 132 |
|
| 133 gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename); |
|
| 134 |
|
| 135 if (!d || d->type != GG_SESSION_DCC_SEND) { |
|
| 136 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n"); |
|
| 137 errno = EINVAL; |
|
| 138 return -1; |
|
| 139 } |
|
| 140 |
|
| 141 if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) { |
|
| 142 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno)); |
|
| 143 return -1; |
|
| 144 } |
|
| 145 |
|
| 146 if (fstat(d->file_fd, &st) == -1) { |
|
| 147 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() " |
|
| 148 "fstat() failed (%s)\n", strerror(errno)); |
|
| 149 close(d->file_fd); |
|
| 150 d->file_fd = -1; |
|
| 151 return -1; |
|
| 152 } |
|
| 153 |
|
| 154 if ((st.st_mode & S_IFDIR)) { |
|
| 155 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n"); |
|
| 156 errno = EINVAL; |
|
| 157 close(d->file_fd); |
|
| 158 d->file_fd = -1; |
|
| 159 return -1; |
|
| 160 } |
|
| 161 |
|
| 162 memset(&d->file_info, 0, sizeof(d->file_info)); |
|
| 163 |
|
| 164 if (!(st.st_mode & S_IWUSR)) |
|
| 165 d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY); |
|
| 166 |
|
| 167 gg_dcc_fill_filetime(st.st_atime, d->file_info.atime); |
|
| 168 gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime); |
|
| 169 gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime); |
|
| 170 |
|
| 171 d->file_info.size = gg_fix32(st.st_size); |
|
| 172 d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */ |
|
| 173 |
|
| 174 if (!(name = strrchr(filename, '/'))) |
|
| 175 name = filename; |
|
| 176 else |
|
| 177 name++; |
|
| 178 |
|
| 179 if (!(ext = strrchr(name, '.'))) |
|
| 180 ext = name + strlen(name); |
|
| 181 |
|
| 182 for (i = 0, p = name; i < 8 && p < ext; i++, p++) |
|
| 183 d->file_info.short_filename[i] = toupper(name[i]); |
|
| 184 |
|
| 185 if (i == 8 && p < ext) { |
|
| 186 d->file_info.short_filename[6] = '~'; |
|
| 187 d->file_info.short_filename[7] = '1'; |
|
| 188 } |
|
| 189 |
|
| 190 if (strlen(ext) > 0) { |
|
| 191 for (j = 0; *ext && j < 4; j++, p++) |
|
| 192 d->file_info.short_filename[i + j] = toupper(ext[j]); |
|
| 193 } |
|
| 194 |
|
| 195 for (q = d->file_info.short_filename; *q; q++) { |
|
| 196 if (*q == 185) { |
|
| 197 *q = 165; |
|
| 198 } else if (*q == 230) { |
|
| 199 *q = 198; |
|
| 200 } else if (*q == 234) { |
|
| 201 *q = 202; |
|
| 202 } else if (*q == 179) { |
|
| 203 *q = 163; |
|
| 204 } else if (*q == 241) { |
|
| 205 *q = 209; |
|
| 206 } else if (*q == 243) { |
|
| 207 *q = 211; |
|
| 208 } else if (*q == 156) { |
|
| 209 *q = 140; |
|
| 210 } else if (*q == 159) { |
|
| 211 *q = 143; |
|
| 212 } else if (*q == 191) { |
|
| 213 *q = 175; |
|
| 214 } |
|
| 215 } |
|
| 216 |
|
| 217 gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\"," |
|
| 218 " dos name \"%s\"\n", name, d->file_info.short_filename); |
|
| 219 strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1); |
|
| 220 |
|
| 221 return 0; |
|
| 222 } |
|
| 223 |
|
| 224 /** |
|
| 225 * \internal Rozpoczyna połączenie bezpośrednie z danym klientem. |
|
| 226 * |
|
| 227 * \param ip Adres IP odbiorcy |
|
| 228 * \param port Port odbiorcy |
|
| 229 * \param my_uin Własny numer |
|
| 230 * \param peer_uin Numer odbiorcy |
|
| 231 * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET) |
|
| 232 * |
|
| 233 * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu |
|
| 234 */ |
|
| 235 static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type) |
|
| 236 { |
|
| 237 struct gg_dcc *d = NULL; |
|
| 238 struct in_addr addr; |
|
| 239 |
|
| 240 addr.s_addr = ip; |
|
| 241 |
|
| 242 gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %u, %u, " |
|
| 243 "%s);\n", inet_ntoa(addr), port, my_uin, peer_uin, |
|
| 244 (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET"); |
|
| 245 |
|
| 246 if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) { |
|
| 247 gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n"); |
|
| 248 errno = EINVAL; |
|
| 249 return NULL; |
|
| 250 } |
|
| 251 |
|
| 252 if (!(d = (void*) calloc(1, sizeof(*d)))) { |
|
| 253 gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n"); |
|
| 254 return NULL; |
|
| 255 } |
|
| 256 |
|
| 257 d->check = GG_CHECK_WRITE; |
|
| 258 d->state = GG_STATE_CONNECTING; |
|
| 259 d->type = type; |
|
| 260 d->timeout = GG_DEFAULT_TIMEOUT; |
|
| 261 d->file_fd = -1; |
|
| 262 d->active = 1; |
|
| 263 d->fd = -1; |
|
| 264 d->uin = my_uin; |
|
| 265 d->peer_uin = peer_uin; |
|
| 266 |
|
| 267 if ((d->fd = gg_connect(&addr, port, 1)) == -1) { |
|
| 268 gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n"); |
|
| 269 free(d); |
|
| 270 return NULL; |
|
| 271 } |
|
| 272 |
|
| 273 return d; |
|
| 274 } |
|
| 275 |
|
| 276 /** |
|
| 277 * Rozpoczyna odbieranie pliku przez zwrotne połączenie bezpośrednie. |
|
| 278 * |
|
| 279 * \param ip Adres IP nadawcy |
|
| 280 * \param port Port nadawcy |
|
| 281 * \param my_uin Własny numer |
|
| 282 * \param peer_uin Numer nadawcy |
|
| 283 * |
|
| 284 * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu |
|
| 285 * |
|
| 286 * \ingroup dcc6 |
|
| 287 */ |
|
| 288 struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) |
|
| 289 { |
|
| 290 gg_debug(GG_DEBUG_MISC, "// gg_dcc_get_file() handing over to gg_dcc_transfer()\n"); |
|
| 291 |
|
| 292 return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET); |
|
| 293 } |
|
| 294 |
|
| 295 /** |
|
| 296 * Rozpoczyna wysyłanie pliku. |
|
| 297 * |
|
| 298 * \param ip Adres IP odbiorcy |
|
| 299 * \param port Port odbiorcy |
|
| 300 * \param my_uin Własny numer |
|
| 301 * \param peer_uin Numer odbiorcy |
|
| 302 * |
|
| 303 * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu |
|
| 304 * |
|
| 305 * \ingroup dcc6 |
|
| 306 */ |
|
| 307 struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) |
|
| 308 { |
|
| 309 gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n"); |
|
| 310 |
|
| 311 return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND); |
|
| 312 } |
|
| 313 |
|
| 314 /** |
|
| 315 * Rozpoczyna połączenie głosowe. |
|
| 316 * |
|
| 317 * \param ip Adres IP odbiorcy |
|
| 318 * \param port Port odbiorcy |
|
| 319 * \param my_uin Własny numer |
|
| 320 * \param peer_uin Numer odbiorcy |
|
| 321 * |
|
| 322 * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu |
|
| 323 * |
|
| 324 * \ingroup dcc6 |
|
| 325 */ |
|
| 326 struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) |
|
| 327 { |
|
| 328 gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n"); |
|
| 329 |
|
| 330 return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE); |
|
| 331 } |
|
| 332 |
|
| 333 /** |
|
| 334 * Ustawia typ przychodzącego połączenia bezpośredniego. |
|
| 335 * |
|
| 336 * Funkcję należy wywołać po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK. |
|
| 337 * |
|
| 338 * \param d Struktura połączenia |
|
| 339 * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub |
|
| 340 * \c GG_SESSION_DCC_VOICE) |
|
| 341 * |
|
| 342 * \ingroup dcc6 |
|
| 343 */ |
|
| 344 void gg_dcc_set_type(struct gg_dcc *d, int type) |
|
| 345 { |
|
| 346 d->type = type; |
|
| 347 d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST; |
|
| 348 } |
|
| 349 |
|
| 350 /** |
|
| 351 * \internal Funkcja zwrotna połączenia bezpośredniego. |
|
| 352 * |
|
| 353 * Pole \c callback struktury \c gg_dcc zawiera wskaźnik do tej funkcji. |
|
| 354 * Wywołuje ona \c gg_dcc_watch_fd() i zachowuje wynik w polu \c event. |
|
| 355 * |
|
| 356 * \note Funkcjonalność funkcji zwrotnej nie jest już wspierana. |
|
| 357 * |
|
| 358 * \param d Struktura połączenia |
|
| 359 * |
|
| 360 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 361 */ |
|
| 362 static int gg_dcc_callback(struct gg_dcc *d) |
|
| 363 { |
|
| 364 struct gg_event *e = gg_dcc_watch_fd(d); |
|
| 365 |
|
| 366 d->event = e; |
|
| 367 |
|
| 368 return (e != NULL) ? 0 : -1; |
|
| 369 } |
|
| 370 |
|
| 371 /** |
|
| 372 * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich. |
|
| 373 * |
|
| 374 * Funkcja przywiązuje gniazdo do pierwszego wolnego portu TCP. |
|
| 375 * |
|
| 376 * \param uin Własny numer |
|
| 377 * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego) |
|
| 378 * |
|
| 379 * \note Ze względu na możliwość podania wartości -1 do parametru będącego |
|
| 380 * 16-bitową liczbą bez znaku, port 65535 nie jest dostępny. |
|
| 381 * |
|
| 382 * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu |
|
| 383 * |
|
| 384 * \ingroup dcc6 |
|
| 385 */ |
|
| 386 struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port) |
|
| 387 { |
|
| 388 struct gg_dcc *c; |
|
| 389 int sock, bound = 0, errno2; |
|
| 390 |
|
| 391 gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port); |
|
| 392 |
|
| 393 if (!uin) { |
|
| 394 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n"); |
|
| 395 errno = EINVAL; |
|
| 396 return NULL; |
|
| 397 } |
|
| 398 |
|
| 399 if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { |
|
| 400 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno)); |
|
| 401 return NULL; |
|
| 402 } |
|
| 403 |
|
| 404 if (port == 0 || port == (uint16_t)-1) |
|
| 405 port = GG_DEFAULT_DCC_PORT; |
|
| 406 |
|
| 407 while (!bound) { |
|
| 408 struct sockaddr_in sin; |
|
| 409 |
|
| 410 memset(&sin, 0, sizeof(sin)); |
|
| 411 sin.sin_family = AF_INET; |
|
| 412 sin.sin_addr.s_addr = INADDR_ANY; |
|
| 413 sin.sin_port = htons(port); |
|
| 414 |
|
| 415 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port); |
|
| 416 if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin))) |
|
| 417 bound = 1; |
|
| 418 else { |
|
| 419 if (++port == 65535) { |
|
| 420 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n"); |
|
| 421 close(sock); |
|
| 422 return NULL; |
|
| 423 } |
|
| 424 } |
|
| 425 } |
|
| 426 |
|
| 427 if (listen(sock, 10)) { |
|
| 428 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno)); |
|
| 429 errno2 = errno; |
|
| 430 close(sock); |
|
| 431 errno = errno2; |
|
| 432 return NULL; |
|
| 433 } |
|
| 434 |
|
| 435 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port); |
|
| 436 |
|
| 437 if (!(c = malloc(sizeof(*c)))) { |
|
| 438 gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n"); |
|
| 439 close(sock); |
|
| 440 return NULL; |
|
| 441 } |
|
| 442 memset(c, 0, sizeof(*c)); |
|
| 443 |
|
| 444 c->port = c->id = port; |
|
| 445 c->fd = sock; |
|
| 446 c->file_fd = -1; |
|
| 447 c->type = GG_SESSION_DCC_SOCKET; |
|
| 448 c->uin = uin; |
|
| 449 c->timeout = -1; |
|
| 450 c->state = GG_STATE_LISTENING; |
|
| 451 c->check = GG_CHECK_READ; |
|
| 452 c->callback = gg_dcc_callback; |
|
| 453 c->destroy = gg_dcc_free; |
|
| 454 |
|
| 455 return c; |
|
| 456 } |
|
| 457 |
|
| 458 /** |
|
| 459 * Wysyła ramkę danych połączenia głosowego. |
|
| 460 * |
|
| 461 * \param d Struktura połączenia |
|
| 462 * \param buf Bufor z danymi |
|
| 463 * \param length Długość bufora z danymi |
|
| 464 * |
|
| 465 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 466 * |
|
| 467 * \ingroup dcc6 |
|
| 468 */ |
|
| 469 int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length) |
|
| 470 { |
|
| 471 struct packet_s { |
|
| 472 uint8_t type; |
|
| 473 uint32_t length; |
|
| 474 } GG_PACKED; |
|
| 475 struct packet_s packet; |
|
| 476 |
|
| 477 gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length); |
|
| 478 if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) { |
|
| 479 gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n"); |
|
| 480 errno = EINVAL; |
|
| 481 return -1; |
|
| 482 } |
|
| 483 |
|
| 484 packet.type = 0x03; /* XXX */ |
|
| 485 packet.length = gg_fix32(length); |
|
| 486 |
|
| 487 if (send(d->fd, &packet, sizeof(packet), 0) < (signed)sizeof(packet)) { |
|
| 488 gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() send() failed\n"); |
|
| 489 return -1; |
|
| 490 } |
|
| 491 gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet)); |
|
| 492 |
|
| 493 if (send(d->fd, buf, length, 0) < length) { |
|
| 494 gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() send() failed\n"); |
|
| 495 return -1; |
|
| 496 } |
|
| 497 gg_dcc_debug_data("write", d->fd, buf, length); |
|
| 498 |
|
| 499 return 0; |
|
| 500 } |
|
| 501 |
|
| 502 /** |
|
| 503 * \internal Odbiera dane z połączenia bezpośredniego z obsługą błędów. |
|
| 504 * |
|
| 505 * \param fd Deskryptor gniazda |
|
| 506 * \param buf Bufor na dane |
|
| 507 * \param size Rozmiar bufora na dane |
|
| 508 */ |
|
| 509 #define gg_dcc_read(fd, buf, size) \ |
|
| 510 { \ |
|
| 511 int _tmp = recv(fd, buf, size, 0); \ |
|
| 512 \ |
|
| 513 if (_tmp < (int) size) { \ |
|
| 514 if (_tmp == -1) { \ |
|
| 515 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed " \ |
|
| 516 "(errno=%d, %s)\n", errno, strerror(errno)); \ |
|
| 517 } else if (_tmp == 0) { \ |
|
| 518 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed, " \ |
|
| 519 "connection broken\n"); \ |
|
| 520 } else { \ |
|
| 521 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed " \ |
|
| 522 "(%d bytes, %" GG_SIZE_FMT " needed)\n", \ |
|
| 523 _tmp, size); \ |
|
| 524 } \ |
|
| 525 e->type = GG_EVENT_DCC_ERROR; \ |
|
| 526 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \ |
|
| 527 return e; \ |
|
| 528 } \ |
|
| 529 gg_dcc_debug_data("read", fd, buf, size); \ |
|
| 530 } |
|
| 531 |
|
| 532 /** |
|
| 533 * \internal Wysyła dane do połączenia bezpośredniego z obsługą błędów. |
|
| 534 * |
|
| 535 * \param fd Deskryptor gniazda |
|
| 536 * \param buf Bufor z danymi |
|
| 537 * \param size Rozmiar bufora z danymi |
|
| 538 */ |
|
| 539 #define gg_dcc_write(fd, buf, size) \ |
|
| 540 { \ |
|
| 541 int write_res; \ |
|
| 542 gg_dcc_debug_data("write", fd, buf, size); \ |
|
| 543 write_res = send(fd, buf, size, 0); \ |
|
| 544 if (write_res < (int) size) { \ |
|
| 545 if (write_res == -1) { \ |
|
| 546 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() " \ |
|
| 547 "failed (errno=%d, %s)\n", errno, strerror(errno)); \ |
|
| 548 } else { \ |
|
| 549 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() " \ |
|
| 550 "failed (%" GG_SIZE_FMT " needed, %d done)\n", \ |
|
| 551 size, write_res); \ |
|
| 552 } \ |
|
| 553 e->type = GG_EVENT_DCC_ERROR; \ |
|
| 554 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \ |
|
| 555 return e; \ |
|
| 556 } \ |
|
| 557 } |
|
| 558 |
|
| 559 /** |
|
| 560 * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. |
|
| 561 * |
|
| 562 * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia |
|
| 563 * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania. |
|
| 564 * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free. |
|
| 565 * |
|
| 566 * \param h Struktura połączenia |
|
| 567 * |
|
| 568 * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd |
|
| 569 * |
|
| 570 * \ingroup dcc6 |
|
| 571 */ |
|
| 572 struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h) |
|
| 573 { |
|
| 574 struct gg_event *e; |
|
| 575 int foo; |
|
| 576 |
|
| 577 gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h); |
|
| 578 |
|
| 579 if (!h || (h->type != GG_SESSION_DCC && |
|
| 580 h->type != GG_SESSION_DCC_SOCKET && |
|
| 581 h->type != GG_SESSION_DCC_SEND && |
|
| 582 h->type != GG_SESSION_DCC_GET && |
|
| 583 h->type != GG_SESSION_DCC_VOICE)) |
|
| 584 { |
|
| 585 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n"); |
|
| 586 errno = EINVAL; |
|
| 587 return NULL; |
|
| 588 } |
|
| 589 |
|
| 590 if (!(e = (void*) calloc(1, sizeof(*e)))) { |
|
| 591 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n"); |
|
| 592 return NULL; |
|
| 593 } |
|
| 594 |
|
| 595 e->type = GG_EVENT_NONE; |
|
| 596 |
|
| 597 if (h->type == GG_SESSION_DCC_SOCKET) { |
|
| 598 struct sockaddr_in sin; |
|
| 599 struct gg_dcc *c; |
|
| 600 int fd; |
|
| 601 socklen_t sin_len = sizeof(sin); |
|
| 602 |
|
| 603 if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { |
|
| 604 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't " |
|
| 605 "accept() new connection (errno=%d, %s)\n", |
|
| 606 errno, strerror(errno)); |
|
| 607 return e; |
|
| 608 } |
|
| 609 |
|
| 610 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct " |
|
| 611 "connection from %s:%d\n", inet_ntoa(sin.sin_addr), |
|
| 612 htons(sin.sin_port)); |
|
| 613 |
|
| 614 if (!gg_fd_set_nonblocking(fd)) { |
|
| 615 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set" |
|
| 616 " nonblocking (errno=%d, %s)\n", |
|
| 617 errno, strerror(errno)); |
|
| 618 close(fd); |
|
| 619 e->type = GG_EVENT_DCC_ERROR; |
|
| 620 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; |
|
| 621 return e; |
|
| 622 } |
|
| 623 |
|
| 624 if (!(c = (void*) calloc(1, sizeof(*c)))) { |
|
| 625 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n"); |
|
| 626 |
|
| 627 free(e); |
|
| 628 close(fd); |
|
| 629 return NULL; |
|
| 630 } |
|
| 631 |
|
| 632 c->fd = fd; |
|
| 633 c->check = GG_CHECK_READ; |
|
| 634 c->state = GG_STATE_READING_UIN_1; |
|
| 635 c->type = GG_SESSION_DCC; |
|
| 636 c->timeout = GG_DEFAULT_TIMEOUT; |
|
| 637 c->file_fd = -1; |
|
| 638 c->remote_addr = sin.sin_addr.s_addr; |
|
| 639 c->remote_port = ntohs(sin.sin_port); |
|
| 640 |
|
| 641 e->type = GG_EVENT_DCC_NEW; |
|
| 642 e->event.dcc_new = c; |
|
| 643 |
|
| 644 return e; |
|
| 645 } else { |
|
| 646 struct gg_dcc_tiny_packet tiny_pkt; |
|
| 647 struct gg_dcc_small_packet small_pkt; |
|
| 648 struct gg_dcc_big_packet big_pkt; |
|
| 649 int size, tmp, res; |
|
| 650 unsigned int utmp; |
|
| 651 socklen_t res_size = sizeof(res); |
|
| 652 char buf[1024], ack[] = "UDAG"; |
|
| 653 void *tmp_buf; |
|
| 654 |
|
| 655 struct gg_dcc_file_info_packet { |
|
| 656 struct gg_dcc_big_packet big; |
|
| 657 struct gg_file_info file_info; |
|
| 658 } GG_PACKED; |
|
| 659 struct gg_dcc_file_info_packet file_info_packet; |
|
| 660 |
|
| 661 switch (h->state) { |
|
| 662 case GG_STATE_READING_UIN_1: |
|
| 663 case GG_STATE_READING_UIN_2: |
|
| 664 { |
|
| 665 uin_t uin; |
|
| 666 |
|
| 667 gg_debug(GG_DEBUG_MISC, |
|
| 668 "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", |
|
| 669 (h->state == GG_STATE_READING_UIN_1) ? 1 : 2); |
|
| 670 |
|
| 671 gg_dcc_read(h->fd, &uin, sizeof(uin)); |
|
| 672 |
|
| 673 if (h->state == GG_STATE_READING_UIN_1) { |
|
| 674 h->state = GG_STATE_READING_UIN_2; |
|
| 675 h->check = GG_CHECK_READ; |
|
| 676 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 677 h->peer_uin = gg_fix32(uin); |
|
| 678 } else { |
|
| 679 h->state = GG_STATE_SENDING_ACK; |
|
| 680 h->check = GG_CHECK_WRITE; |
|
| 681 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 682 h->uin = gg_fix32(uin); |
|
| 683 e->type = GG_EVENT_DCC_CLIENT_ACCEPT; |
|
| 684 } |
|
| 685 |
|
| 686 return e; |
|
| 687 } |
|
| 688 |
|
| 689 case GG_STATE_SENDING_ACK: |
|
| 690 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n"); |
|
| 691 |
|
| 692 gg_dcc_write(h->fd, ack, (size_t)4); |
|
| 693 |
|
| 694 h->state = GG_STATE_READING_TYPE; |
|
| 695 h->check = GG_CHECK_READ; |
|
| 696 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 697 |
|
| 698 return e; |
|
| 699 |
|
| 700 case GG_STATE_READING_TYPE: |
|
| 701 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n"); |
|
| 702 |
|
| 703 gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); |
|
| 704 |
|
| 705 small_pkt.type = gg_fix32(small_pkt.type); |
|
| 706 |
|
| 707 switch (small_pkt.type) { |
|
| 708 case 0x0003: /* XXX */ |
|
| 709 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n"); |
|
| 710 h->type = GG_SESSION_DCC_SEND; |
|
| 711 h->state = GG_STATE_SENDING_FILE_INFO; |
|
| 712 h->check = GG_CHECK_WRITE; |
|
| 713 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 714 |
|
| 715 e->type = GG_EVENT_DCC_CALLBACK; |
|
| 716 |
|
| 717 break; |
|
| 718 |
|
| 719 case 0x0002: /* XXX */ |
|
| 720 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n"); |
|
| 721 h->type = GG_SESSION_DCC_GET; |
|
| 722 h->state = GG_STATE_READING_REQUEST; |
|
| 723 h->check = GG_CHECK_READ; |
|
| 724 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 725 h->incoming = 1; |
|
| 726 |
|
| 727 break; |
|
| 728 |
|
| 729 default: |
|
| 730 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type " |
|
| 731 "(%.4x) from %u\n", small_pkt.type, h->peer_uin); |
|
| 732 e->type = GG_EVENT_DCC_ERROR; |
|
| 733 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; |
|
| 734 } |
|
| 735 |
|
| 736 return e; |
|
| 737 |
|
| 738 case GG_STATE_READING_REQUEST: |
|
| 739 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n"); |
|
| 740 |
|
| 741 gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); |
|
| 742 |
|
| 743 small_pkt.type = gg_fix32(small_pkt.type); |
|
| 744 |
|
| 745 switch (small_pkt.type) { |
|
| 746 case 0x0001: /* XXX */ |
|
| 747 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n"); |
|
| 748 h->state = GG_STATE_READING_FILE_INFO; |
|
| 749 h->check = GG_CHECK_READ; |
|
| 750 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 751 break; |
|
| 752 |
|
| 753 case 0x0003: /* XXX */ |
|
| 754 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n"); |
|
| 755 h->state = GG_STATE_SENDING_VOICE_ACK; |
|
| 756 h->check = GG_CHECK_WRITE; |
|
| 757 h->timeout = GG_DCC_TIMEOUT_VOICE_ACK; |
|
| 758 h->type = GG_SESSION_DCC_VOICE; |
|
| 759 e->type = GG_EVENT_DCC_NEED_VOICE_ACK; |
|
| 760 |
|
| 761 break; |
|
| 762 |
|
| 763 default: |
|
| 764 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown " |
|
| 765 "dcc request (%.4x) from %u\n", |
|
| 766 small_pkt.type, h->peer_uin); |
|
| 767 e->type = GG_EVENT_DCC_ERROR; |
|
| 768 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; |
|
| 769 } |
|
| 770 |
|
| 771 return e; |
|
| 772 |
|
| 773 case GG_STATE_READING_FILE_INFO: |
|
| 774 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n"); |
|
| 775 |
|
| 776 gg_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet)); |
|
| 777 |
|
| 778 memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info)); |
|
| 779 |
|
| 780 h->file_info.mode = gg_fix32(h->file_info.mode); |
|
| 781 h->file_info.size = gg_fix32(h->file_info.size); |
|
| 782 |
|
| 783 h->state = GG_STATE_SENDING_FILE_ACK; |
|
| 784 h->check = GG_CHECK_WRITE; |
|
| 785 h->timeout = GG_DCC_TIMEOUT_FILE_ACK; |
|
| 786 |
|
| 787 e->type = GG_EVENT_DCC_NEED_FILE_ACK; |
|
| 788 |
|
| 789 return e; |
|
| 790 |
|
| 791 case GG_STATE_SENDING_FILE_ACK: |
|
| 792 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n"); |
|
| 793 |
|
| 794 big_pkt.type = gg_fix32(0x0006); /* XXX */ |
|
| 795 big_pkt.dunno1 = gg_fix32(h->offset); |
|
| 796 big_pkt.dunno2 = 0; |
|
| 797 |
|
| 798 gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt)); |
|
| 799 |
|
| 800 h->state = GG_STATE_READING_FILE_HEADER; |
|
| 801 h->chunk_size = sizeof(big_pkt); |
|
| 802 h->chunk_offset = 0; |
|
| 803 h->chunk_buf = NULL; |
|
| 804 tmp_buf = malloc(sizeof(big_pkt)); |
|
| 805 if (!tmp_buf) { |
|
| 806 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); |
|
| 807 free(e); |
|
| 808 return NULL; |
|
| 809 } |
|
| 810 h->chunk_buf = tmp_buf; |
|
| 811 h->check = GG_CHECK_READ; |
|
| 812 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 813 |
|
| 814 return e; |
|
| 815 |
|
| 816 case GG_STATE_SENDING_VOICE_ACK: |
|
| 817 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n"); |
|
| 818 |
|
| 819 tiny_pkt.type = 0x01; /* XXX */ |
|
| 820 |
|
| 821 gg_dcc_write(h->fd, &tiny_pkt, sizeof(tiny_pkt)); |
|
| 822 |
|
| 823 h->state = GG_STATE_READING_VOICE_HEADER; |
|
| 824 h->check = GG_CHECK_READ; |
|
| 825 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 826 |
|
| 827 h->offset = 0; |
|
| 828 |
|
| 829 return e; |
|
| 830 |
|
| 831 case GG_STATE_READING_FILE_HEADER: |
|
| 832 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n"); |
|
| 833 |
|
| 834 tmp = recv(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0); |
|
| 835 |
|
| 836 if (tmp == -1) { |
|
| 837 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() " |
|
| 838 "failed (errno=%d, %s)\n", errno, strerror(errno)); |
|
| 839 e->type = GG_EVENT_DCC_ERROR; |
|
| 840 e->event.dcc_error = GG_ERROR_DCC_NET; |
|
| 841 return e; |
|
| 842 } |
|
| 843 |
|
| 844 gg_dcc_debug_data("read", h->fd, |
|
| 845 h->chunk_buf + h->chunk_offset, |
|
| 846 h->chunk_size - h->chunk_offset); |
|
| 847 |
|
| 848 h->chunk_offset += tmp; |
|
| 849 |
|
| 850 if (h->chunk_offset < h->chunk_size) |
|
| 851 return e; |
|
| 852 |
|
| 853 memcpy(&big_pkt, h->chunk_buf, sizeof(big_pkt)); |
|
| 854 free(h->chunk_buf); |
|
| 855 h->chunk_buf = NULL; |
|
| 856 |
|
| 857 big_pkt.type = gg_fix32(big_pkt.type); |
|
| 858 h->chunk_size = gg_fix32(big_pkt.dunno1); |
|
| 859 h->chunk_offset = 0; |
|
| 860 |
|
| 861 if (big_pkt.type == 0x0005) { /* XXX */ |
|
| 862 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n"); |
|
| 863 e->type = GG_EVENT_DCC_ERROR; |
|
| 864 e->event.dcc_error = GG_ERROR_DCC_REFUSED; |
|
| 865 return e; |
|
| 866 } |
|
| 867 |
|
| 868 if (h->chunk_size == 0) { |
|
| 869 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n"); |
|
| 870 e->type = GG_EVENT_DCC_DONE; |
|
| 871 return e; |
|
| 872 } |
|
| 873 |
|
| 874 h->state = GG_STATE_GETTING_FILE; |
|
| 875 h->check = GG_CHECK_READ; |
|
| 876 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 877 h->established = 1; |
|
| 878 |
|
| 879 return e; |
|
| 880 |
|
| 881 case GG_STATE_READING_VOICE_HEADER: |
|
| 882 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n"); |
|
| 883 |
|
| 884 gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt)); |
|
| 885 |
|
| 886 switch (tiny_pkt.type) { |
|
| 887 case 0x03: /* XXX */ |
|
| 888 h->state = GG_STATE_READING_VOICE_SIZE; |
|
| 889 h->check = GG_CHECK_READ; |
|
| 890 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 891 h->established = 1; |
|
| 892 break; |
|
| 893 case 0x04: /* XXX */ |
|
| 894 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() " |
|
| 895 "peer breaking connection\n"); |
|
| 896 /* XXX zwracać odpowiedni event */ |
|
| 897 default: |
|
| 898 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() " |
|
| 899 "unknown request (%.2x)\n", tiny_pkt.type); |
|
| 900 e->type = GG_EVENT_DCC_ERROR; |
|
| 901 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; |
|
| 902 } |
|
| 903 |
|
| 904 return e; |
|
| 905 |
|
| 906 case GG_STATE_READING_VOICE_SIZE: |
|
| 907 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n"); |
|
| 908 |
|
| 909 gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt)); |
|
| 910 |
|
| 911 small_pkt.type = gg_fix32(small_pkt.type); |
|
| 912 |
|
| 913 if (small_pkt.type < 16 || small_pkt.type > sizeof(buf)) { |
|
| 914 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() " |
|
| 915 "invalid voice frame size (%d)\n", small_pkt.type); |
|
| 916 e->type = GG_EVENT_DCC_ERROR; |
|
| 917 e->event.dcc_error = GG_ERROR_DCC_NET; |
|
| 918 |
|
| 919 return e; |
|
| 920 } |
|
| 921 |
|
| 922 h->chunk_size = small_pkt.type; |
|
| 923 h->chunk_offset = 0; |
|
| 924 |
|
| 925 if (!(h->voice_buf = malloc(h->chunk_size))) { |
|
| 926 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n"); |
|
| 927 free(e); |
|
| 928 return NULL; |
|
| 929 } |
|
| 930 |
|
| 931 h->state = GG_STATE_READING_VOICE_DATA; |
|
| 932 h->check = GG_CHECK_READ; |
|
| 933 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 934 |
|
| 935 return e; |
|
| 936 |
|
| 937 case GG_STATE_READING_VOICE_DATA: |
|
| 938 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n"); |
|
| 939 |
|
| 940 tmp = recv(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0); |
|
| 941 if (tmp < 1) { |
|
| 942 if (tmp == -1) { |
|
| 943 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() " |
|
| 944 "recv() failed (errno=%d, %s)\n", |
|
| 945 errno, strerror(errno)); |
|
| 946 } else { |
|
| 947 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() " |
|
| 948 "recv() failed, connection broken\n"); |
|
| 949 } |
|
| 950 e->type = GG_EVENT_DCC_ERROR; |
|
| 951 e->event.dcc_error = GG_ERROR_DCC_NET; |
|
| 952 return e; |
|
| 953 } |
|
| 954 |
|
| 955 gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp); |
|
| 956 |
|
| 957 h->chunk_offset += tmp; |
|
| 958 |
|
| 959 if (h->chunk_offset >= h->chunk_size) { |
|
| 960 e->type = GG_EVENT_DCC_VOICE_DATA; |
|
| 961 e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf; |
|
| 962 e->event.dcc_voice_data.length = h->chunk_size; |
|
| 963 h->state = GG_STATE_READING_VOICE_HEADER; |
|
| 964 h->voice_buf = NULL; |
|
| 965 } |
|
| 966 |
|
| 967 h->check = GG_CHECK_READ; |
|
| 968 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 969 |
|
| 970 return e; |
|
| 971 |
|
| 972 case GG_STATE_CONNECTING: |
|
| 973 { |
|
| 974 uin_t uins[2]; |
|
| 975 |
|
| 976 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n"); |
|
| 977 |
|
| 978 res = 0; |
|
| 979 if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) { |
|
| 980 gg_debug(GG_DEBUG_MISC, |
|
| 981 "// gg_dcc_watch_fd() connection failed " |
|
| 982 "(fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", |
|
| 983 h->fd, errno, strerror(errno), foo, res, strerror(res)); |
|
| 984 e->type = GG_EVENT_DCC_ERROR; |
|
| 985 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; |
|
| 986 return e; |
|
| 987 } |
|
| 988 |
|
| 989 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n"); |
|
| 990 |
|
| 991 uins[0] = gg_fix32(h->uin); |
|
| 992 uins[1] = gg_fix32(h->peer_uin); |
|
| 993 |
|
| 994 gg_dcc_write(h->fd, uins, sizeof(uins)); |
|
| 995 |
|
| 996 h->state = GG_STATE_READING_ACK; |
|
| 997 h->check = GG_CHECK_READ; |
|
| 998 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 999 |
|
| 1000 return e; |
|
| 1001 } |
|
| 1002 |
|
| 1003 case GG_STATE_READING_ACK: |
|
| 1004 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n"); |
|
| 1005 |
|
| 1006 gg_dcc_read(h->fd, buf, (size_t)4); |
|
| 1007 |
|
| 1008 if (strncmp(buf, ack, 4)) { |
|
| 1009 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n"); |
|
| 1010 |
|
| 1011 e->type = GG_EVENT_DCC_ERROR; |
|
| 1012 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; |
|
| 1013 return e; |
|
| 1014 } |
|
| 1015 |
|
| 1016 h->check = GG_CHECK_WRITE; |
|
| 1017 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1018 h->state = GG_STATE_SENDING_REQUEST; |
|
| 1019 |
|
| 1020 return e; |
|
| 1021 |
|
| 1022 case GG_STATE_SENDING_VOICE_REQUEST: |
|
| 1023 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n"); |
|
| 1024 |
|
| 1025 small_pkt.type = gg_fix32(0x0003); |
|
| 1026 |
|
| 1027 gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); |
|
| 1028 |
|
| 1029 h->state = GG_STATE_READING_VOICE_ACK; |
|
| 1030 h->check = GG_CHECK_READ; |
|
| 1031 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1032 |
|
| 1033 return e; |
|
| 1034 |
|
| 1035 case GG_STATE_SENDING_REQUEST: |
|
| 1036 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n"); |
|
| 1037 |
|
| 1038 small_pkt.type = (h->type == GG_SESSION_DCC_GET) ? |
|
| 1039 gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */ |
|
| 1040 |
|
| 1041 gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); |
|
| 1042 |
|
| 1043 switch (h->type) { |
|
| 1044 case GG_SESSION_DCC_GET: |
|
| 1045 h->state = GG_STATE_READING_REQUEST; |
|
| 1046 h->check = GG_CHECK_READ; |
|
| 1047 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1048 break; |
|
| 1049 |
|
| 1050 case GG_SESSION_DCC_SEND: |
|
| 1051 h->state = GG_STATE_SENDING_FILE_INFO; |
|
| 1052 h->check = GG_CHECK_WRITE; |
|
| 1053 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1054 |
|
| 1055 if (h->file_fd == -1) |
|
| 1056 e->type = GG_EVENT_DCC_NEED_FILE_INFO; |
|
| 1057 break; |
|
| 1058 |
|
| 1059 case GG_SESSION_DCC_VOICE: |
|
| 1060 h->state = GG_STATE_SENDING_VOICE_REQUEST; |
|
| 1061 h->check = GG_CHECK_WRITE; |
|
| 1062 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1063 break; |
|
| 1064 } |
|
| 1065 |
|
| 1066 return e; |
|
| 1067 |
|
| 1068 case GG_STATE_SENDING_FILE_INFO: |
|
| 1069 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n"); |
|
| 1070 |
|
| 1071 if (h->file_fd == -1) { |
|
| 1072 e->type = GG_EVENT_DCC_NEED_FILE_INFO; |
|
| 1073 return e; |
|
| 1074 } |
|
| 1075 |
|
| 1076 small_pkt.type = gg_fix32(0x0001); /* XXX */ |
|
| 1077 |
|
| 1078 gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt)); |
|
| 1079 |
|
| 1080 file_info_packet.big.type = gg_fix32(0x0003); /* XXX */ |
|
| 1081 file_info_packet.big.dunno1 = 0; |
|
| 1082 file_info_packet.big.dunno2 = 0; |
|
| 1083 |
|
| 1084 memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info)); |
|
| 1085 |
|
| 1086 /* zostają teraz u nas, więc odwracamy z powrotem */ |
|
| 1087 h->file_info.size = gg_fix32(h->file_info.size); |
|
| 1088 h->file_info.mode = gg_fix32(h->file_info.mode); |
|
| 1089 |
|
| 1090 gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet)); |
|
| 1091 |
|
| 1092 h->state = GG_STATE_READING_FILE_ACK; |
|
| 1093 h->check = GG_CHECK_READ; |
|
| 1094 h->timeout = GG_DCC_TIMEOUT_FILE_ACK; |
|
| 1095 |
|
| 1096 return e; |
|
| 1097 |
|
| 1098 case GG_STATE_READING_FILE_ACK: |
|
| 1099 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n"); |
|
| 1100 |
|
| 1101 gg_dcc_read(h->fd, &big_pkt, sizeof(big_pkt)); |
|
| 1102 |
|
| 1103 /* XXX sprawdzać wynik */ |
|
| 1104 h->offset = gg_fix32(big_pkt.dunno1); |
|
| 1105 |
|
| 1106 h->state = GG_STATE_SENDING_FILE_HEADER; |
|
| 1107 h->check = GG_CHECK_WRITE; |
|
| 1108 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1109 |
|
| 1110 e->type = GG_EVENT_DCC_ACK; |
|
| 1111 |
|
| 1112 return e; |
|
| 1113 |
|
| 1114 case GG_STATE_READING_VOICE_ACK: |
|
| 1115 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n"); |
|
| 1116 |
|
| 1117 gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt)); |
|
| 1118 |
|
| 1119 if (tiny_pkt.type != 0x01) { |
|
| 1120 gg_debug(GG_DEBUG_MISC, "// invalid " |
|
| 1121 "reply (%.2x), connection " |
|
| 1122 "refused\n", tiny_pkt.type); |
|
| 1123 e->type = GG_EVENT_DCC_ERROR; |
|
| 1124 e->event.dcc_error = GG_ERROR_DCC_REFUSED; |
|
| 1125 return e; |
|
| 1126 } |
|
| 1127 |
|
| 1128 h->state = GG_STATE_READING_VOICE_HEADER; |
|
| 1129 h->check = GG_CHECK_READ; |
|
| 1130 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1131 |
|
| 1132 e->type = GG_EVENT_DCC_ACK; |
|
| 1133 |
|
| 1134 return e; |
|
| 1135 |
|
| 1136 case GG_STATE_SENDING_FILE_HEADER: |
|
| 1137 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n"); |
|
| 1138 |
|
| 1139 h->chunk_offset = 0; |
|
| 1140 |
|
| 1141 if ((h->chunk_size = h->file_info.size - h->offset) > 4096) { |
|
| 1142 h->chunk_size = 4096; |
|
| 1143 big_pkt.type = gg_fix32(0x0003); /* XXX */ |
|
| 1144 } else |
|
| 1145 big_pkt.type = gg_fix32(0x0002); /* XXX */ |
|
| 1146 |
|
| 1147 big_pkt.dunno1 = gg_fix32(h->chunk_size); |
|
| 1148 big_pkt.dunno2 = 0; |
|
| 1149 |
|
| 1150 gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt)); |
|
| 1151 |
|
| 1152 h->state = GG_STATE_SENDING_FILE; |
|
| 1153 h->check = GG_CHECK_WRITE; |
|
| 1154 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1155 h->established = 1; |
|
| 1156 |
|
| 1157 return e; |
|
| 1158 |
|
| 1159 case GG_STATE_SENDING_FILE: |
|
| 1160 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n"); |
|
| 1161 |
|
| 1162 if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) |
|
| 1163 utmp = sizeof(buf); |
|
| 1164 |
|
| 1165 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() " |
|
| 1166 "offset=%d, size=%d\n", |
|
| 1167 h->offset, h->file_info.size); |
|
| 1168 |
|
| 1169 /* koniec pliku? */ |
|
| 1170 if (h->file_info.size == 0) { |
|
| 1171 gg_debug(GG_DEBUG_MISC, |
|
| 1172 "// gg_dcc_watch_fd() read()" |
|
| 1173 "reached eof on empty file\n"); |
|
| 1174 e->type = GG_EVENT_DCC_DONE; |
|
| 1175 |
|
| 1176 return e; |
|
| 1177 } |
|
| 1178 |
|
| 1179 if (h->offset >= h->file_info.size) { |
|
| 1180 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n"); |
|
| 1181 e->type = GG_EVENT_DCC_DONE; |
|
| 1182 return e; |
|
| 1183 } |
|
| 1184 |
|
| 1185 if (lseek(h->file_fd, h->offset, SEEK_SET) != (off_t)h->offset) { |
|
| 1186 gg_debug(GG_DEBUG_MISC, |
|
| 1187 "// gg_dcc_watch_fd() lseek() " |
|
| 1188 "failed. (errno=%d, %s)\n", |
|
| 1189 errno, strerror(errno)); |
|
| 1190 |
|
| 1191 e->type = GG_EVENT_DCC_ERROR; |
|
| 1192 e->event.dcc_error = GG_ERROR_DCC_FILE; |
|
| 1193 |
|
| 1194 return e; |
|
| 1195 } |
|
| 1196 |
|
| 1197 size = read(h->file_fd, buf, utmp); |
|
| 1198 |
|
| 1199 /* błąd */ |
|
| 1200 if (size == -1) { |
|
| 1201 gg_debug(GG_DEBUG_MISC, |
|
| 1202 "// gg_dcc_watch_fd() read() " |
|
| 1203 "failed. (errno=%d, %s)\n", |
|
| 1204 errno, strerror(errno)); |
|
| 1205 |
|
| 1206 e->type = GG_EVENT_DCC_ERROR; |
|
| 1207 e->event.dcc_error = GG_ERROR_DCC_FILE; |
|
| 1208 |
|
| 1209 return e; |
|
| 1210 } |
|
| 1211 |
|
| 1212 /* koniec pliku? */ |
|
| 1213 if (size == 0) { |
|
| 1214 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); |
|
| 1215 e->type = GG_EVENT_DCC_ERROR; |
|
| 1216 e->event.dcc_error = GG_ERROR_DCC_EOF; |
|
| 1217 |
|
| 1218 return e; |
|
| 1219 } |
|
| 1220 |
|
| 1221 /* jeśli wczytaliśmy więcej, utnijmy. */ |
|
| 1222 if (h->offset + size > h->file_info.size) { |
|
| 1223 gg_debug(GG_DEBUG_MISC, |
|
| 1224 "// gg_dcc_watch_fd() read() " |
|
| 1225 "too much (read=%d, ofs=%d, " |
|
| 1226 "size=%d)\n", size, h->offset, |
|
| 1227 h->file_info.size); |
|
| 1228 size = h->file_info.size - h->offset; |
|
| 1229 |
|
| 1230 if (size < 1) { |
|
| 1231 gg_debug(GG_DEBUG_MISC, |
|
| 1232 "// gg_dcc_watch_fd() " |
|
| 1233 "reached EOF after cutting\n"); |
|
| 1234 e->type = GG_EVENT_DCC_DONE; |
|
| 1235 return e; |
|
| 1236 } |
|
| 1237 } |
|
| 1238 |
|
| 1239 tmp = send(h->fd, buf, size, 0); |
|
| 1240 |
|
| 1241 if (tmp == -1) { |
|
| 1242 gg_debug(GG_DEBUG_MISC, |
|
| 1243 "// gg_dcc_watch_fd() send() " |
|
| 1244 "failed (%s)\n", strerror(errno)); |
|
| 1245 e->type = GG_EVENT_DCC_ERROR; |
|
| 1246 e->event.dcc_error = GG_ERROR_DCC_NET; |
|
| 1247 return e; |
|
| 1248 } |
|
| 1249 |
|
| 1250 if (tmp == 0) { |
|
| 1251 gg_debug(GG_DEBUG_MISC, |
|
| 1252 "// gg_dcc_watch_fd() send() " |
|
| 1253 "failed (connection reset)\n"); |
|
| 1254 e->type = GG_EVENT_DCC_ERROR; |
|
| 1255 e->event.dcc_error = GG_ERROR_DCC_NET; |
|
| 1256 return e; |
|
| 1257 } |
|
| 1258 |
|
| 1259 h->offset += tmp; |
|
| 1260 |
|
| 1261 if (h->offset >= h->file_info.size) { |
|
| 1262 e->type = GG_EVENT_DCC_DONE; |
|
| 1263 return e; |
|
| 1264 } |
|
| 1265 |
|
| 1266 h->chunk_offset += tmp; |
|
| 1267 |
|
| 1268 if (h->chunk_offset >= h->chunk_size) { |
|
| 1269 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); |
|
| 1270 h->state = GG_STATE_SENDING_FILE_HEADER; |
|
| 1271 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1272 } else { |
|
| 1273 h->state = GG_STATE_SENDING_FILE; |
|
| 1274 h->timeout = GG_DCC_TIMEOUT_SEND; |
|
| 1275 } |
|
| 1276 |
|
| 1277 h->check = GG_CHECK_WRITE; |
|
| 1278 |
|
| 1279 return e; |
|
| 1280 |
|
| 1281 case GG_STATE_GETTING_FILE: |
|
| 1282 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n"); |
|
| 1283 |
|
| 1284 if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) |
|
| 1285 utmp = sizeof(buf); |
|
| 1286 |
|
| 1287 if (h->offset >= h->file_info.size) { |
|
| 1288 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n"); |
|
| 1289 e->type = GG_EVENT_DCC_DONE; |
|
| 1290 return e; |
|
| 1291 } |
|
| 1292 |
|
| 1293 size = recv(h->fd, buf, utmp, 0); |
|
| 1294 |
|
| 1295 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() " |
|
| 1296 "ofs=%d, size=%d, recv()=%d\n", |
|
| 1297 h->offset, h->file_info.size, size); |
|
| 1298 |
|
| 1299 /* błąd */ |
|
| 1300 if (size == -1) { |
|
| 1301 gg_debug(GG_DEBUG_MISC, |
|
| 1302 "// gg_dcc_watch_fd() recv() " |
|
| 1303 "failed. (errno=%d, %s)\n", |
|
| 1304 errno, strerror(errno)); |
|
| 1305 |
|
| 1306 e->type = GG_EVENT_DCC_ERROR; |
|
| 1307 e->event.dcc_error = GG_ERROR_DCC_NET; |
|
| 1308 |
|
| 1309 return e; |
|
| 1310 } |
|
| 1311 |
|
| 1312 /* koniec? */ |
|
| 1313 if (size == 0) { |
|
| 1314 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() reached eof\n"); |
|
| 1315 e->type = GG_EVENT_DCC_ERROR; |
|
| 1316 e->event.dcc_error = GG_ERROR_DCC_EOF; |
|
| 1317 |
|
| 1318 return e; |
|
| 1319 } |
|
| 1320 |
|
| 1321 tmp = write(h->file_fd, buf, size); |
|
| 1322 |
|
| 1323 if (tmp == -1 || tmp < size) { |
|
| 1324 gg_debug(GG_DEBUG_MISC, |
|
| 1325 "// gg_dcc_watch_fd() write() " |
|
| 1326 "failed (%d:fd=%d:res=%d:%s)\n", |
|
| 1327 tmp, h->file_fd, size, |
|
| 1328 strerror(errno)); |
|
| 1329 e->type = GG_EVENT_DCC_ERROR; |
|
| 1330 e->event.dcc_error = GG_ERROR_DCC_NET; |
|
| 1331 return e; |
|
| 1332 } |
|
| 1333 |
|
| 1334 h->offset += size; |
|
| 1335 |
|
| 1336 if (h->offset >= h->file_info.size) { |
|
| 1337 e->type = GG_EVENT_DCC_DONE; |
|
| 1338 return e; |
|
| 1339 } |
|
| 1340 |
|
| 1341 h->chunk_offset += size; |
|
| 1342 |
|
| 1343 if (h->chunk_offset >= h->chunk_size) { |
|
| 1344 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); |
|
| 1345 h->state = GG_STATE_READING_FILE_HEADER; |
|
| 1346 h->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1347 h->chunk_offset = 0; |
|
| 1348 h->chunk_size = sizeof(big_pkt); |
|
| 1349 h->chunk_buf = NULL; |
|
| 1350 tmp_buf = malloc(sizeof(big_pkt)); |
|
| 1351 if (!tmp_buf) { |
|
| 1352 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); |
|
| 1353 free(e); |
|
| 1354 return NULL; |
|
| 1355 } |
|
| 1356 h->chunk_buf = tmp_buf; |
|
| 1357 } else { |
|
| 1358 h->state = GG_STATE_GETTING_FILE; |
|
| 1359 h->timeout = GG_DCC_TIMEOUT_GET; |
|
| 1360 } |
|
| 1361 |
|
| 1362 h->check = GG_CHECK_READ; |
|
| 1363 |
|
| 1364 return e; |
|
| 1365 |
|
| 1366 default: |
|
| 1367 gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n"); |
|
| 1368 e->type = GG_EVENT_DCC_ERROR; |
|
| 1369 e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; |
|
| 1370 |
|
| 1371 return e; |
|
| 1372 } |
|
| 1373 } |
|
| 1374 |
|
| 1375 return e; |
|
| 1376 } |
|
| 1377 |
|
| 1378 /** |
|
| 1379 * Zwalnia zasoby używane przez połączenie bezpośrednie. |
|
| 1380 * |
|
| 1381 * \param d Struktura połączenia |
|
| 1382 * |
|
| 1383 * \ingroup dcc6 |
|
| 1384 */ |
|
| 1385 void gg_dcc_free(struct gg_dcc *d) |
|
| 1386 { |
|
| 1387 gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d); |
|
| 1388 |
|
| 1389 if (!d) |
|
| 1390 return; |
|
| 1391 |
|
| 1392 if (d->fd != -1) |
|
| 1393 close(d->fd); |
|
| 1394 |
|
| 1395 if (d->file_fd != -1) |
|
| 1396 gg_file_close(d->file_fd); |
|
| 1397 |
|
| 1398 free(d->chunk_buf); |
|
| 1399 free(d); |
|
| 1400 } |
|
| 1401 |
|
| 1402 /* |
|
| 1403 * Local variables: |
|
| 1404 * c-indentation-style: k&r |
|
| 1405 * c-basic-offset: 8 |
|
| 1406 * indent-tabs-mode: notnil |
|
| 1407 * End: |
|
| 1408 * |
|
| 1409 * vim: shiftwidth=8: |
|
| 1410 */ |
|