libpurple/protocols/gg/lib/libgadu.c

changeset 31826
0f6ab56fbf9d
parent 31609
d67fbc90b28a
child 31965
8fadb7594280
equal deleted inserted replaced
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, "&amp;", 5);
1586 break;
1587 case '<':
1588 gg_append(dst, &len, "&lt;", 4);
1589 break;
1590 case '>':
1591 gg_append(dst, &len, "&gt;", 4);
1592 break;
1593 case '\'':
1594 gg_append(dst, &len, "&apos;", 6);
1595 break;
1596 case '\"':
1597 gg_append(dst, &len, "&quot;", 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

mercurial