Remove useless comparison
[pidgin-git.git] / libpurple / protocols / gg / lib / libgadu.c
blobbee283310f381d5b0c4c8809f8c14a71026ea632
1 /* $Id$ */
3 /*
4 * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
5 * Robert J. Woźny <speedy@ziew.org>
6 * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
7 * Tomasz Chiliński <chilek@chilan.com>
8 * Adam Wysocki <gophi@ekg.chmurka.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License Version
12 * 2.1 as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
22 * USA.
25 /**
26 * \file libgadu.c
28 * \brief Główny moduł biblioteki
31 #include "strman.h"
32 #include "network.h"
33 #include "fileio.h"
35 #include "libgadu.h"
36 #include "protocol.h"
37 #include "resolver.h"
38 #include "internal.h"
39 #include "encoding.h"
40 #include "debug.h"
41 #include "session.h"
42 #include "message.h"
43 #include "deflate.h"
44 #include "tvbuilder.h"
45 #include "protobuf.h"
46 #include "packets.pb-c.h"
48 #include <errno.h>
49 #include <stdarg.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <time.h>
53 #ifdef GG_CONFIG_HAVE_GNUTLS
54 # include <gnutls/gnutls.h>
55 #endif
56 #ifdef GG_CONFIG_HAVE_OPENSSL
57 # include <openssl/err.h>
58 # include <openssl/rand.h>
59 #endif
61 /**
62 * Port gniazda nasłuchującego dla połączeń bezpośrednich.
64 * \ingroup ip
66 int gg_dcc_port = 0;
68 /**
69 * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich.
71 * \ingroup ip
73 unsigned long gg_dcc_ip = 0;
75 /**
76 * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia.
78 * \ingroup ip
80 unsigned long gg_local_ip = 0;
82 /**
83 * Flaga włączenia połączeń przez serwer pośredniczący.
85 * \ingroup proxy
87 int gg_proxy_enabled = 0;
89 /**
90 * Adres serwera pośredniczącego.
92 * \ingroup proxy
94 char *gg_proxy_host = NULL;
96 /**
97 * Port serwera pośredniczącego.
99 * \ingroup proxy
101 int gg_proxy_port = 0;
104 * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
106 * \ingroup proxy
108 int gg_proxy_http_only = 0;
111 * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
113 * \ingroup proxy
115 char *gg_proxy_username = NULL;
118 * Hasło użytkownika do autoryzacji serwera pośredniczącego.
120 * \ingroup proxy
122 char *gg_proxy_password = NULL;
124 #ifndef DOXYGEN
126 #ifndef lint
127 static char rcsid[] GG_UNUSED = "$Id$";
128 #endif
130 #endif /* DOXYGEN */
132 static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients);
133 static void gg_compat_message_cleanup(struct gg_session *sess);
135 #ifdef GG_CONFIG_IS_GPL_COMPLIANT
137 * Symbol zdefiniowany tylko dla libgadu zgodnego z licencją GPL.
139 * Zwracana wartość nie jest istotna, a ponadto może się zmienić w przyszłych
140 * wersjach biblioteki. Istotne jest tylko wywołanie tej funkcji w kodzie, który
141 * ma być zgodny z GPL, aby wymusić jej istnienie.
143 * \return Wartość 1.
145 * \ingroup version
147 int gg_is_gpl_compliant(void)
149 return 1;
151 #endif
154 * Zwraca wersję biblioteki.
156 * \return Wskaźnik na statyczny bufor z wersją biblioteki.
158 * \ingroup version
160 const char *gg_libgadu_version(void)
162 return GG_LIBGADU_VERSION;
165 void * gg_new0(size_t size)
167 void *ptr;
169 ptr = malloc(size);
170 if (ptr == NULL) {
171 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR,
172 "//gg_new0(%" GG_SIZE_FMT
173 ") not enough memory\n", size);
174 return NULL;
177 memset(ptr, 0, size);
178 return ptr;
182 gg_required_proto(struct gg_session *gs, int protocol_version)
184 if (gs->protocol_version >= protocol_version)
185 return 1;
187 gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_ERROR, "// requested "
188 "feature requires protocol %#02x, but %#02x is selected\n",
189 protocol_version, gs->protocol_version);
190 return 0;
193 int gg_get_dummy_fd(struct gg_session *sess)
195 struct gg_session_private *p = sess->private_data;
197 if (p->dummyfds_created)
198 return p->dummyfds[0];
200 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, p->dummyfds) == -1) {
201 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_get_dummy_fd() "
202 "unable to create pipes (errno=%d, %s)\n",
203 errno, strerror(errno));
204 return -1;
207 p->dummyfds_created = 1;
208 return p->dummyfds[0];
212 * \internal Liczy skrót z hasła i ziarna.
214 * \param password Hasło
215 * \param seed Ziarno podane przez serwer
217 * \return Wartość skrótu
219 unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
221 unsigned int x, y, z;
223 y = seed;
225 for (x = 0; *password; password++) {
226 x = (x & 0xffffff00) | *password;
227 y ^= x;
228 y += x;
229 x <<= 8;
230 y ^= x;
231 x <<= 8;
232 y -= x;
233 x <<= 8;
234 y ^= x;
236 z = y & 0x1F;
237 y = (y << z) | (y >> (32 - z));
240 return y;
244 * \internal Odbiera od serwera dane binarne.
246 * Funkcja odbiera dane od serwera zajmując się SSL/TLS w razie konieczności.
247 * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
248 * wywołaniami systemowymi.
250 * \param sess Struktura sesji
251 * \param buf Bufor na danymi
252 * \param length Długość bufora
254 * \return To samo co funkcja systemowa \c read
256 int gg_read(struct gg_session *sess, char *buf, int length)
258 struct gg_session_private *p = sess->private_data;
259 int res;
261 #ifdef GG_CONFIG_HAVE_GNUTLS
262 if (sess->ssl != NULL) {
263 for (;;) {
264 res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length);
266 if (res < 0) {
267 if (res == GNUTLS_E_AGAIN)
268 errno = EAGAIN;
269 else if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
270 continue;
271 else
272 errno = EINVAL;
274 return -1;
277 return res;
280 #endif
282 #ifdef GG_CONFIG_HAVE_OPENSSL
283 if (sess->ssl != NULL) {
284 for (;;) {
285 int err;
287 res = SSL_read(sess->ssl, buf, length);
289 if (res < 0) {
290 err = SSL_get_error(sess->ssl, res);
292 if (err == SSL_ERROR_SYSCALL && errno == EINTR)
293 continue;
295 if (err == SSL_ERROR_WANT_READ)
296 errno = EAGAIN;
297 else if (err != SSL_ERROR_SYSCALL)
298 errno = EINVAL;
300 return -1;
303 return res;
306 #endif
308 if (p->socket_handle != NULL) {
309 if (p->socket_manager.read_cb == NULL) {
310 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
311 "// gg_read() socket_manager.read callback is "
312 "empty\n");
313 errno = EINVAL;
314 return -1;
317 do {
318 res = p->socket_manager.read_cb(
319 p->socket_manager.cb_data, p->socket_handle,
320 (unsigned char*)buf, length);
321 } while (res < 0 && errno == EINTR);
323 if (res < 0) {
324 if (errno == EAGAIN)
325 return -1;
326 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
327 "// gg_read() unexpected errno=%d\n", errno);
328 errno = EINVAL;
330 return res;
333 for (;;) {
334 res = recv(sess->fd, buf, length, 0);
336 if (res == -1 && errno == EINTR)
337 continue;
339 return res;
344 * \internal Wysyła do serwera dane binarne.
346 * Funkcja wysyła dane do serwera zajmując się SSL/TLS w razie konieczności.
347 * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
348 * wywołaniami systemowymi.
350 * \note Funkcja nie zajmuje się buforowaniem wysyłanych danych (patrz
351 * gg_write()).
353 * \param sess Struktura sesji
354 * \param buf Bufor z danymi
355 * \param length Długość bufora
357 * \return To samo co funkcja systemowa \c write
359 static int gg_write_common(struct gg_session *sess, const char *buf, int length)
361 struct gg_session_private *p = sess->private_data;
362 int res;
364 #ifdef GG_CONFIG_HAVE_GNUTLS
365 if (sess->ssl != NULL) {
366 for (;;) {
367 res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length);
369 if (res < 0) {
370 if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
371 continue;
373 if (res == GNUTLS_E_AGAIN)
374 errno = EAGAIN;
375 else
376 errno = EINVAL;
378 return -1;
381 return res;
384 #endif
386 #ifdef GG_CONFIG_HAVE_OPENSSL
387 if (sess->ssl != NULL) {
388 for (;;) {
389 int err;
391 res = SSL_write(sess->ssl, buf, length);
393 if (res < 0) {
394 err = SSL_get_error(sess->ssl, res);
396 if (err == SSL_ERROR_SYSCALL && errno == EINTR)
397 continue;
399 if (err == SSL_ERROR_WANT_WRITE)
400 errno = EAGAIN;
401 else if (err != SSL_ERROR_SYSCALL)
402 errno = EINVAL;
404 return -1;
407 return res;
410 #endif
412 if (p->socket_handle != NULL) {
413 if (p->socket_manager.write_cb == NULL) {
414 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
415 "// gg_write_common() socket_manager.write "
416 "callback is empty\n");
417 errno = EINVAL;
418 return -1;
421 do {
422 res = p->socket_manager.write_cb(
423 p->socket_manager.cb_data, p->socket_handle,
424 (const unsigned char*)buf, length);
425 } while (res < 0 && errno == EINTR);
427 if (res < 0) {
428 if (errno == EAGAIN)
429 return -1;
430 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
431 "// gg_read() unexpected errno=%d\n", errno);
432 errno = EINVAL;
435 return res;
438 for (;;) {
439 res = send(sess->fd, buf, length, 0);
441 if (res == -1 && errno == EINTR)
442 continue;
444 return res;
449 * \internal Wysyła do serwera dane binarne.
451 * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności.
453 * \param sess Struktura sesji
454 * \param buf Bufor z danymi
455 * \param length Długość bufora
457 * \return To samo co funkcja systemowa \c write
459 int gg_write(struct gg_session *sess, const char *buf, int length)
461 int res = 0;
463 if (!sess->async) {
464 int written = 0;
466 while (written < length) {
467 res = gg_write_common(sess, buf + written, length - written);
469 if (res == -1)
470 return -1;
472 written += res;
473 res = written;
475 } else {
476 if (sess->send_buf == NULL) {
477 res = gg_write_common(sess, buf, length);
479 if (res == -1 && errno == EAGAIN)
480 res = 0;
481 if (res == -1)
482 return -1;
485 if (res < length) {
486 char *tmp;
488 if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) {
489 errno = ENOMEM;
490 return -1;
493 sess->send_buf = tmp;
495 memcpy(sess->send_buf + sess->send_left, buf + res, length - res);
497 sess->send_left += length - res;
501 return res;
504 void gg_close(struct gg_session *sess)
506 struct gg_session_private *p = sess->private_data;
507 int errno_copy;
509 errno_copy = errno;
511 if (!p->socket_is_external) {
512 if (sess->fd != -1)
513 close(sess->fd);
514 } else {
515 assert(p->socket_manager_type !=
516 GG_SOCKET_MANAGER_TYPE_INTERNAL);
517 if (p->socket_handle != NULL) {
518 p->socket_manager.close_cb(p->socket_manager.cb_data,
519 p->socket_handle);
521 p->socket_is_external = 0;
523 sess->fd = -1;
524 p->socket_handle = NULL;
526 while (p->event_queue) {
527 gg_eventqueue_t *next = p->event_queue->next;
528 gg_event_free(p->event_queue->event);
529 free(p->event_queue);
530 p->event_queue = next;
533 while (p->imgout_queue) {
534 gg_imgout_queue_t *next = p->imgout_queue->next;
535 free(p->imgout_queue);
536 p->imgout_queue = next;
539 if (p->dummyfds_created) {
540 close(p->dummyfds[0]);
541 close(p->dummyfds[1]);
542 p->dummyfds_created = 0;
545 gg_compat_message_cleanup(sess);
547 errno = errno_copy;
551 * \internal Odbiera pakiet od serwera.
553 * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca
554 * w zaalokowanym buforze.
556 * Przy połączeniach asynchronicznych, funkcja może nie być w stanie
557 * skompletować całego pakietu -- w takim przypadku zwróci \c NULL, a kodem błędu
558 * będzie \c EAGAIN.
560 * \param sess Struktura sesji
562 * \return Wskaźnik do zaalokowanego bufora
564 void *gg_recv_packet(struct gg_session *sess)
566 struct gg_header *gh;
567 char *packet;
568 int res;
569 size_t len;
570 uint32_t ghlen = 0;
572 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
574 if (sess == NULL) {
575 errno = EFAULT;
576 return NULL;
579 for (;;) {
580 if (sess->recv_buf == NULL && sess->recv_done == 0) {
581 sess->recv_buf = malloc(sizeof(struct gg_header) + 1);
583 if (sess->recv_buf == NULL) {
584 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
585 return NULL;
589 gh = (struct gg_header*) sess->recv_buf;
591 if ((size_t) sess->recv_done < sizeof(struct gg_header)) {
592 len = sizeof(struct gg_header) - sess->recv_done;
593 gg_debug_session(sess, GG_DEBUG_NET,
594 "// gg_recv_packet() header: %d done, "
595 "%" GG_SIZE_FMT " to go\n",
596 sess->recv_done, len);
597 } else {
598 ghlen = gh ? gg_fix32(gh->length) : 0;
600 if (ghlen > 65535) {
601 gg_debug_session(sess, GG_DEBUG_ERROR,
602 "// gg_recv_packet() invalid packet "
603 "length (%d)\n", ghlen);
604 errno = ERANGE;
605 goto fail;
608 if ((size_t) sess->recv_done >= sizeof(struct gg_header) + ghlen) {
609 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() and that's it\n");
610 break;
613 len = sizeof(struct gg_header) + ghlen - sess->recv_done;
615 gg_debug_session(sess, GG_DEBUG_NET,
616 "// gg_recv_packet() payload: %d done, "
617 "%u length, %" GG_SIZE_FMT " to go\n",
618 sess->recv_done, ghlen, len);
621 res = gg_read(sess, sess->recv_buf + sess->recv_done, len);
623 if (res == 0) {
624 errno = ECONNRESET;
625 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() connection broken\n");
626 goto fail;
629 if (res == -1 && errno == EAGAIN) {
630 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() resource temporarily unavailable\n");
631 goto eagain;
634 if (res == -1) {
635 gg_debug_session(sess, GG_DEBUG_ERROR,
636 "// gg_recv_packet() read failed: errno=%d, "
637 "%s\n", errno, strerror(errno));
638 goto fail;
641 gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() read %d bytes\n", res);
643 if (sess->recv_done + res == sizeof(struct gg_header)) {
644 char *tmp;
645 ghlen = gh ? gg_fix32(gh->length) : 0;
647 gg_debug_session(sess, GG_DEBUG_NET,
648 "// gg_recv_packet() header complete, "
649 "payload %d bytes\n", ghlen);
651 if (ghlen == 0)
652 break;
654 if (ghlen > 65535) {
655 gg_debug_session(sess, GG_DEBUG_ERROR,
656 "// gg_recv_packet() invalid packet "
657 "length (%d)\n", ghlen);
658 errno = ERANGE;
659 goto fail;
662 tmp = realloc(sess->recv_buf, sizeof(struct gg_header) + ghlen + 1);
664 if (tmp == NULL) {
665 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
666 goto fail;
669 sess->recv_buf = tmp;
672 sess->recv_done += res;
675 packet = sess->recv_buf;
676 sess->recv_buf = NULL;
677 sess->recv_done = 0;
679 if (gh == NULL)
680 goto fail;
682 /* Czasami zakładamy, że teksty w pakietach są zakończone zerem */
683 packet[sizeof(struct gg_header) + ghlen] = 0;
685 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet(type=0x%.2x, "
686 "length=%d)\n", gg_fix32(gh->type), ghlen);
687 gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(struct gg_header) + ghlen);
689 gh->type = gg_fix32(gh->type);
690 gh->length = ghlen;
692 return packet;
694 fail:
695 free(sess->recv_buf);
696 sess->recv_buf = NULL;
697 sess->recv_done = 0;
699 eagain:
700 return NULL;
704 * \internal Wysyła pakiet do serwera.
706 * Funkcja konstruuje pakiet do wysłania z dowolnej liczby fragmentów. Jeśli
707 * rozmiar pakietu jest za duży, by móc go wysłać za jednym razem, pozostała
708 * część zostanie zakolejkowana i wysłana, gdy będzie to możliwe.
710 * \param sess Struktura sesji
711 * \param type Rodzaj pakietu
712 * \param ... Lista kolejnych części pakietu (wskaźnik na bufor i długość
713 * typu \c int) zakończona \c NULL
715 * \return 0 jeśli się powiodło, -1 w przypadku błędu
717 int gg_send_packet(struct gg_session *sess, int type, ...)
719 struct gg_header *h;
720 char *tmp;
721 unsigned int tmp_length;
722 void *payload;
723 unsigned int payload_length;
724 va_list ap;
725 int res;
727 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type);
729 tmp_length = sizeof(struct gg_header);
731 if (!(tmp = malloc(tmp_length))) {
732 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for packet header\n");
733 return -1;
736 va_start(ap, type);
738 payload = va_arg(ap, void *);
740 while (payload) {
741 char *tmp2;
743 payload_length = va_arg(ap, unsigned int);
745 if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
746 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for payload\n");
747 free(tmp);
748 va_end(ap);
749 return -1;
752 tmp = tmp2;
754 memcpy(tmp + tmp_length, payload, payload_length);
755 tmp_length += payload_length;
757 payload = va_arg(ap, void *);
760 va_end(ap);
762 h = (struct gg_header*) tmp;
763 h->type = gg_fix32(type);
764 h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
766 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet(type=0x%.2x, "
767 "length=%d)\n", gg_fix32(h->type), gg_fix32(h->length));
768 gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length);
770 res = gg_write(sess, tmp, tmp_length);
772 free(tmp);
774 if (res == -1) {
775 gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() "
776 "write() failed. res = %d, errno = %d (%s)\n",
777 res, errno, strerror(errno));
778 return -1;
781 if (sess->async) {
782 gg_debug_session(sess, GG_DEBUG_NET, "// gg_send_packet() "
783 "partial write(), %d sent, %d left, %d total left\n",
784 res, tmp_length - res, sess->send_left);
787 if (sess->send_buf)
788 sess->check |= GG_CHECK_WRITE;
790 return 0;
794 * \internal Funkcja zwrotna sesji.
796 * Pole \c callback struktury \c gg_session zawiera wskaźnik do tej funkcji.
797 * Wywołuje ona \c gg_watch_fd i zachowuje wynik w polu \c event.
799 * \note Korzystanie z tej funkcjonalności nie jest już zalecane.
801 * \param sess Struktura sesji
803 * \return 0 jeśli się powiodło, -1 w przypadku błędu
805 static int gg_session_callback(struct gg_session *sess)
807 if (!sess) {
808 errno = EFAULT;
809 return -1;
812 return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
816 * Łączy się z serwerem Gadu-Gadu.
818 * Przy połączeniu synchronicznym funkcja zakończy działanie po nawiązaniu
819 * połączenia lub gdy wystąpi błąd. Po udanym połączeniu należy wywoływać
820 * funkcję \c gg_watch_fd(), która odbiera informacje od serwera i zwraca
821 * informacje o zdarzeniach.
823 * Przy połączeniu asynchronicznym funkcja rozpocznie procedurę połączenia
824 * i zwróci zaalokowaną strukturę. Pole \c fd struktury \c gg_session zawiera
825 * deskryptor, który należy obserwować funkcją \c select, \c poll lub za
826 * pomocą mechanizmów użytej pętli zdarzeń (Glib, Qt itp.). Pole \c check
827 * jest maską bitową mówiącą, czy biblioteka chce być informowana o możliwości
828 * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE).
829 * Po zaobserwowaniu zmian na deskryptorze należy wywołać funkcję
830 * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie
831 * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę
832 * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD.
834 * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów
835 * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex().
837 * \note Funkcja zwróci błąd ENOSYS jeśli połączenie SSL było wymagane, ale
838 * obsługa SSL nie jest wkompilowana.
840 * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin,
841 * password, async.
843 * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL
844 * w przypadku błędu.
846 * \ingroup login
848 struct gg_session *gg_login(const struct gg_login_params *p)
850 struct gg_session *sess = NULL;
851 struct gg_session_private *sess_private = NULL;
853 if (p == NULL) {
854 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
855 errno = EFAULT;
856 return NULL;
859 gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);
861 sess = malloc(sizeof(struct gg_session));
863 if (sess == NULL) {
864 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
865 goto fail;
868 memset(sess, 0, sizeof(struct gg_session));
869 sess->fd = -1;
871 sess_private = malloc(sizeof(struct gg_session_private));
873 if (sess_private == NULL) {
874 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session private data\n");
875 goto fail;
878 memset(sess_private, 0, sizeof(struct gg_session_private));
879 sess->private_data = sess_private;
881 if (p->password == NULL || p->uin == 0) {
882 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
883 errno = EFAULT;
884 goto fail;
887 if (!(sess->password = strdup(p->password))) {
888 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
889 goto fail;
892 if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) {
893 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type);
894 errno = EFAULT;
895 goto fail;
898 sess->uin = p->uin;
899 sess->state = GG_STATE_RESOLVING;
900 sess->check = GG_CHECK_READ;
901 sess->timeout = GG_DEFAULT_TIMEOUT;
902 sess->async = p->async;
903 sess->type = GG_SESSION_GG;
904 sess->initial_status = p->status;
905 sess->callback = gg_session_callback;
906 sess->destroy = gg_free_session;
907 sess->port = p->server_port;
908 sess->server_addr = p->server_addr;
909 sess->external_port = p->external_port;
910 sess->external_addr = p->external_addr;
911 sess->client_addr = p->client_addr;
912 sess->client_port = p->client_port;
914 if (GG_LOGIN_PARAMS_HAS_FIELD(p, compatibility))
915 sess_private->compatibility = p->compatibility;
917 if (GG_LOGIN_PARAMS_HAS_FIELD(p, connect_host) && p->connect_host != NULL) {
918 int port = 0;
919 char *colon;
921 sess->connect_host = strdup(p->connect_host);
922 if (sess->connect_host == NULL)
923 goto fail;
925 colon = strchr(sess->connect_host, ':');
926 if (colon != NULL) {
927 colon[0] = '\0';
928 port = atoi(colon + 1);
930 if (port > 0)
931 sess->port = port;
934 if (GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager_type) &&
935 GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager) &&
936 p->socket_manager_type != GG_SOCKET_MANAGER_TYPE_INTERNAL)
938 if ((unsigned int)p->socket_manager_type >
939 GG_SOCKET_MANAGER_TYPE_TLS)
941 gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_login()"
942 " invalid arguments. unknown socket manager "
943 "type (%d)\n", p->socket_manager_type);
944 errno = EFAULT;
945 goto fail;
946 } else {
947 sess_private->socket_manager_type =
948 p->socket_manager_type;
949 memcpy(&sess_private->socket_manager,
950 &p->socket_manager,
951 sizeof(gg_socket_manager_t));
953 } else {
954 sess_private->socket_manager_type =
955 GG_SOCKET_MANAGER_TYPE_INTERNAL;
958 if (GG_LOGIN_PARAMS_HAS_FIELD(p, host_white_list) &&
959 p->host_white_list != NULL)
961 sess_private->host_white_list =
962 gg_strarr_dup(p->host_white_list);
963 if (sess_private->host_white_list == NULL)
964 goto fail;
967 if (p->protocol_features == 0) {
968 sess->protocol_features = GG_FEATURE_MSG80 |
969 GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC |
970 GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 |
971 GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK |
972 GG_FEATURE_TYPING_NOTIFICATION;
973 } else {
974 sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77));
976 if (!(p->protocol_features & GG_FEATURE_STATUS77))
977 sess->protocol_features |= GG_FEATURE_STATUS80;
979 if (!(p->protocol_features & GG_FEATURE_MSG77))
980 sess->protocol_features |= GG_FEATURE_MSG80;
983 if (!(sess->status_flags = p->status_flags))
984 sess->status_flags = GG_STATUS_FLAG_UNKNOWN | GG_STATUS_FLAG_SPAM;
986 if (!p->protocol_version)
987 sess->protocol_version = GG_DEFAULT_PROTOCOL_VERSION;
988 else if (p->protocol_version < 0x2e) {
989 gg_debug(GG_DEBUG_MISC, "// gg_login() libgadu no longer support protocol < 0x2e\n");
990 sess->protocol_version = 0x2e;
991 } else
992 sess->protocol_version = p->protocol_version;
994 if (p->client_version && !purple_strequal(p->client_version, "-"))
995 sess->client_version = strdup(p->client_version);
996 sess->last_sysmsg = p->last_sysmsg;
997 sess->image_size = p->image_size;
998 sess->pid = -1;
999 sess->encoding = p->encoding;
1001 if (gg_session_set_resolver(sess, p->resolver) == -1) {
1002 gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. "
1003 "unsupported resolver type (%d)\n", p->resolver);
1004 errno = EFAULT;
1005 goto fail;
1008 if (p->status_descr) {
1009 sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1);
1011 if (!sess->initial_descr) {
1012 gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
1013 goto fail;
1016 /* XXX pamiętać, żeby nie ciąć w środku znaku utf-8 */
1018 if (strlen(sess->initial_descr) > GG_STATUS_DESCR_MAXSIZE)
1019 sess->initial_descr[GG_STATUS_DESCR_MAXSIZE] = 0;
1022 if (p->tls != GG_SSL_DISABLED) {
1023 #if !defined(GG_CONFIG_HAVE_GNUTLS) && !defined(GG_CONFIG_HAVE_OPENSSL)
1024 gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
1026 if (p->tls == GG_SSL_REQUIRED) {
1027 errno = ENOSYS;
1028 goto fail;
1030 #else
1031 sess->ssl_flag = p->tls;
1032 #endif
1035 if (p->hash_type)
1036 sess->hash_type = p->hash_type;
1037 else
1038 sess->hash_type = GG_LOGIN_HASH_SHA1;
1040 if (sess->server_addr == 0 && sess->connect_host == NULL) {
1041 if (gg_proxy_enabled) {
1042 sess->resolver_host = gg_proxy_host;
1043 sess->proxy_port = gg_proxy_port;
1044 sess->state = (sess->async) ?
1045 GG_STATE_RESOLVE_PROXY_HUB_ASYNC :
1046 GG_STATE_RESOLVE_PROXY_HUB_SYNC;
1047 } else {
1048 sess->resolver_host = GG_APPMSG_HOST;
1049 sess->proxy_port = 0;
1050 sess->state = (sess->async) ? GG_STATE_RESOLVE_HUB_ASYNC : GG_STATE_RESOLVE_HUB_SYNC;
1052 } else {
1053 if (sess->connect_host != NULL)
1054 sess->server_addr = 0;
1055 else {
1056 /* XXX inet_ntoa i wielowątkowość */
1057 sess->connect_host = strdup(inet_ntoa(*(struct in_addr*) &sess->server_addr));
1058 if (sess->connect_host == NULL)
1059 goto fail;
1061 sess->connect_index = 0;
1063 if (gg_proxy_enabled) {
1064 sess->resolver_host = gg_proxy_host;
1065 sess->proxy_port = gg_proxy_port;
1066 if (sess->port == 0)
1067 sess->connect_port[0] = GG_HTTPS_PORT;
1068 else
1069 sess->connect_port[0] = sess->port;
1070 sess->connect_port[1] = 0;
1071 sess->state = (sess->async) ? GG_STATE_RESOLVE_PROXY_GG_ASYNC : GG_STATE_RESOLVE_PROXY_GG_SYNC;
1072 } else {
1073 sess->resolver_host = sess->connect_host;
1074 if (sess->port == 0) {
1075 if (sess->ssl_flag == GG_SSL_DISABLED) {
1076 sess->connect_port[0] = GG_DEFAULT_PORT;
1077 sess->connect_port[1] = GG_HTTPS_PORT;
1078 } else {
1079 sess->connect_port[0] = GG_HTTPS_PORT;
1080 sess->connect_port[1] = 0;
1082 } else {
1083 sess->connect_port[0] = sess->port;
1084 sess->connect_port[1] = 0;
1086 sess->state = (sess->async) ? GG_STATE_RESOLVE_GG_ASYNC : GG_STATE_RESOLVE_GG_SYNC;
1090 /* XXX inaczej gg_watch_fd() wyjdzie z timeoutem */
1091 sess->timeout = GG_DEFAULT_TIMEOUT;
1093 if (!sess->async) {
1094 while (!GG_SESSION_IS_CONNECTED(sess)) {
1095 struct gg_event *ge;
1097 ge = gg_watch_fd(sess);
1099 if (ge == NULL) {
1100 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
1101 goto fail;
1104 if (ge->type == GG_EVENT_CONN_FAILED) {
1105 errno = EACCES;
1106 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() could not login\n");
1107 gg_event_free(ge);
1108 goto fail;
1111 gg_event_free(ge);
1113 } else {
1114 struct gg_event *ge;
1116 ge = gg_watch_fd(sess);
1118 if (ge == NULL) {
1119 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
1120 goto fail;
1123 gg_event_free(ge);
1126 return sess;
1128 fail:
1129 gg_free_session(sess);
1131 return NULL;
1135 * Wysyła do serwera pakiet utrzymania połączenia.
1137 * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia,
1138 * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie
1139 * połączenie.
1141 * \param sess Struktura sesji
1143 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1145 * \ingroup login
1147 int gg_ping(struct gg_session *sess)
1149 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
1151 if (!sess) {
1152 errno = EFAULT;
1153 return -1;
1156 if (sess->state != GG_STATE_CONNECTED) {
1157 errno = ENOTCONN;
1158 return -1;
1161 return gg_send_packet(sess, GG_PING, NULL);
1165 * Kończy połączenie z serwerem.
1167 * Funkcja nie zwalnia zasobów, więc po jej wywołaniu należy użyć
1168 * \c gg_free_session(). Jeśli chce się ustawić opis niedostępności, należy
1169 * wcześniej wywołać funkcję \c gg_change_status_descr() lub
1170 * \c gg_change_status_descr_time().
1172 * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze
1173 * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one
1174 * utracone przy zrywaniu połączenia. Aby mieć pewność, że opis statusu
1175 * zostanie zachowany, należy ustawić stan \c GG_STATUS_NOT_AVAIL_DESCR
1176 * za pomocą funkcji \c gg_change_status_descr() i poczekać na zdarzenie
1177 * \c GG_EVENT_DISCONNECT_ACK.
1179 * \param sess Struktura sesji
1181 * \ingroup login
1183 void gg_logoff(struct gg_session *sess)
1185 if (!sess)
1186 return;
1188 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
1190 #ifdef GG_CONFIG_HAVE_GNUTLS
1191 if (sess->ssl != NULL)
1192 gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR);
1193 #endif
1195 #ifdef GG_CONFIG_HAVE_OPENSSL
1196 if (sess->ssl != NULL)
1197 SSL_shutdown(sess->ssl);
1198 #endif
1200 sess->resolver_cleanup(&sess->resolver, 1);
1202 gg_close(sess);
1204 if (sess->send_buf) {
1205 free(sess->send_buf);
1206 sess->send_buf = NULL;
1207 sess->send_left = 0;
1212 * Zwalnia zasoby używane przez połączenie z serwerem. Funkcję należy wywołać
1213 * po zamknięciu połączenia z serwerem, by nie doprowadzić do wycieku zasobów
1214 * systemowych.
1216 * \param sess Struktura sesji
1218 * \ingroup login
1220 void gg_free_session(struct gg_session *sess)
1222 struct gg_dcc7 *dcc;
1223 gg_chat_list_t *chat;
1225 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_free_session(%p);\n", sess);
1227 if (sess == NULL)
1228 return;
1230 /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
1232 free(sess->resolver_result);
1233 free(sess->connect_host);
1234 free(sess->password);
1235 free(sess->initial_descr);
1236 free(sess->client_version);
1237 free(sess->header_buf);
1238 free(sess->recv_buf);
1240 #ifdef GG_CONFIG_HAVE_GNUTLS
1241 if (sess->ssl != NULL) {
1242 gg_session_gnutls_t *tmp;
1244 tmp = (gg_session_gnutls_t*) sess->ssl;
1245 gnutls_deinit(tmp->session);
1246 gnutls_certificate_free_credentials(tmp->xcred);
1247 gnutls_global_deinit();
1248 free(sess->ssl);
1250 #endif
1252 #ifdef GG_CONFIG_HAVE_OPENSSL
1253 if (sess->ssl)
1254 SSL_free(sess->ssl);
1256 if (sess->ssl_ctx)
1257 SSL_CTX_free(sess->ssl_ctx);
1258 #endif
1260 if (sess->resolver_cleanup != NULL)
1261 sess->resolver_cleanup(&sess->resolver, 1);
1263 gg_close(sess);
1265 while (sess->images) {
1266 struct gg_image_queue *next = sess->images->next;
1268 gg_image_queue_remove(sess, sess->images, 1);
1270 /* a fix for false-positive NULL-dereference */
1271 sess->images = next;
1274 free(sess->send_buf);
1276 for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
1277 dcc->sess = NULL;
1279 chat = sess->private_data->chat_list;
1280 while (chat != NULL) {
1281 gg_chat_list_t *next = chat->next;
1282 free(chat->participants);
1283 free(chat);
1284 chat = next;
1287 gg_strarr_free(sess->private_data->host_white_list);
1289 free(sess->private_data);
1291 free(sess);
1295 * Zmienia status użytkownika.
1297 * \param sess Struktura sesji
1298 * \param status Nowy status użytkownika
1300 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1302 * \ingroup status
1304 int gg_change_status(struct gg_session *sess, int status)
1306 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
1308 return gg_change_status_descr(sess, status, NULL);
1312 * Zmienia status użytkownika na status opisowy.
1314 * \param sess Struktura sesji
1315 * \param status Nowy status użytkownika
1316 * \param descr Opis statusu użytkownika (lub \c NULL)
1318 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1320 * \ingroup status
1322 int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
1324 struct gg_new_status80 p;
1325 char *gen_descr = NULL;
1326 int descr_len = 0;
1327 int descr_null_len = 0;
1328 int res;
1330 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
1332 if (!sess) {
1333 errno = EFAULT;
1334 return -1;
1337 if (sess->state != GG_STATE_CONNECTED) {
1338 errno = ENOTCONN;
1339 return -1;
1342 sess->status = status;
1344 if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
1345 descr = gen_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1);
1347 if (!gen_descr)
1348 return -1;
1351 if (descr) {
1352 descr_len = strlen(descr);
1354 if (descr_len > GG_STATUS_DESCR_MAXSIZE)
1355 descr_len = GG_STATUS_DESCR_MAXSIZE;
1357 /* XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8 */
1358 } else {
1359 descr = "";
1362 p.status = gg_fix32(status);
1363 p.flags = gg_fix32(sess->status_flags);
1364 p.description_size = gg_fix32(descr_len);
1366 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
1367 p.flags = gg_fix32(0x00000014);
1368 descr_null_len = 1;
1371 res = gg_send_packet(sess, GG_NEW_STATUS80,
1372 &p, sizeof(p), descr, descr_len,
1373 "\x00", descr_null_len, NULL);
1375 free(gen_descr);
1377 if (GG_S_NA(status)) {
1378 sess->state = GG_STATE_DISCONNECTING;
1379 sess->timeout = GG_TIMEOUT_DISCONNECT;
1382 return res;
1386 * Zmienia status użytkownika na status opisowy z podanym czasem powrotu.
1388 * \param sess Struktura sesji
1389 * \param status Nowy status użytkownika
1390 * \param descr Opis statusu użytkownika
1391 * \param ts Czas powrotu w postaci uniksowego znacznika czasu
1393 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1395 * \ingroup status
1397 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int ts)
1399 gg_debug_session(sess, GG_DEBUG_FUNCTION,
1400 "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n",
1401 sess, status, descr, ts);
1403 return gg_change_status_descr(sess, status, descr);
1407 * Funkcja zmieniająca flagi statusu.
1409 * \param sess Struktura sesji
1410 * \param flags Nowe flagi statusu
1412 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1414 * \note Aby zmiany weszły w życie, należy ponownie ustawić status za pomocą
1415 * funkcji z rodziny \c gg_change_status().
1417 * \ingroup status
1419 int gg_change_status_flags(struct gg_session *sess, int flags)
1421 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_flags(%p, 0x%08x);\n", sess, flags);
1423 if (sess == NULL) {
1424 errno = EFAULT;
1425 return -1;
1428 sess->status_flags = flags;
1430 return 0;
1433 #ifndef DOXYGEN
1435 static int gg_send_message_110(struct gg_session *sess,
1436 uin_t recipient, uint64_t chat_id,
1437 const char *message, int is_html)
1439 GG110SendMessage msg = GG110_SEND_MESSAGE__INIT;
1440 int packet_type = recipient ? GG_SEND_MSG110 : GG_CHAT_SEND_MSG;
1441 int seq;
1442 char *html_message_gen = NULL, *plain_message_gen = NULL;
1443 const char *html_message, *plain_message;
1444 int succ = 1;
1446 gg_debug_session(sess, GG_DEBUG_FUNCTION,
1447 "** gg_send_message_110(%p, %u, %" PRIu64 ", %p, %d);\n",
1448 sess, recipient, chat_id, message, is_html);
1450 if (message == NULL)
1451 return -1;
1453 if ((recipient == 0) == (chat_id == 0))
1454 return -1;
1456 if (is_html) {
1457 html_message = message;
1459 if (sess->encoding != GG_ENCODING_UTF8) {
1460 html_message = html_message_gen = gg_encoding_convert(
1461 html_message, sess->encoding, GG_ENCODING_UTF8,
1462 -1, -1);
1463 if (html_message_gen == NULL)
1464 return -1;
1467 plain_message = plain_message_gen =
1468 gg_message_html_to_text_110(html_message);
1469 if (plain_message_gen == NULL) {
1470 free(html_message_gen);
1471 return -1;
1473 } else {
1474 plain_message = message;
1476 if (sess->encoding != GG_ENCODING_UTF8) {
1477 plain_message = plain_message_gen = gg_encoding_convert(
1478 plain_message, sess->encoding, GG_ENCODING_UTF8,
1479 -1, -1);
1480 if (plain_message_gen == NULL)
1481 return -1;
1484 html_message = html_message_gen =
1485 gg_message_text_to_html_110(plain_message, -1);
1486 if (html_message_gen == NULL) {
1487 free(plain_message_gen);
1488 return -1;
1492 seq = ++sess->seq;
1494 if (recipient) {
1495 msg.has_recipient = 1;
1496 gg_protobuf_set_uin(&msg.recipient, recipient, NULL);
1499 msg.seq = seq;
1501 /* rzutujemy z const, ale msg i tak nie będzie modyfikowany */
1502 msg.msg_plain = (char*)plain_message;
1503 msg.msg_xhtml = (char*)html_message;
1505 if (chat_id) {
1506 msg.dummy3 = "";
1507 msg.has_chat_id = 1;
1508 msg.chat_id = chat_id;
1511 if (!GG_PROTOBUF_SEND(sess, NULL, packet_type, gg110_send_message, msg))
1512 succ = 0;
1514 free(html_message_gen);
1515 free(plain_message_gen);
1517 return succ ? seq : -1;
1520 static char *
1521 gg_message_legacy_text_to_html(const char *src, gg_encoding_t encoding,
1522 const unsigned char *format, size_t format_len)
1524 size_t len;
1525 char *dst;
1527 if (format == NULL || format_len <= 3) {
1528 format = NULL;
1529 format_len = 0;
1530 } else {
1531 format += 3;
1532 format_len -= 3;
1535 len = gg_message_text_to_html(NULL, src, encoding, format, format_len);
1537 dst = malloc(len + 1);
1538 if (dst == NULL)
1539 return NULL;
1541 gg_message_text_to_html(dst, src, encoding, format, format_len);
1543 return dst;
1547 * \internal Wysyła wiadomość.
1549 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1550 * do potwierdzenia.
1552 * \param sess Struktura sesji
1553 * \param msgclass Klasa wiadomości
1554 * \param recipients_count Liczba adresatów
1555 * \param recipients Wskaźnik do tablicy z numerami adresatów
1556 * \param message Treść wiadomości
1557 * \param format Informacje o formatowaniu
1558 * \param formatlen Długość informacji o formatowaniu
1559 * \param html_message Treść wiadomości HTML
1561 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1563 * \ingroup messages
1565 static int gg_send_message_common(struct gg_session *sess, int msgclass,
1566 int recipients_count, uin_t *recipients, const unsigned char *message,
1567 const unsigned char *format, int formatlen,
1568 const unsigned char *html_message)
1570 struct gg_send_msg80 s80;
1571 const char *cp_msg = NULL, *utf_html_msg = NULL;
1572 char *recoded_msg = NULL, *recoded_html_msg = NULL;
1573 unsigned char *generated_format = NULL;
1574 int seq_no = -1;
1576 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_common("
1577 "%p, %d, %d, %p, %p, %p, %d, %p);\n", sess, msgclass,
1578 recipients_count, recipients, message, format,
1579 formatlen, html_message);
1581 if (!sess) {
1582 errno = EFAULT;
1583 return -1;
1586 if (sess->state != GG_STATE_CONNECTED) {
1587 errno = ENOTCONN;
1588 return -1;
1591 if ((message == NULL && html_message == NULL) ||
1592 recipients_count <= 0 || recipients_count > 0xffff ||
1593 recipients == NULL || (format == NULL && formatlen != 0))
1595 errno = EINVAL;
1596 return -1;
1599 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 &&
1600 recipients_count == 1)
1602 int is_html = (html_message != NULL);
1603 char *formatted_msg = NULL;
1605 if (formatlen > 3 && !is_html) {
1606 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_WARNING,
1607 "// gg_send_message_common() using legacy "
1608 "formatting with new protocol\n");
1609 formatted_msg = gg_message_legacy_text_to_html(
1610 (const char *)message, sess->encoding,
1611 format, formatlen);
1612 if (formatted_msg == NULL)
1613 goto cleanup;
1614 html_message = (unsigned char*)formatted_msg;
1615 is_html = 1;
1618 seq_no = gg_send_message_110(sess, recipients[0], 0,
1619 (const char*)(is_html ? html_message : message),
1620 is_html);
1621 goto cleanup;
1624 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 &&
1625 !gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_LEGACY_CONFER))
1627 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
1628 "// gg_send_message_common() legacy conferences disabled\n");
1629 errno = EINVAL;
1630 return -1;
1633 if (message == NULL) {
1634 char *tmp_msg;
1635 size_t len, fmt_len;
1636 uint16_t fixed_fmt_len;
1638 len = gg_message_html_to_text(NULL, NULL, &fmt_len, (const char*) html_message, sess->encoding);
1640 tmp_msg = malloc(len + 1);
1642 if (tmp_msg == NULL)
1643 goto cleanup;
1645 if (fmt_len != 0) {
1646 generated_format = malloc(fmt_len + 3);
1648 if (generated_format == NULL) {
1649 free(tmp_msg);
1650 goto cleanup;
1653 generated_format[0] = '\x02';
1654 fixed_fmt_len = gg_fix16(fmt_len);
1655 memcpy(generated_format + 1, &fixed_fmt_len, sizeof(fixed_fmt_len));
1656 gg_message_html_to_text(tmp_msg, generated_format + 3,
1657 NULL, (const char*)html_message, sess->encoding);
1659 format = generated_format;
1660 formatlen = fmt_len + 3;
1661 } else {
1662 gg_message_html_to_text(tmp_msg, NULL, NULL, (const char*) html_message, sess->encoding);
1664 format = NULL;
1665 formatlen = 0;
1668 if (sess->encoding != GG_ENCODING_CP1250) {
1669 cp_msg = recoded_msg = gg_encoding_convert(tmp_msg, sess->encoding, GG_ENCODING_CP1250, -1, -1);
1670 free(tmp_msg);
1672 if (cp_msg == NULL)
1673 goto cleanup;
1674 } else {
1675 cp_msg = recoded_msg = tmp_msg;
1677 } else {
1678 if (sess->encoding != GG_ENCODING_CP1250) {
1679 cp_msg = recoded_msg = gg_encoding_convert(
1680 (const char*)message, sess->encoding,
1681 GG_ENCODING_CP1250, -1, -1);
1683 if (cp_msg == NULL)
1684 goto cleanup;
1685 } else {
1686 cp_msg = (const char*) message;
1690 if (html_message == NULL) {
1691 char *formatted_msg;
1693 formatted_msg = gg_message_legacy_text_to_html(
1694 (const char*)message, sess->encoding, format, formatlen);
1695 if (formatted_msg == NULL)
1696 goto cleanup;
1698 if (sess->encoding == GG_ENCODING_UTF8) {
1699 utf_html_msg = recoded_html_msg = formatted_msg;
1700 } else {
1701 utf_html_msg = recoded_html_msg = gg_encoding_convert(
1702 formatted_msg, sess->encoding,
1703 GG_ENCODING_UTF8, -1, -1);
1704 free(formatted_msg);
1706 if (utf_html_msg == NULL)
1707 goto cleanup;
1709 } else {
1710 if (sess->encoding == GG_ENCODING_UTF8) {
1711 utf_html_msg = (const char*) html_message;
1712 } else {
1713 utf_html_msg = recoded_html_msg = gg_encoding_convert(
1714 (const char*)html_message, sess->encoding,
1715 GG_ENCODING_UTF8, -1, -1);
1717 if (utf_html_msg == NULL)
1718 goto cleanup;
1722 /* Drobne odchylenie od protokołu. Jeśli wysyłamy kilka
1723 * wiadomości w ciągu jednej sekundy, zwiększamy poprzednią
1724 * wartość, żeby każda wiadomość miała unikalny numer.
1727 seq_no = time(NULL);
1729 if (seq_no <= sess->seq)
1730 seq_no = sess->seq + 1;
1732 sess->seq = seq_no;
1734 s80.seq = gg_fix32(seq_no);
1735 s80.msgclass = gg_fix32(msgclass);
1736 s80.offset_plain = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1);
1737 s80.offset_attr = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1 + strlen(cp_msg) + 1);
1739 if (recipients_count > 1) {
1740 struct gg_msg_recipients r;
1741 int i, j, k;
1742 uin_t *recps;
1744 r.flag = GG_MSG_OPTION_CONFERENCE;
1745 r.count = gg_fix32(recipients_count - 1);
1747 recps = malloc(sizeof(uin_t) * (recipients_count - 1));
1749 if (!recps) {
1750 seq_no = -1;
1751 goto cleanup;
1754 for (i = 0; i < recipients_count; i++) {
1755 for (j = 0, k = 0; j < recipients_count; j++) {
1756 if (j != i) {
1757 recps[k] = gg_fix32(recipients[j]);
1758 k++;
1762 s80.recipient = gg_fix32(recipients[i]);
1764 if (gg_send_packet(sess, GG_SEND_MSG80, &s80,
1765 sizeof(s80), utf_html_msg,
1766 strlen(utf_html_msg) + 1, cp_msg,
1767 strlen(cp_msg) + 1, &r, sizeof(r), recps,
1768 (recipients_count - 1) * sizeof(uin_t), format,
1769 formatlen, NULL) == -1)
1771 seq_no = -1;
1775 free(recps);
1776 } else {
1777 s80.recipient = gg_fix32(recipients[0]);
1779 if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80),
1780 utf_html_msg, strlen(utf_html_msg) + 1, cp_msg,
1781 strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
1783 seq_no = -1;
1787 cleanup:
1788 free(recoded_msg);
1789 free(recoded_html_msg);
1790 free(generated_format);
1792 if (seq_no >= 0)
1793 gg_compat_message_sent(sess, seq_no, recipients_count, recipients);
1795 return seq_no;
1798 #endif /* DOXYGEN */
1801 * Wysyła wiadomość do użytkownika.
1803 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1804 * do potwierdzenia.
1806 * \param sess Struktura sesji
1807 * \param msgclass Klasa wiadomości
1808 * \param recipient Numer adresata
1809 * \param message Treść wiadomości
1811 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1813 * \ingroup messages
1815 int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
1817 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, "
1818 "%u, %p)\n", sess, msgclass, recipient, message);
1820 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
1821 int seq_no;
1823 seq_no = gg_send_message_110(sess, recipient, 0, (const char*)message, 0);
1825 if (seq_no >= 0)
1826 gg_compat_message_sent(sess, seq_no, 1, &recipient);
1828 return seq_no;
1831 return gg_send_message_common(sess, msgclass, 1, &recipient, message,
1832 (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
1833 9, NULL);
1837 * Wysyła wiadomość formatowaną.
1839 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1840 * do potwierdzenia.
1842 * \param sess Struktura sesji
1843 * \param msgclass Klasa wiadomości
1844 * \param recipient Numer adresata
1845 * \param message Treść wiadomości
1846 * \param format Informacje o formatowaniu
1847 * \param formatlen Długość informacji o formatowaniu
1849 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1851 * \ingroup messages
1853 int gg_send_message_richtext(struct gg_session *sess, int msgclass,
1854 uin_t recipient, const unsigned char *message,
1855 const unsigned char *format, int formatlen)
1857 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext("
1858 "%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient,
1859 message, format, formatlen);
1861 return gg_send_message_common(sess, msgclass, 1, &recipient, message, format, formatlen, NULL);
1865 * Wysyła formatowaną wiadomość HTML.
1867 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1868 * do potwierdzenia.
1870 * \param sess Struktura sesji
1871 * \param msgclass Klasa wiadomości
1872 * \param recipient Numer adresata
1873 * \param html_message Treść wiadomości HTML
1875 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1877 * \ingroup messages
1879 int gg_send_message_html(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *html_message)
1881 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_html(%p, "
1882 "%d, %u, %p);\n", sess, msgclass, recipient, html_message);
1884 return gg_send_message_common(sess, msgclass, 1, &recipient, NULL, NULL, 0, html_message);
1888 * Wysyła wiadomość w ramach konferencji.
1890 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1891 * do potwierdzenia.
1893 * \param sess Struktura sesji
1894 * \param msgclass Klasa wiadomości
1895 * \param recipients_count Liczba adresatów
1896 * \param recipients Wskaźnik do tablicy z numerami adresatów
1897 * \param message Treść wiadomości
1899 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1901 * \ingroup messages
1903 int gg_send_message_confer(struct gg_session *sess, int msgclass,
1904 int recipients_count, uin_t *recipients, const unsigned char *message)
1906 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer("
1907 "%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count,
1908 recipients, message);
1910 return gg_send_message_common(sess, msgclass, recipients_count,
1911 recipients, message,
1912 (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
1913 9, NULL);
1917 * Wysyła wiadomość formatowaną w ramach konferencji.
1919 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1920 * do potwierdzenia.
1922 * \param sess Struktura sesji
1923 * \param msgclass Klasa wiadomości
1924 * \param recipients_count Liczba adresatów
1925 * \param recipients Wskaźnik do tablicy z numerami adresatów
1926 * \param message Treść wiadomości
1927 * \param format Informacje o formatowaniu
1928 * \param formatlen Długość informacji o formatowaniu
1930 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1932 * \ingroup messages
1934 int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass,
1935 int recipients_count, uin_t *recipients, const unsigned char *message,
1936 const unsigned char *format, int formatlen)
1938 gg_debug_session(sess, GG_DEBUG_FUNCTION,
1939 "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, "
1940 "%d);\n", sess, msgclass, recipients_count, recipients, message,
1941 format, formatlen);
1943 return gg_send_message_common(sess, msgclass, recipients_count, recipients, message, format, formatlen, NULL);
1947 * Wysyła formatowaną wiadomość HTML w ramach konferencji.
1949 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
1950 * do potwierdzenia.
1952 * \param sess Struktura sesji
1953 * \param msgclass Klasa wiadomości
1954 * \param recipients_count Liczba adresatów
1955 * \param recipients Wskaźnik do tablicy z numerami adresatów
1956 * \param html_message Treść wiadomości HTML
1958 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1960 * \ingroup messages
1962 int gg_send_message_confer_html(struct gg_session *sess, int msgclass,
1963 int recipients_count, uin_t *recipients,
1964 const unsigned char *html_message)
1966 gg_debug_session(sess, GG_DEBUG_FUNCTION,
1967 "** gg_send_message_confer_html(%p, %d, %d, %p, %p);\n", sess,
1968 msgclass, recipients_count, recipients, html_message);
1970 return gg_send_message_common(sess, msgclass, recipients_count, recipients, NULL, NULL, 0, html_message);
1974 * Wysyła wiadomość binarną przeznaczoną dla klienta.
1976 * Wiadomości między klientami przesyła się np. w celu wywołania zwrotnego
1977 * połączenia bezpośredniego. Funkcja zwraca losowy numer sekwencyjny,
1978 * który można zignorować albo wykorzystać do potwierdzenia.
1980 * \param sess Struktura sesji
1981 * \param msgclass Klasa wiadomości
1982 * \param recipient Numer adresata
1983 * \param message Treść wiadomości
1984 * \param message_len Długość wiadomości
1986 * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
1988 * \ingroup messages
1990 int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient,
1991 const unsigned char *message, int message_len)
1993 struct gg_send_msg s;
1995 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, "
1996 "%d, %u, ...);\n", sess, msgclass, recipient);
1998 if (!sess) {
1999 errno = EFAULT;
2000 return -1;
2003 if (sess->state != GG_STATE_CONNECTED) {
2004 errno = ENOTCONN;
2005 return -1;
2008 s.recipient = gg_fix32(recipient);
2009 s.seq = gg_fix32(0);
2010 s.msgclass = gg_fix32(msgclass);
2012 return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
2016 * Wysyła żądanie obrazka o podanych parametrach.
2018 * Wiadomości obrazkowe nie zawierają samych obrazków, a tylko ich rozmiary
2019 * i sumy kontrolne. Odbiorca najpierw szuka obrazków w swojej pamięci
2020 * podręcznej i dopiero gdy ich nie znajdzie, wysyła żądanie do nadawcy.
2021 * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY.
2023 * \param sess Struktura sesji
2024 * \param recipient Numer adresata
2025 * \param size Rozmiar obrazka w bajtach
2026 * \param crc32 Suma kontrola obrazka
2028 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2030 * \ingroup messages
2032 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
2034 struct gg_send_msg s;
2035 struct gg_msg_image_request r;
2036 char dummy = 0;
2037 int res;
2039 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, "
2040 "%u, 0x%.4x);\n", sess, recipient, size, crc32);
2042 if (!sess) {
2043 errno = EFAULT;
2044 return -1;
2047 if (sess->state != GG_STATE_CONNECTED) {
2048 errno = ENOTCONN;
2049 return -1;
2052 if (size < 0) {
2053 errno = EINVAL;
2054 return -1;
2057 s.recipient = gg_fix32(recipient);
2058 s.seq = gg_fix32(0);
2059 s.msgclass = gg_fix32(GG_CLASS_MSG);
2061 r.flag = GG_MSG_OPTION_IMAGE_REQUEST;
2062 r.size = gg_fix32(size);
2063 r.crc32 = gg_fix32(crc32);
2065 res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);
2067 if (!res) {
2068 struct gg_image_queue *q = malloc(sizeof(*q));
2069 char *buf;
2071 if (!q) {
2072 gg_debug_session(sess, GG_DEBUG_MISC,
2073 "// gg_image_request() not enough memory for "
2074 "image queue\n");
2075 return -1;
2078 buf = malloc(size);
2079 if (size && !buf) {
2080 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
2081 free(q);
2082 return -1;
2085 memset(q, 0, sizeof(*q));
2087 q->sender = recipient;
2088 q->size = size;
2089 q->crc32 = crc32;
2090 q->image = buf;
2092 if (!sess->images)
2093 sess->images = q;
2094 else {
2095 struct gg_image_queue *qq;
2097 for (qq = sess->images; qq->next; qq = qq->next);
2099 qq->next = q;
2103 return res;
2107 * Wysyła żądany obrazek.
2109 * \param sess Struktura sesji
2110 * \param recipient Numer adresata
2111 * \param filename Nazwa pliku
2112 * \param image Bufor z obrazkiem
2113 * \param size Rozmiar obrazka
2115 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2117 * \ingroup messages
2119 int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
2121 struct gg_session_private *p;
2122 struct gg_msg_image_reply *r;
2123 struct gg_send_msg s;
2124 const char *tmp;
2125 char buf[1910];
2126 gg_imgout_queue_t *queue = NULL, *queue_end = NULL;
2128 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, "
2129 "\"%s\", %p, %d);\n", sess, recipient, filename, image, size);
2131 if (!sess || !filename || !image) {
2132 errno = EFAULT;
2133 return -1;
2136 p = sess->private_data;
2138 if (sess->state != GG_STATE_CONNECTED) {
2139 errno = ENOTCONN;
2140 return -1;
2143 if (size < 0) {
2144 errno = EINVAL;
2145 return -1;
2148 /* wytnij ścieżki, zostaw tylko nazwę pliku */
2149 while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
2150 filename = tmp + 1;
2152 if (strlen(filename) < 1 || strlen(filename) > 1024) {
2153 errno = EINVAL;
2154 return -1;
2157 s.recipient = gg_fix32(recipient);
2158 s.seq = gg_fix32(0);
2159 s.msgclass = gg_fix32(GG_CLASS_MSG);
2161 buf[0] = 0;
2162 r = (void*) &buf[1];
2164 r->flag = GG_MSG_OPTION_IMAGE_REPLY;
2165 r->size = gg_fix32(size);
2166 r->crc32 = gg_fix32(gg_crc32(0, (const unsigned char*) image, size));
2168 while (size > 0) {
2169 gg_imgout_queue_t *it;
2170 size_t buflen, chunklen;
2172 /* \0 + struct gg_msg_image_reply */
2173 buflen = sizeof(struct gg_msg_image_reply) + 1;
2175 /* w pierwszym kawałku jest nazwa pliku */
2176 if (r->flag == GG_MSG_OPTION_IMAGE_REPLY) {
2177 strcpy(buf + buflen, filename);
2178 buflen += strlen(filename) + 1;
2181 chunklen = ((size_t) size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t) size;
2183 memcpy(buf + buflen, image, chunklen);
2184 size -= chunklen;
2185 image += chunklen;
2187 it = gg_new0(sizeof(gg_imgout_queue_t));
2188 if (!it)
2189 break;
2190 if (queue_end) {
2191 queue_end->next = it;
2192 queue_end = it;
2193 } else {
2194 queue = queue_end = it;
2197 memcpy(&it->msg_hdr, &s, sizeof(s));
2198 memcpy(it->buf, buf, buflen + chunklen);
2199 it->buf_len = buflen + chunklen;
2201 r->flag = GG_MSG_OPTION_IMAGE_REPLY_MORE;
2204 if (p->imgout_queue) {
2205 queue_end = p->imgout_queue;
2206 while (queue_end->next)
2207 queue_end = queue_end->next;
2208 queue_end->next = queue;
2209 } else {
2210 p->imgout_queue = queue;
2212 gg_image_sendout(sess);
2214 return 0;
2217 void gg_image_sendout(struct gg_session *sess)
2219 struct gg_session_private *p = sess->private_data;
2221 while (p->imgout_waiting_ack < GG_IMGOUT_WAITING_MAX && p->imgout_queue) {
2222 gg_imgout_queue_t *it = p->imgout_queue;
2223 int res;
2225 p->imgout_queue = p->imgout_queue->next;
2226 p->imgout_waiting_ack++;
2228 res = gg_send_packet(sess, GG_SEND_MSG,
2229 &it->msg_hdr, sizeof(it->msg_hdr),
2230 it->buf, it->buf_len,
2231 NULL);
2233 free(it);
2235 if (res == -1)
2236 break;
2240 static int gg_notify105_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
2242 int i = 0;
2244 if (!userlist || !count)
2245 return gg_send_packet(sess, GG_NOTIFY105_LIST_EMPTY, NULL);
2247 while (i < count) {
2248 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
2249 gg_tvbuilder_expected_size(tvb, 2100);
2251 while (i < count) {
2252 size_t prev_size = gg_tvbuilder_get_size(tvb);
2253 gg_tvbuilder_write_uin(tvb, userlist[i]);
2254 gg_tvbuilder_write_uint8(tvb,
2255 (types == NULL) ? GG_USER_NORMAL : types[i]);
2257 /* Oryginalny klient wysyła maksymalnie 2048 bajtów
2258 * danych w każdym pakiecie tego typu.
2260 if (gg_tvbuilder_get_size(tvb) > 2048) {
2261 gg_tvbuilder_strip(tvb, prev_size);
2262 break;
2264 i++;
2267 if (!gg_tvbuilder_send(tvb, (i < count) ?
2268 GG_NOTIFY105_FIRST : GG_NOTIFY105_LAST))
2270 return -1;
2274 return 0;
2278 * Wysyła do serwera listę kontaktów.
2280 * Funkcja informuje serwer o liście kontaktów, których statusy będą
2281 * obserwowane lub kontaktów, które bedą blokowane. Dla każdego z \c count
2282 * kontaktów tablica \c userlist zawiera numer, a tablica \c types rodzaj
2283 * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED).
2285 * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli
2286 * jest pusta.
2288 * \param sess Struktura sesji
2289 * \param userlist Wskaźnik do tablicy numerów kontaktów
2290 * \param types Wskaźnik do tablicy rodzajów kontaktów. Jeżeli NULL, wszystkie kontakty są typu GG_USER_NORMAL.
2291 * \param count Liczba kontaktów
2293 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2295 * \ingroup contacts
2297 int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
2299 struct gg_notify *n;
2300 int i, res = 0;
2302 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
2304 if (!sess) {
2305 errno = EFAULT;
2306 return -1;
2309 if (sess->state != GG_STATE_CONNECTED) {
2310 errno = ENOTCONN;
2311 return -1;
2314 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110)
2315 return gg_notify105_ex(sess, userlist, types, count);
2317 if (!userlist || !count)
2318 return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
2320 while (count > 0) {
2321 int part_count, packet_type;
2323 if (count > 400) {
2324 part_count = 400;
2325 packet_type = GG_NOTIFY_FIRST;
2326 } else {
2327 part_count = count;
2328 packet_type = GG_NOTIFY_LAST;
2331 if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
2332 return -1;
2334 for (i = 0; i < part_count; i++) {
2335 n[i].uin = gg_fix32(userlist[i]);
2336 if (types == NULL)
2337 n[i].dunno1 = GG_USER_NORMAL;
2338 else
2339 n[i].dunno1 = types[i];
2342 if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
2343 free(n);
2344 res = -1;
2345 break;
2348 count -= part_count;
2349 userlist += part_count;
2350 if (types != NULL)
2351 types += part_count;
2353 free(n);
2356 return res;
2360 * Wysyła do serwera listę kontaktów.
2362 * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
2363 * są rodzaju \c GG_USER_NORMAL.
2365 * \param sess Struktura sesji
2366 * \param userlist Wskaźnik do tablicy numerów kontaktów
2367 * \param count Liczba kontaktów
2369 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2371 * \ingroup contacts
2373 int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
2375 return gg_notify_ex(sess, userlist, NULL, count);
2379 * Dodaje kontakt.
2381 * Dodaje do listy kontaktów dany numer w trakcie połączenia. Aby zmienić
2382 * rodzaj kontaktu (np. z normalnego na zablokowany), należy najpierw usunąć
2383 * poprzedni rodzaj, ponieważ serwer operuje na maskach bitowych.
2385 * \param sess Struktura sesji
2386 * \param uin Numer kontaktu
2387 * \param type Rodzaj kontaktu
2389 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2391 * \ingroup contacts
2393 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
2395 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
2397 if (!sess) {
2398 errno = EFAULT;
2399 return -1;
2402 if (sess->state != GG_STATE_CONNECTED) {
2403 errno = ENOTCONN;
2404 return -1;
2407 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
2408 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
2409 gg_tvbuilder_expected_size(tvb, 16);
2411 gg_tvbuilder_write_uin(tvb, uin);
2412 gg_tvbuilder_write_uint8(tvb, type);
2414 if (!gg_tvbuilder_send(tvb, GG_ADD_NOTIFY105))
2415 return -1;
2416 return 0;
2417 } else {
2418 struct gg_add_remove a;
2420 a.uin = gg_fix32(uin);
2421 a.dunno1 = type;
2423 return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
2428 * Dodaje kontakt.
2430 * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
2431 * kontaktów to \c GG_USER_NORMAL.
2433 * \param sess Struktura sesji
2434 * \param uin Numer kontaktu
2436 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2438 * \ingroup contacts
2440 int gg_add_notify(struct gg_session *sess, uin_t uin)
2442 return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
2446 * Usuwa kontakt.
2448 * Usuwa z listy kontaktów dany numer w trakcie połączenia.
2450 * \param sess Struktura sesji
2451 * \param uin Numer kontaktu
2452 * \param type Rodzaj kontaktu
2454 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2456 * \ingroup contacts
2458 int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
2460 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
2462 if (!sess) {
2463 errno = EFAULT;
2464 return -1;
2467 if (sess->state != GG_STATE_CONNECTED) {
2468 errno = ENOTCONN;
2469 return -1;
2472 if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
2473 gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
2474 gg_tvbuilder_expected_size(tvb, 16);
2476 gg_tvbuilder_write_uin(tvb, uin);
2477 gg_tvbuilder_write_uint8(tvb, type);
2479 if (!gg_tvbuilder_send(tvb, GG_REMOVE_NOTIFY105))
2480 return -1;
2481 return 0;
2482 } else {
2483 struct gg_add_remove a;
2485 a.uin = gg_fix32(uin);
2486 a.dunno1 = type;
2488 return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
2493 * Usuwa kontakt.
2495 * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
2496 * kontaktów to \c GG_USER_NORMAL.
2498 * \param sess Struktura sesji
2499 * \param uin Numer kontaktu
2501 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2503 * \ingroup contacts
2505 int gg_remove_notify(struct gg_session *sess, uin_t uin)
2507 return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
2511 * Wysyła do serwera zapytanie dotyczące listy kontaktów.
2513 * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
2514 * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
2515 * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
2516 * listy kontaktów jest ignorowany przez serwer, ale ze względu na
2517 * kompatybilność z innymi klientami, należy przechowywać dane w tym samym
2518 * formacie co oryginalny klient Gadu-Gadu.
2520 * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą
2521 * z protokołu -- wysyła i odbiera kompletną listę.
2523 * \param sess Struktura sesji
2524 * \param type Rodzaj zapytania
2525 * \param request Treść zapytania (może być równe NULL)
2527 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2529 * \ingroup importexport
2531 int gg_userlist_request(struct gg_session *sess, char type, const char *request)
2533 int len;
2535 if (!sess) {
2536 errno = EFAULT;
2537 return -1;
2540 if (sess->state != GG_STATE_CONNECTED) {
2541 errno = ENOTCONN;
2542 return -1;
2545 if (!request) {
2546 sess->userlist_blocks = 1;
2547 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
2550 len = strlen(request);
2552 sess->userlist_blocks = 0;
2554 while (len > 2047) {
2555 sess->userlist_blocks++;
2557 if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
2558 return -1;
2560 if (type == GG_USERLIST_PUT)
2561 type = GG_USERLIST_PUT_MORE;
2563 request += 2047;
2564 len -= 2047;
2567 sess->userlist_blocks++;
2569 return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
2573 * Wysyła do serwera zapytanie dotyczące listy kontaktów (10.0).
2575 * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
2576 * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
2577 * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
2578 * listy kontaktów jest jednak weryfikowany przez serwer, który stara się
2579 * synchronizować listę kontaktów zapisaną w formatach GG 7.0 oraz GG 10.0.
2580 * Serwer przyjmuje listy kontaktów przysłane w formacie niezgodnym z podanym
2581 * jako \c format_type, ale nie zachowuje ich, a przesłanie takiej listy jest
2582 * równoznaczne z usunięciem listy kontaktów.
2584 * Program nie musi się przejmować kompresją listy kontaktów zgodną
2585 * z protokołem -- wysyła i odbiera kompletną listę zapisaną czystym tekstem.
2587 * \param sess Struktura sesji
2588 * \param type Rodzaj zapytania
2589 * \param version Numer ostatniej znanej programowi wersji listy kontaktów lub 0
2590 * \param format_type Typ formatu listy kontaktów
2591 * \param request Treść zapytania (może być równe NULL)
2593 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2595 * \ingroup importexport
2597 int gg_userlist100_request(struct gg_session *sess, char type,
2598 unsigned int version, char format_type, const char *request)
2600 struct gg_userlist100_request pkt;
2601 unsigned char *zrequest;
2602 size_t zrequest_len;
2603 int ret;
2605 if (!sess) {
2606 errno = EFAULT;
2607 return -1;
2610 if (sess->state != GG_STATE_CONNECTED) {
2611 errno = ENOTCONN;
2612 return -1;
2615 pkt.type = type;
2616 pkt.version = gg_fix32(version);
2617 pkt.format_type = format_type;
2618 pkt.unknown1 = 0x01;
2620 if (request == NULL)
2621 return gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), NULL);
2623 zrequest = gg_deflate(request, &zrequest_len);
2625 if (zrequest == NULL) {
2626 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_userlist100_request() gg_deflate() failed\n");
2627 return -1;
2630 ret = gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), zrequest, zrequest_len, NULL);
2632 free(zrequest);
2634 return ret;
2638 * Informuje rozmówcę o pisaniu wiadomości.
2640 * \param sess Struktura sesji
2641 * \param recipient Numer adresata
2642 * \param length Długość wiadomości lub 0 jeśli jest pusta
2644 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2646 * \ingroup messages
2648 int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length){
2649 struct gg_typing_notification pkt;
2650 uin_t uin;
2652 pkt.length = gg_fix16(length);
2653 uin = gg_fix32(recipient);
2654 memcpy(&pkt.uin, &uin, sizeof(uin_t));
2656 return gg_send_packet(sess, GG_TYPING_NOTIFICATION, &pkt, sizeof(pkt), NULL);
2660 * Rozłącza inną sesję multilogowania.
2662 * \param gs Struktura sesji
2663 * \param conn_id Sesja do rozłączenia
2665 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2667 * \ingroup login
2669 int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id)
2671 struct gg_multilogon_disconnect pkt;
2673 pkt.conn_id = conn_id;
2675 return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL);
2679 * Tworzy nową konferencję (11.0).
2681 * \param gs Struktura sesji
2683 * \return Numer sekwencyjny (ten sam, co w \c gg_event_chat_created), lub -1
2684 * w przypadku błędu
2686 * \ingroup chat
2688 int gg_chat_create(struct gg_session *gs)
2690 struct gg_chat_create pkt;
2691 int seq;
2693 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
2694 return -1;
2696 seq = ++gs->seq;
2698 pkt.seq = gg_fix32(seq);
2699 pkt.dummy = 0;
2701 if (gg_send_packet(gs, GG_CHAT_CREATE, &pkt, sizeof(pkt), NULL) == -1)
2702 return -1;
2704 return seq;
2708 * Zaprasza nowych użytkowników do konferencji (11.0).
2710 * \param gs Struktura sesji
2711 * \param id Identyfikator konferencji
2712 * \param participants Lista użytkowników do zaproszenia
2713 * \param participants_count Liczba użytkowników
2715 * \return Numer sekwencyjny w przypadku powodzenia (ten sam, co w
2716 * \c gg_event_chat_invite_ack), lub -1 w przypadku błędu
2718 * \ingroup chat
2720 int gg_chat_invite(struct gg_session *gs, uint64_t id, uin_t *participants,
2721 unsigned int participants_count)
2723 struct gg_chat_invite pkt;
2724 int seq, ret;
2725 unsigned int i;
2726 struct gg_chat_participant
2728 uint32_t uin;
2729 uint32_t dummy;
2730 } GG_PACKED;
2731 struct gg_chat_participant *participants_list;
2732 size_t participants_list_size;
2734 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
2735 return -1;
2737 if (participants_count == 0 || participants_count >=
2738 ~(unsigned int)0 / sizeof(struct gg_chat_participant))
2740 return -1;
2743 participants_list_size = sizeof(struct gg_chat_participant) *
2744 participants_count;
2745 participants_list = malloc(participants_list_size);
2746 if (participants_list == NULL)
2747 return -1;
2749 seq = ++gs->seq;
2750 pkt.id = gg_fix64(id);
2751 pkt.seq = gg_fix32(seq);
2752 pkt.participants_count = gg_fix32(participants_count);
2754 for (i = 0; i < participants_count; i++) {
2755 participants_list[i].uin = gg_fix32(participants[i]);
2756 participants_list[i].dummy = gg_fix32(0x1e);
2759 ret = gg_send_packet(gs, GG_CHAT_INVITE,
2760 &pkt, sizeof(pkt),
2761 participants_list, participants_list_size,
2762 NULL);
2763 free(participants_list);
2765 if (ret == -1)
2766 return -1;
2767 return seq;
2771 * Opuszcza konferencję (11.0).
2773 * \param gs Struktura sesji
2774 * \param id Identyfikator konferencji
2776 * \return 0 jeśli się powiodło, -1 w przypadku błędu
2778 * \ingroup chat
2780 int gg_chat_leave(struct gg_session *gs, uint64_t id)
2782 struct gg_chat_leave pkt;
2783 int seq;
2785 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
2786 return -1;
2788 seq = ++gs->seq;
2789 pkt.id = gg_fix64(id);
2790 pkt.seq = gg_fix32(seq);
2792 if (gg_send_packet(gs, GG_CHAT_LEAVE, &pkt, sizeof(pkt), NULL) == -1)
2793 return -1;
2795 return seq;
2799 * Wysyła wiadomość w ramach konferencji (11.0).
2801 * \param gs Struktura sesji
2802 * \param id Identyfikator konferencji
2803 * \param message Wiadomość
2804 * \param is_html 1, jeżeli wiadomość jest zapisana jako HTML, 0 w p.p.
2806 * \return Numer sekwencyjny (taki sam, jak w \c gg_event_chat_send_msg_ack)
2807 * jeśli się powiodło, -1 w przypadku błędu
2809 * \ingroup chat
2811 int gg_chat_send_message(struct gg_session *gs, uint64_t id, const char *message, int is_html)
2813 if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
2814 return -1;
2816 return gg_send_message_110(gs, 0, id, message, is_html);
2819 /* @} */
2822 * Sprawdza czy biblioteka obsługuje daną funkcję.
2824 * \param feature Identyfikator funkcji.
2826 * \return Wartość niezerowa jeśli funkcja jest obsłgiwana.
2828 * \ingroup version
2830 int gg_libgadu_check_feature(gg_libgadu_feature_t feature)
2832 switch (feature)
2834 case GG_LIBGADU_FEATURE_SSL:
2835 #if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS)
2836 return 1;
2837 #else
2838 return 0;
2839 #endif
2841 case GG_LIBGADU_FEATURE_PTHREAD:
2842 #ifdef GG_CONFIG_HAVE_PTHREAD
2843 return 1;
2844 #else
2845 return 0;
2846 #endif
2848 case GG_LIBGADU_FEATURE_USERLIST100:
2849 #ifdef GG_CONFIG_HAVE_ZLIB
2850 return 1;
2851 #else
2852 return 0;
2853 #endif
2855 /* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */
2859 return 0;
2862 static void gg_socket_manager_error(struct gg_session *sess, enum gg_failure_t failure)
2864 int pipes[2];
2865 uint8_t dummy = 0;
2866 struct gg_session_private *p = sess->private_data;
2868 p->socket_failure = failure;
2870 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
2871 gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to"
2872 " create pipes (errno=%d, %s)\n", errno,
2873 strerror(errno));
2874 return;
2877 p->socket_is_external = 0;
2878 sess->fd = pipes[1];
2879 sess->check = GG_CHECK_READ;
2880 sess->state = GG_STATE_ERROR;
2881 if (send(pipes[0], &dummy, sizeof(dummy), 0) != sizeof(dummy)) {
2882 gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to"
2883 " send via pipe (errno=%d, %s)\n", errno,
2884 strerror(errno));
2885 return;
2887 close(pipes[0]);
2890 int gg_compat_feature_is_enabled(struct gg_session *sess, gg_compat_feature_t feature)
2892 gg_compat_t level;
2894 if (sess == NULL)
2895 return 0;
2897 level = sess->private_data->compatibility;
2899 switch (feature) {
2900 case GG_COMPAT_FEATURE_ACK_EVENT:
2901 case GG_COMPAT_FEATURE_LEGACY_CONFER:
2902 return (level < GG_COMPAT_1_12_0);
2905 return 0;
2908 static gg_msg_list_t * gg_compat_find_sent_message(struct gg_session *sess, int seq, int remove)
2910 struct gg_session_private *p = sess->private_data;
2911 gg_msg_list_t *it, *previous = NULL;
2913 for (it = p->sent_messages; it; it = it->next) {
2914 if (it->seq == seq)
2915 break;
2916 else
2917 previous = it;
2920 if (remove && it) {
2921 if (previous == NULL)
2922 p->sent_messages = it->next;
2923 else
2924 previous->next = it->next;
2927 return it;
2930 static void gg_compat_message_cleanup(struct gg_session *sess)
2932 struct gg_session_private *p = sess->private_data;
2934 while (p->sent_messages) {
2935 gg_msg_list_t *next = p->sent_messages->next;
2936 free(p->sent_messages->recipients);
2937 free(p->sent_messages);
2938 p->sent_messages = next;
2942 static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients)
2944 struct gg_session_private *p = sess->private_data;
2945 gg_msg_list_t *sm;
2946 uin_t *new_recipients;
2947 size_t old_count, i;
2949 if (sess->protocol_version < GG_PROTOCOL_VERSION_110)
2950 return;
2952 if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT))
2953 return;
2955 sm = gg_compat_find_sent_message(sess, seq, 0);
2956 if (!sm) {
2957 sm = gg_new0(sizeof(gg_msg_list_t));
2958 if (!sm)
2959 return;
2960 sm->next = p->sent_messages;
2961 p->sent_messages = sm;
2964 sm->seq = seq;
2965 old_count = sm->recipients_count;
2966 sm->recipients_count += recipients_count;
2968 new_recipients = realloc(sm->recipients, sizeof(uin_t) * sm->recipients_count);
2969 if (new_recipients == NULL) {
2970 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
2971 "// gg_compat_message_sent() not enough memory\n");
2972 return;
2974 sm->recipients = new_recipients;
2976 for (i = 0; i < recipients_count; i++)
2977 sm->recipients[old_count + i] = recipients[i];
2980 void gg_compat_message_ack(struct gg_session *sess, int seq)
2982 gg_msg_list_t *sm;
2983 size_t i;
2985 if (sess->protocol_version < GG_PROTOCOL_VERSION_110)
2986 return;
2988 if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT))
2989 return;
2991 sm = gg_compat_find_sent_message(sess, seq, 1);
2992 if (!sm)
2993 return;
2995 for (i = 0; i < sm->recipients_count; i++) {
2996 struct gg_event *qev;
2998 qev = gg_eventqueue_add(sess);
3000 qev->type = GG_EVENT_ACK;
3001 qev->event.ack.status = GG_ACK_DELIVERED;
3002 qev->event.ack.recipient = sm->recipients[i];
3003 qev->event.ack.seq = seq;
3006 free(sm->recipients);
3007 free(sm);
3011 * Odbiera nowo utworzone gniazdo TCP/TLS.
3013 * Po wywołaniu tej funkcji należy zacząć obserwować deskryptor sesji (nawet
3014 * w przypadku niepowodzenia).
3016 * Jeżeli gniazdo nie zostanie obsłużone, należy je zniszczyć.
3018 * \param handle Uchwyt gniazda
3019 * \param priv Dane prywatne biblioteki libgadu
3020 * \param fd Deskryptor nowo utworzonego gniazda, lub -1 w przypadku błędu
3022 * \return Wartość różna od zera, jeżeli gniazdo zostało obsłużone, 0 w przeciwnym przypadku
3024 * \ingroup socketmanager
3026 int gg_socket_manager_connected(void *handle, void *priv, int fd)
3028 struct gg_session *sess = priv;
3029 struct gg_session_private *p = sess->private_data;
3031 if (p->socket_handle != handle) {
3032 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
3033 "// gg_socket_manager_connected() invalid handle\n");
3034 return 0;
3037 sess->fd = -1;
3039 if (fd < 0) {
3040 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
3041 "// gg_socket_manager_connected() connection error\n");
3042 p->socket_handle = NULL;
3043 gg_socket_manager_error(sess, GG_FAILURE_CONNECTING);
3044 return 0;
3047 if (p->socket_next_state == GG_STATE_TLS_NEGOTIATION) {
3048 if (gg_session_init_ssl(sess) == -1) {
3049 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
3050 "// gg_socket_manager_connected() couldn't "
3051 "initialize ssl\n");
3052 p->socket_handle = NULL;
3053 gg_socket_manager_error(sess, GG_FAILURE_TLS);
3054 return 0;
3058 p->socket_is_external = 1;
3059 sess->fd = fd;
3060 sess->timeout = GG_DEFAULT_TIMEOUT;
3061 sess->state = p->socket_next_state;
3063 gg_debug_session(sess, GG_DEBUG_MISC, "// next state=%s\n",
3064 gg_debug_state(p->socket_next_state));
3066 if (p->socket_next_state == GG_STATE_READING_KEY)
3067 sess->check = GG_CHECK_READ;
3068 else
3069 sess->check = GG_CHECK_WRITE;
3071 return 1;
3075 * Local variables:
3076 * c-indentation-style: k&r
3077 * c-basic-offset: 8
3078 * indent-tabs-mode: notnil
3079 * End:
3081 * vim: shiftwidth=8: