Mercurial > pidgin
comparison libpurple/protocols/gg/lib/libgadu.c @ 31410:93b08d43f684
matekm and kkszysiu collaborated on this patch to update our internal libgadu
to version 1.10.1.
author | John Bailey <rekkanoryo@rekkanoryo.org> |
---|---|
date | Thu, 24 Mar 2011 20:53:13 +0000 |
parents | a8cc50c2279f |
children | 3a90a59ddea2 |
comparison
equal
deleted
inserted
replaced
31409:5043fc53f957 | 31410:93b08d43f684 |
---|---|
1 /* $Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $ */ | 1 /* $Id: libgadu.c 1062 2011-03-13 18:10:24Z wojtekka $ */ |
2 | 2 |
3 /* | 3 /* |
4 * (C) Copyright 2001-2009 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> |
7 * Tomasz Chiliński <chilek@chilan.com> | 7 * Tomasz Chiliński <chilek@chilan.com> |
8 * Adam Wysocki <gophi@ekg.chmurka.net> | 8 * Adam Wysocki <gophi@ekg.chmurka.net> |
9 * | 9 * |
29 */ | 29 */ |
30 | 30 |
31 #include "libgadu.h" | 31 #include "libgadu.h" |
32 #include "libgadu-config.h" | 32 #include "libgadu-config.h" |
33 #include "libgadu-internal.h" | 33 #include "libgadu-internal.h" |
34 #include "libgadu-debug.h" | |
34 | 35 |
35 #include <sys/types.h> | 36 #include <sys/types.h> |
36 | 37 |
37 #ifdef _WIN32 | 38 #ifdef _WIN32 |
38 # include <io.h> | 39 # include <io.h> |
49 #endif | 50 #endif |
50 | 51 |
51 #include "compat.h" | 52 #include "compat.h" |
52 #include "protocol.h" | 53 #include "protocol.h" |
53 #include "resolver.h" | 54 #include "resolver.h" |
55 #include "encoding.h" | |
56 #include "session.h" | |
54 | 57 |
55 #ifndef _WIN32 | 58 #ifndef _WIN32 |
56 # include <errno.h> /* on Win32 this is included above */ | 59 # include <errno.h> /* on Win32 this is included above */ |
57 # include <netdb.h> | 60 # include <netdb.h> |
58 #endif | 61 #endif |
62 #include <stdlib.h> | 65 #include <stdlib.h> |
63 #include <string.h> | 66 #include <string.h> |
64 #include <signal.h> | 67 #include <signal.h> |
65 #include <time.h> | 68 #include <time.h> |
66 #include <unistd.h> | 69 #include <unistd.h> |
70 #ifdef GG_CONFIG_HAVE_GNUTLS | |
71 # include <gnutls/gnutls.h> | |
72 #endif | |
67 #ifdef GG_CONFIG_HAVE_OPENSSL | 73 #ifdef GG_CONFIG_HAVE_OPENSSL |
68 # include <openssl/err.h> | 74 # include <openssl/err.h> |
69 # include <openssl/rand.h> | 75 # include <openssl/rand.h> |
70 #endif | 76 #endif |
71 | 77 |
72 #define GG_LIBGADU_VERSION "1.9.0" | 78 #define GG_LIBGADU_VERSION "1.10.1" |
73 | |
74 /** | |
75 * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową | |
76 * składającą się ze stałych \c GG_DEBUG_... | |
77 * | |
78 * \ingroup debug | |
79 */ | |
80 int gg_debug_level = 0; | |
81 | |
82 /** | |
83 * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno | |
84 * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe | |
85 * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr). | |
86 * | |
87 * \param level Poziom rejestracji | |
88 * \param format Format wiadomości (zgodny z \c printf) | |
89 * \param ap Lista argumentów (zgodna z \c printf) | |
90 * | |
91 * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session. | |
92 * | |
93 * \ingroup debug | |
94 */ | |
95 void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; | |
96 | |
97 /** | |
98 * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno | |
99 * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe | |
100 * \c NULL, informacje są wysyłane do standardowego wyjścia błędu. | |
101 * | |
102 * \param sess Sesja której dotyczy informacja lub \c NULL | |
103 * \param level Poziom rejestracji | |
104 * \param format Format wiadomości (zgodny z \c printf) | |
105 * \param ap Lista argumentów (zgodna z \c printf) | |
106 * | |
107 * \note Funkcja przesłania przez \c gg_debug_handler_session. | |
108 * | |
109 * \ingroup debug | |
110 */ | |
111 void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL; | |
112 | 79 |
113 /** | 80 /** |
114 * Port gniazda nasłuchującego dla połączeń bezpośrednich. | 81 * Port gniazda nasłuchującego dla połączeń bezpośrednich. |
115 * | 82 * |
116 * \ingroup ip | 83 * \ingroup ip |
178 #ifndef lint | 145 #ifndef lint |
179 static char rcsid[] | 146 static char rcsid[] |
180 #ifdef __GNUC__ | 147 #ifdef __GNUC__ |
181 __attribute__ ((unused)) | 148 __attribute__ ((unused)) |
182 #endif | 149 #endif |
183 = "$Id: libgadu.c 923 2010-03-09 20:03:29Z wojtekka $"; | 150 = "$Id: libgadu.c 1062 2011-03-13 18:10:24Z wojtekka $"; |
184 #endif | 151 #endif |
185 | 152 |
186 #endif /* DOXYGEN */ | 153 #endif /* DOXYGEN */ |
187 | 154 |
188 /** | 155 /** |
194 */ | 161 */ |
195 const char *gg_libgadu_version() | 162 const char *gg_libgadu_version() |
196 { | 163 { |
197 return GG_LIBGADU_VERSION; | 164 return GG_LIBGADU_VERSION; |
198 } | 165 } |
166 | |
167 #ifdef GG_CONFIG_HAVE_UINT64_T | |
168 /** | |
169 * \internal Zamienia kolejność bajtów w 64-bitowym słowie. | |
170 * | |
171 * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach | |
172 * big-endianowych odwraca kolejność bajtów w słowie. | |
173 * | |
174 * \param x Liczba do zamiany | |
175 * | |
176 * \return Liczba z odpowiednią kolejnością bajtów | |
177 * | |
178 * \ingroup helper | |
179 */ | |
180 uint64_t gg_fix64(uint64_t x) | |
181 { | |
182 #ifndef GG_CONFIG_BIGENDIAN | |
183 return x; | |
184 #else | |
185 return (uint64_t) | |
186 (((x & (uint64_t) 0x00000000000000ffULL) << 56) | | |
187 ((x & (uint64_t) 0x000000000000ff00ULL) << 40) | | |
188 ((x & (uint64_t) 0x0000000000ff0000ULL) << 24) | | |
189 ((x & (uint64_t) 0x00000000ff000000ULL) << 8) | | |
190 ((x & (uint64_t) 0x000000ff00000000ULL) >> 8) | | |
191 ((x & (uint64_t) 0x0000ff0000000000ULL) >> 24) | | |
192 ((x & (uint64_t) 0x00ff000000000000ULL) >> 40) | | |
193 ((x & (uint64_t) 0xff00000000000000ULL) >> 56)); | |
194 #endif | |
195 } | |
196 #endif /* GG_CONFIG_HAVE_UINT64_T */ | |
199 | 197 |
200 /** | 198 /** |
201 * \internal Zamienia kolejność bajtów w 32-bitowym słowie. | 199 * \internal Zamienia kolejność bajtów w 32-bitowym słowie. |
202 * | 200 * |
203 * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach | 201 * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach |
278 } | 276 } |
279 | 277 |
280 /** | 278 /** |
281 * \internal Odbiera od serwera dane binarne. | 279 * \internal Odbiera od serwera dane binarne. |
282 * | 280 * |
283 * Funkcja odbiera dane od serwera zajmując się TLS w razie konieczności. | 281 * Funkcja odbiera dane od serwera zajmując się SSL/TLS w razie konieczności. |
282 * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi | |
283 * wywołaniami systemowymi. | |
284 * | 284 * |
285 * \param sess Struktura sesji | 285 * \param sess Struktura sesji |
286 * \param buf Bufor na danymi | 286 * \param buf Bufor na danymi |
287 * \param length Długość bufora | 287 * \param length Długość bufora |
288 * | 288 * |
289 * \return To samo co funkcja systemowa \c read | 289 * \return To samo co funkcja systemowa \c read |
290 */ | 290 */ |
291 int gg_read(struct gg_session *sess, char *buf, int length) | 291 int gg_read(struct gg_session *sess, char *buf, int length) |
292 { | 292 { |
293 int res; | 293 #ifdef GG_CONFIG_HAVE_GNUTLS |
294 if (sess->ssl != NULL) { | |
295 for (;;) { | |
296 int res; | |
297 | |
298 res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length); | |
299 | |
300 if (res < 0) { | |
301 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED) | |
302 continue; | |
303 | |
304 if (res == GNUTLS_E_AGAIN) | |
305 errno = EAGAIN; | |
306 else | |
307 errno = EINVAL; | |
308 | |
309 return -1; | |
310 } | |
311 | |
312 return res; | |
313 } | |
314 } | |
315 #endif | |
294 | 316 |
295 #ifdef GG_CONFIG_HAVE_OPENSSL | 317 #ifdef GG_CONFIG_HAVE_OPENSSL |
296 if (sess->ssl) { | 318 if (sess->ssl != NULL) { |
297 int err; | 319 for (;;) { |
298 | 320 int res, err; |
299 res = SSL_read(sess->ssl, buf, length); | 321 |
300 | 322 res = SSL_read(sess->ssl, buf, length); |
301 if (res < 0) { | 323 |
302 err = SSL_get_error(sess->ssl, res); | 324 if (res < 0) { |
303 | 325 err = SSL_get_error(sess->ssl, res); |
304 if (err == SSL_ERROR_WANT_READ) | 326 |
305 errno = EAGAIN; | 327 if (err == SSL_ERROR_SYSCALL && errno == EINTR) |
306 | 328 continue; |
307 return -1; | 329 |
308 } | 330 if (err == SSL_ERROR_WANT_READ) |
309 } else | 331 errno = EAGAIN; |
332 else if (err != SSL_ERROR_SYSCALL) | |
333 errno = EINVAL; | |
334 | |
335 return -1; | |
336 } | |
337 | |
338 return res; | |
339 } | |
340 } | |
310 #endif | 341 #endif |
311 res = read(sess->fd, buf, length); | 342 |
312 | 343 return read(sess->fd, buf, length); |
313 return res; | |
314 } | 344 } |
315 | 345 |
316 /** | 346 /** |
317 * \internal Wysyła do serwera dane binarne. | 347 * \internal Wysyła do serwera dane binarne. |
318 * | 348 * |
319 * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności. | 349 * Funkcja wysyła dane do serwera zajmując się SSL/TLS w razie konieczności. |
350 * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi | |
351 * wywołaniami systemowymi. | |
352 * | |
353 * \note Funkcja nie zajmuje się buforowaniem wysyłanych danych (patrz | |
354 * gg_write()). | |
320 * | 355 * |
321 * \param sess Struktura sesji | 356 * \param sess Struktura sesji |
322 * \param buf Bufor z danymi | 357 * \param buf Bufor z danymi |
323 * \param length Długość bufora | 358 * \param length Długość bufora |
324 * | 359 * |
325 * \return To samo co funkcja systemowa \c write | 360 * \return To samo co funkcja systemowa \c write |
326 */ | 361 */ |
362 static int gg_write_common(struct gg_session *sess, const char *buf, int length) | |
363 { | |
364 #ifdef GG_CONFIG_HAVE_GNUTLS | |
365 if (sess->ssl != NULL) { | |
366 for (;;) { | |
367 int res; | |
368 | |
369 res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length); | |
370 | |
371 if (res < 0) { | |
372 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED) | |
373 continue; | |
374 | |
375 if (res == GNUTLS_E_AGAIN) | |
376 errno = EAGAIN; | |
377 else | |
378 errno = EINVAL; | |
379 | |
380 return -1; | |
381 } | |
382 | |
383 return res; | |
384 } | |
385 } | |
386 #endif | |
387 | |
388 #ifdef GG_CONFIG_HAVE_OPENSSL | |
389 if (sess->ssl != NULL) { | |
390 for (;;) { | |
391 int res, err; | |
392 | |
393 res = SSL_write(sess->ssl, buf, length); | |
394 | |
395 if (res < 0) { | |
396 err = SSL_get_error(sess->ssl, res); | |
397 | |
398 if (err == SSL_ERROR_SYSCALL && errno == EINTR) | |
399 continue; | |
400 | |
401 if (err == SSL_ERROR_WANT_WRITE) | |
402 errno = EAGAIN; | |
403 else if (err != SSL_ERROR_SYSCALL) | |
404 errno = EINVAL; | |
405 | |
406 return -1; | |
407 } | |
408 | |
409 return res; | |
410 } | |
411 } | |
412 #endif | |
413 | |
414 return write(sess->fd, buf, length); | |
415 } | |
416 | |
417 | |
418 | |
419 /** | |
420 * \internal Wysyła do serwera dane binarne. | |
421 * | |
422 * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności. | |
423 * | |
424 * \param sess Struktura sesji | |
425 * \param buf Bufor z danymi | |
426 * \param length Długość bufora | |
427 * | |
428 * \return To samo co funkcja systemowa \c write | |
429 */ | |
327 int gg_write(struct gg_session *sess, const char *buf, int length) | 430 int gg_write(struct gg_session *sess, const char *buf, int length) |
328 { | 431 { |
329 int res = 0; | 432 int res = 0; |
330 | 433 |
331 #ifdef GG_CONFIG_HAVE_OPENSSL | 434 if (!sess->async) { |
332 if (sess->ssl) { | 435 int written = 0; |
333 int err; | 436 |
334 | 437 while (written < length) { |
335 res = SSL_write(sess->ssl, buf, length); | 438 res = gg_write_common(sess, buf + written, length - written); |
336 | 439 |
337 if (res < 0) { | 440 if (res == -1) |
338 err = SSL_get_error(sess->ssl, res); | 441 return -1; |
339 | 442 |
340 if (err == SSL_ERROR_WANT_WRITE) | 443 written += res; |
341 errno = EAGAIN; | 444 res = written; |
342 | 445 } |
343 return -1; | 446 } else { |
344 } | 447 res = 0; |
345 } else | 448 |
346 #endif | 449 if (sess->send_buf == NULL) { |
347 { | 450 res = gg_write_common(sess, buf, length); |
348 if (!sess->async) { | 451 |
349 int written = 0; | 452 if (res == -1) |
350 | 453 return -1; |
351 while (written < length) { | 454 } |
352 res = write(sess->fd, buf + written, length - written); | 455 |
353 | 456 if (res < length) { |
354 if (res == -1) { | 457 char *tmp; |
355 if (errno != EINTR) | 458 |
356 break; | 459 if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) { |
357 | 460 errno = ENOMEM; |
358 continue; | 461 return -1; |
359 } | |
360 | |
361 written += res; | |
362 res = written; | |
363 } | 462 } |
364 } else { | 463 |
365 if (!sess->send_buf) | 464 sess->send_buf = tmp; |
366 res = write(sess->fd, buf, length); | 465 |
367 else | 466 memcpy(sess->send_buf + sess->send_left, buf + res, length - res); |
368 res = 0; | 467 |
369 | 468 sess->send_left += length - res; |
370 if (res == -1) { | |
371 if (errno != EAGAIN) | |
372 return res; | |
373 | |
374 res = 0; | |
375 } | |
376 | |
377 if (res < length) { | |
378 char *tmp; | |
379 | |
380 if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) { | |
381 errno = ENOMEM; | |
382 return -1; | |
383 } | |
384 | |
385 sess->send_buf = tmp; | |
386 | |
387 memcpy(sess->send_buf + sess->send_left, buf + res, length - res); | |
388 | |
389 sess->send_left += length - res; | |
390 } | |
391 } | 469 } |
392 } | 470 } |
393 | 471 |
394 return res; | 472 return res; |
395 } | 473 } |
441 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); | 519 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); |
442 return NULL; | 520 return NULL; |
443 } | 521 } |
444 | 522 |
445 if (ret == -1) { | 523 if (ret == -1) { |
446 if (errno == EINTR) { | |
447 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); | |
448 continue; | |
449 } | |
450 | |
451 if (errno == EAGAIN) { | 524 if (errno == EAGAIN) { |
452 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); | 525 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); |
453 | 526 |
454 if (!(sess->header_buf = malloc(sess->header_done))) { | 527 if (!(sess->header_buf = malloc(sess->header_done))) { |
455 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); | 528 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); |
511 } | 584 } |
512 if (ret > -1 && ret <= size) { | 585 if (ret > -1 && ret <= size) { |
513 offset += ret; | 586 offset += ret; |
514 size -= ret; | 587 size -= ret; |
515 } else if (ret == -1) { | 588 } else if (ret == -1) { |
516 int errno2 = errno; | |
517 | |
518 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); | 589 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); |
519 errno = errno2; | |
520 | 590 |
521 if (errno == EAGAIN) { | 591 if (errno == EAGAIN) { |
522 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); | 592 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); |
523 sess->recv_buf = buf; | 593 sess->recv_buf = buf; |
524 sess->recv_left = size; | 594 sess->recv_left = size; |
525 sess->recv_done = offset; | 595 sess->recv_done = offset; |
526 return NULL; | 596 return NULL; |
527 } | 597 } |
528 if (errno != EINTR) { | 598 |
529 free(buf); | 599 free(buf); |
530 return NULL; | 600 return NULL; |
531 } | |
532 } | 601 } |
533 } | 602 } |
534 | 603 |
535 sess->recv_left = 0; | 604 sess->recv_left = 0; |
536 | 605 |
537 if ((gg_debug_level & GG_DEBUG_DUMP)) { | 606 gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(type=0x%.2x, length=%d)\n", h.type, h.length); |
538 unsigned int i; | 607 gg_debug_dump(sess, GG_DEBUG_DUMP, buf, sizeof(h) + h.length); |
539 | |
540 gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); | |
541 for (i = 0; i < sizeof(h) + h.length; i++) | |
542 gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); | |
543 gg_debug_session(sess, GG_DEBUG_DUMP, "\n"); | |
544 } | |
545 | 608 |
546 return buf; | 609 return buf; |
547 } | 610 } |
548 | 611 |
549 /** | 612 /** |
607 | 670 |
608 h = (struct gg_header*) tmp; | 671 h = (struct gg_header*) tmp; |
609 h->type = gg_fix32(type); | 672 h->type = gg_fix32(type); |
610 h->length = gg_fix32(tmp_length - sizeof(struct gg_header)); | 673 h->length = gg_fix32(tmp_length - sizeof(struct gg_header)); |
611 | 674 |
612 if ((gg_debug_level & GG_DEBUG_DUMP)) { | 675 gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(type=0x%.2x, length=%d)\n", gg_fix32(h->type), gg_fix32(h->length)); |
613 unsigned int i; | 676 gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length); |
614 | |
615 gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); | |
616 for (i = 0; i < tmp_length; ++i) | |
617 gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); | |
618 gg_debug_session(sess, GG_DEBUG_DUMP, "\n"); | |
619 } | |
620 | 677 |
621 res = gg_write(sess, tmp, tmp_length); | 678 res = gg_write(sess, tmp, tmp_length); |
622 | 679 |
623 free(tmp); | 680 free(tmp); |
624 | 681 |
737 sess->destroy = gg_free_session; | 794 sess->destroy = gg_free_session; |
738 sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT); | 795 sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT); |
739 sess->server_addr = p->server_addr; | 796 sess->server_addr = p->server_addr; |
740 sess->external_port = p->external_port; | 797 sess->external_port = p->external_port; |
741 sess->external_addr = p->external_addr; | 798 sess->external_addr = p->external_addr; |
742 | 799 sess->client_port = p->client_port; |
743 sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77)); | 800 |
744 | 801 if (p->protocol_features == 0) { |
745 if (!(p->protocol_features & GG_FEATURE_STATUS77)) | 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; |
746 sess->protocol_features |= GG_FEATURE_STATUS80; | 803 } else { |
747 | 804 sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77)); |
748 if (!(p->protocol_features & GG_FEATURE_MSG77)) | 805 |
749 sess->protocol_features |= GG_FEATURE_MSG80; | 806 if (!(p->protocol_features & GG_FEATURE_STATUS77)) |
807 sess->protocol_features |= GG_FEATURE_STATUS80; | |
808 | |
809 if (!(p->protocol_features & GG_FEATURE_MSG77)) | |
810 sess->protocol_features |= GG_FEATURE_MSG80; | |
811 } | |
812 | |
813 if (!(sess->status_flags = p->status_flags)) | |
814 sess->status_flags = GG_STATUS_FLAG_UNKNOWN | GG_STATUS_FLAG_SPAM; | |
750 | 815 |
751 sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; | 816 sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; |
752 | 817 |
753 if (p->era_omnix) | 818 if (p->era_omnix) |
754 sess->protocol_flags |= GG_ERA_OMNIX_MASK; | 819 sess->protocol_flags |= GG_ERA_OMNIX_MASK; |
772 if (sess->protocol_version >= 0x2d) | 837 if (sess->protocol_version >= 0x2d) |
773 max_length = GG_STATUS_DESCR_MAXSIZE; | 838 max_length = GG_STATUS_DESCR_MAXSIZE; |
774 else | 839 else |
775 max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0; | 840 max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0; |
776 | 841 |
777 if (sess->protocol_version >= 0x2d && p->encoding != GG_ENCODING_UTF8) | 842 if (sess->protocol_version >= 0x2d) |
778 sess->initial_descr = gg_cp_to_utf8(p->status_descr); | 843 sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1); |
779 else | 844 else |
780 sess->initial_descr = strdup(p->status_descr); | 845 sess->initial_descr = strdup(p->status_descr); |
781 | 846 |
782 if (!sess->initial_descr) { | 847 if (!sess->initial_descr) { |
783 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); | 848 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); |
789 if (strlen(sess->initial_descr) > max_length) | 854 if (strlen(sess->initial_descr) > max_length) |
790 sess->initial_descr[max_length] = 0; | 855 sess->initial_descr[max_length] = 0; |
791 } | 856 } |
792 | 857 |
793 if (p->tls == 1) { | 858 if (p->tls == 1) { |
794 #ifdef GG_CONFIG_HAVE_OPENSSL | 859 #ifdef GG_CONFIG_HAVE_GNUTLS |
860 gg_session_gnutls_t *tmp; | |
861 | |
862 tmp = malloc(sizeof(gg_session_gnutls_t)); | |
863 | |
864 if (tmp == NULL) { | |
865 gg_debug(GG_DEBUG_MISC, "// gg_login() out of memory for GnuTLS session\n"); | |
866 goto fail; | |
867 } | |
868 | |
869 sess->ssl = tmp; | |
870 | |
871 gnutls_global_init(); | |
872 gnutls_certificate_allocate_credentials(&tmp->xcred); | |
873 gnutls_init(&tmp->session, GNUTLS_CLIENT); | |
874 gnutls_priority_set_direct(tmp->session, "NORMAL:-VERS-TLS", NULL); | |
875 // gnutls_priority_set_direct(tmp->session, "NONE:+VERS-SSL3.0:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL", NULL); | |
876 gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred); | |
877 #elif defined(GG_CONFIG_HAVE_OPENSSL) | |
795 char buf[1024]; | 878 char buf[1024]; |
796 | 879 |
797 OpenSSL_add_ssl_algorithms(); | 880 OpenSSL_add_ssl_algorithms(); |
798 | 881 |
799 if (!RAND_status()) { | 882 if (!RAND_status()) { |
808 | 891 |
809 RAND_seed((void *) rdata, sizeof(rdata)); | 892 RAND_seed((void *) rdata, sizeof(rdata)); |
810 RAND_seed((void *) &rstruct, sizeof(rstruct)); | 893 RAND_seed((void *) &rstruct, sizeof(rstruct)); |
811 } | 894 } |
812 | 895 |
813 sess->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); | 896 sess->ssl_ctx = SSL_CTX_new(SSLv3_client_method()); |
814 | 897 |
815 if (!sess->ssl_ctx) { | 898 if (!sess->ssl_ctx) { |
816 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); | 899 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); |
817 gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf); | 900 gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf); |
818 goto fail; | 901 goto fail; |
848 if (!p->async) { | 931 if (!p->async) { |
849 struct in_addr addr; | 932 struct in_addr addr; |
850 | 933 |
851 if (!sess->server_addr) { | 934 if (!sess->server_addr) { |
852 if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { | 935 if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { |
853 if (gg_gethostbyname_real(hostname, &addr, 0) == -1) { | 936 struct in_addr *addr_list = NULL; |
937 int addr_count; | |
938 | |
939 if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 0) == -1 || addr_count == 0) { | |
854 gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); | 940 gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); |
941 free(addr_list); | |
855 goto fail; | 942 goto fail; |
856 } | 943 } |
944 | |
945 addr = addr_list[0]; | |
946 | |
947 free(addr_list); | |
857 } | 948 } |
858 } else { | 949 } else { |
859 addr.s_addr = sess->server_addr; | 950 addr.s_addr = sess->server_addr; |
860 port = sess->port; | 951 port = sess->port; |
861 } | 952 } |
974 * wcześniej wywołać funkcję \c gg_change_status_descr() lub | 1065 * wcześniej wywołać funkcję \c gg_change_status_descr() lub |
975 * \c gg_change_status_descr_time(). | 1066 * \c gg_change_status_descr_time(). |
976 * | 1067 * |
977 * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze | 1068 * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze |
978 * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one | 1069 * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one |
979 * utracone przy zrywaniu połączenia. | 1070 * utracone przy zrywaniu połączenia. Aby mieć pewność, że opis statusu |
1071 * zostanie zachowany, należy ustawić stan \c GG_STATUS_NOT_AVAIL_DESCR | |
1072 * za pomocą funkcji \c gg_change_status_descr() i poczekać na zdarzenie | |
1073 * \c GG_EVENT_DISCONNECT_ACK. | |
980 * | 1074 * |
981 * \param sess Struktura sesji | 1075 * \param sess Struktura sesji |
982 * | 1076 * |
983 * \ingroup login | 1077 * \ingroup login |
984 */ | 1078 */ |
987 if (!sess) | 1081 if (!sess) |
988 return; | 1082 return; |
989 | 1083 |
990 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); | 1084 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); |
991 | 1085 |
992 if (GG_S_NA(sess->status)) | 1086 #ifdef GG_CONFIG_HAVE_GNUTLS |
993 gg_change_status(sess, GG_STATUS_NOT_AVAIL); | 1087 if (sess->ssl != NULL) |
1088 gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR); | |
1089 #endif | |
994 | 1090 |
995 #ifdef GG_CONFIG_HAVE_OPENSSL | 1091 #ifdef GG_CONFIG_HAVE_OPENSSL |
996 if (sess->ssl) | 1092 if (sess->ssl != NULL) |
997 SSL_shutdown(sess->ssl); | 1093 SSL_shutdown(sess->ssl); |
998 #endif | 1094 #endif |
999 | 1095 |
1000 sess->resolver_cleanup(&sess->resolver, 1); | 1096 sess->resolver_cleanup(&sess->resolver, 1); |
1001 | 1097 |
1002 if (sess->fd != -1) { | 1098 if (sess->fd != -1) { |
1003 shutdown(sess->fd, SHUT_RDWR); | 1099 shutdown(sess->fd, SHUT_RDWR); |
1004 close(sess->fd); | 1100 close(sess->fd); |
1005 sess->fd = -1; | 1101 sess->fd = -1; |
1006 } | 1102 } |
1103 | |
1104 #ifdef GG_CONFIG_HAVE_GNUTLS | |
1105 if (sess->ssl != NULL) { | |
1106 gg_session_gnutls_t *tmp; | |
1107 | |
1108 tmp = (gg_session_gnutls_t*) sess->ssl; | |
1109 gnutls_deinit(tmp->session); | |
1110 gnutls_certificate_free_credentials(tmp->xcred); | |
1111 gnutls_global_deinit(); | |
1112 free(sess->ssl); | |
1113 } | |
1114 #endif | |
1007 | 1115 |
1008 if (sess->send_buf) { | 1116 if (sess->send_buf) { |
1009 free(sess->send_buf); | 1117 free(sess->send_buf); |
1010 sess->send_buf = NULL; | 1118 sess->send_buf = NULL; |
1011 sess->send_left = 0; | 1119 sess->send_left = 0; |
1101 | 1209 |
1102 sess->status = status; | 1210 sess->status = status; |
1103 | 1211 |
1104 if (sess->protocol_version >= 0x2d) { | 1212 if (sess->protocol_version >= 0x2d) { |
1105 if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) { | 1213 if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) { |
1106 new_descr = gg_cp_to_utf8(descr); | 1214 new_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1); |
1107 | 1215 |
1108 if (!new_descr) | 1216 if (!new_descr) |
1109 return -1; | 1217 return -1; |
1110 } | 1218 } |
1111 | 1219 |
1138 | 1246 |
1139 if (packet_type == GG_NEW_STATUS80) { | 1247 if (packet_type == GG_NEW_STATUS80) { |
1140 struct gg_new_status80 p; | 1248 struct gg_new_status80 p; |
1141 | 1249 |
1142 p.status = gg_fix32(status); | 1250 p.status = gg_fix32(status); |
1143 p.flags = gg_fix32(0x00800001); | 1251 p.flags = gg_fix32(sess->status_flags); |
1144 p.description_size = gg_fix32(descr_len); | 1252 p.description_size = gg_fix32(descr_len); |
1145 res = gg_send_packet(sess, | 1253 res = gg_send_packet(sess, |
1146 packet_type, | 1254 packet_type, |
1147 &p, | 1255 &p, |
1148 sizeof(p), | 1256 sizeof(p), |
1166 (time) ? sizeof(new_time) : 0, | 1274 (time) ? sizeof(new_time) : 0, |
1167 NULL); | 1275 NULL); |
1168 } | 1276 } |
1169 | 1277 |
1170 free(new_descr); | 1278 free(new_descr); |
1279 | |
1280 if (GG_S_NA(status)) | |
1281 sess->state = GG_STATE_DISCONNECTING; | |
1282 | |
1171 return res; | 1283 return res; |
1172 } | 1284 } |
1173 | 1285 |
1174 #endif /* DOXYGEN */ | 1286 #endif /* DOXYGEN */ |
1175 | 1287 |
1223 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) | 1335 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) |
1224 { | 1336 { |
1225 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); | 1337 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); |
1226 | 1338 |
1227 return gg_change_status_common(sess, status, descr, time); | 1339 return gg_change_status_common(sess, status, descr, time); |
1340 } | |
1341 | |
1342 /** | |
1343 * Funkcja zmieniająca flagi statusu. | |
1344 * | |
1345 * \param sess Struktura sesji | |
1346 * \param flags Nowe flagi statusu | |
1347 * | |
1348 * \return 0 jeśli się powiodło, -1 w przypadku błędu | |
1349 * | |
1350 * \note Aby zmiany weszły w życie, należy ponownie ustawić status za pomocą | |
1351 * funkcji z rodziny \c gg_change_status(). | |
1352 * | |
1353 * \ingroup status | |
1354 */ | |
1355 int gg_change_status_flags(struct gg_session *sess, int flags) | |
1356 { | |
1357 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_flags(%p, 0x%08x);\n", sess, flags); | |
1358 | |
1359 if (sess == NULL) { | |
1360 errno = EFAULT; | |
1361 return -1; | |
1362 } | |
1363 | |
1364 sess->status_flags = flags; | |
1365 | |
1366 return 0; | |
1228 } | 1367 } |
1229 | 1368 |
1230 /** | 1369 /** |
1231 * Wysyła wiadomość do użytkownika. | 1370 * Wysyła wiadomość do użytkownika. |
1232 * | 1371 * |
1375 if (src[i] == 0) | 1514 if (src[i] == 0) |
1376 attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR); | 1515 attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR); |
1377 | 1516 |
1378 format_idx += 3; | 1517 format_idx += 3; |
1379 | 1518 |
1380 if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) { | 1519 if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 0)) { |
1381 if (char_pos != 0) { | 1520 if (char_pos != 0) { |
1382 if ((old_attr & GG_FONT_UNDERLINE) != 0) | 1521 if ((old_attr & GG_FONT_UNDERLINE) != 0) |
1383 gg_append(dst, &len, "</u>", 4); | 1522 gg_append(dst, &len, "</u>", 4); |
1384 | 1523 |
1385 if ((old_attr & GG_FONT_ITALIC) != 0) | 1524 if ((old_attr & GG_FONT_ITALIC) != 0) |
1386 gg_append(dst, &len, "</i>", 4); | 1525 gg_append(dst, &len, "</i>", 4); |
1387 | 1526 |
1388 if ((old_attr & GG_FONT_BOLD) != 0) | 1527 if ((old_attr & GG_FONT_BOLD) != 0) |
1389 gg_append(dst, &len, "</b>", 4); | 1528 gg_append(dst, &len, "</b>", 4); |
1390 | 1529 |
1391 gg_append(dst, &len, "</span>", 7); | 1530 if (src[i] != 0) |
1531 gg_append(dst, &len, "</span>", 7); | |
1392 } | 1532 } |
1393 | 1533 |
1394 if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { | 1534 if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { |
1395 color = &format[format_idx]; | 1535 color = &format[format_idx]; |
1396 format_idx += 3; | 1536 format_idx += 3; |
1397 } else { | 1537 } else { |
1398 color = (const unsigned char*) "\x00\x00\x00"; | 1538 color = (const unsigned char*) "\x00\x00\x00"; |
1399 } | 1539 } |
1400 | 1540 |
1401 if (dst != NULL) | 1541 if (src[i] != 0) { |
1402 sprintf(&dst[len], span_fmt, color[0], color[1], color[2]); | 1542 if (dst != NULL) |
1403 len += span_len; | 1543 sprintf(&dst[len], span_fmt, color[0], color[1], color[2]); |
1544 len += span_len; | |
1545 } | |
1404 } else if (char_pos == 0 && src[0] != 0) { | 1546 } else if (char_pos == 0 && src[0] != 0) { |
1405 if (dst != NULL) | 1547 if (dst != NULL) |
1406 sprintf(&dst[len], span_fmt, 0, 0, 0); | 1548 sprintf(&dst[len], span_fmt, 0, 0, 0); |
1407 len += span_len; | 1549 len += span_len; |
1408 } | 1550 } |
1541 errno = EINVAL; | 1683 errno = EINVAL; |
1542 return -1; | 1684 return -1; |
1543 } | 1685 } |
1544 | 1686 |
1545 if (sess->encoding == GG_ENCODING_UTF8) { | 1687 if (sess->encoding == GG_ENCODING_UTF8) { |
1546 if (!(cp_msg = gg_utf8_to_cp((const char *) message))) | 1688 if (!(cp_msg = gg_encoding_convert((const char *) message, GG_ENCODING_UTF8, GG_ENCODING_CP1250, -1, -1))) |
1547 return -1; | 1689 return -1; |
1548 | 1690 |
1549 utf_msg = (char*) message; | 1691 utf_msg = (char*) message; |
1550 } else { | 1692 } else { |
1551 if (sess->protocol_version >= 0x2d) { | 1693 if (sess->protocol_version >= 0x2d) { |
1552 if (!(utf_msg = gg_cp_to_utf8((const char *) message))) | 1694 if (!(utf_msg = gg_encoding_convert((const char *) message, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1))) |
1553 return -1; | 1695 return -1; |
1554 } | 1696 } |
1555 | 1697 |
1556 cp_msg = (char*) message; | 1698 cp_msg = (char*) message; |
1557 } | 1699 } |
2190 sess->userlist_blocks++; | 2332 sess->userlist_blocks++; |
2191 | 2333 |
2192 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); | 2334 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); |
2193 } | 2335 } |
2194 | 2336 |
2337 /** | |
2338 * Informuje rozmówcę o pisaniu wiadomości. | |
2339 * | |
2340 * \param sess Struktura sesji | |
2341 * \param recipient Numer adresata | |
2342 * \param length Długość wiadomości lub 0 jeśli jest pusta | |
2343 * | |
2344 * \return 0 jeśli się powiodło, -1 w przypadku błędu | |
2345 * | |
2346 * \ingroup messages | |
2347 */ | |
2348 int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length){ | |
2349 struct gg_typing_notification pkt; | |
2350 uin_t uin; | |
2351 | |
2352 pkt.length = gg_fix16(length); | |
2353 uin = gg_fix32(recipient); | |
2354 memcpy(&pkt.uin, &uin, sizeof(uin_t)); | |
2355 | |
2356 return gg_send_packet(sess, GG_TYPING_NOTIFICATION, &pkt, sizeof(pkt), NULL); | |
2357 } | |
2358 | |
2359 /** | |
2360 * Rozłącza inną sesję multilogowania. | |
2361 * | |
2362 * \param gs Struktura sesji | |
2363 * \param conn_id Sesja do rozłączenia | |
2364 * | |
2365 * \return 0 jeśli się powiodło, -1 w przypadku błędu | |
2366 * | |
2367 * \ingroup login | |
2368 */ | |
2369 int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id) | |
2370 { | |
2371 struct gg_multilogon_disconnect pkt; | |
2372 | |
2373 pkt.conn_id = conn_id; | |
2374 | |
2375 return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL); | |
2376 } | |
2377 | |
2195 /* @} */ | 2378 /* @} */ |
2196 | 2379 |
2197 /* | 2380 /* |
2198 * Local variables: | 2381 * Local variables: |
2199 * c-indentation-style: k&r | 2382 * c-indentation-style: k&r |