Mercurial > pidgin.yaz
diff libpurple/protocols/gg/lib/libgadu.c @ 29938:6359fde67f4c
Update our internal libgadu to 1.9.0-rc2. This does not yet build on Windows.
Refs #10542. The Windows build errors are the only reason this isn't on
`im.pidgin.pidgin` already.
author | John Bailey <rekkanoryo@rekkanoryo.org> |
---|---|
date | Sun, 21 Feb 2010 16:52:42 +0000 |
parents | 259bbfb423d4 |
children | db6735e579f8 |
line wrap: on
line diff
--- a/libpurple/protocols/gg/lib/libgadu.c Sun Feb 21 00:11:56 2010 +0000 +++ b/libpurple/protocols/gg/lib/libgadu.c Sun Feb 21 16:52:42 2010 +0000 @@ -1,10 +1,11 @@ -/* $Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $ */ /* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wo糿y <speedy@ziew.org> - * Arkadiusz Mi秌iewicz <arekm@pld-linux.org> - * Tomasz Chili駍ki <chilek@chilan.com> + * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Wo藕ny <speedy@ziew.org> + * Arkadiusz Mi艣kiewicz <arekm@pld-linux.org> + * Tomasz Chili艅ski <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -17,227 +18,187 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ -#include "libgadu.h" +/** + * \file libgadu.c + * + * \brief G艂贸wny modu艂 biblioteki + */ #include <sys/types.h> -#ifndef _WIN32 -#include <sys/wait.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #ifdef sun # include <sys/filio.h> #endif -#else -#include <io.h> -#include <fcntl.h> -#include <errno.h> -#define SHUT_RDWR SD_BOTH -#endif -#include "libgadu-config.h" +#include "compat.h" +#include "libgadu.h" +#include "protocol.h" +#include "resolver.h" +#include "libgadu-internal.h" #include <errno.h> -#ifndef _WIN32 #include <netdb.h> -#endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <signal.h> +#include <time.h> #include <unistd.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL # include <openssl/err.h> # include <openssl/rand.h> #endif -#include "compat.h" +#define GG_LIBGADU_VERSION "1.9.0-rc2" + +/** + * Poziom rejestracji informacji odpluskwiaj膮cych. Zmienna jest mask膮 bitow膮 + * sk艂adaj膮c膮 si臋 ze sta艂ych \c GG_DEBUG_... + * + * \ingroup debug + */ +int gg_debug_level = 0; -int gg_debug_level = 0; +/** + * Funkcja, do kt贸rej s膮 przekazywane informacje odpluskwiaj膮ce. Je艣li zar贸wno + * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, s膮 r贸wne + * \c NULL, informacje s膮 wysy艂ane do standardowego wyj艣cia b艂臋du (\c stderr). + * + * \param level Poziom rejestracji + * \param format Format wiadomo艣ci (zgodny z \c printf) + * \param ap Lista argument贸w (zgodna z \c printf) + * + * \note Funkcja jest przes艂aniana przez \c gg_debug_handler_session. + * + * \ingroup debug + */ void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; +/** + * Funkcja, do kt贸rej s膮 przekazywane informacje odpluskwiaj膮ce. Je艣li zar贸wno + * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, s膮 r贸wne + * \c NULL, informacje s膮 wysy艂ane do standardowego wyj艣cia b艂臋du. + * + * \param sess Sesja kt贸rej dotyczy informacja lub \c NULL + * \param level Poziom rejestracji + * \param format Format wiadomo艣ci (zgodny z \c printf) + * \param ap Lista argument贸w (zgodna z \c printf) + * + * \note Funkcja przes艂ania przez \c gg_debug_handler_session. + * + * \ingroup debug + */ +void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL; + +/** + * Port gniazda nas艂uchuj膮cego dla po艂膮cze艅 bezpo艣rednich. + * + * \ingroup ip + */ int gg_dcc_port = 0; + +/** + * Adres IP gniazda nas艂uchuj膮cego dla po艂膮cze艅 bezpo艣rednich. + * + * \ingroup ip + */ unsigned long gg_dcc_ip = 0; +/** + * Adres lokalnego interfejsu IP, z kt贸rego wywo艂ywane s膮 wszystkie po艂膮czenia. + * + * \ingroup ip + */ unsigned long gg_local_ip = 0; -/* - * zmienne opisuj眂e parametry proxy http. + +/** + * Flaga w艂膮czenia po艂膮cze艅 przez serwer po艣rednicz膮cy. + * + * \ingroup proxy + */ +int gg_proxy_enabled = 0; + +/** + * Adres serwera po艣rednicz膮cego. + * + * \ingroup proxy */ char *gg_proxy_host = NULL; + +/** + * Port serwera po艣rednicz膮cego. + * + * \ingroup proxy + */ int gg_proxy_port = 0; -int gg_proxy_enabled = 0; + +/** + * Flaga u偶ywania serwera po艣rednicz膮cego jedynie dla us艂ug HTTP. + * + * \ingroup proxy + */ int gg_proxy_http_only = 0; + +/** + * Nazwa u偶ytkownika do autoryzacji serwera po艣rednicz膮cego. + * + * \ingroup proxy + */ char *gg_proxy_username = NULL; + +/** + * Has艂o u偶ytkownika do autoryzacji serwera po艣rednicz膮cego. + * + * \ingroup proxy + */ char *gg_proxy_password = NULL; -#ifndef lint +#ifndef DOXYGEN + +#ifndef lint static char rcsid[] #ifdef __GNUC__ __attribute__ ((unused)) #endif -= "$Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $"; -#endif - -#ifdef _WIN32 -/** - * Deal with the fact that you can't select() on a win32 file fd. - * This makes it practically impossible to tie into purple's event loop. - * - * -This is thanks to Tor Lillqvist. - * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. - */ -static int -socket_pipe (int *fds) -{ - SOCKET temp, socket1 = -1, socket2 = -1; - struct sockaddr_in saddr; - int len; - u_long arg; - fd_set read_set, write_set; - struct timeval tv; - - temp = socket(AF_INET, SOCK_STREAM, 0); - - if (temp == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { - goto out0; - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { - goto out0; - } - - if (listen(temp, 1) == SOCKET_ERROR) { - goto out0; - } - - len = sizeof(saddr); - if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { - goto out0; - } - - socket1 = socket(AF_INET, SOCK_STREAM, 0); - - if (socket1 == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out1; - } - - if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || - WSAGetLastError() != WSAEWOULDBLOCK) { - goto out1; - } - - FD_ZERO(&read_set); - FD_SET(temp, &read_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { - goto out1; - } - - if (!FD_ISSET(temp, &read_set)) { - goto out1; - } - - socket2 = accept(temp, (struct sockaddr *) &saddr, &len); - if (socket2 == INVALID_SOCKET) { - goto out1; - } - - FD_ZERO(&write_set); - FD_SET(socket1, &write_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { - goto out2; - } - - if (!FD_ISSET(socket1, &write_set)) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - fds[0] = socket1; - fds[1] = socket2; - - closesocket (temp); - - return 0; - -out2: - closesocket (socket2); -out1: - closesocket (socket1); -out0: - closesocket (temp); - errno = EIO; /* XXX */ - - return -1; -} += "$Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $"; #endif -/* - * gg_libgadu_version() +#endif /* DOXYGEN */ + +/** + * Zwraca wersj臋 biblioteki. * - * zwraca wersj libgadu. + * \return Wska藕nik na statyczny bufor z wersj膮 biblioteki. * - * - brak - * - * wersja libgadu. + * \ingroup version */ const char *gg_libgadu_version() { return GG_LIBGADU_VERSION; } -/* - * gg_fix32() +/** + * \internal Zamienia kolejno艣膰 bajt贸w w 32-bitowym s艂owie. + * + * Ze wzgl臋du na little-endianowo艣膰 protoko艂u Gadu-Gadu, na maszynach + * big-endianowych odwraca kolejno艣膰 bajt贸w w s艂owie. * - * zamienia kolejno舵 bajt體 w liczbie 32-bitowej tak, by odpowiada砤 - * kolejno禼i bajt體 w protokole GG. ze wzgl阣u na LE-owo舵 serwera, - * zamienia tylko na maszynach BE-wych. + * \param x Liczba do zamiany * - * - x - liczba do zamiany + * \return Liczba z odpowiedni膮 kolejno艣ci膮 bajt贸w * - * liczba z odpowiedni kolejno禼i bajt體. + * \ingroup helper */ uint32_t gg_fix32(uint32_t x) { -#ifndef __GG_LIBGADU_BIGENDIAN +#ifndef GG_CONFIG_BIGENDIAN return x; #else return (uint32_t) @@ -248,20 +209,21 @@ #endif } -/* - * gg_fix16() +/** + * \internal Zamienia kolejno艣膰 bajt贸w w 16-bitowym s艂owie. + * + * Ze wzgl臋du na little-endianowo艣膰 protoko艂u Gadu-Gadu, na maszynach + * big-endianowych zamienia kolejno艣膰 bajt贸w w s艂owie. * - * zamienia kolejno舵 bajt體 w liczbie 16-bitowej tak, by odpowiada砤 - * kolejno禼i bajt體 w protokole GG. ze wzgl阣u na LE-owo舵 serwera, - * zamienia tylko na maszynach BE-wych. + * \param x Liczba do zamiany * - * - x - liczba do zamiany + * \return Liczba z odpowiedni膮 kolejno艣ci膮 bajt贸w * - * liczba z odpowiedni kolejno禼i bajt體. + * \ingroup helper */ uint16_t gg_fix16(uint16_t x) { -#ifndef __GG_LIBGADU_BIGENDIAN +#ifndef GG_CONFIG_BIGENDIAN return x; #else return (uint16_t) @@ -270,15 +232,13 @@ #endif } -/* - * gg_login_hash() // funkcja wewn阾rzna - * - * liczy hash z has砤 i danego seeda. - * - * - password - has硂 do hashowania - * - seed - warto舵 podana przez serwer +/** + * \internal Liczy skr贸t z has艂a i ziarna. * - * hash. + * \param password Has艂o + * \param seed Ziarno podane przez serwer + * + * \return Warto艣膰 skr贸tu */ unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) { @@ -304,313 +264,22 @@ return y; } -#ifndef _WIN32 - -/* - * gg_resolve() // funkcja wewn阾rzna - * - * tworzy potok, forkuje si i w drugim procesie zaczyna resolvowa - * podanego hosta. zapisuje w sesji deskryptor potoku. je秎i co tam - * b阣zie gotowego, znaczy, 縠 mo縩a wczyta struct in_addr. je秎i - * nie znajdzie, zwraca INADDR_NONE. - * - * - fd - wska糿ik gdzie wrzuci deskryptor - * - pid - gdzie wrzuci pid procesu potomnego - * - hostname - nazwa hosta do zresolvowania +/** + * \internal Odbiera od serwera dane binarne. * - * 0, -1. - */ -int gg_resolve(int *fd, int *pid, const char *hostname) -{ - int pipes[2], res; - struct in_addr a; - int errno2; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname); - - if (!fd || !pid) { - errno = EFAULT; - return -1; - } - - if (pipe(pipes) == -1) - return -1; - - if ((res = fork()) == -1) { - errno2 = errno; - close(pipes[0]); - close(pipes[1]); - errno = errno2; - return -1; - } - - if (!res) { - if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(pipes[1], &a, sizeof(a)); - - _exit(0); - } - - close(pipes[1]); - - *fd = pipes[0]; - *pid = res; - - return 0; -} -#endif - -#ifdef __GG_LIBGADU_HAVE_PTHREAD - -struct gg_resolve_pthread_data { - char *hostname; - int fd; -}; - -static void *gg_resolve_pthread_thread(void *arg) -{ - struct gg_resolve_pthread_data *d = arg; - struct in_addr a; - - pthread_detach(pthread_self()); - - if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(d->hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(d->fd, &a, sizeof(a)); - close(d->fd); - - free(d->hostname); - d->hostname = NULL; - - free(d); - - pthread_exit(NULL); - - return NULL; /* 縠by kompilator nie marudzi */ -} - -/* - * gg_resolve_pthread() // funkcja wewn阾rzna - * - * tworzy potok, nowy w眛ek i w nim zaczyna resolvowa podanego hosta. - * zapisuje w sesji deskryptor potoku. je秎i co tam b阣zie gotowego, - * znaczy, 縠 mo縩a wczyta struct in_addr. je秎i nie znajdzie, zwraca - * INADDR_NONE. - * - * - fd - wska糿ik do zmiennej przechowuj眂ej desktyptor resolvera - * - resolver - wska糿ik do wska糿ika resolvera - * - hostname - nazwa hosta do zresolvowania + * Funkcja odbiera dane od serwera zajmuj膮c si臋 TLS w razie konieczno艣ci. * - * 0, -1. - */ -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) -{ - struct gg_resolve_pthread_data *d = NULL; - pthread_t *tmp; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname); - - if (!resolver || !fd || !hostname) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (!(tmp = malloc(sizeof(pthread_t)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n"); - return -1; - } - - if (pipe(pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); - free(tmp); - return -1; - } - - if (!(d = malloc(sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->hostname = NULL; - - if (!(d->hostname = strdup(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->fd = pipes[1]; - - if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n"); - new_errno = errno; - goto cleanup; - } - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp); - - *resolver = tmp; - - *fd = pipes[0]; - - return 0; - -cleanup: - if (d) { - free(d->hostname); - free(d); - } - - close(pipes[0]); - close(pipes[1]); - - free(tmp); - - errno = new_errno; - - return -1; -} - -#elif defined _WIN32 - -struct gg_resolve_win32thread_data { - char *hostname; - int fd; -}; - -static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) -{ - struct gg_resolve_win32thread_data *d = arg; - struct in_addr a; - - if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(d->hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(d->fd, &a, sizeof(a)); - close(d->fd); - - free(d->hostname); - d->hostname = NULL; - - free(d); - - return 0; -} - - -int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) -{ - struct gg_resolve_win32thread_data *d = NULL; - HANDLE h; - DWORD dwTId; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); - - if (!resolver || !fd || !hostname) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (socket_pipe(pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); - return -1; - } - - if (!(d = malloc(sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); - new_errno = GetLastError(); - goto cleanup; - } - - d->hostname = NULL; - - if (!(d->hostname = strdup(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); - new_errno = GetLastError(); - goto cleanup; - } - - d->fd = pipes[1]; - - h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, - d, 0, &dwTId); - - if (h == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); - new_errno = GetLastError(); - goto cleanup; - } - - *resolver = h; - *fd = pipes[0]; - - return 0; - -cleanup: - if (d) { - free(d->hostname); - free(d); - } - - close(pipes[0]); - close(pipes[1]); - - errno = new_errno; - - return -1; - -} -#endif - -/* - * gg_read() // funkcja pomocnicza + * \param sess Struktura sesji + * \param buf Bufor na danymi + * \param length D艂ugo艣膰 bufora * - * czyta z gniazda okre秎on ilo舵 bajt體. bierze pod uwag, czy mamy - * po潮czenie zwyk砮 czy TLS. - * - * - sess - sesja, - * - buf - bufor, - * - length - ilo舵 bajt體, - * - * takie same warto禼i jak read(). + * \return To samo co funkcja systemowa \c read */ int gg_read(struct gg_session *sess, char *buf, int length) { int res; -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { int err; @@ -631,23 +300,22 @@ return res; } -/* - * gg_write() // funkcja pomocnicza +/** + * \internal Wysy艂a do serwera dane binarne. * - * zapisuje do gniazda okre秎on ilo舵 bajt體. bierze pod uwag, czy mamy - * po潮czenie zwyk砮 czy TLS. + * Funkcja wysy艂a dane do serwera zajmuj膮c si臋 TLS w razie konieczno艣ci. * - * - sess - sesja, - * - buf - bufor, - * - length - ilo舵 bajt體, + * \param sess Struktura sesji + * \param buf Bufor z danymi + * \param length D艂ugo艣膰 bufora * - * takie same warto禼i jak write(). + * \return To samo co funkcja systemowa \c write */ int gg_write(struct gg_session *sess, const char *buf, int length) { int res = 0; -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { int err; @@ -664,19 +332,48 @@ } else #endif { - int written = 0; - - while (written < length) { - res = write(sess->fd, buf + written, length - written); + if (!sess->async) { + int written = 0; + + while (written < length) { + res = write(sess->fd, buf + written, length - written); + + if (res == -1) { + if (errno != EINTR) + break; + + continue; + } + + written += res; + res = written; + } + } else { + if (!sess->send_buf) + res = write(sess->fd, buf, length); + else + res = 0; if (res == -1) { - if (errno == EAGAIN) - continue; - else - break; - } else { - written += res; - res = written; + if (errno != EAGAIN) + return res; + + res = 0; + } + + if (res < length) { + char *tmp; + + if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) { + errno = ENOMEM; + return -1; + } + + sess->send_buf = tmp; + + memcpy(sess->send_buf + sess->send_left, buf + res, length - res); + + sess->send_left += length - res; } } } @@ -684,26 +381,29 @@ return res; } -/* - * gg_recv_packet() // funkcja wewn阾rzna +/** + * \internal Odbiera pakiet od serwera. * - * odbiera jeden pakiet i zwraca wska糿ik do niego. pami赕 po nim - * nale縴 zwolni za pomoc free(). + * Funkcja odczytuje nag艂贸wek pakietu, a nast臋pnie jego zawarto艣膰 i zwraca + * w zaalokowanym buforze. * - * - sess - opis sesji + * Przy po艂膮czeniach asynchronicznych, funkcja mo偶e nie by膰 w stanie + * skompletowa膰 ca艂ego pakietu -- w takim przypadku zwr贸ci -1, a kodem b艂臋du + * b臋dzie \c EAGAIN. * - * w przypadku b酬du NULL, kod b酬du w errno. nale縴 zwr骳i uwag, 縠 gdy - * po潮czenie jest nieblokuj眂e, a kod b酬du wynosi EAGAIN, nie uda硂 si - * odczyta ca砮go pakietu i nie nale縴 tego traktowa jako b潮d. + * \param sess Struktura sesji + * + * \return Wska藕nik do zaalokowanego bufora */ void *gg_recv_packet(struct gg_session *sess) { struct gg_header h; char *buf = NULL; - int ret = 0, offset, size = 0; + int ret = 0; + unsigned int offset, size = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); + if (!sess) { errno = EFAULT; return NULL; @@ -712,7 +412,7 @@ if (sess->recv_left < 1) { if (sess->header_buf) { memcpy(&h, sess->header_buf, sess->header_done); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); free(sess->header_buf); sess->header_buf = NULL; } else @@ -721,34 +421,36 @@ while (sess->header_done < sizeof(h)) { ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); if (!ret) { errno = ECONNRESET; - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); return NULL; } if (ret == -1) { if (errno == EINTR) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); continue; } if (errno == EAGAIN) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); if (!(sess->header_buf = malloc(sess->header_done))) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); return NULL; } memcpy(sess->header_buf, &h, sess->header_done); + errno = EAGAIN; + return NULL; } - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); return NULL; } @@ -761,22 +463,22 @@ h.length = gg_fix32(h.length); } else memcpy(&h, sess->recv_buf, sizeof(h)); - - /* jakie sensowne limity na rozmiar pakietu */ + + /* jakie艣 sensowne limity na rozmiar pakietu */ if (h.length > 65535) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); errno = ERANGE; return NULL; } if (sess->recv_left > 0) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); size = sess->recv_left; offset = sess->recv_done; buf = sess->recv_buf; } else { if (!(buf = malloc(sizeof(h) + h.length + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); return NULL; } @@ -788,24 +490,23 @@ while (size > 0) { ret = gg_read(sess, buf + sizeof(h) + offset, size); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); if (!ret) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); - free(buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); errno = ECONNRESET; return NULL; } if (ret > -1 && ret <= size) { offset += ret; size -= ret; - } else if (ret == -1) { + } else if (ret == -1) { int errno2 = errno; - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); errno = errno2; if (errno == EAGAIN) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); sess->recv_buf = buf; sess->recv_left = size; sess->recv_done = offset; @@ -823,49 +524,45 @@ if ((gg_debug_level & GG_DEBUG_DUMP)) { unsigned int i; - gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); - for (i = 0; i < sizeof(h) + h.length; i++) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); + for (i = 0; i < sizeof(h) + h.length; i++) + gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); + gg_debug_session(sess, GG_DEBUG_DUMP, "\n"); } return buf; } -/* - * gg_send_packet() // funkcja wewn阾rzna +/** + * \internal Wysy艂a pakiet do serwera. * - * konstruuje pakiet i wysy砤 go do serwera. + * Funkcja konstruuje pakiet do wys艂ania z dowolnej liczby fragment贸w. Je艣li + * rozmiar pakietu jest za du偶y, by m贸c go wys艂a膰 za jednym razem, pozosta艂a + * cz臋艣膰 zostanie zakolejkowana i wys艂ana, gdy b臋dzie to mo偶liwe. * - * - sock - deskryptor gniazda - * - type - typ pakietu - * - payload_1 - pierwsza cz甓 pakietu - * - payload_length_1 - d硊go舵 pierwszej cz甓ci - * - payload_2 - druga cz甓 pakietu - * - payload_length_2 - d硊go舵 drugiej cz甓ci - * - ... - kolejne cz甓ci pakietu i ich d硊go禼i - * - NULL - ko馽owym parametr (konieczny!) + * \param sess Struktura sesji + * \param type Rodzaj pakietu + * \param ... Lista kolejnych cz臋艣ci pakietu (wska藕nik na bufor i d艂ugo艣膰 + * typu \c int) zako艅czona \c NULL * - * je秎i si powiod硂, zwraca 0, w przypadku b酬du -1. je秎i errno == ENOMEM, - * zabrak硂 pami阠i. inaczej by b潮d przy wysy砤niu pakietu. dla errno == 0 - * nie wys砤no ca砮go pakietu. + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du */ int gg_send_packet(struct gg_session *sess, int type, ...) { struct gg_header *h; char *tmp; - int tmp_length; + unsigned int tmp_length; void *payload; unsigned int payload_length; va_list ap; int res; - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type); tmp_length = sizeof(struct gg_header); if (!(tmp = malloc(tmp_length))) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); return -1; } @@ -879,14 +576,14 @@ payload_length = va_arg(ap, unsigned int); if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); free(tmp); va_end(ap); return -1; } tmp = tmp2; - + memcpy(tmp + tmp_length, payload, payload_length); tmp_length += payload_length; @@ -900,54 +597,83 @@ h->length = gg_fix32(tmp_length - sizeof(struct gg_header)); if ((gg_debug_level & GG_DEBUG_DUMP)) { - int i; + unsigned int i; - gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); for (i = 0; i < tmp_length; ++i) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); + gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); + gg_debug_session(sess, GG_DEBUG_DUMP, "\n"); } - - if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); - free(tmp); + + res = gg_write(sess, tmp, tmp_length); + + free(tmp); + + if (res == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); return -1; } - - free(tmp); + + if (sess->async) + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() partial write(), %d sent, %d left, %d total left\n", res, tmp_length - res, sess->send_left); + + if (sess->send_buf) + sess->check |= GG_CHECK_WRITE; + return 0; } -/* - * gg_session_callback() // funkcja wewn阾rzna +/** + * \internal Funkcja zwrotna sesji. + * + * Pole \c callback struktury \c gg_session zawiera wska藕nik do tej funkcji. + * Wywo艂uje ona \c gg_watch_fd i zachowuje wynik w polu \c event. + * + * \note Korzystanie z tej funkcjonalno艣ci nie jest ju偶 zalecane. * - * wywo硑wany z gg_session->callback, wykonuje gg_watch_fd() i pakuje - * do gg_session->event jego wynik. + * \param sess Struktura sesji + * + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du */ -static int gg_session_callback(struct gg_session *s) +static int gg_session_callback(struct gg_session *sess) { - if (!s) { + if (!sess) { errno = EFAULT; return -1; } - return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1; + return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1; } -/* - * gg_login() +/** + * 艁膮czy si臋 z serwerem Gadu-Gadu. * - * rozpoczyna procedur 潮czenia si z serwerem. reszt obs硊guje si przez - * gg_watch_fd(). + * Przy po艂膮czeniu synchronicznym funkcja zako艅czy dzia艂anie po nawi膮zaniu + * po艂膮czenia lub gdy wyst膮pi b艂膮d. Po udanym po艂膮czeniu nale偶y wywo艂ywa膰 + * funkcj臋 \c gg_watch_fd(), kt贸ra odbiera informacje od serwera i zwraca + * informacje o zdarzeniach. * - * UWAGA! program musi obs硊縴 SIGCHLD, je秎i 潮czy si asynchronicznie, - * 縠by poprawnie zamkn辨 proces resolvera. + * Przy po艂膮czeniu asynchronicznym funkcja rozpocznie procedur臋 po艂膮czenia + * i zwr贸ci zaalokowan膮 struktur臋. Pole \c fd struktury \c gg_session zawiera + * deskryptor, kt贸ry nale偶y obserwowa膰 funkcj膮 \c select, \c poll lub za + * pomoc膮 mechanizm贸w u偶ytej p臋tli zdarze艅 (Glib, Qt itp.). Pole \c check + * jest mask膮 bitow膮 m贸wi膮c膮, czy biblioteka chce by膰 informowana o mo偶liwo艣ci + * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE). + * Po zaobserwowaniu zmian na deskryptorze nale偶y wywo艂a膰 funkcj臋 + * \c gg_watch_fd(). Podczas korzystania z po艂膮cze艅 asynchronicznych, w trakcie + * po艂膮czenia mo偶e zosta膰 stworzony dodatkowy proces rozwi膮zuj膮cy nazw臋 + * serwera -- z tego powodu program musi poprawnie obs艂u偶y膰 sygna艂 SIGCHLD. * - * - p - struktura opisuj眂a pocz眛kowy stan. wymagane pola: uin, - * password + * \note Po nawi膮zaniu po艂膮czenia z serwerem nale偶y wys艂a膰 list臋 kontakt贸w + * za pomoc膮 funkcji \c gg_notify() lub \c gg_notify_ex(). * - * w przypadku b酬du NULL, je秎i idzie dobrze (async) albo posz硂 - * dobrze (sync), zwr骳i wska糿ik do zaalokowanej struct gg_session. + * \param p Struktura opisuj膮ca parametry po艂膮czenia. Wymagane pola: uin, + * password, async. + * + * \return Wska藕nik do zaalokowanej struktury sesji \c gg_session lub NULL + * w przypadku b艂臋du. + * + * \ingroup login */ struct gg_session *gg_login(const struct gg_login_params *p) { @@ -981,8 +707,9 @@ goto fail; } - if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) { - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); + if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type); + errno = EFAULT; goto fail; } @@ -999,18 +726,59 @@ sess->server_addr = p->server_addr; sess->external_port = p->external_port; sess->external_addr = p->external_addr; + + sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77)); + + if (!(p->protocol_features & GG_FEATURE_STATUS77)) + sess->protocol_features |= GG_FEATURE_STATUS80; + + if (!(p->protocol_features & GG_FEATURE_MSG77)) + sess->protocol_features |= GG_FEATURE_MSG80; + sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; + if (p->era_omnix) - sess->protocol_version |= GG_ERA_OMNIX_MASK; + sess->protocol_flags |= GG_ERA_OMNIX_MASK; if (p->has_audio) - sess->protocol_version |= GG_HAS_AUDIO_MASK; + sess->protocol_flags |= GG_HAS_AUDIO_MASK; sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL; sess->last_sysmsg = p->last_sysmsg; sess->image_size = p->image_size; sess->pid = -1; + sess->encoding = p->encoding; + + if (gg_session_set_resolver(sess, p->resolver) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unsupported resolver type (%d)\n", p->resolver); + errno = EFAULT; + goto fail; + } + + if (p->status_descr) { + int max_length; + + if (sess->protocol_version >= 0x2d) + max_length = GG_STATUS_DESCR_MAXSIZE; + else + max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0; + + if (sess->protocol_version >= 0x2d && p->encoding != GG_ENCODING_UTF8) + sess->initial_descr = gg_cp_to_utf8(p->status_descr); + else + sess->initial_descr = strdup(p->status_descr); + + if (!sess->initial_descr) { + gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); + goto fail; + } + + // XXX pami臋ta膰, 偶eby nie ci膮膰 w 艣rodku znaku utf-8 + + if (strlen(sess->initial_descr) > max_length) + sess->initial_descr[max_length] = 0; + } if (p->tls == 1) { -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL char buf[1024]; OpenSSL_add_ssl_algorithms(); @@ -1023,7 +791,7 @@ } rstruct; time(&rstruct.time); - rstruct.ptr = (void *) &rstruct; + rstruct.ptr = (void *) &rstruct; RAND_seed((void *) rdata, sizeof(rdata)); RAND_seed((void *) &rstruct, sizeof(rstruct)); @@ -1050,7 +818,7 @@ gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); #endif } - + if (gg_proxy_enabled) { hostname = gg_proxy_host; sess->proxy_port = port = gg_proxy_port; @@ -1059,37 +827,50 @@ port = GG_APPMSG_PORT; } - if (!p->async) { - struct in_addr a; + if (p->hash_type) + sess->hash_type = p->hash_type; + else + sess->hash_type = GG_LOGIN_HASH_SHA1; - if (!p->server_addr || !p->server_port) { - if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(hostname))) { + if (!p->async) { + struct in_addr addr; + + if (!sess->server_addr) { + if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { + if (gg_gethostbyname_real(hostname, &addr, 0) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); goto fail; - } else { - a.s_addr = hn->s_addr; - free(hn); } } } else { - a.s_addr = p->server_addr; - port = p->server_port; + addr.s_addr = sess->server_addr; + port = sess->port; } - sess->hub_addr = a.s_addr; + sess->hub_addr = addr.s_addr; if (gg_proxy_enabled) - sess->proxy_addr = a.s_addr; + sess->proxy_addr = addr.s_addr; + + if ((sess->fd = gg_connect(&addr, port, 0)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + + /* nie wysz艂o? pr贸bujemy portu 443. */ + if (sess->server_addr) { + sess->port = GG_HTTPS_PORT; - if ((sess->fd = gg_connect(&a, port, 0)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail; + if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, 0)) == -1) { + /* ostatnia deska ratunku zawiod艂a? + * w takim razie zwijamy manatki. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail; + } + } else { + goto fail; + } } - if (p->server_addr && p->server_port) + if (sess->server_addr) sess->state = GG_STATE_CONNECTING_GG; else sess->state = GG_STATE_CONNECTING_HUB; @@ -1114,15 +895,9 @@ return sess; } - + if (!sess->server_addr || gg_proxy_enabled) { -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) { -#elif defined _WIN32 - if (gg_resolve_win32thread(&sess->fd, &sess->resolver, hostname)) { -#else - if (gg_resolve(&sess->fd, &sess->pid, hostname)) { -#endif + if (sess->resolver_start(&sess->fd, &sess->resolver, hostname) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail; } @@ -1133,49 +908,121 @@ } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; + sess->soft_timeout = 1; } return sess; fail: if (sess) { - if (sess->password) - free(sess->password); - if (sess->initial_descr) - free(sess->initial_descr); + free(sess->password); + free(sess->initial_descr); free(sess); } - + return NULL; } -/* - * gg_free_session() +/** + * Wysy艂a do serwera pakiet utrzymania po艂膮czenia. + * + * Klient powinien regularnie co minut臋 wysy艂a膰 pakiet utrzymania po艂膮czenia, + * inaczej serwer uzna, 偶e klient straci艂 艂膮czno艣膰 z sieci膮 i zerwie + * po艂膮czenie. + * + * \param sess Struktura sesji + * + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du * - * pr骲uje zamkn辨 po潮czenia i zwalnia pami赕 zajmowan przez sesj. + * \ingroup login + */ +int gg_ping(struct gg_session *sess) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + return gg_send_packet(sess, GG_PING, NULL); +} + +/** + * Ko艅czy po艂膮czenie z serwerem. * - * - sess - opis sesji + * Funkcja nie zwalnia zasob贸w, wi臋c po jej wywo艂aniu nale偶y u偶y膰 + * \c gg_free_session(). Je艣li chce si臋 ustawi膰 opis niedost臋pno艣ci, nale偶y + * wcze艣niej wywo艂a膰 funkcj臋 \c gg_change_status_descr() lub + * \c gg_change_status_descr_time(). + * + * \note Je艣li w buforze nadawczym po艂膮czenia z serwerem znajduj膮 si臋 jeszcze + * dane (np. z powodu strat pakiet贸w na 艂膮czu), prawdopodobnie zostan膮 one + * utracone przy zrywaniu po艂膮czenia. + * + * \param sess Struktura sesji + * + * \ingroup login */ -void gg_free_session(struct gg_session *sess) +void gg_logoff(struct gg_session *sess) { if (!sess) return; - /* XXX dopisa zwalnianie i zamykanie wszystkiego, co mog硂 zosta */ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); + + if (GG_S_NA(sess->status)) + gg_change_status(sess, GG_STATUS_NOT_AVAIL); + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (sess->ssl) + SSL_shutdown(sess->ssl); +#endif + + sess->resolver_cleanup(&sess->resolver, 1); - if (sess->password) - free(sess->password); - - if (sess->initial_descr) - free(sess->initial_descr); + if (sess->fd != -1) { + shutdown(sess->fd, SHUT_RDWR); + close(sess->fd); + sess->fd = -1; + } + + if (sess->send_buf) { + free(sess->send_buf); + sess->send_buf = NULL; + sess->send_left = 0; + } +} - if (sess->client_version) - free(sess->client_version); +/** + * Zwalnia zasoby u偶ywane przez po艂膮czenie z serwerem. Funkcj臋 nale偶y wywo艂a膰 + * po zamkni臋ciu po艂膮czenia z serwerem, by nie doprowadzi膰 do wycieku zasob贸w + * systemowych. + * + * \param sess Struktura sesji + * + * \ingroup login + */ +void gg_free_session(struct gg_session *sess) +{ + struct gg_dcc7 *dcc; - if (sess->header_buf) - free(sess->header_buf); + if (!sess) + return; + + /* XXX dopisa膰 zwalnianie i zamykanie wszystkiego, co mog艂o zosta膰 */ -#ifdef __GG_LIBGADU_HAVE_OPENSSL + free(sess->password); + free(sess->initial_descr); + free(sess->client_version); + free(sess->header_buf); + +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) SSL_free(sess->ssl); @@ -1183,23 +1030,7 @@ SSL_CTX_free(sess->ssl_ctx); #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; - } -#else - if (sess->pid != -1) - waitpid(sess->pid, NULL, WNOHANG); -#endif + sess->resolver_cleanup(&sess->resolver, 1); if (sess->fd != -1) close(sess->fd); @@ -1207,24 +1038,37 @@ while (sess->images) gg_image_queue_remove(sess, sess->images, 1); + free(sess->send_buf); + + for (dcc = sess->dcc7_list; dcc; dcc = dcc->next) + dcc->sess = NULL; + free(sess); } -/* - * gg_change_status() +#ifndef DOXYGEN + +/** + * \internal Funkcja wysy艂aj膮ca pakiet zmiany statusu u偶ytkownika. * - * zmienia status u縴tkownika. przydatne do /away i /busy oraz /quit. + * \param sess Struktura sesji + * \param status Nowy status u偶ytkownika + * \param descr Opis statusu u偶ytkownika (lub \c NULL) + * \param time Czas powrotu w postaci uniksowego znacznika czasu (lub 0) * - * - sess - opis sesji - * - status - nowy status u縴tkownika + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du * - * 0, -1. + * \ingroup status */ -int gg_change_status(struct gg_session *sess, int status) +static int gg_change_status_common(struct gg_session *sess, int status, const char *descr, int time) { - struct gg_new_status p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); + char *new_descr = NULL; + uint32_t new_time; + int descr_len = 0; + int descr_len_max; + int packet_type; + int append_null = 0; + int res; if (!sess) { errno = EFAULT; @@ -1236,67 +1080,422 @@ return -1; } - p.status = gg_fix32(status); + /* XXX, obcina膰 stany kt贸rych stary protok贸艂 niezna (czyt. dnd->aw; ffc->av) */ + + /* dodaj flag臋 obs艂ugi po艂膮cze艅 g艂osowych zgodn膮 z GG 7.x */ + if ((sess->protocol_version >= 0x2a) && (sess->protocol_version < 0x2d /* ? */ ) && (sess->protocol_flags & GG_HAS_AUDIO_MASK) && !GG_S_I(status)) + status |= GG_STATUS_VOICE_MASK; sess->status = status; - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL); + if (sess->protocol_version >= 0x2d) { + if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) { + new_descr = gg_cp_to_utf8(descr); + + if (!new_descr) + return -1; + } + + if (sess->protocol_version >= 0x2e) + packet_type = GG_NEW_STATUS80; + else /* sess->protocol_version == 0x2d */ + packet_type = GG_NEW_STATUS80BETA; + descr_len_max = GG_STATUS_DESCR_MAXSIZE; + append_null = 1; + + } else { + packet_type = GG_NEW_STATUS; + descr_len_max = GG_STATUS_DESCR_MAXSIZE_PRE_8_0; + + if (time != 0) + append_null = 1; + } + + if (descr) { + descr_len = strlen((new_descr) ? new_descr : descr); + + if (descr_len > descr_len_max) + descr_len = descr_len_max; + + // XXX pami臋ta膰 o tym, 偶eby nie ucina膰 w 艣rodku znaku utf-8 + } + + if (time) + new_time = gg_fix32(time); + + if (packet_type == GG_NEW_STATUS80) { + struct gg_new_status80 p; + + p.status = gg_fix32(status); + p.flags = gg_fix32(0x00800001); + p.description_size = gg_fix32(descr_len); + res = gg_send_packet(sess, + packet_type, + &p, + sizeof(p), + (new_descr) ? new_descr : descr, + descr_len, + NULL); + + } else { + struct gg_new_status p; + + p.status = gg_fix32(status); + res = gg_send_packet(sess, + packet_type, + &p, + sizeof(p), + (new_descr) ? new_descr : descr, + descr_len, + (append_null) ? "\0" : NULL, + (append_null) ? 1 : 0, + (time) ? &new_time : NULL, + (time) ? sizeof(new_time) : 0, + NULL); + } + + free(new_descr); + return res; } -/* - * gg_change_status_descr() +#endif /* DOXYGEN */ + +/** + * Zmienia status u偶ytkownika. * - * zmienia status u縴tkownika na opisowy. + * \param sess Struktura sesji + * \param status Nowy status u偶ytkownika + * + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du * - * - sess - opis sesji - * - status - nowy status u縴tkownika - * - descr - opis statusu + * \ingroup status + */ +int gg_change_status(struct gg_session *sess, int status) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); + + return gg_change_status_common(sess, status, NULL, 0); +} + +/** + * Zmienia status u偶ytkownika na status opisowy. * - * 0, -1. + * \param sess Struktura sesji + * \param status Nowy status u偶ytkownika + * \param descr Opis statusu u偶ytkownika + * + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup status */ int gg_change_status_descr(struct gg_session *sess, int status, const char *descr) { - struct gg_new_status p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); - - if (!sess || !descr) { - errno = EFAULT; - return -1; - } + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - p.status = gg_fix32(status); - - sess->status = status; - - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL); + return gg_change_status_common(sess, status, descr, 0); } -/* - * gg_change_status_descr_time() - * - * zmienia status u縴tkownika na opisowy z godzin powrotu. +/** + * Zmienia status u偶ytkownika na status opisowy z podanym czasem powrotu. * - * - sess - opis sesji - * - status - nowy status u縴tkownika - * - descr - opis statusu - * - time - czas w formacie uniksowym + * \param sess Struktura sesji + * \param status Nowy status u偶ytkownika + * \param descr Opis statusu u偶ytkownika + * \param time Czas powrotu w postaci uniksowego znacznika czasu * - * 0, -1. + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup status */ int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) { - struct gg_new_status p; - uint32_t newtime; + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); + + return gg_change_status_common(sess, status, descr, time); +} + +/** + * Wysy艂a wiadomo艣膰 do u偶ytkownika. + * + * Zwraca losowy numer sekwencyjny, kt贸ry mo偶na zignorowa膰 albo wykorzysta膰 + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomo艣ci + * \param recipient Numer adresata + * \param message Tre艣膰 wiadomo艣ci + * + * \return Numer sekwencyjny wiadomo艣ci lub -1 w przypadku b艂臋du. + * + * \ingroup messages + */ +int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); + + return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, NULL, 0); +} + +/** + * Wysy艂a wiadomo艣膰 formatowan膮. + * + * Zwraca losowy numer sekwencyjny, kt贸ry mo偶na zignorowa膰 albo wykorzysta膰 + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomo艣ci + * \param recipient Numer adresata + * \param message Tre艣膰 wiadomo艣ci + * \param format Informacje o formatowaniu + * \param formatlen D艂ugo艣膰 informacji o formatowaniu + * + * \return Numer sekwencyjny wiadomo艣ci lub -1 w przypadku b艂臋du. + * + * \ingroup messages + */ +int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); + + return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, format, formatlen); +} + +/** + * Wysy艂a wiadomo艣膰 w ramach konferencji. + * + * Zwraca losowy numer sekwencyjny, kt贸ry mo偶na zignorowa膰 albo wykorzysta膰 + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomo艣ci + * \param recipients_count Liczba adresat贸w + * \param recipients Wska藕nik do tablicy z numerami adresat贸w + * \param message Tre艣膰 wiadomo艣ci + * + * \return Numer sekwencyjny wiadomo艣ci lub -1 w przypadku b艂臋du. + * + * \ingroup messages + */ +int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); + + return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); +} + +/** + * \internal Dodaje tekst na koniec bufora. + * + * \param dst Wska藕nik na bufor roboczy + * \param pos Wska藕nik na aktualne po艂o偶enie w buforze roboczym + * \param src Dodawany tekst + * \param len D艂ugo艣膰 dodawanego tekstu + */ +static void gg_append(char *dst, int *pos, const void *src, int len) +{ + if (dst != NULL) + memcpy(&dst[*pos], src, len); + + *pos += len; +} + +/** + * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML. + * + * \param dst Bufor wynikowy (mo偶e by膰 \c NULL) + * \param utf_msg Tekst 藕r贸d艂owy + * \param format Atrybuty tekstu 藕r贸d艂owego + * \param format_len D艂ugo艣膰 bloku atrybut贸w tekstu 藕r贸d艂owego + * + * \note Dokleja \c \\0 na ko艅cu bufora wynikowego. + * + * \return D艂ugo艣膰 tekstu wynikowego bez \c \\0 (nawet je艣li \c dst to \c NULL). + */ +static int gg_convert_to_html(char *dst, const char *utf_msg, const unsigned char *format, int format_len) +{ + const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">"; + const int span_len = 75; + const char img_fmt[] = "<img src=\"%02x%02x%02x%02x%02x%02x%02x%02x\">"; + const int img_len = 28; + int char_pos = 0; + int format_idx = 3; + unsigned char old_attr = 0; + const unsigned char *color = (const unsigned char*) "\x00\x00\x00"; + int len, i; + + len = 0; + + for (i = 0; utf_msg[i] != 0; i++) { + unsigned char attr; + int attr_pos; + + if (format_idx + 3 <= format_len) { + attr_pos = format[format_idx] | (format[format_idx + 1] << 8); + attr = format[format_idx + 2]; + } else { + attr_pos = -1; + attr = 0; + } + + if (attr_pos == char_pos) { + format_idx += 3; + + if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) { + if (char_pos != 0) { + if ((old_attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "</u>", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "</i>", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "</b>", 4); - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); + gg_append(dst, &len, "</span>", 7); + } + + if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { + color = &format[format_idx]; + format_idx += 3; + } else { + color = (const unsigned char*) "\x00\x00\x00"; + } + + if (dst != NULL) + sprintf(&dst[len], span_fmt, color[0], color[1], color[2]); + len += span_len; + } else if (char_pos == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + len += span_len; + } + + if ((attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "<b>", 3); + + if ((attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "<i>", 3); + + if ((attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "<u>", 3); + + if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) { + if (dst != NULL) { + sprintf(&dst[len], img_fmt, + format[format_idx + 9], + format[format_idx + 8], + format[format_idx + 7], + format[format_idx + 6], + format[format_idx + 5], + format[format_idx + 4], + format[format_idx + 3], + format[format_idx + 2]); + } + + len += img_len; + format_idx += 10; + } + + old_attr = attr; + } else if (i == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + + len += span_len; + } - if (!sess || !descr || !time) { + switch (utf_msg[i]) { + case '&': + gg_append(dst, &len, "&", 5); + break; + case '<': + gg_append(dst, &len, "<", 4); + break; + case '>': + gg_append(dst, &len, ">", 4); + break; + case '\'': + gg_append(dst, &len, "'", 6); + break; + case '\"': + gg_append(dst, &len, """, 6); + break; + case '\n': + gg_append(dst, &len, "<br>", 4); + break; + case '\r': + break; + default: + if (dst != NULL) + dst[len] = utf_msg[i]; + len++; + } + + /* Sprawd藕, czy bajt nie jest kontynuacj膮 znaku unikodowego. */ + + if ((utf_msg[i] & 0xc0) != 0xc0) + char_pos++; + } + + if ((old_attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "</u>", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "</i>", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "</b>", 4); + + /* Dla pustych tekst贸w dodaj pusty <span>. */ + + if (i == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + + len += span_len; + } + + gg_append(dst, &len, "</span>", 7); + + if (dst != NULL) + dst[len] = 0; + + return len; +} + +/** + * Wysy艂a wiadomo艣膰 formatowan膮 w ramach konferencji. + * + * Zwraca losowy numer sekwencyjny, kt贸ry mo偶na zignorowa膰 albo wykorzysta膰 + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomo艣ci + * \param recipients_count Liczba adresat贸w + * \param recipients Wska藕nik do tablicy z numerami adresat贸w + * \param message Tre艣膰 wiadomo艣ci + * \param format Informacje o formatowaniu + * \param formatlen D艂ugo艣膰 informacji o formatowaniu + * + * \return Numer sekwencyjny wiadomo艣ci lub -1 w przypadku b艂臋du. + * + * \ingroup messages + */ +int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) +{ + struct gg_send_msg s; + struct gg_send_msg80 s80; + struct gg_msg_recipients r; + char *cp_msg = NULL; + char *utf_msg = NULL; + char *html_msg = NULL; + int seq_no; + int i, j, k; + uin_t *recps; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); + + if (!sess) { errno = EFAULT; return -1; } @@ -1306,75 +1505,184 @@ return -1; } - p.status = gg_fix32(status); + if (message == NULL || recipients_count <= 0 || recipients_count > 0xffff || (recipients_count != 1 && recipients == NULL)) { + errno = EINVAL; + return -1; + } + + if (sess->encoding == GG_ENCODING_UTF8) { + if (!(cp_msg = gg_utf8_to_cp((const char *) message))) + return -1; + + utf_msg = (char*) message; + } else { + if (sess->protocol_version >= 0x2d) { + if (!(utf_msg = gg_cp_to_utf8((const char *) message))) + return -1; + } + + cp_msg = (char*) message; + } + + if (sess->protocol_version < 0x2d) { + if (!sess->seq) + sess->seq = 0x01740000 | (rand() & 0xffff); + seq_no = sess->seq; + sess->seq += (rand() % 0x300) + 0x300; - sess->status = status; + s.msgclass = gg_fix32(msgclass); + s.seq = gg_fix32(seq_no); + } else { + int len; + + // Drobne odchylenie od protoko艂u. Je艣li wysy艂amy kilka + // wiadomo艣ci w ci膮gu jednej sekundy, zwi臋kszamy poprzedni膮 + // warto艣膰, 偶eby ka偶da wiadomo艣膰 mia艂a unikalny numer. + + seq_no = time(NULL); + + if (seq_no <= sess->seq) + seq_no = sess->seq + 1; + + sess->seq = seq_no; + + if (format == NULL || formatlen < 3) { + format = (unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00"; + formatlen = 9; + } + + len = gg_convert_to_html(NULL, utf_msg, format, formatlen); + + html_msg = malloc(len + 1); + + if (html_msg == NULL) { + seq_no = -1; + goto cleanup; + } + + gg_convert_to_html(html_msg, utf_msg, format, formatlen); - newtime = gg_fix32(time); + s80.seq = gg_fix32(seq_no); + s80.msgclass = gg_fix32(msgclass); + s80.offset_plain = gg_fix32(sizeof(s80) + strlen(html_msg) + 1); + s80.offset_attr = gg_fix32(sizeof(s80) + strlen(html_msg) + 1 + strlen(cp_msg) + 1); + } + + if (recipients_count > 1) { + r.flag = 0x01; + r.count = gg_fix32(recipients_count - 1); + + recps = malloc(sizeof(uin_t) * recipients_count); + + if (!recps) { + seq_no = -1; + goto cleanup; + } + + for (i = 0; i < recipients_count; i++) { + for (j = 0, k = 0; j < recipients_count; j++) { + if (recipients[j] != recipients[i]) { + recps[k] = gg_fix32(recipients[j]); + k++; + } + } + + if (sess->protocol_version < 0x2d) { + s.recipient = gg_fix32(recipients[i]); - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL); + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) + seq_no = -1; + } else { + s80.recipient = gg_fix32(recipients[i]); + + if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) + seq_no = -1; + } + } + + free(recps); + } else { + if (sess->protocol_version < 0x2d) { + s.recipient = gg_fix32(recipients[0]); + + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1) + seq_no = -1; + } else { + s80.recipient = gg_fix32(recipients[0]); + + if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1) + seq_no = -1; + } + } + +cleanup: + if (cp_msg != (char*) message) + free(cp_msg); + + if (utf_msg != (char*) message) + free(utf_msg); + + free(html_msg); + + return seq_no; } -/* - * gg_logoff() +/** + * Wysy艂a wiadomo艣膰 binarn膮 przeznaczon膮 dla klienta. + * + * Wiadomo艣ci mi臋dzy klientami przesy艂a si臋 np. w celu wywo艂ania zwrotnego + * po艂膮czenia bezpo艣redniego. Funkcja zwraca losowy numer sekwencyjny, + * kt贸ry mo偶na zignorowa膰 albo wykorzysta膰 do potwierdzenia. * - * wylogowuje u縴tkownika i zamyka po潮czenie, ale nie zwalnia pami阠i. + * \param sess Struktura sesji + * \param msgclass Klasa wiadomo艣ci + * \param recipient Numer adresata + * \param message Tre艣膰 wiadomo艣ci + * \param message_len D艂ugo艣膰 wiadomo艣ci * - * - sess - opis sesji + * \return Numer sekwencyjny wiadomo艣ci lub -1 w przypadku b艂臋du. + * + * \ingroup messages */ -void gg_logoff(struct gg_session *sess) +int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) { - if (!sess) - return; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); - - if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK)) - gg_change_status(sess, GG_STATUS_NOT_AVAIL); - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - SSL_shutdown(sess->ssl); -#endif + struct gg_send_msg s; -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); + + if (!sess) { + errno = EFAULT; + return -1; } -#else - if (sess->pid != -1) { - waitpid(sess->pid, NULL, WNOHANG); - sess->pid = -1; + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; } -#endif - - if (sess->fd != -1) { - shutdown(sess->fd, SHUT_RDWR); - close(sess->fd); - sess->fd = -1; - } + + s.recipient = gg_fix32(recipient); + s.seq = gg_fix32(0); + s.msgclass = gg_fix32(msgclass); + + return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); } -/* - * gg_image_request() +/** + * Wysy艂a 偶膮danie obrazka o podanych parametrach. * - * wysy砤 勘danie wys砤nia obrazka o podanych parametrach. + * Wiadomo艣ci obrazkowe nie zawieraj膮 samych obrazk贸w, a tylko ich rozmiary + * i sumy kontrolne. Odbiorca najpierw szuka obrazk贸w w swojej pami臋ci + * podr臋cznej i dopiero gdy ich nie znajdzie, wysy艂a 偶膮danie do nadawcy. + * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY. * - * - sess - opis sesji - * - recipient - numer adresata - * - size - rozmiar obrazka - * - crc32 - suma kontrolna obrazka + * \param sess Struktura sesji + * \param recipient Numer adresata + * \param size Rozmiar obrazka w bajtach + * \param crc32 Suma kontrola obrazka * - * 0/-1 + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup messages */ int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32) { @@ -1383,13 +1691,13 @@ char dummy = 0; int res; - gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); if (!sess) { errno = EFAULT; return -1; } - + if (sess->state != GG_STATE_CONNECTED) { errno = ENOTCONN; return -1; @@ -1407,7 +1715,7 @@ r.flag = 0x04; r.size = gg_fix32(size); r.crc32 = gg_fix32(crc32); - + res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL); if (!res) { @@ -1415,14 +1723,14 @@ char *buf; if (!q) { - gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); return -1; } buf = malloc(size); if (size && !buf) { - gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); free(q); return -1; } @@ -1449,20 +1757,20 @@ return res; } -/* - * gg_image_reply() - * - * wysy砤 勘dany obrazek. +/** + * Wysy艂a 偶膮dany obrazek. * - * - sess - opis sesji - * - recipient - numer adresata - * - filename - nazwa pliku - * - image - bufor z obrazkiem - * - size - rozmiar obrazka + * \param sess Struktura sesji + * \param recipient Numer adresata + * \param filename Nazwa pliku + * \param image Bufor z obrazkiem + * \param size Rozmiar obrazka * - * 0/-1 + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup messages */ -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size) +int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size) { struct gg_msg_image_reply *r; struct gg_send_msg s; @@ -1470,7 +1778,7 @@ char buf[1910]; int res = -1; - gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); if (!sess || !filename || !image) { errno = EFAULT; @@ -1487,7 +1795,7 @@ return -1; } - /* wytnij 禼ie縦i, zostaw tylko nazw pliku */ + /* wytnij 艣cie偶ki, zostaw tylko nazw臋 pliku */ while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\'))) filename = tmp + 1; @@ -1495,7 +1803,7 @@ errno = EINVAL; return -1; } - + s.recipient = gg_fix32(recipient); s.seq = gg_fix32(0); s.msgclass = gg_fix32(GG_CLASS_MSG); @@ -1505,26 +1813,26 @@ r->flag = 0x05; r->size = gg_fix32(size); - r->crc32 = gg_fix32(gg_crc32(0, image, size)); + r->crc32 = gg_fix32(gg_crc32(0, (unsigned char*) image, size)); while (size > 0) { - size_t buflen, chunklen; - + int buflen, chunklen; + /* \0 + struct gg_msg_image_reply */ buflen = sizeof(struct gg_msg_image_reply) + 1; - /* w pierwszym kawa砶u jest nazwa pliku */ + /* w pierwszym kawa艂ku jest nazwa pliku */ if (r->flag == 0x05) { strcpy(buf + buflen, filename); buflen += strlen(filename) + 1; } - chunklen = ((size_t)size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t)size; + chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size; memcpy(buf + buflen, image, chunklen); size -= chunklen; image += chunklen; - + res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL); if (res == -1) @@ -1536,83 +1844,34 @@ return res; } -/* - * gg_send_message_ctcp() +/** + * Wysy艂a do serwera list臋 kontakt贸w. * - * wysy砤 wiadomo舵 do innego u縴tkownika. zwraca losowy numer - * sekwencyjny, kt髍y mo縩a zignorowa albo wykorzysta do potwierdzenia. + * Funkcja informuje serwer o li艣cie kontakt贸w, kt贸rych statusy b臋d膮 + * obserwowane lub kontakt贸w, kt贸re bed膮 blokowane. Dla ka偶dego z \c count + * kontakt贸w tablica \c userlist zawiera numer, a tablica \c types rodzaj + * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED). + * + * List臋 kontakt贸w nale偶y \b zawsze wysy艂a膰 po po艂膮czeniu, nawet je艣li + * jest pusta. * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo禼i - * - recipient - numer adresata - * - message - tre舵 wiadomo禼i - * - message_len - d硊go舵 + * \param sess Struktura sesji + * \param userlist Wska藕nik do tablicy numer贸w kontakt贸w + * \param types Wska藕nik do tablicy rodzaj贸w kontakt贸w + * \param count Liczba kontakt贸w * - * numer sekwencyjny wiadomo禼i lub -1 w przypadku b酬du. + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup contacts */ -int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) +int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) { - struct gg_send_msg s; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - s.recipient = gg_fix32(recipient); - s.seq = gg_fix32(0); - s.msgclass = gg_fix32(msgclass); - - return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); -} + struct gg_notify *n; + uin_t *u; + char *t; + int i, res = 0; -/* - * gg_send_message() - * - * wysy砤 wiadomo舵 do innego u縴tkownika. zwraca losowy numer - * sekwencyjny, kt髍y mo縩a zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo禼i - * - recipient - numer adresata - * - message - tre舵 wiadomo禼i - * - * numer sekwencyjny wiadomo禼i lub -1 w przypadku b酬du. - */ -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); - - return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0); -} - -/* - * gg_send_message_richtext() - * - * wysy砤 kolorow wiadomo舵 do innego u縴tkownika. zwraca losowy numer - * sekwencyjny, kt髍y mo縩a zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo禼i - * - recipient - numer adresata - * - message - tre舵 wiadomo禼i - * - format - informacje o formatowaniu - * - formatlen - d硊go舵 informacji o formatowaniu - * - * numer sekwencyjny wiadomo禼i lub -1 w przypadku b酬du. - */ -int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) -{ - struct gg_send_msg s; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); if (!sess) { errno = EFAULT; @@ -1624,186 +1883,12 @@ return -1; } - if (!message) { - errno = EFAULT; - return -1; - } - - s.recipient = gg_fix32(recipient); - if (!sess->seq) - sess->seq = 0x01740000 | (rand() & 0xffff); - s.seq = gg_fix32(sess->seq); - s.msgclass = gg_fix32(msgclass); - sess->seq += (rand() % 0x300) + 0x300; - - if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, format, formatlen, NULL) == -1) - return -1; - - return gg_fix32(s.seq); -} - -/* - * gg_send_message_confer() - * - * wysy砤 wiadomo舵 do kilku u縴tkownikow (konferencja). zwraca losowy numer - * sekwencyjny, kt髍y mo縩a zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo禼i - * - recipients_count - ilo舵 adresat體 - * - recipients - numerki adresat體 - * - message - tre舵 wiadomo禼i - * - * numer sekwencyjny wiadomo禼i lub -1 w przypadku b酬du. - */ -int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); - - return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); -} - -/* - * gg_send_message_confer_richtext() - * - * wysy砤 kolorow wiadomo舵 do kilku u縴tkownikow (konferencja). zwraca - * losowy numer sekwencyjny, kt髍y mo縩a zignorowa albo wykorzysta do - * potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomo禼i - * - recipients_count - ilo舵 adresat體 - * - recipients - numerki adresat體 - * - message - tre舵 wiadomo禼i - * - format - informacje o formatowaniu - * - formatlen - d硊go舵 informacji o formatowaniu - * - * numer sekwencyjny wiadomo禼i lub -1 w przypadku b酬du. - */ -int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) -{ - struct gg_send_msg s; - struct gg_msg_recipients r; - int i, j, k; - uin_t *recps; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) { - errno = EINVAL; - return -1; - } - - r.flag = 0x01; - r.count = gg_fix32(recipients_count - 1); - - if (!sess->seq) - sess->seq = 0x01740000 | (rand() & 0xffff); - s.seq = gg_fix32(sess->seq); - s.msgclass = gg_fix32(msgclass); - - recps = malloc(sizeof(uin_t) * recipients_count); - if (!recps) - return -1; - - for (i = 0; i < recipients_count; i++) { - - s.recipient = gg_fix32(recipients[i]); - - for (j = 0, k = 0; j < recipients_count; j++) - if (recipients[j] != recipients[i]) { - recps[k] = gg_fix32(recipients[j]); - k++; - } - - if (!i) - sess->seq += (rand() % 0x300) + 0x300; - - if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) { - free(recps); - return -1; - } - } - - free(recps); - - return gg_fix32(s.seq); -} - -/* - * gg_ping() - * - * wysy砤 do serwera pakiet ping. - * - * - sess - opis sesji - * - * 0, -1. - */ -int gg_ping(struct gg_session *sess) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - return gg_send_packet(sess, GG_PING, NULL); -} - -/* - * gg_notify_ex() - * - * wysy砤 serwerowi list kontakt體 (wraz z odpowiadaj眂ymi im typami user體), - * dzi阫i czemu wie, czyj stan nas interesuje. - * - * - sess - opis sesji - * - userlist - wska糿ik do tablicy numer體 - * - types - wska糿ik do tablicy typ體 u縴tkownik體 - * - count - ilo舵 numerk體 - * - * 0, -1. - */ -int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) -{ - struct gg_notify *n; - uin_t *u; - char *t; - int i, res = 0; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - if (!userlist || !count) return gg_send_packet(sess, GG_LIST_EMPTY, NULL); - + while (count > 0) { int part_count, packet_type; - + if (count > 400) { part_count = 400; packet_type = GG_NOTIFY_FIRST; @@ -1814,12 +1899,12 @@ if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) return -1; - - for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { + + for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { n[i].uin = gg_fix32(*u); n[i].dunno1 = *t; } - + if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { free(n); res = -1; @@ -1836,17 +1921,19 @@ return res; } -/* - * gg_notify() +/** + * Wysy艂a do serwera list臋 kontakt贸w. * - * wysy砤 serwerowi list kontakt體, dzi阫i czemu wie, czyj stan nas - * interesuje. + * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty + * s膮 rodzaju \c GG_USER_NORMAL. * - * - sess - opis sesji - * - userlist - wska糿ik do tablicy numer體 - * - count - ilo舵 numerk體 + * \param sess Struktura sesji + * \param userlist Wska藕nik do tablicy numer贸w kontakt贸w + * \param count Liczba kontakt贸w * - * 0, -1. + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup contacts */ int gg_notify(struct gg_session *sess, uin_t *userlist, int count) { @@ -1854,13 +1941,13 @@ uin_t *u; int i, res = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); + if (!sess) { errno = EFAULT; return -1; } - + if (sess->state != GG_STATE_CONNECTED) { errno = ENOTCONN; return -1; @@ -1868,10 +1955,10 @@ if (!userlist || !count) return gg_send_packet(sess, GG_LIST_EMPTY, NULL); - + while (count > 0) { int part_count, packet_type; - + if (count > 400) { part_count = 400; packet_type = GG_NOTIFY_FIRST; @@ -1879,15 +1966,15 @@ part_count = count; packet_type = GG_NOTIFY_LAST; } - + if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) return -1; - - for (u = userlist, i = 0; i < part_count; u++, i++) { + + for (u = userlist, i = 0; i < part_count; u++, i++) { n[i].uin = gg_fix32(*u); n[i].dunno1 = GG_USER_NORMAL; } - + if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { res = -1; free(n); @@ -1903,73 +1990,27 @@ return res; } -/* - * gg_add_notify_ex() +/** + * Dodaje kontakt. * - * dodaje do listy kontakt體 dany numer w trakcie po潮czenia. - * dodawanemu u縴tkownikowi okre秎amy jego typ (patrz protocol.html) + * Dodaje do listy kontakt贸w dany numer w trakcie po艂膮czenia. Aby zmieni膰 + * rodzaj kontaktu (np. z normalnego na zablokowany), nale偶y najpierw usun膮膰 + * poprzedni rodzaj, poniewa偶 serwer operuje na maskach bitowych. * - * - sess - opis sesji - * - uin - numer - * - type - typ + * \param sess Struktura sesji + * \param uin Numer kontaktu + * \param type Rodzaj kontaktu * - * 0, -1. + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup contacts */ int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type) { struct gg_add_remove a; - gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - a.uin = gg_fix32(uin); - a.dunno1 = type; - - return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); -} + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); -/* - * gg_add_notify() - * - * dodaje do listy kontakt體 dany numer w trakcie po潮czenia. - * - * - sess - opis sesji - * - uin - numer - * - * 0, -1. - */ -int gg_add_notify(struct gg_session *sess, uin_t uin) -{ - return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); -} - -/* - * gg_remove_notify_ex() - * - * usuwa z listy kontakt體 w trakcie po潮czenia. - * usuwanemu u縴tkownikowi okre秎amy jego typ (patrz protocol.html) - * - * - sess - opis sesji - * - uin - numer - * - type - typ - * - * 0, -1. - */ -int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) -{ - struct gg_add_remove a; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); - if (!sess) { errno = EFAULT; return -1; @@ -1982,35 +2023,101 @@ a.uin = gg_fix32(uin); a.dunno1 = type; - + + return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); +} + +/** + * Dodaje kontakt. + * + * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich + * kontakt贸w to \c GG_USER_NORMAL. + * + * \param sess Struktura sesji + * \param uin Numer kontaktu + * + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup contacts + */ +int gg_add_notify(struct gg_session *sess, uin_t uin) +{ + return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); +} + +/** + * Usuwa kontakt. + * + * Usuwa z listy kontakt贸w dany numer w trakcie po艂膮czenia. + * + * \param sess Struktura sesji + * \param uin Numer kontaktu + * \param type Rodzaj kontaktu + * + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup contacts + */ +int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) +{ + struct gg_add_remove a; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + a.uin = gg_fix32(uin); + a.dunno1 = type; + return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL); } -/* - * gg_remove_notify() +/** + * Usuwa kontakt. * - * usuwa z listy kontakt體 w trakcie po潮czenia. + * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich + * kontakt贸w to \c GG_USER_NORMAL. * - * - sess - opis sesji - * - uin - numer + * \param sess Struktura sesji + * \param uin Numer kontaktu * - * 0, -1. + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup contacts */ int gg_remove_notify(struct gg_session *sess, uin_t uin) { return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); } -/* - * gg_userlist_request() +/** + * Wysy艂a do serwera zapytanie dotycz膮ce listy kontakt贸w. * - * wysy砤 勘danie/zapytanie listy kontakt體 na serwerze. + * Funkcja s艂u偶y do importu lub eksportu listy kontakt贸w do serwera. + * W odr贸偶nieniu od funkcji \c gg_notify(), ta lista kontakt贸w jest przez + * serwer jedynie przechowywana i nie ma wp艂ywu na po艂膮czenie. Format + * listy kontakt贸w jest ignorowany przez serwer, ale ze wzgl臋du na + * kompatybilno艣膰 z innymi klientami, nale偶y przechowywa膰 dane w tym samym + * formacie co oryginalny klient Gadu-Gadu. * - * - sess - opis sesji - * - type - rodzaj zapytania/勘dania - * - request - tre舵 zapytania/勘dania (mo縠 by NULL) + * Program nie musi si臋 przejmowa膰 fragmentacj膮 listy kontakt贸w wynikaj膮c膮 + * z protoko艂u -- wysy艂a i odbiera kompletn膮 list臋. * - * 0, -1 + * \param sess Struktura sesji + * \param type Rodzaj zapytania + * \param request Tre艣膰 zapytania (mo偶e by膰 r贸wne NULL) + * + * \return 0 je艣li si臋 powiod艂o, -1 w przypadku b艂臋du + * + * \ingroup importexport */ int gg_userlist_request(struct gg_session *sess, char type, const char *request) { @@ -2020,7 +2127,7 @@ errno = EFAULT; return -1; } - + if (sess->state != GG_STATE_CONNECTED) { errno = ENOTCONN; return -1; @@ -2030,7 +2137,7 @@ sess->userlist_blocks = 1; return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); } - + len = strlen(request); sess->userlist_blocks = 0; @@ -2053,6 +2160,8 @@ return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); } +/* @} */ + /* * Local variables: * c-indentation-style: k&r