| 1 /* $Id$ */ |
|
| 2 |
|
| 3 /* |
|
| 4 * (C) Copyright 2001-2010 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 * Adam Wysocki <gophi@ekg.chmurka.net> |
|
| 9 * |
|
| 10 * This program is free software; you can redistribute it and/or modify |
|
| 11 * it under the terms of the GNU Lesser General Public License Version |
|
| 12 * 2.1 as published by the Free Software Foundation. |
|
| 13 * |
|
| 14 * This program is distributed in the hope that it will be useful, |
|
| 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 17 * GNU Lesser General Public License for more details. |
|
| 18 * |
|
| 19 * You should have received a copy of the GNU Lesser General Public |
|
| 20 * License along with this program; if not, write to the Free Software |
|
| 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, |
|
| 22 * USA. |
|
| 23 */ |
|
| 24 |
|
| 25 /** |
|
| 26 * \file libgadu.c |
|
| 27 * |
|
| 28 * \brief Główny moduł biblioteki |
|
| 29 */ |
|
| 30 |
|
| 31 #include "strman.h" |
|
| 32 #include "network.h" |
|
| 33 #include "fileio.h" |
|
| 34 |
|
| 35 #include "libgadu.h" |
|
| 36 #include "protocol.h" |
|
| 37 #include "resolver.h" |
|
| 38 #include "internal.h" |
|
| 39 #include "encoding.h" |
|
| 40 #include "debug.h" |
|
| 41 #include "session.h" |
|
| 42 #include "message.h" |
|
| 43 #include "deflate.h" |
|
| 44 #include "tvbuilder.h" |
|
| 45 #include "protobuf.h" |
|
| 46 #include "packets.pb-c.h" |
|
| 47 |
|
| 48 #include <errno.h> |
|
| 49 #include <stdarg.h> |
|
| 50 #include <stdlib.h> |
|
| 51 #include <string.h> |
|
| 52 #include <time.h> |
|
| 53 #ifdef GG_CONFIG_HAVE_GNUTLS |
|
| 54 # include <gnutls/gnutls.h> |
|
| 55 #endif |
|
| 56 #ifdef GG_CONFIG_HAVE_OPENSSL |
|
| 57 # include <openssl/err.h> |
|
| 58 # include <openssl/rand.h> |
|
| 59 #endif |
|
| 60 |
|
| 61 /** |
|
| 62 * Port gniazda nasłuchującego dla połączeń bezpośrednich. |
|
| 63 * |
|
| 64 * \ingroup ip |
|
| 65 */ |
|
| 66 int gg_dcc_port = 0; |
|
| 67 |
|
| 68 /** |
|
| 69 * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich. |
|
| 70 * |
|
| 71 * \ingroup ip |
|
| 72 */ |
|
| 73 unsigned long gg_dcc_ip = 0; |
|
| 74 |
|
| 75 /** |
|
| 76 * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia. |
|
| 77 * |
|
| 78 * \ingroup ip |
|
| 79 */ |
|
| 80 unsigned long gg_local_ip = 0; |
|
| 81 |
|
| 82 /** |
|
| 83 * Flaga włączenia połączeń przez serwer pośredniczący. |
|
| 84 * |
|
| 85 * \ingroup proxy |
|
| 86 */ |
|
| 87 int gg_proxy_enabled = 0; |
|
| 88 |
|
| 89 /** |
|
| 90 * Adres serwera pośredniczącego. |
|
| 91 * |
|
| 92 * \ingroup proxy |
|
| 93 */ |
|
| 94 char *gg_proxy_host = NULL; |
|
| 95 |
|
| 96 /** |
|
| 97 * Port serwera pośredniczącego. |
|
| 98 * |
|
| 99 * \ingroup proxy |
|
| 100 */ |
|
| 101 int gg_proxy_port = 0; |
|
| 102 |
|
| 103 /** |
|
| 104 * Flaga używania serwera pośredniczącego jedynie dla usług HTTP. |
|
| 105 * |
|
| 106 * \ingroup proxy |
|
| 107 */ |
|
| 108 int gg_proxy_http_only = 0; |
|
| 109 |
|
| 110 /** |
|
| 111 * Nazwa użytkownika do autoryzacji serwera pośredniczącego. |
|
| 112 * |
|
| 113 * \ingroup proxy |
|
| 114 */ |
|
| 115 char *gg_proxy_username = NULL; |
|
| 116 |
|
| 117 /** |
|
| 118 * Hasło użytkownika do autoryzacji serwera pośredniczącego. |
|
| 119 * |
|
| 120 * \ingroup proxy |
|
| 121 */ |
|
| 122 char *gg_proxy_password = NULL; |
|
| 123 |
|
| 124 #ifndef DOXYGEN |
|
| 125 |
|
| 126 #ifndef lint |
|
| 127 static char rcsid[] GG_UNUSED = "$Id$"; |
|
| 128 #endif |
|
| 129 |
|
| 130 #endif /* DOXYGEN */ |
|
| 131 |
|
| 132 static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients); |
|
| 133 static void gg_compat_message_cleanup(struct gg_session *sess); |
|
| 134 |
|
| 135 #ifdef GG_CONFIG_IS_GPL_COMPLIANT |
|
| 136 /** |
|
| 137 * Symbol zdefiniowany tylko dla libgadu zgodnego z licencją GPL. |
|
| 138 * |
|
| 139 * Zwracana wartość nie jest istotna, a ponadto może się zmienić w przyszłych |
|
| 140 * wersjach biblioteki. Istotne jest tylko wywołanie tej funkcji w kodzie, który |
|
| 141 * ma być zgodny z GPL, aby wymusić jej istnienie. |
|
| 142 * |
|
| 143 * \return Wartość 1. |
|
| 144 * |
|
| 145 * \ingroup version |
|
| 146 */ |
|
| 147 int gg_is_gpl_compliant(void) |
|
| 148 { |
|
| 149 return 1; |
|
| 150 } |
|
| 151 #endif |
|
| 152 |
|
| 153 /** |
|
| 154 * Zwraca wersję biblioteki. |
|
| 155 * |
|
| 156 * \return Wskaźnik na statyczny bufor z wersją biblioteki. |
|
| 157 * |
|
| 158 * \ingroup version |
|
| 159 */ |
|
| 160 const char *gg_libgadu_version(void) |
|
| 161 { |
|
| 162 return GG_LIBGADU_VERSION; |
|
| 163 } |
|
| 164 |
|
| 165 void * gg_new0(size_t size) |
|
| 166 { |
|
| 167 void *ptr; |
|
| 168 |
|
| 169 ptr = malloc(size); |
|
| 170 if (ptr == NULL) { |
|
| 171 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 172 "//gg_new0(%" GG_SIZE_FMT |
|
| 173 ") not enough memory\n", size); |
|
| 174 return NULL; |
|
| 175 } |
|
| 176 |
|
| 177 memset(ptr, 0, size); |
|
| 178 return ptr; |
|
| 179 } |
|
| 180 |
|
| 181 int |
|
| 182 gg_required_proto(struct gg_session *gs, int protocol_version) |
|
| 183 { |
|
| 184 if (gs->protocol_version >= protocol_version) |
|
| 185 return 1; |
|
| 186 |
|
| 187 gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_ERROR, "// requested " |
|
| 188 "feature requires protocol %#02x, but %#02x is selected\n", |
|
| 189 protocol_version, gs->protocol_version); |
|
| 190 return 0; |
|
| 191 } |
|
| 192 |
|
| 193 int gg_get_dummy_fd(struct gg_session *sess) |
|
| 194 { |
|
| 195 struct gg_session_private *p = sess->private_data; |
|
| 196 |
|
| 197 if (p->dummyfds_created) |
|
| 198 return p->dummyfds[0]; |
|
| 199 |
|
| 200 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, p->dummyfds) == -1) { |
|
| 201 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_get_dummy_fd() " |
|
| 202 "unable to create pipes (errno=%d, %s)\n", |
|
| 203 errno, strerror(errno)); |
|
| 204 return -1; |
|
| 205 } |
|
| 206 |
|
| 207 p->dummyfds_created = 1; |
|
| 208 return p->dummyfds[0]; |
|
| 209 } |
|
| 210 |
|
| 211 /** |
|
| 212 * \internal Liczy skrót z hasła i ziarna. |
|
| 213 * |
|
| 214 * \param password Hasło |
|
| 215 * \param seed Ziarno podane przez serwer |
|
| 216 * |
|
| 217 * \return Wartość skrótu |
|
| 218 */ |
|
| 219 unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) |
|
| 220 { |
|
| 221 unsigned int x, y, z; |
|
| 222 |
|
| 223 y = seed; |
|
| 224 |
|
| 225 for (x = 0; *password; password++) { |
|
| 226 x = (x & 0xffffff00) | *password; |
|
| 227 y ^= x; |
|
| 228 y += x; |
|
| 229 x <<= 8; |
|
| 230 y ^= x; |
|
| 231 x <<= 8; |
|
| 232 y -= x; |
|
| 233 x <<= 8; |
|
| 234 y ^= x; |
|
| 235 |
|
| 236 z = y & 0x1F; |
|
| 237 y = (y << z) | (y >> (32 - z)); |
|
| 238 } |
|
| 239 |
|
| 240 return y; |
|
| 241 } |
|
| 242 |
|
| 243 /** |
|
| 244 * \internal Odbiera od serwera dane binarne. |
|
| 245 * |
|
| 246 * Funkcja odbiera dane od serwera zajmując się SSL/TLS w razie konieczności. |
|
| 247 * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi |
|
| 248 * wywołaniami systemowymi. |
|
| 249 * |
|
| 250 * \param sess Struktura sesji |
|
| 251 * \param buf Bufor na danymi |
|
| 252 * \param length Długość bufora |
|
| 253 * |
|
| 254 * \return To samo co funkcja systemowa \c read |
|
| 255 */ |
|
| 256 int gg_read(struct gg_session *sess, char *buf, int length) |
|
| 257 { |
|
| 258 struct gg_session_private *p = sess->private_data; |
|
| 259 int res; |
|
| 260 |
|
| 261 #ifdef GG_CONFIG_HAVE_GNUTLS |
|
| 262 if (sess->ssl != NULL) { |
|
| 263 for (;;) { |
|
| 264 res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length); |
|
| 265 |
|
| 266 if (res < 0) { |
|
| 267 if (res == GNUTLS_E_AGAIN) |
|
| 268 errno = EAGAIN; |
|
| 269 else if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED) |
|
| 270 continue; |
|
| 271 else |
|
| 272 errno = EINVAL; |
|
| 273 |
|
| 274 return -1; |
|
| 275 } |
|
| 276 |
|
| 277 return res; |
|
| 278 } |
|
| 279 } |
|
| 280 #endif |
|
| 281 |
|
| 282 #ifdef GG_CONFIG_HAVE_OPENSSL |
|
| 283 if (sess->ssl != NULL) { |
|
| 284 for (;;) { |
|
| 285 int err; |
|
| 286 |
|
| 287 res = SSL_read(sess->ssl, buf, length); |
|
| 288 |
|
| 289 if (res < 0) { |
|
| 290 err = SSL_get_error(sess->ssl, res); |
|
| 291 |
|
| 292 if (err == SSL_ERROR_SYSCALL && errno == EINTR) |
|
| 293 continue; |
|
| 294 |
|
| 295 if (err == SSL_ERROR_WANT_READ) |
|
| 296 errno = EAGAIN; |
|
| 297 else if (err != SSL_ERROR_SYSCALL) |
|
| 298 errno = EINVAL; |
|
| 299 |
|
| 300 return -1; |
|
| 301 } |
|
| 302 |
|
| 303 return res; |
|
| 304 } |
|
| 305 } |
|
| 306 #endif |
|
| 307 |
|
| 308 if (p->socket_handle != NULL) { |
|
| 309 if (p->socket_manager.read_cb == NULL) { |
|
| 310 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 311 "// gg_read() socket_manager.read callback is " |
|
| 312 "empty\n"); |
|
| 313 errno = EINVAL; |
|
| 314 return -1; |
|
| 315 } |
|
| 316 |
|
| 317 do { |
|
| 318 res = p->socket_manager.read_cb( |
|
| 319 p->socket_manager.cb_data, p->socket_handle, |
|
| 320 (unsigned char*)buf, length); |
|
| 321 } while (res < 0 && errno == EINTR); |
|
| 322 |
|
| 323 if (res < 0) { |
|
| 324 if (errno == EAGAIN) |
|
| 325 return -1; |
|
| 326 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 327 "// gg_read() unexpected errno=%d\n", errno); |
|
| 328 errno = EINVAL; |
|
| 329 } |
|
| 330 return res; |
|
| 331 } |
|
| 332 |
|
| 333 for (;;) { |
|
| 334 res = recv(sess->fd, buf, length, 0); |
|
| 335 |
|
| 336 if (res == -1 && errno == EINTR) |
|
| 337 continue; |
|
| 338 |
|
| 339 return res; |
|
| 340 } |
|
| 341 } |
|
| 342 |
|
| 343 /** |
|
| 344 * \internal Wysyła do serwera dane binarne. |
|
| 345 * |
|
| 346 * Funkcja wysyła dane do serwera zajmując się SSL/TLS w razie konieczności. |
|
| 347 * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi |
|
| 348 * wywołaniami systemowymi. |
|
| 349 * |
|
| 350 * \note Funkcja nie zajmuje się buforowaniem wysyłanych danych (patrz |
|
| 351 * gg_write()). |
|
| 352 * |
|
| 353 * \param sess Struktura sesji |
|
| 354 * \param buf Bufor z danymi |
|
| 355 * \param length Długość bufora |
|
| 356 * |
|
| 357 * \return To samo co funkcja systemowa \c write |
|
| 358 */ |
|
| 359 static int gg_write_common(struct gg_session *sess, const char *buf, int length) |
|
| 360 { |
|
| 361 struct gg_session_private *p = sess->private_data; |
|
| 362 int res; |
|
| 363 |
|
| 364 #ifdef GG_CONFIG_HAVE_GNUTLS |
|
| 365 if (sess->ssl != NULL) { |
|
| 366 for (;;) { |
|
| 367 res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length); |
|
| 368 |
|
| 369 if (res < 0) { |
|
| 370 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED) |
|
| 371 continue; |
|
| 372 |
|
| 373 if (res == GNUTLS_E_AGAIN) |
|
| 374 errno = EAGAIN; |
|
| 375 else |
|
| 376 errno = EINVAL; |
|
| 377 |
|
| 378 return -1; |
|
| 379 } |
|
| 380 |
|
| 381 return res; |
|
| 382 } |
|
| 383 } |
|
| 384 #endif |
|
| 385 |
|
| 386 #ifdef GG_CONFIG_HAVE_OPENSSL |
|
| 387 if (sess->ssl != NULL) { |
|
| 388 for (;;) { |
|
| 389 int err; |
|
| 390 |
|
| 391 res = SSL_write(sess->ssl, buf, length); |
|
| 392 |
|
| 393 if (res < 0) { |
|
| 394 err = SSL_get_error(sess->ssl, res); |
|
| 395 |
|
| 396 if (err == SSL_ERROR_SYSCALL && errno == EINTR) |
|
| 397 continue; |
|
| 398 |
|
| 399 if (err == SSL_ERROR_WANT_WRITE) |
|
| 400 errno = EAGAIN; |
|
| 401 else if (err != SSL_ERROR_SYSCALL) |
|
| 402 errno = EINVAL; |
|
| 403 |
|
| 404 return -1; |
|
| 405 } |
|
| 406 |
|
| 407 return res; |
|
| 408 } |
|
| 409 } |
|
| 410 #endif |
|
| 411 |
|
| 412 if (p->socket_handle != NULL) { |
|
| 413 if (p->socket_manager.write_cb == NULL) { |
|
| 414 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 415 "// gg_write_common() socket_manager.write " |
|
| 416 "callback is empty\n"); |
|
| 417 errno = EINVAL; |
|
| 418 return -1; |
|
| 419 } |
|
| 420 |
|
| 421 do { |
|
| 422 res = p->socket_manager.write_cb( |
|
| 423 p->socket_manager.cb_data, p->socket_handle, |
|
| 424 (const unsigned char*)buf, length); |
|
| 425 } while (res < 0 && errno == EINTR); |
|
| 426 |
|
| 427 if (res < 0) { |
|
| 428 if (errno == EAGAIN) |
|
| 429 return -1; |
|
| 430 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 431 "// gg_read() unexpected errno=%d\n", errno); |
|
| 432 errno = EINVAL; |
|
| 433 } |
|
| 434 |
|
| 435 return res; |
|
| 436 } |
|
| 437 |
|
| 438 for (;;) { |
|
| 439 res = send(sess->fd, buf, length, 0); |
|
| 440 |
|
| 441 if (res == -1 && errno == EINTR) |
|
| 442 continue; |
|
| 443 |
|
| 444 return res; |
|
| 445 } |
|
| 446 } |
|
| 447 |
|
| 448 /** |
|
| 449 * \internal Wysyła do serwera dane binarne. |
|
| 450 * |
|
| 451 * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności. |
|
| 452 * |
|
| 453 * \param sess Struktura sesji |
|
| 454 * \param buf Bufor z danymi |
|
| 455 * \param length Długość bufora |
|
| 456 * |
|
| 457 * \return To samo co funkcja systemowa \c write |
|
| 458 */ |
|
| 459 int gg_write(struct gg_session *sess, const char *buf, int length) |
|
| 460 { |
|
| 461 int res = 0; |
|
| 462 |
|
| 463 if (!sess->async) { |
|
| 464 int written = 0; |
|
| 465 |
|
| 466 while (written < length) { |
|
| 467 res = gg_write_common(sess, buf + written, length - written); |
|
| 468 |
|
| 469 if (res == -1) |
|
| 470 return -1; |
|
| 471 |
|
| 472 written += res; |
|
| 473 res = written; |
|
| 474 } |
|
| 475 } else { |
|
| 476 if (sess->send_buf == NULL) { |
|
| 477 res = gg_write_common(sess, buf, length); |
|
| 478 |
|
| 479 if (res == -1 && errno == EAGAIN) |
|
| 480 res = 0; |
|
| 481 if (res == -1) |
|
| 482 return -1; |
|
| 483 } |
|
| 484 |
|
| 485 if (res < length) { |
|
| 486 char *tmp; |
|
| 487 |
|
| 488 if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) { |
|
| 489 errno = ENOMEM; |
|
| 490 return -1; |
|
| 491 } |
|
| 492 |
|
| 493 sess->send_buf = tmp; |
|
| 494 |
|
| 495 memcpy(sess->send_buf + sess->send_left, buf + res, length - res); |
|
| 496 |
|
| 497 sess->send_left += length - res; |
|
| 498 } |
|
| 499 } |
|
| 500 |
|
| 501 return res; |
|
| 502 } |
|
| 503 |
|
| 504 void gg_close(struct gg_session *sess) |
|
| 505 { |
|
| 506 struct gg_session_private *p = sess->private_data; |
|
| 507 int errno_copy; |
|
| 508 |
|
| 509 errno_copy = errno; |
|
| 510 |
|
| 511 if (!p->socket_is_external) { |
|
| 512 if (sess->fd != -1) |
|
| 513 close(sess->fd); |
|
| 514 } else { |
|
| 515 assert(p->socket_manager_type != |
|
| 516 GG_SOCKET_MANAGER_TYPE_INTERNAL); |
|
| 517 if (p->socket_handle != NULL) { |
|
| 518 p->socket_manager.close_cb(p->socket_manager.cb_data, |
|
| 519 p->socket_handle); |
|
| 520 } |
|
| 521 p->socket_is_external = 0; |
|
| 522 } |
|
| 523 sess->fd = -1; |
|
| 524 p->socket_handle = NULL; |
|
| 525 |
|
| 526 while (p->event_queue) { |
|
| 527 gg_eventqueue_t *next = p->event_queue->next; |
|
| 528 gg_event_free(p->event_queue->event); |
|
| 529 free(p->event_queue); |
|
| 530 p->event_queue = next; |
|
| 531 } |
|
| 532 |
|
| 533 while (p->imgout_queue) { |
|
| 534 gg_imgout_queue_t *next = p->imgout_queue->next; |
|
| 535 free(p->imgout_queue); |
|
| 536 p->imgout_queue = next; |
|
| 537 } |
|
| 538 |
|
| 539 if (p->dummyfds_created) { |
|
| 540 close(p->dummyfds[0]); |
|
| 541 close(p->dummyfds[1]); |
|
| 542 p->dummyfds_created = 0; |
|
| 543 } |
|
| 544 |
|
| 545 gg_compat_message_cleanup(sess); |
|
| 546 |
|
| 547 errno = errno_copy; |
|
| 548 } |
|
| 549 |
|
| 550 /** |
|
| 551 * \internal Odbiera pakiet od serwera. |
|
| 552 * |
|
| 553 * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca |
|
| 554 * w zaalokowanym buforze. |
|
| 555 * |
|
| 556 * Przy połączeniach asynchronicznych, funkcja może nie być w stanie |
|
| 557 * skompletować całego pakietu -- w takim przypadku zwróci \c NULL, a kodem błędu |
|
| 558 * będzie \c EAGAIN. |
|
| 559 * |
|
| 560 * \param sess Struktura sesji |
|
| 561 * |
|
| 562 * \return Wskaźnik do zaalokowanego bufora |
|
| 563 */ |
|
| 564 void *gg_recv_packet(struct gg_session *sess) |
|
| 565 { |
|
| 566 struct gg_header *gh; |
|
| 567 char *packet; |
|
| 568 int res; |
|
| 569 size_t len; |
|
| 570 uint32_t ghlen = 0; |
|
| 571 |
|
| 572 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); |
|
| 573 |
|
| 574 if (sess == NULL) { |
|
| 575 errno = EFAULT; |
|
| 576 return NULL; |
|
| 577 } |
|
| 578 |
|
| 579 for (;;) { |
|
| 580 if (sess->recv_buf == NULL && sess->recv_done == 0) { |
|
| 581 sess->recv_buf = malloc(sizeof(struct gg_header) + 1); |
|
| 582 |
|
| 583 if (sess->recv_buf == NULL) { |
|
| 584 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n"); |
|
| 585 return NULL; |
|
| 586 } |
|
| 587 } |
|
| 588 |
|
| 589 gh = (struct gg_header*) sess->recv_buf; |
|
| 590 |
|
| 591 if ((size_t) sess->recv_done < sizeof(struct gg_header)) { |
|
| 592 len = sizeof(struct gg_header) - sess->recv_done; |
|
| 593 gg_debug_session(sess, GG_DEBUG_NET, |
|
| 594 "// gg_recv_packet() header: %d done, " |
|
| 595 "%" GG_SIZE_FMT " to go\n", |
|
| 596 sess->recv_done, len); |
|
| 597 } else { |
|
| 598 ghlen = gh ? gg_fix32(gh->length) : 0; |
|
| 599 |
|
| 600 if (ghlen > 65535) { |
|
| 601 gg_debug_session(sess, GG_DEBUG_ERROR, |
|
| 602 "// gg_recv_packet() invalid packet " |
|
| 603 "length (%d)\n", ghlen); |
|
| 604 errno = ERANGE; |
|
| 605 goto fail; |
|
| 606 } |
|
| 607 |
|
| 608 if ((size_t) sess->recv_done >= sizeof(struct gg_header) + ghlen) { |
|
| 609 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() and that's it\n"); |
|
| 610 break; |
|
| 611 } |
|
| 612 |
|
| 613 len = sizeof(struct gg_header) + ghlen - sess->recv_done; |
|
| 614 |
|
| 615 gg_debug_session(sess, GG_DEBUG_NET, |
|
| 616 "// gg_recv_packet() payload: %d done, " |
|
| 617 "%u length, %" GG_SIZE_FMT " to go\n", |
|
| 618 sess->recv_done, ghlen, len); |
|
| 619 } |
|
| 620 |
|
| 621 res = gg_read(sess, sess->recv_buf + sess->recv_done, len); |
|
| 622 |
|
| 623 if (res == 0) { |
|
| 624 errno = ECONNRESET; |
|
| 625 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() connection broken\n"); |
|
| 626 goto fail; |
|
| 627 } |
|
| 628 |
|
| 629 if (res == -1 && errno == EAGAIN) { |
|
| 630 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() resource temporarily unavailable\n"); |
|
| 631 goto eagain; |
|
| 632 } |
|
| 633 |
|
| 634 if (res == -1) { |
|
| 635 gg_debug_session(sess, GG_DEBUG_ERROR, |
|
| 636 "// gg_recv_packet() read failed: errno=%d, " |
|
| 637 "%s\n", errno, strerror(errno)); |
|
| 638 goto fail; |
|
| 639 } |
|
| 640 |
|
| 641 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() read %d bytes\n", res); |
|
| 642 |
|
| 643 if (sess->recv_done + res == sizeof(struct gg_header)) { |
|
| 644 char *tmp; |
|
| 645 ghlen = gh ? gg_fix32(gh->length) : 0; |
|
| 646 |
|
| 647 gg_debug_session(sess, GG_DEBUG_NET, |
|
| 648 "// gg_recv_packet() header complete, " |
|
| 649 "payload %d bytes\n", ghlen); |
|
| 650 |
|
| 651 if (ghlen == 0) |
|
| 652 break; |
|
| 653 |
|
| 654 if (ghlen > 65535) { |
|
| 655 gg_debug_session(sess, GG_DEBUG_ERROR, |
|
| 656 "// gg_recv_packet() invalid packet " |
|
| 657 "length (%d)\n", ghlen); |
|
| 658 errno = ERANGE; |
|
| 659 goto fail; |
|
| 660 } |
|
| 661 |
|
| 662 tmp = realloc(sess->recv_buf, sizeof(struct gg_header) + ghlen + 1); |
|
| 663 |
|
| 664 if (tmp == NULL) { |
|
| 665 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n"); |
|
| 666 goto fail; |
|
| 667 } |
|
| 668 |
|
| 669 sess->recv_buf = tmp; |
|
| 670 } |
|
| 671 |
|
| 672 sess->recv_done += res; |
|
| 673 } |
|
| 674 |
|
| 675 packet = sess->recv_buf; |
|
| 676 sess->recv_buf = NULL; |
|
| 677 sess->recv_done = 0; |
|
| 678 |
|
| 679 if (gh == NULL) |
|
| 680 goto fail; |
|
| 681 |
|
| 682 /* Czasami zakładamy, że teksty w pakietach są zakończone zerem */ |
|
| 683 packet[sizeof(struct gg_header) + ghlen] = 0; |
|
| 684 |
|
| 685 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet(type=0x%.2x, " |
|
| 686 "length=%d)\n", gg_fix32(gh->type), ghlen); |
|
| 687 gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(struct gg_header) + ghlen); |
|
| 688 |
|
| 689 gh->type = gg_fix32(gh->type); |
|
| 690 gh->length = ghlen; |
|
| 691 |
|
| 692 return packet; |
|
| 693 |
|
| 694 fail: |
|
| 695 free(sess->recv_buf); |
|
| 696 sess->recv_buf = NULL; |
|
| 697 sess->recv_done = 0; |
|
| 698 |
|
| 699 eagain: |
|
| 700 return NULL; |
|
| 701 } |
|
| 702 |
|
| 703 /** |
|
| 704 * \internal Wysyła pakiet do serwera. |
|
| 705 * |
|
| 706 * Funkcja konstruuje pakiet do wysłania z dowolnej liczby fragmentów. Jeśli |
|
| 707 * rozmiar pakietu jest za duży, by móc go wysłać za jednym razem, pozostała |
|
| 708 * część zostanie zakolejkowana i wysłana, gdy będzie to możliwe. |
|
| 709 * |
|
| 710 * \param sess Struktura sesji |
|
| 711 * \param type Rodzaj pakietu |
|
| 712 * \param ... Lista kolejnych części pakietu (wskaźnik na bufor i długość |
|
| 713 * typu \c int) zakończona \c NULL |
|
| 714 * |
|
| 715 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 716 */ |
|
| 717 int gg_send_packet(struct gg_session *sess, int type, ...) |
|
| 718 { |
|
| 719 struct gg_header *h; |
|
| 720 char *tmp; |
|
| 721 unsigned int tmp_length; |
|
| 722 void *payload; |
|
| 723 unsigned int payload_length; |
|
| 724 va_list ap; |
|
| 725 int res; |
|
| 726 |
|
| 727 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type); |
|
| 728 |
|
| 729 tmp_length = sizeof(struct gg_header); |
|
| 730 |
|
| 731 if (!(tmp = malloc(tmp_length))) { |
|
| 732 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for packet header\n"); |
|
| 733 return -1; |
|
| 734 } |
|
| 735 |
|
| 736 va_start(ap, type); |
|
| 737 |
|
| 738 payload = va_arg(ap, void *); |
|
| 739 |
|
| 740 while (payload) { |
|
| 741 char *tmp2; |
|
| 742 |
|
| 743 payload_length = va_arg(ap, unsigned int); |
|
| 744 |
|
| 745 if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) { |
|
| 746 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for payload\n"); |
|
| 747 free(tmp); |
|
| 748 va_end(ap); |
|
| 749 return -1; |
|
| 750 } |
|
| 751 |
|
| 752 tmp = tmp2; |
|
| 753 |
|
| 754 memcpy(tmp + tmp_length, payload, payload_length); |
|
| 755 tmp_length += payload_length; |
|
| 756 |
|
| 757 payload = va_arg(ap, void *); |
|
| 758 } |
|
| 759 |
|
| 760 va_end(ap); |
|
| 761 |
|
| 762 h = (struct gg_header*) tmp; |
|
| 763 h->type = gg_fix32(type); |
|
| 764 h->length = gg_fix32(tmp_length - sizeof(struct gg_header)); |
|
| 765 |
|
| 766 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet(type=0x%.2x, " |
|
| 767 "length=%d)\n", gg_fix32(h->type), gg_fix32(h->length)); |
|
| 768 gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length); |
|
| 769 |
|
| 770 res = gg_write(sess, tmp, tmp_length); |
|
| 771 |
|
| 772 free(tmp); |
|
| 773 |
|
| 774 if (res == -1) { |
|
| 775 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() " |
|
| 776 "write() failed. res = %d, errno = %d (%s)\n", |
|
| 777 res, errno, strerror(errno)); |
|
| 778 return -1; |
|
| 779 } |
|
| 780 |
|
| 781 if (sess->async) { |
|
| 782 gg_debug_session(sess, GG_DEBUG_NET, "// gg_send_packet() " |
|
| 783 "partial write(), %d sent, %d left, %d total left\n", |
|
| 784 res, tmp_length - res, sess->send_left); |
|
| 785 } |
|
| 786 |
|
| 787 if (sess->send_buf) |
|
| 788 sess->check |= GG_CHECK_WRITE; |
|
| 789 |
|
| 790 return 0; |
|
| 791 } |
|
| 792 |
|
| 793 /** |
|
| 794 * \internal Funkcja zwrotna sesji. |
|
| 795 * |
|
| 796 * Pole \c callback struktury \c gg_session zawiera wskaźnik do tej funkcji. |
|
| 797 * Wywołuje ona \c gg_watch_fd i zachowuje wynik w polu \c event. |
|
| 798 * |
|
| 799 * \note Korzystanie z tej funkcjonalności nie jest już zalecane. |
|
| 800 * |
|
| 801 * \param sess Struktura sesji |
|
| 802 * |
|
| 803 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 804 */ |
|
| 805 static int gg_session_callback(struct gg_session *sess) |
|
| 806 { |
|
| 807 if (!sess) { |
|
| 808 errno = EFAULT; |
|
| 809 return -1; |
|
| 810 } |
|
| 811 |
|
| 812 return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1; |
|
| 813 } |
|
| 814 |
|
| 815 /** |
|
| 816 * Łączy się z serwerem Gadu-Gadu. |
|
| 817 * |
|
| 818 * Przy połączeniu synchronicznym funkcja zakończy działanie po nawiązaniu |
|
| 819 * połączenia lub gdy wystąpi błąd. Po udanym połączeniu należy wywoływać |
|
| 820 * funkcję \c gg_watch_fd(), która odbiera informacje od serwera i zwraca |
|
| 821 * informacje o zdarzeniach. |
|
| 822 * |
|
| 823 * Przy połączeniu asynchronicznym funkcja rozpocznie procedurę połączenia |
|
| 824 * i zwróci zaalokowaną strukturę. Pole \c fd struktury \c gg_session zawiera |
|
| 825 * deskryptor, który należy obserwować funkcją \c select, \c poll lub za |
|
| 826 * pomocą mechanizmów użytej pętli zdarzeń (Glib, Qt itp.). Pole \c check |
|
| 827 * jest maską bitową mówiącą, czy biblioteka chce być informowana o możliwości |
|
| 828 * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE). |
|
| 829 * Po zaobserwowaniu zmian na deskryptorze należy wywołać funkcję |
|
| 830 * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie |
|
| 831 * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę |
|
| 832 * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD. |
|
| 833 * |
|
| 834 * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów |
|
| 835 * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex(). |
|
| 836 * |
|
| 837 * \note Funkcja zwróci błąd ENOSYS jeśli połączenie SSL było wymagane, ale |
|
| 838 * obsługa SSL nie jest wkompilowana. |
|
| 839 * |
|
| 840 * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin, |
|
| 841 * password, async. |
|
| 842 * |
|
| 843 * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL |
|
| 844 * w przypadku błędu. |
|
| 845 * |
|
| 846 * \ingroup login |
|
| 847 */ |
|
| 848 struct gg_session *gg_login(const struct gg_login_params *p) |
|
| 849 { |
|
| 850 struct gg_session *sess = NULL; |
|
| 851 struct gg_session_private *sess_private = NULL; |
|
| 852 |
|
| 853 if (p == NULL) { |
|
| 854 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p); |
|
| 855 errno = EFAULT; |
|
| 856 return NULL; |
|
| 857 } |
|
| 858 |
|
| 859 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async); |
|
| 860 |
|
| 861 sess = malloc(sizeof(struct gg_session)); |
|
| 862 |
|
| 863 if (sess == NULL) { |
|
| 864 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n"); |
|
| 865 goto fail; |
|
| 866 } |
|
| 867 |
|
| 868 memset(sess, 0, sizeof(struct gg_session)); |
|
| 869 sess->fd = -1; |
|
| 870 |
|
| 871 sess_private = malloc(sizeof(struct gg_session_private)); |
|
| 872 |
|
| 873 if (sess_private == NULL) { |
|
| 874 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session private data\n"); |
|
| 875 goto fail; |
|
| 876 } |
|
| 877 |
|
| 878 memset(sess_private, 0, sizeof(struct gg_session_private)); |
|
| 879 sess->private_data = sess_private; |
|
| 880 |
|
| 881 if (p->password == NULL || p->uin == 0) { |
|
| 882 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n"); |
|
| 883 errno = EFAULT; |
|
| 884 goto fail; |
|
| 885 } |
|
| 886 |
|
| 887 if (!(sess->password = strdup(p->password))) { |
|
| 888 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n"); |
|
| 889 goto fail; |
|
| 890 } |
|
| 891 |
|
| 892 if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) { |
|
| 893 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type); |
|
| 894 errno = EFAULT; |
|
| 895 goto fail; |
|
| 896 } |
|
| 897 |
|
| 898 sess->uin = p->uin; |
|
| 899 sess->state = GG_STATE_RESOLVING; |
|
| 900 sess->check = GG_CHECK_READ; |
|
| 901 sess->timeout = GG_DEFAULT_TIMEOUT; |
|
| 902 sess->async = p->async; |
|
| 903 sess->type = GG_SESSION_GG; |
|
| 904 sess->initial_status = p->status; |
|
| 905 sess->callback = gg_session_callback; |
|
| 906 sess->destroy = gg_free_session; |
|
| 907 sess->port = p->server_port; |
|
| 908 sess->server_addr = p->server_addr; |
|
| 909 sess->external_port = p->external_port; |
|
| 910 sess->external_addr = p->external_addr; |
|
| 911 sess->client_addr = p->client_addr; |
|
| 912 sess->client_port = p->client_port; |
|
| 913 |
|
| 914 if (GG_LOGIN_PARAMS_HAS_FIELD(p, compatibility)) |
|
| 915 sess_private->compatibility = p->compatibility; |
|
| 916 |
|
| 917 if (GG_LOGIN_PARAMS_HAS_FIELD(p, connect_host) && p->connect_host != NULL) { |
|
| 918 int port = 0; |
|
| 919 char *colon; |
|
| 920 |
|
| 921 sess->connect_host = strdup(p->connect_host); |
|
| 922 if (sess->connect_host == NULL) |
|
| 923 goto fail; |
|
| 924 |
|
| 925 colon = strchr(sess->connect_host, ':'); |
|
| 926 if (colon != NULL) { |
|
| 927 colon[0] = '\0'; |
|
| 928 port = atoi(colon + 1); |
|
| 929 } |
|
| 930 if (port > 0) |
|
| 931 sess->port = port; |
|
| 932 } |
|
| 933 |
|
| 934 if (GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager_type) && |
|
| 935 GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager) && |
|
| 936 p->socket_manager_type != GG_SOCKET_MANAGER_TYPE_INTERNAL) |
|
| 937 { |
|
| 938 if ((unsigned int)p->socket_manager_type > |
|
| 939 GG_SOCKET_MANAGER_TYPE_TLS) |
|
| 940 { |
|
| 941 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_login()" |
|
| 942 " invalid arguments. unknown socket manager " |
|
| 943 "type (%d)\n", p->socket_manager_type); |
|
| 944 errno = EFAULT; |
|
| 945 goto fail; |
|
| 946 } else { |
|
| 947 sess_private->socket_manager_type = |
|
| 948 p->socket_manager_type; |
|
| 949 memcpy(&sess_private->socket_manager, |
|
| 950 &p->socket_manager, |
|
| 951 sizeof(gg_socket_manager_t)); |
|
| 952 } |
|
| 953 } else { |
|
| 954 sess_private->socket_manager_type = |
|
| 955 GG_SOCKET_MANAGER_TYPE_INTERNAL; |
|
| 956 } |
|
| 957 |
|
| 958 if (GG_LOGIN_PARAMS_HAS_FIELD(p, host_white_list) && |
|
| 959 p->host_white_list != NULL) |
|
| 960 { |
|
| 961 sess_private->host_white_list = |
|
| 962 gg_strarr_dup(p->host_white_list); |
|
| 963 if (sess_private->host_white_list == NULL) |
|
| 964 goto fail; |
|
| 965 } |
|
| 966 |
|
| 967 if (p->protocol_features == 0) { |
|
| 968 sess->protocol_features = GG_FEATURE_MSG80 | |
|
| 969 GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | |
|
| 970 GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | |
|
| 971 GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | |
|
| 972 GG_FEATURE_TYPING_NOTIFICATION; |
|
| 973 } else { |
|
| 974 sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77)); |
|
| 975 |
|
| 976 if (!(p->protocol_features & GG_FEATURE_STATUS77)) |
|
| 977 sess->protocol_features |= GG_FEATURE_STATUS80; |
|
| 978 |
|
| 979 if (!(p->protocol_features & GG_FEATURE_MSG77)) |
|
| 980 sess->protocol_features |= GG_FEATURE_MSG80; |
|
| 981 } |
|
| 982 |
|
| 983 if (!(sess->status_flags = p->status_flags)) |
|
| 984 sess->status_flags = GG_STATUS_FLAG_UNKNOWN | GG_STATUS_FLAG_SPAM; |
|
| 985 |
|
| 986 if (!p->protocol_version) |
|
| 987 sess->protocol_version = GG_DEFAULT_PROTOCOL_VERSION; |
|
| 988 else if (p->protocol_version < 0x2e) { |
|
| 989 gg_debug(GG_DEBUG_MISC, "// gg_login() libgadu no longer support protocol < 0x2e\n"); |
|
| 990 sess->protocol_version = 0x2e; |
|
| 991 } else |
|
| 992 sess->protocol_version = p->protocol_version; |
|
| 993 |
|
| 994 if (p->client_version && strcmp(p->client_version, "-") != 0) |
|
| 995 sess->client_version = strdup(p->client_version); |
|
| 996 sess->last_sysmsg = p->last_sysmsg; |
|
| 997 sess->image_size = p->image_size; |
|
| 998 sess->pid = -1; |
|
| 999 sess->encoding = p->encoding; |
|
| 1000 |
|
| 1001 if (gg_session_set_resolver(sess, p->resolver) == -1) { |
|
| 1002 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. " |
|
| 1003 "unsupported resolver type (%d)\n", p->resolver); |
|
| 1004 errno = EFAULT; |
|
| 1005 goto fail; |
|
| 1006 } |
|
| 1007 |
|
| 1008 if (p->status_descr) { |
|
| 1009 sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1); |
|
| 1010 |
|
| 1011 if (!sess->initial_descr) { |
|
| 1012 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); |
|
| 1013 goto fail; |
|
| 1014 } |
|
| 1015 |
|
| 1016 /* XXX pamiętać, żeby nie ciąć w środku znaku utf-8 */ |
|
| 1017 |
|
| 1018 if (strlen(sess->initial_descr) > GG_STATUS_DESCR_MAXSIZE) |
|
| 1019 sess->initial_descr[GG_STATUS_DESCR_MAXSIZE] = 0; |
|
| 1020 } |
|
| 1021 |
|
| 1022 if (p->tls != GG_SSL_DISABLED) { |
|
| 1023 #if !defined(GG_CONFIG_HAVE_GNUTLS) && !defined(GG_CONFIG_HAVE_OPENSSL) |
|
| 1024 gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); |
|
| 1025 |
|
| 1026 if (p->tls == GG_SSL_REQUIRED) { |
|
| 1027 errno = ENOSYS; |
|
| 1028 goto fail; |
|
| 1029 } |
|
| 1030 #else |
|
| 1031 sess->ssl_flag = p->tls; |
|
| 1032 #endif |
|
| 1033 } |
|
| 1034 |
|
| 1035 if (p->hash_type) |
|
| 1036 sess->hash_type = p->hash_type; |
|
| 1037 else |
|
| 1038 sess->hash_type = GG_LOGIN_HASH_SHA1; |
|
| 1039 |
|
| 1040 if (sess->server_addr == 0 && sess->connect_host == NULL) { |
|
| 1041 if (gg_proxy_enabled) { |
|
| 1042 sess->resolver_host = gg_proxy_host; |
|
| 1043 sess->proxy_port = gg_proxy_port; |
|
| 1044 sess->state = (sess->async) ? |
|
| 1045 GG_STATE_RESOLVE_PROXY_HUB_ASYNC : |
|
| 1046 GG_STATE_RESOLVE_PROXY_HUB_SYNC; |
|
| 1047 } else { |
|
| 1048 sess->resolver_host = GG_APPMSG_HOST; |
|
| 1049 sess->proxy_port = 0; |
|
| 1050 sess->state = (sess->async) ? GG_STATE_RESOLVE_HUB_ASYNC : GG_STATE_RESOLVE_HUB_SYNC; |
|
| 1051 } |
|
| 1052 } else { |
|
| 1053 if (sess->connect_host != NULL) |
|
| 1054 sess->server_addr = 0; |
|
| 1055 else { |
|
| 1056 /* XXX inet_ntoa i wielowątkowość */ |
|
| 1057 sess->connect_host = strdup(inet_ntoa(*(struct in_addr*) &sess->server_addr)); |
|
| 1058 if (sess->connect_host == NULL) |
|
| 1059 goto fail; |
|
| 1060 } |
|
| 1061 sess->connect_index = 0; |
|
| 1062 |
|
| 1063 if (gg_proxy_enabled) { |
|
| 1064 sess->resolver_host = gg_proxy_host; |
|
| 1065 sess->proxy_port = gg_proxy_port; |
|
| 1066 if (sess->port == 0) |
|
| 1067 sess->connect_port[0] = GG_HTTPS_PORT; |
|
| 1068 else |
|
| 1069 sess->connect_port[0] = sess->port; |
|
| 1070 sess->connect_port[1] = 0; |
|
| 1071 sess->state = (sess->async) ? GG_STATE_RESOLVE_PROXY_GG_ASYNC : GG_STATE_RESOLVE_PROXY_GG_SYNC; |
|
| 1072 } else { |
|
| 1073 sess->resolver_host = sess->connect_host; |
|
| 1074 if (sess->port == 0) { |
|
| 1075 if (sess->ssl_flag == GG_SSL_DISABLED) { |
|
| 1076 sess->connect_port[0] = GG_DEFAULT_PORT; |
|
| 1077 sess->connect_port[1] = GG_HTTPS_PORT; |
|
| 1078 } else { |
|
| 1079 sess->connect_port[0] = GG_HTTPS_PORT; |
|
| 1080 sess->connect_port[1] = 0; |
|
| 1081 } |
|
| 1082 } else { |
|
| 1083 sess->connect_port[0] = sess->port; |
|
| 1084 sess->connect_port[1] = 0; |
|
| 1085 } |
|
| 1086 sess->state = (sess->async) ? GG_STATE_RESOLVE_GG_ASYNC : GG_STATE_RESOLVE_GG_SYNC; |
|
| 1087 } |
|
| 1088 } |
|
| 1089 |
|
| 1090 /* XXX inaczej gg_watch_fd() wyjdzie z timeoutem */ |
|
| 1091 sess->timeout = GG_DEFAULT_TIMEOUT; |
|
| 1092 |
|
| 1093 if (!sess->async) { |
|
| 1094 while (!GG_SESSION_IS_CONNECTED(sess)) { |
|
| 1095 struct gg_event *ge; |
|
| 1096 |
|
| 1097 ge = gg_watch_fd(sess); |
|
| 1098 |
|
| 1099 if (ge == NULL) { |
|
| 1100 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n"); |
|
| 1101 goto fail; |
|
| 1102 } |
|
| 1103 |
|
| 1104 if (ge->type == GG_EVENT_CONN_FAILED) { |
|
| 1105 errno = EACCES; |
|
| 1106 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() could not login\n"); |
|
| 1107 gg_event_free(ge); |
|
| 1108 goto fail; |
|
| 1109 } |
|
| 1110 |
|
| 1111 gg_event_free(ge); |
|
| 1112 } |
|
| 1113 } else { |
|
| 1114 struct gg_event *ge; |
|
| 1115 |
|
| 1116 ge = gg_watch_fd(sess); |
|
| 1117 |
|
| 1118 if (ge == NULL) { |
|
| 1119 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n"); |
|
| 1120 goto fail; |
|
| 1121 } |
|
| 1122 |
|
| 1123 gg_event_free(ge); |
|
| 1124 } |
|
| 1125 |
|
| 1126 return sess; |
|
| 1127 |
|
| 1128 fail: |
|
| 1129 gg_free_session(sess); |
|
| 1130 |
|
| 1131 return NULL; |
|
| 1132 } |
|
| 1133 |
|
| 1134 /** |
|
| 1135 * Wysyła do serwera pakiet utrzymania połączenia. |
|
| 1136 * |
|
| 1137 * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia, |
|
| 1138 * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie |
|
| 1139 * połączenie. |
|
| 1140 * |
|
| 1141 * \param sess Struktura sesji |
|
| 1142 * |
|
| 1143 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 1144 * |
|
| 1145 * \ingroup login |
|
| 1146 */ |
|
| 1147 int gg_ping(struct gg_session *sess) |
|
| 1148 { |
|
| 1149 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); |
|
| 1150 |
|
| 1151 if (!sess) { |
|
| 1152 errno = EFAULT; |
|
| 1153 return -1; |
|
| 1154 } |
|
| 1155 |
|
| 1156 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1157 errno = ENOTCONN; |
|
| 1158 return -1; |
|
| 1159 } |
|
| 1160 |
|
| 1161 return gg_send_packet(sess, GG_PING, NULL); |
|
| 1162 } |
|
| 1163 |
|
| 1164 /** |
|
| 1165 * Kończy połączenie z serwerem. |
|
| 1166 * |
|
| 1167 * Funkcja nie zwalnia zasobów, więc po jej wywołaniu należy użyć |
|
| 1168 * \c gg_free_session(). Jeśli chce się ustawić opis niedostępności, należy |
|
| 1169 * wcześniej wywołać funkcję \c gg_change_status_descr() lub |
|
| 1170 * \c gg_change_status_descr_time(). |
|
| 1171 * |
|
| 1172 * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze |
|
| 1173 * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one |
|
| 1174 * utracone przy zrywaniu połączenia. Aby mieć pewność, że opis statusu |
|
| 1175 * zostanie zachowany, należy ustawić stan \c GG_STATUS_NOT_AVAIL_DESCR |
|
| 1176 * za pomocą funkcji \c gg_change_status_descr() i poczekać na zdarzenie |
|
| 1177 * \c GG_EVENT_DISCONNECT_ACK. |
|
| 1178 * |
|
| 1179 * \param sess Struktura sesji |
|
| 1180 * |
|
| 1181 * \ingroup login |
|
| 1182 */ |
|
| 1183 void gg_logoff(struct gg_session *sess) |
|
| 1184 { |
|
| 1185 if (!sess) |
|
| 1186 return; |
|
| 1187 |
|
| 1188 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); |
|
| 1189 |
|
| 1190 #ifdef GG_CONFIG_HAVE_GNUTLS |
|
| 1191 if (sess->ssl != NULL) |
|
| 1192 gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR); |
|
| 1193 #endif |
|
| 1194 |
|
| 1195 #ifdef GG_CONFIG_HAVE_OPENSSL |
|
| 1196 if (sess->ssl != NULL) |
|
| 1197 SSL_shutdown(sess->ssl); |
|
| 1198 #endif |
|
| 1199 |
|
| 1200 sess->resolver_cleanup(&sess->resolver, 1); |
|
| 1201 |
|
| 1202 gg_close(sess); |
|
| 1203 |
|
| 1204 if (sess->send_buf) { |
|
| 1205 free(sess->send_buf); |
|
| 1206 sess->send_buf = NULL; |
|
| 1207 sess->send_left = 0; |
|
| 1208 } |
|
| 1209 } |
|
| 1210 |
|
| 1211 /** |
|
| 1212 * Zwalnia zasoby używane przez połączenie z serwerem. Funkcję należy wywołać |
|
| 1213 * po zamknięciu połączenia z serwerem, by nie doprowadzić do wycieku zasobów |
|
| 1214 * systemowych. |
|
| 1215 * |
|
| 1216 * \param sess Struktura sesji |
|
| 1217 * |
|
| 1218 * \ingroup login |
|
| 1219 */ |
|
| 1220 void gg_free_session(struct gg_session *sess) |
|
| 1221 { |
|
| 1222 struct gg_dcc7 *dcc; |
|
| 1223 gg_chat_list_t *chat; |
|
| 1224 |
|
| 1225 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_free_session(%p);\n", sess); |
|
| 1226 |
|
| 1227 if (sess == NULL) |
|
| 1228 return; |
|
| 1229 |
|
| 1230 /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */ |
|
| 1231 |
|
| 1232 free(sess->resolver_result); |
|
| 1233 free(sess->connect_host); |
|
| 1234 free(sess->password); |
|
| 1235 free(sess->initial_descr); |
|
| 1236 free(sess->client_version); |
|
| 1237 free(sess->header_buf); |
|
| 1238 free(sess->recv_buf); |
|
| 1239 |
|
| 1240 #ifdef GG_CONFIG_HAVE_GNUTLS |
|
| 1241 if (sess->ssl != NULL) { |
|
| 1242 gg_session_gnutls_t *tmp; |
|
| 1243 |
|
| 1244 tmp = (gg_session_gnutls_t*) sess->ssl; |
|
| 1245 gnutls_deinit(tmp->session); |
|
| 1246 gnutls_certificate_free_credentials(tmp->xcred); |
|
| 1247 gnutls_global_deinit(); |
|
| 1248 free(sess->ssl); |
|
| 1249 } |
|
| 1250 #endif |
|
| 1251 |
|
| 1252 #ifdef GG_CONFIG_HAVE_OPENSSL |
|
| 1253 if (sess->ssl) |
|
| 1254 SSL_free(sess->ssl); |
|
| 1255 |
|
| 1256 if (sess->ssl_ctx) |
|
| 1257 SSL_CTX_free(sess->ssl_ctx); |
|
| 1258 #endif |
|
| 1259 |
|
| 1260 if (sess->resolver_cleanup != NULL) |
|
| 1261 sess->resolver_cleanup(&sess->resolver, 1); |
|
| 1262 |
|
| 1263 gg_close(sess); |
|
| 1264 |
|
| 1265 while (sess->images) { |
|
| 1266 struct gg_image_queue *next = sess->images->next; |
|
| 1267 |
|
| 1268 gg_image_queue_remove(sess, sess->images, 1); |
|
| 1269 |
|
| 1270 /* a fix for false-positive NULL-dereference */ |
|
| 1271 sess->images = next; |
|
| 1272 } |
|
| 1273 |
|
| 1274 free(sess->send_buf); |
|
| 1275 |
|
| 1276 for (dcc = sess->dcc7_list; dcc; dcc = dcc->next) |
|
| 1277 dcc->sess = NULL; |
|
| 1278 |
|
| 1279 chat = sess->private_data->chat_list; |
|
| 1280 while (chat != NULL) { |
|
| 1281 gg_chat_list_t *next = chat->next; |
|
| 1282 free(chat->participants); |
|
| 1283 free(chat); |
|
| 1284 chat = next; |
|
| 1285 } |
|
| 1286 |
|
| 1287 gg_strarr_free(sess->private_data->host_white_list); |
|
| 1288 |
|
| 1289 free(sess->private_data); |
|
| 1290 |
|
| 1291 free(sess); |
|
| 1292 } |
|
| 1293 |
|
| 1294 /** |
|
| 1295 * Zmienia status użytkownika. |
|
| 1296 * |
|
| 1297 * \param sess Struktura sesji |
|
| 1298 * \param status Nowy status użytkownika |
|
| 1299 * |
|
| 1300 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 1301 * |
|
| 1302 * \ingroup status |
|
| 1303 */ |
|
| 1304 int gg_change_status(struct gg_session *sess, int status) |
|
| 1305 { |
|
| 1306 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); |
|
| 1307 |
|
| 1308 return gg_change_status_descr(sess, status, NULL); |
|
| 1309 } |
|
| 1310 |
|
| 1311 /** |
|
| 1312 * Zmienia status użytkownika na status opisowy. |
|
| 1313 * |
|
| 1314 * \param sess Struktura sesji |
|
| 1315 * \param status Nowy status użytkownika |
|
| 1316 * \param descr Opis statusu użytkownika (lub \c NULL) |
|
| 1317 * |
|
| 1318 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 1319 * |
|
| 1320 * \ingroup status |
|
| 1321 */ |
|
| 1322 int gg_change_status_descr(struct gg_session *sess, int status, const char *descr) |
|
| 1323 { |
|
| 1324 struct gg_new_status80 p; |
|
| 1325 char *gen_descr = NULL; |
|
| 1326 int descr_len = 0; |
|
| 1327 int descr_null_len = 0; |
|
| 1328 int res; |
|
| 1329 |
|
| 1330 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); |
|
| 1331 |
|
| 1332 if (!sess) { |
|
| 1333 errno = EFAULT; |
|
| 1334 return -1; |
|
| 1335 } |
|
| 1336 |
|
| 1337 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1338 errno = ENOTCONN; |
|
| 1339 return -1; |
|
| 1340 } |
|
| 1341 |
|
| 1342 sess->status = status; |
|
| 1343 |
|
| 1344 if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) { |
|
| 1345 descr = gen_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1); |
|
| 1346 |
|
| 1347 if (!gen_descr) |
|
| 1348 return -1; |
|
| 1349 } |
|
| 1350 |
|
| 1351 if (descr) { |
|
| 1352 descr_len = strlen(descr); |
|
| 1353 |
|
| 1354 if (descr_len > GG_STATUS_DESCR_MAXSIZE) |
|
| 1355 descr_len = GG_STATUS_DESCR_MAXSIZE; |
|
| 1356 |
|
| 1357 /* XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8 */ |
|
| 1358 } else { |
|
| 1359 descr = ""; |
|
| 1360 } |
|
| 1361 |
|
| 1362 p.status = gg_fix32(status); |
|
| 1363 p.flags = gg_fix32(sess->status_flags); |
|
| 1364 p.description_size = gg_fix32(descr_len); |
|
| 1365 |
|
| 1366 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) { |
|
| 1367 p.flags = gg_fix32(0x00000014); |
|
| 1368 descr_null_len = 1; |
|
| 1369 } |
|
| 1370 |
|
| 1371 res = gg_send_packet(sess, GG_NEW_STATUS80, |
|
| 1372 &p, sizeof(p), descr, descr_len, |
|
| 1373 "\x00", descr_null_len, NULL); |
|
| 1374 |
|
| 1375 free(gen_descr); |
|
| 1376 |
|
| 1377 if (GG_S_NA(status)) { |
|
| 1378 sess->state = GG_STATE_DISCONNECTING; |
|
| 1379 sess->timeout = GG_TIMEOUT_DISCONNECT; |
|
| 1380 } |
|
| 1381 |
|
| 1382 return res; |
|
| 1383 } |
|
| 1384 |
|
| 1385 /** |
|
| 1386 * Zmienia status użytkownika na status opisowy z podanym czasem powrotu. |
|
| 1387 * |
|
| 1388 * \param sess Struktura sesji |
|
| 1389 * \param status Nowy status użytkownika |
|
| 1390 * \param descr Opis statusu użytkownika |
|
| 1391 * \param ts Czas powrotu w postaci uniksowego znacznika czasu |
|
| 1392 * |
|
| 1393 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 1394 * |
|
| 1395 * \ingroup status |
|
| 1396 */ |
|
| 1397 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int ts) |
|
| 1398 { |
|
| 1399 gg_debug_session(sess, GG_DEBUG_FUNCTION, |
|
| 1400 "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", |
|
| 1401 sess, status, descr, ts); |
|
| 1402 |
|
| 1403 return gg_change_status_descr(sess, status, descr); |
|
| 1404 } |
|
| 1405 |
|
| 1406 /** |
|
| 1407 * Funkcja zmieniająca flagi statusu. |
|
| 1408 * |
|
| 1409 * \param sess Struktura sesji |
|
| 1410 * \param flags Nowe flagi statusu |
|
| 1411 * |
|
| 1412 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 1413 * |
|
| 1414 * \note Aby zmiany weszły w życie, należy ponownie ustawić status za pomocą |
|
| 1415 * funkcji z rodziny \c gg_change_status(). |
|
| 1416 * |
|
| 1417 * \ingroup status |
|
| 1418 */ |
|
| 1419 int gg_change_status_flags(struct gg_session *sess, int flags) |
|
| 1420 { |
|
| 1421 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_flags(%p, 0x%08x);\n", sess, flags); |
|
| 1422 |
|
| 1423 if (sess == NULL) { |
|
| 1424 errno = EFAULT; |
|
| 1425 return -1; |
|
| 1426 } |
|
| 1427 |
|
| 1428 sess->status_flags = flags; |
|
| 1429 |
|
| 1430 return 0; |
|
| 1431 } |
|
| 1432 |
|
| 1433 #ifndef DOXYGEN |
|
| 1434 |
|
| 1435 static int gg_send_message_110(struct gg_session *sess, |
|
| 1436 uin_t recipient, uint64_t chat_id, |
|
| 1437 const char *message, int is_html) |
|
| 1438 { |
|
| 1439 GG110SendMessage msg = GG110_SEND_MESSAGE__INIT; |
|
| 1440 int packet_type = recipient ? GG_SEND_MSG110 : GG_CHAT_SEND_MSG; |
|
| 1441 int seq; |
|
| 1442 char *html_message_gen = NULL, *plain_message_gen = NULL; |
|
| 1443 const char *html_message, *plain_message; |
|
| 1444 int succ = 1; |
|
| 1445 |
|
| 1446 gg_debug_session(sess, GG_DEBUG_FUNCTION, |
|
| 1447 "** gg_send_message_110(%p, %u, %" PRIu64 ", %p, %d);\n", |
|
| 1448 sess, recipient, chat_id, message, is_html); |
|
| 1449 |
|
| 1450 if (message == NULL) |
|
| 1451 return -1; |
|
| 1452 |
|
| 1453 if ((recipient == 0) == (chat_id == 0)) |
|
| 1454 return -1; |
|
| 1455 |
|
| 1456 if (is_html) { |
|
| 1457 html_message = message; |
|
| 1458 |
|
| 1459 if (sess->encoding != GG_ENCODING_UTF8) { |
|
| 1460 html_message = html_message_gen = gg_encoding_convert( |
|
| 1461 html_message, sess->encoding, GG_ENCODING_UTF8, |
|
| 1462 -1, -1); |
|
| 1463 if (html_message_gen == NULL) |
|
| 1464 return -1; |
|
| 1465 } |
|
| 1466 |
|
| 1467 plain_message = plain_message_gen = |
|
| 1468 gg_message_html_to_text_110(html_message); |
|
| 1469 if (plain_message_gen == NULL) { |
|
| 1470 free(html_message_gen); |
|
| 1471 return -1; |
|
| 1472 } |
|
| 1473 } else { |
|
| 1474 plain_message = message; |
|
| 1475 |
|
| 1476 if (sess->encoding != GG_ENCODING_UTF8) { |
|
| 1477 plain_message = plain_message_gen = gg_encoding_convert( |
|
| 1478 plain_message, sess->encoding, GG_ENCODING_UTF8, |
|
| 1479 -1, -1); |
|
| 1480 if (plain_message_gen == NULL) |
|
| 1481 return -1; |
|
| 1482 } |
|
| 1483 |
|
| 1484 html_message = html_message_gen = |
|
| 1485 gg_message_text_to_html_110(plain_message, -1); |
|
| 1486 if (html_message_gen == NULL) { |
|
| 1487 free(plain_message_gen); |
|
| 1488 return -1; |
|
| 1489 } |
|
| 1490 } |
|
| 1491 |
|
| 1492 seq = ++sess->seq; |
|
| 1493 |
|
| 1494 if (recipient) { |
|
| 1495 msg.has_recipient = 1; |
|
| 1496 gg_protobuf_set_uin(&msg.recipient, recipient, NULL); |
|
| 1497 } |
|
| 1498 |
|
| 1499 msg.seq = seq; |
|
| 1500 |
|
| 1501 /* rzutujemy z const, ale msg i tak nie będzie modyfikowany */ |
|
| 1502 msg.msg_plain = (char*)plain_message; |
|
| 1503 msg.msg_xhtml = (char*)html_message; |
|
| 1504 |
|
| 1505 if (chat_id) { |
|
| 1506 msg.dummy3 = ""; |
|
| 1507 msg.has_chat_id = 1; |
|
| 1508 msg.chat_id = chat_id; |
|
| 1509 } |
|
| 1510 |
|
| 1511 if (!GG_PROTOBUF_SEND(sess, NULL, packet_type, gg110_send_message, msg)) |
|
| 1512 succ = 0; |
|
| 1513 |
|
| 1514 free(html_message_gen); |
|
| 1515 free(plain_message_gen); |
|
| 1516 |
|
| 1517 return succ ? seq : -1; |
|
| 1518 } |
|
| 1519 |
|
| 1520 static char * |
|
| 1521 gg_message_legacy_text_to_html(const char *src, gg_encoding_t encoding, |
|
| 1522 const unsigned char *format, size_t format_len) |
|
| 1523 { |
|
| 1524 size_t len; |
|
| 1525 char *dst; |
|
| 1526 |
|
| 1527 if (format == NULL || format_len <= 3) { |
|
| 1528 format = NULL; |
|
| 1529 format_len = 0; |
|
| 1530 } else { |
|
| 1531 format += 3; |
|
| 1532 format_len -= 3; |
|
| 1533 } |
|
| 1534 |
|
| 1535 len = gg_message_text_to_html(NULL, src, encoding, format, format_len); |
|
| 1536 |
|
| 1537 dst = malloc(len + 1); |
|
| 1538 if (dst == NULL) |
|
| 1539 return NULL; |
|
| 1540 |
|
| 1541 gg_message_text_to_html(dst, src, encoding, format, format_len); |
|
| 1542 |
|
| 1543 return dst; |
|
| 1544 } |
|
| 1545 |
|
| 1546 /** |
|
| 1547 * \internal Wysyła wiadomość. |
|
| 1548 * |
|
| 1549 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać |
|
| 1550 * do potwierdzenia. |
|
| 1551 * |
|
| 1552 * \param sess Struktura sesji |
|
| 1553 * \param msgclass Klasa wiadomości |
|
| 1554 * \param recipients_count Liczba adresatów |
|
| 1555 * \param recipients Wskaźnik do tablicy z numerami adresatów |
|
| 1556 * \param message Treść wiadomości |
|
| 1557 * \param format Informacje o formatowaniu |
|
| 1558 * \param formatlen Długość informacji o formatowaniu |
|
| 1559 * \param html_message Treść wiadomości HTML |
|
| 1560 * |
|
| 1561 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1562 * |
|
| 1563 * \ingroup messages |
|
| 1564 */ |
|
| 1565 static int gg_send_message_common(struct gg_session *sess, int msgclass, |
|
| 1566 int recipients_count, uin_t *recipients, const unsigned char *message, |
|
| 1567 const unsigned char *format, int formatlen, |
|
| 1568 const unsigned char *html_message) |
|
| 1569 { |
|
| 1570 struct gg_send_msg80 s80; |
|
| 1571 const char *cp_msg = NULL, *utf_html_msg = NULL; |
|
| 1572 char *recoded_msg = NULL, *recoded_html_msg = NULL; |
|
| 1573 unsigned char *generated_format = NULL; |
|
| 1574 int seq_no = -1; |
|
| 1575 |
|
| 1576 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_common(" |
|
| 1577 "%p, %d, %d, %p, %p, %p, %d, %p);\n", sess, msgclass, |
|
| 1578 recipients_count, recipients, message, format, |
|
| 1579 formatlen, html_message); |
|
| 1580 |
|
| 1581 if (!sess) { |
|
| 1582 errno = EFAULT; |
|
| 1583 return -1; |
|
| 1584 } |
|
| 1585 |
|
| 1586 if (sess->state != GG_STATE_CONNECTED) { |
|
| 1587 errno = ENOTCONN; |
|
| 1588 return -1; |
|
| 1589 } |
|
| 1590 |
|
| 1591 if ((message == NULL && html_message == NULL) || |
|
| 1592 recipients_count <= 0 || recipients_count > 0xffff || |
|
| 1593 recipients == NULL || (format == NULL && formatlen != 0)) |
|
| 1594 { |
|
| 1595 errno = EINVAL; |
|
| 1596 return -1; |
|
| 1597 } |
|
| 1598 |
|
| 1599 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 && |
|
| 1600 recipients_count == 1) |
|
| 1601 { |
|
| 1602 int is_html = (html_message != NULL); |
|
| 1603 char *formatted_msg = NULL; |
|
| 1604 |
|
| 1605 if (formatlen > 3 && !is_html) { |
|
| 1606 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_WARNING, |
|
| 1607 "// gg_send_message_common() using legacy " |
|
| 1608 "formatting with new protocol\n"); |
|
| 1609 formatted_msg = gg_message_legacy_text_to_html( |
|
| 1610 (const char *)message, sess->encoding, |
|
| 1611 format, formatlen); |
|
| 1612 if (formatted_msg == NULL) |
|
| 1613 goto cleanup; |
|
| 1614 html_message = (unsigned char*)formatted_msg; |
|
| 1615 is_html = 1; |
|
| 1616 } |
|
| 1617 |
|
| 1618 seq_no = gg_send_message_110(sess, recipients[0], 0, |
|
| 1619 (const char*)(is_html ? html_message : message), |
|
| 1620 is_html); |
|
| 1621 goto cleanup; |
|
| 1622 } |
|
| 1623 |
|
| 1624 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 && |
|
| 1625 !gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_LEGACY_CONFER)) |
|
| 1626 { |
|
| 1627 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 1628 "// gg_send_message_common() legacy conferences disabled\n"); |
|
| 1629 errno = EINVAL; |
|
| 1630 return -1; |
|
| 1631 } |
|
| 1632 |
|
| 1633 if (message == NULL) { |
|
| 1634 char *tmp_msg; |
|
| 1635 size_t len, fmt_len; |
|
| 1636 uint16_t fixed_fmt_len; |
|
| 1637 |
|
| 1638 len = gg_message_html_to_text(NULL, NULL, &fmt_len, (const char*) html_message, sess->encoding); |
|
| 1639 |
|
| 1640 tmp_msg = malloc(len + 1); |
|
| 1641 |
|
| 1642 if (tmp_msg == NULL) |
|
| 1643 goto cleanup; |
|
| 1644 |
|
| 1645 if (fmt_len != 0) { |
|
| 1646 generated_format = malloc(fmt_len + 3); |
|
| 1647 |
|
| 1648 if (generated_format == NULL) { |
|
| 1649 free(tmp_msg); |
|
| 1650 goto cleanup; |
|
| 1651 } |
|
| 1652 |
|
| 1653 generated_format[0] = '\x02'; |
|
| 1654 fixed_fmt_len = gg_fix16(fmt_len); |
|
| 1655 memcpy(generated_format + 1, &fixed_fmt_len, sizeof(fixed_fmt_len)); |
|
| 1656 gg_message_html_to_text(tmp_msg, generated_format + 3, |
|
| 1657 NULL, (const char*)html_message, sess->encoding); |
|
| 1658 |
|
| 1659 format = generated_format; |
|
| 1660 formatlen = fmt_len + 3; |
|
| 1661 } else { |
|
| 1662 gg_message_html_to_text(tmp_msg, NULL, NULL, (const char*) html_message, sess->encoding); |
|
| 1663 |
|
| 1664 format = NULL; |
|
| 1665 formatlen = 0; |
|
| 1666 } |
|
| 1667 |
|
| 1668 if (sess->encoding != GG_ENCODING_CP1250) { |
|
| 1669 cp_msg = recoded_msg = gg_encoding_convert(tmp_msg, sess->encoding, GG_ENCODING_CP1250, -1, -1); |
|
| 1670 free(tmp_msg); |
|
| 1671 |
|
| 1672 if (cp_msg == NULL) |
|
| 1673 goto cleanup; |
|
| 1674 } else { |
|
| 1675 cp_msg = recoded_msg = tmp_msg; |
|
| 1676 } |
|
| 1677 } else { |
|
| 1678 if (sess->encoding != GG_ENCODING_CP1250) { |
|
| 1679 cp_msg = recoded_msg = gg_encoding_convert( |
|
| 1680 (const char*)message, sess->encoding, |
|
| 1681 GG_ENCODING_CP1250, -1, -1); |
|
| 1682 |
|
| 1683 if (cp_msg == NULL) |
|
| 1684 goto cleanup; |
|
| 1685 } else { |
|
| 1686 cp_msg = (const char*) message; |
|
| 1687 } |
|
| 1688 } |
|
| 1689 |
|
| 1690 if (html_message == NULL) { |
|
| 1691 char *formatted_msg; |
|
| 1692 |
|
| 1693 formatted_msg = gg_message_legacy_text_to_html( |
|
| 1694 (const char*)message, sess->encoding, format, formatlen); |
|
| 1695 if (formatted_msg == NULL) |
|
| 1696 goto cleanup; |
|
| 1697 |
|
| 1698 if (sess->encoding == GG_ENCODING_UTF8) { |
|
| 1699 utf_html_msg = recoded_html_msg = formatted_msg; |
|
| 1700 } else { |
|
| 1701 utf_html_msg = recoded_html_msg = gg_encoding_convert( |
|
| 1702 formatted_msg, sess->encoding, |
|
| 1703 GG_ENCODING_UTF8, -1, -1); |
|
| 1704 free(formatted_msg); |
|
| 1705 |
|
| 1706 if (utf_html_msg == NULL) |
|
| 1707 goto cleanup; |
|
| 1708 } |
|
| 1709 } else { |
|
| 1710 if (sess->encoding == GG_ENCODING_UTF8) { |
|
| 1711 utf_html_msg = (const char*) html_message; |
|
| 1712 } else { |
|
| 1713 utf_html_msg = recoded_html_msg = gg_encoding_convert( |
|
| 1714 (const char*)html_message, sess->encoding, |
|
| 1715 GG_ENCODING_UTF8, -1, -1); |
|
| 1716 |
|
| 1717 if (utf_html_msg == NULL) |
|
| 1718 goto cleanup; |
|
| 1719 } |
|
| 1720 } |
|
| 1721 |
|
| 1722 /* Drobne odchylenie od protokołu. Jeśli wysyłamy kilka |
|
| 1723 * wiadomości w ciągu jednej sekundy, zwiększamy poprzednią |
|
| 1724 * wartość, żeby każda wiadomość miała unikalny numer. |
|
| 1725 */ |
|
| 1726 |
|
| 1727 seq_no = time(NULL); |
|
| 1728 |
|
| 1729 if (seq_no <= sess->seq) |
|
| 1730 seq_no = sess->seq + 1; |
|
| 1731 |
|
| 1732 sess->seq = seq_no; |
|
| 1733 |
|
| 1734 s80.seq = gg_fix32(seq_no); |
|
| 1735 s80.msgclass = gg_fix32(msgclass); |
|
| 1736 s80.offset_plain = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1); |
|
| 1737 s80.offset_attr = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1 + strlen(cp_msg) + 1); |
|
| 1738 |
|
| 1739 if (recipients_count > 1) { |
|
| 1740 struct gg_msg_recipients r; |
|
| 1741 int i, j, k; |
|
| 1742 uin_t *recps; |
|
| 1743 |
|
| 1744 r.flag = GG_MSG_OPTION_CONFERENCE; |
|
| 1745 r.count = gg_fix32(recipients_count - 1); |
|
| 1746 |
|
| 1747 recps = malloc(sizeof(uin_t) * (recipients_count - 1)); |
|
| 1748 |
|
| 1749 if (!recps) { |
|
| 1750 seq_no = -1; |
|
| 1751 goto cleanup; |
|
| 1752 } |
|
| 1753 |
|
| 1754 for (i = 0; i < recipients_count; i++) { |
|
| 1755 for (j = 0, k = 0; j < recipients_count; j++) { |
|
| 1756 if (j != i) { |
|
| 1757 recps[k] = gg_fix32(recipients[j]); |
|
| 1758 k++; |
|
| 1759 } |
|
| 1760 } |
|
| 1761 |
|
| 1762 s80.recipient = gg_fix32(recipients[i]); |
|
| 1763 |
|
| 1764 if (gg_send_packet(sess, GG_SEND_MSG80, &s80, |
|
| 1765 sizeof(s80), utf_html_msg, |
|
| 1766 strlen(utf_html_msg) + 1, cp_msg, |
|
| 1767 strlen(cp_msg) + 1, &r, sizeof(r), recps, |
|
| 1768 (recipients_count - 1) * sizeof(uin_t), format, |
|
| 1769 formatlen, NULL) == -1) |
|
| 1770 { |
|
| 1771 seq_no = -1; |
|
| 1772 } |
|
| 1773 } |
|
| 1774 |
|
| 1775 free(recps); |
|
| 1776 } else { |
|
| 1777 s80.recipient = gg_fix32(recipients[0]); |
|
| 1778 |
|
| 1779 if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), |
|
| 1780 utf_html_msg, strlen(utf_html_msg) + 1, cp_msg, |
|
| 1781 strlen(cp_msg) + 1, format, formatlen, NULL) == -1) |
|
| 1782 { |
|
| 1783 seq_no = -1; |
|
| 1784 } |
|
| 1785 } |
|
| 1786 |
|
| 1787 cleanup: |
|
| 1788 free(recoded_msg); |
|
| 1789 free(recoded_html_msg); |
|
| 1790 free(generated_format); |
|
| 1791 |
|
| 1792 if (seq_no >= 0) |
|
| 1793 gg_compat_message_sent(sess, seq_no, recipients_count, recipients); |
|
| 1794 |
|
| 1795 return seq_no; |
|
| 1796 } |
|
| 1797 |
|
| 1798 #endif /* DOXYGEN */ |
|
| 1799 |
|
| 1800 /** |
|
| 1801 * Wysyła wiadomość do użytkownika. |
|
| 1802 * |
|
| 1803 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać |
|
| 1804 * do potwierdzenia. |
|
| 1805 * |
|
| 1806 * \param sess Struktura sesji |
|
| 1807 * \param msgclass Klasa wiadomości |
|
| 1808 * \param recipient Numer adresata |
|
| 1809 * \param message Treść wiadomości |
|
| 1810 * |
|
| 1811 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1812 * |
|
| 1813 * \ingroup messages |
|
| 1814 */ |
|
| 1815 int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) |
|
| 1816 { |
|
| 1817 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, " |
|
| 1818 "%u, %p)\n", sess, msgclass, recipient, message); |
|
| 1819 |
|
| 1820 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) { |
|
| 1821 int seq_no; |
|
| 1822 |
|
| 1823 seq_no = gg_send_message_110(sess, recipient, 0, (const char*)message, 0); |
|
| 1824 |
|
| 1825 if (seq_no >= 0) |
|
| 1826 gg_compat_message_sent(sess, seq_no, 1, &recipient); |
|
| 1827 |
|
| 1828 return seq_no; |
|
| 1829 } |
|
| 1830 |
|
| 1831 return gg_send_message_common(sess, msgclass, 1, &recipient, message, |
|
| 1832 (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00", |
|
| 1833 9, NULL); |
|
| 1834 } |
|
| 1835 |
|
| 1836 /** |
|
| 1837 * Wysyła wiadomość formatowaną. |
|
| 1838 * |
|
| 1839 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać |
|
| 1840 * do potwierdzenia. |
|
| 1841 * |
|
| 1842 * \param sess Struktura sesji |
|
| 1843 * \param msgclass Klasa wiadomości |
|
| 1844 * \param recipient Numer adresata |
|
| 1845 * \param message Treść wiadomości |
|
| 1846 * \param format Informacje o formatowaniu |
|
| 1847 * \param formatlen Długość informacji o formatowaniu |
|
| 1848 * |
|
| 1849 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1850 * |
|
| 1851 * \ingroup messages |
|
| 1852 */ |
|
| 1853 int gg_send_message_richtext(struct gg_session *sess, int msgclass, |
|
| 1854 uin_t recipient, const unsigned char *message, |
|
| 1855 const unsigned char *format, int formatlen) |
|
| 1856 { |
|
| 1857 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext(" |
|
| 1858 "%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, |
|
| 1859 message, format, formatlen); |
|
| 1860 |
|
| 1861 return gg_send_message_common(sess, msgclass, 1, &recipient, message, format, formatlen, NULL); |
|
| 1862 } |
|
| 1863 |
|
| 1864 /** |
|
| 1865 * Wysyła formatowaną wiadomość HTML. |
|
| 1866 * |
|
| 1867 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać |
|
| 1868 * do potwierdzenia. |
|
| 1869 * |
|
| 1870 * \param sess Struktura sesji |
|
| 1871 * \param msgclass Klasa wiadomości |
|
| 1872 * \param recipient Numer adresata |
|
| 1873 * \param html_message Treść wiadomości HTML |
|
| 1874 * |
|
| 1875 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1876 * |
|
| 1877 * \ingroup messages |
|
| 1878 */ |
|
| 1879 int gg_send_message_html(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *html_message) |
|
| 1880 { |
|
| 1881 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_html(%p, " |
|
| 1882 "%d, %u, %p);\n", sess, msgclass, recipient, html_message); |
|
| 1883 |
|
| 1884 return gg_send_message_common(sess, msgclass, 1, &recipient, NULL, NULL, 0, html_message); |
|
| 1885 } |
|
| 1886 |
|
| 1887 /** |
|
| 1888 * Wysyła wiadomość w ramach konferencji. |
|
| 1889 * |
|
| 1890 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać |
|
| 1891 * do potwierdzenia. |
|
| 1892 * |
|
| 1893 * \param sess Struktura sesji |
|
| 1894 * \param msgclass Klasa wiadomości |
|
| 1895 * \param recipients_count Liczba adresatów |
|
| 1896 * \param recipients Wskaźnik do tablicy z numerami adresatów |
|
| 1897 * \param message Treść wiadomości |
|
| 1898 * |
|
| 1899 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1900 * |
|
| 1901 * \ingroup messages |
|
| 1902 */ |
|
| 1903 int gg_send_message_confer(struct gg_session *sess, int msgclass, |
|
| 1904 int recipients_count, uin_t *recipients, const unsigned char *message) |
|
| 1905 { |
|
| 1906 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer(" |
|
| 1907 "%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, |
|
| 1908 recipients, message); |
|
| 1909 |
|
| 1910 return gg_send_message_common(sess, msgclass, recipients_count, |
|
| 1911 recipients, message, |
|
| 1912 (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00", |
|
| 1913 9, NULL); |
|
| 1914 } |
|
| 1915 |
|
| 1916 /** |
|
| 1917 * Wysyła wiadomość formatowaną w ramach konferencji. |
|
| 1918 * |
|
| 1919 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać |
|
| 1920 * do potwierdzenia. |
|
| 1921 * |
|
| 1922 * \param sess Struktura sesji |
|
| 1923 * \param msgclass Klasa wiadomości |
|
| 1924 * \param recipients_count Liczba adresatów |
|
| 1925 * \param recipients Wskaźnik do tablicy z numerami adresatów |
|
| 1926 * \param message Treść wiadomości |
|
| 1927 * \param format Informacje o formatowaniu |
|
| 1928 * \param formatlen Długość informacji o formatowaniu |
|
| 1929 * |
|
| 1930 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1931 * |
|
| 1932 * \ingroup messages |
|
| 1933 */ |
|
| 1934 int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, |
|
| 1935 int recipients_count, uin_t *recipients, const unsigned char *message, |
|
| 1936 const unsigned char *format, int formatlen) |
|
| 1937 { |
|
| 1938 gg_debug_session(sess, GG_DEBUG_FUNCTION, |
|
| 1939 "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, " |
|
| 1940 "%d);\n", sess, msgclass, recipients_count, recipients, message, |
|
| 1941 format, formatlen); |
|
| 1942 |
|
| 1943 return gg_send_message_common(sess, msgclass, recipients_count, recipients, message, format, formatlen, NULL); |
|
| 1944 } |
|
| 1945 |
|
| 1946 /** |
|
| 1947 * Wysyła formatowaną wiadomość HTML w ramach konferencji. |
|
| 1948 * |
|
| 1949 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać |
|
| 1950 * do potwierdzenia. |
|
| 1951 * |
|
| 1952 * \param sess Struktura sesji |
|
| 1953 * \param msgclass Klasa wiadomości |
|
| 1954 * \param recipients_count Liczba adresatów |
|
| 1955 * \param recipients Wskaźnik do tablicy z numerami adresatów |
|
| 1956 * \param html_message Treść wiadomości HTML |
|
| 1957 * |
|
| 1958 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1959 * |
|
| 1960 * \ingroup messages |
|
| 1961 */ |
|
| 1962 int gg_send_message_confer_html(struct gg_session *sess, int msgclass, |
|
| 1963 int recipients_count, uin_t *recipients, |
|
| 1964 const unsigned char *html_message) |
|
| 1965 { |
|
| 1966 gg_debug_session(sess, GG_DEBUG_FUNCTION, |
|
| 1967 "** gg_send_message_confer_html(%p, %d, %d, %p, %p);\n", sess, |
|
| 1968 msgclass, recipients_count, recipients, html_message); |
|
| 1969 |
|
| 1970 return gg_send_message_common(sess, msgclass, recipients_count, recipients, NULL, NULL, 0, html_message); |
|
| 1971 } |
|
| 1972 |
|
| 1973 /** |
|
| 1974 * Wysyła wiadomość binarną przeznaczoną dla klienta. |
|
| 1975 * |
|
| 1976 * Wiadomości między klientami przesyła się np. w celu wywołania zwrotnego |
|
| 1977 * połączenia bezpośredniego. Funkcja zwraca losowy numer sekwencyjny, |
|
| 1978 * który można zignorować albo wykorzystać do potwierdzenia. |
|
| 1979 * |
|
| 1980 * \param sess Struktura sesji |
|
| 1981 * \param msgclass Klasa wiadomości |
|
| 1982 * \param recipient Numer adresata |
|
| 1983 * \param message Treść wiadomości |
|
| 1984 * \param message_len Długość wiadomości |
|
| 1985 * |
|
| 1986 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
|
| 1987 * |
|
| 1988 * \ingroup messages |
|
| 1989 */ |
|
| 1990 int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, |
|
| 1991 const unsigned char *message, int message_len) |
|
| 1992 { |
|
| 1993 struct gg_send_msg s; |
|
| 1994 |
|
| 1995 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, " |
|
| 1996 "%d, %u, ...);\n", sess, msgclass, recipient); |
|
| 1997 |
|
| 1998 if (!sess) { |
|
| 1999 errno = EFAULT; |
|
| 2000 return -1; |
|
| 2001 } |
|
| 2002 |
|
| 2003 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2004 errno = ENOTCONN; |
|
| 2005 return -1; |
|
| 2006 } |
|
| 2007 |
|
| 2008 s.recipient = gg_fix32(recipient); |
|
| 2009 s.seq = gg_fix32(0); |
|
| 2010 s.msgclass = gg_fix32(msgclass); |
|
| 2011 |
|
| 2012 return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); |
|
| 2013 } |
|
| 2014 |
|
| 2015 /** |
|
| 2016 * Wysyła żądanie obrazka o podanych parametrach. |
|
| 2017 * |
|
| 2018 * Wiadomości obrazkowe nie zawierają samych obrazków, a tylko ich rozmiary |
|
| 2019 * i sumy kontrolne. Odbiorca najpierw szuka obrazków w swojej pamięci |
|
| 2020 * podręcznej i dopiero gdy ich nie znajdzie, wysyła żądanie do nadawcy. |
|
| 2021 * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY. |
|
| 2022 * |
|
| 2023 * \param sess Struktura sesji |
|
| 2024 * \param recipient Numer adresata |
|
| 2025 * \param size Rozmiar obrazka w bajtach |
|
| 2026 * \param crc32 Suma kontrola obrazka |
|
| 2027 * |
|
| 2028 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2029 * |
|
| 2030 * \ingroup messages |
|
| 2031 */ |
|
| 2032 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32) |
|
| 2033 { |
|
| 2034 struct gg_send_msg s; |
|
| 2035 struct gg_msg_image_request r; |
|
| 2036 char dummy = 0; |
|
| 2037 int res; |
|
| 2038 |
|
| 2039 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, " |
|
| 2040 "%u, 0x%.4x);\n", sess, recipient, size, crc32); |
|
| 2041 |
|
| 2042 if (!sess) { |
|
| 2043 errno = EFAULT; |
|
| 2044 return -1; |
|
| 2045 } |
|
| 2046 |
|
| 2047 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2048 errno = ENOTCONN; |
|
| 2049 return -1; |
|
| 2050 } |
|
| 2051 |
|
| 2052 if (size < 0) { |
|
| 2053 errno = EINVAL; |
|
| 2054 return -1; |
|
| 2055 } |
|
| 2056 |
|
| 2057 s.recipient = gg_fix32(recipient); |
|
| 2058 s.seq = gg_fix32(0); |
|
| 2059 s.msgclass = gg_fix32(GG_CLASS_MSG); |
|
| 2060 |
|
| 2061 r.flag = GG_MSG_OPTION_IMAGE_REQUEST; |
|
| 2062 r.size = gg_fix32(size); |
|
| 2063 r.crc32 = gg_fix32(crc32); |
|
| 2064 |
|
| 2065 res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL); |
|
| 2066 |
|
| 2067 if (!res) { |
|
| 2068 struct gg_image_queue *q = malloc(sizeof(*q)); |
|
| 2069 char *buf; |
|
| 2070 |
|
| 2071 if (!q) { |
|
| 2072 gg_debug_session(sess, GG_DEBUG_MISC, |
|
| 2073 "// gg_image_request() not enough memory for " |
|
| 2074 "image queue\n"); |
|
| 2075 return -1; |
|
| 2076 } |
|
| 2077 |
|
| 2078 buf = malloc(size); |
|
| 2079 if (size && !buf) { |
|
| 2080 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); |
|
| 2081 free(q); |
|
| 2082 return -1; |
|
| 2083 } |
|
| 2084 |
|
| 2085 memset(q, 0, sizeof(*q)); |
|
| 2086 |
|
| 2087 q->sender = recipient; |
|
| 2088 q->size = size; |
|
| 2089 q->crc32 = crc32; |
|
| 2090 q->image = buf; |
|
| 2091 |
|
| 2092 if (!sess->images) |
|
| 2093 sess->images = q; |
|
| 2094 else { |
|
| 2095 struct gg_image_queue *qq; |
|
| 2096 |
|
| 2097 for (qq = sess->images; qq->next; qq = qq->next); |
|
| 2098 |
|
| 2099 qq->next = q; |
|
| 2100 } |
|
| 2101 } |
|
| 2102 |
|
| 2103 return res; |
|
| 2104 } |
|
| 2105 |
|
| 2106 /** |
|
| 2107 * Wysyła żądany obrazek. |
|
| 2108 * |
|
| 2109 * \param sess Struktura sesji |
|
| 2110 * \param recipient Numer adresata |
|
| 2111 * \param filename Nazwa pliku |
|
| 2112 * \param image Bufor z obrazkiem |
|
| 2113 * \param size Rozmiar obrazka |
|
| 2114 * |
|
| 2115 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2116 * |
|
| 2117 * \ingroup messages |
|
| 2118 */ |
|
| 2119 int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size) |
|
| 2120 { |
|
| 2121 struct gg_session_private *p; |
|
| 2122 struct gg_msg_image_reply *r; |
|
| 2123 struct gg_send_msg s; |
|
| 2124 const char *tmp; |
|
| 2125 char buf[1910]; |
|
| 2126 gg_imgout_queue_t *queue = NULL, *queue_end = NULL; |
|
| 2127 |
|
| 2128 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, " |
|
| 2129 "\"%s\", %p, %d);\n", sess, recipient, filename, image, size); |
|
| 2130 |
|
| 2131 if (!sess || !filename || !image) { |
|
| 2132 errno = EFAULT; |
|
| 2133 return -1; |
|
| 2134 } |
|
| 2135 |
|
| 2136 p = sess->private_data; |
|
| 2137 |
|
| 2138 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2139 errno = ENOTCONN; |
|
| 2140 return -1; |
|
| 2141 } |
|
| 2142 |
|
| 2143 if (size < 0) { |
|
| 2144 errno = EINVAL; |
|
| 2145 return -1; |
|
| 2146 } |
|
| 2147 |
|
| 2148 /* wytnij ścieżki, zostaw tylko nazwę pliku */ |
|
| 2149 while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\'))) |
|
| 2150 filename = tmp + 1; |
|
| 2151 |
|
| 2152 if (strlen(filename) < 1 || strlen(filename) > 1024) { |
|
| 2153 errno = EINVAL; |
|
| 2154 return -1; |
|
| 2155 } |
|
| 2156 |
|
| 2157 s.recipient = gg_fix32(recipient); |
|
| 2158 s.seq = gg_fix32(0); |
|
| 2159 s.msgclass = gg_fix32(GG_CLASS_MSG); |
|
| 2160 |
|
| 2161 buf[0] = 0; |
|
| 2162 r = (void*) &buf[1]; |
|
| 2163 |
|
| 2164 r->flag = GG_MSG_OPTION_IMAGE_REPLY; |
|
| 2165 r->size = gg_fix32(size); |
|
| 2166 r->crc32 = gg_fix32(gg_crc32(0, (const unsigned char*) image, size)); |
|
| 2167 |
|
| 2168 while (size > 0) { |
|
| 2169 gg_imgout_queue_t *it; |
|
| 2170 size_t buflen, chunklen; |
|
| 2171 |
|
| 2172 /* \0 + struct gg_msg_image_reply */ |
|
| 2173 buflen = sizeof(struct gg_msg_image_reply) + 1; |
|
| 2174 |
|
| 2175 /* w pierwszym kawałku jest nazwa pliku */ |
|
| 2176 if (r->flag == GG_MSG_OPTION_IMAGE_REPLY) { |
|
| 2177 strcpy(buf + buflen, filename); |
|
| 2178 buflen += strlen(filename) + 1; |
|
| 2179 } |
|
| 2180 |
|
| 2181 chunklen = ((size_t) size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t) size; |
|
| 2182 |
|
| 2183 memcpy(buf + buflen, image, chunklen); |
|
| 2184 size -= chunklen; |
|
| 2185 image += chunklen; |
|
| 2186 |
|
| 2187 it = gg_new0(sizeof(gg_imgout_queue_t)); |
|
| 2188 if (!it) |
|
| 2189 break; |
|
| 2190 if (queue_end) { |
|
| 2191 queue_end->next = it; |
|
| 2192 queue_end = it; |
|
| 2193 } else { |
|
| 2194 queue = queue_end = it; |
|
| 2195 } |
|
| 2196 |
|
| 2197 memcpy(&it->msg_hdr, &s, sizeof(s)); |
|
| 2198 memcpy(it->buf, buf, buflen + chunklen); |
|
| 2199 it->buf_len = buflen + chunklen; |
|
| 2200 |
|
| 2201 r->flag = GG_MSG_OPTION_IMAGE_REPLY_MORE; |
|
| 2202 } |
|
| 2203 |
|
| 2204 if (p->imgout_queue) { |
|
| 2205 queue_end = p->imgout_queue; |
|
| 2206 while (queue_end->next) |
|
| 2207 queue_end = queue_end->next; |
|
| 2208 queue_end->next = queue; |
|
| 2209 } else { |
|
| 2210 p->imgout_queue = queue; |
|
| 2211 } |
|
| 2212 gg_image_sendout(sess); |
|
| 2213 |
|
| 2214 return 0; |
|
| 2215 } |
|
| 2216 |
|
| 2217 void gg_image_sendout(struct gg_session *sess) |
|
| 2218 { |
|
| 2219 struct gg_session_private *p = sess->private_data; |
|
| 2220 |
|
| 2221 while (p->imgout_waiting_ack < GG_IMGOUT_WAITING_MAX && p->imgout_queue) { |
|
| 2222 gg_imgout_queue_t *it = p->imgout_queue; |
|
| 2223 int res; |
|
| 2224 |
|
| 2225 p->imgout_queue = p->imgout_queue->next; |
|
| 2226 p->imgout_waiting_ack++; |
|
| 2227 |
|
| 2228 res = gg_send_packet(sess, GG_SEND_MSG, |
|
| 2229 &it->msg_hdr, sizeof(it->msg_hdr), |
|
| 2230 it->buf, it->buf_len, |
|
| 2231 NULL); |
|
| 2232 |
|
| 2233 free(it); |
|
| 2234 |
|
| 2235 if (res == -1) |
|
| 2236 break; |
|
| 2237 } |
|
| 2238 } |
|
| 2239 |
|
| 2240 static int gg_notify105_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) |
|
| 2241 { |
|
| 2242 int i = 0; |
|
| 2243 |
|
| 2244 if (!userlist || !count) |
|
| 2245 return gg_send_packet(sess, GG_NOTIFY105_LIST_EMPTY, NULL); |
|
| 2246 |
|
| 2247 while (i < count) { |
|
| 2248 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL); |
|
| 2249 gg_tvbuilder_expected_size(tvb, 2100); |
|
| 2250 |
|
| 2251 while (i < count) { |
|
| 2252 size_t prev_size = gg_tvbuilder_get_size(tvb); |
|
| 2253 gg_tvbuilder_write_uin(tvb, userlist[i]); |
|
| 2254 gg_tvbuilder_write_uint8(tvb, |
|
| 2255 (types == NULL) ? GG_USER_NORMAL : types[i]); |
|
| 2256 |
|
| 2257 /* Oryginalny klient wysyła maksymalnie 2048 bajtów |
|
| 2258 * danych w każdym pakiecie tego typu. |
|
| 2259 */ |
|
| 2260 if (gg_tvbuilder_get_size(tvb) > 2048) { |
|
| 2261 gg_tvbuilder_strip(tvb, prev_size); |
|
| 2262 break; |
|
| 2263 } |
|
| 2264 i++; |
|
| 2265 } |
|
| 2266 |
|
| 2267 if (!gg_tvbuilder_send(tvb, (i < count) ? |
|
| 2268 GG_NOTIFY105_FIRST : GG_NOTIFY105_LAST)) |
|
| 2269 { |
|
| 2270 return -1; |
|
| 2271 } |
|
| 2272 } |
|
| 2273 |
|
| 2274 return 0; |
|
| 2275 } |
|
| 2276 |
|
| 2277 /** |
|
| 2278 * Wysyła do serwera listę kontaktów. |
|
| 2279 * |
|
| 2280 * Funkcja informuje serwer o liście kontaktów, których statusy będą |
|
| 2281 * obserwowane lub kontaktów, które bedą blokowane. Dla każdego z \c count |
|
| 2282 * kontaktów tablica \c userlist zawiera numer, a tablica \c types rodzaj |
|
| 2283 * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED). |
|
| 2284 * |
|
| 2285 * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli |
|
| 2286 * jest pusta. |
|
| 2287 * |
|
| 2288 * \param sess Struktura sesji |
|
| 2289 * \param userlist Wskaźnik do tablicy numerów kontaktów |
|
| 2290 * \param types Wskaźnik do tablicy rodzajów kontaktów. Jeżeli NULL, wszystkie kontakty są typu GG_USER_NORMAL. |
|
| 2291 * \param count Liczba kontaktów |
|
| 2292 * |
|
| 2293 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2294 * |
|
| 2295 * \ingroup contacts |
|
| 2296 */ |
|
| 2297 int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) |
|
| 2298 { |
|
| 2299 struct gg_notify *n; |
|
| 2300 int i, res = 0; |
|
| 2301 |
|
| 2302 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); |
|
| 2303 |
|
| 2304 if (!sess) { |
|
| 2305 errno = EFAULT; |
|
| 2306 return -1; |
|
| 2307 } |
|
| 2308 |
|
| 2309 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2310 errno = ENOTCONN; |
|
| 2311 return -1; |
|
| 2312 } |
|
| 2313 |
|
| 2314 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) |
|
| 2315 return gg_notify105_ex(sess, userlist, types, count); |
|
| 2316 |
|
| 2317 if (!userlist || !count) |
|
| 2318 return gg_send_packet(sess, GG_LIST_EMPTY, NULL); |
|
| 2319 |
|
| 2320 while (count > 0) { |
|
| 2321 int part_count, packet_type; |
|
| 2322 |
|
| 2323 if (count > 400) { |
|
| 2324 part_count = 400; |
|
| 2325 packet_type = GG_NOTIFY_FIRST; |
|
| 2326 } else { |
|
| 2327 part_count = count; |
|
| 2328 packet_type = GG_NOTIFY_LAST; |
|
| 2329 } |
|
| 2330 |
|
| 2331 if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) |
|
| 2332 return -1; |
|
| 2333 |
|
| 2334 for (i = 0; i < part_count; i++) { |
|
| 2335 n[i].uin = gg_fix32(userlist[i]); |
|
| 2336 if (types == NULL) |
|
| 2337 n[i].dunno1 = GG_USER_NORMAL; |
|
| 2338 else |
|
| 2339 n[i].dunno1 = types[i]; |
|
| 2340 } |
|
| 2341 |
|
| 2342 if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { |
|
| 2343 free(n); |
|
| 2344 res = -1; |
|
| 2345 break; |
|
| 2346 } |
|
| 2347 |
|
| 2348 count -= part_count; |
|
| 2349 userlist += part_count; |
|
| 2350 if (types != NULL) |
|
| 2351 types += part_count; |
|
| 2352 |
|
| 2353 free(n); |
|
| 2354 } |
|
| 2355 |
|
| 2356 return res; |
|
| 2357 } |
|
| 2358 |
|
| 2359 /** |
|
| 2360 * Wysyła do serwera listę kontaktów. |
|
| 2361 * |
|
| 2362 * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty |
|
| 2363 * są rodzaju \c GG_USER_NORMAL. |
|
| 2364 * |
|
| 2365 * \param sess Struktura sesji |
|
| 2366 * \param userlist Wskaźnik do tablicy numerów kontaktów |
|
| 2367 * \param count Liczba kontaktów |
|
| 2368 * |
|
| 2369 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2370 * |
|
| 2371 * \ingroup contacts |
|
| 2372 */ |
|
| 2373 int gg_notify(struct gg_session *sess, uin_t *userlist, int count) |
|
| 2374 { |
|
| 2375 return gg_notify_ex(sess, userlist, NULL, count); |
|
| 2376 } |
|
| 2377 |
|
| 2378 /** |
|
| 2379 * Dodaje kontakt. |
|
| 2380 * |
|
| 2381 * Dodaje do listy kontaktów dany numer w trakcie połączenia. Aby zmienić |
|
| 2382 * rodzaj kontaktu (np. z normalnego na zablokowany), należy najpierw usunąć |
|
| 2383 * poprzedni rodzaj, ponieważ serwer operuje na maskach bitowych. |
|
| 2384 * |
|
| 2385 * \param sess Struktura sesji |
|
| 2386 * \param uin Numer kontaktu |
|
| 2387 * \param type Rodzaj kontaktu |
|
| 2388 * |
|
| 2389 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2390 * |
|
| 2391 * \ingroup contacts |
|
| 2392 */ |
|
| 2393 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type) |
|
| 2394 { |
|
| 2395 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); |
|
| 2396 |
|
| 2397 if (!sess) { |
|
| 2398 errno = EFAULT; |
|
| 2399 return -1; |
|
| 2400 } |
|
| 2401 |
|
| 2402 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2403 errno = ENOTCONN; |
|
| 2404 return -1; |
|
| 2405 } |
|
| 2406 |
|
| 2407 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) { |
|
| 2408 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL); |
|
| 2409 gg_tvbuilder_expected_size(tvb, 16); |
|
| 2410 |
|
| 2411 gg_tvbuilder_write_uin(tvb, uin); |
|
| 2412 gg_tvbuilder_write_uint8(tvb, type); |
|
| 2413 |
|
| 2414 if (!gg_tvbuilder_send(tvb, GG_ADD_NOTIFY105)) |
|
| 2415 return -1; |
|
| 2416 return 0; |
|
| 2417 } else { |
|
| 2418 struct gg_add_remove a; |
|
| 2419 |
|
| 2420 a.uin = gg_fix32(uin); |
|
| 2421 a.dunno1 = type; |
|
| 2422 |
|
| 2423 return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); |
|
| 2424 } |
|
| 2425 } |
|
| 2426 |
|
| 2427 /** |
|
| 2428 * Dodaje kontakt. |
|
| 2429 * |
|
| 2430 * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich |
|
| 2431 * kontaktów to \c GG_USER_NORMAL. |
|
| 2432 * |
|
| 2433 * \param sess Struktura sesji |
|
| 2434 * \param uin Numer kontaktu |
|
| 2435 * |
|
| 2436 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2437 * |
|
| 2438 * \ingroup contacts |
|
| 2439 */ |
|
| 2440 int gg_add_notify(struct gg_session *sess, uin_t uin) |
|
| 2441 { |
|
| 2442 return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); |
|
| 2443 } |
|
| 2444 |
|
| 2445 /** |
|
| 2446 * Usuwa kontakt. |
|
| 2447 * |
|
| 2448 * Usuwa z listy kontaktów dany numer w trakcie połączenia. |
|
| 2449 * |
|
| 2450 * \param sess Struktura sesji |
|
| 2451 * \param uin Numer kontaktu |
|
| 2452 * \param type Rodzaj kontaktu |
|
| 2453 * |
|
| 2454 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2455 * |
|
| 2456 * \ingroup contacts |
|
| 2457 */ |
|
| 2458 int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) |
|
| 2459 { |
|
| 2460 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); |
|
| 2461 |
|
| 2462 if (!sess) { |
|
| 2463 errno = EFAULT; |
|
| 2464 return -1; |
|
| 2465 } |
|
| 2466 |
|
| 2467 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2468 errno = ENOTCONN; |
|
| 2469 return -1; |
|
| 2470 } |
|
| 2471 |
|
| 2472 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) { |
|
| 2473 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL); |
|
| 2474 gg_tvbuilder_expected_size(tvb, 16); |
|
| 2475 |
|
| 2476 gg_tvbuilder_write_uin(tvb, uin); |
|
| 2477 gg_tvbuilder_write_uint8(tvb, type); |
|
| 2478 |
|
| 2479 if (!gg_tvbuilder_send(tvb, GG_REMOVE_NOTIFY105)) |
|
| 2480 return -1; |
|
| 2481 return 0; |
|
| 2482 } else { |
|
| 2483 struct gg_add_remove a; |
|
| 2484 |
|
| 2485 a.uin = gg_fix32(uin); |
|
| 2486 a.dunno1 = type; |
|
| 2487 |
|
| 2488 return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL); |
|
| 2489 } |
|
| 2490 } |
|
| 2491 |
|
| 2492 /** |
|
| 2493 * Usuwa kontakt. |
|
| 2494 * |
|
| 2495 * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich |
|
| 2496 * kontaktów to \c GG_USER_NORMAL. |
|
| 2497 * |
|
| 2498 * \param sess Struktura sesji |
|
| 2499 * \param uin Numer kontaktu |
|
| 2500 * |
|
| 2501 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2502 * |
|
| 2503 * \ingroup contacts |
|
| 2504 */ |
|
| 2505 int gg_remove_notify(struct gg_session *sess, uin_t uin) |
|
| 2506 { |
|
| 2507 return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); |
|
| 2508 } |
|
| 2509 |
|
| 2510 /** |
|
| 2511 * Wysyła do serwera zapytanie dotyczące listy kontaktów. |
|
| 2512 * |
|
| 2513 * Funkcja służy do importu lub eksportu listy kontaktów do serwera. |
|
| 2514 * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez |
|
| 2515 * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format |
|
| 2516 * listy kontaktów jest ignorowany przez serwer, ale ze względu na |
|
| 2517 * kompatybilność z innymi klientami, należy przechowywać dane w tym samym |
|
| 2518 * formacie co oryginalny klient Gadu-Gadu. |
|
| 2519 * |
|
| 2520 * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą |
|
| 2521 * z protokołu -- wysyła i odbiera kompletną listę. |
|
| 2522 * |
|
| 2523 * \param sess Struktura sesji |
|
| 2524 * \param type Rodzaj zapytania |
|
| 2525 * \param request Treść zapytania (może być równe NULL) |
|
| 2526 * |
|
| 2527 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2528 * |
|
| 2529 * \ingroup importexport |
|
| 2530 */ |
|
| 2531 int gg_userlist_request(struct gg_session *sess, char type, const char *request) |
|
| 2532 { |
|
| 2533 int len; |
|
| 2534 |
|
| 2535 if (!sess) { |
|
| 2536 errno = EFAULT; |
|
| 2537 return -1; |
|
| 2538 } |
|
| 2539 |
|
| 2540 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2541 errno = ENOTCONN; |
|
| 2542 return -1; |
|
| 2543 } |
|
| 2544 |
|
| 2545 if (!request) { |
|
| 2546 sess->userlist_blocks = 1; |
|
| 2547 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); |
|
| 2548 } |
|
| 2549 |
|
| 2550 len = strlen(request); |
|
| 2551 |
|
| 2552 sess->userlist_blocks = 0; |
|
| 2553 |
|
| 2554 while (len > 2047) { |
|
| 2555 sess->userlist_blocks++; |
|
| 2556 |
|
| 2557 if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1) |
|
| 2558 return -1; |
|
| 2559 |
|
| 2560 if (type == GG_USERLIST_PUT) |
|
| 2561 type = GG_USERLIST_PUT_MORE; |
|
| 2562 |
|
| 2563 request += 2047; |
|
| 2564 len -= 2047; |
|
| 2565 } |
|
| 2566 |
|
| 2567 sess->userlist_blocks++; |
|
| 2568 |
|
| 2569 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); |
|
| 2570 } |
|
| 2571 |
|
| 2572 /** |
|
| 2573 * Wysyła do serwera zapytanie dotyczące listy kontaktów (10.0). |
|
| 2574 * |
|
| 2575 * Funkcja służy do importu lub eksportu listy kontaktów do serwera. |
|
| 2576 * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez |
|
| 2577 * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format |
|
| 2578 * listy kontaktów jest jednak weryfikowany przez serwer, który stara się |
|
| 2579 * synchronizować listę kontaktów zapisaną w formatach GG 7.0 oraz GG 10.0. |
|
| 2580 * Serwer przyjmuje listy kontaktów przysłane w formacie niezgodnym z podanym |
|
| 2581 * jako \c format_type, ale nie zachowuje ich, a przesłanie takiej listy jest |
|
| 2582 * równoznaczne z usunięciem listy kontaktów. |
|
| 2583 * |
|
| 2584 * Program nie musi się przejmować kompresją listy kontaktów zgodną |
|
| 2585 * z protokołem -- wysyła i odbiera kompletną listę zapisaną czystym tekstem. |
|
| 2586 * |
|
| 2587 * \param sess Struktura sesji |
|
| 2588 * \param type Rodzaj zapytania |
|
| 2589 * \param version Numer ostatniej znanej programowi wersji listy kontaktów lub 0 |
|
| 2590 * \param format_type Typ formatu listy kontaktów |
|
| 2591 * \param request Treść zapytania (może być równe NULL) |
|
| 2592 * |
|
| 2593 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2594 * |
|
| 2595 * \ingroup importexport |
|
| 2596 */ |
|
| 2597 int gg_userlist100_request(struct gg_session *sess, char type, |
|
| 2598 unsigned int version, char format_type, const char *request) |
|
| 2599 { |
|
| 2600 struct gg_userlist100_request pkt; |
|
| 2601 unsigned char *zrequest; |
|
| 2602 size_t zrequest_len; |
|
| 2603 int ret; |
|
| 2604 |
|
| 2605 if (!sess) { |
|
| 2606 errno = EFAULT; |
|
| 2607 return -1; |
|
| 2608 } |
|
| 2609 |
|
| 2610 if (sess->state != GG_STATE_CONNECTED) { |
|
| 2611 errno = ENOTCONN; |
|
| 2612 return -1; |
|
| 2613 } |
|
| 2614 |
|
| 2615 pkt.type = type; |
|
| 2616 pkt.version = gg_fix32(version); |
|
| 2617 pkt.format_type = format_type; |
|
| 2618 pkt.unknown1 = 0x01; |
|
| 2619 |
|
| 2620 if (request == NULL) |
|
| 2621 return gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), NULL); |
|
| 2622 |
|
| 2623 zrequest = gg_deflate(request, &zrequest_len); |
|
| 2624 |
|
| 2625 if (zrequest == NULL) { |
|
| 2626 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_userlist100_request() gg_deflate() failed\n"); |
|
| 2627 return -1; |
|
| 2628 } |
|
| 2629 |
|
| 2630 ret = gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), zrequest, zrequest_len, NULL); |
|
| 2631 |
|
| 2632 free(zrequest); |
|
| 2633 |
|
| 2634 return ret; |
|
| 2635 } |
|
| 2636 |
|
| 2637 /** |
|
| 2638 * Informuje rozmówcę o pisaniu wiadomości. |
|
| 2639 * |
|
| 2640 * \param sess Struktura sesji |
|
| 2641 * \param recipient Numer adresata |
|
| 2642 * \param length Długość wiadomości lub 0 jeśli jest pusta |
|
| 2643 * |
|
| 2644 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2645 * |
|
| 2646 * \ingroup messages |
|
| 2647 */ |
|
| 2648 int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length){ |
|
| 2649 struct gg_typing_notification pkt; |
|
| 2650 uin_t uin; |
|
| 2651 |
|
| 2652 pkt.length = gg_fix16(length); |
|
| 2653 uin = gg_fix32(recipient); |
|
| 2654 memcpy(&pkt.uin, &uin, sizeof(uin_t)); |
|
| 2655 |
|
| 2656 return gg_send_packet(sess, GG_TYPING_NOTIFICATION, &pkt, sizeof(pkt), NULL); |
|
| 2657 } |
|
| 2658 |
|
| 2659 /** |
|
| 2660 * Rozłącza inną sesję multilogowania. |
|
| 2661 * |
|
| 2662 * \param gs Struktura sesji |
|
| 2663 * \param conn_id Sesja do rozłączenia |
|
| 2664 * |
|
| 2665 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2666 * |
|
| 2667 * \ingroup login |
|
| 2668 */ |
|
| 2669 int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id) |
|
| 2670 { |
|
| 2671 struct gg_multilogon_disconnect pkt; |
|
| 2672 |
|
| 2673 pkt.conn_id = conn_id; |
|
| 2674 |
|
| 2675 return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL); |
|
| 2676 } |
|
| 2677 |
|
| 2678 /** |
|
| 2679 * Tworzy nową konferencję (11.0). |
|
| 2680 * |
|
| 2681 * \param gs Struktura sesji |
|
| 2682 * |
|
| 2683 * \return Numer sekwencyjny (ten sam, co w \c gg_event_chat_created), lub -1 |
|
| 2684 * w przypadku błędu |
|
| 2685 * |
|
| 2686 * \ingroup chat |
|
| 2687 */ |
|
| 2688 int gg_chat_create(struct gg_session *gs) |
|
| 2689 { |
|
| 2690 struct gg_chat_create pkt; |
|
| 2691 int seq; |
|
| 2692 |
|
| 2693 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110)) |
|
| 2694 return -1; |
|
| 2695 |
|
| 2696 seq = ++gs->seq; |
|
| 2697 |
|
| 2698 pkt.seq = gg_fix32(seq); |
|
| 2699 pkt.dummy = 0; |
|
| 2700 |
|
| 2701 if (gg_send_packet(gs, GG_CHAT_CREATE, &pkt, sizeof(pkt), NULL) == -1) |
|
| 2702 return -1; |
|
| 2703 |
|
| 2704 return seq; |
|
| 2705 } |
|
| 2706 |
|
| 2707 /** |
|
| 2708 * Zaprasza nowych użytkowników do konferencji (11.0). |
|
| 2709 * |
|
| 2710 * \param gs Struktura sesji |
|
| 2711 * \param id Identyfikator konferencji |
|
| 2712 * \param participants Lista użytkowników do zaproszenia |
|
| 2713 * \param participants_count Liczba użytkowników |
|
| 2714 * |
|
| 2715 * \return Numer sekwencyjny w przypadku powodzenia (ten sam, co w |
|
| 2716 * \c gg_event_chat_invite_ack), lub -1 w przypadku błędu |
|
| 2717 * |
|
| 2718 * \ingroup chat |
|
| 2719 */ |
|
| 2720 int gg_chat_invite(struct gg_session *gs, uint64_t id, uin_t *participants, |
|
| 2721 unsigned int participants_count) |
|
| 2722 { |
|
| 2723 struct gg_chat_invite pkt; |
|
| 2724 int seq, ret; |
|
| 2725 unsigned int i; |
|
| 2726 struct gg_chat_participant |
|
| 2727 { |
|
| 2728 uint32_t uin; |
|
| 2729 uint32_t dummy; |
|
| 2730 } GG_PACKED; |
|
| 2731 struct gg_chat_participant *participants_list; |
|
| 2732 size_t participants_list_size; |
|
| 2733 |
|
| 2734 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110)) |
|
| 2735 return -1; |
|
| 2736 |
|
| 2737 if (participants_count == 0 || participants_count >= |
|
| 2738 ~(unsigned int)0 / sizeof(struct gg_chat_participant)) |
|
| 2739 { |
|
| 2740 return -1; |
|
| 2741 } |
|
| 2742 |
|
| 2743 participants_list_size = sizeof(struct gg_chat_participant) * |
|
| 2744 participants_count; |
|
| 2745 participants_list = malloc(participants_list_size); |
|
| 2746 if (participants_list == NULL) |
|
| 2747 return -1; |
|
| 2748 |
|
| 2749 seq = ++gs->seq; |
|
| 2750 pkt.id = gg_fix64(id); |
|
| 2751 pkt.seq = gg_fix32(seq); |
|
| 2752 pkt.participants_count = gg_fix32(participants_count); |
|
| 2753 |
|
| 2754 for (i = 0; i < participants_count; i++) { |
|
| 2755 participants_list[i].uin = gg_fix32(participants[i]); |
|
| 2756 participants_list[i].dummy = gg_fix32(0x1e); |
|
| 2757 } |
|
| 2758 |
|
| 2759 ret = gg_send_packet(gs, GG_CHAT_INVITE, |
|
| 2760 &pkt, sizeof(pkt), |
|
| 2761 participants_list, participants_list_size, |
|
| 2762 NULL); |
|
| 2763 free(participants_list); |
|
| 2764 |
|
| 2765 if (ret == -1) |
|
| 2766 return -1; |
|
| 2767 return seq; |
|
| 2768 } |
|
| 2769 |
|
| 2770 /** |
|
| 2771 * Opuszcza konferencję (11.0). |
|
| 2772 * |
|
| 2773 * \param gs Struktura sesji |
|
| 2774 * \param id Identyfikator konferencji |
|
| 2775 * |
|
| 2776 * \return 0 jeśli się powiodło, -1 w przypadku błędu |
|
| 2777 * |
|
| 2778 * \ingroup chat |
|
| 2779 */ |
|
| 2780 int gg_chat_leave(struct gg_session *gs, uint64_t id) |
|
| 2781 { |
|
| 2782 struct gg_chat_leave pkt; |
|
| 2783 int seq; |
|
| 2784 |
|
| 2785 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110)) |
|
| 2786 return -1; |
|
| 2787 |
|
| 2788 seq = ++gs->seq; |
|
| 2789 pkt.id = gg_fix64(id); |
|
| 2790 pkt.seq = gg_fix32(seq); |
|
| 2791 |
|
| 2792 if (gg_send_packet(gs, GG_CHAT_LEAVE, &pkt, sizeof(pkt), NULL) == -1) |
|
| 2793 return -1; |
|
| 2794 |
|
| 2795 return seq; |
|
| 2796 } |
|
| 2797 |
|
| 2798 /** |
|
| 2799 * Wysyła wiadomość w ramach konferencji (11.0). |
|
| 2800 * |
|
| 2801 * \param gs Struktura sesji |
|
| 2802 * \param id Identyfikator konferencji |
|
| 2803 * \param message Wiadomość |
|
| 2804 * \param is_html 1, jeżeli wiadomość jest zapisana jako HTML, 0 w p.p. |
|
| 2805 * |
|
| 2806 * \return Numer sekwencyjny (taki sam, jak w \c gg_event_chat_send_msg_ack) |
|
| 2807 * jeśli się powiodło, -1 w przypadku błędu |
|
| 2808 * |
|
| 2809 * \ingroup chat |
|
| 2810 */ |
|
| 2811 int gg_chat_send_message(struct gg_session *gs, uint64_t id, const char *message, int is_html) |
|
| 2812 { |
|
| 2813 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110)) |
|
| 2814 return -1; |
|
| 2815 |
|
| 2816 return gg_send_message_110(gs, 0, id, message, is_html); |
|
| 2817 } |
|
| 2818 |
|
| 2819 /* @} */ |
|
| 2820 |
|
| 2821 /** |
|
| 2822 * Sprawdza czy biblioteka obsługuje daną funkcję. |
|
| 2823 * |
|
| 2824 * \param feature Identyfikator funkcji. |
|
| 2825 * |
|
| 2826 * \return Wartość niezerowa jeśli funkcja jest obsłgiwana. |
|
| 2827 * |
|
| 2828 * \ingroup version |
|
| 2829 */ |
|
| 2830 int gg_libgadu_check_feature(gg_libgadu_feature_t feature) |
|
| 2831 { |
|
| 2832 switch (feature) |
|
| 2833 { |
|
| 2834 case GG_LIBGADU_FEATURE_SSL: |
|
| 2835 #if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS) |
|
| 2836 return 1; |
|
| 2837 #else |
|
| 2838 return 0; |
|
| 2839 #endif |
|
| 2840 |
|
| 2841 case GG_LIBGADU_FEATURE_PTHREAD: |
|
| 2842 #ifdef GG_CONFIG_HAVE_PTHREAD |
|
| 2843 return 1; |
|
| 2844 #else |
|
| 2845 return 0; |
|
| 2846 #endif |
|
| 2847 |
|
| 2848 case GG_LIBGADU_FEATURE_USERLIST100: |
|
| 2849 #ifdef GG_CONFIG_HAVE_ZLIB |
|
| 2850 return 1; |
|
| 2851 #else |
|
| 2852 return 0; |
|
| 2853 #endif |
|
| 2854 |
|
| 2855 /* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */ |
|
| 2856 |
|
| 2857 } |
|
| 2858 |
|
| 2859 return 0; |
|
| 2860 } |
|
| 2861 |
|
| 2862 static void gg_socket_manager_error(struct gg_session *sess, enum gg_failure_t failure) |
|
| 2863 { |
|
| 2864 int pipes[2]; |
|
| 2865 uint8_t dummy = 0; |
|
| 2866 struct gg_session_private *p = sess->private_data; |
|
| 2867 |
|
| 2868 p->socket_failure = failure; |
|
| 2869 |
|
| 2870 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) { |
|
| 2871 gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to" |
|
| 2872 " create pipes (errno=%d, %s)\n", errno, |
|
| 2873 strerror(errno)); |
|
| 2874 return; |
|
| 2875 } |
|
| 2876 |
|
| 2877 p->socket_is_external = 0; |
|
| 2878 sess->fd = pipes[1]; |
|
| 2879 sess->check = GG_CHECK_READ; |
|
| 2880 sess->state = GG_STATE_ERROR; |
|
| 2881 if (send(pipes[0], &dummy, sizeof(dummy), 0) != sizeof(dummy)) { |
|
| 2882 gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to" |
|
| 2883 " send via pipe (errno=%d, %s)\n", errno, |
|
| 2884 strerror(errno)); |
|
| 2885 return; |
|
| 2886 } |
|
| 2887 close(pipes[0]); |
|
| 2888 } |
|
| 2889 |
|
| 2890 int gg_compat_feature_is_enabled(struct gg_session *sess, gg_compat_feature_t feature) |
|
| 2891 { |
|
| 2892 gg_compat_t level; |
|
| 2893 |
|
| 2894 if (sess == NULL) |
|
| 2895 return 0; |
|
| 2896 |
|
| 2897 level = sess->private_data->compatibility; |
|
| 2898 |
|
| 2899 switch (feature) { |
|
| 2900 case GG_COMPAT_FEATURE_ACK_EVENT: |
|
| 2901 case GG_COMPAT_FEATURE_LEGACY_CONFER: |
|
| 2902 return (level < GG_COMPAT_1_12_0); |
|
| 2903 } |
|
| 2904 |
|
| 2905 return 0; |
|
| 2906 } |
|
| 2907 |
|
| 2908 static gg_msg_list_t * gg_compat_find_sent_message(struct gg_session *sess, int seq, int remove) |
|
| 2909 { |
|
| 2910 struct gg_session_private *p = sess->private_data; |
|
| 2911 gg_msg_list_t *it, *previous = NULL; |
|
| 2912 |
|
| 2913 for (it = p->sent_messages; it; it = it->next) { |
|
| 2914 if (it->seq == seq) |
|
| 2915 break; |
|
| 2916 else |
|
| 2917 previous = it; |
|
| 2918 } |
|
| 2919 |
|
| 2920 if (remove && it) { |
|
| 2921 if (previous == NULL) |
|
| 2922 p->sent_messages = it->next; |
|
| 2923 else |
|
| 2924 previous->next = it->next; |
|
| 2925 } |
|
| 2926 |
|
| 2927 return it; |
|
| 2928 } |
|
| 2929 |
|
| 2930 static void gg_compat_message_cleanup(struct gg_session *sess) |
|
| 2931 { |
|
| 2932 struct gg_session_private *p = sess->private_data; |
|
| 2933 |
|
| 2934 while (p->sent_messages) { |
|
| 2935 gg_msg_list_t *next = p->sent_messages->next; |
|
| 2936 free(p->sent_messages->recipients); |
|
| 2937 free(p->sent_messages); |
|
| 2938 p->sent_messages = next; |
|
| 2939 } |
|
| 2940 } |
|
| 2941 |
|
| 2942 static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients) |
|
| 2943 { |
|
| 2944 struct gg_session_private *p = sess->private_data; |
|
| 2945 gg_msg_list_t *sm; |
|
| 2946 uin_t *new_recipients; |
|
| 2947 size_t old_count, i; |
|
| 2948 |
|
| 2949 if (sess->protocol_version < GG_PROTOCOL_VERSION_110) |
|
| 2950 return; |
|
| 2951 |
|
| 2952 if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT)) |
|
| 2953 return; |
|
| 2954 |
|
| 2955 sm = gg_compat_find_sent_message(sess, seq, 0); |
|
| 2956 if (!sm) { |
|
| 2957 sm = gg_new0(sizeof(gg_msg_list_t)); |
|
| 2958 if (!sm) |
|
| 2959 return; |
|
| 2960 sm->next = p->sent_messages; |
|
| 2961 p->sent_messages = sm; |
|
| 2962 } |
|
| 2963 |
|
| 2964 sm->seq = seq; |
|
| 2965 old_count = sm->recipients_count; |
|
| 2966 sm->recipients_count += recipients_count; |
|
| 2967 |
|
| 2968 new_recipients = realloc(sm->recipients, sizeof(uin_t) * sm->recipients_count); |
|
| 2969 if (new_recipients == NULL) { |
|
| 2970 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 2971 "// gg_compat_message_sent() not enough memory\n"); |
|
| 2972 return; |
|
| 2973 } |
|
| 2974 sm->recipients = new_recipients; |
|
| 2975 |
|
| 2976 for (i = 0; i < recipients_count; i++) |
|
| 2977 sm->recipients[old_count + i] = recipients[i]; |
|
| 2978 } |
|
| 2979 |
|
| 2980 void gg_compat_message_ack(struct gg_session *sess, int seq) |
|
| 2981 { |
|
| 2982 gg_msg_list_t *sm; |
|
| 2983 size_t i; |
|
| 2984 |
|
| 2985 if (sess->protocol_version < GG_PROTOCOL_VERSION_110) |
|
| 2986 return; |
|
| 2987 |
|
| 2988 if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT)) |
|
| 2989 return; |
|
| 2990 |
|
| 2991 sm = gg_compat_find_sent_message(sess, seq, 1); |
|
| 2992 if (!sm) |
|
| 2993 return; |
|
| 2994 |
|
| 2995 for (i = 0; i < sm->recipients_count; i++) { |
|
| 2996 struct gg_event *qev; |
|
| 2997 |
|
| 2998 qev = gg_eventqueue_add(sess); |
|
| 2999 |
|
| 3000 qev->type = GG_EVENT_ACK; |
|
| 3001 qev->event.ack.status = GG_ACK_DELIVERED; |
|
| 3002 qev->event.ack.recipient = sm->recipients[i]; |
|
| 3003 qev->event.ack.seq = seq; |
|
| 3004 } |
|
| 3005 |
|
| 3006 free(sm->recipients); |
|
| 3007 free(sm); |
|
| 3008 } |
|
| 3009 |
|
| 3010 /** |
|
| 3011 * Odbiera nowo utworzone gniazdo TCP/TLS. |
|
| 3012 * |
|
| 3013 * Po wywołaniu tej funkcji należy zacząć obserwować deskryptor sesji (nawet |
|
| 3014 * w przypadku niepowodzenia). |
|
| 3015 * |
|
| 3016 * Jeżeli gniazdo nie zostanie obsłużone, należy je zniszczyć. |
|
| 3017 * |
|
| 3018 * \param handle Uchwyt gniazda |
|
| 3019 * \param priv Dane prywatne biblioteki libgadu |
|
| 3020 * \param fd Deskryptor nowo utworzonego gniazda, lub -1 w przypadku błędu |
|
| 3021 * |
|
| 3022 * \return Wartość różna od zera, jeżeli gniazdo zostało obsłużone, 0 w przeciwnym przypadku |
|
| 3023 * |
|
| 3024 * \ingroup socketmanager |
|
| 3025 */ |
|
| 3026 int gg_socket_manager_connected(void *handle, void *priv, int fd) |
|
| 3027 { |
|
| 3028 struct gg_session *sess = priv; |
|
| 3029 struct gg_session_private *p = sess->private_data; |
|
| 3030 |
|
| 3031 if (p->socket_handle != handle) { |
|
| 3032 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 3033 "// gg_socket_manager_connected() invalid handle\n"); |
|
| 3034 return 0; |
|
| 3035 } |
|
| 3036 |
|
| 3037 sess->fd = -1; |
|
| 3038 |
|
| 3039 if (fd < 0) { |
|
| 3040 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 3041 "// gg_socket_manager_connected() connection error\n"); |
|
| 3042 p->socket_handle = NULL; |
|
| 3043 gg_socket_manager_error(sess, GG_FAILURE_CONNECTING); |
|
| 3044 return 0; |
|
| 3045 } |
|
| 3046 |
|
| 3047 if (p->socket_next_state == GG_STATE_TLS_NEGOTIATION) { |
|
| 3048 if (gg_session_init_ssl(sess) == -1) { |
|
| 3049 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, |
|
| 3050 "// gg_socket_manager_connected() couldn't " |
|
| 3051 "initialize ssl\n"); |
|
| 3052 p->socket_handle = NULL; |
|
| 3053 gg_socket_manager_error(sess, GG_FAILURE_TLS); |
|
| 3054 return 0; |
|
| 3055 } |
|
| 3056 } |
|
| 3057 |
|
| 3058 p->socket_is_external = 1; |
|
| 3059 sess->fd = fd; |
|
| 3060 sess->timeout = GG_DEFAULT_TIMEOUT; |
|
| 3061 sess->state = p->socket_next_state; |
|
| 3062 |
|
| 3063 gg_debug_session(sess, GG_DEBUG_MISC, "// next state=%s\n", |
|
| 3064 gg_debug_state(p->socket_next_state)); |
|
| 3065 |
|
| 3066 if (p->socket_next_state == GG_STATE_READING_KEY) |
|
| 3067 sess->check = GG_CHECK_READ; |
|
| 3068 else |
|
| 3069 sess->check = GG_CHECK_WRITE; |
|
| 3070 |
|
| 3071 return 1; |
|
| 3072 } |
|
| 3073 |
|
| 3074 /* |
|
| 3075 * Local variables: |
|
| 3076 * c-indentation-style: k&r |
|
| 3077 * c-basic-offset: 8 |
|
| 3078 * indent-tabs-mode: notnil |
|
| 3079 * End: |
|
| 3080 * |
|
| 3081 * vim: shiftwidth=8: |
|
| 3082 */ |
|