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,
28 * \brief Główny moduł biblioteki
44 #include "tvbuilder.h"
46 #include "packets.pb-c.h"
53 #ifdef GG_CONFIG_HAVE_GNUTLS
54 # include <gnutls/gnutls.h>
56 #ifdef GG_CONFIG_HAVE_OPENSSL
57 # include <openssl/err.h>
58 # include <openssl/rand.h>
62 * Port gniazda nasłuchującego dla połączeń bezpośrednich.
69 * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich.
73 unsigned long gg_dcc_ip
= 0;
76 * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia.
80 unsigned long gg_local_ip
= 0;
83 * Flaga włączenia połączeń przez serwer pośredniczący.
87 int gg_proxy_enabled
= 0;
90 * Adres serwera pośredniczącego.
94 char *gg_proxy_host
= NULL
;
97 * Port serwera pośredniczącego.
101 int gg_proxy_port
= 0;
104 * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
108 int gg_proxy_http_only
= 0;
111 * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
115 char *gg_proxy_username
= NULL
;
118 * Hasło użytkownika do autoryzacji serwera pośredniczącego.
122 char *gg_proxy_password
= NULL
;
127 static char rcsid
[] GG_UNUSED
= "$Id$";
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.
147 int gg_is_gpl_compliant(void)
154 * Zwraca wersję biblioteki.
156 * \return Wskaźnik na statyczny bufor z wersją biblioteki.
160 const char *gg_libgadu_version(void)
162 return GG_LIBGADU_VERSION
;
165 void * gg_new0(size_t size
)
171 gg_debug(GG_DEBUG_MISC
| GG_DEBUG_ERROR
,
172 "//gg_new0(%" GG_SIZE_FMT
173 ") not enough memory\n", size
);
177 memset(ptr
, 0, size
);
182 gg_required_proto(struct gg_session
*gs
, int protocol_version
)
184 if (gs
->protocol_version
>= protocol_version
)
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
);
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
));
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
;
225 for (x
= 0; *password
; password
++) {
226 x
= (x
& 0xffffff00) | *password
;
237 y
= (y
<< z
) | (y
>> (32 - z
));
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
;
261 #ifdef GG_CONFIG_HAVE_GNUTLS
262 if (sess
->ssl
!= NULL
) {
264 res
= gnutls_record_recv(GG_SESSION_GNUTLS(sess
), buf
, length
);
267 if (res
== GNUTLS_E_AGAIN
)
269 else if (!gnutls_error_is_fatal(res
) || res
== GNUTLS_E_INTERRUPTED
)
282 #ifdef GG_CONFIG_HAVE_OPENSSL
283 if (sess
->ssl
!= NULL
) {
287 res
= SSL_read(sess
->ssl
, buf
, length
);
290 err
= SSL_get_error(sess
->ssl
, res
);
292 if (err
== SSL_ERROR_SYSCALL
&& errno
== EINTR
)
295 if (err
== SSL_ERROR_WANT_READ
)
297 else if (err
!= SSL_ERROR_SYSCALL
)
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 "
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
);
326 gg_debug_session(sess
, GG_DEBUG_MISC
| GG_DEBUG_ERROR
,
327 "// gg_read() unexpected errno=%d\n", errno
);
334 res
= recv(sess
->fd
, buf
, length
, 0);
336 if (res
== -1 && errno
== EINTR
)
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
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
;
364 #ifdef GG_CONFIG_HAVE_GNUTLS
365 if (sess
->ssl
!= NULL
) {
367 res
= gnutls_record_send(GG_SESSION_GNUTLS(sess
), buf
, length
);
370 if (!gnutls_error_is_fatal(res
) || res
== GNUTLS_E_INTERRUPTED
)
373 if (res
== GNUTLS_E_AGAIN
)
386 #ifdef GG_CONFIG_HAVE_OPENSSL
387 if (sess
->ssl
!= NULL
) {
391 res
= SSL_write(sess
->ssl
, buf
, length
);
394 err
= SSL_get_error(sess
->ssl
, res
);
396 if (err
== SSL_ERROR_SYSCALL
&& errno
== EINTR
)
399 if (err
== SSL_ERROR_WANT_WRITE
)
401 else if (err
!= SSL_ERROR_SYSCALL
)
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");
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
);
430 gg_debug_session(sess
, GG_DEBUG_MISC
| GG_DEBUG_ERROR
,
431 "// gg_read() unexpected errno=%d\n", errno
);
439 res
= send(sess
->fd
, buf
, length
, 0);
441 if (res
== -1 && errno
== EINTR
)
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
)
466 while (written
< length
) {
467 res
= gg_write_common(sess
, buf
+ written
, length
- written
);
476 if (sess
->send_buf
== NULL
) {
477 res
= gg_write_common(sess
, buf
, length
);
479 if (res
== -1 && errno
== EAGAIN
)
488 if (!(tmp
= realloc(sess
->send_buf
, sess
->send_left
+ length
- res
))) {
493 sess
->send_buf
= tmp
;
495 memcpy(sess
->send_buf
+ sess
->send_left
, buf
+ res
, length
- res
);
497 sess
->send_left
+= length
- res
;
504 void gg_close(struct gg_session
*sess
)
506 struct gg_session_private
*p
= sess
->private_data
;
511 if (!p
->socket_is_external
) {
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
,
521 p
->socket_is_external
= 0;
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
);
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
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
;
572 gg_debug_session(sess
, GG_DEBUG_FUNCTION
, "** gg_recv_packet(%p);\n", sess
);
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");
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
);
598 ghlen
= gh
? gg_fix32(gh
->length
) : 0;
601 gg_debug_session(sess
, GG_DEBUG_ERROR
,
602 "// gg_recv_packet() invalid packet "
603 "length (%d)\n", ghlen
);
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");
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
);
625 gg_debug_session(sess
, GG_DEBUG_ERROR
, "// gg_recv_packet() connection broken\n");
629 if (res
== -1 && errno
== EAGAIN
) {
630 gg_debug_session(sess
, GG_DEBUG_NET
, "// gg_recv_packet() resource temporarily unavailable\n");
635 gg_debug_session(sess
, GG_DEBUG_ERROR
,
636 "// gg_recv_packet() read failed: errno=%d, "
637 "%s\n", errno
, strerror(errno
));
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
)) {
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
);
655 gg_debug_session(sess
, GG_DEBUG_ERROR
,
656 "// gg_recv_packet() invalid packet "
657 "length (%d)\n", ghlen
);
662 tmp
= realloc(sess
->recv_buf
, sizeof(struct gg_header
) + ghlen
+ 1);
665 gg_debug_session(sess
, GG_DEBUG_ERROR
, "// gg_recv_packet() out of memory\n");
669 sess
->recv_buf
= tmp
;
672 sess
->recv_done
+= res
;
675 packet
= sess
->recv_buf
;
676 sess
->recv_buf
= NULL
;
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
);
695 free(sess
->recv_buf
);
696 sess
->recv_buf
= 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
, ...)
721 unsigned int tmp_length
;
723 unsigned int payload_length
;
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");
738 payload
= va_arg(ap
, void *);
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");
754 memcpy(tmp
+ tmp_length
, payload
, payload_length
);
755 tmp_length
+= payload_length
;
757 payload
= va_arg(ap
, void *);
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
);
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
));
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
);
788 sess
->check
|= GG_CHECK_WRITE
;
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
)
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,
843 * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL
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
;
854 gg_debug(GG_DEBUG_FUNCTION
, "** gg_login(%p);\n", p
);
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
));
864 gg_debug(GG_DEBUG_MISC
, "// gg_login() not enough memory for session data\n");
868 memset(sess
, 0, sizeof(struct gg_session
));
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");
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");
887 if (!(sess
->password
= strdup(p
->password
))) {
888 gg_debug(GG_DEBUG_MISC
, "// gg_login() not enough memory for password\n");
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
);
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
) {
921 sess
->connect_host
= strdup(p
->connect_host
);
922 if (sess
->connect_host
== NULL
)
925 colon
= strchr(sess
->connect_host
, ':');
928 port
= atoi(colon
+ 1);
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
);
947 sess_private
->socket_manager_type
=
948 p
->socket_manager_type
;
949 memcpy(&sess_private
->socket_manager
,
951 sizeof(gg_socket_manager_t
));
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
)
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
;
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;
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
;
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
);
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");
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
) {
1031 sess
->ssl_flag
= p
->tls
;
1036 sess
->hash_type
= p
->hash_type
;
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
;
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
;
1053 if (sess
->connect_host
!= NULL
)
1054 sess
->server_addr
= 0;
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
)
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
;
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
;
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
;
1079 sess
->connect_port
[0] = GG_HTTPS_PORT
;
1080 sess
->connect_port
[1] = 0;
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
;
1094 while (!GG_SESSION_IS_CONNECTED(sess
)) {
1095 struct gg_event
*ge
;
1097 ge
= gg_watch_fd(sess
);
1100 gg_debug(GG_DEBUG_MISC
, "// gg_session_connect() critical error in gg_watch_fd()\n");
1104 if (ge
->type
== GG_EVENT_CONN_FAILED
) {
1106 gg_debug(GG_DEBUG_MISC
, "// gg_session_connect() could not login\n");
1114 struct gg_event
*ge
;
1116 ge
= gg_watch_fd(sess
);
1119 gg_debug(GG_DEBUG_MISC
, "// gg_session_connect() critical error in gg_watch_fd()\n");
1129 gg_free_session(sess
);
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
1141 * \param sess Struktura sesji
1143 * \return 0 jeśli się powiodło, -1 w przypadku błędu
1147 int gg_ping(struct gg_session
*sess
)
1149 gg_debug_session(sess
, GG_DEBUG_FUNCTION
, "** gg_ping(%p);\n", sess
);
1156 if (sess
->state
!= GG_STATE_CONNECTED
) {
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
1183 void gg_logoff(struct gg_session
*sess
)
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
);
1195 #ifdef GG_CONFIG_HAVE_OPENSSL
1196 if (sess
->ssl
!= NULL
)
1197 SSL_shutdown(sess
->ssl
);
1200 sess
->resolver_cleanup(&sess
->resolver
, 1);
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
1216 * \param sess Struktura sesji
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
);
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();
1252 #ifdef GG_CONFIG_HAVE_OPENSSL
1254 SSL_free(sess
->ssl
);
1257 SSL_CTX_free(sess
->ssl_ctx
);
1260 if (sess
->resolver_cleanup
!= NULL
)
1261 sess
->resolver_cleanup(&sess
->resolver
, 1);
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
)
1279 chat
= sess
->private_data
->chat_list
;
1280 while (chat
!= NULL
) {
1281 gg_chat_list_t
*next
= chat
->next
;
1282 free(chat
->participants
);
1287 gg_strarr_free(sess
->private_data
->host_white_list
);
1289 free(sess
->private_data
);
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
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
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
;
1327 int descr_null_len
= 0;
1330 gg_debug_session(sess
, GG_DEBUG_FUNCTION
, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess
, status
, descr
);
1337 if (sess
->state
!= GG_STATE_CONNECTED
) {
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);
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 */
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);
1371 res
= gg_send_packet(sess
, GG_NEW_STATUS80
,
1372 &p
, sizeof(p
), descr
, descr_len
,
1373 "\x00", descr_null_len
, NULL
);
1377 if (GG_S_NA(status
)) {
1378 sess
->state
= GG_STATE_DISCONNECTING
;
1379 sess
->timeout
= GG_TIMEOUT_DISCONNECT
;
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
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().
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
);
1428 sess
->status_flags
= flags
;
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
;
1442 char *html_message_gen
= NULL
, *plain_message_gen
= NULL
;
1443 const char *html_message
, *plain_message
;
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
)
1453 if ((recipient
== 0) == (chat_id
== 0))
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
,
1463 if (html_message_gen
== NULL
)
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
);
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
,
1480 if (plain_message_gen
== NULL
)
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
);
1495 msg
.has_recipient
= 1;
1496 gg_protobuf_set_uin(&msg
.recipient
, recipient
, NULL
);
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
;
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
))
1514 free(html_message_gen
);
1515 free(plain_message_gen
);
1517 return succ
? seq
: -1;
1521 gg_message_legacy_text_to_html(const char *src
, gg_encoding_t encoding
,
1522 const unsigned char *format
, size_t format_len
)
1527 if (format
== NULL
|| format_len
<= 3) {
1535 len
= gg_message_text_to_html(NULL
, src
, encoding
, format
, format_len
);
1537 dst
= malloc(len
+ 1);
1541 gg_message_text_to_html(dst
, src
, encoding
, format
, format_len
);
1547 * \internal Wysyła wiadomość.
1549 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
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.
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
;
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
);
1586 if (sess
->state
!= GG_STATE_CONNECTED
) {
1591 if ((message
== NULL
&& html_message
== NULL
) ||
1592 recipients_count
<= 0 || recipients_count
> 0xffff ||
1593 recipients
== NULL
|| (format
== NULL
&& formatlen
!= 0))
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
,
1612 if (formatted_msg
== NULL
)
1614 html_message
= (unsigned char*)formatted_msg
;
1618 seq_no
= gg_send_message_110(sess
, recipients
[0], 0,
1619 (const char*)(is_html
? html_message
: message
),
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");
1633 if (message
== NULL
) {
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
)
1646 generated_format
= malloc(fmt_len
+ 3);
1648 if (generated_format
== NULL
) {
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;
1662 gg_message_html_to_text(tmp_msg
, NULL
, NULL
, (const char*) html_message
, sess
->encoding
);
1668 if (sess
->encoding
!= GG_ENCODING_CP1250
) {
1669 cp_msg
= recoded_msg
= gg_encoding_convert(tmp_msg
, sess
->encoding
, GG_ENCODING_CP1250
, -1, -1);
1675 cp_msg
= recoded_msg
= tmp_msg
;
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);
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
)
1698 if (sess
->encoding
== GG_ENCODING_UTF8
) {
1699 utf_html_msg
= recoded_html_msg
= formatted_msg
;
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
)
1710 if (sess
->encoding
== GG_ENCODING_UTF8
) {
1711 utf_html_msg
= (const char*) html_message
;
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
)
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;
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
;
1744 r
.flag
= GG_MSG_OPTION_CONFERENCE
;
1745 r
.count
= gg_fix32(recipients_count
- 1);
1747 recps
= malloc(sizeof(uin_t
) * (recipients_count
- 1));
1754 for (i
= 0; i
< recipients_count
; i
++) {
1755 for (j
= 0, k
= 0; j
< recipients_count
; j
++) {
1757 recps
[k
] = gg_fix32(recipients
[j
]);
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)
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)
1789 free(recoded_html_msg
);
1790 free(generated_format
);
1793 gg_compat_message_sent(sess
, seq_no
, recipients_count
, recipients
);
1798 #endif /* DOXYGEN */
1801 * Wysyła wiadomość do użytkownika.
1803 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
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.
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
) {
1823 seq_no
= gg_send_message_110(sess
, recipient
, 0, (const char*)message
, 0);
1826 gg_compat_message_sent(sess
, seq_no
, 1, &recipient
);
1831 return gg_send_message_common(sess
, msgclass
, 1, &recipient
, message
,
1832 (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
1837 * Wysyła wiadomość formatowaną.
1839 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
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.
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ć
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.
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ć
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.
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",
1917 * Wysyła wiadomość formatowaną w ramach konferencji.
1919 * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
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.
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
,
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ć
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.
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.
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
);
2003 if (sess
->state
!= GG_STATE_CONNECTED
) {
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
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
;
2039 gg_debug_session(sess
, GG_DEBUG_FUNCTION
, "** gg_image_request(%p, %d, "
2040 "%u, 0x%.4x);\n", sess
, recipient
, size
, crc32
);
2047 if (sess
->state
!= GG_STATE_CONNECTED
) {
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
);
2068 struct gg_image_queue
*q
= malloc(sizeof(*q
));
2072 gg_debug_session(sess
, GG_DEBUG_MISC
,
2073 "// gg_image_request() not enough memory for "
2080 gg_debug_session(sess
, GG_DEBUG_MISC
, "// gg_image_request() not enough memory for image\n");
2085 memset(q
, 0, sizeof(*q
));
2087 q
->sender
= recipient
;
2095 struct gg_image_queue
*qq
;
2097 for (qq
= sess
->images
; qq
->next
; qq
= qq
->next
);
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
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
;
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
) {
2136 p
= sess
->private_data
;
2138 if (sess
->state
!= GG_STATE_CONNECTED
) {
2148 /* wytnij ścieżki, zostaw tylko nazwę pliku */
2149 while ((tmp
= strrchr(filename
, '/')) || (tmp
= strrchr(filename
, '\\')))
2152 if (strlen(filename
) < 1 || strlen(filename
) > 1024) {
2157 s
.recipient
= gg_fix32(recipient
);
2158 s
.seq
= gg_fix32(0);
2159 s
.msgclass
= gg_fix32(GG_CLASS_MSG
);
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
));
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
);
2187 it
= gg_new0(sizeof(gg_imgout_queue_t
));
2191 queue_end
->next
= it
;
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
;
2210 p
->imgout_queue
= queue
;
2212 gg_image_sendout(sess
);
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
;
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
,
2240 static int gg_notify105_ex(struct gg_session
*sess
, uin_t
*userlist
, char *types
, int count
)
2244 if (!userlist
|| !count
)
2245 return gg_send_packet(sess
, GG_NOTIFY105_LIST_EMPTY
, NULL
);
2248 gg_tvbuilder_t
*tvb
= gg_tvbuilder_new(sess
, NULL
);
2249 gg_tvbuilder_expected_size(tvb
, 2100);
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
);
2267 if (!gg_tvbuilder_send(tvb
, (i
< count
) ?
2268 GG_NOTIFY105_FIRST
: GG_NOTIFY105_LAST
))
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
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
2297 int gg_notify_ex(struct gg_session
*sess
, uin_t
*userlist
, char *types
, int count
)
2299 struct gg_notify
*n
;
2302 gg_debug_session(sess
, GG_DEBUG_FUNCTION
, "** gg_notify_ex(%p, %p, %p, %d);\n", sess
, userlist
, types
, count
);
2309 if (sess
->state
!= GG_STATE_CONNECTED
) {
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
);
2321 int part_count
, packet_type
;
2325 packet_type
= GG_NOTIFY_FIRST
;
2328 packet_type
= GG_NOTIFY_LAST
;
2331 if (!(n
= (struct gg_notify
*) malloc(sizeof(*n
) * part_count
)))
2334 for (i
= 0; i
< part_count
; i
++) {
2335 n
[i
].uin
= gg_fix32(userlist
[i
]);
2337 n
[i
].dunno1
= GG_USER_NORMAL
;
2339 n
[i
].dunno1
= types
[i
];
2342 if (gg_send_packet(sess
, packet_type
, n
, sizeof(*n
) * part_count
, NULL
) == -1) {
2348 count
-= part_count
;
2349 userlist
+= part_count
;
2351 types
+= part_count
;
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
2373 int gg_notify(struct gg_session
*sess
, uin_t
*userlist
, int count
)
2375 return gg_notify_ex(sess
, userlist
, NULL
, count
);
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
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
);
2402 if (sess
->state
!= GG_STATE_CONNECTED
) {
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
))
2418 struct gg_add_remove a
;
2420 a
.uin
= gg_fix32(uin
);
2423 return gg_send_packet(sess
, GG_ADD_NOTIFY
, &a
, sizeof(a
), NULL
);
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
2440 int gg_add_notify(struct gg_session
*sess
, uin_t uin
)
2442 return gg_add_notify_ex(sess
, uin
, GG_USER_NORMAL
);
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
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
);
2467 if (sess
->state
!= GG_STATE_CONNECTED
) {
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
))
2483 struct gg_add_remove a
;
2485 a
.uin
= gg_fix32(uin
);
2488 return gg_send_packet(sess
, GG_REMOVE_NOTIFY
, &a
, sizeof(a
), NULL
);
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
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
)
2540 if (sess
->state
!= GG_STATE_CONNECTED
) {
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)
2560 if (type
== GG_USERLIST_PUT
)
2561 type
= GG_USERLIST_PUT_MORE
;
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
;
2610 if (sess
->state
!= GG_STATE_CONNECTED
) {
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");
2630 ret
= gg_send_packet(sess
, GG_USERLIST100_REQUEST
, &pkt
, sizeof(pkt
), zrequest
, zrequest_len
, NULL
);
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
2648 int gg_typing_notification(struct gg_session
*sess
, uin_t recipient
, int length
){
2649 struct gg_typing_notification pkt
;
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
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
2688 int gg_chat_create(struct gg_session
*gs
)
2690 struct gg_chat_create pkt
;
2693 if (!gg_required_proto(gs
, GG_PROTOCOL_VERSION_110
))
2698 pkt
.seq
= gg_fix32(seq
);
2701 if (gg_send_packet(gs
, GG_CHAT_CREATE
, &pkt
, sizeof(pkt
), NULL
) == -1)
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
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
;
2726 struct gg_chat_participant
2731 struct gg_chat_participant
*participants_list
;
2732 size_t participants_list_size
;
2734 if (!gg_required_proto(gs
, GG_PROTOCOL_VERSION_110
))
2737 if (participants_count
== 0 || participants_count
>=
2738 ~(unsigned int)0 / sizeof(struct gg_chat_participant
))
2743 participants_list_size
= sizeof(struct gg_chat_participant
) *
2745 participants_list
= malloc(participants_list_size
);
2746 if (participants_list
== NULL
)
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
,
2761 participants_list
, participants_list_size
,
2763 free(participants_list
);
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
2780 int gg_chat_leave(struct gg_session
*gs
, uint64_t id
)
2782 struct gg_chat_leave pkt
;
2785 if (!gg_required_proto(gs
, GG_PROTOCOL_VERSION_110
))
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)
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
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
))
2816 return gg_send_message_110(gs
, 0, id
, message
, is_html
);
2822 * Sprawdza czy biblioteka obsługuje daną funkcję.
2824 * \param feature Identyfikator funkcji.
2826 * \return Wartość niezerowa jeśli funkcja jest obsłgiwana.
2830 int gg_libgadu_check_feature(gg_libgadu_feature_t feature
)
2834 case GG_LIBGADU_FEATURE_SSL
:
2835 #if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS)
2841 case GG_LIBGADU_FEATURE_PTHREAD
:
2842 #ifdef GG_CONFIG_HAVE_PTHREAD
2848 case GG_LIBGADU_FEATURE_USERLIST100
:
2849 #ifdef GG_CONFIG_HAVE_ZLIB
2855 /* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */
2862 static void gg_socket_manager_error(struct gg_session
*sess
, enum gg_failure_t failure
)
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
,
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
,
2890 int gg_compat_feature_is_enabled(struct gg_session
*sess
, gg_compat_feature_t feature
)
2897 level
= sess
->private_data
->compatibility
;
2900 case GG_COMPAT_FEATURE_ACK_EVENT
:
2901 case GG_COMPAT_FEATURE_LEGACY_CONFER
:
2902 return (level
< GG_COMPAT_1_12_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
) {
2921 if (previous
== NULL
)
2922 p
->sent_messages
= it
->next
;
2924 previous
->next
= it
->next
;
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
;
2946 uin_t
*new_recipients
;
2947 size_t old_count
, i
;
2949 if (sess
->protocol_version
< GG_PROTOCOL_VERSION_110
)
2952 if (!gg_compat_feature_is_enabled(sess
, GG_COMPAT_FEATURE_ACK_EVENT
))
2955 sm
= gg_compat_find_sent_message(sess
, seq
, 0);
2957 sm
= gg_new0(sizeof(gg_msg_list_t
));
2960 sm
->next
= p
->sent_messages
;
2961 p
->sent_messages
= sm
;
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");
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
)
2985 if (sess
->protocol_version
< GG_PROTOCOL_VERSION_110
)
2988 if (!gg_compat_feature_is_enabled(sess
, GG_COMPAT_FEATURE_ACK_EVENT
))
2991 sm
= gg_compat_find_sent_message(sess
, seq
, 1);
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
);
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");
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
);
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
);
3058 p
->socket_is_external
= 1;
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
;
3069 sess
->check
= GG_CHECK_WRITE
;
3076 * c-indentation-style: k&r
3078 * indent-tabs-mode: notnil
3081 * vim: shiftwidth=8: