| 1 /* $Id: libgadu.c 14520 2005-11-25 00:32:45Z rlaager $ */ |
|
| 2 |
|
| 3 /* |
|
| 4 * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> |
|
| 5 * Robert J. Woźny <speedy@ziew.org> |
|
| 6 * Arkadiusz Miśkiewicz <arekm@pld-linux.org> |
|
| 7 * Tomasz Chiliński <chilek@chilan.com> |
|
| 8 * |
|
| 9 * This program is free software; you can redistribute it and/or modify |
|
| 10 * it under the terms of the GNU Lesser General Public License Version |
|
| 11 * 2.1 as published by the Free Software Foundation. |
|
| 12 * |
|
| 13 * This program is distributed in the hope that it will be useful, |
|
| 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 16 * GNU Lesser General Public License for more details. |
|
| 17 * |
|
| 18 * You should have received a copy of the GNU Lesser General Public |
|
| 19 * License along with this program; if not, write to the Free Software |
|
| 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, |
|
| 21 * USA. |
|
| 22 */ |
|
| 23 |
|
| 24 #include <sys/types.h> |
|
| 25 #ifndef _WIN32 |
|
| 26 #include <sys/wait.h> |
|
| 27 #include <sys/socket.h> |
|
| 28 #include <netinet/in.h> |
|
| 29 #include <arpa/inet.h> |
|
| 30 #ifdef sun |
|
| 31 # include <sys/filio.h> |
|
| 32 #endif |
|
| 33 #else |
|
| 34 #include <io.h> |
|
| 35 #include <fcntl.h> |
|
| 36 #include <errno.h> |
|
| 37 #define SHUT_RDWR SD_BOTH |
|
| 38 #endif |
|
| 39 |
|
| 40 #include "libgadu-config.h" |
|
| 41 |
|
| 42 #include <errno.h> |
|
| 43 #ifndef _WIN32 |
|
| 44 #include <netdb.h> |
|
| 45 #endif |
|
| 46 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
|
| 47 # include <pthread.h> |
|
| 48 #endif |
|
| 49 #include <stdarg.h> |
|
| 50 #include <stdio.h> |
|
| 51 #include <stdlib.h> |
|
| 52 #include <string.h> |
|
| 53 #include <unistd.h> |
|
| 54 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
|
| 55 # include <openssl/err.h> |
|
| 56 # include <openssl/rand.h> |
|
| 57 #endif |
|
| 58 |
|
| 59 #include "compat.h" |
|
| 60 #include "libgadu.h" |
|
| 61 |
|
| 62 int gg_debug_level = 0; |
|
| 63 void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; |
|
| 64 |
|
| 65 int gg_dcc_port = 0; |
|
| 66 unsigned long gg_dcc_ip = 0; |
|
| 67 |
|
| 68 unsigned long gg_local_ip = 0; |
|
| 69 /* |
|
| 70 * zmienne opisujące parametry proxy http. |
|
| 71 */ |
|
| 72 char *gg_proxy_host = NULL; |
|
| 73 int gg_proxy_port = 0; |
|
| 74 int gg_proxy_enabled = 0; |
|
| 75 int gg_proxy_http_only = 0; |
|
| 76 char *gg_proxy_username = NULL; |
|
| 77 char *gg_proxy_password = NULL; |
|
| 78 |
|
| 79 #ifndef lint |
|
| 80 static char rcsid[] |
|
| 81 #ifdef __GNUC__ |
|
| 82 __attribute__ ((unused)) |
|
| 83 #endif |
|
| 84 = "$Id: libgadu.c 14520 2005-11-25 00:32:45Z rlaager $"; |
|
| 85 #endif |
|
| 86 |
|
| 87 #ifdef _WIN32 |
|
| 88 /** |
|
| 89 * Deal with the fact that you can't select() on a win32 file fd. |
|
| 90 * This makes it practically impossible to tie into gaim's event loop. |
|
| 91 * |
|
| 92 * -This is thanks to Tor Lillqvist. |
|
| 93 * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. |
|
| 94 */ |
|
| 95 static int |
|
| 96 socket_pipe (int *fds) |
|
| 97 { |
|
| 98 SOCKET temp, socket1 = -1, socket2 = -1; |
|
| 99 struct sockaddr_in saddr; |
|
| 100 int len; |
|
| 101 u_long arg; |
|
| 102 fd_set read_set, write_set; |
|
| 103 struct timeval tv; |
|
| 104 |
|
| 105 temp = socket(AF_INET, SOCK_STREAM, 0); |
|
| 106 |
|
| 107 if (temp == INVALID_SOCKET) { |
|
| 108 goto out0; |
|
| 109 } |
|
| 110 |
|
| 111 arg = 1; |
|
| 112 if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { |
|
| 113 goto out0; |
|
| 114 } |
|
| 115 |
|
| 116 memset(&saddr, 0, sizeof(saddr)); |
|
| 117 saddr.sin_family = AF_INET; |
|
| 118 saddr.sin_port = 0; |
|
| 119 saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
|
| 120 |
|
| 121 if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { |
|
| 122 goto out0; |
|
| 123 } |
|
| 124 |
|
| 125 if (listen(temp, 1) == SOCKET_ERROR) { |
|
| 126 goto out0; |
|
| 127 } |
|
| 128 |
|
| 129 len = sizeof(saddr); |
|
| 130 if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { |
|
| 131 goto out0; |
|
| 132 } |
|
| 133 |
|
| 134 socket1 = socket(AF_INET, SOCK_STREAM, 0); |
|
| 135 |
|
| 136 if (socket1 == INVALID_SOCKET) { |
|
| 137 goto out0; |
|
| 138 } |
|
| 139 |
|
| 140 arg = 1; |
|
| 141 if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { |
|
| 142 goto out1; |
|
| 143 } |
|
| 144 |
|
| 145 if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || |
|
| 146 WSAGetLastError() != WSAEWOULDBLOCK) { |
|
| 147 goto out1; |
|
| 148 } |
|
| 149 |
|
| 150 FD_ZERO(&read_set); |
|
| 151 FD_SET(temp, &read_set); |
|
| 152 |
|
| 153 tv.tv_sec = 0; |
|
| 154 tv.tv_usec = 0; |
|
| 155 |
|
| 156 if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { |
|
| 157 goto out1; |
|
| 158 } |
|
| 159 |
|
| 160 if (!FD_ISSET(temp, &read_set)) { |
|
| 161 goto out1; |
|
| 162 } |
|
| 163 |
|
| 164 socket2 = accept(temp, (struct sockaddr *) &saddr, &len); |
|
| 165 if (socket2 == INVALID_SOCKET) { |
|
| 166 goto out1; |
|
| 167 } |
|
| 168 |
|
| 169 FD_ZERO(&write_set); |
|
| 170 FD_SET(socket1, &write_set); |
|
| 171 |
|
| 172 tv.tv_sec = 0; |
|
| 173 tv.tv_usec = 0; |
|
| 174 |
|
| 175 if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { |
|
| 176 goto out2; |
|
| 177 } |
|
| 178 |
|
| 179 if (!FD_ISSET(socket1, &write_set)) { |
|
| 180 goto out2; |
|
| 181 } |
|
| 182 |
|
| 183 arg = 0; |
|
| 184 if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { |
|
| 185 goto out2; |
|
| 186 } |
|
| 187 |
|
| 188 arg = 0; |
|
| 189 if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { |
|
| 190 goto out2; |
|
| 191 } |
|
| 192 |
|
| 193 fds[0] = socket1; |
|
| 194 fds[1] = socket2; |
|
| 195 |
|
| 196 closesocket (temp); |
|
| 197 |
|
| 198 return 0; |
|
| 199 |
|
| 200 out2: |
|
| 201 closesocket (socket2); |
|
| 202 out1: |
|
| 203 closesocket (socket1); |
|
| 204 out0: |
|
| 205 closesocket (temp); |
|
| 206 errno = EIO; /* XXX */ |
|
| 207 |
|
| 208 return -1; |
|
| 209 } |
|
| 210 #endif |
|
| 211 |
|
| 212 /* |
|
| 213 * gg_libgadu_version() |
|
| 214 * |
|
| 215 * zwraca wersję libgadu. |
|
| 216 * |
|
| 217 * - brak |
|
| 218 * |
|
| 219 * wersja libgadu. |
|
| 220 */ |
|
| 221 const char *gg_libgadu_version() |
|
| 222 { |
|
| 223 return GG_LIBGADU_VERSION; |
|
| 224 } |
|
| 225 |
|
| 226 /* |
|
| 227 * gg_fix32() |
|
| 228 * |
|
| 229 * zamienia kolejność bajtów w liczbie 32-bitowej tak, by odpowiadała |
|
| 230 * kolejności bajtów w protokole GG. ze względu na LE-owość serwera, |
|
| 231 * zamienia tylko na maszynach BE-wych. |
|
| 232 * |
|
| 233 * - x - liczba do zamiany |
|
| 234 * |
|
| 235 * liczba z odpowiednią kolejnością bajtów. |
|
| 236 */ |
|
| 237 uint32_t gg_fix32(uint32_t x) |
|
| 238 { |
|
| 239 #ifndef __GG_LIBGADU_BIGENDIAN |
|
| 240 return x; |
|
| 241 #else |
|
| 242 return (uint32_t) |
|
| 243 (((x & (uint32_t) 0x000000ffU) << 24) | |
|
| 244 ((x & (uint32_t) 0x0000ff00U) << 8) | |
|
| 245 ((x & (uint32_t) 0x00ff0000U) >> 8) | |
|
| 246 ((x & (uint32_t) 0xff000000U) >> 24)); |
|
| 247 #endif |
|
| 248 } |
|
| 249 |
|
| 250 /* |
|
| 251 * gg_fix16() |
|
| 252 * |
|
| 253 * zamienia kolejność bajtów w liczbie 16-bitowej tak, by odpowiadała |
|
| 254 * kolejności bajtów w protokole GG. ze względu na LE-owość serwera, |
|
| 255 * zamienia tylko na maszynach BE-wych. |
|
| 256 * |
|
| 257 * - x - liczba do zamiany |
|
| 258 * |
|
| 259 * liczba z odpowiednią kolejnością bajtów. |
|
| 260 */ |
|
| 261 uint16_t gg_fix16(uint16_t x) |
|
| 262 { |
|
| 263 #ifndef __GG_LIBGADU_BIGENDIAN |
|
| 264 return x; |
|
| 265 #else |
|
| 266 return (uint16_t) |
|
| 267 (((x & (uint16_t) 0x00ffU) << 8) | |
|
| 268 ((x & (uint16_t) 0xff00U) >> 8)); |
|
| 269 #endif |
|
| 270 } |
|
| 271 |
|
| 272 /* |
|
| 273 * gg_login_hash() // funkcja wewnętrzna |
|
| 274 * |
|
| 275 * liczy hash z hasła i danego seeda. |
|
| 276 * |
|
| 277 * - password - hasło do hashowania |
|
| 278 * - seed - wartość podana przez serwer |
|
| 279 * |
|
| 280 * hash. |
|
| 281 */ |
|
| 282 unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) |
|
| 283 { |
|
| 284 unsigned int x, y, z; |
|
| 285 |
|
| 286 y = seed; |
|
| 287 |
|
| 288 for (x = 0; *password; password++) { |
|
| 289 x = (x & 0xffffff00) | *password; |
|
| 290 y ^= x; |
|
| 291 y += x; |
|
| 292 x <<= 8; |
|
| 293 y ^= x; |
|
| 294 x <<= 8; |
|
| 295 y -= x; |
|
| 296 x <<= 8; |
|
| 297 y ^= x; |
|
| 298 |
|
| 299 z = y & 0x1F; |
|
| 300 y = (y << z) | (y >> (32 - z)); |
|
| 301 } |
|
| 302 |
|
| 303 return y; |
|
| 304 } |
|
| 305 |
|
| 306 #ifndef _WIN32 |
|
| 307 |
|
| 308 /* |
|
| 309 * gg_resolve() // funkcja wewnętrzna |
|
| 310 * |
|
| 311 * tworzy potok, forkuje się i w drugim procesie zaczyna resolvować |
|
| 312 * podanego hosta. zapisuje w sesji deskryptor potoku. jeśli coś tam |
|
| 313 * będzie gotowego, znaczy, że można wczytać struct in_addr. jeśli |
|
| 314 * nie znajdzie, zwraca INADDR_NONE. |
|
| 315 * |
|
| 316 * - fd - wskaźnik gdzie wrzucić deskryptor |
|
| 317 * - pid - gdzie wrzucić pid procesu potomnego |
|
| 318 * - hostname - nazwa hosta do zresolvowania |
|
| 319 * |
|
| 320 * 0, -1. |
|
| 321 */ |
|
| 322 int gg_resolve(int *fd, int *pid, const char *hostname) |
|
| 323 { |
|
| 324 int pipes[2], res; |
|
| 325 struct in_addr a; |
|
| 326 int errno2; |
|
| 327 |
|
| 328 gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname); |
|
| 329 |
|
| 330 if (!fd || !pid) { |
|
| 331 errno = EFAULT; |
|
| 332 return -1; |
|
| 333 } |
|
| 334 |
|
| 335 if (pipe(pipes) == -1) |
|
| 336 return -1; |
|
| 337 |
|
| 338 if ((res = fork()) == -1) { |
|
| 339 errno2 = errno; |
|
| 340 close(pipes[0]); |
|
| 341 close(pipes[1]); |
|
| 342 errno = errno2; |
|
| 343 return -1; |
|
| 344 } |
|
| 345 |
|
| 346 if (!res) { |
|
| 347 if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { |
|
| 348 struct in_addr *hn; |
|
| 349 |
|
| 350 if (!(hn = gg_gethostbyname(hostname))) |
|
| 351 a.s_addr = INADDR_NONE; |
|
| 352 else { |
|
| 353 a.s_addr = hn->s_addr; |
|
| 354 free(hn); |
|
| 355 } |
|
| 356 } |
|
| 357 |
|
| 358 write(pipes[1], &a, sizeof(a)); |
|
| 359 |
|
| 360 exit(0); |
|
| 361 } |
|
| 362 |
|
| 363 close(pipes[1]); |
|
| 364 |
|
| 365 *fd = pipes[0]; |
|
| 366 *pid = res; |
|
| 367 |
|
| 368 return 0; |
|
| 369 } |
|
| 370 #endif |
|
| 371 |
|
| 372 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
|
| 373 |
|
| 374 struct gg_resolve_pthread_data { |
|
| 375 char *hostname; |
|
| 376 int fd; |
|
| 377 }; |
|
| 378 |
|
| 379 static void *gg_resolve_pthread_thread(void *arg) |
|
| 380 { |
|
| 381 struct gg_resolve_pthread_data *d = arg; |
|
| 382 struct in_addr a; |
|
| 383 |
|
| 384 pthread_detach(pthread_self()); |
|
| 385 |
|
| 386 if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { |
|
| 387 struct in_addr *hn; |
|
| 388 |
|
| 389 if (!(hn = gg_gethostbyname(d->hostname))) |
|
| 390 a.s_addr = INADDR_NONE; |
|
| 391 else { |
|
| 392 a.s_addr = hn->s_addr; |
|
| 393 free(hn); |
|
| 394 } |
|
| 395 } |
|
| 396 |
|
| 397 write(d->fd, &a, sizeof(a)); |
|
| 398 close(d->fd); |
|
| 399 |
|
| 400 free(d->hostname); |
|
| 401 d->hostname = NULL; |
|
| 402 |
|
| 403 free(d); |
|
| 404 |
|
| 405 pthread_exit(NULL); |
|
| 406 |
|
| 407 return NULL; /* żeby kompilator nie marudził */ |
|
| 408 } |
|
| 409 |
|
| 410 /* |
|
| 411 * gg_resolve_pthread() // funkcja wewnętrzna |
|
| 412 * |
|
| 413 * tworzy potok, nowy wątek i w nim zaczyna resolvować podanego hosta. |
|
| 414 * zapisuje w sesji deskryptor potoku. jeśli coś tam będzie gotowego, |
|
| 415 * znaczy, że można wczytać struct in_addr. jeśli nie znajdzie, zwraca |
|
| 416 * INADDR_NONE. |
|
| 417 * |
|
| 418 * - fd - wskaźnik do zmiennej przechowującej desktyptor resolvera |
|
| 419 * - resolver - wskaźnik do wskaźnika resolvera |
|
| 420 * - hostname - nazwa hosta do zresolvowania |
|
| 421 * |
|
| 422 * 0, -1. |
|
| 423 */ |
|
| 424 int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) |
|
| 425 { |
|
| 426 struct gg_resolve_pthread_data *d = NULL; |
|
| 427 pthread_t *tmp; |
|
| 428 int pipes[2], new_errno; |
|
| 429 |
|
| 430 gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname); |
|
| 431 |
|
| 432 if (!resolver || !fd || !hostname) { |
|
| 433 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n"); |
|
| 434 errno = EFAULT; |
|
| 435 return -1; |
|
| 436 } |
|
| 437 |
|
| 438 if (!(tmp = malloc(sizeof(pthread_t)))) { |
|
| 439 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n"); |
|
| 440 return -1; |
|
| 441 } |
|
| 442 |
|
| 443 if (pipe(pipes) == -1) { |
|
| 444 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); |
|
| 445 free(tmp); |
|
| 446 return -1; |
|
| 447 } |
|
| 448 |
|
| 449 if (!(d = malloc(sizeof(*d)))) { |
|
| 450 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); |
|
| 451 new_errno = errno; |
|
| 452 goto cleanup; |
|
| 453 } |
|
| 454 |
|
| 455 d->hostname = NULL; |
|
| 456 |
|
| 457 if (!(d->hostname = strdup(hostname))) { |
|
| 458 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); |
|
| 459 new_errno = errno; |
|
| 460 goto cleanup; |
|
| 461 } |
|
| 462 |
|
| 463 d->fd = pipes[1]; |
|
| 464 |
|
| 465 if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) { |
|
| 466 gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n"); |
|
| 467 new_errno = errno; |
|
| 468 goto cleanup; |
|
| 469 } |
|
| 470 |
|
| 471 gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp); |
|
| 472 |
|
| 473 *resolver = tmp; |
|
| 474 |
|
| 475 *fd = pipes[0]; |
|
| 476 |
|
| 477 return 0; |
|
| 478 |
|
| 479 cleanup: |
|
| 480 if (d) { |
|
| 481 free(d->hostname); |
|
| 482 free(d); |
|
| 483 } |
|
| 484 |
|
| 485 close(pipes[0]); |
|
| 486 close(pipes[1]); |
|
| 487 |
|
| 488 free(tmp); |
|
| 489 |
|
| 490 errno = new_errno; |
|
| 491 |
|
| 492 return -1; |
|
| 493 } |
|
| 494 |
|
| 495 #elif defined _WIN32 |
|
| 496 |
|
| 497 struct gg_resolve_win32thread_data { |
|
| 498 char *hostname; |
|
| 499 int fd; |
|
| 500 }; |
|
| 501 |
|
| 502 static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) |
|
| 503 { |
|
| 504 struct gg_resolve_win32thread_data *d = arg; |
|
| 505 struct in_addr a; |
|
| 506 |
|
| 507 if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { |
|
| 508 struct in_addr *hn; |
|
| 509 |
|
| 510 if (!(hn = gg_gethostbyname(d->hostname))) |
|
| 511 a.s_addr = INADDR_NONE; |
|
| 512 else { |
|
| 513 a.s_addr = hn->s_addr; |
|
| 514 free(hn); |
|
| 515 } |
|
| 516 } |
|
| 517 |
|
| 518 write(d->fd, &a, sizeof(a)); |
|
| 519 close(d->fd); |
|
| 520 |
|
| 521 free(d->hostname); |
|
| 522 d->hostname = NULL; |
|
| 523 |
|
| 524 free(d); |
|
| 525 |
|
| 526 return 0; |
|
| 527 } |
|
| 528 |
|
| 529 |
|
| 530 int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) |
|
| 531 { |
|
| 532 struct gg_resolve_win32thread_data *d = NULL; |
|
| 533 HANDLE h; |
|
| 534 DWORD dwTId; |
|
| 535 int pipes[2], new_errno; |
|
| 536 |
|
| 537 gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); |
|
| 538 |
|
| 539 if (!resolver || !fd || !hostname) { |
|
| 540 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); |
|
| 541 errno = EFAULT; |
|
| 542 return -1; |
|
| 543 } |
|
| 544 |
|
| 545 if (socket_pipe(pipes) == -1) { |
|
| 546 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); |
|
| 547 return -1; |
|
| 548 } |
|
| 549 |
|
| 550 if (!(d = malloc(sizeof(*d)))) { |
|
| 551 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); |
|
| 552 new_errno = GetLastError(); |
|
| 553 goto cleanup; |
|
| 554 } |
|
| 555 |
|
| 556 d->hostname = NULL; |
|
| 557 |
|
| 558 if (!(d->hostname = strdup(hostname))) { |
|
| 559 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); |
|
| 560 new_errno = GetLastError(); |
|
| 561 goto cleanup; |
|
| 562 } |
|
| 563 |
|
| 564 d->fd = pipes[1]; |
|
| 565 |
|
| 566 h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, |
|
| 567 d, 0, &dwTId); |
|
| 568 |
|
| 569 if (h == NULL) { |
|
| 570 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); |
|
| 571 new_errno = GetLastError(); |
|
| 572 goto cleanup; |
|
| 573 } |
|
| 574 |
|
| 575 *resolver = h; |
|
| 576 *fd = pipes[0]; |
|
| 577 |
|
| 578 return 0; |
|
| 579 |
|
| 580 cleanup: |
|
| 581 if (d) { |
|
| 582 free(d->hostname); |
|
| 583 free(d); |
|
| 584 } |
|
| 585 |
|
| 586 close(pipes[0]); |
|
| 587 close(pipes[1]); |
|
| 588 |
|
| 589 errno = new_errno; |
|
| 590 |
|
| 591 return -1; |
|
| 592 |
|
| 593 } |
|
| 594 #endif |
|
| 595 |
|
| 596 /* |
|
| 597 * gg_read() // funkcja pomocnicza |
|
| 598 * |
|
| 599 * czyta z gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy |
|
| 600 * połączenie zwykłe czy TLS. |
|
| 601 * |
|
| 602 * - sess - sesja, |
|
| 603 * - buf - bufor, |
|
| 604 * - length - ilość bajtów, |
|
| 605 * |
|
| 606 * takie same wartości jak read(). |
|
| 607 */ |
|
| 608 int gg_read(struct gg_session *sess, char *buf, int length) |
|
| 609 { |
|
| 610 int res; |
|
| 611 |
|
| 612 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
|
| 613 if (sess->ssl) { |
|
| 614 int err; |
|
| 615 |
|
| 616 res = SSL_read(sess->ssl, buf, length); |
|
| 617 |
|
| 618 if (res < 0) { |
|
| 619 err = SSL_get_error(sess->ssl, res); |
|
| 620 |
|
| 621 if (err == SSL_ERROR_WANT_READ) |
|
| 622 errno = EAGAIN; |
|
| 623 |
|
| 624 return -1; |
|
| 625 } |
|
| 626 } else |
|
| 627 #endif |
|
| 628 res = read(sess->fd, buf, length); |
|
| 629 |
|
| 630 return res; |
|
| 631 } |
|
| 632 |
|
| 633 /* |
|
| 634 * gg_write() // funkcja pomocnicza |
|
| 635 * |
|
| 636 * zapisuje do gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy |
|
| 637 * połączenie zwykłe czy TLS. |
|
| 638 * |
|
| 639 * - sess - sesja, |
|
| 640 * - buf - bufor, |
|
| 641 * - length - ilość bajtów, |
|
| 642 * |
|
| 643 * takie same wartości jak write(). |
|
| 644 */ |
|
| 645 int gg_write(struct gg_session *sess, const char *buf, int length) |
|
| 646 { |
|
| 647 int res = 0; |
|
| 648 |
|
| 649 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
|
| 650 if (sess->ssl) { |
|
| 651 int err; |
|
| 652 |
|
| 653 res = SSL_write(sess->ssl, buf, length); |
|
| 654 |
|
| 655 if (res < 0) { |
|
| 656 err = SSL_get_error(sess->ssl, res); |
|
| 657 |
|
| 658 if (err == SSL_ERROR_WANT_WRITE) |
|
| 659 errno = EAGAIN; |
|
| 660 |
|
| 661 return -1; |
|
| 662 } |
|
| 663 } else |
|
| 664 #endif |
|
| 665 { |
|
| 666 int written = 0; |
|
| 667 |
|
| 668 while (written < length) { |
|
| 669 res = write(sess->fd, buf + written, length - written); |
|
| 670 |
|
| 671 if (res == -1) { |
|
| 672 if (errno == EAGAIN) |
|
| 673 continue; |
|
| 674 else |
|
| 675 break; |
|
| 676 } else { |
|
| 677 written += res; |
|
| 678 res = written; |
|
| 679 } |
|
| 680 } |
|
| 681 } |
|
| 682 |
|
| 683 return res; |
|
| 684 } |
|
| 685 |
|
| 686 /* |
|
| 687 * gg_recv_packet() // funkcja wewnętrzna |
|
| 688 * |
|
| 689 * odbiera jeden pakiet i zwraca wskaźnik do niego. pamięć po nim |
|
| 690 * należy zwolnić za pomocą free(). |
|
| 691 * |
|
| 692 * - sess - opis sesji |
|
| 693 * |
|
| 694 * w przypadku błędu NULL, kod błędu w errno. należy zwrócić uwagę, że gdy |
|
| 695 * połączenie jest nieblokujące, a kod błędu wynosi EAGAIN, nie udało się |
|
| 696 * odczytać całego pakietu i nie należy tego traktować jako błąd. |
|
| 697 */ |
|
| 698 void *gg_recv_packet(struct gg_session *sess) |
|
| 699 { |
|
| 700 struct gg_header h; |
|
| 701 char *buf = NULL; |
|
| 702 int ret = 0, offset, size = 0; |
|
| 703 |
|
| 704 gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); |
|
| 705 |
|
| 706 if (!sess) { |
|
| 707 errno = EFAULT; |
|
| 708 return NULL; |
|
| 709 } |
|
| 710 |
|
| 711 if (sess->recv_left < 1) { |
|
| 712 if (sess->header_buf) { |
|
| 713 memcpy(&h, sess->header_buf, sess->header_done); |
|
| 714 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); |
|
| 715 free(sess->header_buf); |
|
| 716 sess->header_buf = NULL; |
|
| 717 } else |
|
| 718 sess->header_done = 0; |
|
| 719 |
|
| 720 while (sess->header_done < sizeof(h)) { |
|
| 721 ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done); |
|
| 722 |
|
| 723 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); |
|
| 724 |
|
| 725 if (!ret) { |
|
| 726 errno = ECONNRESET; |
|
| 727 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); |
|
| 728 return NULL; |
|
| 729 } |
|
| 730 |
|
| 731 if (ret == -1) { |
|
| 732 if (errno == EINTR) { |
|
| 733 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); |
|
| 734 continue; |
|
| 735 } |
|
| 736 |
|
| 737 if (errno == EAGAIN) { |
|
| 738 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); |
|
| 739 |
|
| 740 if (!(sess->header_buf = malloc(sess->header_done))) { |
|
| 741 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); |
|
| 742 return NULL; |
|
| 743 } |
|
| 744 |
|
| 745 memcpy(sess->header_buf, &h, sess->header_done); |
|
| 746 |
|
| 747 return NULL; |
|
| 748 } |
|
| 749 |
|
| 750 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); |
|
| 751 |
|
| 752 return NULL; |
|
| 753 } |
|
| 754 |
|
| 755 sess->header_done += ret; |
|
| 756 |
|
| 757 } |
|
| 758 |
|
| 759 h.type = gg_fix32(h.type); |
|
| 760 h.length = gg_fix32(h.length); |
|
| 761 } else |
|
| 762 memcpy(&h, sess->recv_buf, sizeof(h)); |
|
| 763 |
|
| 764 /* jakieś sensowne limity na rozmiar pakietu */ |
|
| 765 if (h.length > 65535) { |
|
| 766 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); |
|
| 767 errno = ERANGE; |
|
| 768 return NULL; |
|
| 769 } |
|
| 770 |
|
| 771 if (sess->recv_left > 0) { |
|
| 772 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); |
|
| 773 size = sess->recv_left; |
|
| 774 offset = sess->recv_done; |
|
| 775 buf = sess->recv_buf; |
|
| 776 } else { |
|
| 777 if (!(buf = malloc(sizeof(h) + h.length + 1))) { |
|
| 778 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); |
|
| 779 return NULL; |
|
| 780 } |
|
| 781 |
|
| 782 memcpy(buf, &h, sizeof(h)); |
|
| 783 |
|
| 784 offset = 0; |
|
| 785 size = h.length; |
|
| 786 } |
|
| 787 |
|
| 788 while (size > 0) { |
|
| 789 ret = gg_read(sess, buf + sizeof(h) + offset, size); |
|
| 790 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); |
|
| 791 if (!ret) { |
|
| 792 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); |
|
| 793 errno = ECONNRESET; |
|
| 794 return NULL; |
|
| 795 } |
|
| 796 if (ret > -1 && ret <= size) { |
|
| 797 offset += ret; |
|
| 798 size -= ret; |
|
| 799 } else if (ret == -1) { |
|
| 800 int errno2 = errno; |
|
| 801 |
|
| 802 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); |
|
| 803 errno = errno2; |
|
| 804 |
|
| 805 if (errno == EAGAIN) { |
|
| 806 gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); |
|
| 807 sess->recv_buf = buf; |
|
| 808 sess->recv_left = size; |
|
| 809 sess->recv_done = offset; |
|
| 810 return NULL; |
|
| 811 } |
|
| 812 if (errno != EINTR) { |
|
| 813 free(buf); |
|
| 814 return NULL; |
|
| 815 } |
|
| 816 } |
|
| 817 } |
|
| 818 |
|
| 819 sess->recv_left = 0; |
|
| 820 |
|
| 821 if ((gg_debug_level & GG_DEBUG_DUMP)) { |
|
| 822 unsigned int i; |
|
| 823 |
|
| 824 gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); |
|
| 825 for (i = 0; i < sizeof(h) + h.length; i++) |
|
| 826 gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); |
|
| 827 gg_debug(GG_DEBUG_DUMP, "\n"); |
|
| 828 } |
|
| 829 |
|
| 830 return buf; |
|
| 831 } |
|
| 832 |
|
| 833 /* |
|
| 834 * gg_send_packet() // funkcja wewnętrzna |
|
| 835 * |
|
| 836 * konstruuje pakiet i wysyła go do serwera. |
|
| 837 * |
|
| 838 * - sock - deskryptor gniazda |
|
| 839 * - type - typ pakietu |
|
| 840 * - payload_1 - pierwsza część pakietu |
|
| 841 * - payload_length_1 - długość pierwszej części |
|
| 842 * - payload_2 - druga część pakietu |
|
| 843 * - payload_length_2 - długość drugiej części |
|
| 844 * - ... - kolejne części pakietu i ich długości |
|
| 845 * - NULL - końcowym parametr (konieczny!) |
|
| 846 * |
|
| 847 * jeśli się powiodło, zwraca 0, w przypadku błędu -1. jeśli errno == ENOMEM, |
|
| 848 * zabrakło pamięci. inaczej był błąd przy wysyłaniu pakietu. dla errno == 0 |
|
| 849 * nie wysłano całego pakietu. |
|
| 850 */ |
|
| 851 int gg_send_packet(struct gg_session *sess, int type, ...) |
|
| 852 { |
|
| 853 struct gg_header *h; |
|
| 854 char *tmp; |
|
| 855 int tmp_length; |
|
| 856 void *payload; |
|
| 857 unsigned int payload_length; |
|
| 858 va_list ap; |
|
| 859 int res; |
|
| 860 |
|
| 861 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type); |
|
| 862 |
|
| 863 tmp_length = sizeof(struct gg_header); |
|
| 864 |
|
| 865 if (!(tmp = malloc(tmp_length))) { |
|
| 866 gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); |
|
| 867 return -1; |
|
| 868 } |
|
| 869 |
|
| 870 va_start(ap, type); |
|
| 871 |
|
| 872 payload = va_arg(ap, void *); |
|
| 873 |
|
| 874 while (payload) { |
|
| 875 char *tmp2; |
|
| 876 |
|
| 877 payload_length = va_arg(ap, unsigned int); |
|
| 878 |
|
| 879 if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) { |
|
| 880 gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); |
|
| 881 free(tmp); |
|
| 882 va_end(ap); |
|
| 883 return -1; |
|
| 884 } |
|
| 885 |
|
| 886 tmp = tmp2; |
|
| 887 |
|
| 888 memcpy(tmp + tmp_length, payload, payload_length); |
|
| 889 tmp_length += payload_length; |
|
| 890 |
|
| 891 payload = va_arg(ap, void *); |
|
| 892 } |
|
| 893 |
|
| 894 va_end(ap); |
|
| 895 |
|
| 896 h = (struct gg_header*) tmp; |
|
| 897 h->type = gg_fix32(type); |
|
| 898 h->length = gg_fix32(tmp_length - sizeof(struct gg_header)); |
|
| 899 |
|
| 900 if ((gg_debug_level & GG_DEBUG_DUMP)) { |
|
| 901 int i; |
|
| 902 |
|
| 903 gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); |
|
| 904 for (i = 0; i < tmp_length; ++i) |
|
| 905 gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); |
|
| 906 gg_debug(GG_DEBUG_DUMP, "\n"); |
|
| 907 } |
|
| 908 |
|
| 909 if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) { |
|
| 910 gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); |
|
| 911 free(tmp); |
|
| 912 return -1; |
|
| 913 } |
|
| 914 |
|
| 915 free(tmp); |
|
| 916 return 0; |
|
| 917 } |
|
| 918 |
|
| 919 /* |
|
| 920 * gg_session_callback() // funkcja wewnętrzna |
|
| 921 * |
|
| 922 * wywoływany z gg_session->callback, wykonuje gg_watch_fd() i pakuje |
|
| 923 * do gg_session->event jego wynik. |
|
| 924 */ |
|
| 925 static int gg_session_callback(struct gg_session *s) |
|
| 926 { |
|
| 927 if (!s) { |
|
| 928 errno = EFAULT; |
|
| 929 return -1; |
|
| 930 } |
|
| 931 |
|
| 932 return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1; |
|
| 933 } |
|
| 934 |
|
| 935 /* |
|
| 936 * gg_login() |
|
| 937 * |
|
| 938 * rozpoczyna procedurę łączenia się z serwerem. resztę obsługuje się przez |
|
| 939 * gg_watch_fd(). |
|
| 940 * |
|
| 941 * UWAGA! program musi obsłużyć SIGCHLD, jeśli łączy się asynchronicznie, |
|
| 942 * żeby poprawnie zamknąć proces resolvera. |
|
| 943 * |
|
| 944 * - p - struktura opisująca początkowy stan. wymagane pola: uin, |
|
| 945 * password |
|
| 946 * |
|
| 947 * w przypadku błędu NULL, jeśli idzie dobrze (async) albo poszło |
|
| 948 * dobrze (sync), zwróci wskaźnik do zaalokowanej struct gg_session. |
|
| 949 */ |
|
| 950 struct gg_session *gg_login(const struct gg_login_params *p) |
|
| 951 { |
|
| 952 struct gg_session *sess = NULL; |
|
| 953 char *hostname; |
|
| 954 int port; |
|
| 955 |
|
| 956 if (!p) { |
|
| 957 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p); |
|
| 958 errno = EFAULT; |
|
| 959 return NULL; |
|
| 960 } |
|
| 961 |
|
| 962 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async); |
|
| 963 |
|
| 964 if (!(sess = malloc(sizeof(struct gg_session)))) { |
|
| 965 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n"); |
|
| 966 goto fail; |
|
| 967 } |
|
| 968 |
|
| 969 memset(sess, 0, sizeof(struct gg_session)); |
|
| 970 |
|
| 971 if (!p->password || !p->uin) { |
|
| 972 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n"); |
|
| 973 errno = EFAULT; |
|
| 974 goto fail; |
|
| 975 } |
|
| 976 |
|
| 977 if (!(sess->password = strdup(p->password))) { |
|
| 978 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n"); |
|
| 979 goto fail; |
|
| 980 } |
|
| 981 |
|
| 982 if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) { |
|
| 983 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); |
|
| 984 goto fail; |
|
| 985 } |
|
| 986 |
|
| 987 sess->uin = p->uin; |
|
| 988 sess->state = GG_STATE_RESOLVING; |
|
| 989 sess->check = GG_CHECK_READ; |
|
| 990 sess->timeout = GG_DEFAULT_TIMEOUT; |
|
| 991 sess->async = p->async; |
|
| 992 sess->type = GG_SESSION_GG; |
|
| 993 sess->initial_status = p->status; |
|
| 994 sess->callback = gg_session_callback; |
|
| 995 sess->destroy = gg_free_session; |
|
| 996 sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT); |
|
| 997 sess->server_addr = p->server_addr; |
|
| 998 sess->external_port = p->external_port; |
|
| 999 sess->external_addr = p->external_addr; |
|
| 1000 sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; |
|
| 1001 if (p->era_omnix) |
|
| 1002 sess->protocol_version |= GG_ERA_OMNIX_MASK; |
|
| 1003 if (p->has_audio) |
|
| 1004 sess->protocol_version |= GG_HAS_AUDIO_MASK; |
|
| 1005 sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL; |
|
| 1006 sess->last_sysmsg = p->last_sysmsg; |
|
| 1007 sess->image_size = p->image_size; |
|
| 1008 sess->pid = -1; |
|
| 1009 |
|
| 1010 if (p->tls == 1) { |
|
| 1011 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
|
| 1012 char buf[1024]; |
|
| 1013 |
|
| 1014 OpenSSL_add_ssl_algorithms(); |
|
| 1015 |
|
| 1016 if (!RAND_status()) { |
|
| 1017 char rdata[1024]; |
|
| 1018 struct { |
|
| 1019 time_t time; |
|
| 1020 void *ptr; |
|
| 1021 } rstruct; |
|
| 1022 |
|
| 1023 time(&rstruct.time); |
|
| 1024 rstruct.ptr = (void *) &rstruct; |
|
| 1025 |
|
| 1026 RAND_seed((void *) rdata, sizeof(rdata)); |
|
| 1027 RAND_seed((void *) &rstruct, sizeof(rstruct)); |
|
| 1028 } |
|
| 1029 |
|
| 1030 sess->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); |
|
| 1031 |
|
| 1032 if (!sess->ssl_ctx) { |
|
| 1033 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); |
|
| 1034 gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf); |
|
| 1035 goto fail; |
|
| 1036 } |
|
| 1037 |
|
| 1038 SSL_CTX_set_verify(sess->ssl_ctx, SSL_VERIFY_NONE, NULL); |
|
| 1039 |
|
| 1040 sess->ssl = SSL_new(sess->ssl_ctx); |
|
| 1041 |
|
| 1042 if (!sess->ssl) { |
|
| 1043 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); |
|
| 1044 gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf); |
|
| 1045 goto fail; |
|
| 1046 } |
|
| 1047 #else |
|
| 1048 gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); |
|
| 1049 #endif |
|
| 1050 } |
|
| 1051 |
|
| 1052 if (gg_proxy_enabled) { |
|
| 1053 hostname = gg_proxy_host; |
|
| 1054 sess->proxy_port = port = gg_proxy_port; |
|
| 1055 } else { |
|
| 1056 hostname = GG_APPMSG_HOST; |
|
| 1057 port = GG_APPMSG_PORT; |
|
| 1058 } |
|
| 1059 |
|
| 1060 if (!p->async) { |
|
| 1061 struct in_addr a; |
|
| 1062 |
|
| 1063 if (!p->server_addr || !p->server_port) { |
|
| 1064 if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { |
|
| 1065 struct in_addr *hn; |
|
| 1066 |
|
| 1067 if (!(hn = gg_gethostbyname(hostname))) { |
|
| 1068 gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); |
|
| 1069 goto fail; |
|
| 1070 } else { |
|
| 1071 a.s_addr = hn->s_addr; |
|
| 1072 free(hn); |
|
| 1073 } |
|
| 1074 } |
|
| 1075 } else { |
|
| 1076 a.s_addr = p->server_addr; |
|
| 1077 port = p->server_port; |
|
| 1078 } |
|
| 1079 |
|
| 1080 sess->hub_addr = a.s_addr; |
|
| 1081 |
|
| 1082 if (gg_proxy_enabled) |
|
| 1083 sess->proxy_addr = a.s_addr; |
|
| 1084 |
|
| 1085 if ((sess->fd = gg_connect(&a, port, 0)) == -1) { |
|
| 1086 gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); |
|
| 1087 goto fail; |
|
| 1088 } |
|
| 1089 |
|
| 1090 if (p->server_addr && p->server_port) |
|
| 1091 sess->state = GG_STATE_CONNECTING_GG; |
|
| 1092 else |
|
| 1093 sess->state = GG_STATE_CONNECTING_HUB; |
|
| 1094 |
|
| 1095 while (sess->state != GG_STATE_CONNECTED) { |
|
| 1096 struct gg_event *e; |
|
| 1097 |
|
| 1098 if (!(e = gg_watch_fd(sess))) { |
|
| 1099 gg_debug(GG_DEBUG_MISC, "// gg_login() critical error in gg_watch_fd()\n"); |
|
| 1100 goto fail; |
|
| 1101 } |
|
| 1102 |
|
| 1103 if (e->type == GG_EVENT_CONN_FAILED) { |
|
| 1104 errno = EACCES; |
|
| 1105 gg_debug(GG_DEBUG_MISC, "// gg_login() could not login\n"); |
|
| 1106 gg_event_free(e); |
|
| 1107 goto fail; |
|
| 1108 } |
|
| 1109 |
|
| 1110 gg_event_free(e); |
|
| 1111 } |
|
| 1112 |
|
| 1113 return sess; |
|
| 1114 } |
|
| 1115 |
|
| 1116 if (!sess->server_addr || gg_proxy_enabled) { |
|
| 1117 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
|
| 1118 if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) { |
|
| 1119 #elif defined _WIN32 |
|
| 1120 if (gg_resolve_win32thread(&sess->fd, &sess->resolver, hostname)) { |
|
| 1121 #else |
|
| 1122 if (gg_resolve(&sess->fd, &sess->pid, hostname)) { |
|
| 1123 #endif |
|
| 1124 gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); |
|
| 1125 goto fail; |
|
| 1126 } |
|
| 1127 } else { |
|
| 1128 if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { |
|
| 1129 gg_debug(GG_DEBUG_MISC, "// gg_login() direct connection failed (errno=%d, %s)\n", errno, strerror(errno)); |
|
| 1130 goto fail; |
|
| 1131 } |
|
| 1132 sess->state = GG_STATE_CONNECTING_GG; |
|
| 1133 sess->check = GG_CHECK_WRITE; |
|
| 1134 } |
|
| 1135 |
|
| 1136 return sess; |
|
| 1137 |
|
| 1138 fail: |
|
| 1139 if (sess) { |
|
| 1140 if (sess->password) |
|
| 1141 free(sess->password); |
|
| 1142 if (sess->initial_descr) |
|
| 1143 free(sess->initial_descr); |
|
| 1144 free(sess); |
|
| 1145 } |
|
| 1146 |
|
| 1147 return NULL; |
|
| 1148 } |
|
| 1149 |
|
| 1150 /* |
|
| 1151 * gg_free_session() |
|
| 1152 * |
|
| 1153 * próbuje zamknąć połączenia i zwalnia pamięć zajmowaną przez sesję. |
|
| 1154 * |
|
| 1155 * - sess - opis sesji |
|
| 1156 */ |
|
| 1157 void gg_free_session(struct gg_session *sess) |
|
| 1158 { |
|
| 1159 if (!sess) |
|
| 1160 return; |
|
| 1161 |
|
| 1162 /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */ |
|
| 1163 |
|
| 1164 if (sess->password) |
|
| 1165 free(sess->password); |
|
| 1166 |
|
| 1167 if (sess->initial_descr) |
|
| 1168 free(sess->initial_descr); |
|
| 1169 |
|
| 1170 if (sess->client_version) |
|
| 1171 free(sess->client_version); |
|
| 1172 |
|
| 1173 if (sess->header_buf) |
|
| 1174 free(sess->header_buf); |
|
| 1175 |
|
| 1176 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
|
| 1177 if (sess->ssl) |
|
| 1178 SSL_free(sess->ssl); |
|
| 1179 |
|
| 1180 if (sess->ssl_ctx) |
|
| 1181 SSL_CTX_free(sess->ssl_ctx); |
|
| 1182 #endif |
|
| 1183 |
|
| 1184 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
|
| 1185 if (sess->resolver) { |
|
| 1186 pthread_cancel(*((pthread_t*) sess->resolver)); |
|
| 1187 free(sess->resolver); |
|
| 1188 sess->resolver = NULL; |
|
| 1189 } |
|
| 1190 #elif defined _WIN32 |
|
| 1191 if (sess->resolver) { |
|
| 1192 HANDLE h = sess->resolver; |
|
| 1193 TerminateThread(h, 0); |
|
| 1194 CloseHandle(h); |
|
| 1195 sess->resolver = NULL; |
|
| 1196 } |
|
| 1197 #else |
|
| 1198 if (sess->pid != -1) |
|
| 1199 waitpid(sess->pid, NULL, WNOHANG); |
|
| 1200 #endif |
|
| 1201 |
|
| 1202 if (sess->fd != -1) |
|
| 1203 close(sess->fd); |
|
| 1204 |
|
| 1205 while (sess->images) |
|
| 1206 gg_image_queue_remove(sess, sess->images, 1); |
|
| 1207 |
|
| 1208 free(sess); |
|
| 1209 } |
|
| 1210 |
|
| 1211 /* |
|
| 1212 * gg_change_status() |
|
| 1213 * |
|
| 1214 * zmienia status użytkownika. przydatne do /away i /busy oraz /quit. |
|
| 1215 * |
|
| 1216 * - sess - opis sesji |
|
| 1217 * - status - nowy status użytkownika |
|
| 1218 * |
|
| 1219 * 0, -1. |
|
| 1220 */ |
|
| 1221 int gg_change_status(struct gg_session *sess, int status) |
|
| 1222 { |
|
| 1223 struct gg_new_status p; |
|
| 1224 |
|
| 1225 gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); |
|
| 1226 |
|
| 1227 if (!sess) { |
|
| 1228 errno = EFAULT; |
|
| 1229 return -1; |
|
| 1230 } |
|
| 1231 |
|
| 1232 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1233 errno = ENOTCONN; |
|
| 1234 return -1; |
|
| 1235 } |
|
| 1236 |
|
| 1237 p.status = gg_fix32(status); |
|
| 1238 |
|
| 1239 sess->status = status; |
|
| 1240 |
|
| 1241 return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL); |
|
| 1242 } |
|
| 1243 |
|
| 1244 /* |
|
| 1245 * gg_change_status_descr() |
|
| 1246 * |
|
| 1247 * zmienia status użytkownika na opisowy. |
|
| 1248 * |
|
| 1249 * - sess - opis sesji |
|
| 1250 * - status - nowy status użytkownika |
|
| 1251 * - descr - opis statusu |
|
| 1252 * |
|
| 1253 * 0, -1. |
|
| 1254 */ |
|
| 1255 int gg_change_status_descr(struct gg_session *sess, int status, const char *descr) |
|
| 1256 { |
|
| 1257 struct gg_new_status p; |
|
| 1258 |
|
| 1259 gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); |
|
| 1260 |
|
| 1261 if (!sess || !descr) { |
|
| 1262 errno = EFAULT; |
|
| 1263 return -1; |
|
| 1264 } |
|
| 1265 |
|
| 1266 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1267 errno = ENOTCONN; |
|
| 1268 return -1; |
|
| 1269 } |
|
| 1270 |
|
| 1271 p.status = gg_fix32(status); |
|
| 1272 |
|
| 1273 sess->status = status; |
|
| 1274 |
|
| 1275 return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL); |
|
| 1276 } |
|
| 1277 |
|
| 1278 /* |
|
| 1279 * gg_change_status_descr_time() |
|
| 1280 * |
|
| 1281 * zmienia status użytkownika na opisowy z godziną powrotu. |
|
| 1282 * |
|
| 1283 * - sess - opis sesji |
|
| 1284 * - status - nowy status użytkownika |
|
| 1285 * - descr - opis statusu |
|
| 1286 * - time - czas w formacie uniksowym |
|
| 1287 * |
|
| 1288 * 0, -1. |
|
| 1289 */ |
|
| 1290 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) |
|
| 1291 { |
|
| 1292 struct gg_new_status p; |
|
| 1293 uint32_t newtime; |
|
| 1294 |
|
| 1295 gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); |
|
| 1296 |
|
| 1297 if (!sess || !descr || !time) { |
|
| 1298 errno = EFAULT; |
|
| 1299 return -1; |
|
| 1300 } |
|
| 1301 |
|
| 1302 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1303 errno = ENOTCONN; |
|
| 1304 return -1; |
|
| 1305 } |
|
| 1306 |
|
| 1307 p.status = gg_fix32(status); |
|
| 1308 |
|
| 1309 sess->status = status; |
|
| 1310 |
|
| 1311 newtime = gg_fix32(time); |
|
| 1312 |
|
| 1313 return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL); |
|
| 1314 } |
|
| 1315 |
|
| 1316 /* |
|
| 1317 * gg_logoff() |
|
| 1318 * |
|
| 1319 * wylogowuje użytkownika i zamyka połączenie, ale nie zwalnia pamięci. |
|
| 1320 * |
|
| 1321 * - sess - opis sesji |
|
| 1322 */ |
|
| 1323 void gg_logoff(struct gg_session *sess) |
|
| 1324 { |
|
| 1325 if (!sess) |
|
| 1326 return; |
|
| 1327 |
|
| 1328 gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); |
|
| 1329 |
|
| 1330 if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK)) |
|
| 1331 gg_change_status(sess, GG_STATUS_NOT_AVAIL); |
|
| 1332 |
|
| 1333 #ifdef __GG_LIBGADU_HAVE_OPENSSL |
|
| 1334 if (sess->ssl) |
|
| 1335 SSL_shutdown(sess->ssl); |
|
| 1336 #endif |
|
| 1337 |
|
| 1338 #ifdef __GG_LIBGADU_HAVE_PTHREAD |
|
| 1339 if (sess->resolver) { |
|
| 1340 pthread_cancel(*((pthread_t*) sess->resolver)); |
|
| 1341 free(sess->resolver); |
|
| 1342 sess->resolver = NULL; |
|
| 1343 } |
|
| 1344 #elif defined _WIN32 |
|
| 1345 if (sess->resolver) { |
|
| 1346 HANDLE h = sess->resolver; |
|
| 1347 TerminateThread(h, 0); |
|
| 1348 CloseHandle(h); |
|
| 1349 sess->resolver = NULL; |
|
| 1350 } |
|
| 1351 #else |
|
| 1352 if (sess->pid != -1) { |
|
| 1353 waitpid(sess->pid, NULL, WNOHANG); |
|
| 1354 sess->pid = -1; |
|
| 1355 } |
|
| 1356 #endif |
|
| 1357 |
|
| 1358 if (sess->fd != -1) { |
|
| 1359 shutdown(sess->fd, SHUT_RDWR); |
|
| 1360 close(sess->fd); |
|
| 1361 sess->fd = -1; |
|
| 1362 } |
|
| 1363 } |
|
| 1364 |
|
| 1365 /* |
|
| 1366 * gg_image_request() |
|
| 1367 * |
|
| 1368 * wysyła żądanie wysłania obrazka o podanych parametrach. |
|
| 1369 * |
|
| 1370 * - sess - opis sesji |
|
| 1371 * - recipient - numer adresata |
|
| 1372 * - size - rozmiar obrazka |
|
| 1373 * - crc32 - suma kontrolna obrazka |
|
| 1374 * |
|
| 1375 * 0/-1 |
|
| 1376 */ |
|
| 1377 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32) |
|
| 1378 { |
|
| 1379 struct gg_send_msg s; |
|
| 1380 struct gg_msg_image_request r; |
|
| 1381 char dummy = 0; |
|
| 1382 int res; |
|
| 1383 |
|
| 1384 gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); |
|
| 1385 |
|
| 1386 if (!sess) { |
|
| 1387 errno = EFAULT; |
|
| 1388 return -1; |
|
| 1389 } |
|
| 1390 |
|
| 1391 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1392 errno = ENOTCONN; |
|
| 1393 return -1; |
|
| 1394 } |
|
| 1395 |
|
| 1396 if (size < 0) { |
|
| 1397 errno = EINVAL; |
|
| 1398 return -1; |
|
| 1399 } |
|
| 1400 |
|
| 1401 s.recipient = gg_fix32(recipient); |
|
| 1402 s.seq = gg_fix32(0); |
|
| 1403 s.msgclass = gg_fix32(GG_CLASS_MSG); |
|
| 1404 |
|
| 1405 r.flag = 0x04; |
|
| 1406 r.size = gg_fix32(size); |
|
| 1407 r.crc32 = gg_fix32(crc32); |
|
| 1408 |
|
| 1409 res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL); |
|
| 1410 |
|
| 1411 if (!res) { |
|
| 1412 struct gg_image_queue *q = malloc(sizeof(*q)); |
|
| 1413 char *buf; |
|
| 1414 |
|
| 1415 if (!q) { |
|
| 1416 gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); |
|
| 1417 return -1; |
|
| 1418 } |
|
| 1419 |
|
| 1420 buf = malloc(size); |
|
| 1421 if (size && !buf) |
|
| 1422 { |
|
| 1423 gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); |
|
| 1424 free(q); |
|
| 1425 return -1; |
|
| 1426 } |
|
| 1427 |
|
| 1428 memset(q, 0, sizeof(*q)); |
|
| 1429 |
|
| 1430 q->sender = recipient; |
|
| 1431 q->size = size; |
|
| 1432 q->crc32 = crc32; |
|
| 1433 q->image = buf; |
|
| 1434 |
|
| 1435 if (!sess->images) |
|
| 1436 sess->images = q; |
|
| 1437 else { |
|
| 1438 struct gg_image_queue *qq; |
|
| 1439 |
|
| 1440 for (qq = sess->images; qq->next; qq = qq->next) |
|
| 1441 ; |
|
| 1442 |
|
| 1443 qq->next = q; |
|
| 1444 } |
|
| 1445 } |
|
| 1446 |
|
| 1447 return res; |
|
| 1448 } |
|
| 1449 |
|
| 1450 /* |
|
| 1451 * gg_image_reply() |
|
| 1452 * |
|
| 1453 * wysyła żądany obrazek. |
|
| 1454 * |
|
| 1455 * - sess - opis sesji |
|
| 1456 * - recipient - numer adresata |
|
| 1457 * - filename - nazwa pliku |
|
| 1458 * - image - bufor z obrazkiem |
|
| 1459 * - size - rozmiar obrazka |
|
| 1460 * |
|
| 1461 * 0/-1 |
|
| 1462 */ |
|
| 1463 int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size) |
|
| 1464 { |
|
| 1465 struct gg_msg_image_reply *r; |
|
| 1466 struct gg_send_msg s; |
|
| 1467 const char *tmp; |
|
| 1468 char buf[1910]; |
|
| 1469 int res = -1; |
|
| 1470 |
|
| 1471 gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); |
|
| 1472 |
|
| 1473 if (!sess || !filename || !image) { |
|
| 1474 errno = EFAULT; |
|
| 1475 return -1; |
|
| 1476 } |
|
| 1477 |
|
| 1478 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1479 errno = ENOTCONN; |
|
| 1480 return -1; |
|
| 1481 } |
|
| 1482 |
|
| 1483 if (size < 0) { |
|
| 1484 errno = EINVAL; |
|
| 1485 return -1; |
|
| 1486 } |
|
| 1487 |
|
| 1488 /* wytnij ścieżki, zostaw tylko nazwę pliku */ |
|
| 1489 while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\'))) |
|
| 1490 filename = tmp + 1; |
|
| 1491 |
|
| 1492 if (strlen(filename) < 1 || strlen(filename) > 1024) { |
|
| 1493 errno = EINVAL; |
|
| 1494 return -1; |
|
| 1495 } |
|
| 1496 |
|
| 1497 s.recipient = gg_fix32(recipient); |
|
| 1498 s.seq = gg_fix32(0); |
|
| 1499 s.msgclass = gg_fix32(GG_CLASS_MSG); |
|
| 1500 |
|
| 1501 buf[0] = 0; |
|
| 1502 r = (void*) &buf[1]; |
|
| 1503 |
|
| 1504 r->flag = 0x05; |
|
| 1505 r->size = gg_fix32(size); |
|
| 1506 r->crc32 = gg_fix32(gg_crc32(0, image, size)); |
|
| 1507 |
|
| 1508 while (size > 0) { |
|
| 1509 size_t buflen, chunklen; |
|
| 1510 |
|
| 1511 /* \0 + struct gg_msg_image_reply */ |
|
| 1512 buflen = sizeof(struct gg_msg_image_reply) + 1; |
|
| 1513 |
|
| 1514 /* w pierwszym kawałku jest nazwa pliku */ |
|
| 1515 if (r->flag == 0x05) { |
|
| 1516 strcpy(buf + buflen, filename); |
|
| 1517 buflen += strlen(filename) + 1; |
|
| 1518 } |
|
| 1519 |
|
| 1520 chunklen = ((size_t)size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t)size; |
|
| 1521 |
|
| 1522 memcpy(buf + buflen, image, chunklen); |
|
| 1523 size -= chunklen; |
|
| 1524 image += chunklen; |
|
| 1525 |
|
| 1526 res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL); |
|
| 1527 |
|
| 1528 if (res == -1) |
|
| 1529 break; |
|
| 1530 |
|
| 1531 r->flag = 0x06; |
|
| 1532 } |
|
| 1533 |
|
| 1534 return res; |
|
| 1535 } |
|
| 1536 |
|
| 1537 /* |
|
| 1538 * gg_send_message_ctcp() |
|
| 1539 * |
|
| 1540 * wysyła wiadomość do innego użytkownika. zwraca losowy numer |
|
| 1541 * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. |
|
| 1542 * |
|
| 1543 * - sess - opis sesji |
|
| 1544 * - msgclass - rodzaj wiadomości |
|
| 1545 * - recipient - numer adresata |
|
| 1546 * - message - treść wiadomości |
|
| 1547 * - message_len - długość |
|
| 1548 * |
|
| 1549 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1550 */ |
|
| 1551 int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) |
|
| 1552 { |
|
| 1553 struct gg_send_msg s; |
|
| 1554 |
|
| 1555 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); |
|
| 1556 |
|
| 1557 if (!sess) { |
|
| 1558 errno = EFAULT; |
|
| 1559 return -1; |
|
| 1560 } |
|
| 1561 |
|
| 1562 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1563 errno = ENOTCONN; |
|
| 1564 return -1; |
|
| 1565 } |
|
| 1566 |
|
| 1567 s.recipient = gg_fix32(recipient); |
|
| 1568 s.seq = gg_fix32(0); |
|
| 1569 s.msgclass = gg_fix32(msgclass); |
|
| 1570 |
|
| 1571 return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); |
|
| 1572 } |
|
| 1573 |
|
| 1574 /* |
|
| 1575 * gg_send_message() |
|
| 1576 * |
|
| 1577 * wysyła wiadomość do innego użytkownika. zwraca losowy numer |
|
| 1578 * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. |
|
| 1579 * |
|
| 1580 * - sess - opis sesji |
|
| 1581 * - msgclass - rodzaj wiadomości |
|
| 1582 * - recipient - numer adresata |
|
| 1583 * - message - treść wiadomości |
|
| 1584 * |
|
| 1585 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1586 */ |
|
| 1587 int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) |
|
| 1588 { |
|
| 1589 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); |
|
| 1590 |
|
| 1591 return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0); |
|
| 1592 } |
|
| 1593 |
|
| 1594 /* |
|
| 1595 * gg_send_message_richtext() |
|
| 1596 * |
|
| 1597 * wysyła kolorową wiadomość do innego użytkownika. zwraca losowy numer |
|
| 1598 * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. |
|
| 1599 * |
|
| 1600 * - sess - opis sesji |
|
| 1601 * - msgclass - rodzaj wiadomości |
|
| 1602 * - recipient - numer adresata |
|
| 1603 * - message - treść wiadomości |
|
| 1604 * - format - informacje o formatowaniu |
|
| 1605 * - formatlen - długość informacji o formatowaniu |
|
| 1606 * |
|
| 1607 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1608 */ |
|
| 1609 int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) |
|
| 1610 { |
|
| 1611 struct gg_send_msg s; |
|
| 1612 |
|
| 1613 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); |
|
| 1614 |
|
| 1615 if (!sess) { |
|
| 1616 errno = EFAULT; |
|
| 1617 return -1; |
|
| 1618 } |
|
| 1619 |
|
| 1620 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1621 errno = ENOTCONN; |
|
| 1622 return -1; |
|
| 1623 } |
|
| 1624 |
|
| 1625 if (!message) { |
|
| 1626 errno = EFAULT; |
|
| 1627 return -1; |
|
| 1628 } |
|
| 1629 |
|
| 1630 s.recipient = gg_fix32(recipient); |
|
| 1631 if (!sess->seq) |
|
| 1632 sess->seq = 0x01740000 | (rand() & 0xffff); |
|
| 1633 s.seq = gg_fix32(sess->seq); |
|
| 1634 s.msgclass = gg_fix32(msgclass); |
|
| 1635 sess->seq += (rand() % 0x300) + 0x300; |
|
| 1636 |
|
| 1637 if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, format, formatlen, NULL) == -1) |
|
| 1638 return -1; |
|
| 1639 |
|
| 1640 return gg_fix32(s.seq); |
|
| 1641 } |
|
| 1642 |
|
| 1643 /* |
|
| 1644 * gg_send_message_confer() |
|
| 1645 * |
|
| 1646 * wysyła wiadomość do kilku użytkownikow (konferencja). zwraca losowy numer |
|
| 1647 * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia. |
|
| 1648 * |
|
| 1649 * - sess - opis sesji |
|
| 1650 * - msgclass - rodzaj wiadomości |
|
| 1651 * - recipients_count - ilość adresatów |
|
| 1652 * - recipients - numerki adresatów |
|
| 1653 * - message - treść wiadomości |
|
| 1654 * |
|
| 1655 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1656 */ |
|
| 1657 int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) |
|
| 1658 { |
|
| 1659 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); |
|
| 1660 |
|
| 1661 return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); |
|
| 1662 } |
|
| 1663 |
|
| 1664 /* |
|
| 1665 * gg_send_message_confer_richtext() |
|
| 1666 * |
|
| 1667 * wysyła kolorową wiadomość do kilku użytkownikow (konferencja). zwraca |
|
| 1668 * losowy numer sekwencyjny, który można zignorować albo wykorzystać do |
|
| 1669 * potwierdzenia. |
|
| 1670 * |
|
| 1671 * - sess - opis sesji |
|
| 1672 * - msgclass - rodzaj wiadomości |
|
| 1673 * - recipients_count - ilość adresatów |
|
| 1674 * - recipients - numerki adresatów |
|
| 1675 * - message - treść wiadomości |
|
| 1676 * - format - informacje o formatowaniu |
|
| 1677 * - formatlen - długość informacji o formatowaniu |
|
| 1678 * |
|
| 1679 * numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1680 */ |
|
| 1681 int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) |
|
| 1682 { |
|
| 1683 struct gg_send_msg s; |
|
| 1684 struct gg_msg_recipients r; |
|
| 1685 int i, j, k; |
|
| 1686 uin_t *recps; |
|
| 1687 |
|
| 1688 gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); |
|
| 1689 |
|
| 1690 if (!sess) { |
|
| 1691 errno = EFAULT; |
|
| 1692 return -1; |
|
| 1693 } |
|
| 1694 |
|
| 1695 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1696 errno = ENOTCONN; |
|
| 1697 return -1; |
|
| 1698 } |
|
| 1699 |
|
| 1700 if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) { |
|
| 1701 errno = EINVAL; |
|
| 1702 return -1; |
|
| 1703 } |
|
| 1704 |
|
| 1705 r.flag = 0x01; |
|
| 1706 r.count = gg_fix32(recipients_count - 1); |
|
| 1707 |
|
| 1708 if (!sess->seq) |
|
| 1709 sess->seq = 0x01740000 | (rand() & 0xffff); |
|
| 1710 s.seq = gg_fix32(sess->seq); |
|
| 1711 s.msgclass = gg_fix32(msgclass); |
|
| 1712 |
|
| 1713 recps = malloc(sizeof(uin_t) * recipients_count); |
|
| 1714 if (!recps) |
|
| 1715 return -1; |
|
| 1716 |
|
| 1717 for (i = 0; i < recipients_count; i++) { |
|
| 1718 |
|
| 1719 s.recipient = gg_fix32(recipients[i]); |
|
| 1720 |
|
| 1721 for (j = 0, k = 0; j < recipients_count; j++) |
|
| 1722 if (recipients[j] != recipients[i]) { |
|
| 1723 recps[k] = gg_fix32(recipients[j]); |
|
| 1724 k++; |
|
| 1725 } |
|
| 1726 |
|
| 1727 if (!i) |
|
| 1728 sess->seq += (rand() % 0x300) + 0x300; |
|
| 1729 |
|
| 1730 if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) { |
|
| 1731 free(recps); |
|
| 1732 return -1; |
|
| 1733 } |
|
| 1734 } |
|
| 1735 |
|
| 1736 free(recps); |
|
| 1737 |
|
| 1738 return gg_fix32(s.seq); |
|
| 1739 } |
|
| 1740 |
|
| 1741 /* |
|
| 1742 * gg_ping() |
|
| 1743 * |
|
| 1744 * wysyła do serwera pakiet ping. |
|
| 1745 * |
|
| 1746 * - sess - opis sesji |
|
| 1747 * |
|
| 1748 * 0, -1. |
|
| 1749 */ |
|
| 1750 int gg_ping(struct gg_session *sess) |
|
| 1751 { |
|
| 1752 gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); |
|
| 1753 |
|
| 1754 if (!sess) { |
|
| 1755 errno = EFAULT; |
|
| 1756 return -1; |
|
| 1757 } |
|
| 1758 |
|
| 1759 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1760 errno = ENOTCONN; |
|
| 1761 return -1; |
|
| 1762 } |
|
| 1763 |
|
| 1764 return gg_send_packet(sess, GG_PING, NULL); |
|
| 1765 } |
|
| 1766 |
|
| 1767 /* |
|
| 1768 * gg_notify_ex() |
|
| 1769 * |
|
| 1770 * wysyła serwerowi listę kontaktów (wraz z odpowiadającymi im typami userów), |
|
| 1771 * dzięki czemu wie, czyj stan nas interesuje. |
|
| 1772 * |
|
| 1773 * - sess - opis sesji |
|
| 1774 * - userlist - wskaźnik do tablicy numerów |
|
| 1775 * - types - wskaźnik do tablicy typów użytkowników |
|
| 1776 * - count - ilość numerków |
|
| 1777 * |
|
| 1778 * 0, -1. |
|
| 1779 */ |
|
| 1780 int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) |
|
| 1781 { |
|
| 1782 struct gg_notify *n; |
|
| 1783 uin_t *u; |
|
| 1784 char *t; |
|
| 1785 int i, res = 0; |
|
| 1786 |
|
| 1787 gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); |
|
| 1788 |
|
| 1789 if (!sess) { |
|
| 1790 errno = EFAULT; |
|
| 1791 return -1; |
|
| 1792 } |
|
| 1793 |
|
| 1794 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1795 errno = ENOTCONN; |
|
| 1796 return -1; |
|
| 1797 } |
|
| 1798 |
|
| 1799 if (!userlist || !count) |
|
| 1800 return gg_send_packet(sess, GG_LIST_EMPTY, NULL); |
|
| 1801 |
|
| 1802 while (count > 0) { |
|
| 1803 int part_count, packet_type; |
|
| 1804 |
|
| 1805 if (count > 400) { |
|
| 1806 part_count = 400; |
|
| 1807 packet_type = GG_NOTIFY_FIRST; |
|
| 1808 } else { |
|
| 1809 part_count = count; |
|
| 1810 packet_type = GG_NOTIFY_LAST; |
|
| 1811 } |
|
| 1812 |
|
| 1813 if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) |
|
| 1814 return -1; |
|
| 1815 |
|
| 1816 for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { |
|
| 1817 n[i].uin = gg_fix32(*u); |
|
| 1818 n[i].dunno1 = *t; |
|
| 1819 } |
|
| 1820 |
|
| 1821 if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { |
|
| 1822 free(n); |
|
| 1823 res = -1; |
|
| 1824 break; |
|
| 1825 } |
|
| 1826 |
|
| 1827 count -= part_count; |
|
| 1828 userlist += part_count; |
|
| 1829 types += part_count; |
|
| 1830 |
|
| 1831 free(n); |
|
| 1832 } |
|
| 1833 |
|
| 1834 return res; |
|
| 1835 } |
|
| 1836 |
|
| 1837 /* |
|
| 1838 * gg_notify() |
|
| 1839 * |
|
| 1840 * wysyła serwerowi listę kontaktów, dzięki czemu wie, czyj stan nas |
|
| 1841 * interesuje. |
|
| 1842 * |
|
| 1843 * - sess - opis sesji |
|
| 1844 * - userlist - wskaźnik do tablicy numerów |
|
| 1845 * - count - ilość numerków |
|
| 1846 * |
|
| 1847 * 0, -1. |
|
| 1848 */ |
|
| 1849 int gg_notify(struct gg_session *sess, uin_t *userlist, int count) |
|
| 1850 { |
|
| 1851 struct gg_notify *n; |
|
| 1852 uin_t *u; |
|
| 1853 int i, res = 0; |
|
| 1854 |
|
| 1855 gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); |
|
| 1856 |
|
| 1857 if (!sess) { |
|
| 1858 errno = EFAULT; |
|
| 1859 return -1; |
|
| 1860 } |
|
| 1861 |
|
| 1862 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1863 errno = ENOTCONN; |
|
| 1864 return -1; |
|
| 1865 } |
|
| 1866 |
|
| 1867 if (!userlist || !count) |
|
| 1868 return gg_send_packet(sess, GG_LIST_EMPTY, NULL); |
|
| 1869 |
|
| 1870 while (count > 0) { |
|
| 1871 int part_count, packet_type; |
|
| 1872 |
|
| 1873 if (count > 400) { |
|
| 1874 part_count = 400; |
|
| 1875 packet_type = GG_NOTIFY_FIRST; |
|
| 1876 } else { |
|
| 1877 part_count = count; |
|
| 1878 packet_type = GG_NOTIFY_LAST; |
|
| 1879 } |
|
| 1880 |
|
| 1881 if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) |
|
| 1882 return -1; |
|
| 1883 |
|
| 1884 for (u = userlist, i = 0; i < part_count; u++, i++) { |
|
| 1885 n[i].uin = gg_fix32(*u); |
|
| 1886 n[i].dunno1 = GG_USER_NORMAL; |
|
| 1887 } |
|
| 1888 |
|
| 1889 if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { |
|
| 1890 res = -1; |
|
| 1891 free(n); |
|
| 1892 break; |
|
| 1893 } |
|
| 1894 |
|
| 1895 free(n); |
|
| 1896 |
|
| 1897 userlist += part_count; |
|
| 1898 count -= part_count; |
|
| 1899 } |
|
| 1900 |
|
| 1901 return res; |
|
| 1902 } |
|
| 1903 |
|
| 1904 /* |
|
| 1905 * gg_add_notify_ex() |
|
| 1906 * |
|
| 1907 * dodaje do listy kontaktów dany numer w trakcie połączenia. |
|
| 1908 * dodawanemu użytkownikowi określamy jego typ (patrz protocol.html) |
|
| 1909 * |
|
| 1910 * - sess - opis sesji |
|
| 1911 * - uin - numer |
|
| 1912 * - type - typ |
|
| 1913 * |
|
| 1914 * 0, -1. |
|
| 1915 */ |
|
| 1916 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type) |
|
| 1917 { |
|
| 1918 struct gg_add_remove a; |
|
| 1919 |
|
| 1920 gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); |
|
| 1921 |
|
| 1922 if (!sess) { |
|
| 1923 errno = EFAULT; |
|
| 1924 return -1; |
|
| 1925 } |
|
| 1926 |
|
| 1927 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1928 errno = ENOTCONN; |
|
| 1929 return -1; |
|
| 1930 } |
|
| 1931 |
|
| 1932 a.uin = gg_fix32(uin); |
|
| 1933 a.dunno1 = type; |
|
| 1934 |
|
| 1935 return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); |
|
| 1936 } |
|
| 1937 |
|
| 1938 /* |
|
| 1939 * gg_add_notify() |
|
| 1940 * |
|
| 1941 * dodaje do listy kontaktów dany numer w trakcie połączenia. |
|
| 1942 * |
|
| 1943 * - sess - opis sesji |
|
| 1944 * - uin - numer |
|
| 1945 * |
|
| 1946 * 0, -1. |
|
| 1947 */ |
|
| 1948 int gg_add_notify(struct gg_session *sess, uin_t uin) |
|
| 1949 { |
|
| 1950 return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); |
|
| 1951 } |
|
| 1952 |
|
| 1953 /* |
|
| 1954 * gg_remove_notify_ex() |
|
| 1955 * |
|
| 1956 * usuwa z listy kontaktów w trakcie połączenia. |
|
| 1957 * usuwanemu użytkownikowi określamy jego typ (patrz protocol.html) |
|
| 1958 * |
|
| 1959 * - sess - opis sesji |
|
| 1960 * - uin - numer |
|
| 1961 * - type - typ |
|
| 1962 * |
|
| 1963 * 0, -1. |
|
| 1964 */ |
|
| 1965 int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) |
|
| 1966 { |
|
| 1967 struct gg_add_remove a; |
|
| 1968 |
|
| 1969 gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); |
|
| 1970 |
|
| 1971 if (!sess) { |
|
| 1972 errno = EFAULT; |
|
| 1973 return -1; |
|
| 1974 } |
|
| 1975 |
|
| 1976 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1977 errno = ENOTCONN; |
|
| 1978 return -1; |
|
| 1979 } |
|
| 1980 |
|
| 1981 a.uin = gg_fix32(uin); |
|
| 1982 a.dunno1 = type; |
|
| 1983 |
|
| 1984 return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL); |
|
| 1985 } |
|
| 1986 |
|
| 1987 /* |
|
| 1988 * gg_remove_notify() |
|
| 1989 * |
|
| 1990 * usuwa z listy kontaktów w trakcie połączenia. |
|
| 1991 * |
|
| 1992 * - sess - opis sesji |
|
| 1993 * - uin - numer |
|
| 1994 * |
|
| 1995 * 0, -1. |
|
| 1996 */ |
|
| 1997 int gg_remove_notify(struct gg_session *sess, uin_t uin) |
|
| 1998 { |
|
| 1999 return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); |
|
| 2000 } |
|
| 2001 |
|
| 2002 /* |
|
| 2003 * gg_userlist_request() |
|
| 2004 * |
|
| 2005 * wysyła żądanie/zapytanie listy kontaktów na serwerze. |
|
| 2006 * |
|
| 2007 * - sess - opis sesji |
|
| 2008 * - type - rodzaj zapytania/żądania |
|
| 2009 * - request - treść zapytania/żądania (może być NULL) |
|
| 2010 * |
|
| 2011 * 0, -1 |
|
| 2012 */ |
|
| 2013 int gg_userlist_request(struct gg_session *sess, char type, const char *request) |
|
| 2014 { |
|
| 2015 int len; |
|
| 2016 |
|
| 2017 if (!sess) { |
|
| 2018 errno = EFAULT; |
|
| 2019 return -1; |
|
| 2020 } |
|
| 2021 |
|
| 2022 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2023 errno = ENOTCONN; |
|
| 2024 return -1; |
|
| 2025 } |
|
| 2026 |
|
| 2027 if (!request) { |
|
| 2028 sess->userlist_blocks = 1; |
|
| 2029 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); |
|
| 2030 } |
|
| 2031 |
|
| 2032 len = strlen(request); |
|
| 2033 |
|
| 2034 sess->userlist_blocks = 0; |
|
| 2035 |
|
| 2036 while (len > 2047) { |
|
| 2037 sess->userlist_blocks++; |
|
| 2038 |
|
| 2039 if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1) |
|
| 2040 return -1; |
|
| 2041 |
|
| 2042 if (type == GG_USERLIST_PUT) |
|
| 2043 type = GG_USERLIST_PUT_MORE; |
|
| 2044 |
|
| 2045 request += 2047; |
|
| 2046 len -= 2047; |
|
| 2047 } |
|
| 2048 |
|
| 2049 sess->userlist_blocks++; |
|
| 2050 |
|
| 2051 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); |
|
| 2052 } |
|
| 2053 |
|
| 2054 /* |
|
| 2055 * Local variables: |
|
| 2056 * c-indentation-style: k&r |
|
| 2057 * c-basic-offset: 8 |
|
| 2058 * indent-tabs-mode: notnil |
|
| 2059 * End: |
|
| 2060 * |
|
| 2061 * vim: shiftwidth=8: |
|
| 2062 */ |
|