summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/gadu/libgadu/libgadu.c
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/gadu/libgadu/libgadu.c')
-rw-r--r--kopete/protocols/gadu/libgadu/libgadu.c1818
1 files changed, 0 insertions, 1818 deletions
diff --git a/kopete/protocols/gadu/libgadu/libgadu.c b/kopete/protocols/gadu/libgadu/libgadu.c
deleted file mode 100644
index 79f0a170..00000000
--- a/kopete/protocols/gadu/libgadu/libgadu.c
+++ /dev/null
@@ -1,1818 +0,0 @@
-/* $Id$ */
-
-/*
- * (C) Copyright 2001-2006 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
- * 2.1 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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 02110-1301,
- * USA.
- */
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#ifdef sun
-# include <sys/filio.h>
-#endif
-
-#include "libgadu-config.h"
-
-#include <errno.h>
-#include <netdb.h>
-#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 <unistd.h>
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-# include <openssl/err.h>
-# include <openssl/rand.h>
-#endif
-
-#include "compat.h"
-#include "libgadu.h"
-
-int gg_debug_level = 0;
-void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
-
-int gg_dcc_port = 0;
-unsigned long gg_dcc_ip = 0;
-
-unsigned long gg_local_ip = 0;
-/*
- * zmienne opisujące parametry proxy http.
- */
-char *gg_proxy_host = NULL;
-int gg_proxy_port = 0;
-int gg_proxy_enabled = 0;
-int gg_proxy_http_only = 0;
-char *gg_proxy_username = NULL;
-char *gg_proxy_password = NULL;
-
-#ifndef lint
-static char rcsid[]
-#ifdef __GNUC__
-__attribute__ ((unused))
-#endif
-= "$Id$";
-#endif
-
-/*
- * gg_libgadu_version()
- *
- * zwraca wersję libgadu.
- *
- * - brak
- *
- * wersja libgadu.
- */
-const char *gg_libgadu_version()
-{
- return GG_LIBGADU_VERSION;
-}
-
-/*
- * gg_fix32()
- *
- * zamienia kolejność bajtów w liczbie 32-bitowej tak, by odpowiadała
- * kolejności bajtów w protokole GG. ze względu na LE-owość serwera,
- * zamienia tylko na maszynach BE-wych.
- *
- * - x - liczba do zamiany
- *
- * liczba z odpowiednią kolejnością bajtów.
- */
-uint32_t gg_fix32(uint32_t x)
-{
-#ifndef __GG_LIBGADU_BIGENDIAN
- return x;
-#else
- return (uint32_t)
- (((x & (uint32_t) 0x000000ffU) << 24) |
- ((x & (uint32_t) 0x0000ff00U) << 8) |
- ((x & (uint32_t) 0x00ff0000U) >> 8) |
- ((x & (uint32_t) 0xff000000U) >> 24));
-#endif
-}
-
-/*
- * gg_fix16()
- *
- * zamienia kolejność bajtów w liczbie 16-bitowej tak, by odpowiadała
- * kolejności bajtów w protokole GG. ze względu na LE-owość serwera,
- * zamienia tylko na maszynach BE-wych.
- *
- * - x - liczba do zamiany
- *
- * liczba z odpowiednią kolejnością bajtów.
- */
-uint16_t gg_fix16(uint16_t x)
-{
-#ifndef __GG_LIBGADU_BIGENDIAN
- return x;
-#else
- return (uint16_t)
- (((x & (uint16_t) 0x00ffU) << 8) |
- ((x & (uint16_t) 0xff00U) >> 8));
-#endif
-}
-
-/*
- * gg_login_hash() // funkcja wewnętrzna
- *
- * liczy hash z hasła i danego seeda.
- *
- * - password - hasło do hashowania
- * - seed - wartość podana przez serwer
- *
- * hash.
- */
-unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
-{
- unsigned int x, y, z;
-
- y = seed;
-
- for (x = 0; *password; password++) {
- x = (x & 0xffffff00) | *password;
- y ^= x;
- y += x;
- x <<= 8;
- y ^= x;
- x <<= 8;
- y -= x;
- x <<= 8;
- y ^= x;
-
- z = y & 0x1F;
- y = (y << z) | (y >> (32 - z));
- }
-
- return y;
-}
-
-/*
- * gg_resolve() // funkcja wewnętrzna
- *
- * tworzy potok, forkuje się i w drugim procesie zaczyna resolvować
- * podanego hosta. zapisuje w sesji deskryptor potoku. jeśli coś tam
- * będzie gotowego, znaczy, że można wczytać struct in_addr. jeśli
- * nie znajdzie, zwraca INADDR_NONE.
- *
- * - fd - wskaźnik gdzie wrzucić deskryptor
- * - pid - gdzie wrzucić pid procesu potomnego
- * - hostname - nazwa hosta do zresolvowania
- *
- * 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) {
- close(pipes[0]);
-
- 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;
-}
-
-#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; /* żeby kompilator nie marudził */
-}
-
-/*
- * gg_resolve_pthread() // funkcja wewnętrzna
- *
- * tworzy potok, nowy wątek i w nim zaczyna resolvować podanego hosta.
- * zapisuje w sesji deskryptor potoku. jeśli coś tam będzie gotowego,
- * znaczy, że można wczytać struct in_addr. jeśli nie znajdzie, zwraca
- * INADDR_NONE.
- *
- * - fd - wskaźnik do zmiennej przechowującej desktyptor resolvera
- * - resolver - wskaźnik do wskaźnika resolvera
- * - hostname - nazwa hosta do zresolvowania
- *
- * 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;
-}
-
-#endif
-
-/*
- * gg_read() // funkcja pomocnicza
- *
- * czyta z gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy
- * połączenie zwykłe czy TLS.
- *
- * - sess - sesja,
- * - buf - bufor,
- * - length - ilość bajtów,
- *
- * takie same wartości jak read().
- */
-int gg_read(struct gg_session *sess, char *buf, int length)
-{
- int res;
-
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
- if (sess->ssl) {
- int err;
-
- res = SSL_read(sess->ssl, buf, length);
-
- if (res < 0) {
- err = SSL_get_error(sess->ssl, res);
-
- if (err == SSL_ERROR_WANT_READ)
- errno = EAGAIN;
-
- return -1;
- }
- } else
-#endif
- res = read(sess->fd, buf, length);
-
- return res;
-}
-
-/*
- * gg_write() // funkcja pomocnicza
- *
- * zapisuje do gniazda określoną ilość bajtów. bierze pod uwagę, czy mamy
- * połączenie zwykłe czy TLS.
- *
- * - sess - sesja,
- * - buf - bufor,
- * - length - ilość bajtów,
- *
- * takie same wartości jak write().
- */
-int gg_write(struct gg_session *sess, const char *buf, int length)
-{
- int res = 0;
-
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
- if (sess->ssl) {
- int err;
-
- res = SSL_write(sess->ssl, buf, length);
-
- if (res < 0) {
- err = SSL_get_error(sess->ssl, res);
-
- if (err == SSL_ERROR_WANT_WRITE)
- errno = EAGAIN;
-
- return -1;
- }
- } else
-#endif
- {
- int written = 0;
-
- while (written < length) {
- res = write(sess->fd, buf + written, length - written);
-
- if (res == -1) {
- if (errno == EAGAIN)
- continue;
- else
- break;
- } else {
- written += res;
- res = written;
- }
- }
- }
-
- return res;
-}
-
-/*
- * gg_recv_packet() // funkcja wewnętrzna
- *
- * odbiera jeden pakiet i zwraca wskaźnik do niego. pamięć po nim
- * należy zwolnić za pomocą free().
- *
- * - sess - opis sesji
- *
- * w przypadku błędu NULL, kod błędu w errno. należy zwrócić uwagę, że gdy
- * połączenie jest nieblokujące, a kod błędu wynosi EAGAIN, nie udało się
- * odczytać całego pakietu i nie należy tego traktować jako błąd.
- */
-void *gg_recv_packet(struct gg_session *sess)
-{
- struct gg_header h;
- char *buf = NULL;
- int ret = 0;
- unsigned int offset, size = 0;
-
- gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
-
- if (!sess) {
- errno = EFAULT;
- return NULL;
- }
-
- 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);
- free(sess->header_buf);
- sess->header_buf = NULL;
- } else
- sess->header_done = 0;
-
- 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);
-
- if (!ret) {
- errno = ECONNRESET;
- gg_debug(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");
- continue;
- }
-
- if (errno == EAGAIN) {
- gg_debug(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");
- return NULL;
- }
-
- memcpy(sess->header_buf, &h, sess->header_done);
-
- return NULL;
- }
-
- gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
-
- return NULL;
- }
-
- sess->header_done += ret;
-
- }
-
- h.type = gg_fix32(h.type);
- h.length = gg_fix32(h.length);
- } else
- memcpy(&h, sess->recv_buf, sizeof(h));
-
- /* 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);
- errno = ERANGE;
- return NULL;
- }
-
- if (sess->recv_left > 0) {
- gg_debug(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");
- return NULL;
- }
-
- memcpy(buf, &h, sizeof(h));
-
- offset = 0;
- size = h.length;
- }
-
- 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);
- if (!ret) {
- gg_debug(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) {
- int errno2 = errno;
-
- gg_debug(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);
- sess->recv_buf = buf;
- sess->recv_left = size;
- sess->recv_done = offset;
- return NULL;
- }
- if (errno != EINTR) {
- free(buf);
- return NULL;
- }
- }
- }
-
- sess->recv_left = 0;
-
- 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");
- }
-
- return buf;
-}
-
-/*
- * gg_send_packet() // funkcja wewnętrzna
- *
- * konstruuje pakiet i wysyła go do serwera.
- *
- * - sock - deskryptor gniazda
- * - type - typ pakietu
- * - payload_1 - pierwsza część pakietu
- * - payload_length_1 - długość pierwszej części
- * - payload_2 - druga część pakietu
- * - payload_length_2 - długość drugiej części
- * - ... - kolejne części pakietu i ich długości
- * - NULL - końcowym parametr (konieczny!)
- *
- * jeśli się powiodło, zwraca 0, w przypadku błędu -1. jeśli errno == ENOMEM,
- * zabrakło pamięci. inaczej był błąd przy wysyłaniu pakietu. dla errno == 0
- * nie wysłano całego pakietu.
- */
-int gg_send_packet(struct gg_session *sess, int type, ...)
-{
- struct gg_header *h;
- char *tmp;
- 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);
-
- 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");
- return -1;
- }
-
- va_start(ap, type);
-
- payload = va_arg(ap, void *);
-
- while (payload) {
- char *tmp2;
-
- 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");
- free(tmp);
- va_end(ap);
- return -1;
- }
-
- tmp = tmp2;
-
- memcpy(tmp + tmp_length, payload, payload_length);
- tmp_length += payload_length;
-
- payload = va_arg(ap, void *);
- }
-
- va_end(ap);
-
- h = (struct gg_header*) tmp;
- h->type = gg_fix32(type);
- h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
-
- if ((gg_debug_level & GG_DEBUG_DUMP)) {
- unsigned int i;
-
- gg_debug(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");
- }
-
- 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);
- return -1;
- }
-
- free(tmp);
- return 0;
-}
-
-/*
- * gg_session_callback() // funkcja wewnętrzna
- *
- * wywoływany z gg_session->callback, wykonuje gg_watch_fd() i pakuje
- * do gg_session->event jego wynik.
- */
-static int gg_session_callback(struct gg_session *s)
-{
- if (!s) {
- errno = EFAULT;
- return -1;
- }
-
- return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1;
-}
-
-/*
- * gg_login()
- *
- * rozpoczyna procedurę łączenia się z serwerem. resztę obsługuje się przez
- * gg_watch_fd().
- *
- * UWAGA! program musi obsłużyć SIGCHLD, jeśli łączy się asynchronicznie,
- * żeby poprawnie zamknąć proces resolvera.
- *
- * - p - struktura opisująca początkowy stan. wymagane pola: uin,
- * password
- *
- * w przypadku błędu NULL, jeśli idzie dobrze (async) albo poszło
- * dobrze (sync), zwróci wskaźnik do zaalokowanej struct gg_session.
- */
-struct gg_session *gg_login(const struct gg_login_params *p)
-{
- struct gg_session *sess = NULL;
- char *hostname;
- int port;
-
- if (!p) {
- gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
- errno = EFAULT;
- return NULL;
- }
-
- gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);
-
- if (!(sess = malloc(sizeof(struct gg_session)))) {
- gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
- goto fail;
- }
-
- memset(sess, 0, sizeof(struct gg_session));
-
- if (!p->password || !p->uin) {
- gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
- errno = EFAULT;
- goto fail;
- }
-
- if (!(sess->password = strdup(p->password))) {
- gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
- 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");
- goto fail;
- }
-
- sess->uin = p->uin;
- sess->state = GG_STATE_RESOLVING;
- sess->check = GG_CHECK_READ;
- sess->timeout = GG_DEFAULT_TIMEOUT;
- sess->async = p->async;
- sess->type = GG_SESSION_GG;
- sess->initial_status = p->status;
- sess->callback = gg_session_callback;
- sess->destroy = gg_free_session;
- sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT);
- sess->server_addr = p->server_addr;
- sess->external_port = p->external_port;
- sess->external_addr = p->external_addr;
- sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION;
- if (p->era_omnix)
- sess->protocol_version |= GG_ERA_OMNIX_MASK;
- if (p->has_audio)
- sess->protocol_version |= 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;
-
- if (p->tls == 1) {
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
- char buf[1024];
-
- OpenSSL_add_ssl_algorithms();
-
- if (!RAND_status()) {
- char rdata[1024];
- struct {
- time_t time;
- void *ptr;
- } rstruct;
-
- time(&rstruct.time);
- rstruct.ptr = (void *) &rstruct;
-
- RAND_seed((void *) rdata, sizeof(rdata));
- RAND_seed((void *) &rstruct, sizeof(rstruct));
- }
-
- sess->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
-
- if (!sess->ssl_ctx) {
- ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
- gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf);
- goto fail;
- }
-
- SSL_CTX_set_verify(sess->ssl_ctx, SSL_VERIFY_NONE, NULL);
-
- sess->ssl = SSL_new(sess->ssl_ctx);
-
- if (!sess->ssl) {
- ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
- gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf);
- goto fail;
- }
-#else
- 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;
- } else {
- hostname = GG_APPMSG_HOST;
- port = GG_APPMSG_PORT;
- }
-
- if (!p->async) {
- struct in_addr a;
-
- 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))) {
- 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;
- }
-
- sess->hub_addr = a.s_addr;
-
- if (gg_proxy_enabled)
- sess->proxy_addr = a.s_addr;
-
- 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 (p->server_addr && p->server_port)
- sess->state = GG_STATE_CONNECTING_GG;
- else
- sess->state = GG_STATE_CONNECTING_HUB;
-
- while (sess->state != GG_STATE_CONNECTED) {
- struct gg_event *e;
-
- if (!(e = gg_watch_fd(sess))) {
- gg_debug(GG_DEBUG_MISC, "// gg_login() critical error in gg_watch_fd()\n");
- goto fail;
- }
-
- if (e->type == GG_EVENT_CONN_FAILED) {
- errno = EACCES;
- gg_debug(GG_DEBUG_MISC, "// gg_login() could not login\n");
- gg_event_free(e);
- goto fail;
- }
-
- gg_event_free(e);
- }
-
- return sess;
- }
-
- if (!sess->server_addr || gg_proxy_enabled) {
-#ifndef __GG_LIBGADU_HAVE_PTHREAD
- if (gg_resolve(&sess->fd, &sess->pid, hostname)) {
-#else
- if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) {
-#endif
- gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail;
- }
- } else {
- if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
- gg_debug(GG_DEBUG_MISC, "// gg_login() direct connection failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail;
- }
- sess->state = GG_STATE_CONNECTING_GG;
- sess->check = GG_CHECK_WRITE;
- }
-
- return sess;
-
-fail:
- if (sess) {
- if (sess->password)
- free(sess->password);
- if (sess->initial_descr)
- free(sess->initial_descr);
- free(sess);
- }
-
- return NULL;
-}
-
-/*
- * gg_free_session()
- *
- * próbuje zamknąć połączenia i zwalnia pamięć zajmowaną przez sesję.
- *
- * - sess - opis sesji
- */
-void gg_free_session(struct gg_session *sess)
-{
- if (!sess)
- return;
-
- /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
-
- if (sess->password)
- free(sess->password);
-
- if (sess->initial_descr)
- free(sess->initial_descr);
-
- if (sess->client_version)
- free(sess->client_version);
-
- if (sess->header_buf)
- free(sess->header_buf);
-
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
- if (sess->ssl)
- SSL_free(sess->ssl);
-
- if (sess->ssl_ctx)
- 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;
- }
-#else
- if (sess->pid != -1) {
- kill(sess->pid, SIGTERM);
- waitpid(sess->pid, NULL, WNOHANG);
- }
-#endif
-
- if (sess->fd != -1)
- close(sess->fd);
-
- while (sess->images)
- gg_image_queue_remove(sess, sess->images, 1);
-
- free(sess);
-}
-
-/*
- * gg_change_status()
- *
- * zmienia status użytkownika. przydatne do /away i /busy oraz /quit.
- *
- * - sess - opis sesji
- * - status - nowy status użytkownika
- *
- * 0, -1.
- */
-int gg_change_status(struct gg_session *sess, int status)
-{
- struct gg_new_status p;
-
- gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
-
- if (!sess) {
- errno = EFAULT;
- return -1;
- }
-
- 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), NULL);
-}
-
-/*
- * gg_change_status_descr()
- *
- * zmienia status użytkownika na opisowy.
- *
- * - sess - opis sesji
- * - status - nowy status użytkownika
- * - descr - opis statusu
- *
- * 0, -1.
- */
-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;
- }
-
- 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);
-}
-
-/*
- * gg_change_status_descr_time()
- *
- * zmienia status użytkownika na opisowy z godziną powrotu.
- *
- * - sess - opis sesji
- * - status - nowy status użytkownika
- * - descr - opis statusu
- * - time - czas w formacie uniksowym
- *
- * 0, -1.
- */
-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(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
-
- if (!sess || !descr || !time) {
- errno = EFAULT;
- return -1;
- }
-
- if (sess->state != GG_STATE_CONNECTED) {
- errno = ENOTCONN;
- return -1;
- }
-
- p.status = gg_fix32(status);
-
- sess->status = status;
-
- newtime = gg_fix32(time);
-
- 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);
-}
-
-/*
- * gg_logoff()
- *
- * wylogowuje użytkownika i zamyka połączenie, ale nie zwalnia pamięci.
- *
- * - sess - opis sesji
- */
-void gg_logoff(struct gg_session *sess)
-{
- 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
-
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
- if (sess->resolver) {
- pthread_cancel(*((pthread_t*) sess->resolver));
- free(sess->resolver);
- sess->resolver = NULL;
- }
-#else
- if (sess->pid != -1) {
- kill(sess->pid, SIGTERM);
- waitpid(sess->pid, NULL, WNOHANG);
- sess->pid = -1;
- }
-#endif
-
- if (sess->fd != -1) {
- shutdown(sess->fd, SHUT_RDWR);
- close(sess->fd);
- sess->fd = -1;
- }
-}
-
-/*
- * gg_image_request()
- *
- * wysyła żądanie wysłania obrazka o podanych parametrach.
- *
- * - sess - opis sesji
- * - recipient - numer adresata
- * - size - rozmiar obrazka
- * - crc32 - suma kontrolna obrazka
- *
- * 0/-1
- */
-int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
-{
- struct gg_send_msg s;
- struct gg_msg_image_request r;
- char dummy = 0;
- int res;
-
- gg_debug(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;
- }
-
- if (size < 0) {
- errno = EINVAL;
- return -1;
- }
-
- s.recipient = gg_fix32(recipient);
- s.seq = gg_fix32(0);
- s.msgclass = gg_fix32(GG_CLASS_MSG);
-
- 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) {
- struct gg_image_queue *q = malloc(sizeof(*q));
- char *buf;
-
- if (!q) {
- gg_debug(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");
- free(q);
- return -1;
- }
-
- memset(q, 0, sizeof(*q));
-
- q->sender = recipient;
- q->size = size;
- q->crc32 = crc32;
- q->image = buf;
-
- if (!sess->images)
- sess->images = q;
- else {
- struct gg_image_queue *qq;
-
- for (qq = sess->images; qq->next; qq = qq->next)
- ;
-
- qq->next = q;
- }
- }
-
- return res;
-}
-
-/*
- * gg_image_reply()
- *
- * wysyła żądany obrazek.
- *
- * - sess - opis sesji
- * - recipient - numer adresata
- * - filename - nazwa pliku
- * - image - bufor z obrazkiem
- * - size - rozmiar obrazka
- *
- * 0/-1
- */
-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;
- const char *tmp;
- 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);
-
- if (!sess || !filename || !image) {
- errno = EFAULT;
- return -1;
- }
-
- if (sess->state != GG_STATE_CONNECTED) {
- errno = ENOTCONN;
- return -1;
- }
-
- if (size < 0) {
- errno = EINVAL;
- return -1;
- }
-
- /* wytnij ścieżki, zostaw tylko nazwę pliku */
- while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
- filename = tmp + 1;
-
- if (strlen(filename) < 1 || strlen(filename) > 1024) {
- errno = EINVAL;
- return -1;
- }
-
- s.recipient = gg_fix32(recipient);
- s.seq = gg_fix32(0);
- s.msgclass = gg_fix32(GG_CLASS_MSG);
-
- buf[0] = 0;
- r = (void*) &buf[1];
-
- r->flag = 0x05;
- r->size = gg_fix32(size);
- r->crc32 = gg_fix32(gg_crc32(0, image, size));
-
- while (size > 0) {
- int buflen, chunklen;
-
- /* \0 + struct gg_msg_image_reply */
- buflen = sizeof(struct gg_msg_image_reply) + 1;
-
- /* w pierwszym kawałku jest nazwa pliku */
- if (r->flag == 0x05) {
- strcpy(buf + buflen, filename);
- buflen += strlen(filename) + 1;
- }
-
- 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)
- break;
-
- r->flag = 0x06;
- }
-
- return res;
-}
-
-/*
- * gg_send_message_ctcp()
- *
- * wysyła wiadomość do innego użytkownika. zwraca losowy numer
- * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomości
- * - recipient - numer adresata
- * - message - treść wiadomości
- * - message_len - długość
- *
- * numer sekwencyjny wiadomości lub -1 w przypadku błędu.
- */
-int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
-{
- 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);
-}
-
-/*
- * gg_send_message()
- *
- * wysyła wiadomość do innego użytkownika. zwraca losowy numer
- * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomości
- * - recipient - numer adresata
- * - message - treść wiadomości
- *
- * numer sekwencyjny wiadomości 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ła kolorową wiadomość do innego użytkownika. zwraca losowy numer
- * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomości
- * - recipient - numer adresata
- * - message - treść wiadomości
- * - format - informacje o formatowaniu
- * - formatlen - długość informacji o formatowaniu
- *
- * numer sekwencyjny wiadomości 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);
-
- if (!sess) {
- errno = EFAULT;
- return -1;
- }
-
- if (sess->state != GG_STATE_CONNECTED) {
- errno = ENOTCONN;
- 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(message) + 1, format, formatlen, NULL) == -1)
- return -1;
-
- return gg_fix32(s.seq);
-}
-
-/*
- * gg_send_message_confer()
- *
- * wysyła wiadomość do kilku użytkownikow (konferencja). zwraca losowy numer
- * sekwencyjny, który można zignorować albo wykorzystać do potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomości
- * - recipients_count - ilość adresatów
- * - recipients - numerki adresatów
- * - message - treść wiadomości
- *
- * numer sekwencyjny wiadomości 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ła kolorową wiadomość do kilku użytkownikow (konferencja). zwraca
- * losowy numer sekwencyjny, który można zignorować albo wykorzystać do
- * potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomości
- * - recipients_count - ilość adresatów
- * - recipients - numerki adresatów
- * - message - treść wiadomości
- * - format - informacje o formatowaniu
- * - formatlen - długość informacji o formatowaniu
- *
- * numer sekwencyjny wiadomości 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(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ła 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ła serwerowi listę kontaktów (wraz z odpowiadającymi im typami userów),
- * dzięki czemu wie, czyj stan nas interesuje.
- *
- * - sess - opis sesji
- * - userlist - wskaźnik do tablicy numerów
- * - types - wskaźnik do tablicy typów użytkowników
- * - count - ilość numerków
- *
- * 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;
- } else {
- part_count = count;
- packet_type = GG_NOTIFY_LAST;
- }
-
- 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++) {
- 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;
- break;
- }
-
- count -= part_count;
- userlist += part_count;
- types += part_count;
-
- free(n);
- }
-
- return res;
-}
-
-/*
- * gg_notify()
- *
- * wysyła serwerowi listę kontaktów, dzięki czemu wie, czyj stan nas
- * interesuje.
- *
- * - sess - opis sesji
- * - userlist - wskaźnik do tablicy numerów
- * - count - ilość numerków
- *
- * 0, -1.
- */
-int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
-{
- struct gg_notify *n;
- uin_t *u;
- int i, res = 0;
-
- gg_debug(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;
- }
-
- 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;
- } else {
- 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++) {
- 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);
- break;
- }
-
- free(n);
-
- userlist += part_count;
- count -= part_count;
- }
-
- return res;
-}
-
-/*
- * gg_add_notify_ex()
- *
- * dodaje do listy kontaktów dany numer w trakcie połączenia.
- * dodawanemu użytkownikowi określamy jego typ (patrz protocol.html)
- *
- * - sess - opis sesji
- * - uin - numer
- * - type - typ
- *
- * 0, -1.
- */
-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_add_notify()
- *
- * dodaje do listy kontaktów 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 w trakcie połączenia.
- * usuwanemu użytkownikowi określamy 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;
- }
-
- 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 z listy kontaktów w trakcie połączenia.
- *
- * - sess - opis sesji
- * - uin - numer
- *
- * 0, -1.
- */
-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 żądanie/zapytanie listy kontaktów na serwerze.
- *
- * - sess - opis sesji
- * - type - rodzaj zapytania/żądania
- * - request - treść zapytania/żądania (może być NULL)
- *
- * 0, -1
- */
-int gg_userlist_request(struct gg_session *sess, char type, const char *request)
-{
- int len;
-
- if (!sess) {
- errno = EFAULT;
- return -1;
- }
-
- if (sess->state != GG_STATE_CONNECTED) {
- errno = ENOTCONN;
- return -1;
- }
-
- if (!request) {
- sess->userlist_blocks = 1;
- return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
- }
-
- len = strlen(request);
-
- sess->userlist_blocks = 0;
-
- while (len > 2047) {
- sess->userlist_blocks++;
-
- if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
- return -1;
-
- if (type == GG_USERLIST_PUT)
- type = GG_USERLIST_PUT_MORE;
-
- request += 2047;
- len -= 2047;
- }
-
- sess->userlist_blocks++;
-
- return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
-}
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */