Fri, 09 Nov 2007 08:48:40 +0000
applied changes from 32f31e981f0618a4167aa98bcc22c2dab13d1550
through a9f75de97d6cdf8fe8bf091b95def6c648aac82f
applied changes from a9f75de97d6cdf8fe8bf091b95def6c648aac82f
through 7c9f2e0cc4967a3eaade95d32f164349b6d1aa03
applied changes from a9f75de97d6cdf8fe8bf091b95def6c648aac82f
through fc4350a15fdd1f51b4496568afaa83355e18b714
applied changes from fc4350a15fdd1f51b4496568afaa83355e18b714
through 18ccd2ba2c1c9b7fa3dfedf72b48b3bd01c3a7c4
applied changes from a9f75de97d6cdf8fe8bf091b95def6c648aac82f
through 20236f54c97e87512b7eb716559a4bd86b73f833
applied changes from 868a040ee69c6e45b9132e7254a3f523e55385b2
through 0e154355bb3e8bdaeb793b142075b60671b37a48
applied changes from 329395b9793793f35bcf231033c1eb942513ab01
through 9d8120be512c235d76a8f6fee60cae024da8772e
|
14253
b63ebf84c42b
This is a hand-crafted commit to migrate across subversion revisions
Ethan Blanton <elb@pidgin.im>
parents:
12323
diff
changeset
|
1 | /* $Id: dcc.c 16856 2006-08-19 01:13:25Z evands $ */ |
| 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 | |
|
19859
71d37b57eff2
The FSF changed its address a while ago; our files were out of date.
John Bailey <rekkanoryo@rekkanoryo.org>
parents:
15435
diff
changeset
|
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, |
| 11360 | 19 | * USA. |
| 20 | */ | |
| 21 | ||
| 22 | #include <sys/types.h> | |
| 23 | #include <sys/stat.h> | |
|
11546
acb5676e57bb
[gaim-migrate @ 13801]
Daniel Atallah <datallah@pidgin.im>
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
acb5676e57bb
[gaim-migrate @ 13801]
Daniel Atallah <datallah@pidgin.im>
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
e65f13592888
[gaim-migrate @ 14520]
Richard Laager <rlaager@pidgin.im>
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
f52908fb23b0
[gaim-migrate @ 14627]
Richard Laager <rlaager@pidgin.im>
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
e65f13592888
[gaim-migrate @ 14520]
Richard Laager <rlaager@pidgin.im>
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
e65f13592888
[gaim-migrate @ 14520]
Richard Laager <rlaager@pidgin.im>
parents:
11546
diff
changeset
|
571 | int fd; |
|
e65f13592888
[gaim-migrate @ 14520]
Richard Laager <rlaager@pidgin.im>
parents:
11546
diff
changeset
|
572 | socklen_t sin_len = sizeof(sin); |
|
e65f13592888
[gaim-migrate @ 14520]
Richard Laager <rlaager@pidgin.im>
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
e65f13592888
[gaim-migrate @ 14520]
Richard Laager <rlaager@pidgin.im>
parents:
11546
diff
changeset
|
619 | int size, tmp, res; |
|
e65f13592888
[gaim-migrate @ 14520]
Richard Laager <rlaager@pidgin.im>
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 | */ |