| 31825:92996c775af3 | 31826:0f6ab56fbf9d |
|---|---|
| 1 /* $Id: libgadu.c 1062 2011-03-13 18:10:24Z wojtekka $ */ | 1 /* $Id: libgadu.c 1102 2011-05-05 21:17:57Z wojtekka $ */ |
| 2 | 2 |
| 3 /* | 3 /* |
| 4 * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl> | 4 * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl> |
| 5 * Robert J. Woźny <speedy@ziew.org> | 5 * Robert J. Woźny <speedy@ziew.org> |
| 6 * Arkadiusz Miśkiewicz <arekm@pld-linux.org> | 6 * Arkadiusz Miśkiewicz <arekm@pld-linux.org> |
| 26 * \file libgadu.c | 26 * \file libgadu.c |
| 27 * | 27 * |
| 28 * \brief Główny moduł biblioteki | 28 * \brief Główny moduł biblioteki |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "libgadu.h" | |
| 32 #include "libgadu-config.h" | |
| 33 #include "libgadu-internal.h" | |
| 34 #include "libgadu-debug.h" | |
| 35 | |
| 36 #include <sys/types.h> | 31 #include <sys/types.h> |
| 37 | |
| 38 #ifdef _WIN32 | 32 #ifdef _WIN32 |
| 39 # include <io.h> | 33 # include <io.h> |
| 40 # include <fcntl.h> | 34 # include <fcntl.h> |
| 41 # include <errno.h> | 35 # include <errno.h> |
| 42 # define SHUT_RDWR SD_BOTH | 36 # define SHUT_RDWR SD_BOTH |
| 48 # include <sys/filio.h> | 42 # include <sys/filio.h> |
| 49 # endif | 43 # endif |
| 50 #endif | 44 #endif |
| 51 | 45 |
| 52 #include "compat.h" | 46 #include "compat.h" |
| 47 #include "libgadu.h" | |
| 48 #include "libgadu-config.h" | |
| 53 #include "protocol.h" | 49 #include "protocol.h" |
| 54 #include "resolver.h" | 50 #include "resolver.h" |
| 51 #include "libgadu-internal.h" | |
| 55 #include "encoding.h" | 52 #include "encoding.h" |
| 53 #include "libgadu-debug.h" | |
| 56 #include "session.h" | 54 #include "session.h" |
| 55 #include "message.h" | |
| 56 #include "deflate.h" | |
| 57 | 57 |
| 58 #ifndef _WIN32 | 58 #ifndef _WIN32 |
| 59 # include <errno.h> /* on Win32 this is included above */ | 59 # include <errno.h> /* on Win32 this is included above */ |
| 60 # include <netdb.h> | 60 # include <netdb.h> |
| 61 #endif | 61 #endif |
| 62 | |
| 63 #include <stdarg.h> | 62 #include <stdarg.h> |
| 64 #include <stdio.h> | 63 #include <stdio.h> |
| 65 #include <stdlib.h> | 64 #include <stdlib.h> |
| 66 #include <string.h> | 65 #include <string.h> |
| 67 #include <signal.h> | 66 #include <signal.h> |
| 73 #ifdef GG_CONFIG_HAVE_OPENSSL | 72 #ifdef GG_CONFIG_HAVE_OPENSSL |
| 74 # include <openssl/err.h> | 73 # include <openssl/err.h> |
| 75 # include <openssl/rand.h> | 74 # include <openssl/rand.h> |
| 76 #endif | 75 #endif |
| 77 | 76 |
| 78 #define GG_LIBGADU_VERSION "1.10.1" | 77 #define GG_LIBGADU_VERSION "1.11.0" |
| 79 | 78 |
| 80 /** | 79 /** |
| 81 * Port gniazda nasłuchującego dla połączeń bezpośrednich. | 80 * Port gniazda nasłuchującego dla połączeń bezpośrednich. |
| 82 * | 81 * |
| 83 * \ingroup ip | 82 * \ingroup ip |
| 84 */ | 83 */ |
| 85 int gg_dcc_port = 0; | 84 int gg_dcc_port = 0; |
| 86 | 85 |
| 87 /** | 86 /** |
| 145 #ifndef lint | 144 #ifndef lint |
| 146 static char rcsid[] | 145 static char rcsid[] |
| 147 #ifdef __GNUC__ | 146 #ifdef __GNUC__ |
| 148 __attribute__ ((unused)) | 147 __attribute__ ((unused)) |
| 149 #endif | 148 #endif |
| 150 = "$Id: libgadu.c 1062 2011-03-13 18:10:24Z wojtekka $"; | 149 = "$Id: libgadu.c 1102 2011-05-05 21:17:57Z wojtekka $"; |
| 151 #endif | 150 #endif |
| 152 | 151 |
| 153 #endif /* DOXYGEN */ | 152 #endif /* DOXYGEN */ |
| 154 | 153 |
| 155 /** | 154 /** |
| 288 * | 287 * |
| 289 * \return To samo co funkcja systemowa \c read | 288 * \return To samo co funkcja systemowa \c read |
| 290 */ | 289 */ |
| 291 int gg_read(struct gg_session *sess, char *buf, int length) | 290 int gg_read(struct gg_session *sess, char *buf, int length) |
| 292 { | 291 { |
| 292 int res; | |
| 293 | |
| 293 #ifdef GG_CONFIG_HAVE_GNUTLS | 294 #ifdef GG_CONFIG_HAVE_GNUTLS |
| 294 if (sess->ssl != NULL) { | 295 if (sess->ssl != NULL) { |
| 295 for (;;) { | 296 for (;;) { |
| 296 int res; | |
| 297 | |
| 298 res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length); | 297 res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length); |
| 299 | 298 |
| 300 if (res < 0) { | 299 if (res < 0) { |
| 301 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED) | 300 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED) |
| 302 continue; | 301 continue; |
| 315 #endif | 314 #endif |
| 316 | 315 |
| 317 #ifdef GG_CONFIG_HAVE_OPENSSL | 316 #ifdef GG_CONFIG_HAVE_OPENSSL |
| 318 if (sess->ssl != NULL) { | 317 if (sess->ssl != NULL) { |
| 319 for (;;) { | 318 for (;;) { |
| 320 int res, err; | 319 int err; |
| 321 | 320 |
| 322 res = SSL_read(sess->ssl, buf, length); | 321 res = SSL_read(sess->ssl, buf, length); |
| 323 | 322 |
| 324 if (res < 0) { | 323 if (res < 0) { |
| 325 err = SSL_get_error(sess->ssl, res); | 324 err = SSL_get_error(sess->ssl, res); |
| 338 return res; | 337 return res; |
| 339 } | 338 } |
| 340 } | 339 } |
| 341 #endif | 340 #endif |
| 342 | 341 |
| 343 return read(sess->fd, buf, length); | 342 for (;;) { |
| 343 res = read(sess->fd, buf, length); | |
| 344 | |
| 345 if (res == -1 && errno == EINTR) | |
| 346 continue; | |
| 347 | |
| 348 return res; | |
| 349 } | |
| 344 } | 350 } |
| 345 | 351 |
| 346 /** | 352 /** |
| 347 * \internal Wysyła do serwera dane binarne. | 353 * \internal Wysyła do serwera dane binarne. |
| 348 * | 354 * |
| 359 * | 365 * |
| 360 * \return To samo co funkcja systemowa \c write | 366 * \return To samo co funkcja systemowa \c write |
| 361 */ | 367 */ |
| 362 static int gg_write_common(struct gg_session *sess, const char *buf, int length) | 368 static int gg_write_common(struct gg_session *sess, const char *buf, int length) |
| 363 { | 369 { |
| 370 int res; | |
| 371 | |
| 364 #ifdef GG_CONFIG_HAVE_GNUTLS | 372 #ifdef GG_CONFIG_HAVE_GNUTLS |
| 365 if (sess->ssl != NULL) { | 373 if (sess->ssl != NULL) { |
| 366 for (;;) { | 374 for (;;) { |
| 367 int res; | |
| 368 | |
| 369 res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length); | 375 res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length); |
| 370 | 376 |
| 371 if (res < 0) { | 377 if (res < 0) { |
| 372 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED) | 378 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED) |
| 373 continue; | 379 continue; |
| 386 #endif | 392 #endif |
| 387 | 393 |
| 388 #ifdef GG_CONFIG_HAVE_OPENSSL | 394 #ifdef GG_CONFIG_HAVE_OPENSSL |
| 389 if (sess->ssl != NULL) { | 395 if (sess->ssl != NULL) { |
| 390 for (;;) { | 396 for (;;) { |
| 391 int res, err; | 397 int err; |
| 392 | 398 |
| 393 res = SSL_write(sess->ssl, buf, length); | 399 res = SSL_write(sess->ssl, buf, length); |
| 394 | 400 |
| 395 if (res < 0) { | 401 if (res < 0) { |
| 396 err = SSL_get_error(sess->ssl, res); | 402 err = SSL_get_error(sess->ssl, res); |
| 409 return res; | 415 return res; |
| 410 } | 416 } |
| 411 } | 417 } |
| 412 #endif | 418 #endif |
| 413 | 419 |
| 414 return write(sess->fd, buf, length); | 420 for (;;) { |
| 421 res = write(sess->fd, buf, length); | |
| 422 | |
| 423 if (res == -1 && errno == EINTR) | |
| 424 continue; | |
| 425 | |
| 426 return res; | |
| 427 } | |
| 415 } | 428 } |
| 416 | 429 |
| 417 | 430 |
| 418 | 431 |
| 419 /** | 432 /** |
| 487 * \return Wskaźnik do zaalokowanego bufora | 500 * \return Wskaźnik do zaalokowanego bufora |
| 488 */ | 501 */ |
| 489 void *gg_recv_packet(struct gg_session *sess) | 502 void *gg_recv_packet(struct gg_session *sess) |
| 490 { | 503 { |
| 491 struct gg_header h; | 504 struct gg_header h; |
| 492 char *buf = NULL; | 505 char *packet; |
| 493 int ret = 0; | 506 int ret = 0; |
| 494 unsigned int offset, size = 0; | 507 unsigned int offset, size = 0; |
| 495 | 508 |
| 496 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); | 509 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); |
| 497 | 510 |
| 540 | 553 |
| 541 return NULL; | 554 return NULL; |
| 542 } | 555 } |
| 543 | 556 |
| 544 sess->header_done += ret; | 557 sess->header_done += ret; |
| 545 | |
| 546 } | 558 } |
| 547 | 559 |
| 548 h.type = gg_fix32(h.type); | 560 h.type = gg_fix32(h.type); |
| 549 h.length = gg_fix32(h.length); | 561 h.length = gg_fix32(h.length); |
| 550 } else | 562 } else |
| 559 | 571 |
| 560 if (sess->recv_left > 0) { | 572 if (sess->recv_left > 0) { |
| 561 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); | 573 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); |
| 562 size = sess->recv_left; | 574 size = sess->recv_left; |
| 563 offset = sess->recv_done; | 575 offset = sess->recv_done; |
| 564 buf = sess->recv_buf; | |
| 565 } else { | 576 } else { |
| 566 if (!(buf = malloc(sizeof(h) + h.length + 1))) { | 577 if (!(sess->recv_buf = malloc(sizeof(h) + h.length + 1))) { |
| 567 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); | 578 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); |
| 568 return NULL; | 579 return NULL; |
| 569 } | 580 } |
| 570 | 581 |
| 571 memcpy(buf, &h, sizeof(h)); | 582 memcpy(sess->recv_buf, &h, sizeof(h)); |
| 572 | 583 |
| 573 offset = 0; | 584 offset = 0; |
| 574 size = h.length; | 585 size = h.length; |
| 575 } | 586 } |
| 576 | 587 |
| 577 while (size > 0) { | 588 while (size > 0) { |
| 578 ret = gg_read(sess, buf + sizeof(h) + offset, size); | 589 ret = gg_read(sess, sess->recv_buf + sizeof(h) + offset, size); |
| 579 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); | 590 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, sess->recv_buf + sizeof(h) + offset, size, ret); |
| 580 if (!ret) { | 591 if (!ret) { |
| 581 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); | 592 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); |
| 582 errno = ECONNRESET; | 593 errno = ECONNRESET; |
| 583 return NULL; | 594 goto fail; |
| 584 } | 595 } |
| 585 if (ret > -1 && ret <= size) { | 596 if (ret > -1 && ret <= size) { |
| 586 offset += ret; | 597 offset += ret; |
| 587 size -= ret; | 598 size -= ret; |
| 588 } else if (ret == -1) { | 599 } else if (ret == -1) { |
| 589 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); | 600 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); |
| 590 | 601 |
| 591 if (errno == EAGAIN) { | 602 if (errno == EAGAIN) { |
| 592 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); | 603 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); |
| 593 sess->recv_buf = buf; | |
| 594 sess->recv_left = size; | 604 sess->recv_left = size; |
| 595 sess->recv_done = offset; | 605 sess->recv_done = offset; |
| 596 return NULL; | 606 return NULL; |
| 597 } | 607 } |
| 598 | 608 |
| 599 free(buf); | 609 goto fail; |
| 600 return NULL; | 610 } |
| 601 } | 611 } |
| 602 } | 612 |
| 603 | 613 packet = sess->recv_buf; |
| 614 sess->recv_buf = NULL; | |
| 604 sess->recv_left = 0; | 615 sess->recv_left = 0; |
| 605 | 616 |
| 606 gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(type=0x%.2x, length=%d)\n", h.type, h.length); | 617 gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(type=0x%.2x, length=%d)\n", h.type, h.length); |
| 607 gg_debug_dump(sess, GG_DEBUG_DUMP, buf, sizeof(h) + h.length); | 618 gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(h) + h.length); |
| 608 | 619 |
| 609 return buf; | 620 return packet; |
| 621 | |
| 622 fail: | |
| 623 free(sess->recv_buf); | |
| 624 sess->recv_buf = NULL; | |
| 625 sess->recv_left = 0; | |
| 626 | |
| 627 return NULL; | |
| 610 } | 628 } |
| 611 | 629 |
| 612 /** | 630 /** |
| 613 * \internal Wysyła pakiet do serwera. | 631 * \internal Wysyła pakiet do serwera. |
| 614 * | 632 * |
| 733 * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie | 751 * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie |
| 734 * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę | 752 * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę |
| 735 * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD. | 753 * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD. |
| 736 * | 754 * |
| 737 * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów | 755 * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów |
| 738 * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex(). | 756 * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex(). |
| 757 * | |
| 758 * \note Funkcja zwróci błąd ENOSYS jeśli połączenie SSL było wymagane, ale | |
| 759 * obsługa SSL nie jest wkompilowana. | |
| 739 * | 760 * |
| 740 * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin, | 761 * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin, |
| 741 * password, async. | 762 * password, async. |
| 742 * | 763 * |
| 743 * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL | 764 * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL |
| 794 sess->destroy = gg_free_session; | 815 sess->destroy = gg_free_session; |
| 795 sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT); | 816 sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT); |
| 796 sess->server_addr = p->server_addr; | 817 sess->server_addr = p->server_addr; |
| 797 sess->external_port = p->external_port; | 818 sess->external_port = p->external_port; |
| 798 sess->external_addr = p->external_addr; | 819 sess->external_addr = p->external_addr; |
| 820 sess->client_addr = p->client_addr; | |
| 799 sess->client_port = p->client_port; | 821 sess->client_port = p->client_port; |
| 800 | 822 |
| 801 if (p->protocol_features == 0) { | 823 if (p->protocol_features == 0) { |
| 802 sess->protocol_features = GG_FEATURE_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION; | 824 sess->protocol_features = GG_FEATURE_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION; |
| 803 } else { | 825 } else { |
| 846 | 868 |
| 847 if (!sess->initial_descr) { | 869 if (!sess->initial_descr) { |
| 848 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); | 870 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); |
| 849 goto fail; | 871 goto fail; |
| 850 } | 872 } |
| 851 | 873 |
| 852 // XXX pamiętać, żeby nie ciąć w środku znaku utf-8 | 874 // XXX pamiętać, żeby nie ciąć w środku znaku utf-8 |
| 853 | 875 |
| 854 if (strlen(sess->initial_descr) > max_length) | 876 if (strlen(sess->initial_descr) > max_length) |
| 855 sess->initial_descr[max_length] = 0; | 877 sess->initial_descr[max_length] = 0; |
| 856 } | 878 } |
| 857 | 879 |
| 858 if (p->tls == 1) { | 880 if (p->tls != GG_SSL_DISABLED) { |
| 859 #ifdef GG_CONFIG_HAVE_GNUTLS | 881 #ifdef GG_CONFIG_HAVE_GNUTLS |
| 860 gg_session_gnutls_t *tmp; | 882 gg_session_gnutls_t *tmp; |
| 861 | 883 |
| 862 tmp = malloc(sizeof(gg_session_gnutls_t)); | 884 tmp = malloc(sizeof(gg_session_gnutls_t)); |
| 863 | 885 |
| 910 gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf); | 932 gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf); |
| 911 goto fail; | 933 goto fail; |
| 912 } | 934 } |
| 913 #else | 935 #else |
| 914 gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); | 936 gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); |
| 937 | |
| 938 if (p->tls == GG_SSL_REQUIRED) { | |
| 939 errno = ENOSYS; | |
| 940 goto fail; | |
| 941 } | |
| 915 #endif | 942 #endif |
| 916 } | 943 } |
| 917 | 944 |
| 918 if (gg_proxy_enabled) { | 945 if (gg_proxy_enabled) { |
| 919 hostname = gg_proxy_host; | 946 hostname = gg_proxy_host; |
| 1139 /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */ | 1166 /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */ |
| 1140 | 1167 |
| 1141 free(sess->password); | 1168 free(sess->password); |
| 1142 free(sess->initial_descr); | 1169 free(sess->initial_descr); |
| 1143 free(sess->client_version); | 1170 free(sess->client_version); |
| 1171 free(sess->recv_buf); | |
| 1144 free(sess->header_buf); | 1172 free(sess->header_buf); |
| 1145 | 1173 |
| 1146 #ifdef GG_CONFIG_HAVE_OPENSSL | 1174 #ifdef GG_CONFIG_HAVE_OPENSSL |
| 1147 if (sess->ssl) | 1175 if (sess->ssl) |
| 1148 SSL_free(sess->ssl); | 1176 SSL_free(sess->ssl); |
| 1275 NULL); | 1303 NULL); |
| 1276 } | 1304 } |
| 1277 | 1305 |
| 1278 free(new_descr); | 1306 free(new_descr); |
| 1279 | 1307 |
| 1280 if (GG_S_NA(status)) | 1308 if (GG_S_NA(status)) { |
| 1281 sess->state = GG_STATE_DISCONNECTING; | 1309 sess->state = GG_STATE_DISCONNECTING; |
| 1310 sess->timeout = GG_TIMEOUT_DISCONNECT; | |
| 1311 } | |
| 1282 | 1312 |
| 1283 return res; | 1313 return res; |
| 1284 } | 1314 } |
| 1285 | 1315 |
| 1286 #endif /* DOXYGEN */ | 1316 #endif /* DOXYGEN */ |
| 1431 int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) | 1461 int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) |
| 1432 { | 1462 { |
| 1433 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); | 1463 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); |
| 1434 | 1464 |
| 1435 return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); | 1465 return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); |
| 1436 } | |
| 1437 | |
| 1438 /** | |
| 1439 * \internal Dodaje tekst na koniec bufora. | |
| 1440 * | |
| 1441 * \param dst Wskaźnik na bufor roboczy | |
| 1442 * \param pos Wskaźnik na aktualne położenie w buforze roboczym | |
| 1443 * \param src Dodawany tekst | |
| 1444 * \param len Długość dodawanego tekstu | |
| 1445 */ | |
| 1446 static void gg_append(char *dst, int *pos, const void *src, int len) | |
| 1447 { | |
| 1448 if (dst != NULL) | |
| 1449 memcpy(&dst[*pos], src, len); | |
| 1450 | |
| 1451 *pos += len; | |
| 1452 } | |
| 1453 | |
| 1454 /** | |
| 1455 * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML. | |
| 1456 * | |
| 1457 * \param dst Bufor wynikowy (może być \c NULL) | |
| 1458 * \param src Tekst źródłowy w UTF-8 | |
| 1459 * \param format Atrybuty tekstu źródłowego | |
| 1460 * \param format_len Długość bloku atrybutów tekstu źródłowego | |
| 1461 * | |
| 1462 * \note Wynikowy tekst nie jest idealnym kodem HTML, ponieważ ma jak | |
| 1463 * dokładniej odzwierciedlać to, co wygenerowałby oryginalny klient. | |
| 1464 * | |
| 1465 * \note Dokleja \c \\0 na końcu bufora wynikowego. | |
| 1466 * | |
| 1467 * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL). | |
| 1468 */ | |
| 1469 static int gg_convert_to_html(char *dst, const char *src, const unsigned char *format, int format_len) | |
| 1470 { | |
| 1471 const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">"; | |
| 1472 const int span_len = 75; | |
| 1473 const char img_fmt[] = "<img name=\"%02x%02x%02x%02x%02x%02x%02x%02x\">"; | |
| 1474 const int img_len = 29; | |
| 1475 int char_pos = 0; | |
| 1476 int format_idx = 0; | |
| 1477 unsigned char old_attr = 0; | |
| 1478 const unsigned char *color = (const unsigned char*) "\x00\x00\x00"; | |
| 1479 int len, i; | |
| 1480 | |
| 1481 len = 0; | |
| 1482 | |
| 1483 /* Nie mamy atrybutów dla pierwsze znaku, a tekst nie jest pusty, więc | |
| 1484 * tak czy inaczej trzeba otworzyć <span>. */ | |
| 1485 | |
| 1486 if (src[0] != 0 && (format_idx + 3 > format_len || (format[format_idx] | (format[format_idx + 1] << 8)) != 0)) { | |
| 1487 if (dst != NULL) | |
| 1488 sprintf(&dst[len], span_fmt, 0, 0, 0); | |
| 1489 | |
| 1490 len += span_len; | |
| 1491 } | |
| 1492 | |
| 1493 /* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek | |
| 1494 * na końcu tekstu. */ | |
| 1495 | |
| 1496 for (i = 0; ; i++) { | |
| 1497 /* Analizuj atrybuty tak długo jak dotyczą aktualnego znaku. */ | |
| 1498 for (;;) { | |
| 1499 unsigned char attr; | |
| 1500 int attr_pos; | |
| 1501 | |
| 1502 if (format_idx + 3 > format_len) | |
| 1503 break; | |
| 1504 | |
| 1505 attr_pos = format[format_idx] | (format[format_idx + 1] << 8); | |
| 1506 | |
| 1507 if (attr_pos != char_pos) | |
| 1508 break; | |
| 1509 | |
| 1510 attr = format[format_idx + 2]; | |
| 1511 | |
| 1512 /* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */ | |
| 1513 | |
| 1514 if (src[i] == 0) | |
| 1515 attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR); | |
| 1516 | |
| 1517 format_idx += 3; | |
| 1518 | |
| 1519 if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 0)) { | |
| 1520 if (char_pos != 0) { | |
| 1521 if ((old_attr & GG_FONT_UNDERLINE) != 0) | |
| 1522 gg_append(dst, &len, "</u>", 4); | |
| 1523 | |
| 1524 if ((old_attr & GG_FONT_ITALIC) != 0) | |
| 1525 gg_append(dst, &len, "</i>", 4); | |
| 1526 | |
| 1527 if ((old_attr & GG_FONT_BOLD) != 0) | |
| 1528 gg_append(dst, &len, "</b>", 4); | |
| 1529 | |
| 1530 if (src[i] != 0) | |
| 1531 gg_append(dst, &len, "</span>", 7); | |
| 1532 } | |
| 1533 | |
| 1534 if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { | |
| 1535 color = &format[format_idx]; | |
| 1536 format_idx += 3; | |
| 1537 } else { | |
| 1538 color = (const unsigned char*) "\x00\x00\x00"; | |
| 1539 } | |
| 1540 | |
| 1541 if (src[i] != 0) { | |
| 1542 if (dst != NULL) | |
| 1543 sprintf(&dst[len], span_fmt, color[0], color[1], color[2]); | |
| 1544 len += span_len; | |
| 1545 } | |
| 1546 } else if (char_pos == 0 && src[0] != 0) { | |
| 1547 if (dst != NULL) | |
| 1548 sprintf(&dst[len], span_fmt, 0, 0, 0); | |
| 1549 len += span_len; | |
| 1550 } | |
| 1551 | |
| 1552 if ((attr & GG_FONT_BOLD) != 0) | |
| 1553 gg_append(dst, &len, "<b>", 3); | |
| 1554 | |
| 1555 if ((attr & GG_FONT_ITALIC) != 0) | |
| 1556 gg_append(dst, &len, "<i>", 3); | |
| 1557 | |
| 1558 if ((attr & GG_FONT_UNDERLINE) != 0) | |
| 1559 gg_append(dst, &len, "<u>", 3); | |
| 1560 | |
| 1561 if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) { | |
| 1562 if (dst != NULL) { | |
| 1563 sprintf(&dst[len], img_fmt, | |
| 1564 format[format_idx + 9], | |
| 1565 format[format_idx + 8], | |
| 1566 format[format_idx + 7], | |
| 1567 format[format_idx + 6], | |
| 1568 format[format_idx + 5], | |
| 1569 format[format_idx + 4], | |
| 1570 format[format_idx + 3], | |
| 1571 format[format_idx + 2]); | |
| 1572 } | |
| 1573 | |
| 1574 len += img_len; | |
| 1575 format_idx += 10; | |
| 1576 } | |
| 1577 | |
| 1578 old_attr = attr; | |
| 1579 } | |
| 1580 | |
| 1581 /* Doklej znak zachowując htmlowe escapowanie. */ | |
| 1582 | |
| 1583 switch (src[i]) { | |
| 1584 case '&': | |
| 1585 gg_append(dst, &len, "&", 5); | |
| 1586 break; | |
| 1587 case '<': | |
| 1588 gg_append(dst, &len, "<", 4); | |
| 1589 break; | |
| 1590 case '>': | |
| 1591 gg_append(dst, &len, ">", 4); | |
| 1592 break; | |
| 1593 case '\'': | |
| 1594 gg_append(dst, &len, "'", 6); | |
| 1595 break; | |
| 1596 case '\"': | |
| 1597 gg_append(dst, &len, """, 6); | |
| 1598 break; | |
| 1599 case '\n': | |
| 1600 gg_append(dst, &len, "<br>", 4); | |
| 1601 break; | |
| 1602 case '\r': | |
| 1603 case 0: | |
| 1604 break; | |
| 1605 default: | |
| 1606 if (dst != NULL) | |
| 1607 dst[len] = src[i]; | |
| 1608 len++; | |
| 1609 } | |
| 1610 | |
| 1611 /* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */ | |
| 1612 | |
| 1613 if ((src[i] & 0xc0) != 0xc0) | |
| 1614 char_pos++; | |
| 1615 | |
| 1616 if (src[i] == 0) | |
| 1617 break; | |
| 1618 } | |
| 1619 | |
| 1620 /* Zamknij tagi. */ | |
| 1621 | |
| 1622 if ((old_attr & GG_FONT_UNDERLINE) != 0) | |
| 1623 gg_append(dst, &len, "</u>", 4); | |
| 1624 | |
| 1625 if ((old_attr & GG_FONT_ITALIC) != 0) | |
| 1626 gg_append(dst, &len, "</i>", 4); | |
| 1627 | |
| 1628 if ((old_attr & GG_FONT_BOLD) != 0) | |
| 1629 gg_append(dst, &len, "</b>", 4); | |
| 1630 | |
| 1631 if (src[0] != 0) | |
| 1632 gg_append(dst, &len, "</span>", 7); | |
| 1633 | |
| 1634 if (dst != NULL) | |
| 1635 dst[len] = 0; | |
| 1636 | |
| 1637 return len; | |
| 1638 } | 1466 } |
| 1639 | 1467 |
| 1640 /** | 1468 /** |
| 1641 * Wysyła wiadomość formatowaną w ramach konferencji. | 1469 * Wysyła wiadomość formatowaną w ramach konferencji. |
| 1642 * | 1470 * |
| 1650 * \param message Treść wiadomości | 1478 * \param message Treść wiadomości |
| 1651 * \param format Informacje o formatowaniu | 1479 * \param format Informacje o formatowaniu |
| 1652 * \param formatlen Długość informacji o formatowaniu | 1480 * \param formatlen Długość informacji o formatowaniu |
| 1653 * | 1481 * |
| 1654 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. | 1482 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. |
| 1655 * | 1483 * |
| 1656 * \ingroup messages | 1484 * \ingroup messages |
| 1657 */ | 1485 */ |
| 1658 int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) | 1486 int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) |
| 1659 { | 1487 { |
| 1660 struct gg_send_msg s; | 1488 struct gg_send_msg s; |
| 1706 | 1534 |
| 1707 s.msgclass = gg_fix32(msgclass); | 1535 s.msgclass = gg_fix32(msgclass); |
| 1708 s.seq = gg_fix32(seq_no); | 1536 s.seq = gg_fix32(seq_no); |
| 1709 } else { | 1537 } else { |
| 1710 int len; | 1538 int len; |
| 1711 | 1539 |
| 1712 // Drobne odchylenie od protokołu. Jeśli wysyłamy kilka | 1540 // Drobne odchylenie od protokołu. Jeśli wysyłamy kilka |
| 1713 // wiadomości w ciągu jednej sekundy, zwiększamy poprzednią | 1541 // wiadomości w ciągu jednej sekundy, zwiększamy poprzednią |
| 1714 // wartość, żeby każda wiadomość miała unikalny numer. | 1542 // wartość, żeby każda wiadomość miała unikalny numer. |
| 1715 | 1543 |
| 1716 seq_no = time(NULL); | 1544 seq_no = time(NULL); |
| 1723 if (format == NULL || formatlen < 3) { | 1551 if (format == NULL || formatlen < 3) { |
| 1724 format = (unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00"; | 1552 format = (unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00"; |
| 1725 formatlen = 9; | 1553 formatlen = 9; |
| 1726 } | 1554 } |
| 1727 | 1555 |
| 1728 len = gg_convert_to_html(NULL, utf_msg, format + 3, formatlen - 3); | 1556 len = gg_message_text_to_html(NULL, utf_msg, (char*) format + 3, formatlen - 3); |
| 1729 | 1557 |
| 1730 html_msg = malloc(len + 1); | 1558 html_msg = malloc(len + 1); |
| 1731 | 1559 |
| 1732 if (html_msg == NULL) { | 1560 if (html_msg == NULL) { |
| 1733 seq_no = -1; | 1561 seq_no = -1; |
| 1734 goto cleanup; | 1562 goto cleanup; |
| 1735 } | 1563 } |
| 1736 | 1564 |
| 1737 gg_convert_to_html(html_msg, utf_msg, format + 3, formatlen - 3); | 1565 gg_message_text_to_html(html_msg, utf_msg, (char*) format + 3, formatlen - 3); |
| 1738 | 1566 |
| 1739 s80.seq = gg_fix32(seq_no); | 1567 s80.seq = gg_fix32(seq_no); |
| 1740 s80.msgclass = gg_fix32(msgclass); | 1568 s80.msgclass = gg_fix32(msgclass); |
| 1741 s80.offset_plain = gg_fix32(sizeof(s80) + strlen(html_msg) + 1); | 1569 s80.offset_plain = gg_fix32(sizeof(s80) + strlen(html_msg) + 1); |
| 1742 s80.offset_attr = gg_fix32(sizeof(s80) + strlen(html_msg) + 1 + strlen(cp_msg) + 1); | 1570 s80.offset_attr = gg_fix32(sizeof(s80) + strlen(html_msg) + 1 + strlen(cp_msg) + 1); |
| 2333 | 2161 |
| 2334 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); | 2162 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); |
| 2335 } | 2163 } |
| 2336 | 2164 |
| 2337 /** | 2165 /** |
| 2166 * Wysyła do serwera zapytanie dotyczące listy kontaktów (10.0). | |
| 2167 * | |
| 2168 * Funkcja służy do importu lub eksportu listy kontaktów do serwera. | |
| 2169 * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez | |
| 2170 * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format | |
| 2171 * listy kontaktów jest jednak weryfikowany przez serwer, który stara się | |
| 2172 * synchronizować listę kontaktów zapisaną w formatach GG 7.0 oraz GG 10.0. | |
| 2173 * Serwer przyjmuje listy kontaktów przysłane w formacie niezgodnym z podanym | |
| 2174 * jako \c format_type, ale nie zachowuje ich, a przesłanie takiej listy jest | |
| 2175 * równoznaczne z usunięciem listy kontaktów. | |
| 2176 * | |
| 2177 * Program nie musi się przejmować kompresją listy kontaktów zgodną | |
| 2178 * z protokołem -- wysyła i odbiera kompletną listę zapisaną czystym tekstem. | |
| 2179 * | |
| 2180 * \param sess Struktura sesji | |
| 2181 * \param type Rodzaj zapytania | |
| 2182 * \param version Numer ostatniej znanej programowi wersji listy kontaktów lub 0 | |
| 2183 * \param format_type Typ formatu listy kontaktów | |
| 2184 * \param request Treść zapytania (może być równe NULL) | |
| 2185 * | |
| 2186 * \return 0 jeśli się powiodło, -1 w przypadku błędu | |
| 2187 * | |
| 2188 * \ingroup importexport | |
| 2189 */ | |
| 2190 int gg_userlist100_request(struct gg_session *sess, char type, unsigned int version, char format_type, const char *request) | |
| 2191 { | |
| 2192 struct gg_userlist100_request pkt; | |
| 2193 unsigned char *zrequest; | |
| 2194 size_t zrequest_len; | |
| 2195 int ret; | |
| 2196 | |
| 2197 if (!sess) { | |
| 2198 errno = EFAULT; | |
| 2199 return -1; | |
| 2200 } | |
| 2201 | |
| 2202 if (sess->state != GG_STATE_CONNECTED) { | |
| 2203 errno = ENOTCONN; | |
| 2204 return -1; | |
| 2205 } | |
| 2206 | |
| 2207 pkt.type = type; | |
| 2208 pkt.version = gg_fix32(version); | |
| 2209 pkt.format_type = format_type; | |
| 2210 pkt.unknown1 = 0x01; | |
| 2211 | |
| 2212 if (request == NULL) | |
| 2213 return gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), NULL); | |
| 2214 | |
| 2215 zrequest = gg_deflate(request, &zrequest_len); | |
| 2216 | |
| 2217 if (zrequest == NULL) { | |
| 2218 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_userlist100_request() gg_deflate() failed"); | |
| 2219 return -1; | |
| 2220 } | |
| 2221 | |
| 2222 ret = gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), zrequest, zrequest_len, NULL); | |
| 2223 | |
| 2224 free(zrequest); | |
| 2225 | |
| 2226 return ret; | |
| 2227 } | |
| 2228 | |
| 2229 /** | |
| 2338 * Informuje rozmówcę o pisaniu wiadomości. | 2230 * Informuje rozmówcę o pisaniu wiadomości. |
| 2339 * | 2231 * |
| 2340 * \param sess Struktura sesji | 2232 * \param sess Struktura sesji |
| 2341 * \param recipient Numer adresata | 2233 * \param recipient Numer adresata |
| 2342 * \param length Długość wiadomości lub 0 jeśli jest pusta | 2234 * \param length Długość wiadomości lub 0 jeśli jest pusta |
| 2374 | 2266 |
| 2375 return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL); | 2267 return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL); |
| 2376 } | 2268 } |
| 2377 | 2269 |
| 2378 /* @} */ | 2270 /* @} */ |
| 2271 | |
| 2272 /** | |
| 2273 * Sprawdza czy biblioteka obsługuje daną funkcję. | |
| 2274 * | |
| 2275 * \param feature Identyfikator funkcji. | |
| 2276 * | |
| 2277 * \return Wartość niezerowa jeśli funkcja jest obsłgiwana. | |
| 2278 * | |
| 2279 * \ingroup version | |
| 2280 */ | |
| 2281 int gg_libgadu_check_feature(gg_libgadu_feature_t feature) | |
| 2282 { | |
| 2283 switch (feature) | |
| 2284 { | |
| 2285 case GG_LIBGADU_FEATURE_SSL: | |
| 2286 #if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS) | |
| 2287 return 1; | |
| 2288 #else | |
| 2289 return 0; | |
| 2290 #endif | |
| 2291 | |
| 2292 case GG_LIBGADU_FEATURE_PTHREAD: | |
| 2293 #ifdef GG_CONFIG_HAVE_PTHREAD | |
| 2294 return 1; | |
| 2295 #else | |
| 2296 return 0; | |
| 2297 #endif | |
| 2298 | |
| 2299 case GG_LIBGADU_FEATURE_USERLIST100: | |
| 2300 #ifdef GG_CONFIG_HAVE_ZLIB | |
| 2301 return 1; | |
| 2302 #else | |
| 2303 return 0; | |
| 2304 #endif | |
| 2305 | |
| 2306 /* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */ | |
| 2307 | |
| 2308 } | |
| 2309 | |
| 2310 return 0; | |
| 2311 } | |
| 2379 | 2312 |
| 2380 /* | 2313 /* |
| 2381 * Local variables: | 2314 * Local variables: |
| 2382 * c-indentation-style: k&r | 2315 * c-indentation-style: k&r |
| 2383 * c-basic-offset: 8 | 2316 * c-basic-offset: 8 |