summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Bailey <rekkanoryo@rekkanoryo.org>2010-02-21 16:52:42 +0000
committerJohn Bailey <rekkanoryo@rekkanoryo.org>2010-02-21 16:52:42 +0000
commit8bd990d1bf845c7f2d94ff85a3b8f7ae945c5005 (patch)
tree38f689c8efc671aa3ecc128307f836457ad85bb6
parent944a5d0d1fa30e136bb82626038dfc1bf07d6419 (diff)
downloadpidgin-8bd990d1bf845c7f2d94ff85a3b8f7ae945c5005.tar.gz
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.
-rw-r--r--configure.ac1
-rw-r--r--libpurple/protocols/gg/Makefile.am16
-rw-r--r--libpurple/protocols/gg/Makefile.mingw3
-rw-r--r--libpurple/protocols/gg/lib/common.c754
-rw-r--r--libpurple/protocols/gg/lib/compat.h12
-rw-r--r--libpurple/protocols/gg/lib/dcc.c529
-rw-r--r--libpurple/protocols/gg/lib/dcc7.c1212
-rw-r--r--libpurple/protocols/gg/lib/events.c1593
-rw-r--r--libpurple/protocols/gg/lib/http.c177
-rw-r--r--libpurple/protocols/gg/lib/libgadu-internal.h30
-rw-r--r--libpurple/protocols/gg/lib/libgadu.c2265
-rw-r--r--libpurple/protocols/gg/lib/libgadu.h2175
-rw-r--r--libpurple/protocols/gg/lib/obsolete.c41
-rw-r--r--libpurple/protocols/gg/lib/protocol.h165
-rw-r--r--libpurple/protocols/gg/lib/pubdir.c342
-rw-r--r--libpurple/protocols/gg/lib/pubdir50.c287
-rw-r--r--libpurple/protocols/gg/lib/resolver.c753
-rw-r--r--libpurple/protocols/gg/lib/resolver.h28
-rw-r--r--libpurple/protocols/gg/lib/sha1.c303
19 files changed, 7631 insertions, 3055 deletions
diff --git a/configure.ac b/configure.ac
index 5b13f8b816..75c1016b79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2583,6 +2583,7 @@ fi
echo Build with Cyrus SASL support. : $enable_cyrus_sasl
echo Use kerberos 4 with zephyr.... : $kerberos
echo Use external libzephyr........ : $zephyr
+echo Use external libgadu.......... : $gadu_libs
echo Install pixmaps............... : $enable_pixmaps
echo Install translations.......... : $enable_i18n
echo Has you....................... : yes
diff --git a/libpurple/protocols/gg/Makefile.am b/libpurple/protocols/gg/Makefile.am
index 60f6804186..7bc99c9563 100644
--- a/libpurple/protocols/gg/Makefile.am
+++ b/libpurple/protocols/gg/Makefile.am
@@ -4,14 +4,19 @@ EXTRA_DIST = \
lib/compat.h \
lib/COPYING \
lib/dcc.c \
+ lib/dcc7.c \
lib/events.c \
lib/http.c \
lib/libgadu.c \
lib/libgadu-config.h \
lib/libgadu.h \
lib/obsolete.c \
+ lib/protocol.h \
+ lib/pubdir.c \
lib/pubdir50.c \
- lib/pubdir.c
+ lib/resolver.c \
+ lib/resolver.h \
+ lib/sha1.c
pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
@@ -20,16 +25,21 @@ INTGGSOURCES = \
lib/common.c \
lib/compat.h \
lib/dcc.c \
+ lib/dcc7.c \
lib/events.c \
lib/http.c \
lib/libgadu.c \
lib/libgadu-config.h \
lib/libgadu.h \
lib/obsolete.c \
+ lib/protocol.h \
+ lib/pubdir.c \
lib/pubdir50.c \
- lib/pubdir.c
+ lib/resolver.c \
+ lib/resolver.h \
+ lib/sha1.c
-INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib
+INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED
endif
GGSOURCES = \
diff --git a/libpurple/protocols/gg/Makefile.mingw b/libpurple/protocols/gg/Makefile.mingw
index 3c4e8f4c95..030589224e 100644
--- a/libpurple/protocols/gg/Makefile.mingw
+++ b/libpurple/protocols/gg/Makefile.mingw
@@ -41,12 +41,15 @@ LIB_PATHS += -L$(GTK_TOP)/lib \
##
C_SRC = \
lib/common.c \
+ lib/dcc.c \
+ lib/dcc7.c \
lib/events.c \
lib/http.c \
lib/libgadu.c \
lib/obsolete.c \
lib/pubdir.c \
lib/pubdir50.c \
+ lib/resolver.c \
buddylist.c \
confer.c \
gg.c \
diff --git a/libpurple/protocols/gg/lib/common.c b/libpurple/protocols/gg/lib/common.c
index 9d2b49494f..e57f5ced88 100644
--- a/libpurple/protocols/gg/lib/common.c
+++ b/libpurple/protocols/gg/lib/common.c
@@ -1,8 +1,8 @@
-/* $Id: common.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: common.c 878 2009-11-16 23:48:19Z wojtekka $ */
/*
* (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- * Robert J. Wony <speedy@ziew.org>
+ * Robert J. Woźny <speedy@ziew.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License Version
@@ -15,13 +15,38 @@
*
* 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"
+/*
+ * Funkcje konwersji między UTF-8 i CP1250 są oparte o kod biblioteki iconv.
+ * Informacje o prawach autorskich oryginalnego kodu zamieszczono poniżej:
+ *
+ * Copyright (C) 1999-2001, 2004 Free Software Foundation, Inc.
+ * This file is part of the GNU LIBICONV Library.
+ *
+ * The GNU LIBICONV Library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * The GNU LIBICONV Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU LIBICONV Library; see the file COPYING.LIB.
+ * If not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Fifth Floor, Boston, MA 02110-1301, USA.
+ */
-#ifndef _WIN32
+/**
+ * \file common.c
+ *
+ * \brief Funkcje wykorzystywane przez różne moduły biblioteki
+ */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -30,94 +55,133 @@
#ifdef sun
# include <sys/filio.h>
#endif
-#endif
#include <errno.h>
#include <fcntl.h>
-#ifndef _WIN32
#include <netdb.h>
-#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include "libgadu.h"
+#include "libgadu-internal.h"
+
+/**
+ * Plik, do którego będą przekazywane informacje odpluskwiania.
+ *
+ * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację
+ * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub
+ * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane
+ * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr).
+ *
+ * \ingroup debug
+ */
FILE *gg_debug_file = NULL;
#ifndef GG_DEBUG_DISABLE
-/*
- * gg_debug() // funkcja wewntrzna
+/**
+ * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji.
+ *
+ * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w
+ * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana.
+ * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu.
+ *
+ * \param sess Struktura sesji (może być \c NULL)
+ * \param level Poziom informacji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
+ */
+void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
+{
+ if (gg_debug_handler_session)
+ (*gg_debug_handler_session)(sess, level, format, ap);
+ else if (gg_debug_handler)
+ (*gg_debug_handler)(level, format, ap);
+ else if (gg_debug_level & level)
+ vfprintf(gg_debug_file ? gg_debug_file : stderr, format, ap);
+}
+
+
+/**
+ * \internal Przekazuje informację odpluskawiania.
*
- * wywietla komunikat o danym poziomie, o ile uytkownik sobie tego yczy.
+ * \param level Poziom wiadomości
+ * \param format Format wiadomości (zgodny z \c printf)
*
- * - level - poziom wiadomoci
- * - format... - tre wiadomoci (kompatybilna z printf())
+ * \ingroup debug
*/
void gg_debug(int level, const char *format, ...)
{
va_list ap;
int old_errno = errno;
-
- if (gg_debug_handler) {
- va_start(ap, format);
- (*gg_debug_handler)(level, format, ap);
- va_end(ap);
-
- goto cleanup;
- }
-
- if ((gg_debug_level & level)) {
- va_start(ap, format);
- vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap);
- va_end(ap);
- }
+ va_start(ap, format);
+ gg_debug_common(NULL, level, format, ap);
+ va_end(ap);
+ errno = old_errno;
+}
-cleanup:
+/**
+ * \internal Przekazuje informację odpluskwiania związaną z sesją.
+ *
+ * \param sess Struktura sesji
+ * \param level Poziom wiadomości
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \ingroup debug
+ */
+void gg_debug_session(struct gg_session *sess, int level, const char *format, ...)
+{
+ va_list ap;
+ int old_errno = errno;
+ va_start(ap, format);
+ gg_debug_common(sess, level, format, ap);
+ va_end(ap);
errno = old_errno;
}
#endif
-/*
- * gg_vsaprintf() // funkcja pomocnicza
+/**
+ * \internal Odpowiednik funkcji \c vsprintf alokujący miejsce na wynik.
+ *
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
*
- * robi dokadnie to samo, co vsprintf(), tyle e alokuje sobie wczeniej
- * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj
- * vsnprintf() zgodn z C99, jak i na wczeniejszych.
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
*
- * - format - opis wywietlanego tekstu jak dla printf()
- * - ap - lista argumentw dla printf()
+ * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
*
- * zaalokowany bufor, ktry naley pniej zwolni, lub NULL
- * jeli nie udao si wykona zadania.
+ * \ingroup helper
*/
char *gg_vsaprintf(const char *format, va_list ap)
{
int size = 0;
const char *start;
char *buf = NULL;
-
-#ifdef __GG_LIBGADU_HAVE_VA_COPY
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
va_list aq;
va_copy(aq, ap);
#else
-# ifdef __GG_LIBGADU_HAVE___VA_COPY
+# ifdef GG_CONFIG_HAVE___VA_COPY
va_list aq;
__va_copy(aq, ap);
# endif
#endif
- start = format;
+ start = format;
-#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF
+#ifndef GG_CONFIG_HAVE_C99_VSNPRINTF
{
int res;
char *tmp;
-
+
size = 128;
do {
size *= 2;
@@ -132,9 +196,9 @@ char *gg_vsaprintf(const char *format, va_list ap)
#else
{
char tmp[2];
-
- /* libce Solarisa przy buforze NULL zawsze zwracaj -1, wic
- * musimy poda co istniejcego jako cel printf()owania. */
+
+ /* libce Solarisa przy buforze NULL zawsze zwracają -1, więc
+ * musimy podać coś istniejącego jako cel printf()owania. */
size = vsnprintf(tmp, sizeof(tmp), format, ap);
if (!(buf = malloc(size + 1)))
return NULL;
@@ -142,33 +206,33 @@ char *gg_vsaprintf(const char *format, va_list ap)
#endif
format = start;
-
-#ifdef __GG_LIBGADU_HAVE_VA_COPY
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
vsnprintf(buf, size + 1, format, aq);
va_end(aq);
#else
-# ifdef __GG_LIBGADU_HAVE___VA_COPY
+# ifdef GG_CONFIG_HAVE___VA_COPY
vsnprintf(buf, size + 1, format, aq);
va_end(aq);
# else
vsnprintf(buf, size + 1, format, ap);
# endif
#endif
-
+
return buf;
}
-/*
- * gg_saprintf() // funkcja pomocnicza
+/**
+ * \internal Odpowiednik funkcji \c sprintf alokujący miejsce na wynik.
*
- * robi dokadnie to samo, co sprintf(), tyle e alokuje sobie wczeniej
- * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj
- * vsnprintf() zgodn z C99, jak i na wczeniejszych.
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
*
- * - format... - tre taka sama jak w funkcji printf()
+ * \param format Format wiadomości (zgodny z \c printf)
*
- * zaalokowany bufor, ktry naley pniej zwolni, lub NULL
- * jeli nie udao si wykona zadania.
+ * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
+ *
+ * \ingroup helper
*/
char *gg_saprintf(const char *format, ...)
{
@@ -182,18 +246,17 @@ char *gg_saprintf(const char *format, ...)
return res;
}
-/*
- * gg_get_line() // funkcja pomocnicza
- *
- * podaje kolejn lini z bufora tekstowego. niszczy go bezpowrotnie, dzielc
- * na kolejne stringi. zdarza si, nie ma potrzeby pisania funkcji dublujcej
- * bufor eby tylko mie nieruszone dane wejciowe, skoro i tak nie bd nam
- * poniej potrzebne. obcina `\r\n'.
- *
- * - ptr - wskanik do zmiennej, ktra przechowuje aktualn pozycj
- * w przemiatanym buforze
- *
- * wskanik do kolejnej linii tekstu lub NULL, jeli to ju koniec bufora.
+/**
+ * \internal Pobiera linię tekstu z bufora.
+ *
+ * Funkcja niszczy bufor źródłowy bezpowrotnie, dzieląc go na kolejne ciągi
+ * znaków i obcina znaki końca linii.
+ *
+ * \param ptr Wskaźnik do zmiennej, która przechowuje aktualne położenie
+ * w analizowanym buforze
+ *
+ * \return Wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec
+ * bufora.
*/
char *gg_get_line(char **ptr)
{
@@ -207,27 +270,71 @@ char *gg_get_line(char **ptr)
if (!(foo = strchr(*ptr, '\n')))
*ptr += strlen(*ptr);
else {
+ size_t len;
*ptr = foo + 1;
*foo = 0;
- if (strlen(res) > 1 && res[strlen(res) - 1] == '\r')
- res[strlen(res) - 1] = 0;
+
+ len = strlen(res);
+
+ if (len > 1 && res[len - 1] == '\r')
+ res[len - 1] = 0;
}
return res;
}
-/*
- * gg_connect() // funkcja pomocnicza
+/**
+ * \internal Czyta linię tekstu z gniazda.
*
- * czy si z serwerem. pierwszy argument jest typu (void *), eby nie
- * musie niczego inkludowa w libgadu.h i nie psu jaki gupich zalenoci
- * na dziwnych systemach.
+ * Funkcja czyta tekst znak po znaku, więc nie jest efektywna, ale dzięki
+ * brakowi buforowania, nie koliduje z innymi funkcjami odczytu.
*
- * - addr - adres serwera (struct in_addr *)
- * - port - port serwera
- * - async - asynchroniczne poczenie
+ * \param sock Deskryptor gniazda
+ * \param buf Wskaźnik do bufora
+ * \param length Długość bufora
*
- * deskryptor gniazda lub -1 w przypadku bdu (kod bdu w zmiennej errno).
+ * \return Zwraca \c buf jeśli się powiodło, lub \c NULL w przypadku błędu.
+ */
+char *gg_read_line(int sock, char *buf, int length)
+{
+ int ret;
+
+ if (!buf || length < 0)
+ return NULL;
+
+ for (; length > 1; buf++, length--) {
+ do {
+ if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) {
+ gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno));
+ *buf = 0;
+ return NULL;
+ } else if (ret == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n");
+ *buf = 0;
+ return NULL;
+ }
+ } while (ret == -1 && errno == EINTR);
+
+ if (*buf == '\n') {
+ buf++;
+ break;
+ }
+ }
+
+ *buf = 0;
+ return buf;
+}
+
+/**
+ * \internal Nawiązuje połączenie TCP.
+ *
+ * \param addr Wskaźnik na strukturę \c in_addr z adresem serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego połączenia
+ *
+ * \return Deskryptor gniazda lub -1 w przypadku błędu
+ *
+ * \ingroup helper
*/
int gg_connect(void *addr, int port, int async)
{
@@ -237,7 +344,7 @@ int gg_connect(void *addr, int port, int async)
struct sockaddr_in myaddr;
gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
-
+
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno));
return -1;
@@ -250,6 +357,9 @@ int gg_connect(void *addr, int port, int async)
if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno));
+ errno2 = errno;
+ close(sock);
+ errno = errno2;
return -1;
}
@@ -274,7 +384,7 @@ int gg_connect(void *addr, int port, int async)
sin.sin_port = htons(port);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = a->s_addr;
-
+
if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
if (errno && (!async || errno != EINPROGRESS)) {
gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno));
@@ -285,84 +395,46 @@ int gg_connect(void *addr, int port, int async)
}
gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
}
-
+
return sock;
}
-/*
- * gg_read_line() // funkcja pomocnicza
- *
- * czyta jedn lini tekstu z gniazda.
- *
- * - sock - deskryptor gniazda
- * - buf - wskanik do bufora
- * - length - dugo bufora
+/**
+ * \internal Usuwa znaki końca linii.
*
- * jeli trafi na bd odczytu lub podano nieprawidowe parametry, zwraca NULL.
- * inaczej zwraca buf.
- */
-char *gg_read_line(int sock, char *buf, int length)
-{
- int ret;
-
- if (!buf || length < 0)
- return NULL;
-
- for (; length > 1; buf++, length--) {
- do {
- if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) {
- gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno));
- *buf = 0;
- return NULL;
- } else if (ret == 0) {
- gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n");
- *buf = 0;
- return NULL;
- }
- } while (ret == -1 && errno == EINTR);
-
- if (*buf == '\n') {
- buf++;
- break;
- }
- }
-
- *buf = 0;
- return buf;
-}
-
-/*
- * gg_chomp() // funkcja pomocnicza
+ * Funkcja działa bezpośrednio na buforze.
*
- * ucina "\r\n" lub "\n" z koca linii.
+ * \param line Bufor z tekstem
*
- * - line - linia do przycicia
+ * \ingroup helper
*/
void gg_chomp(char *line)
{
int len;
-
+
if (!line)
return;
len = strlen(line);
-
+
if (len > 0 && line[len - 1] == '\n')
line[--len] = 0;
if (len > 0 && line[len - 1] == '\r')
line[--len] = 0;
}
-/*
- * gg_urlencode() // funkcja wewntrzna
+/**
+ * \internal Koduje ciąg znaków do postacji adresu HTTP.
*
- * zamienia podany tekst na cig znakw do formularza http. przydaje si
- * przy rnych usugach katalogu publicznego.
+ * Zamienia znaki niedrukowalne, spoza ASCII i mające specjalne znaczenie
+ * dla protokołu HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkową
+ * wartością znaku.
+ *
+ * \param str Ciąg znaków do zakodowania
*
- * - str - cig znakw do zakodowania
+ * \return Zaalokowany bufor lub \c NULL w przypadku błędu.
*
- * zaalokowany bufor, ktry naley pniej zwolni albo NULL
- * w przypadku bdu.
+ * \ingroup helper
*/
char *gg_urlencode(const char *str)
{
@@ -400,16 +472,19 @@ char *gg_urlencode(const char *str)
return buf;
}
-/*
- * gg_http_hash() // funkcja wewntrzna
+/**
+ * \internal Wyznacza skrót dla usług HTTP.
+ *
+ * Funkcja jest wykorzystywana do wyznaczania skrótu adresu e-mail, hasła
+ * i innych wartości przekazywanych jako parametry usług HTTP.
*
- * funkcja liczca hash dla adresu e-mail, hasa i paru innych.
+ * W parametrze \c format należy umieścić znaki określające postać kolejnych
+ * parametrów: \c 's' jeśli parametr jest ciągiem znaków, \c 'u' jeśli jest
+ * liczbą.
*
- * - format... - format kolejnych parametrw ('s' jeli dany parametr jest
- * cigiem znakw lub 'u' jeli numerem GG)
+ * \param format Format kolejnych parametrów (niezgodny z \c printf)
*
- * hash wykorzystywany przy rejestracji i wszelkich manipulacjach wasnego
- * wpisu w katalogu publicznym.
+ * \return Wartość skrótu
*/
int gg_http_hash(const char *format, ...)
{
@@ -428,7 +503,7 @@ int gg_http_hash(const char *format, ...)
} else {
if (!(arg = va_arg(ap, char*)))
arg = "";
- }
+ }
i = 0;
while ((c = (unsigned char) arg[i++]) != 0) {
@@ -442,95 +517,6 @@ int gg_http_hash(const char *format, ...)
return (b < 0 ? -b : b);
}
-/*
- * gg_gethostbyname() // funkcja pomocnicza
- *
- * odpowiednik gethostbyname() troszczcy si o wspbieno, gdy mamy do
- * dyspozycji funkcj gethostbyname_r().
- *
- * - hostname - nazwa serwera
- *
- * zwraca wskanik na struktur in_addr, ktr naley zwolni.
- */
-struct in_addr *gg_gethostbyname(const char *hostname)
-{
- struct in_addr *addr = NULL;
-
-#ifdef HAVE_GETHOSTBYNAME_R
- char *tmpbuf = NULL, *buf = NULL;
- struct hostent *hp = NULL, *hp2 = NULL;
- int h_errnop, ret;
- size_t buflen = 1024;
- int new_errno;
-
- new_errno = ENOMEM;
-
- if (!(addr = malloc(sizeof(struct in_addr))))
- goto cleanup;
-
- if (!(hp = calloc(1, sizeof(*hp))))
- goto cleanup;
-
- if (!(buf = malloc(buflen)))
- goto cleanup;
-
- tmpbuf = buf;
-
- while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) {
- buflen *= 2;
-
- if (!(tmpbuf = realloc(buf, buflen)))
- break;
-
- buf = tmpbuf;
- }
-
- if (ret)
- new_errno = h_errnop;
-
- if (ret || !hp2 || !tmpbuf)
- goto cleanup;
-
- memcpy(addr, hp->h_addr, sizeof(struct in_addr));
-
- free(buf);
- free(hp);
-
- return addr;
-
-cleanup:
- errno = new_errno;
-
- if (addr)
- free(addr);
- if (hp)
- free(hp);
- if (buf)
- free(buf);
-
- return NULL;
-#else
- struct hostent *hp;
-
- if (!(addr = malloc(sizeof(struct in_addr)))) {
- goto cleanup;
- }
-
- if (!(hp = gethostbyname(hostname)))
- goto cleanup;
-
- memcpy(addr, hp->h_addr, sizeof(struct in_addr));
-
- return addr;
-
-cleanup:
- if (addr)
- free(addr);
-
- return NULL;
-#endif
-}
-
#ifdef ASSIGN_SOCKETS_TO_THREADS
typedef struct gg_win32_thread {
@@ -541,23 +527,22 @@ typedef struct gg_win32_thread {
struct gg_win32_thread *gg_win32_threads = 0;
-/*
- * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32
+/**
+ * \internal Zwraca deskryptor gniazda, które było ostatnio tworzone dla wątku.
*
- * zwraca deskryptor gniazda, ktre byo ostatnio tworzone dla wtku
- * o podanym identyfikatorze.
+ * Jeśli na win32 przy połączeniach synchronicznych zapamiętamy w jakim
+ * wątku uruchomiliśmy funkcję, która się z czymkolwiek łączy, to z osobnego
+ * wątku możemy anulować połączenie poprzez \c gg_win32_thread_socket(watek,-1)
*
- * jeli na win32 przy poczeniach synchronicznych zapamitamy w jakim
- * wtku uruchomilimy funkcj, ktra si z czymkolwiek czy, to z osobnego
- * wtku moemy anulowa poczenie poprzez gg_win32_thread_socket(watek, -1);
- *
- * - thread_id - id wtku. jeli jest rwne 0, brany jest aktualny wtek,
- * jeli rwne -1, usuwa wpis o podanym sockecie.
- * - socket - deskryptor gniazda. jeli rwne 0, zwraca deskryptor gniazda
- * dla podanego wtku, jeli rwne -1, usuwa wpis, jeli co
- * innego, ustawia dla podanego wtku dany numer deskryptora.
+ * \param thread_id Identyfikator wątku (jeśli jest równe 0, brany jest
+ * aktualny wątek, jeśli równe -1, usuwa wpis dotyczący
+ * danego gniazda sockecie)
+ * \param socket Deskryptor gniazda (jeśli równe 0, zwraca deskryptor gniazda
+ * dla podanego wątku, jeśli równe -1, usuwa wpis, jeśli coś
+ * innego, ustawia dla podanego wątku dany numer deskryptora)
*
- * jeli socket jest rwne 0, zwraca deskryptor gniazda dla podanego wtku.
+ * \return Jeśli socket jest równe 0, zwraca deskryptor gniazda dla podanego
+ * wątku.
*/
int gg_win32_thread_socket(int thread_id, int socket)
{
@@ -567,7 +552,7 @@ int gg_win32_thread_socket(int thread_id, int socket)
if (!thread_id)
thread_id = GetCurrentThreadId();
-
+
while (wsk) {
if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) {
if (close) {
@@ -593,7 +578,7 @@ int gg_win32_thread_socket(int thread_id, int socket)
closesocket(socket);
if (close || !socket)
return 0;
-
+
/* Dodaje nowy element */
wsk = malloc(sizeof(gg_win32_thread));
wsk->id = thread_id;
@@ -606,28 +591,33 @@ int gg_win32_thread_socket(int thread_id, int socket)
#endif /* ASSIGN_SOCKETS_TO_THREADS */
+/**
+ * \internal Zestaw znaków kodowania base64.
+ */
static char gg_base64_charset[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-/*
- * gg_base64_encode()
+/**
+ * \internal Koduje ciąg znaków do base64.
+ *
+ * Wynik funkcji należy zwolnić za pomocą \c free.
*
- * zapisuje cig znakw w base64.
+ * \param buf Bufor z danami do zakodowania
*
- * - buf - cig znakw.
+ * \return Zaalokowany bufor z zakodowanymi danymi
*
- * zaalokowany bufor.
+ * \ingroup helper
*/
char *gg_base64_encode(const char *buf)
{
char *out, *res;
unsigned int i = 0, j = 0, k = 0, len = strlen(buf);
-
+
res = out = malloc((len / 3 + 1) * 4 + 2);
if (!res)
return NULL;
-
+
while (j <= len) {
switch (i % 4) {
case 0:
@@ -660,20 +650,22 @@ char *gg_base64_encode(const char *buf)
if (i % 4)
for (j = 0; j < 4 - (i % 4); j++, out++)
*out = '=';
-
+
*out = 0;
-
+
return res;
}
-/*
- * gg_base64_decode()
+/**
+ * \internal Dekoduje ciąg znaków zapisany w base64.
*
- * dekoduje cig znakw z base64.
+ * Wynik funkcji należy zwolnić za pomocą \c free.
*
- * - buf - cig znakw.
+ * \param buf Bufor źródłowy z danymi do zdekodowania
*
- * zaalokowany bufor.
+ * \return Zaalokowany bufor ze zdekodowanymi danymi
+ *
+ * \ingroup helper
*/
char *gg_base64_decode(const char *buf)
{
@@ -683,7 +675,7 @@ char *gg_base64_decode(const char *buf)
if (!buf)
return NULL;
-
+
save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
if (!save)
@@ -720,23 +712,24 @@ char *gg_base64_decode(const char *buf)
index %= 4;
}
*res = 0;
-
+
return save;
}
-/*
- * gg_proxy_auth() // funkcja wewntrzna
+/**
+ * \internal Tworzy nagłówek autoryzacji serwera pośredniczącego.
*
- * tworzy nagwek autoryzacji dla proxy.
- *
- * zaalokowany tekst lub NULL, jeli proxy nie jest wczone lub nie wymaga
- * autoryzacji.
+ * Dane pobiera ze zmiennych globalnych \c gg_proxy_username i
+ * \c gg_proxy_password.
+ *
+ * \return Zaalokowany bufor z tekstem lub NULL, jeśli serwer pośredniczący
+ * nie jest używany lub nie wymaga autoryzacji.
*/
char *gg_proxy_auth()
{
char *tmp, *enc, *out;
unsigned int tmp_size;
-
+
if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password)
return NULL;
@@ -749,14 +742,14 @@ char *gg_proxy_auth()
free(tmp);
return NULL;
}
-
+
free(tmp);
if (!(out = malloc(strlen(enc) + 40))) {
free(enc);
return NULL;
}
-
+
snprintf(out, strlen(enc) + 40, "Proxy-Authorization: Basic %s\r\n", enc);
free(enc);
@@ -764,13 +757,21 @@ char *gg_proxy_auth()
return out;
}
+/**
+ * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej.
+ */
static uint32_t gg_crc32_table[256];
+
+/**
+ * \internal Flaga wypełnienia tablicy pomocniczej do wyznaczania sumy
+ * kontrolnej.
+ */
static int gg_crc32_initialized = 0;
-/*
- * gg_crc32_make_table() // funkcja wewntrzna
+/**
+ * \internal Tworzy tablicę pomocniczą do wyznaczania sumy kontrolnej.
*/
-static void gg_crc32_make_table()
+static void gg_crc32_make_table(void)
{
uint32_t h = 1;
unsigned int i, j;
@@ -787,16 +788,15 @@ static void gg_crc32_make_table()
gg_crc32_initialized = 1;
}
-/*
- * gg_crc32()
+/**
+ * Wyznacza sumę kontrolną CRC32.
*
- * wyznacza sum kontroln CRC32 danego bloku danych.
+ * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeśli liczona
+ * jest suma kontrolna pierwszego bloku
+ * \param buf Bufor danych
+ * \param len Długość bufora danych
*
- * - crc - suma kontrola poprzedniego bloku danych lub 0 jeli pierwszy
- * - buf - bufor danych
- * - size - ilo danych
- *
- * suma kontrolna CRC32.
+ * \return Suma kontrolna.
*/
uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
{
@@ -814,6 +814,186 @@ uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
return crc ^ 0xffffffffL;
}
+/**
+ * \internal Tablica konwersji między CP1250 a UTF-8.
+ */
+static const uint16_t table_cp1250[] = {
+ 0x20ac, '?', 0x201a, '?', 0x201e, 0x2026, 0x2020, 0x2021,
+ '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,
+ '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
+ '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,
+ 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,
+ 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,
+ 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,
+ 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,
+ 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,
+ 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,
+ 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,
+ 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,
+ 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,
+ 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9,
+};
+
+/**
+ * \internal Zamienia tekst kodowany CP1250 na UTF-8.
+ *
+ * \param b Tekst źródłowy w CP1250.
+ *
+ * \return Zaalokowany bufor z tekstem w UTF-8.
+ */
+char *gg_cp_to_utf8(const char *b)
+{
+ unsigned char *buf = (unsigned char *) b;
+ char *newbuf;
+ int newlen = 0;
+ int i, j;
+
+ for (i = 0; buf[i]; i++) {
+ uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
+
+ if (znak < 0x80) newlen += 1;
+ else if (znak < 0x800) newlen += 2;
+ else newlen += 3;
+ }
+
+ if (!(newbuf = malloc(newlen+1))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_cp_to_utf8() not enough memory\n");
+ return NULL;
+ }
+
+ for (i = 0, j = 0; buf[i]; i++) {
+ uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
+ int count;
+
+ if (znak < 0x80) count = 1;
+ else if (znak < 0x800) count = 2;
+ else count = 3;
+
+ switch (count) {
+ case 3: newbuf[j+2] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0x800;
+ case 2: newbuf[j+1] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0xc0;
+ case 1: newbuf[j] = znak;
+ }
+ j += count;
+ }
+ newbuf[j] = '\0';
+
+ return newbuf;
+}
+
+/**
+ * \internal Dekoduje jeden znak UTF-8.
+ *
+ * \note Funkcja nie jest kompletną implementacją UTF-8, a wersją uproszczoną
+ * do potrzeb kodowania CP1250.
+ *
+ * \param s Tekst źródłowy.
+ * \param n Długość tekstu źródłowego.
+ * \param ch Wskaźnik na wynik dekodowania.
+ *
+ * \return Długość zdekodowanej sekwencji w bajtach lub wartość mniejsza
+ * od zera w przypadku błędu.
+ */
+static int gg_utf8_helper(unsigned char *s, int n, uint16_t *ch)
+{
+ unsigned char c = s[0];
+
+ if (c < 0x80) {
+ *ch = c;
+ return 1;
+ }
+
+ if (c < 0xc2)
+ return -1;
+
+ if (c < 0xe0) {
+ if (n < 2)
+ return -2;
+ if (!((s[1] ^ 0x80) < 0x40))
+ return -1;
+ *ch = ((uint16_t) (c & 0x1f) << 6) | (uint16_t) (s[1] ^ 0x80);
+ return 2;
+ }
+
+ if (c < 0xf0) {
+ if (n < 3)
+ return -2;
+ if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (c >= 0xe1 || s[1] >= 0xa0)))
+ return -1;
+ *ch = ((uint16_t) (c & 0x0f) << 12) | ((uint16_t) (s[1] ^ 0x80) << 6) | (uint16_t) (s[2] ^ 0x80);
+ return 3;
+ }
+
+ return -1;
+}
+
+/**
+ * \internal Zamienia tekst kodowany UTF-8 na CP1250.
+ *
+ * \param b Tekst źródłowy w UTF-8.
+ *
+ * \return Zaalokowany bufor z tekstem w CP1250.
+ */
+char *gg_utf8_to_cp(const char *b)
+{
+ unsigned char *buf = (unsigned char *) b;
+ char *newbuf;
+ int newlen = 0;
+ int len;
+ int i, j;
+
+ len = strlen(b);
+
+ for (i = 0; i < len; newlen++) {
+ uint16_t discard;
+ int ret;
+
+ ret = gg_utf8_helper(&buf[i], len - i, &discard);
+
+ if (ret > 0)
+ i += ret;
+ else
+ i++;
+ }
+
+ if (!(newbuf = malloc(newlen+1))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_utf8_to_cp() not enough memory\n");
+ return NULL;
+ }
+
+ for (i = 0, j = 0; buf[i]; j++) {
+ uint16_t znak;
+ int ret, k;
+
+ ret = gg_utf8_helper(&buf[i], len - i, &znak);
+
+ if (ret > 0) {
+ i += ret;
+ } else {
+ znak = '?';
+ i++;
+ }
+
+ if (znak < 0x80) {
+ newbuf[j] = znak;
+ continue;
+ }
+
+ newbuf[j] = '?';
+
+ for (k = 0; k < (sizeof(table_cp1250)/sizeof(table_cp1250[0])); k++) {
+ if (table_cp1250[k] == znak) {
+ newbuf[j] = (0x80 | k);
+ break;
+ }
+ }
+ }
+ newbuf[j] = '\0';
+
+ return newbuf;
+}
/*
* Local variables:
diff --git a/libpurple/protocols/gg/lib/compat.h b/libpurple/protocols/gg/lib/compat.h
index 58b7a72641..46fe305e71 100644
--- a/libpurple/protocols/gg/lib/compat.h
+++ b/libpurple/protocols/gg/lib/compat.h
@@ -1,8 +1,8 @@
-/* $Id: compat.h 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: compat.h 506 2008-01-14 22:15:05Z wojtekka $ */
/*
* (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- * Robert J. Wony <speedy@ziew.org>
+ * Robert J. Woźny <speedy@ziew.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License Version
@@ -15,10 +15,16 @@
*
* 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.
*/
+/**
+ * \file compat.h
+ *
+ * \brief Makra zapewniające kompatybilność API na różnych systemach
+ */
+
#ifndef __COMPAT_H
#define __COMPAT_H
diff --git a/libpurple/protocols/gg/lib/dcc.c b/libpurple/protocols/gg/lib/dcc.c
index 418bdbcaad..159cda139c 100644
--- a/libpurple/protocols/gg/lib/dcc.c
+++ b/libpurple/protocols/gg/lib/dcc.c
@@ -1,8 +1,9 @@
-/* $Id: dcc.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: dcc.c 711 2009-04-16 00:52:47Z darkjames $ */
/*
- * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- * Tomasz Chiliski <chilek@chilan.com>
+ * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ * 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
@@ -15,15 +16,18 @@
*
* 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 dcc.c
+ *
+ * \brief Obsługa połączeń bezpośrednich do wersji Gadu-Gadu 6.x
+ */
#include <sys/types.h>
#include <sys/stat.h>
-#ifndef _WIN32
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -31,7 +35,6 @@
#ifdef sun
# include <sys/filio.h>
#endif
-#endif
#include <ctype.h>
#include <errno.h>
@@ -43,67 +46,72 @@
#include <unistd.h>
#include "compat.h"
+#include "libgadu.h"
+
#ifndef GG_DEBUG_DISABLE
-/*
- * gg_dcc_debug_data() // funkcja wewntrzna
+
+/**
+ * \internal Przekazuje zawartość pakietu do odpluskwiania.
*
- * wywietla zrzut pakietu w hexie.
- *
- * - prefix - prefiks zrzutu pakietu
- * - fd - deskryptor gniazda
- * - buf - bufor z danymi
- * - size - rozmiar danych
+ * \param prefix Prefiks informacji
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danumi
+ * \param size Rozmiar bufora z danymi
*/
static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size)
{
unsigned int i;
-
+
gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size);
-
+
for (i = 0; i < size; i++)
gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]);
-
+
gg_debug(GG_DEBUG_MISC, "\n");
}
#else
#define gg_dcc_debug_data(a,b,c,d) do { } while (0)
#endif
-/*
+/**
+ * Wysyła żądanie zwrotnego połączenia bezpośredniego.
+ *
+ * Funkcję wykorzystuje się, jeśli nie ma możliwości połączenia się z odbiorcą
+ * pliku lub rozmowy głosowej. Po otrzymaniu żądania druga strona spróbuje
+ * nawiązać zwrotne połączenie bezpośrednie z nadawcą.
* gg_dcc_request()
*
- * wysya informacj o tym, e dany klient powinien si z nami poczy.
- * wykorzystywane, kiedy druga strona, ktrej chcemy co wysa jest za
- * maskarad.
+ * \param sess Struktura sesji
+ * \param uin Numer odbiorcy
*
- * - sess - struktura opisujca sesj GG
- * - uin - numerek odbiorcy
+ * \return Patrz \c gg_send_message_ctcp()
*
- * patrz gg_send_message_ctcp().
+ * \ingroup dcc6
*/
int gg_dcc_request(struct gg_session *sess, uin_t uin)
{
- return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char *)"\002", 1);
+ return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (unsigned char*) "\002", 1);
}
-/*
- * gg_dcc_fill_filetime() // funkcja wewntrzna
+/**
+ * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32.
*
- * zamienia czas w postaci unixowej na windowsowy.
+ * \note Funkcja działa jedynie gdy kompilator obsługuje typ danych
+ * \c long \c long.
*
- * - unix - czas w postaci unixowej
- * - filetime - czas w postaci windowsowej
+ * \param ut Czas w postaci uniksowej
+ * \param ft Czas w postaci API WIN32
*/
static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
{
-#ifdef __GG_LIBGADU_HAVE_LONG_LONG
+#ifdef GG_CONFIG_HAVE_LONG_LONG
unsigned long long tmp;
tmp = ut;
tmp += 11644473600LL;
tmp *= 10000000LL;
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
ft[0] = (uint32_t) tmp;
ft[1] = (uint32_t) (tmp >> 32);
#else
@@ -114,31 +122,33 @@ static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
#endif
}
-/*
- * gg_dcc_fill_file_info()
+/**
+ * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
*
- * wypenia pola struct gg_dcc niezbdne do wysania pliku.
+ * \note Większą funkcjonalność zapewnia funkcja \c gg_dcc_fill_file_info2().
*
- * - d - struktura opisujca poczenie DCC
- * - filename - nazwa pliku
+ * \param d Struktura połączenia
+ * \param filename Nazwa pliku
*
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
*/
int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename)
{
return gg_dcc_fill_file_info2(d, filename, filename);
}
-/*
- * gg_dcc_fill_file_info2()
+/**
+ * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
*
- * wypenia pola struct gg_dcc niezbdne do wysania pliku.
+ * \param d Struktura połączenia
+ * \param filename Nazwa pliku zapisywana w strukturze
+ * \param local_filename Nazwa pliku w lokalnym systemie plików
*
- * - d - struktura opisujca poczenie DCC
- * - filename - nazwa pliku
- * - local_filename - nazwa na lokalnym systemie plikw
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0, -1.
+ * \ingroup dcc6
*/
int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename)
{
@@ -225,25 +235,23 @@ int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *l
*q = 175;
}
}
-
+
gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename);
- strncpy((char *)d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
+ strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
return 0;
}
-/*
- * gg_dcc_transfer() // funkcja wewntrzna
- *
- * inicjuje proces wymiany pliku z danym klientem.
+/**
+ * \internal Rozpoczyna połączenie bezpośrednie z danym klientem.
*
- * - ip - adres ip odbiorcy
- * - port - port odbiorcy
- * - my_uin - wasny numer
- * - peer_uin - numer obiorcy
- * - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET)
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
+ * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET)
*
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
*/
static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
{
@@ -251,9 +259,9 @@ static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin,
struct in_addr addr;
addr.s_addr = ip;
-
+
gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET");
-
+
if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n");
errno = EINVAL;
@@ -284,18 +292,17 @@ static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin,
return d;
}
-/*
- * gg_dcc_get_file()
- *
- * inicjuje proces odbierania pliku od danego klienta, gdy ten wysa do
- * nas danie poczenia.
+/**
+ * Rozpoczyna odbieranie pliku przez zwrotne połączenie bezpośrednie.
*
- * - ip - adres ip odbiorcy
- * - port - port odbiorcy
- * - my_uin - wasny numer
- * - peer_uin - numer obiorcy
+ * \param ip Adres IP nadawcy
+ * \param port Port nadawcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer nadawcy
*
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
*/
struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
{
@@ -304,17 +311,17 @@ struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t p
return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET);
}
-/*
- * gg_dcc_send_file()
- *
- * inicjuje proces wysyania pliku do danego klienta.
+/**
+ * Rozpoczyna wysyłanie pliku.
*
- * - ip - adres ip odbiorcy
- * - port - port odbiorcy
- * - my_uin - wasny numer
- * - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
*
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
*/
struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
{
@@ -323,17 +330,17 @@ struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t
return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND);
}
-/*
- * gg_dcc_voice_chat()
- *
- * prbuje nawiza poczenie gosowe.
+/**
+ * Rozpoczyna połączenie głosowe.
*
- * - ip - adres ip odbiorcy
- * - port - port odbiorcy
- * - my_uin - wasny numer
- * - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
*
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
*/
struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
{
@@ -342,30 +349,34 @@ struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t
return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE);
}
-/*
- * gg_dcc_set_type()
+/**
+ * Ustawia typ przychodzącego połączenia bezpośredniego.
*
- * po zdarzeniu GG_EVENT_DCC_CALLBACK naley ustawi typ poczenia za
- * pomoc tej funkcji.
+ * Funkcję należy wywołać po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK.
*
- * - d - struktura opisujca poczenie
- * - type - typ poczenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE)
+ * \param d Struktura połączenia
+ * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub
+ * \c GG_SESSION_DCC_VOICE)
+ *
+ * \ingroup dcc6
*/
void gg_dcc_set_type(struct gg_dcc *d, int type)
{
d->type = type;
d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST;
}
-
-/*
- * gg_dcc_callback() // funkcja wewntrzna
+
+/**
+ * \internal Funkcja zwrotna połączenia bezpośredniego.
+ *
+ * Pole \c callback struktury \c gg_dcc zawiera wskaźnik do tej funkcji.
+ * Wywołuje ona \c gg_watch_fd() i zachowuje wynik w polu \c event.
*
- * wywoywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza
- * rezultat w struct gg_dcc->event.
+ * \note Funkcjonalność funkcjo zwrotnej nie jest już wspierana.
*
- * - d - structura opisujca poczenie
+ * \param d Struktura połączenia
*
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*/
static int gg_dcc_callback(struct gg_dcc *d)
{
@@ -376,25 +387,26 @@ static int gg_dcc_callback(struct gg_dcc *d)
return (e != NULL) ? 0 : -1;
}
-/*
- * gg_dcc_socket_create()
+/**
+ * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich.
+ *
+ * Funkcja przywiązuje gniazdo do pierwszego wolnego portu TCP.
*
- * tworzy gniazdo dla bezporedniej komunikacji midzy klientami.
+ * \param uin Własny numer
+ * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
*
- * - uin - wasny numer
- * - port - preferowany port, jeli rwny 0 lub -1, prbuje domylnego
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
*
- * zaalokowana struct gg_dcc, ktr poniej naley zwolni funkcj
- * gg_dcc_free(), albo NULL jeli wystpi bd.
+ * \ingroup dcc6
*/
struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
{
struct gg_dcc *c;
struct sockaddr_in sin;
int sock, bound = 0, errno2;
-
+
gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port);
-
+
if (!uin) {
gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n");
errno = EINVAL;
@@ -408,12 +420,12 @@ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
if (!port)
port = GG_DEFAULT_DCC_PORT;
-
+
while (!bound) {
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
-
+
gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port);
if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin)))
bound = 1;
@@ -433,7 +445,7 @@ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
errno = errno2;
return NULL;
}
-
+
gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port);
if (!(c = malloc(sizeof(*c)))) {
@@ -452,20 +464,20 @@ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
c->check = GG_CHECK_READ;
c->callback = gg_dcc_callback;
c->destroy = gg_dcc_free;
-
+
return c;
}
-/*
- * gg_dcc_voice_send()
+/**
+ * Wysyła ramkę danych połączenia głosowego.
*
- * wysya ramk danych dla rozmowy gosowej.
+ * \param d Struktura połączenia
+ * \param buf Bufor z danymi
+ * \param length Długość bufora z danymi
*
- * - d - struktura opisujca poczenie dcc
- * - buf - bufor z danymi
- * - length - rozmiar ramki
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0, -1.
+ * \ingroup dcc6
*/
int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
{
@@ -500,7 +512,14 @@ int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
return 0;
}
-#define gg_read(fd, buf, size) \
+/**
+ * \internal Odbiera dane z połączenia bezpośredniego z obsługą błędów.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor na dane
+ * \param size Rozmiar bufora na dane
+ */
+#define gg_dcc_read(fd, buf, size) \
{ \
int tmp = read(fd, buf, size); \
\
@@ -517,9 +536,16 @@ int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
return e; \
} \
gg_dcc_debug_data("read", fd, buf, size); \
-}
+}
-#define gg_write(fd, buf, size) \
+/**
+ * \internal Wysyła dane do połączenia bezpośredniego z obsługą błędów.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danymi
+ * \param size Rozmiar bufora z danymi
+ */
+#define gg_dcc_write(fd, buf, size) \
{ \
int tmp; \
gg_dcc_debug_data("write", fd, buf, size); \
@@ -536,14 +562,18 @@ int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
} \
}
-/*
- * gg_dcc_watch_fd()
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free.
*
- * funkcja, ktr naley wywoa, gdy co si zmieni na gg_dcc->fd.
+ * \param h Struktura połączenia
*
- * - h - struktura zwrcona przez gg_create_dcc_socket()
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
*
- * zaalokowana struct gg_event lub NULL, jeli zabrako pamici na ni.
+ * \ingroup dcc6
*/
struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
{
@@ -551,7 +581,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
int foo;
gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h);
-
+
if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n");
errno = EINVAL;
@@ -568,10 +598,9 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
if (h->type == GG_SESSION_DCC_SOCKET) {
struct sockaddr_in sin;
struct gg_dcc *c;
- int fd;
- socklen_t sin_len = sizeof(sin);
- int one = 1;
-
+ int fd, one = 1;
+ unsigned int sin_len = sizeof(sin);
+
if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno));
return e;
@@ -607,7 +636,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
c->file_fd = -1;
c->remote_addr = sin.sin_addr.s_addr;
c->remote_port = ntohs(sin.sin_port);
-
+
e->type = GG_EVENT_DCC_NEW;
e->event.dcc_new = c;
@@ -617,8 +646,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
struct gg_dcc_small_packet small;
struct gg_dcc_big_packet big;
int size, tmp, res;
- socklen_t res_size = sizeof(res);
- unsigned int utmp;
+ unsigned int utmp, res_size = sizeof(res);
char buf[1024], ack[] = "UDAG";
struct gg_dcc_file_info_packet {
@@ -634,8 +662,8 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
uin_t uin;
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
-
- gg_read(h->fd, &uin, sizeof(uin));
+
+ gg_dcc_read(h->fd, &uin, sizeof(uin));
if (h->state == GG_STATE_READING_UIN_1) {
h->state = GG_STATE_READING_UIN_2;
@@ -656,7 +684,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
case GG_STATE_SENDING_ACK:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
- gg_write(h->fd, ack, 4);
+ gg_dcc_write(h->fd, ack, 4);
h->state = GG_STATE_READING_TYPE;
h->check = GG_CHECK_READ;
@@ -666,8 +694,8 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
case GG_STATE_READING_TYPE:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n");
-
- gg_read(h->fd, &small, sizeof(small));
+
+ gg_dcc_read(h->fd, &small, sizeof(small));
small.type = gg_fix32(small.type);
@@ -680,7 +708,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->timeout = GG_DEFAULT_TIMEOUT;
e->type = GG_EVENT_DCC_CALLBACK;
-
+
break;
case 0x0002: /* XXX */
@@ -703,8 +731,8 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
case GG_STATE_READING_REQUEST:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n");
-
- gg_read(h->fd, &small, sizeof(small));
+
+ gg_dcc_read(h->fd, &small, sizeof(small));
small.type = gg_fix32(small.type);
@@ -715,7 +743,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->check = GG_CHECK_READ;
h->timeout = GG_DEFAULT_TIMEOUT;
break;
-
+
case 0x0003: /* XXX */
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n");
h->state = GG_STATE_SENDING_VOICE_ACK;
@@ -725,22 +753,22 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
e->type = GG_EVENT_DCC_NEED_VOICE_ACK;
break;
-
+
default:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin);
e->type = GG_EVENT_DCC_ERROR;
e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
}
-
+
return e;
case GG_STATE_READING_FILE_INFO:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n");
-
- gg_read(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+ gg_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet));
memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info));
-
+
h->file_info.mode = gg_fix32(h->file_info.mode);
h->file_info.size = gg_fix32(h->file_info.size);
@@ -749,17 +777,17 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
e->type = GG_EVENT_DCC_NEED_FILE_ACK;
-
+
return e;
case GG_STATE_SENDING_FILE_ACK:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n");
-
+
big.type = gg_fix32(0x0006); /* XXX */
big.dunno1 = gg_fix32(h->offset);
big.dunno2 = 0;
- gg_write(h->fd, &big, sizeof(big));
+ gg_dcc_write(h->fd, &big, sizeof(big));
h->state = GG_STATE_READING_FILE_HEADER;
h->chunk_size = sizeof(big);
@@ -773,25 +801,25 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->timeout = GG_DEFAULT_TIMEOUT;
return e;
-
+
case GG_STATE_SENDING_VOICE_ACK:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n");
-
+
tiny.type = 0x01; /* XXX */
- gg_write(h->fd, &tiny, sizeof(tiny));
+ gg_dcc_write(h->fd, &tiny, sizeof(tiny));
h->state = GG_STATE_READING_VOICE_HEADER;
h->check = GG_CHECK_READ;
h->timeout = GG_DEFAULT_TIMEOUT;
h->offset = 0;
-
+
return e;
-
+
case GG_STATE_READING_FILE_HEADER:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n");
-
+
tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
if (tmp == -1) {
@@ -802,7 +830,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
}
gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
-
+
h->chunk_offset += tmp;
if (h->chunk_offset < h->chunk_size)
@@ -823,7 +851,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
return e;
}
- if (h->chunk_size == 0) {
+ if (h->chunk_size == 0) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n");
e->type = GG_EVENT_DCC_DONE;
return e;
@@ -833,13 +861,13 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->check = GG_CHECK_READ;
h->timeout = GG_DEFAULT_TIMEOUT;
h->established = 1;
-
+
return e;
case GG_STATE_READING_VOICE_HEADER:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n");
-
- gg_read(h->fd, &tiny, sizeof(tiny));
+
+ gg_dcc_read(h->fd, &tiny, sizeof(tiny));
switch (tiny.type) {
case 0x03: /* XXX */
@@ -850,19 +878,19 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
break;
case 0x04: /* XXX */
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n");
- /* XXX zwraca odpowiedni event */
+ /* XXX zwracać odpowiedni event */
default:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type);
e->type = GG_EVENT_DCC_ERROR;
e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
}
-
+
return e;
case GG_STATE_READING_VOICE_SIZE:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n");
-
- gg_read(h->fd, &small, sizeof(small));
+
+ gg_dcc_read(h->fd, &small, sizeof(small));
small.type = gg_fix32(small.type);
@@ -870,7 +898,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type);
e->type = GG_EVENT_DCC_ERROR;
e->event.dcc_error = GG_ERROR_DCC_NET;
-
+
return e;
}
@@ -879,18 +907,19 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
if (!(h->voice_buf = malloc(h->chunk_size))) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
+ free(e);
return NULL;
}
h->state = GG_STATE_READING_VOICE_DATA;
h->check = GG_CHECK_READ;
h->timeout = GG_DEFAULT_TIMEOUT;
-
+
return e;
case GG_STATE_READING_VOICE_DATA:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n");
-
+
tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
if (tmp < 1) {
if (tmp == -1) {
@@ -909,7 +938,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
if (h->chunk_offset >= h->chunk_size) {
e->type = GG_EVENT_DCC_VOICE_DATA;
- e->event.dcc_voice_data.data = h->voice_buf;
+ e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf;
e->event.dcc_voice_data.length = h->chunk_size;
h->state = GG_STATE_READING_VOICE_HEADER;
h->voice_buf = NULL;
@@ -917,7 +946,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->check = GG_CHECK_READ;
h->timeout = GG_DEFAULT_TIMEOUT;
-
+
return e;
case GG_STATE_CONNECTING:
@@ -925,7 +954,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
uin_t uins[2];
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n");
-
+
res = 0;
if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res));
@@ -935,23 +964,23 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
}
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n");
-
+
uins[0] = gg_fix32(h->uin);
uins[1] = gg_fix32(h->peer_uin);
- gg_write(h->fd, uins, sizeof(uins));
-
+ gg_dcc_write(h->fd, uins, sizeof(uins));
+
h->state = GG_STATE_READING_ACK;
h->check = GG_CHECK_READ;
h->timeout = GG_DEFAULT_TIMEOUT;
-
+
return e;
}
case GG_STATE_READING_ACK:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n");
-
- gg_read(h->fd, buf, 4);
+
+ gg_dcc_read(h->fd, buf, 4);
if (strncmp(buf, ack, 4)) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
@@ -964,29 +993,29 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->check = GG_CHECK_WRITE;
h->timeout = GG_DEFAULT_TIMEOUT;
h->state = GG_STATE_SENDING_REQUEST;
-
+
return e;
case GG_STATE_SENDING_VOICE_REQUEST:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n");
small.type = gg_fix32(0x0003);
-
- gg_write(h->fd, &small, sizeof(small));
+
+ gg_dcc_write(h->fd, &small, sizeof(small));
h->state = GG_STATE_READING_VOICE_ACK;
h->check = GG_CHECK_READ;
h->timeout = GG_DEFAULT_TIMEOUT;
-
+
return e;
-
+
case GG_STATE_SENDING_REQUEST:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n");
small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */
-
- gg_write(h->fd, &small, sizeof(small));
-
+
+ gg_dcc_write(h->fd, &small, sizeof(small));
+
switch (h->type) {
case GG_SESSION_DCC_GET:
h->state = GG_STATE_READING_REQUEST;
@@ -1002,7 +1031,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
if (h->file_fd == -1)
e->type = GG_EVENT_DCC_NEED_FILE_INFO;
break;
-
+
case GG_SESSION_DCC_VOICE:
h->state = GG_STATE_SENDING_VOICE_REQUEST;
h->check = GG_CHECK_WRITE;
@@ -1011,7 +1040,7 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
}
return e;
-
+
case GG_STATE_SENDING_FILE_INFO:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
@@ -1021,8 +1050,8 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
}
small.type = gg_fix32(0x0001); /* XXX */
-
- gg_write(h->fd, &small, sizeof(small));
+
+ gg_dcc_write(h->fd, &small, sizeof(small));
file_info_packet.big.type = gg_fix32(0x0003); /* XXX */
file_info_packet.big.dunno1 = 0;
@@ -1030,26 +1059,26 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info));
- /* zostaj teraz u nas, wic odwracamy z powrotem */
+ /* zostają teraz u nas, więc odwracamy z powrotem */
h->file_info.size = gg_fix32(h->file_info.size);
h->file_info.mode = gg_fix32(h->file_info.mode);
-
- gg_write(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+ gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet));
h->state = GG_STATE_READING_FILE_ACK;
h->check = GG_CHECK_READ;
h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
return e;
-
+
case GG_STATE_READING_FILE_ACK:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n");
-
- gg_read(h->fd, &big, sizeof(big));
- /* XXX sprawdza wynik */
+ gg_dcc_read(h->fd, &big, sizeof(big));
+
+ /* XXX sprawdzać wynik */
h->offset = gg_fix32(big.dunno1);
-
+
h->state = GG_STATE_SENDING_FILE_HEADER;
h->check = GG_CHECK_WRITE;
h->timeout = GG_DEFAULT_TIMEOUT;
@@ -1060,8 +1089,8 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
case GG_STATE_READING_VOICE_ACK:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n");
-
- gg_read(h->fd, &tiny, sizeof(tiny));
+
+ gg_dcc_read(h->fd, &tiny, sizeof(tiny));
if (tiny.type != 0x01) {
gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type);
@@ -1075,14 +1104,14 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->timeout = GG_DEFAULT_TIMEOUT;
e->type = GG_EVENT_DCC_ACK;
-
+
return e;
case GG_STATE_SENDING_FILE_HEADER:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n");
-
+
h->chunk_offset = 0;
-
+
if ((h->chunk_size = h->file_info.size - h->offset) > 4096) {
h->chunk_size = 4096;
big.type = gg_fix32(0x0003); /* XXX */
@@ -1091,8 +1120,8 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
big.dunno1 = gg_fix32(h->chunk_size);
big.dunno2 = 0;
-
- gg_write(h->fd, &big, sizeof(big));
+
+ gg_dcc_write(h->fd, &big, sizeof(big));
h->state = GG_STATE_SENDING_FILE;
h->check = GG_CHECK_WRITE;
@@ -1100,13 +1129,13 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->established = 1;
return e;
-
+
case GG_STATE_SENDING_FILE:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n");
-
+
if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
utmp = sizeof(buf);
-
+
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size);
/* koniec pliku? */
@@ -1117,11 +1146,17 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
return e;
}
+ if (h->offset >= h->file_info.size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
lseek(h->file_fd, h->offset, SEEK_SET);
size = read(h->file_fd, buf, utmp);
- /* bd */
+ /* błąd */
if (size == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
@@ -1139,8 +1174,8 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
return e;
}
-
- /* jeli wczytalimy wicej, utnijmy. */
+
+ /* jeśli wczytaliśmy więcej, utnijmy. */
if (h->offset + size > h->file_info.size) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size);
size = h->file_info.size - h->offset;
@@ -1161,15 +1196,22 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
return e;
}
- h->offset += size;
-
+ if (tmp == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (connection reset)\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ h->offset += tmp;
+
if (h->offset >= h->file_info.size) {
e->type = GG_EVENT_DCC_DONE;
return e;
}
-
- h->chunk_offset += size;
-
+
+ h->chunk_offset += tmp;
+
if (h->chunk_offset >= h->chunk_size) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
h->state = GG_STATE_SENDING_FILE_HEADER;
@@ -1178,22 +1220,28 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->state = GG_STATE_SENDING_FILE;
h->timeout = GG_DCC_TIMEOUT_SEND;
}
-
+
h->check = GG_CHECK_WRITE;
return e;
case GG_STATE_GETTING_FILE:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n");
-
+
if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
utmp = sizeof(buf);
-
+
+ if (h->offset >= h->file_info.size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
size = read(h->fd, buf, utmp);
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size);
-
- /* bd */
+
+ /* błąd */
if (size == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
@@ -1211,9 +1259,9 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
return e;
}
-
+
tmp = write(h->file_fd, buf, size);
-
+
if (tmp == -1 || tmp < size) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno));
e->type = GG_EVENT_DCC_ERROR;
@@ -1222,14 +1270,14 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
}
h->offset += size;
-
+
if (h->offset >= h->file_info.size) {
e->type = GG_EVENT_DCC_DONE;
return e;
}
h->chunk_offset += size;
-
+
if (h->chunk_offset >= h->chunk_size) {
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
h->state = GG_STATE_READING_FILE_HEADER;
@@ -1245,11 +1293,11 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
h->state = GG_STATE_GETTING_FILE;
h->timeout = GG_DCC_TIMEOUT_GET;
}
-
+
h->check = GG_CHECK_READ;
return e;
-
+
default:
gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n");
e->type = GG_EVENT_DCC_ERROR;
@@ -1258,35 +1306,28 @@ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
return e;
}
}
-
+
return e;
}
-#undef gg_read
-#undef gg_write
-
-/*
- * gg_dcc_free()
+/**
+ * Zwalnia zasoby używane przez połączenie bezpośrednie.
*
- * zwalnia pami po strukturze poczenia dcc.
+ * \param d Struktura połączenia
*
- * - d - zwalniana struktura
+ * \ingroup dcc6
*/
void gg_dcc_free(struct gg_dcc *d)
{
gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d);
-
+
if (!d)
return;
if (d->fd != -1)
close(d->fd);
- if (d->chunk_buf) {
- free(d->chunk_buf);
- d->chunk_buf = NULL;
- }
-
+ free(d->chunk_buf);
free(d);
}
diff --git a/libpurple/protocols/gg/lib/dcc7.c b/libpurple/protocols/gg/lib/dcc7.c
new file mode 100644
index 0000000000..16fcca0665
--- /dev/null
+++ b/libpurple/protocols/gg/lib/dcc7.c
@@ -0,0 +1,1212 @@
+/* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */
+
+/*
+ * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Tomasz Chiliński <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110,
+ * USA.
+ */
+
+/**
+ * \file dcc7.c
+ *
+ * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+#include <time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "libgadu.h"
+
+#define gg_debug_dcc(dcc, fmt...) \
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, fmt)
+
+/**
+ * \internal Dodaje połączenie bezpośrednie do sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
+
+ if (!sess || !dcc || dcc->next) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ dcc->next = sess->dcc7_list;
+ sess->dcc7_list = dcc;
+
+ return 0;
+}
+
+/**
+ * \internal Usuwa połączenie bezpośrednie z sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+ struct gg_dcc7 *tmp;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
+
+ if (!sess || !dcc) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sess->dcc7_list == dcc) {
+ sess->dcc7_list = dcc->next;
+ dcc->next = NULL;
+ return 0;
+ }
+
+ for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+ if (tmp->next == dcc) {
+ tmp = dcc->next;
+ dcc->next = NULL;
+ return 0;
+ }
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * \internal Zwraca strukturę połączenia o danym identyfikatorze.
+ *
+ * \param sess Struktura sesji
+ * \param id Identyfikator połączenia
+ * \param uin Numer nadawcy lub odbiorcy
+ *
+ * \return Struktura połączenia lub \c NULL jeśli nie znaleziono
+ */
+static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
+{
+ struct gg_dcc7 *tmp;
+ int empty;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
+
+ empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
+
+ for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+ if (empty) {
+ if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
+ return tmp;
+ } else {
+ if (!memcmp(&tmp->cid, &id, sizeof(id)))
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \internal Nawiązuje połączenie bezpośrednie
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p, %p)\n", sess, dcc);
+
+ if (!sess || !dcc) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
+ return -1;
+ }
+
+ dcc->state = GG_STATE_CONNECTING;
+ dcc->check = GG_CHECK_WRITE;
+ dcc->timeout = GG_DCC7_TIMEOUT_CONNECT;
+ dcc->soft_timeout = 1;
+
+ return 0;
+}
+
+/**
+ * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego
+ *
+ * \param dcc Struktura połączenia
+ * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port)
+{
+ struct sockaddr_in sin;
+ int fd;
+
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
+
+ if (!dcc) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ // XXX losować porty?
+
+ if (!port)
+ port = GG_DEFAULT_DCC_PORT;
+
+ while (1) {
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons(port);
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port);
+
+ if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin)))
+ break;
+
+ if (port++ == 65535) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n");
+ close(fd);
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ if (listen(fd, 1)) {
+ int errsv = errno;
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
+ close(fd);
+ errno = errsv;
+ return -1;
+ }
+
+ dcc->fd = fd;
+ dcc->local_port = port;
+
+ dcc->state = GG_STATE_LISTENING;
+ dcc->check = GG_CHECK_READ;
+ dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+ return 0;
+}
+
+/**
+ * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
+{
+ struct gg_dcc7_info pkt;
+
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
+
+ // XXX dać możliwość konfiguracji?
+
+ dcc->local_addr = dcc->sess->client_addr;
+
+ if (gg_dcc7_listen(dcc, 0) == -1)
+ return -1;
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.uin = gg_fix32(dcc->peer_uin);
+ pkt.type = GG_DCC7_TYPE_P2P;
+ pkt.id = dcc->cid;
+ snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), dcc->local_port);
+
+ return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Odwraca połączenie po nieudanym connect()
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
+{
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
+
+ if (dcc->reverse) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
+ return -1;
+ }
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
+ close(dcc->fd);
+ dcc->fd = -1;
+ dcc->reverse = 1;
+
+ return gg_dcc7_listen_and_send_info(dcc);
+}
+
+/**
+ * \internal Wysyła do serwera żądanie nadania identyfikatora sesji
+ *
+ * \param sess Struktura sesji
+ * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
+{
+ struct gg_dcc7_id_request pkt;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type);
+
+ if (!sess) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n");
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type);
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.type = gg_fix32(type);
+
+ return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Rozpoczyna wysyłanie pliku.
+ *
+ * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz
+ * \c gg_dcc_send_file_fd().
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param fd Deskryptor pliku
+ * \param size Rozmiar pliku
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku
+ * \param seek Flaga mówiąca, czy można używać lseek()
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash, int seek)
+{
+ struct gg_dcc7 *dcc = NULL;
+
+ if (!sess || !rcpt || !filename1250 || !hash || fd == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n");
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n");
+ goto fail;
+ }
+
+ if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
+ goto fail;
+
+ memset(dcc, 0, sizeof(struct gg_dcc7));
+ dcc->type = GG_SESSION_DCC7_SEND;
+ dcc->dcc_type = GG_DCC7_TYPE_FILE;
+ dcc->state = GG_STATE_REQUESTING_ID;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+ dcc->sess = sess;
+ dcc->fd = -1;
+ dcc->uin = sess->uin;
+ dcc->peer_uin = rcpt;
+ dcc->file_fd = fd;
+ dcc->size = size;
+ dcc->seek = seek;
+
+ strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1);
+ dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+
+ memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
+
+ if (gg_dcc7_session_add(sess, dcc) == -1)
+ goto fail;
+
+ return dcc;
+
+fail:
+ free(dcc);
+ return NULL;
+}
+
+/**
+ * Rozpoczyna wysyłanie pliku o danej nazwie.
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param filename Nazwa pliku w lokalnym systemie plików
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku (lub \c NULL jeśli ma być wyznaczony)
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash)
+{
+ struct gg_dcc7 *dcc = NULL;
+ const char *tmp;
+ char hash_buf[GG_DCC7_HASH_LEN];
+ struct stat st;
+ int fd = -1;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p)\n", sess, rcpt, filename, hash);
+
+ if (!sess || !rcpt || !filename) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n");
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (!filename1250)
+ filename1250 = filename;
+
+ if (stat(filename, &st) == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno));
+ goto fail;
+ }
+
+ if ((st.st_mode & S_IFDIR)) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if ((fd = open(filename, O_RDONLY)) == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!hash) {
+ if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
+ goto fail;
+
+ hash = hash_buf;
+ }
+
+ if ((tmp = strrchr(filename1250, '/')))
+ filename1250 = tmp + 1;
+
+ if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1)))
+ goto fail;
+
+ return dcc;
+
+fail:
+ if (fd != -1) {
+ int errsv = errno;
+ close(fd);
+ errno = errsv;
+ }
+
+ free(dcc);
+ return NULL;
+}
+
+/**
+ * \internal Rozpoczyna wysyłanie pliku o danym deskryptorze.
+ *
+ * \note Wysyłanie pliku nie będzie działać poprawnie, jeśli deskryptor
+ * źródłowy jest w trybie nieblokującym i w pewnym momencie zabraknie danych.
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param fd Deskryptor pliku
+ * \param size Rozmiar pliku
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash)
+{
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, %d, %d, %u, \"%s\", %p)\n", sess, rcpt, fd, size, filename1250, hash);
+
+ return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0);
+}
+
+
+/**
+ * Potwierdza chęć odebrania pliku.
+ *
+ * \param dcc Struktura połączenia
+ * \param offset Początkowy offset przy wznawianiu przesyłania pliku
+ *
+ * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset
+ * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub
+ * podobną.
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
+{
+ struct gg_dcc7_accept pkt;
+
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
+
+ if (!dcc || !dcc->sess) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.uin = gg_fix32(dcc->peer_uin);
+ pkt.id = dcc->cid;
+ pkt.offset = gg_fix32(offset);
+
+ if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1)
+ return -1;
+
+ dcc->offset = offset;
+
+ return gg_dcc7_listen_and_send_info(dcc);
+}
+
+/**
+ * Odrzuca próbę przesłania pliku.
+ *
+ * \param dcc Struktura połączenia
+ * \param reason Powód odrzucenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
+{
+ struct gg_dcc7_reject pkt;
+
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
+
+ if (!dcc || !dcc->sess) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.uin = gg_fix32(dcc->peer_uin);
+ pkt.id = dcc->cid;
+ pkt.reason = gg_fix32(reason);
+
+ return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+ struct gg_dcc7_id_reply *p = payload;
+ struct gg_dcc7 *tmp;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+ for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
+
+ if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != gg_fix32(p->type))
+ continue;
+
+ tmp->cid = p->id;
+
+ switch (tmp->dcc_type) {
+ case GG_DCC7_TYPE_FILE:
+ {
+ struct gg_dcc7_new s;
+
+ memset(&s, 0, sizeof(s));
+ s.id = tmp->cid;
+ s.type = gg_fix32(GG_DCC7_TYPE_FILE);
+ s.uin_from = gg_fix32(tmp->uin);
+ s.uin_to = gg_fix32(tmp->peer_uin);
+ s.size = gg_fix32(tmp->size);
+
+ strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN);
+
+ tmp->state = GG_STATE_WAITING_FOR_ACCEPT;
+ tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+ return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+ struct gg_dcc7_accept *p = payload;
+ struct gg_dcc7 *dcc;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+ if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n");
+ // XXX wysłać reject?
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ return 0;
+ }
+
+ if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ return 0;
+ }
+
+ // XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT?
+
+ dcc->offset = gg_fix32(p->offset);
+ dcc->state = GG_STATE_WAITING_FOR_INFO;
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+ struct gg_dcc7_info *p = payload;
+ struct gg_dcc7 *dcc;
+ char *tmp;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+ if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
+ return 0;
+ }
+
+ if (p->type != GG_DCC7_TYPE_P2P) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ return 0;
+ }
+
+ if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ return 0;
+ }
+
+ if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ return 0;
+ }
+
+ // jeśli nadal czekamy na połączenie przychodzące, a druga strona nie
+ // daje rady i oferuje namiary na siebie, bierzemy co dają.
+
+ if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ return 0;
+ }
+
+ if (dcc->state == GG_STATE_LISTENING) {
+ close(dcc->fd);
+ dcc->fd = -1;
+ dcc->reverse = 1;
+ }
+
+ if (dcc->type == GG_SESSION_DCC7_SEND) {
+ e->type = GG_EVENT_DCC7_ACCEPT;
+ e->event.dcc7_accept.dcc7 = dcc;
+ e->event.dcc7_accept.type = gg_fix32(p->type);
+ e->event.dcc7_accept.remote_ip = dcc->remote_addr;
+ e->event.dcc7_accept.remote_port = dcc->remote_port;
+ } else {
+ e->type = GG_EVENT_DCC7_PENDING;
+ }
+
+ if (gg_dcc7_connect(sess, dcc) == -1) {
+ if (gg_dcc7_reverse_connect(dcc) == -1) {
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_NET;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+ struct gg_dcc7_reject *p = payload;
+ struct gg_dcc7 *dcc;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+ if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n");
+ return 0;
+ }
+
+ if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ return 0;
+ }
+
+ e->type = GG_EVENT_DCC7_REJECT;
+ e->event.dcc7_reject.dcc7 = dcc;
+ e->event.dcc7_reject.reason = gg_fix32(p->reason);
+
+ // XXX ustawić state na rejected?
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet nowego połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+ struct gg_dcc7_new *p = payload;
+ struct gg_dcc7 *dcc;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+ switch (gg_fix32(p->type)) {
+ case GG_DCC7_TYPE_FILE:
+ if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n");
+ return -1;
+ }
+
+ memset(dcc, 0, sizeof(struct gg_dcc7));
+ dcc->type = GG_SESSION_DCC7_GET;
+ dcc->dcc_type = GG_DCC7_TYPE_FILE;
+ dcc->fd = -1;
+ dcc->file_fd = -1;
+ dcc->uin = sess->uin;
+ dcc->peer_uin = gg_fix32(p->uin_from);
+ dcc->cid = p->id;
+ dcc->sess = sess;
+
+ if (gg_dcc7_session_add(sess, dcc) == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+ gg_dcc7_free(dcc);
+ return -1;
+ }
+
+ dcc->size = gg_fix32(p->size);
+ strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1);
+ dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+ memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN);
+
+ e->type = GG_EVENT_DCC7_NEW;
+ e->event.dcc7_new = dcc;
+
+ break;
+
+ case GG_DCC7_TYPE_VOICE:
+ if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n");
+ return -1;
+ }
+
+ memset(dcc, 0, sizeof(struct gg_dcc7));
+
+ dcc->type = GG_SESSION_DCC7_VOICE;
+ dcc->dcc_type = GG_DCC7_TYPE_VOICE;
+ dcc->fd = -1;
+ dcc->file_fd = -1;
+ dcc->uin = sess->uin;
+ dcc->peer_uin = gg_fix32(p->uin_from);
+ dcc->cid = p->id;
+ dcc->sess = sess;
+
+ if (gg_dcc7_session_add(sess, dcc) == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+ gg_dcc7_free(dcc);
+ return -1;
+ }
+
+ e->type = GG_EVENT_DCC7_NEW;
+ e->event.dcc7_new = dcc;
+
+ break;
+
+ default:
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from));
+
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Ustawia odpowiednie stany wewnętrzne w zależności od rodzaju
+ * połączenia.
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu.
+ */
+static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
+{
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
+
+ if (!dcc) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (dcc->type) {
+ case GG_SESSION_DCC7_GET:
+ dcc->state = GG_STATE_GETTING_FILE;
+ dcc->check = GG_CHECK_READ;
+ return 0;
+
+ case GG_SESSION_DCC7_SEND:
+ dcc->state = GG_STATE_SENDING_FILE;
+ dcc->check = GG_CHECK_WRITE;
+ return 0;
+
+ case GG_SESSION_DCC7_VOICE:
+ dcc->state = GG_STATE_READING_VOICE_DATA;
+ dcc->check = GG_CHECK_READ;
+ return 0;
+ }
+
+ errno = EINVAL;
+
+ return -1;
+}
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup dcc7
+ */
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
+{
+ struct gg_event *e;
+
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
+
+ if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(e = malloc(sizeof(struct gg_event)))) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
+ return NULL;
+ }
+
+ memset(e, 0, sizeof(struct gg_event));
+ e->type = GG_EVENT_NONE;
+
+ switch (dcc->state) {
+ case GG_STATE_LISTENING:
+ {
+ struct sockaddr_in sin;
+ int fd, one = 1;
+ unsigned int sin_len = sizeof(sin);
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
+
+ if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
+ return e;
+ }
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+
+#ifdef FIONBIO
+ if (ioctl(fd, FIONBIO, &one) == -1) {
+#else
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
+ close(fd);
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+ return e;
+ }
+
+ close(dcc->fd);
+ dcc->fd = fd;
+
+ dcc->state = GG_STATE_READING_ID;
+ dcc->check = GG_CHECK_READ;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+ dcc->incoming = 1;
+
+ dcc->remote_port = ntohs(sin.sin_port);
+ dcc->remote_addr = sin.sin_addr.s_addr;
+
+ e->type = GG_EVENT_DCC7_CONNECTED;
+ e->event.dcc7_connected.dcc7 = dcc;
+
+ return e;
+ }
+
+ case GG_STATE_CONNECTING:
+ {
+ int res = 0, error = 0;
+ unsigned int error_size = sizeof(error);
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
+
+ dcc->soft_timeout = 0;
+
+ if (dcc->timeout == 0)
+ error = ETIMEDOUT;
+
+ if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
+
+ if (gg_dcc7_reverse_connect(dcc) != -1) {
+ e->type = GG_EVENT_DCC7_PENDING;
+ } else {
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_NET;
+ }
+
+ return e;
+ }
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
+
+ dcc->state = GG_STATE_SENDING_ID;
+ dcc->check = GG_CHECK_WRITE;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+ dcc->incoming = 0;
+
+ return e;
+ }
+
+ case GG_STATE_READING_ID:
+ {
+ gg_dcc7_id_t id;
+ int res;
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
+
+ if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+ return e;
+ }
+
+ if (memcmp(&id, &dcc->cid, sizeof(id))) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+ return e;
+ }
+
+ if (dcc->incoming) {
+ dcc->state = GG_STATE_SENDING_ID;
+ dcc->check = GG_CHECK_WRITE;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+ } else {
+ gg_dcc7_postauth_fixup(dcc);
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+ }
+
+ return e;
+ }
+
+ case GG_STATE_SENDING_ID:
+ {
+ int res;
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
+
+ if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+ return e;
+ }
+
+ if (dcc->incoming) {
+ gg_dcc7_postauth_fixup(dcc);
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+ } else {
+ dcc->state = GG_STATE_READING_ID;
+ dcc->check = GG_CHECK_READ;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+ }
+
+ return e;
+ }
+
+ case GG_STATE_SENDING_FILE:
+ {
+ char buf[1024];
+ int chunk, res;
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+ if (dcc->offset >= dcc->size) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
+ e->type = GG_EVENT_DCC7_DONE;
+ return e;
+ }
+
+ if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_FILE;
+ return e;
+ }
+
+ if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
+ chunk = sizeof(buf);
+
+ if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
+ return e;
+ }
+
+ if ((res = write(dcc->fd, buf, res)) == -1) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_NET;
+ return e;
+ }
+
+ dcc->offset += res;
+
+ if (dcc->offset >= dcc->size) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ e->type = GG_EVENT_DCC7_DONE;
+ return e;
+ }
+
+ dcc->state = GG_STATE_SENDING_FILE;
+ dcc->check = GG_CHECK_WRITE;
+ dcc->timeout = GG_DCC7_TIMEOUT_SEND;
+
+ return e;
+ }
+
+ case GG_STATE_GETTING_FILE:
+ {
+ char buf[1024];
+ int res, wres;
+
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+ if (dcc->offset >= dcc->size) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ e->type = GG_EVENT_DCC7_DONE;
+ return e;
+ }
+
+ if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
+ return e;
+ }
+
+ // XXX zapisywać do skutku?
+
+ if ((wres = write(dcc->file_fd, buf, res)) < res) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_FILE;
+ return e;
+ }
+
+ dcc->offset += res;
+
+ if (dcc->offset >= dcc->size) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ e->type = GG_EVENT_DCC7_DONE;
+ return e;
+ }
+
+ dcc->state = GG_STATE_GETTING_FILE;
+ dcc->check = GG_CHECK_READ;
+ dcc->timeout = GG_DCC7_TIMEOUT_GET;
+
+ return e;
+ }
+
+ default:
+ {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+
+ return e;
+ }
+ }
+
+ return e;
+}
+
+/**
+ * Zwalnia zasoby używane przez połączenie bezpośrednie.
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \ingroup dcc7
+ */
+void gg_dcc7_free(struct gg_dcc7 *dcc)
+{
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
+
+ if (!dcc)
+ return;
+
+ if (dcc->fd != -1)
+ close(dcc->fd);
+
+ if (dcc->file_fd != -1)
+ close(dcc->file_fd);
+
+ if (dcc->sess)
+ gg_dcc7_session_remove(dcc->sess, dcc);
+
+ free(dcc);
+}
+
diff --git a/libpurple/protocols/gg/lib/events.c b/libpurple/protocols/gg/lib/events.c
index 2a347c11cf..06999dac18 100644
--- a/libpurple/protocols/gg/lib/events.c
+++ b/libpurple/protocols/gg/lib/events.c
@@ -1,9 +1,10 @@
-/* $Id: events.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: events.c 855 2009-10-12 21:42:51Z wojtekka $ */
/*
- * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
- * Robert J. Wony <speedy@ziew.org>
- * Arkadiusz Mikiewicz <arekm@pld-linux.org>
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. Woźny <speedy@ziew.org>
+ * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ * 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
@@ -16,71 +17,74 @@
*
* 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 events.c
+ *
+ * \brief Obsługa zdarzeń
+ */
#include <sys/types.h>
-#ifndef _WIN32
-#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#endif
-#include "libgadu-config.h"
+#include "compat.h"
+#include "libgadu.h"
+#include "protocol.h"
+#include "libgadu-internal.h"
#include <errno.h>
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-# include <pthread.h>
-#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#include <ctype.h>
+#ifdef GG_CONFIG_HAVE_OPENSSL
# include <openssl/err.h>
# include <openssl/x509.h>
#endif
-#include "compat.h"
-
-/*
- * gg_event_free()
+/**
+ * Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
*
- * zwalnia pami zajmowan przez informacj o zdarzeniu.
+ * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci
+ * strukturę \c gg_event.
*
- * - e - wskanik do informacji o zdarzeniu
+ * \param e Struktura zdarzenia
+ *
+ * \ingroup events
*/
void gg_event_free(struct gg_event *e)
{
gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
-
+
if (!e)
return;
-
+
switch (e->type) {
case GG_EVENT_MSG:
free(e->event.msg.message);
free(e->event.msg.formats);
free(e->event.msg.recipients);
break;
-
+
case GG_EVENT_NOTIFY:
free(e->event.notify);
break;
-
+
case GG_EVENT_NOTIFY60:
{
int i;
for (i = 0; e->event.notify60[i].uin; i++)
free(e->event.notify60[i].descr);
-
+
free(e->event.notify60);
break;
@@ -89,7 +93,7 @@ void gg_event_free(struct gg_event *e)
case GG_EVENT_STATUS60:
free(e->event.status60.descr);
break;
-
+
case GG_EVENT_STATUS:
free(e->event.status.descr);
break;
@@ -112,26 +116,30 @@ void gg_event_free(struct gg_event *e)
case GG_EVENT_USERLIST:
free(e->event.userlist.reply);
break;
-
+
case GG_EVENT_IMAGE_REPLY:
free(e->event.image_reply.filename);
free(e->event.image_reply.image);
break;
+
+ case GG_EVENT_XML_EVENT:
+ free(e->event.xml_event.data);
+ break;
}
free(e);
}
-/*
- * gg_image_queue_remove()
- *
- * usuwa z kolejki dany wpis.
+/** \cond internal */
+
+/**
+ * \internal Usuwa obrazek z kolejki do wysłania.
*
- * - s - sesja
- * - q - kolejka
- * - freeq - czy zwolni kolejk
+ * \param s Struktura sesji
+ * \param q Struktura obrazka
+ * \param freeq Flaga zwolnienia elementu kolejki
*
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
*/
int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
{
@@ -162,13 +170,14 @@ int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int fr
return 0;
}
-/*
- * gg_image_queue_parse() // funkcja wewntrzna
- *
- * parsuje przychodzcy pakiet z obrazkiem.
+/**
+ * \internal Analizuje przychodzący pakiet z obrazkiem.
*
- * - e - opis zdarzenia
- * -
+ * \param e Struktura zdarzenia
+ * \param p Bufor z danymi
+ * \param len Długość bufora
+ * \param sess Struktura sesji
+ * \param sender Numer nadawcy
*/
static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender)
{
@@ -180,8 +189,8 @@ static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len,
return;
}
- /* znajd dany obrazek w kolejce danej sesji */
-
+ /* znajdź dany obrazek w kolejce danej sesji */
+
for (qq = sess->images, q = NULL; qq; qq = qq->next) {
if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) {
q = qq;
@@ -190,34 +199,23 @@ static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len,
}
if (!q) {
- gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
return;
}
if (p[0] == 0x05) {
- unsigned int i, ok = 0;
-
q->done = 0;
len -= sizeof(struct gg_msg_image_reply);
p += sizeof(struct gg_msg_image_reply);
- /* sprawd, czy mamy tekst zakoczony \0 */
-
- for (i = 0; i < len; i++) {
- if (!p[i]) {
- ok = 1;
- break;
- }
- }
-
- if (!ok) {
- gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
+ if (memchr(p, 0, len) == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
return;
}
if (!(q->filename = strdup(p))) {
- gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
return;
}
@@ -230,11 +228,11 @@ static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len,
if (q->done + len > q->size)
len = q->size - q->done;
-
+
memcpy(q->image + q->done, p, len);
q->done += len;
- /* jeli skoczono odbiera obrazek, wygeneruj zdarzenie */
+ /* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
if (q->done >= q->size) {
e->type = GG_EVENT_IMAGE_REPLY;
@@ -250,78 +248,55 @@ static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len,
}
}
-/*
- * gg_handle_recv_msg() // funkcja wewntrzna
+/**
+ * \internal Analizuje informacje rozszerzone wiadomości.
+ *
+ * \param sess Struktura sesji.
+ * \param e Struktura zdarzenia.
+ * \param sender Numer nadawcy.
+ * \param p Wskaźnik na dane rozszerzone.
+ * \param packet_end Wskaźnik na koniec pakietu.
*
- * obsuguje pakiet z przychodzc wiadomoci, rozbijajc go na dodatkowe
- * struktury (konferencje, kolorki) w razie potrzeby.
- *
- * - h - nagwek pakietu
- * - e - opis zdarzenia
- *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 jeśli wiadomość obsłużono i wynik ma
+ * zostać przekazany aplikacji, -2 jeśli wystąpił błąd ogólny, -3 jeśli
+ * wiadomość jest niepoprawna.
*/
-static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end)
{
- struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
- char *p, *packet_end = (char*) r + h->length;
-
- gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
-
- if (!r->seq && !r->msgclass) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
- e->type = GG_EVENT_NONE;
- return 0;
- }
-
- for (p = (char*) r + sizeof(*r); *p; p++) {
- if (*p == 0x02 && p == packet_end - 1) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
- break;
- }
- if (p >= packet_end) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
- goto malformed;
- }
- }
-
- p++;
-
- /* przeanalizuj dodatkowe opcje */
while (p < packet_end) {
switch (*p) {
case 0x01: /* konferencja */
{
struct gg_msg_recipients *m = (void*) p;
uint32_t i, count;
-
+
p += sizeof(*m);
-
+
if (p > packet_end) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
goto malformed;
}
count = gg_fix32(m->count);
if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
goto malformed;
}
-
+
if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
goto fail;
}
-
+
for (i = 0; i < count; i++, p += sizeof(uint32_t)) {
uint32_t u;
memcpy(&u, p, sizeof(uint32_t));
e->event.msg.recipients[i] = gg_fix32(u);
}
-
+
e->event.msg.recipients_count = count;
-
+
break;
}
@@ -329,9 +304,9 @@ static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg
{
uint16_t len;
char *buf;
-
+
if (p + 3 > packet_end) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
goto malformed;
}
@@ -339,18 +314,18 @@ static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg
len = gg_fix16(len);
if (!(buf = malloc(len))) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
goto fail;
}
p += 3;
if (p + len > packet_end) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
free(buf);
goto malformed;
}
-
+
memcpy(buf, p, len);
e->event.msg.formats = buf;
@@ -366,17 +341,17 @@ static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg
struct gg_msg_image_request *i = (void*) p;
if (p + sizeof(*i) > packet_end) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
goto malformed;
}
- e->event.image_request.sender = gg_fix32(r->sender);
+ e->event.image_request.sender = sender;
e->event.image_request.size = gg_fix32(i->size);
e->event.image_request.crc32 = gg_fix32(i->crc32);
e->type = GG_EVENT_IMAGE_REQUEST;
- return 0;
+ goto handled;
}
case 0x05: /* image_reply */
@@ -386,75 +361,347 @@ static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg
if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
- /* pusta odpowied - klient po drugiej stronie nie ma danego obrazka */
+ /* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
e->type = GG_EVENT_IMAGE_REPLY;
- e->event.image_reply.sender = gg_fix32(r->sender);
+ e->event.image_reply.sender = sender;
e->event.image_reply.size = 0;
e->event.image_reply.crc32 = gg_fix32(rep->crc32);
e->event.image_reply.filename = NULL;
e->event.image_reply.image = NULL;
- return 0;
+ goto handled;
} else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n");
goto malformed;
}
rep->size = gg_fix32(rep->size);
rep->crc32 = gg_fix32(rep->crc32);
- gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender));
+ gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender);
- return 0;
+ goto handled;
}
default:
{
- gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
p = packet_end;
}
}
}
+ return 0;
+
+handled:
+ return -1;
+
+fail:
+ return -2;
+
+malformed:
+ return -3;
+}
+
+/**
+ * \internal Analizuje przychodzący pakiet z wiadomością.
+ *
+ * Rozbija pakiet na poszczególne składniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wskaźnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+ struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
+ char *p, *packet_end = (char*) r + h->length;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
+
+ if (!r->seq && !r->msgclass) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
+ e->type = GG_EVENT_NONE;
+ return 0;
+ }
+
+ /* znajdź \0 */
+ for (p = (char*) r + sizeof(*r); ; p++) {
+ if (p >= packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
+ goto malformed;
+ }
+
+ if (*p == 0x02 && p == packet_end - 1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
+ break;
+ }
+
+ if (!*p)
+ break;
+ }
+
+ p++;
+
+ switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) {
+ case -1: // handled
+ return 0;
+
+ case -2: // failed
+ goto fail;
+
+ case -3: // malformed
+ goto malformed;
+ }
+
e->type = GG_EVENT_MSG;
e->event.msg.msgclass = gg_fix32(r->msgclass);
e->event.msg.sender = gg_fix32(r->sender);
e->event.msg.time = gg_fix32(r->time);
- e->event.msg.message = (unsigned char *)strdup((char*) r + sizeof(*r));
+ e->event.msg.seq = gg_fix32(r->seq);
+ e->event.msg.message = (unsigned char*) strdup((char*) r + sizeof(*r));
return 0;
malformed:
e->type = GG_EVENT_NONE;
+ free(e->event.msg.message);
+ free(e->event.msg.recipients);
+ free(e->event.msg.formats);
+ return 0;
+
+fail:
+ free(e->event.msg.message);
free(e->event.msg.recipients);
free(e->event.msg.formats);
+ return -1;
+}
+
+/**
+ * \internal Zamienia tekst w formacie HTML na czysty tekst.
+ *
+ * \param dst Bufor wynikowy (może być \c NULL)
+ * \param html Tekst źródłowy
+ *
+ * \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_from_html(char *dst, const char *html)
+{
+ const char *src, *entity, *tag;
+ int len, in_tag, in_entity;
+
+ len = 0;
+ in_tag = 0;
+ tag = NULL;
+ in_entity = 0;
+ entity = NULL;
+
+ for (src = html; *src != 0; src++) {
+ if (*src == '<') {
+ tag = src;
+ in_tag = 1;
+ continue;
+ }
+
+ if (in_tag && (*src == '>')) {
+ if (strncmp(tag, "<br", 3) == 0) {
+ if (dst != NULL)
+ dst[len] = '\n';
+ len++;
+ }
+ in_tag = 0;
+ continue;
+ }
+
+ if (in_tag)
+ continue;
+
+ if (*src == '&') {
+ in_entity = 1;
+ entity = src;
+ continue;
+ }
+
+ if (in_entity && *src == ';') {
+ in_entity = 0;
+ if (dst != NULL) {
+ if (strncmp(entity, "&lt;", 4) == 0)
+ dst[len] = '<';
+ else if (strncmp(entity, "&gt;", 4) == 0)
+ dst[len] = '>';
+ else if (strncmp(entity, "&quot;", 6) == 0)
+ dst[len] = '"';
+ else if (strncmp(entity, "&apos;", 6) == 0)
+ dst[len] = '\'';
+ else if (strncmp(entity, "&amp;", 5) == 0)
+ dst[len] = '&';
+ else
+ dst[len] = '?';
+ }
+ len++;
+ continue;
+ }
+
+ if (in_entity && !(isalnum(*src) || *src == '#'))
+ in_entity = 0;
+
+ if (in_entity)
+ continue;
+
+ if (dst != NULL)
+ dst[len] = *src;
+
+ len++;
+ }
+
+ if (dst != NULL)
+ dst[len] = 0;
+
+ return len;
+}
+
+/**
+ * \internal Analizuje przychodzący pakiet z wiadomością protokołu Gadu-Gadu 8.0.
+ *
+ * Rozbija pakiet na poszczególne składniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wskaźnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+ char *packet = (char*) h + sizeof(struct gg_header);
+ struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet;
+ uint32_t offset_plain;
+ uint32_t offset_attr;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e);
+
+ if (!r->seq && !r->msgclass) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n");
+ goto malformed;
+ }
+
+ offset_plain = gg_fix32(r->offset_plain);
+ offset_attr = gg_fix32(r->offset_attr);
+
+ if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n");
+ goto malformed;
+ }
+
+ if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n");
+ offset_attr = 0; /* nie parsuj attr. */
+ /* goto ignore; */
+ }
+
+ /* Normalna sytuacja, więc nie podpada pod powyższy warunek. */
+ if (offset_attr == h->length)
+ offset_attr = 0;
+
+ if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n");
+ goto malformed;
+ }
+
+ if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n");
+ goto malformed;
+ }
+
+ e->type = GG_EVENT_MSG;
+ e->event.msg.msgclass = gg_fix32(r->msgclass);
+ e->event.msg.sender = gg_fix32(r->sender);
+ e->event.msg.time = gg_fix32(r->time);
+ e->event.msg.seq = gg_fix32(r->seq);
+
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ e->event.msg.message = (unsigned char*) strdup(packet + offset_plain);
+ } else {
+ if (offset_plain > sizeof(struct gg_recv_msg80)) {
+ int len;
+
+ len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80));
+
+ e->event.msg.message = malloc(len + 1);
+
+ if (e->event.msg.message == NULL)
+ goto fail;
+
+ gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80));
+ } else {
+ e->event.msg.message = (unsigned char*) gg_cp_to_utf8(packet + offset_plain);
+ }
+ }
+
+ if (offset_plain > sizeof(struct gg_recv_msg80)) {
+ if (sess->encoding == GG_ENCODING_UTF8)
+ e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80));
+ else
+ e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80));
+ } else {
+ e->event.msg.xhtml_message = NULL;
+ }
+
+ if (offset_attr != 0) {
+ switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) {
+ case -1: // handled
+ return 0;
+
+ case -2: // failed
+ goto fail;
+
+ case -3: // malformed
+ goto malformed;
+ }
+ }
return 0;
fail:
+ free(e->event.msg.message);
+ free(e->event.msg.xhtml_message);
free(e->event.msg.recipients);
free(e->event.msg.formats);
return -1;
+
+malformed:
+ e->type = GG_EVENT_NONE;
+ free(e->event.msg.message);
+ free(e->event.msg.xhtml_message);
+ free(e->event.msg.recipients);
+ free(e->event.msg.formats);
+ return 0;
}
-/*
- * gg_watch_fd_connected() // funkcja wewntrzna
+/**
+ * \internal Odbiera pakiet od serwera.
*
- * patrzy na gniazdo, odbiera pakiet i wypenia struktur zdarzenia.
+ * Analizuje pakiet i wypełnia strukturę zdarzenia.
*
- * - sess - struktura opisujca sesj
- * - e - opis zdarzenia
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
*
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
*/
static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
{
struct gg_header *h = NULL;
char *p;
- gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
if (!sess) {
errno = EFAULT;
@@ -462,76 +709,86 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
}
if (!(h = gg_recv_packet(sess))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
goto fail;
}
p = (char*) h + sizeof(struct gg_header);
-
+
switch (h->type) {
case GG_RECV_MSG:
{
if (h->length >= sizeof(struct gg_recv_msg))
if (gg_handle_recv_msg(h, e, sess))
goto fail;
-
+
break;
}
+ case GG_RECV_MSG80:
+ {
+ if (h->length >= sizeof(struct gg_recv_msg80))
+ if (gg_handle_recv_msg80(h, e, sess))
+ goto fail;
+
+ break;
+ }
+
+
case GG_NOTIFY_REPLY:
{
struct gg_notify_reply *n = (void*) p;
unsigned int count, i;
char *tmp;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
if (h->length < sizeof(*n)) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
errno = EINVAL;
goto fail;
}
if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) {
e->type = GG_EVENT_NOTIFY_DESCR;
-
+
if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
goto fail;
}
e->event.notify_descr.notify[1].uin = 0;
memcpy(e->event.notify_descr.notify, p, sizeof(*n));
e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin);
e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status);
- e->event.notify_descr.notify[0].remote_ip = e->event.notify_descr.notify[0].remote_ip;
e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port);
+ e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version);
count = h->length - sizeof(*n);
if (!(tmp = malloc(count + 1))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
goto fail;
}
memcpy(tmp, p + sizeof(*n), count);
tmp[count] = 0;
e->event.notify_descr.descr = tmp;
-
+
} else {
e->type = GG_EVENT_NOTIFY;
-
+
if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
goto fail;
}
-
+
memcpy(e->event.notify, p, h->length);
count = h->length / sizeof(*n);
e->event.notify[count].uin = 0;
-
+
for (i = 0; i < count; i++) {
e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin);
e->event.notify[i].status = gg_fix32(e->event.notify[i].status);
- e->event.notify[i].remote_ip = e->event.notify[i].remote_ip;
e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port);
+ e->event.notify[i].version = gg_fix32(e->event.notify[i].version);
}
}
@@ -542,7 +799,7 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
{
struct gg_status *s = (void*) p;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
if (h->length >= sizeof(*s)) {
e->type = GG_EVENT_STATUS;
@@ -564,23 +821,176 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
break;
}
+ case GG_NOTIFY_REPLY77:
+ case GG_NOTIFY_REPLY80BETA:
+ {
+ struct gg_notify_reply77 *n = (void*) p;
+ unsigned int length = h->length, i = 0;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ e->type = GG_EVENT_NOTIFY60;
+ e->event.notify60 = malloc(sizeof(*e->event.notify60));
+
+ if (!e->event.notify60) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+
+ e->event.notify60[0].uin = 0;
+
+ while (length >= sizeof(struct gg_notify_reply77)) {
+ uin_t uin = gg_fix32(n->uin);
+ char *tmp;
+
+ e->event.notify60[i].uin = uin & 0x00ffffff;
+ e->event.notify60[i].status = n->status;
+ e->event.notify60[i].remote_ip = n->remote_ip;
+ e->event.notify60[i].remote_port = gg_fix16(n->remote_port);
+ e->event.notify60[i].version = n->version;
+ e->event.notify60[i].image_size = n->image_size;
+ e->event.notify60[i].descr = NULL;
+ e->event.notify60[i].time = 0;
+
+ if (uin & 0x40000000)
+ e->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
+ if (uin & 0x20000000)
+ e->event.notify60[i].version |= GG_HAS_AUDIO7_MASK;
+ if (uin & 0x08000000)
+ e->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
+
+ if (GG_S_D(n->status)) {
+ unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77));
+
+ if (sizeof(struct gg_notify_reply77) + descr_len <= length) {
+ char *descr;
+
+ if (!(descr = malloc(descr_len + 1))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+
+ memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len);
+ descr[descr_len] = 0;
+
+ if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) {
+ char *cp_descr = gg_utf8_to_cp(descr);
+
+ if (!cp_descr) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ free(descr);
+ goto fail;
+ }
+
+ free(descr);
+ descr = cp_descr;
+ }
+
+ e->event.notify60[i].descr = descr;
+
+ /* XXX czas */
+
+ length -= sizeof(struct gg_notify_reply77) + descr_len + 1;
+ n = (void*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1);
+ } else {
+ length = 0;
+ }
+
+ } else {
+ length -= sizeof(struct gg_notify_reply77);
+ n = (void*) ((char*) n + sizeof(struct gg_notify_reply77));
+ }
+
+ if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ free(e->event.notify60);
+ goto fail;
+ }
+
+ e->event.notify60 = (void*) tmp;
+ e->event.notify60[++i].uin = 0;
+ }
+
+ break;
+ }
+
+ case GG_STATUS77:
+ case GG_STATUS80BETA:
+ {
+ struct gg_status77 *s = (void*) p;
+ uint32_t uin;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+ if (h->length < sizeof(*s))
+ break;
+
+ uin = gg_fix32(s->uin);
+
+ e->type = GG_EVENT_STATUS60;
+ e->event.status60.uin = uin & 0x00ffffff;
+ e->event.status60.status = s->status;
+ e->event.status60.remote_ip = s->remote_ip;
+ e->event.status60.remote_port = gg_fix16(s->remote_port);
+ e->event.status60.version = s->version;
+ e->event.status60.image_size = s->image_size;
+ e->event.status60.descr = NULL;
+ e->event.status60.time = 0;
+
+ if (uin & 0x40000000)
+ e->event.status60.version |= GG_HAS_AUDIO_MASK;
+ if (uin & 0x20000000)
+ e->event.status60.version |= GG_HAS_AUDIO7_MASK;
+ if (uin & 0x08000000)
+ e->event.status60.version |= GG_ERA_OMNIX_MASK;
+
+ if (h->length > sizeof(*s)) {
+ int len = h->length - sizeof(*s);
+ char *buf = malloc(len + 1);
+
+ /* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ?
+ * - goto fail; (?)
+ */
+ if (buf) {
+ memcpy(buf, (char*) p + sizeof(*s), len);
+ buf[len] = 0;
+
+ if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) {
+ char *cp_buf = gg_utf8_to_cp(buf);
+ free(buf);
+ buf = cp_buf;
+ }
+ }
+
+ e->event.status60.descr = buf;
+
+ if (len > 4 && p[h->length - 5] == 0) {
+ uint32_t t;
+ memcpy(&t, p + h->length - 4, sizeof(uint32_t));
+ e->event.status60.time = gg_fix32(t);
+ }
+ }
+
+ break;
+ }
+
case GG_NOTIFY_REPLY60:
{
struct gg_notify_reply60 *n = (void*) p;
unsigned int length = h->length, i = 0;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
e->type = GG_EVENT_NOTIFY60;
e->event.notify60 = malloc(sizeof(*e->event.notify60));
if (!e->event.notify60) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
goto fail;
}
e->event.notify60[0].uin = 0;
-
+
while (length >= sizeof(struct gg_notify_reply60)) {
uin_t uin = gg_fix32(n->uin);
char *tmp;
@@ -602,9 +1012,9 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
if (GG_S_D(n->status)) {
unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
- if (descr_len < length) {
+ if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
goto fail;
}
@@ -612,17 +1022,20 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
e->event.notify60[i].descr[descr_len] = 0;
/* XXX czas */
+
+ length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
+ n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+ } else {
+ length = 0;
}
-
- length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
- n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+
} else {
length -= sizeof(struct gg_notify_reply60);
n = (void*) ((char*) n + sizeof(struct gg_notify_reply60));
}
if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
free(e->event.notify60);
goto fail;
}
@@ -633,13 +1046,13 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
break;
}
-
+
case GG_STATUS60:
{
struct gg_status60 *s = (void*) p;
uint32_t uin;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
if (h->length < sizeof(*s))
break;
@@ -682,11 +1095,132 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
break;
}
+ case GG_STATUS80:
+ {
+ struct gg_notify_reply80 *s = (void*) p;
+ uint32_t descr_len;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+ if (h->length < sizeof(*s))
+ break;
+
+ e->type = GG_EVENT_STATUS60;
+ e->event.status60.uin = gg_fix32(s->uin);
+ e->event.status60.status = gg_fix32(s->status);
+ e->event.status60.remote_ip = s->remote_ip;
+ e->event.status60.remote_port = gg_fix16(s->remote_port);
+ e->event.status60.image_size = s->image_size;
+ e->event.status60.descr = NULL;
+ e->event.status60.version = 0x00; /* not-supported */
+ e->event.status60.time = 0; /* not-supported */
+
+ descr_len = gg_fix32(s->descr_len);
+
+ if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) {
+ char *buf = malloc(descr_len + 1);
+
+ if (buf) {
+ memcpy(buf, (char*) p + sizeof(*s), descr_len);
+ buf[descr_len] = 0;
+
+ if (sess->encoding != GG_ENCODING_UTF8) {
+ char *cp_buf = gg_utf8_to_cp(buf);
+ free(buf);
+ buf = cp_buf;
+ }
+ }
+
+ e->event.status60.descr = buf;
+ }
+ break;
+ }
+
+ case GG_NOTIFY_REPLY80:
+ {
+ struct gg_notify_reply80 *n = (void*) p;
+ unsigned int length = h->length, i = 0;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ e->type = GG_EVENT_NOTIFY60;
+ e->event.notify60 = malloc(sizeof(*e->event.notify60));
+
+ if (!e->event.notify60) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+
+ e->event.notify60[0].uin = 0;
+
+ while (length >= sizeof(struct gg_notify_reply80)) {
+ uint32_t descr_len;
+ char *tmp;
+
+ e->event.notify60[i].uin = gg_fix32(n->uin);
+ e->event.notify60[i].status = gg_fix32(n->status);
+ e->event.notify60[i].remote_ip = n->remote_ip;
+ e->event.notify60[i].remote_port= gg_fix16(n->remote_port);
+ e->event.notify60[i].image_size = n->image_size;
+ e->event.notify60[i].descr = NULL;
+ e->event.notify60[i].version = 0x00; /* not-supported */
+ e->event.notify60[i].time = 0; /* not-supported */
+
+ descr_len = gg_fix32(n->descr_len);
+
+ length -= sizeof(struct gg_notify_reply80);
+ n = (void*) ((char*) n + sizeof(struct gg_notify_reply80));
+
+ if (descr_len) {
+ if (length >= descr_len) {
+ /* XXX, GG_S_D(n->status) */
+ char *descr;
+
+ if (!(descr = malloc(descr_len + 1))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+
+ memcpy(descr, n, descr_len);
+ descr[descr_len] = 0;
+
+ if (sess->encoding != GG_ENCODING_UTF8) {
+ char *cp_descr = gg_utf8_to_cp(descr);
+
+ if (!cp_descr) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ free(descr);
+ goto fail;
+ }
+
+ free(descr);
+ descr = cp_descr;
+ }
+ e->event.notify60[i].descr = descr;
+
+ length -= descr_len;
+ n = (void*) ((char*) n + descr_len);
+ } else
+ length = 0;
+ }
+
+ if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ free(e->event.notify60);
+ goto fail;
+ }
+
+ e->event.notify60 = (void*) tmp;
+ e->event.notify60[++i].uin = 0;
+ }
+ break;
+ }
+
case GG_SEND_MSG_ACK:
{
struct gg_send_msg_ack *s = (void*) p;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
if (h->length < sizeof(*s))
break;
@@ -699,9 +1233,9 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
break;
}
- case GG_PONG:
+ case GG_PONG:
{
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
e->type = GG_EVENT_PONG;
sess->last_pong = time(NULL);
@@ -711,27 +1245,47 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
case GG_DISCONNECTING:
{
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
e->type = GG_EVENT_DISCONNECT;
break;
}
+ case GG_DISCONNECT_ACK:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n");
+ e->type = GG_EVENT_DISCONNECT_ACK;
+ break;
+ }
+
+ case GG_XML_EVENT:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n");
+ e->type = GG_EVENT_XML_EVENT;
+ if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n");
+ goto fail;
+ }
+ memcpy(e->event.xml_event.data, p, h->length);
+ e->event.xml_event.data[h->length] = 0;
+ break;
+ }
+
case GG_PUBDIR50_REPLY:
{
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
- if (gg_pubdir50_handle_reply(e, p, h->length) == -1)
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
+ if (gg_pubdir50_handle_reply_sess(sess, e, p, h->length) == -1)
goto fail;
break;
}
case GG_USERLIST_REPLY:
{
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
if (h->length < 1)
break;
- /* jeli odpowied na eksport, wywoaj zdarzenie tylko
+ /* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
* gdy otrzymano wszystkie odpowiedzi */
if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) {
if (--sess->userlist_blocks)
@@ -743,11 +1297,11 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
if (h->length > 1) {
char *tmp;
unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0;
-
- gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
-
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
+
if (!(tmp = realloc(sess->userlist_reply, len + h->length))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
free(sess->userlist_reply);
sess->userlist_reply = NULL;
goto fail;
@@ -769,10 +1323,75 @@ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
break;
}
+ case GG_DCC7_ID_REPLY:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
+
+ if (h->length < sizeof(struct gg_dcc7_id_reply))
+ break;
+
+ if (gg_dcc7_handle_id(sess, e, p, h->length) == -1)
+ goto fail;
+
+ break;
+ }
+
+ case GG_DCC7_ACCEPT:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
+
+ if (h->length < sizeof(struct gg_dcc7_accept))
+ break;
+
+ if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1)
+ goto fail;
+
+ break;
+ }
+
+ case GG_DCC7_NEW:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
+
+ if (h->length < sizeof(struct gg_dcc7_new))
+ break;
+
+ if (gg_dcc7_handle_new(sess, e, p, h->length) == -1)
+ goto fail;
+
+ break;
+ }
+
+ case GG_DCC7_REJECT:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
+
+ if (h->length < sizeof(struct gg_dcc7_reject))
+ break;
+
+ if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1)
+ goto fail;
+
+ break;
+ }
+
+ case GG_DCC7_INFO:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
+
+ if (h->length < sizeof(struct gg_dcc7_info))
+ break;
+
+ if (gg_dcc7_handle_info(sess, e, p, h->length) == -1)
+ goto fail;
+
+ break;
+ }
+
default:
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
}
-
+
free(h);
return 0;
@@ -781,19 +1400,20 @@ fail:
return -1;
}
-/*
- * gg_watch_fd()
+/** \endcond */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
*
- * funkcja, ktr naley wywoa, gdy co si stanie z obserwowanym
- * deskryptorem. zwraca klientowi informacj o tym, co si dzieje.
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
*
- * - sess - opis sesji
+ * \param sess Struktura sesji
*
- * wskanik do struktury gg_event, ktr trzeba zwolni pniej
- * za pomoc gg_event_free(). jesli rodzaj zdarzenia jest rwny
- * GG_EVENT_NONE, naley je zignorowa. jeli zwrcio NULL,
- * stao si co niedobrego -- albo zabrako pamici albo zerwao
- * poczenie.
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup events
*/
struct gg_event *gg_watch_fd(struct gg_session *sess)
{
@@ -802,68 +1422,79 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
int port = 0;
int errno2 = 0;
- gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
-
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
+
if (!sess) {
errno = EFAULT;
return NULL;
}
if (!(e = (void*) calloc(1, sizeof(*e)))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
return NULL;
}
e->type = GG_EVENT_NONE;
+ if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
+
+ res = write(sess->fd, sess->send_buf, sess->send_left);
+
+ if (res == -1 && errno != EAGAIN) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno));
+
+ if (sess->state == GG_STATE_READING_REPLY)
+ goto fail_connecting;
+ else
+ goto done;
+ }
+
+ if (res == sess->send_left) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
+ free(sess->send_buf);
+ sess->send_buf = NULL;
+ sess->send_left = 0;
+ } else if (res > 0) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res);
+
+ memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
+ sess->send_left -= res;
+ }
+ }
+
switch (sess->state) {
case GG_STATE_RESOLVING:
{
struct in_addr addr;
int failed = 0;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
failed = 1;
errno2 = errno;
}
-
+
close(sess->fd);
sess->fd = -1;
-#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
- waitpid(sess->pid, NULL, 0);
- sess->pid = -1;
-#endif
+ sess->resolver_cleanup(&sess->resolver, 0);
if (failed) {
errno = errno2;
goto fail_resolving;
}
- /* jeli jestemy w resolverze i mamy ustawiony port
- * proxy, znaczy, e resolvowalimy proxy. zatem
+ /* jeśli jesteśmy w resolverze i mamy ustawiony port
+ * proxy, znaczy, że resolvowaliśmy proxy. zatem
* wpiszmy jego adres. */
if (sess->proxy_port)
sess->proxy_addr = addr.s_addr;
/* zapiszmy sobie adres huba i adres serwera (do
- * bezporedniego poczenia, jeli hub ley)
+ * bezpośredniego połączenia, jeśli hub leży)
* z resolvera. */
if (sess->proxy_addr && sess->proxy_port)
port = sess->proxy_port;
@@ -872,21 +1503,27 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
port = GG_APPMSG_PORT;
}
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
-
- /* czymy si albo z hubem, albo z proxy, zalenie
- * od tego, co resolvowalimy. */
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
+
+ /* łączymy się albo z hubem, albo z proxy, zależnie
+ * od tego, co resolvowaliśmy. */
if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
- /* jeli w trybie asynchronicznym gg_connect()
- * zwrci bd, nie ma sensu prbowa dalej. */
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+ /* jeśli w trybie asynchronicznym gg_connect()
+ * zwróci błąd, nie ma sensu próbować dalej. */
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
goto fail_connecting;
}
- /* jeli podano serwer i czmy si przez proxy,
- * jest to bezporednie poczenie, inaczej jest
+ /* jeśli podano serwer i łączmy się przez proxy,
+ * jest to bezpośrednie połączenie, inaczej jest
* do huba. */
- sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB;
+
+ if (sess->proxy_addr && sess->proxy_port && sess->server_addr) {
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->soft_timeout = 1;
+ } else
+ sess->state = GG_STATE_CONNECTING_HUB;
+
sess->check = GG_CHECK_WRITE;
sess->timeout = GG_DEFAULT_TIMEOUT;
@@ -897,43 +1534,26 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
{
char buf[1024], *client, *auth;
int res = 0;
- socklen_t res_size = sizeof(res);
- const char *host, *appmsg;
+ unsigned int res_size = sizeof(res);
+ const char *host;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
- /* jeli asynchroniczne, sprawdzamy, czy nie wystpi
- * przypadkiem jaki bd. */
+ /* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
+ * przypadkiem jakiś błąd. */
if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
- /* no tak, nie udao si poczy z proxy. nawet
- * nie prbujemy dalej. */
- if (sess->proxy_addr && sess->proxy_port) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
- goto fail_connecting;
- }
-
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res));
- close(sess->fd);
-
- if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) {
- /* przy asynchronicznych, gg_connect()
- * zwraca -1 przy bdach socket(),
- * ioctl(), braku routingu itd. dlatego
- * nawet nie prbujemy dalej. */
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
- goto fail_connecting;
- }
+ if (sess->proxy_addr && sess->proxy_port)
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+ else
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res));
- sess->state = GG_STATE_CONNECTING_GG;
- sess->check = GG_CHECK_WRITE;
- sess->timeout = GG_DEFAULT_TIMEOUT;
- break;
+ goto fail_connecting;
}
-
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
goto fail_connecting;
}
@@ -942,41 +1562,43 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
else
host = "";
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
- if (sess->ssl)
- appmsg = "appmsg3.asp";
- else
-#endif
- appmsg = "appmsg2.asp";
-
auth = gg_proxy_auth();
- snprintf(buf, sizeof(buf) - 1,
- "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
- "Host: " GG_APPMSG_HOST "\r\n"
- "User-Agent: " GG_HTTP_USERAGENT "\r\n"
- "Pragma: no-cache\r\n"
- "%s"
- "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
-
- if (auth)
- free(auth);
-
+#ifdef GG_CONFIG_HAVE_OPENSSL
+ if (sess->ssl) {
+ snprintf(buf, sizeof(buf) - 1,
+ "GET %s/appsvc/appmsg3.asp?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
+ "Host: " GG_APPMSG_HOST "\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Pragma: no-cache\r\n"
+ "%s"
+ "\r\n", host, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
+ } else
+#endif
+ {
+ snprintf(buf, sizeof(buf) - 1,
+ "GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
+ "Host: " GG_APPMSG_HOST "\r\n"
+ "%s"
+ "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+ }
+
+ free(auth);
free(client);
- /* zwolnij pami po wersji klienta. */
+ /* zwolnij pamięć po wersji klienta. */
if (sess->client_version) {
free(sess->client_version);
sess->client_version = NULL;
}
- gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
-
- /* zapytanie jest krtkie, wic zawsze zmieci si
- * do bufora gniazda. jeli write() zwrci mniej,
- * stao si co zego. */
+ gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
+
+ /* zapytanie jest krótkie, więc zawsze zmieści się
+ * do bufora gniazda. jeśli write() zwróci mniej,
+ * stało się coś złego. */
if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
e->type = GG_EVENT_CONN_FAILED;
e->event.failure = GG_FAILURE_WRITING;
@@ -999,72 +1621,36 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
int port = GG_DEFAULT_PORT;
struct in_addr addr;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
- /* czytamy lini z gniazda i obcinamy \r\n. */
+ /* czytamy linię z gniazda i obcinamy \r\n. */
gg_read_line(sess->fd, buf, sizeof(buf) - 1);
gg_chomp(buf);
- gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
-
- /* sprawdzamy, czy wszystko w porzdku. */
- if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n");
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
- close(sess->fd);
-
- /* jeli otrzymalimy jakie dziwne informacje,
- * prbujemy si czy z pominiciem huba. */
- if (sess->proxy_addr && sess->proxy_port) {
- if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
- /* trudno. nie wyszo. */
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail_connecting;
- }
-
- sess->state = GG_STATE_CONNECTING_GG;
- sess->check = GG_CHECK_WRITE;
- sess->timeout = GG_DEFAULT_TIMEOUT;
- break;
- }
-
- sess->port = GG_DEFAULT_PORT;
-
- /* czymy si na port 8074 huba. */
- if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
-
- sess->port = GG_HTTPS_PORT;
-
- /* czymy si na port 443. */
- if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail_connecting;
- }
- }
-
- sess->state = GG_STATE_CONNECTING_GG;
- sess->check = GG_CHECK_WRITE;
- sess->timeout = GG_DEFAULT_TIMEOUT;
- break;
+ /* sprawdzamy, czy wszystko w porządku. */
+ if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
+ goto fail_connecting;
}
-
- /* ignorujemy reszt nagwka. */
+
+ /* ignorujemy resztę nagłówka. */
while (strcmp(buf, "\r\n") && strcmp(buf, ""))
gg_read_line(sess->fd, buf, sizeof(buf) - 1);
- /* czytamy pierwsz lini danych. */
+ /* czytamy pierwszą linię danych. */
gg_read_line(sess->fd, buf, sizeof(buf) - 1);
gg_chomp(buf);
-
- /* jeli pierwsza liczba w linii nie jest rwna zeru,
- * oznacza to, e mamy wiadomo systemow. */
+
+ /* jeśli pierwsza liczba w linii nie jest równa zeru,
+ * oznacza to, że mamy wiadomość systemową. */
if (atoi(buf)) {
char tmp[1024], *foo, *sysmsg_buf = NULL;
int len = 0;
-
+
while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) {
if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
break;
}
@@ -1074,23 +1660,32 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
strcpy(sysmsg_buf, tmp);
else
strcat(sysmsg_buf, tmp);
-
+
len += strlen(tmp);
}
-
+
e->type = GG_EVENT_MSG;
e->event.msg.msgclass = atoi(buf);
e->event.msg.sender = 0;
- e->event.msg.message = (unsigned char *)sysmsg_buf;
+ e->event.msg.message = (unsigned char*) sysmsg_buf;
}
-
+
close(sess->fd);
-
- gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
+
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
/* analizujemy otrzymane dane. */
tmp = buf;
-
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+ if (!sess->ssl)
+#endif
+ {
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ while (*tmp && *tmp == ' ')
+ tmp++;
+ }
while (*tmp && *tmp != ' ')
tmp++;
while (*tmp && *tmp == ' ')
@@ -1105,36 +1700,43 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
port = atoi(tmp + 1);
}
+ if (!strcmp(host, "notoperating")) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
+ sess->fd = -1;
+ goto fail_unavailable;
+ }
+
addr.s_addr = inet_addr(host);
sess->server_addr = addr.s_addr;
if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) {
- /* jeli mamy proxy, czymy si z nim. */
+ /* jeśli mamy proxy, łączymy się z nim. */
if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
- /* nie wyszo? trudno. */
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
+ /* nie wyszło? trudno. */
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
goto fail_connecting;
}
-
+
sess->state = GG_STATE_CONNECTING_GG;
sess->check = GG_CHECK_WRITE;
sess->timeout = GG_DEFAULT_TIMEOUT;
+ sess->soft_timeout = 1;
break;
}
sess->port = port;
- /* czymy si z waciwym serwerem. */
+ /* łączymy się z właściwym serwerem. */
if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
sess->port = GG_HTTPS_PORT;
- /* nie wyszo? prbujemy portu 443. */
+ /* nie wyszło? próbujemy portu 443. */
if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
- /* ostatnia deska ratunku zawioda?
+ /* ostatnia deska ratunku zawiodła?
* w takim razie zwijamy manatki. */
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
goto fail_connecting;
}
}
@@ -1142,23 +1744,26 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
sess->state = GG_STATE_CONNECTING_GG;
sess->check = GG_CHECK_WRITE;
sess->timeout = GG_DEFAULT_TIMEOUT;
-
+ sess->soft_timeout = 1;
+
break;
}
case GG_STATE_CONNECTING_GG:
{
int res = 0;
- socklen_t res_size = sizeof(res);
+ unsigned int res_size = sizeof(res);
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
+ sess->soft_timeout = 0;
- /* jeli wystpi bd podczas czenia si... */
+ /* jeśli wystąpił błąd podczas łączenia się... */
if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
- /* jeli nie udao si poczenie z proxy,
- * nie mamy czego prbowa wicej. */
+ /* jeśli nie udało się połączenie z proxy,
+ * nie mamy czego próbować więcej. */
if (sess->proxy_addr && sess->proxy_port) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
goto fail_connecting;
}
@@ -1170,36 +1775,46 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
errno = ETIMEDOUT;
#endif
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
- /* jeli logujemy si po TLS, nie prbujemy
- * si czy ju z niczym innym w przypadku
- * bdu. nie do, e nie ma sensu, to i
- * trzeba by si bawi w tworzenie na nowo
+#ifdef GG_CONFIG_HAVE_OPENSSL
+ /* jeśli logujemy się po TLS, nie próbujemy
+ * się łączyć już z niczym innym w przypadku
+ * błędu. nie dość, że nie ma sensu, to i
+ * trzeba by się bawić w tworzenie na nowo
* SSL i SSL_CTX. */
if (sess->ssl) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
goto fail_connecting;
}
#endif
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
+
+ if (sess->port == GG_HTTPS_PORT)
+ goto fail_connecting;
sess->port = GG_HTTPS_PORT;
- /* prbujemy na port 443. */
+ /* próbujemy na port 443. */
if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
goto fail_connecting;
}
+
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ sess->soft_timeout = 1;
+
+ break;
}
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
-
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+
if (gg_proxy_http_only)
sess->proxy_port = 0;
- /* jeli mamy proxy, wylijmy zapytanie. */
+ /* jeśli mamy proxy, wyślijmy zapytanie. */
if (sess->proxy_addr && sess->proxy_port) {
char buf[100], *auth = gg_proxy_auth();
struct in_addr addr;
@@ -1211,20 +1826,22 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf);
-
- /* wysyamy zapytanie. jest ono na tyle krtkie,
- * e musi si zmieci w buforze gniazda. jeli
- * write() zawiedzie, stao si co zego. */
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf);
+
+ /* wysyłamy zapytanie. jest ono na tyle krótkie,
+ * że musi się zmieścić w buforze gniazda. jeśli
+ * write() zawiedzie, stało się coś złego. */
if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ free(auth);
goto fail_connecting;
}
if (auth) {
- gg_debug(GG_DEBUG_MISC, "// %s", auth);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// %s", auth);
if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ free(auth);
goto fail_connecting;
}
@@ -1232,12 +1849,12 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
}
if (write(sess->fd, "\r\n", 2) < 2) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
goto fail_connecting;
}
}
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
if (sess->ssl) {
SSL_set_fd(sess->ssl, sess->fd);
@@ -1256,19 +1873,19 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
break;
}
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
case GG_STATE_TLS_NEGOTIATION:
{
int res;
X509 *peer;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
if ((res = SSL_connect(sess->ssl)) <= 0) {
int err = SSL_get_error(sess->ssl, res);
if (res == 0) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
e->type = GG_EVENT_CONN_FAILED;
e->event.failure = GG_FAILURE_TLS;
@@ -1277,9 +1894,9 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
sess->fd = -1;
break;
}
-
+
if (err == SSL_ERROR_WANT_READ) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
sess->state = GG_STATE_TLS_NEGOTIATION;
sess->check = GG_CHECK_READ;
@@ -1287,7 +1904,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
break;
} else if (err == SSL_ERROR_WANT_WRITE) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
sess->state = GG_STATE_TLS_NEGOTIATION;
sess->check = GG_CHECK_WRITE;
@@ -1299,8 +1916,8 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
-
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
+
e->type = GG_EVENT_CONN_FAILED;
e->event.failure = GG_FAILURE_TLS;
sess->state = GG_STATE_IDLE;
@@ -1310,20 +1927,20 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
}
}
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl));
peer = SSL_get_peer_certificate(sess->ssl);
if (!peer)
- gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n");
else {
char buf[1024];
X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
- gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf);
X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
- gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
}
sess->state = GG_STATE_READING_KEY;
@@ -1336,45 +1953,48 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
case GG_STATE_READING_KEY:
{
- struct gg_header *h;
+ struct gg_header *h;
struct gg_welcome *w;
- struct gg_login60 l;
- unsigned int hash;
- char *password = sess->password;
+ unsigned char *password = (unsigned char*) sess->password;
int ret;
-
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
+ uint8_t login_hash[64];
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
- memset(&l, 0, sizeof(l));
- l.dunno2 = 0xbe;
+ memset(login_hash, 0, sizeof(login_hash));
- /* XXX bardzo, bardzo, bardzo gupi pomys na pozbycie
- * si tekstu wrzucanego przez proxy. */
+ /* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie
+ * się tekstu wrzucanego przez proxy. */
if (sess->proxy_addr && sess->proxy_port) {
char buf[100];
strcpy(buf, "");
gg_read_line(sess->fd, buf, sizeof(buf) - 1);
gg_chomp(buf);
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf);
-
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf);
+
while (strcmp(buf, "")) {
gg_read_line(sess->fd, buf, sizeof(buf) - 1);
gg_chomp(buf);
if (strcmp(buf, ""))
- gg_debug(GG_DEBUG_MISC, "// %s\n", buf);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// %s\n", buf);
}
/* XXX niech czeka jeszcze raz w tej samej
- * fazie. gupio, ale dziaa. */
+ * fazie. głupio, ale działa. */
sess->proxy_port = 0;
-
+
break;
}
/* czytaj pierwszy pakiet. */
if (!(h = gg_recv_packet(sess))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+ if (errno == EAGAIN) {
+ sess->check = GG_CHECK_READ;
+ break;
+ }
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
e->type = GG_EVENT_CONN_FAILED;
e->event.failure = GG_FAILURE_READING;
@@ -1387,7 +2007,7 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
}
if (h->type != GG_WELCOME) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
free(h);
close(sess->fd);
sess->fd = -1;
@@ -1397,61 +2017,127 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
sess->state = GG_STATE_IDLE;
break;
}
-
+
w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header));
w->key = gg_fix32(w->key);
- hash = gg_login_hash((unsigned char *)password, w->key);
-
- gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash);
-
- free(h);
+ switch (sess->hash_type) {
+ case GG_LOGIN_HASH_GG32:
+ {
+ unsigned int hash;
+
+ hash = gg_fix32(gg_login_hash(password, w->key));
+ gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash);
+ memcpy(login_hash, &hash, sizeof(hash));
+ break;
+ }
+
+ case GG_LOGIN_HASH_SHA1:
+ {
+ char tmp[41];
+ int i;
+
+ gg_login_hash_sha1((char*) password, w->key, login_hash);
+ for (i = 0; i < 40; i += 2)
+ snprintf(tmp + i, sizeof(tmp) - i, "%02x", login_hash[i / 2]);
+
+ gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp);
+
+ break;
+ }
+ }
+
+ free(h);
free(sess->password);
sess->password = NULL;
- {
- struct in_addr dcc_ip;
- dcc_ip.s_addr = gg_dcc_ip;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip));
- }
-
if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
struct sockaddr_in sin;
- socklen_t sin_len = sizeof(sin);
+ unsigned int sin_len = sizeof(sin);
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
- l.local_ip = sin.sin_addr.s_addr;
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
+ sess->client_addr = sin.sin_addr.s_addr;
} else {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
- l.local_ip = 0;
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
+ sess->client_addr = 0;
}
- } else
- l.local_ip = gg_dcc_ip;
-
- l.uin = gg_fix32(sess->uin);
- l.hash = gg_fix32(hash);
- l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
- l.version = gg_fix32(sess->protocol_version);
- l.local_port = gg_fix16(gg_dcc_port);
- l.image_size = sess->image_size;
-
- if (sess->external_addr && sess->external_port > 1023) {
- l.external_ip = sess->external_addr;
- l.external_port = gg_fix16(sess->external_port);
- }
+ } else
+ sess->client_addr = gg_dcc_ip;
- gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n");
- ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL);
+ if (sess->protocol_version >= 0x2e) {
+ struct gg_login80 l;
+
+ uint32_t tmp_version_len = gg_fix32(strlen(GG8_VERSION));
+ uint32_t tmp_descr_len = gg_fix32((sess->initial_descr) ? strlen(sess->initial_descr) : 0);
+
+ memset(&l, 0, sizeof(l));
+ l.uin = gg_fix32(sess->uin);
+ memcpy(l.language, GG8_LANG, sizeof(l.language));
+ l.hash_type = sess->hash_type;
+ memcpy(l.hash, login_hash, sizeof(login_hash));
+ l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+ l.flags = gg_fix32(0x00800001);
+ l.features = gg_fix32(sess->protocol_features);
+ l.image_size = sess->image_size;
+ l.dunno2 = 0x64;
+
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n");
+ ret = gg_send_packet(sess, GG_LOGIN80,
+ &l, sizeof(l),
+ &tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION),
+ &tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+ NULL);
+
+ } else if (sess->protocol_version == 0x2d) {
+ struct gg_login70 l;
+
+ memset(&l, 0, sizeof(l));
+ l.uin = gg_fix32(sess->uin);
+ l.local_ip = (sess->external_addr) ? sess->external_addr : sess->client_addr;
+ l.local_port = gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
+ l.hash_type = sess->hash_type;
+ memcpy(l.hash, login_hash, sizeof(login_hash));
+ l.image_size = sess->image_size;
+ l.dunno2 = 0x64;
+ l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+ l.version = gg_fix32(sess->protocol_version | sess->protocol_flags);
+
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n");
+ ret = gg_send_packet(sess, GG_LOGIN80BETA,
+ &l, sizeof(l),
+ sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+ (sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0,
+ NULL);
+ } else {
+ struct gg_login70 l;
+
+ memset(&l, 0, sizeof(l));
+ l.local_ip = (sess->external_addr) ? sess->external_addr : sess->client_addr;
+ l.uin = gg_fix32(sess->uin);
+ l.local_port = gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
+ l.hash_type = sess->hash_type;
+ memcpy(l.hash, login_hash, sizeof(login_hash));
+ l.image_size = sess->image_size;
+ l.dunno2 = 0xbe;
+ l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+ l.version = gg_fix32(sess->protocol_version | sess->protocol_flags);
+
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n");
+ ret = gg_send_packet(sess, GG_LOGIN70,
+ &l, sizeof(l),
+ sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+ NULL);
+ }
free(sess->initial_descr);
sess->initial_descr = NULL;
if (ret == -1) {
- gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
errno2 = errno;
close(sess->fd);
errno = errno2;
@@ -1461,8 +2147,9 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
sess->state = GG_STATE_IDLE;
break;
}
-
+
sess->state = GG_STATE_READING_REPLY;
+ sess->check = GG_CHECK_READ;
break;
}
@@ -1471,10 +2158,15 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
{
struct gg_header *h;
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
if (!(h = gg_recv_packet(sess))) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+ if (errno == EAGAIN) {
+ sess->check = GG_CHECK_READ;
+ break;
+ }
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
e->type = GG_EVENT_CONN_FAILED;
e->event.failure = GG_FAILURE_READING;
sess->state = GG_STATE_IDLE;
@@ -1484,11 +2176,12 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
sess->fd = -1;
break;
}
-
- if (h->type == GG_LOGIN_OK) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
+
+ if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
e->type = GG_EVENT_CONN_SUCCESS;
sess->state = GG_STATE_CONNECTED;
+ sess->check = GG_CHECK_READ;
sess->timeout = -1;
sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL;
free(h);
@@ -1496,15 +2189,15 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
}
if (h->type == GG_LOGIN_FAILED) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
e->event.failure = GG_FAILURE_PASSWORD;
errno = EACCES;
- } else if (h->type == GG_NEED_EMAIL) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n");
- e->event.failure = GG_FAILURE_NEED_EMAIL;
+ } else if (h->type == GG_DISCONNECTING) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
+ e->event.failure = GG_FAILURE_INTRUDER;
errno = EACCES;
} else {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
e->event.failure = GG_FAILURE_INVALID;
errno = EINVAL;
}
@@ -1522,13 +2215,13 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
case GG_STATE_CONNECTED:
{
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
sess->last_event = time(NULL);
-
+
if ((res = gg_watch_fd_connected(sess, e)) == -1) {
- gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
if (errno == EAGAIN) {
e->type = GG_EVENT_NONE;
@@ -1536,6 +2229,9 @@ struct gg_event *gg_watch_fd(struct gg_session *sess)
} else
res = -1;
}
+
+ sess->check = GG_CHECK_READ;
+
break;
}
}
@@ -1544,10 +2240,13 @@ done:
if (res == -1) {
free(e);
e = NULL;
+ } else {
+ if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED))
+ sess->check |= GG_CHECK_WRITE;
}
return e;
-
+
fail_connecting:
if (sess->fd != -1) {
errno2 = errno;
@@ -1565,6 +2264,12 @@ fail_resolving:
e->event.failure = GG_FAILURE_RESOLVING;
sess->state = GG_STATE_IDLE;
goto done;
+
+fail_unavailable:
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_UNAVAILABLE;
+ sess->state = GG_STATE_IDLE;
+ goto done;
}
/*
diff --git a/libpurple/protocols/gg/lib/http.c b/libpurple/protocols/gg/lib/http.c
index 39fa3ff25b..00d9dd2d4a 100644
--- a/libpurple/protocols/gg/lib/http.c
+++ b/libpurple/protocols/gg/lib/http.c
@@ -1,4 +1,4 @@
-/* $Id: http.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: http.c 833 2009-10-01 20:48:01Z wojtekka $ */
/*
* (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,52 +14,60 @@
*
* 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 http.c
+ *
+ * \brief Obsługa połączeń HTTP
+ */
#include <sys/types.h>
-#ifndef _WIN32
-#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#endif
-#include "libgadu-config.h"
+#include "compat.h"
+#include "libgadu.h"
+#include "resolver.h"
#include <ctype.h>
#include <errno.h>
-#ifndef _WIN32
#include <netdb.h>
-#endif
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-# include <pthread.h>
-#endif
+#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "compat.h"
-
-/*
- * gg_http_connect() // funkcja pomocnicza
+/**
+ * Rozpoczyna połączenie HTTP.
+ *
+ * Funkcja przeprowadza połączenie HTTP przy połączeniu synchronicznym,
+ * zwracając wynik w polach struktury \c gg_http, lub błąd, gdy sesja się
+ * nie powiedzie.
+ *
+ * Przy połączeniu asynchronicznym, funkcja rozpoczyna połączenie, a dalsze
+ * etapy będą przeprowadzane po wykryciu zmian (\c watch) na obserwowanym
+ * deskryptorze (\c fd) i wywołaniu funkcji \c gg_http_watch_fd().
*
- * rozpoczyna poczenie po http.
+ * Po zakończeniu, należy zwolnić strukturę za pomocą funkcji
+ * \c gg_http_free(). Połączenie asynchroniczne można zatrzymać w każdej
+ * chwili za pomocą \c gg_http_stop().
*
- * - hostname - adres serwera
- * - port - port serwera
- * - async - asynchroniczne poczenie
- * - method - metoda http (GET, POST, cokolwiek)
- * - path - cieka do zasobu (musi by poprzedzona ,,/'')
- * - header - nagwek zapytania plus ewentualne dane dla POST
+ * \param hostname Adres serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego połączenia
+ * \param method Metoda HTTP
+ * \param path Ścieżka do zasobu (musi być poprzedzona znakiem '/')
+ * \param header Nagłówek zapytania plus ewentualne dane dla POST
*
- * zaalokowana struct gg_http, ktr poniej naley
- * zwolni funkcj gg_http_free(), albo NULL jeli wystpi bd.
+ * \return Zaalokowana struktura \c gg_http lub NULL, jeśli wystąpił błąd.
+ *
+ * \ingroup http
*/
struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header)
{
@@ -70,7 +78,7 @@ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const
errno = EFAULT;
return NULL;
}
-
+
if (!(h = malloc(sizeof(*h))))
return NULL;
memset(h, 0, sizeof(*h));
@@ -80,6 +88,8 @@ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const
h->fd = -1;
h->type = GG_SESSION_HTTP;
+ gg_http_set_resolver(h, GG_RESOLVER_DEFAULT);
+
if (gg_proxy_enabled) {
char *auth = gg_proxy_auth();
@@ -88,9 +98,8 @@ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const
"", header);
hostname = gg_proxy_host;
h->port = port = gg_proxy_port;
+ free(auth);
- if (auth)
- free(auth);
} else {
h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
method, path, header);
@@ -102,17 +111,11 @@ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const
errno = ENOMEM;
return NULL;
}
-
+
gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
if (async) {
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
- if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
-#elif defined _WIN32
- if (gg_resolve_win32thread(&h->fd, &h->resolver, hostname)) {
-#else
- if (gg_resolve(&h->fd, &h->pid, hostname)) {
-#endif
+ if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
gg_http_free(h);
errno = ENOENT;
@@ -125,19 +128,16 @@ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const
h->check = GG_CHECK_READ;
h->timeout = GG_DEFAULT_TIMEOUT;
} else {
- struct in_addr *hn, a;
+ struct in_addr addr;
- if (!(hn = gg_gethostbyname(hostname))) {
+ if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
gg_http_free(h);
errno = ENOENT;
return NULL;
- } else {
- a.s_addr = hn->s_addr;
- free(hn);
}
- if (!(h->fd = gg_connect(&a, port, 0)) == -1) {
+ if ((h->fd = gg_connect(&addr, port, 0)) == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno));
gg_http_free(h);
return NULL;
@@ -159,10 +159,12 @@ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const
h->callback = gg_http_watch_fd;
h->destroy = gg_http_free;
-
+
return h;
}
+#ifndef DOXYGEN
+
#define gg_http_error(x) \
close(h->fd); \
h->fd = -1; \
@@ -170,17 +172,22 @@ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const
h->error = x; \
return 0;
-/*
- * gg_http_watch_fd()
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe
+ * \c GG_STATE_PARSING. W tym miejscu działanie przejmuje zwykle funkcja
+ * korzystająca z \c gg_http_watch_fd(). W przypadku błędu połączenia,
+ * pole \c state będzie równe \c GG_STATE_ERROR, a kod błędu znajdzie się
+ * w polu \c error.
*
- * przy asynchronicznej obsudze HTTP funkcj t naley wywoa, jeli
- * zmienio si co na obserwowanym deskryptorze.
+ * \param h Struktura połączenia
*
- * - h - struktura opisujca poczenie
+ * \return \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * jeli wszystko poszo dobrze to 0, inaczej -1. poczenie bdzie
- * zakoczone, jeli h->state == GG_STATE_PARSING. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * \ingroup http
*/
int gg_http_watch_fd(struct gg_http *h)
{
@@ -205,22 +212,7 @@ int gg_http_watch_fd(struct gg_http *h)
close(h->fd);
h->fd = -1;
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
- if (h->resolver) {
- pthread_cancel(*((pthread_t *) h->resolver));
- free(h->resolver);
- h->resolver = NULL;
- }
-#elif defined _WIN32
- if (h->resolver) {
- HANDLE hnd = h->resolver;
- TerminateThread(hnd, 0);
- CloseHandle(hnd);
- h->resolver = NULL;
- }
-#else
- waitpid(h->pid, NULL, 0);
-#endif
+ h->resolver_cleanup(&h->resolver, 0);
gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port);
@@ -238,7 +230,7 @@ int gg_http_watch_fd(struct gg_http *h)
if (h->state == GG_STATE_CONNECTING) {
int res = 0;
- socklen_t res_size = sizeof(res);
+ unsigned int res_size = sizeof(res);
if (h->async && (getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
gg_debug(GG_DEBUG_MISC, "=> http, async connection failed (errno=%d, %s)\n", (res) ? res : errno , strerror((res) ? res : errno));
@@ -257,14 +249,14 @@ int gg_http_watch_fd(struct gg_http *h)
}
if (h->state == GG_STATE_SENDING_QUERY) {
- ssize_t res;
+ int res;
if ((res = write(h->fd, h->query, strlen(h->query))) < 1) {
gg_debug(GG_DEBUG_MISC, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h->query), res, errno);
gg_http_error(GG_ERROR_WRITING);
}
- if (res < 0 || (size_t)res < strlen(h->query)) {
+ if (res < strlen(h->query)) {
gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res);
memmove(h->query, h->query + res, strlen(h->query) - res + 1);
@@ -346,7 +338,7 @@ int gg_http_watch_fd(struct gg_http *h)
h->body_size = 0;
line = h->header;
*tmp = 0;
-
+
gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
while (line) {
@@ -449,7 +441,7 @@ int gg_http_watch_fd(struct gg_http *h)
return 0;
}
-
+
if (h->fd != -1)
close(h->fd);
@@ -460,14 +452,14 @@ int gg_http_watch_fd(struct gg_http *h)
return -1;
}
-#undef gg_http_error
-
-/*
- * gg_http_stop()
+/**
+ * Kończy asynchroniczne połączenie HTTP.
+ *
+ * Po zatrzymaniu należy zwolnić zasoby funkcją \c gg_http_free().
*
- * jeli poczenie jest w trakcie, przerywa je. nie zwalnia h->data.
- *
- * - h - struktura opisujca poczenie
+ * \param h Struktura połączenia
+ *
+ * \ingroup http
*/
void gg_http_stop(struct gg_http *h)
{
@@ -477,15 +469,20 @@ void gg_http_stop(struct gg_http *h)
if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
return;
- if (h->fd != -1)
+ if (h->fd != -1) {
close(h->fd);
- h->fd = -1;
+ h->fd = -1;
+ }
+
+ h->resolver_cleanup(&h->resolver, 1);
}
-/*
- * gg_http_free_fields() // funkcja wewntrzna
+/**
+ * \internal Zwalnia pola struktury \c gg_http.
*
- * zwalnia pola struct gg_http, ale nie zwalnia samej struktury.
+ * Funkcja zwalnia same pola, nie zwalnia struktury.
+ *
+ * \param h Struktura połączenia
*/
void gg_http_free_fields(struct gg_http *h)
{
@@ -501,19 +498,21 @@ void gg_http_free_fields(struct gg_http *h)
free(h->query);
h->query = NULL;
}
-
+
if (h->header) {
free(h->header);
h->header = NULL;
}
}
-/*
- * gg_http_free()
+/**
+ * Zwalnia zasoby po połączeniu HTTP.
+ *
+ * Jeśli połączenie nie zostało jeszcze zakończone, jest przerywane.
*
- * prbuje zamkn poczenie i zwalnia pami po nim.
+ * \param h Struktura połączenia
*
- * - h - struktura, ktr naley zlikwidowa
+ * \ingroup http
*/
void gg_http_free(struct gg_http *h)
{
diff --git a/libpurple/protocols/gg/lib/libgadu-internal.h b/libpurple/protocols/gg/lib/libgadu-internal.h
new file mode 100644
index 0000000000..07628f5122
--- /dev/null
+++ b/libpurple/protocols/gg/lib/libgadu-internal.h
@@ -0,0 +1,30 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef LIBGADU_INTERNAL_H
+#define LIBGADU_INTERNAL_H
+
+#include "libgadu.h"
+
+char *gg_cp_to_utf8(const char *b);
+char *gg_utf8_to_cp(const char *b);
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length);
+
+#endif /* LIBGADU_INTERNAL_H */
diff --git a/libpurple/protocols/gg/lib/libgadu.c b/libpurple/protocols/gg/lib/libgadu.c
index fe85738205..200ebdc2a6 100644
--- a/libpurple/protocols/gg/lib/libgadu.c
+++ b/libpurple/protocols/gg/lib/libgadu.c
@@ -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. Wony <speedy@ziew.org>
- * Arkadiusz Mikiewicz <arekm@pld-linux.org>
- * Tomasz Chiliski <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;
+
+/**
+ * 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 opisujce parametry proxy http.
+
+/**
+ * Flaga włączenia połączeń przez serwer pośredniczący.
+ *
+ * \ingroup proxy
*/
-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: 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.
+ * Adres serwera pośredniczącego.
*
- * -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.
+ * \ingroup proxy
*/
-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;
- }
+char *gg_proxy_host = NULL;
- arg = 0;
- if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) {
- goto out2;
- }
+/**
+ * Port serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_port = 0;
- fds[0] = socket1;
- fds[1] = socket2;
+/**
+ * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_http_only = 0;
- closesocket (temp);
+/**
+ * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
+char *gg_proxy_username = NULL;
- return 0;
+/**
+ * Hasło użytkownika do autoryzacji serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
+char *gg_proxy_password = NULL;
-out2:
- closesocket (socket2);
-out1:
- closesocket (socket1);
-out0:
- closesocket (temp);
- errno = EIO; /* XXX */
+#ifndef DOXYGEN
- return -1;
-}
+#ifndef lint
+static char rcsid[]
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+= "$Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $";
#endif
-/*
- * gg_libgadu_version()
- *
- * zwraca wersj libgadu.
+#endif /* DOXYGEN */
+
+/**
+ * Zwraca wersję biblioteki.
*
- * - brak
+ * \return Wskaźnik na statyczny bufor z wersją biblioteki.
*
- * 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.
*
- * zamienia kolejno bajtw w liczbie 32-bitowej tak, by odpowiadaa
- * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera,
- * zamienia tylko na maszynach BE-wych.
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejność bajtów w słowie.
*
- * - x - liczba do zamiany
+ * \param x Liczba do zamiany
*
- * liczba z odpowiedni kolejnoci bajtw.
+ * \return Liczba z odpowiednią kolejnością bajtów
+ *
+ * \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 @@ uint32_t gg_fix32(uint32_t x)
#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 bajtw w liczbie 16-bitowej tak, by odpowiadaa
- * kolejnoci bajtw w protokole GG. ze wzgldu 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 kolejnoci bajtw.
+ * \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 @@ uint16_t gg_fix16(uint16_t x)
#endif
}
-/*
- * gg_login_hash() // funkcja wewntrzna
- *
- * liczy hash z hasa i danego seeda.
- *
- * - password - haso do hashowania
- * - seed - warto podana przez serwer
+/**
+ * \internal Liczy skrót z hasła i ziarna.
+ *
+ * \param password Hasło
+ * \param seed Ziarno podane przez serwer
*
- * hash.
+ * \return Wartość skrótu
*/
unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
{
@@ -304,313 +264,22 @@ unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
return y;
}
-#ifndef _WIN32
-
-/*
- * gg_resolve() // funkcja wewntrzna
- *
- * tworzy potok, forkuje si i w drugim procesie zaczyna resolvowa
- * podanego hosta. zapisuje w sesji deskryptor potoku. jeli co tam
- * bdzie gotowego, znaczy, e mona wczyta struct in_addr. jeli
- * nie znajdzie, zwraca INADDR_NONE.
- *
- * - fd - wskanik 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) {
- 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; /* eby kompilator nie marudzi */
-}
-
-/*
- * gg_resolve_pthread() // funkcja wewntrzna
- *
- * tworzy potok, nowy wtek i w nim zaczyna resolvowa podanego hosta.
- * zapisuje w sesji deskryptor potoku. jeli co tam bdzie gotowego,
- * znaczy, e mona wczyta struct in_addr. jeli nie znajdzie, zwraca
- * INADDR_NONE.
- *
- * - fd - wskanik do zmiennej przechowujcej desktyptor resolvera
- * - resolver - wskanik do wskanika 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;
-}
-
-#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
+/**
+ * \internal Odbiera od serwera dane binarne.
*
- * czyta z gniazda okrelon ilo bajtw. bierze pod uwag, czy mamy
- * poczenie zwyke czy TLS.
+ * Funkcja odbiera dane od serwera zajmując się TLS w razie konieczności.
*
- * - sess - sesja,
- * - buf - bufor,
- * - length - ilo bajtw,
+ * \param sess Struktura sesji
+ * \param buf Bufor na danymi
+ * \param length Długość bufora
*
- * takie same wartoci 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 @@ int gg_read(struct gg_session *sess, char *buf, int length)
return res;
}
-/*
- * gg_write() // funkcja pomocnicza
+/**
+ * \internal Wysyła do serwera dane binarne.
*
- * zapisuje do gniazda okrelon ilo bajtw. bierze pod uwag, czy mamy
- * poczenie zwyke czy TLS.
+ * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności.
*
- * - sess - sesja,
- * - buf - bufor,
- * - length - ilo bajtw,
+ * \param sess Struktura sesji
+ * \param buf Bufor z danymi
+ * \param length Długość bufora
*
- * takie same wartoci 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,46 +332,78 @@ int gg_write(struct gg_session *sess, const char *buf, int length)
} 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;
- if (res == -1) {
- if (errno == EAGAIN)
continue;
- else
- break;
- } else {
+ }
+
written += res;
res = written;
}
+ } else {
+ if (!sess->send_buf)
+ res = write(sess->fd, buf, length);
+ else
+ res = 0;
+
+ if (res == -1) {
+ 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;
+ }
}
}
return res;
}
-/*
- * gg_recv_packet() // funkcja wewntrzna
+/**
+ * \internal Odbiera pakiet od serwera.
*
- * odbiera jeden pakiet i zwraca wskanik do niego. pami po nim
- * naley 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 bdu NULL, kod bdu w errno. naley zwrci uwag, e gdy
- * poczenie jest nieblokujce, a kod bdu wynosi EAGAIN, nie udao si
- * odczyta caego pakietu i nie naley tego traktowa jako bd.
+ * \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_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
- gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
-
if (!sess) {
errno = EFAULT;
return NULL;
@@ -712,7 +412,7 @@ void *gg_recv_packet(struct gg_session *sess)
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 @@ void *gg_recv_packet(struct gg_session *sess)
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 @@ void *gg_recv_packet(struct gg_session *sess)
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 @@ void *gg_recv_packet(struct gg_session *sess)
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 @@ void *gg_recv_packet(struct gg_session *sess)
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 wewntrzna
- *
- * konstruuje pakiet i wysya go do serwera.
- *
- * - sock - deskryptor gniazda
- * - type - typ pakietu
- * - payload_1 - pierwsza cz pakietu
- * - payload_length_1 - dugo pierwszej czci
- * - payload_2 - druga cz pakietu
- * - payload_length_2 - dugo drugiej czci
- * - ... - kolejne czci pakietu i ich dugoci
- * - NULL - kocowym parametr (konieczny!)
- *
- * jeli si powiodo, zwraca 0, w przypadku bdu -1. jeli errno == ENOMEM,
- * zabrako pamici. inaczej by bd przy wysyaniu pakietu. dla errno == 0
- * nie wysano caego pakietu.
+/**
+ * \internal Wysyła pakiet 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.
+ *
+ * \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
+ *
+ * \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 @@ int gg_send_packet(struct gg_session *sess, int type, ...)
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 @@ int gg_send_packet(struct gg_session *sess, int type, ...)
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 wewntrzna
+/**
+ * \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.
*
- * wywoywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje
- * do gg_session->event jego wynik.
+ * \note Korzystanie z tej funkcjonalności nie jest już zalecane.
+ *
+ * \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()
- *
- * rozpoczyna procedur czenia si z serwerem. reszt obsuguje si przez
- * gg_watch_fd().
- *
- * UWAGA! program musi obsuy SIGCHLD, jeli czy si asynchronicznie,
- * eby poprawnie zamkn proces resolvera.
- *
- * - p - struktura opisujca pocztkowy stan. wymagane pola: uin,
- * password
- *
- * w przypadku bdu NULL, jeli idzie dobrze (async) albo poszo
- * dobrze (sync), zwrci wskanik do zaalokowanej struct gg_session.
+/**
+ * Łączy się z serwerem Gadu-Gadu.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * \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().
+ *
+ * \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 @@ struct gg_session *gg_login(const struct gg_login_params *p)
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 @@ struct gg_session *gg_login(const struct gg_login_params *p)
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 @@ struct gg_session *gg_login(const struct gg_login_params *p)
} 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 @@ struct gg_session *gg_login(const struct gg_login_params *p)
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 @@ struct gg_session *gg_login(const struct gg_login_params *p)
port = GG_APPMSG_PORT;
}
+ if (p->hash_type)
+ sess->hash_type = p->hash_type;
+ else
+ sess->hash_type = GG_LOGIN_HASH_SHA1;
+
if (!p->async) {
- struct in_addr a;
+ struct in_addr addr;
- 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 (!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(&a, port, 0)) == -1) {
+ 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));
- goto fail;
+
+ /* nie wyszło? próbujemy portu 443. */
+ if (sess->server_addr) {
+ sess->port = GG_HTTPS_PORT;
+
+ 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 @@ struct gg_session *gg_login(const struct gg_login_params *p)
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 @@ struct gg_session *gg_login(const struct gg_login_params *p)
}
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.
*
- * prbuje zamkn poczenia i zwalnia pami zajmowan przez sesj.
+ * \param sess Struktura sesji
*
- * - sess - opis sesji
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup login
*/
-void gg_free_session(struct gg_session *sess)
+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.
+ *
+ * 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_logoff(struct gg_session *sess)
{
if (!sess)
return;
- /* XXX dopisa zwalnianie i zamykanie wszystkiego, co mogo zosta */
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
- if (sess->password)
- free(sess->password);
-
- if (sess->initial_descr)
- free(sess->initial_descr);
+ if (GG_S_NA(sess->status))
+ gg_change_status(sess, GG_STATUS_NOT_AVAIL);
- if (sess->client_version)
- free(sess->client_version);
+#ifdef GG_CONFIG_HAVE_OPENSSL
+ if (sess->ssl)
+ SSL_shutdown(sess->ssl);
+#endif
- if (sess->header_buf)
- free(sess->header_buf);
+ sess->resolver_cleanup(&sess->resolver, 1);
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ 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;
+ }
+}
+
+/**
+ * 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)
+ return;
+
+ /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
+
+ 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 @@ void gg_free_session(struct gg_session *sess)
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 @@ void gg_free_session(struct gg_session *sess)
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 uytkownika. 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 uytkownika
+ * \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 @@ int gg_change_status(struct gg_session *sess, int status)
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.
+ *
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
*
- * zmienia status uytkownika na opisowy.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * - sess - opis sesji
- * - status - nowy status uytkownika
- * - 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_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
- gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
+ return gg_change_status_common(sess, status, descr, 0);
+}
- if (!sess || !descr) {
- errno = EFAULT;
- return -1;
+/**
+ * Zmienia status użytkownika na status opisowy z podanym czasem powrotu.
+ *
+ * \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
+ *
+ * \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)
+{
+ 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_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;
+ }
+
+ switch (utf_msg[i]) {
+ case '&':
+ gg_append(dst, &len, "&amp;", 5);
+ break;
+ case '<':
+ gg_append(dst, &len, "&lt;", 4);
+ break;
+ case '>':
+ gg_append(dst, &len, "&gt;", 4);
+ break;
+ case '\'':
+ gg_append(dst, &len, "&apos;", 6);
+ break;
+ case '\"':
+ gg_append(dst, &len, "&quot;", 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 (sess->state != GG_STATE_CONNECTED) {
- errno = ENOTCONN;
- return -1;
+ 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;
}
- p.status = gg_fix32(status);
+ gg_append(dst, &len, "</span>", 7);
- sess->status = status;
+ if (dst != NULL)
+ dst[len] = 0;
- 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 len;
}
-/*
- * gg_change_status_descr_time()
+/**
+ * Wysyła wiadomość formatowaną w ramach konferencji.
*
- * zmienia status uytkownika na opisowy z godzin powrotu.
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
*
- * - sess - opis sesji
- * - status - nowy status uytkownika
- * - descr - opis statusu
- * - time - czas w formacie uniksowym
+ * \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
*
- * 0, -1.
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
*/
-int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time)
+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_new_status p;
- uint32_t newtime;
+ 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(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+ 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 || !descr || !time) {
+ if (!sess) {
errno = EFAULT;
return -1;
}
@@ -1306,75 +1505,184 @@ int gg_change_status_descr_time(struct gg_session *sess, int status, const char
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;
+ }
- sess->status = status;
+ if (sess->encoding == GG_ENCODING_UTF8) {
+ if (!(cp_msg = gg_utf8_to_cp((const char *) message)))
+ return -1;
- newtime = gg_fix32(time);
+ utf_msg = (char*) message;
+ } else {
+ if (sess->protocol_version >= 0x2d) {
+ if (!(utf_msg = gg_cp_to_utf8((const char *) message)))
+ return -1;
+ }
- 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);
+ 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;
+
+ 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);
+
+ 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]);
+
+ 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.
+ *
+ * \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
*
- * wylogowuje uytkownika i zamyka poczenie, ale nie zwalnia pamici.
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
*
- * - sess - opis sesji
+ * \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);
+ struct gg_send_msg s;
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
- if (sess->ssl)
- SSL_shutdown(sess->ssl);
-#endif
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
-#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);
- sess->pid = -1;
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
}
-#endif
-
- if (sess->fd != -1) {
- shutdown(sess->fd, SHUT_RDWR);
- close(sess->fd);
- sess->fd = -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_image_request()
+/**
+ * Wysyła żądanie 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.
*
- * wysya danie wysania obrazka o podanych parametrach.
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param size Rozmiar obrazka w bajtach
+ * \param crc32 Suma kontrola obrazka
*
- * - sess - opis sesji
- * - recipient - numer adresata
- * - size - rozmiar obrazka
- * - crc32 - suma kontrolna obrazka
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0/-1
+ * \ingroup messages
*/
int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
{
@@ -1383,13 +1691,13 @@ int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_
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 @@ int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_
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 @@ int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_
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 @@ int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_
return res;
}
-/*
- * gg_image_reply()
+/**
+ * Wysyła żądany obrazek.
*
- * wysya dany obrazek.
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param filename Nazwa pliku
+ * \param image Bufor z obrazkiem
+ * \param size Rozmiar obrazka
*
- * - sess - opis sesji
- * - recipient - numer adresata
- * - filename - nazwa pliku
- * - image - bufor z obrazkiem
- * - size - rozmiar obrazka
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0/-1
+ * \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 @@ int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filenam
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 @@ int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filenam
return -1;
}
- /* wytnij cieki, 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 @@ int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filenam
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 @@ int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filenam
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 kawaku 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,248 +1844,25 @@ int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filenam
return res;
}
-/*
- * gg_send_message_ctcp()
- *
- * wysya wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomoci
- * - recipient - numer adresata
- * - message - tre wiadomoci
- * - message_len - dugo
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-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()
- *
- * wysya wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomoci
- * - recipient - numer adresata
- * - message - tre wiadomoci
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-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()
- *
- * wysya kolorow wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomoci
- * - recipient - numer adresata
- * - message - tre wiadomoci
- * - format - informacje o formatowaniu
- * - formatlen - dugo informacji o formatowaniu
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-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((const char *)message) + 1, format, formatlen, NULL) == -1)
- return -1;
-
- return gg_fix32(s.seq);
-}
-
-/*
- * gg_send_message_confer()
- *
- * wysya wiadomo do kilku uytkownikow (konferencja). zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomoci
- * - recipients_count - ilo adresatw
- * - recipients - numerki adresatw
- * - message - tre wiadomoci
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-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()
- *
- * wysya kolorow wiadomo do kilku uytkownikow (konferencja). zwraca
- * losowy numer sekwencyjny, ktry mona zignorowa albo wykorzysta do
- * potwierdzenia.
+/**
+ * Wysyła do serwera listę kontaktów.
*
- * - sess - opis sesji
- * - msgclass - rodzaj wiadomoci
- * - recipients_count - ilo adresatw
- * - recipients - numerki adresatw
- * - message - tre wiadomoci
- * - format - informacje o formatowaniu
- * - formatlen - dugo informacji o formatowaniu
+ * 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).
*
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-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()
+ * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli
+ * jest pusta.
*
- * wysya do serwera pakiet ping.
+ * \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
*
- * - sess - opis sesji
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 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()
- *
- * wysya serwerowi list kontaktw (wraz z odpowiadajcymi im typami userw),
- * dziki czemu wie, czyj stan nas interesuje.
- *
- * - sess - opis sesji
- * - userlist - wskanik do tablicy numerw
- * - types - wskanik do tablicy typw uytkownikw
- * - count - ilo numerkw
- *
- * 0, -1.
+ * \ingroup contacts
*/
int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
{
@@ -1786,13 +1871,13 @@ int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int coun
char *t;
int i, res = 0;
- gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
-
+ gg_debug_session(sess, 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;
@@ -1800,10 +1885,10 @@ int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int coun
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 @@ int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int coun
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 @@ int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int coun
return res;
}
-/*
- * gg_notify()
+/**
+ * Wysyła do serwera listę kontaktów.
+ *
+ * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
+ * są rodzaju \c GG_USER_NORMAL.
*
- * wysya serwerowi list kontaktw, dziki czemu wie, czyj stan nas
- * interesuje.
+ * \param sess Struktura sesji
+ * \param userlist Wskaźnik do tablicy numerów kontaktów
+ * \param count Liczba kontaktów
*
- * - sess - opis sesji
- * - userlist - wskanik do tablicy numerw
- * - count - ilo numerkw
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0, -1.
+ * \ingroup contacts
*/
int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
{
@@ -1854,13 +1941,13 @@ int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
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 @@ int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
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 @@ int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
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,24 +1990,27 @@ int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
return res;
}
-/*
- * gg_add_notify_ex()
+/**
+ * Dodaje kontakt.
+ *
+ * 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.
*
- * dodaje do listy kontaktw dany numer w trakcie poczenia.
- * dodawanemu uytkownikowi okrelamy jego typ (patrz protocol.html)
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
*
- * - sess - opis sesji
- * - uin - numer
- * - type - typ
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0, -1.
+ * \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);
-
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
if (!sess) {
errno = EFAULT;
return -1;
@@ -1930,46 +2020,50 @@ int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
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 kontakt.
+ *
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontaktów to \c GG_USER_NORMAL.
*
- * dodaje do listy kontaktw dany numer w trakcie poczenia.
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
*
- * - sess - opis sesji
- * - uin - numer
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0, -1.
+ * \ingroup contacts
*/
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 kontakt.
+ *
+ * Usuwa z listy kontaktów dany numer w trakcie połączenia.
*
- * usuwa z listy kontaktw w trakcie poczenia.
- * usuwanemu uytkownikowi okrelamy jego typ (patrz protocol.html)
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
*
- * - sess - opis sesji
- * - uin - numer
- * - type - typ
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0, -1.
+ * \ingroup contacts
*/
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);
-
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
if (!sess) {
errno = EFAULT;
return -1;
@@ -1982,35 +2076,48 @@ int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
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.
+ *
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontaktów to \c GG_USER_NORMAL.
*
- * usuwa z listy kontaktw w trakcie poczenia.
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
*
- * - sess - opis sesji
- * - uin - numer
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0, -1.
+ * \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.
+ *
+ * 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.
+ *
+ * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą
+ * z protokołu -- wysyła i odbiera kompletną listę.
*
- * wysya danie/zapytanie listy kontaktw na serwerze.
+ * \param sess Struktura sesji
+ * \param type Rodzaj zapytania
+ * \param request Treść zapytania (może być równe NULL)
*
- * - sess - opis sesji
- * - type - rodzaj zapytania/dania
- * - request - tre zapytania/dania (moe by NULL)
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0, -1
+ * \ingroup importexport
*/
int gg_userlist_request(struct gg_session *sess, char type, const char *request)
{
@@ -2020,7 +2127,7 @@ int gg_userlist_request(struct gg_session *sess, char type, const char *request)
errno = EFAULT;
return -1;
}
-
+
if (sess->state != GG_STATE_CONNECTED) {
errno = ENOTCONN;
return -1;
@@ -2030,7 +2137,7 @@ int gg_userlist_request(struct gg_session *sess, char type, const char *request)
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 @@ int gg_userlist_request(struct gg_session *sess, char type, const char *request)
return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
}
+/* @} */
+
/*
* Local variables:
* c-indentation-style: k&r
diff --git a/libpurple/protocols/gg/lib/libgadu.h b/libpurple/protocols/gg/lib/libgadu.h
index edbfbfe17c..52a1ce033f 100644
--- a/libpurple/protocols/gg/lib/libgadu.h
+++ b/libpurple/protocols/gg/lib/libgadu.h
@@ -1,12 +1,13 @@
-/* $Id: libgadu.h 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: libgadu.h.in 878 2009-11-16 23:48:19Z wojtekka $ */
/*
- * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
- * Robert J. Wony <speedy@ziew.org>
- * Arkadiusz Mikiewicz <arekm@pld-linux.org>
- * Tomasz Chiliski <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>
* Piotr Wysocki <wysek@linux.bydg.org>
* Dawid Jarosz <dawjar@poczta.onet.pl>
+ * Jakub Zawadzki <darkjames@darkjames.ath.cx>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License Version
@@ -19,338 +20,567 @@
*
* 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.
*/
+/**
+ * \file libgadu.h
+ *
+ * \brief Główny plik nagłówkowy biblioteki
+ */
+
#ifndef __GG_LIBGADU_H
#define __GG_LIBGADU_H
#ifdef __cplusplus
-#ifdef _MSC_VER
+#ifdef _WIN32
#pragma pack(push, 1)
#endif
extern "C" {
#endif
-#include <libgadu-config.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdarg.h>
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+/** \cond ignore */
+
+/* Defined if libgadu was compiled for bigendian machine. */
+#undef GG_CONFIG_BIGENDIAN
+
+/* Defined if this machine has gethostbyname_r(). */
+#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+
+/* Defined if libgadu was compiled and linked with pthread support. */
+#undef GG_CONFIG_HAVE_PTHREAD
+
+/* Defined if pthread resolver is the default one. */
+#undef GG_CONFIG_PTHREAD_DEFAULT
+
+/* Defined if this machine has C99-compiliant vsnprintf(). */
+#undef GG_CONFIG_HAVE_C99_VSNPRINTF
+
+/* Defined if this machine has va_copy(). */
+#undef GG_CONFIG_HAVE_VA_COPY
+
+/* Defined if this machine has __va_copy(). */
+#undef GG_CONFIG_HAVE___VA_COPY
+
+/* Defined if this machine supports long long. */
+#undef GG_CONFIG_HAVE_LONG_LONG
+
+/* Defined if libgadu was compiled and linked with TLS support. */
+#undef GG_CONFIG_HAVE_OPENSSL
+
+/* Defined if uintX_t types are defined in <stdint.h>. */
+#undef GG_CONFIG_HAVE_STDINT_H
+
+/* Defined if uintX_t types are defined in <inttypes.h>. */
+#undef GG_CONFIG_HAVE_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/inttypes.h>. */
+#undef GG_CONFIG_HAVE_SYS_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/int_types.h>. */
+#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+
+/* Defined if uintX_t types are defined in <sys/types.h>. */
+#undef GG_CONFIG_HAVE_SYS_TYPES_H
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
#include <openssl/ssl.h>
#endif
-/*
- * typedef uin_t
- *
- * typ reprezentujcy numer osoby.
+#ifdef GG_CONFIG_HAVE_STDINT_H
+#include <stdint.h>
+#else
+# ifdef GG_CONFIG_HAVE_INTTYPES_H
+# include <inttypes.h>
+# else
+# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H
+# include <sys/inttypes.h>
+# else
+# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+# else
+# ifdef GG_CONFIG_HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# else
+
+#ifndef __AC_STDINT_H
+#define __AC_STDINT_H
+
+/* ISO C 9X: 7.18 Integer types <stdint.h> */
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+
+#ifndef __CYGWIN__
+#define __int8_t_defined
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+#endif
+
+#endif /* __AC_STDINT_H */
+
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/** \endcond */
+
+/**
+ * Numer Gadu-Gadu.
*/
typedef uint32_t uin_t;
-/*
- * oglna struktura opisujca rne sesje. przydatna w klientach.
+/**
+ * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x.
*/
-#define gg_common_head(x) \
- int fd; /* podgldany deskryptor */ \
- int check; /* sprawdzamy zapis czy odczyt */ \
- int state; /* aktualny stan maszynki */ \
- int error; /* kod bdu dla GG_STATE_ERROR */ \
- int type; /* rodzaj sesji */ \
- int id; /* identyfikator */ \
- int timeout; /* sugerowany timeout w sekundach */ \
- int (*callback)(x*); /* callback przy zmianach */ \
- void (*destroy)(x*); /* funkcja niszczenia */
+typedef struct {
+ uint8_t id[8];
+} gg_dcc7_id_t;
+/**
+ * Makro deklarujące pola wspólne dla struktur sesji.
+ */
+#define gg_common_head(x) \
+ int fd; /**< Obserwowany deskryptor */ \
+ int check; /**< Informacja o żądaniu odczytu/zapisu (patrz \ref gg_check_t) */ \
+ int state; /**< Aktualny stan połączenia (patrz \ref gg_state_t) */ \
+ int error; /**< Kod błędu dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \
+ int type; /**< Rodzaj sesji (patrz \ref gg_session_t) */ \
+ int id; /**< Identyfikator sesji */ \
+ int timeout; /**< Czas pozostały do zakończenia stanu */ \
+ int (*callback)(x*); /**< Funkcja zwrotna */ \
+ void (*destroy)(x*); /**< Funkcja zwalniania zasobów */
+
+/**
+ * Struktura wspólna dla wszystkich sesji i połączeń. Pozwala na proste
+ * rzutowanie niezależne od rodzaju połączenia.
+ */
struct gg_common {
gg_common_head(struct gg_common)
};
struct gg_image_queue;
-/*
- * struct gg_session
+struct gg_dcc7;
+
+/**
+ * Sposób rozwiązywania nazw serwerów.
+ */
+typedef enum {
+ GG_RESOLVER_DEFAULT = 0, /**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */
+ GG_RESOLVER_FORK, /**< Rozwiązywanie nazw bazujące na procesach */
+ GG_RESOLVER_PTHREAD, /**< Rozwiązywanie nazw bazujące na wątkach */
+ GG_RESOLVER_CUSTOM, /**< Funkcje rozwiązywania nazw dostarczone przed aplikację */
+ GG_RESOLVER_INVALID = -1 /**< Nieprawidłowy sposób rozwiązywania nazw (wynik \c gg_session_get_resolver) */
+} gg_resolver_t;
+
+/**
+ * Rodzaj kodowania znaków.
+ */
+typedef enum {
+ GG_ENCODING_CP1250 = 0, /**< Kodowanie CP1250 */
+ GG_ENCODING_UTF8, /**< Kodowanie UTF-8 */
+ GG_ENCODING_INVALID = -1 /**< Nieprawidłowe kodowanie */
+} gg_encoding_t;
+
+/**
+ * Sesja Gadu-Gadu.
+ *
+ * Tworzona przez funkcję \c gg_login(), zwalniana przez \c gg_free_session().
*
- * struktura opisujca dan sesj. tworzona przez gg_login(), zwalniana
- * przez gg_free_session().
+ * \ingroup login
*/
struct gg_session {
gg_common_head(struct gg_session)
- int async; /* czy poczenie jest asynchroniczne */
- int pid; /* pid procesu resolvera */
- int port; /* port, z ktrym si czymy */
- int seq; /* numer sekwencyjny ostatniej wiadomoci */
- int last_pong; /* czas otrzymania ostatniego ping/pong */
- int last_event; /* czas otrzymania ostatniego pakietu */
+ int async; /**< Flaga połączenia asynchronicznego */
+ int pid; /**< Numer procesu rozwiązującego nazwę serwera */
+ int port; /**< Port serwera */
+ int seq; /**< Numer sekwencyjny ostatniej wiadomości */
+ int last_pong; /**< Czas otrzymania ostatniej ramki utrzymaniowej */
+ int last_event; /**< Czas otrzymania ostatniego pakietu */
- struct gg_event *event; /* zdarzenie po ->callback() */
+ struct gg_event *event; /**< Zdarzenie po wywołaniu \c callback */
- uint32_t proxy_addr; /* adres proxy, keszowany */
- uint16_t proxy_port; /* port proxy */
+ uint32_t proxy_addr; /**< Adres serwera pośredniczącego */
+ uint16_t proxy_port; /**< Port serwera pośredniczącego */
- uint32_t hub_addr; /* adres huba po resolvniciu */
- uint32_t server_addr; /* adres serwera, od huba */
+ uint32_t hub_addr; /**< Adres huba po rozwiązaniu nazwy */
+ uint32_t server_addr; /**< Adres serwera otrzymany od huba */
- uint32_t client_addr; /* adres klienta */
- uint16_t client_port; /* port, na ktrym klient sucha */
+ uint32_t client_addr; /**< Adres gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
+ uint16_t client_port; /**< Port gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
- uint32_t external_addr; /* adres zewnetrzny klienta */
- uint16_t external_port; /* port zewnetrzny klienta */
+ uint32_t external_addr; /**< Publiczny adres dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
+ uint16_t external_port; /**< Publiczny port dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
- uin_t uin; /* numerek klienta */
- char *password; /* i jego haso. zwalniane automagicznie */
+ uin_t uin; /**< Własny numer Gadu-Gadu */
+ char *password; /**< Hasło (zwalniane po użyciu) */
- int initial_status; /* pocztkowy stan klienta */
- int status; /* aktualny stan klienta */
+ int initial_status; /**< Początkowy status */
+ int status; /**< Aktualny status */
- char *recv_buf; /* bufor na otrzymywane pakiety */
- int recv_done; /* ile ju wczytano do bufora */
- int recv_left; /* i ile jeszcze trzeba wczyta */
+ char *recv_buf; /**< Bufor na odbierany pakiety */
+ int recv_done; /**< Liczba wczytanych bajtów pakietu */
+ int recv_left; /**< Liczba pozostałych do wczytania bajtów pakietu */
- int protocol_version; /* wersja uywanego protokou */
- char *client_version; /* wersja uywanego klienta */
- int last_sysmsg; /* ostatnia wiadomo systemowa */
+ int protocol_version; /**< Wersja protokołu (bez flag) */
+ char *client_version; /**< Wersja klienta */
+ int last_sysmsg; /**< Numer ostatniej wiadomości systemowej */
- char *initial_descr; /* pocztkowy opis stanu klienta */
+ char *initial_descr; /**< Początkowy opis statusu */
- void *resolver; /* wskanik na informacje resolvera */
+ void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
- char *header_buf; /* bufor na pocztek nagwka */
- unsigned int header_done;/* ile ju mamy */
+ char *header_buf; /**< Bufor na początek nagłówka pakietu */
+ unsigned int header_done; /**< Liczba wczytanych bajtów nagłówka pakietu */
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
- SSL *ssl; /* sesja TLS */
- SSL_CTX *ssl_ctx; /* kontekst sesji? */
+#ifdef GG_CONFIG_HAVE_OPENSSL
+ SSL *ssl; /**< Struktura TLS */
+ SSL_CTX *ssl_ctx; /**< Kontekst sesji TLS */
#else
- void *ssl; /* zachowujemy ABI */
- void *ssl_ctx;
+ void *ssl; /**< Struktura TLS */
+ void *ssl_ctx; /**< Kontekst sesji TLS */
#endif
- int image_size; /* maksymalny rozmiar obrazkw w KiB */
+ int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+
+ char *userlist_reply; /**< Bufor z odbieraną listą kontaktów */
+
+ int userlist_blocks; /**< Liczba części listy kontaktów */
- char *userlist_reply; /* fragment odpowiedzi listy kontaktw */
+ struct gg_image_queue *images; /**< Lista wczytywanych obrazków */
- int userlist_blocks; /* na ile kawakw podzielono list kontaktw */
+ int hash_type; /**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
- struct gg_image_queue *images; /* aktualnie wczytywane obrazki */
+ char *send_buf; /**< Bufor z danymi do wysłania */
+ int send_left; /**< Liczba bajtów do wysłania */
+
+ struct gg_dcc7 *dcc7_list; /**< Lista połączeń bezpośrednich skojarzonych z sesją */
+
+ int soft_timeout; /**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_watch_fd() */
+
+ int protocol_flags; /**< Flagi protokołu */
+
+ gg_encoding_t encoding; /**< Rodzaj kodowania znaków */
+
+ gg_resolver_t resolver_type; /**< Sposób rozwiązywania nazw serwerów */
+ int (*resolver_start)(int *fd, void **private_data, const char *hostname); /**< Funkcja rozpoczynająca rozwiązywanie nazwy */
+ void (*resolver_cleanup)(void **private_data, int force); /**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
+
+ int protocol_features; /**< Opcje protokołu */
};
-/*
- * struct gg_http
+/**
+ * Połączenie HTTP.
*
- * oglna struktura opisujca stan wszystkich operacji HTTP. tworzona
- * przez gg_http_connect(), zwalniana przez gg_http_free().
+ * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free().
+ *
+ * \ingroup http
*/
struct gg_http {
gg_common_head(struct gg_http)
- int async; /* czy poczenie asynchroniczne */
- int pid; /* pid procesu resolvera */
- int port; /* port, z ktrym si czymy */
+ int async; /**< Flaga połączenia asynchronicznego */
+ int pid; /**< Identyfikator procesu rozwiązującego nazwę serwera */
+ int port; /**< Port */
+
+ char *query; /**< Zapytanie HTTP */
+ char *header; /**< Odebrany nagłówek */
+ int header_size; /**< Rozmiar wczytanego nagłówka */
+ char *body; /**< Odebrana strona */
+ unsigned int body_size; /**< Rozmiar strony */
- char *query; /* bufor zapytania http */
- char *header; /* bufor nagwka */
- int header_size; /* rozmiar wczytanego nagwka */
- char *body; /* bufor otrzymanych informacji */
- unsigned int body_size; /* oczekiwana ilo informacji */
+ void *data; /**< Dane prywatne usługi HTTP */
- void *data; /* dane danej operacji http */
+ char *user_data; /**< Dane prywatne użytkownika (nie są zwalniane) */
- char *user_data; /* dane uytkownika, nie s zwalniane przez gg_http_free() */
+ void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę */
- void *resolver; /* wskanik na informacje resolvera */
+ unsigned int body_done; /**< Liczba odebranych bajtów strony */
- unsigned int body_done; /* ile ju treci odebrano? */
+ gg_resolver_t resolver_type; /**< Sposób rozwiązywania nazw serwerów */
+ int (*resolver_start)(int *fd, void **private_data, const char *hostname); /**< Funkcja rozpoczynająca rozwiązywanie nazwy */
+ void (*resolver_cleanup)(void **private_data, int force); /**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
};
+/** \cond ignore */
+
#ifdef __GNUC__
#define GG_PACKED __attribute__ ((packed))
+#ifndef GG_IGNORE_DEPRECATED
+#define GG_DEPRECATED __attribute__ ((deprecated))
+#else
+#define GG_DEPRECATED
+#endif
#else
#define GG_PACKED
+#define GG_DEPRECATED
#endif
-#define GG_MAX_PATH 276
+/** \endcond */
-/*
- * struct gg_file_info
+#define GG_MAX_PATH 276 /**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
+
+/**
+ * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
*
- * odpowiednik windowsowej struktury WIN32_FIND_DATA niezbdnej przy
- * wysyaniu plikw.
+ * Wykorzystywana przy połączeniach bezpośrednich do wersji Gadu-Gadu 6.x.
*/
struct gg_file_info {
- uint32_t mode; /* dwFileAttributes */
- uint32_t ctime[2]; /* ftCreationTime */
- uint32_t atime[2]; /* ftLastAccessTime */
- uint32_t mtime[2]; /* ftLastWriteTime */
- uint32_t size_hi; /* nFileSizeHigh */
- uint32_t size; /* nFileSizeLow */
- uint32_t reserved0; /* dwReserved0 */
- uint32_t reserved1; /* dwReserved1 */
- unsigned char filename[GG_MAX_PATH - 14]; /* cFileName */
- unsigned char short_filename[14]; /* cAlternateFileName */
-} GG_PACKED;
-
-/*
- * struct gg_dcc
+ uint32_t mode; /**< dwFileAttributes */
+ uint32_t ctime[2]; /**< ftCreationTime */
+ uint32_t atime[2]; /**< ftLastAccessTime */
+ uint32_t mtime[2]; /**< ftLastWriteTime */
+ uint32_t size_hi; /**< nFileSizeHigh */
+ uint32_t size; /**< nFileSizeLow */
+ uint32_t reserved0; /**< dwReserved0 */
+ uint32_t reserved1; /**< dwReserved1 */
+ unsigned char filename[GG_MAX_PATH - 14]; /**< cFileName */
+ unsigned char short_filename[14]; /**< cAlternateFileName */
+} /** \cond ignore */ GG_PACKED /** \endcond */;
+
+/**
+ * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x.
*
- * struktura opisujca nasuchujce gniazdo pocze midzy klientami.
- * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free().
+ * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(),
+ * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez
+ * \c gg_dcc_free().
+ *
+ * \ingroup dcc6
*/
struct gg_dcc {
gg_common_head(struct gg_dcc)
- struct gg_event *event; /* opis zdarzenia */
-
- int active; /* czy to my si czymy? */
- int port; /* port, na ktrym siedzi */
- uin_t uin; /* uin klienta */
- uin_t peer_uin; /* uin drugiej strony */
- int file_fd; /* deskryptor pliku */
- unsigned int offset; /* offset w pliku */
- unsigned int chunk_size;/* rozmiar kawaka */
- unsigned int chunk_offset;/* offset w aktualnym kawaku */
+ struct gg_event *event; /**< Zdarzenie po wywołaniu \c callback */
+
+ int active; /**< Flaga połączenia aktywnego (nieużywana) */
+ int port; /**< Port gniazda nasłuchującego */
+ uin_t uin; /**< Własny numer Gadu-Gadu */
+ uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony połączenia */
+ int file_fd; /**< deskryptor pliku */
+ unsigned int offset; /**< Położenie w pliku */
+ unsigned int chunk_size;
+ /**< Rozmiar kawałka pliku */
+ unsigned int chunk_offset;
+ /**< Położenie w aktualnym kawałku pliku */
struct gg_file_info file_info;
- /* informacje o pliku */
- int established; /* poczenie ustanowione */
- uint8_t *voice_buf; /* bufor na pakiet poczenia gosowego */
- int incoming; /* poczenie przychodzce */
- char *chunk_buf; /* bufor na kawaek danych */
- uint32_t remote_addr; /* adres drugiej strony */
- uint16_t remote_port; /* port drugiej strony */
+ /**< Informacje o pliku */
+ int established; /**< Flaga ustanowienia połączenia */
+ char *voice_buf; /**< Bufor na pakiet połączenia głosowego */
+ int incoming; /**< Flaga połączenia przychodzącego */
+ char *chunk_buf; /**< Bufor na fragment danych */
+ uint32_t remote_addr; /**< Adres drugiej strony */
+ uint16_t remote_port; /**< Port drugiej strony */
};
-/*
- * enum gg_session_t
+#define GG_DCC7_HASH_LEN 20 /**< Maksymalny rozmiar skrótu pliku w połączeniach bezpośrenich */
+#define GG_DCC7_FILENAME_LEN 255 /**< Maksymalny rozmiar nazwy pliku w połączeniach bezpośrednich */
+#define GG_DCC7_INFO_LEN 64 /**< Maksymalny rozmiar informacji o połączeniach bezpośrednich */
+
+/**
+ * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x.
*
- * rodzaje sesji.
+ * \ingroup dcc7
+ */
+struct gg_dcc7 {
+ gg_common_head(struct gg_dcc7)
+
+ gg_dcc7_id_t cid; /**< Identyfikator połączenia */
+
+ struct gg_event *event; /**< Struktura zdarzenia */
+
+ uin_t uin; /**< Własny numer Gadu-Gadu */
+ uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony połączenia */
+
+ int file_fd; /**< Deskryptor przesyłanego pliku */
+ unsigned int offset; /**< Aktualne położenie w przesyłanym pliku */
+ unsigned int size; /**< Rozmiar przesyłanego pliku */
+ unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
+ /**< Nazwa przesyłanego pliku */
+ unsigned char hash[GG_DCC7_HASH_LEN];
+ /**< Skrót SHA1 przesyłanego pliku */
+
+ int dcc_type; /**< Rodzaj połączenia bezpośredniego */
+ int established; /**< Flaga ustanowienia połączenia */
+ int incoming; /**< Flaga połączenia przychodzącego */
+ int reverse; /**< Flaga połączenia zwrotnego */
+
+ uint32_t local_addr; /**< Adres lokalny */
+ uint16_t local_port; /**< Port lokalny */
+
+ uint32_t remote_addr; /**< Adres drugiej strony */
+ uint16_t remote_port; /**< Port drugiej strony */
+
+ struct gg_session *sess;
+ /**< Sesja do której przypisano połączenie */
+ struct gg_dcc7 *next; /**< Następne połączenie w liście */
+
+ int soft_timeout; /**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_dcc7_watch_fd() */
+ int seek; /**< Flaga mówiąca, że można zmieniać położenie w wysyłanym pliku */
+};
+
+/**
+ * Rodzaj sesji.
*/
enum gg_session_t {
- GG_SESSION_GG = 1, /* poczenie z serwerem gg */
- GG_SESSION_HTTP, /* oglna sesja http */
- GG_SESSION_SEARCH, /* szukanie */
- GG_SESSION_REGISTER, /* rejestrowanie */
- GG_SESSION_REMIND, /* przypominanie hasa */
- GG_SESSION_PASSWD, /* zmiana hasa */
- GG_SESSION_CHANGE, /* zmiana informacji o sobie */
- GG_SESSION_DCC, /* oglne poczenie DCC */
- GG_SESSION_DCC_SOCKET, /* nasuchujcy socket */
- GG_SESSION_DCC_SEND, /* wysyanie pliku */
- GG_SESSION_DCC_GET, /* odbieranie pliku */
- GG_SESSION_DCC_VOICE, /* rozmowa gosowa */
- GG_SESSION_USERLIST_GET, /* pobieranie userlisty */
- GG_SESSION_USERLIST_PUT, /* wysyanie userlisty */
- GG_SESSION_UNREGISTER, /* usuwanie konta */
- GG_SESSION_USERLIST_REMOVE, /* usuwanie userlisty */
- GG_SESSION_TOKEN, /* pobieranie tokenu */
-
- GG_SESSION_USER0 = 256, /* zdefiniowana dla uytkownika */
- GG_SESSION_USER1, /* j.w. */
- GG_SESSION_USER2, /* j.w. */
- GG_SESSION_USER3, /* j.w. */
- GG_SESSION_USER4, /* j.w. */
- GG_SESSION_USER5, /* j.w. */
- GG_SESSION_USER6, /* j.w. */
- GG_SESSION_USER7 /* j.w. */
+ GG_SESSION_GG = 1, /**< Połączenie z serwerem Gadu-Gadu */
+ GG_SESSION_HTTP, /**< Połączenie HTTP */
+ GG_SESSION_SEARCH, /**< Wyszukiwanie w katalogu publicznym (nieaktualne) */
+ GG_SESSION_REGISTER, /**< Rejestracja nowego konta */
+ GG_SESSION_REMIND, /**< Przypominanie hasła */
+ GG_SESSION_PASSWD, /**< Zmiana hasła */
+ GG_SESSION_CHANGE, /**< Zmiana informacji w katalogu publicznym (nieaktualne) */
+ GG_SESSION_DCC, /**< Połączenie bezpośrednie (do wersji 6.x) */
+ GG_SESSION_DCC_SOCKET, /**< Gniazdo nasłuchujące (do wersji 6.x) */
+ GG_SESSION_DCC_SEND, /**< Wysyłanie pliku (do wersji 6.x) */
+ GG_SESSION_DCC_GET, /**< Odbieranie pliku (do wersji 6.x) */
+ GG_SESSION_DCC_VOICE, /**< Rozmowa głosowa (do wersji 6.x) */
+ GG_SESSION_USERLIST_GET, /**< Import listy kontaktów z serwera (nieaktualne) */
+ GG_SESSION_USERLIST_PUT, /**< Eksport listy kontaktów do serwera (nieaktualne) */
+ GG_SESSION_UNREGISTER, /**< Usuwanie konta */
+ GG_SESSION_USERLIST_REMOVE, /**< Usuwanie listy kontaktów z serwera (nieaktualne) */
+ GG_SESSION_TOKEN, /**< Pobieranie tokenu */
+ GG_SESSION_DCC7_SOCKET, /**< Gniazdo nasłuchujące (od wersji 7.x) */
+ GG_SESSION_DCC7_SEND, /**< Wysyłanie pliku (od wersji 7.x) */
+ GG_SESSION_DCC7_GET, /**< Odbieranie pliku (od wersji 7.x) */
+ GG_SESSION_DCC7_VOICE, /**< Rozmowa głosowa (od wersji 7.x) */
+
+ GG_SESSION_USER0 = 256, /**< Rodzaj zadeklarowany dla użytkownika */
+ GG_SESSION_USER1, /**< Rodzaj zadeklarowany dla użytkownika */
+ GG_SESSION_USER2, /**< Rodzaj zadeklarowany dla użytkownika */
+ GG_SESSION_USER3, /**< Rodzaj zadeklarowany dla użytkownika */
+ GG_SESSION_USER4, /**< Rodzaj zadeklarowany dla użytkownika */
+ GG_SESSION_USER5, /**< Rodzaj zadeklarowany dla użytkownika */
+ GG_SESSION_USER6, /**< Rodzaj zadeklarowany dla użytkownika */
+ GG_SESSION_USER7 /**< Rodzaj zadeklarowany dla użytkownika */
};
-/*
- * enum gg_state_t
- *
- * opisuje stan asynchronicznej maszyny.
+/**
+ * Aktualny stan sesji.
*/
enum gg_state_t {
- /* wsplne */
- GG_STATE_IDLE = 0, /* nie powinno wystpi. */
- GG_STATE_RESOLVING, /* wywoa gethostbyname() */
- GG_STATE_CONNECTING, /* wywoa connect() */
- GG_STATE_READING_DATA, /* czeka na dane http */
- GG_STATE_ERROR, /* wystpi bd. kod w x->error */
-
- /* gg_session */
- GG_STATE_CONNECTING_HUB, /* wywoa connect() na huba */
- GG_STATE_CONNECTING_GG, /* wywoa connect() na serwer */
- GG_STATE_READING_KEY, /* czeka na klucz */
- GG_STATE_READING_REPLY, /* czeka na odpowied */
- GG_STATE_CONNECTED, /* poczy si */
-
- /* gg_http */
- GG_STATE_SENDING_QUERY, /* wysya zapytanie http */
- GG_STATE_READING_HEADER, /* czeka na nagwek http */
- GG_STATE_PARSING, /* przetwarza dane */
- GG_STATE_DONE, /* skoczy */
-
- /* gg_dcc */
- GG_STATE_LISTENING, /* czeka na poczenia */
+ /* wspólne */
+ GG_STATE_IDLE = 0, /**< Nie dzieje się nic */
+ GG_STATE_RESOLVING, /**< Oczekiwanie na rozwiązanie nazwy serwera */
+ GG_STATE_CONNECTING, /**< Oczekiwanie na połączenie */
+ GG_STATE_READING_DATA, /**< Oczekiwanie na dane */
+ GG_STATE_ERROR, /**< Kod błędu w polu \c error */
+
+ /* gg_session */
+ GG_STATE_CONNECTING_HUB, /**< Oczekiwanie na połączenie z hubem */
+ GG_STATE_CONNECTING_GG, /**< Oczekiwanie na połączenie z serwerem */
+ GG_STATE_READING_KEY, /**< Oczekiwanie na klucz */
+ GG_STATE_READING_REPLY, /**< Oczekiwanie na odpowiedź serwera */
+ GG_STATE_CONNECTED, /**< Połączono z serwerem */
+
+ /* gg_http */
+ GG_STATE_SENDING_QUERY, /**< Wysłano zapytanie HTTP */
+ GG_STATE_READING_HEADER, /**< Oczekiwanie na nagłówek HTTP */
+ GG_STATE_PARSING, /**< Przetwarzanie danych */
+ GG_STATE_DONE, /**< Połączenie zakończone */
+
+ /* gg_dcc */
+ GG_STATE_LISTENING, /* czeka na połączenia */
GG_STATE_READING_UIN_1, /* czeka na uin peera */
- GG_STATE_READING_UIN_2, /* czeka na swj uin */
- GG_STATE_SENDING_ACK, /* wysya potwierdzenie dcc */
+ GG_STATE_READING_UIN_2, /* czeka na swój uin */
+ GG_STATE_SENDING_ACK, /* wysyła potwierdzenie dcc */
GG_STATE_READING_ACK, /* czeka na potwierdzenie dcc */
- GG_STATE_READING_REQUEST, /* czeka na komend */
- GG_STATE_SENDING_REQUEST, /* wysya komend */
- GG_STATE_SENDING_FILE_INFO, /* wysya informacje o pliku */
+ GG_STATE_READING_REQUEST, /* czeka na komendę */
+ GG_STATE_SENDING_REQUEST, /* wysyła komendę */
+ GG_STATE_SENDING_FILE_INFO, /* wysyła informacje o pliku */
GG_STATE_READING_PRE_FILE_INFO, /* czeka na pakiet przed file_info */
GG_STATE_READING_FILE_INFO, /* czeka na informacje o pliku */
- GG_STATE_SENDING_FILE_ACK, /* wysya potwierdzenie pliku */
+ GG_STATE_SENDING_FILE_ACK, /* wysyła potwierdzenie pliku */
GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */
- GG_STATE_SENDING_FILE_HEADER, /* wysya nagwek pliku */
- GG_STATE_READING_FILE_HEADER, /* czeka na nagwek */
+ GG_STATE_SENDING_FILE_HEADER, /* wysyła nagłówek pliku */
+ GG_STATE_READING_FILE_HEADER, /* czeka na nagłówek */
GG_STATE_GETTING_FILE, /* odbiera plik */
- GG_STATE_SENDING_FILE, /* wysya plik */
+ GG_STATE_SENDING_FILE, /* wysyła plik */
GG_STATE_READING_VOICE_ACK, /* czeka na potwierdzenie voip */
GG_STATE_READING_VOICE_HEADER, /* czeka na rodzaj bloku voip */
GG_STATE_READING_VOICE_SIZE, /* czeka na rozmiar bloku voip */
GG_STATE_READING_VOICE_DATA, /* czeka na dane voip */
- GG_STATE_SENDING_VOICE_ACK, /* wysya potwierdzenie voip */
- GG_STATE_SENDING_VOICE_REQUEST, /* wysya danie voip */
- GG_STATE_READING_TYPE, /* czeka na typ poczenia */
+ GG_STATE_SENDING_VOICE_ACK, /* wysyła potwierdzenie voip */
+ GG_STATE_SENDING_VOICE_REQUEST, /* wysyła żądanie voip */
+ GG_STATE_READING_TYPE, /* czeka na typ połączenia */
/* nowe. bez sensu jest to API. */
- GG_STATE_TLS_NEGOTIATION /* negocjuje poczenie TLS */
+ GG_STATE_TLS_NEGOTIATION, /**< Negocjacja połączenia szyfrowanego */
+
+ GG_STATE_REQUESTING_ID, /**< Oczekiwanie na nadanie identyfikatora połączenia bezpośredniego */
+ GG_STATE_WAITING_FOR_ACCEPT, /**< Oczekiwanie na potwierdzenie lub odrzucenie połączenia bezpośredniego */
+ GG_STATE_WAITING_FOR_INFO, /**< Oczekiwanie na informacje o połączeniu bezpośrednim */
+
+ GG_STATE_READING_ID, /**< Odebranie identyfikatora połączenia bezpośredniego */
+ GG_STATE_SENDING_ID /**< Wysłano identyfikatora połączenia bezpośredniego */
};
-/*
- * enum gg_check_t
+/**
+ * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać
+ * z deskryptora. Maska bitowa.
*
- * informuje, co proces klienta powinien sprawdzi na deskryptorze danego
- * poczenia.
+ * \ingroup events
*/
enum gg_check_t {
- GG_CHECK_NONE = 0, /* nic. nie powinno wystpi */
- GG_CHECK_WRITE = 1, /* sprawdzamy moliwo zapisu */
- GG_CHECK_READ = 2 /* sprawdzamy moliwo odczytu */
+ GG_CHECK_NONE = 0, /**< Nie sprawdzaj niczego */
+ GG_CHECK_WRITE = 1, /**< Sprawdź możliwość zapisu */
+ GG_CHECK_READ = 2 /**< Sprawdź możliwość odczytu */
};
-/*
- * struct gg_login_params
+/**
+ * Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione
+ * do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu
+ * kolejnych opcji połączenia. Część parametrów, które nie są już aktualne
+ * lub nie mają znaczenia, została usunięta z dokumentacji.
*
- * parametry gg_login(). przeniesiono do struktury, eby unikn problemw
- * z cigymi zmianami API, gdy dodano co nowego do protokou.
+ * \ingroup login
*/
struct gg_login_params {
- uin_t uin; /* numerek */
- char *password; /* haso */
- int async; /* asynchroniczne sockety? */
- int status; /* pocztkowy status klienta */
- char *status_descr; /* opis statusu */
- uint32_t server_addr; /* adres serwera gg */
- uint16_t server_port; /* port serwera gg */
- uint32_t client_addr; /* adres dcc klienta */
- uint16_t client_port; /* port dcc klienta */
- int protocol_version; /* wersja protokou */
- char *client_version; /* wersja klienta */
- int has_audio; /* czy ma dwik? */
- int last_sysmsg; /* ostatnia wiadomo systemowa */
- uint32_t external_addr; /* adres widziany na zewnatrz */
- uint16_t external_port; /* port widziany na zewnatrz */
- int tls; /* czy czymy po TLS? */
- int image_size; /* maksymalny rozmiar obrazka w KiB */
- int era_omnix; /* czy udawa klienta era omnix? */
-
- char dummy[6 * sizeof(int)]; /* miejsce na kolejnych 6 zmiennych,
- * eby z dodaniem parametru nie
- * zmienia si rozmiar struktury */
+ uin_t uin; /**< Numer Gadu-Gadu */
+ char *password; /**< Hasło */
+ int async; /**< Flaga asynchronicznego połączenia (domyślnie nie) */
+ int status; /**< Początkowy status użytkownika (domyślnie \c GG_STATUS_AVAIL) */
+ char *status_descr; /**< Początkowy opis użytkownika (domyślnie brak) */
+ uint32_t server_addr; /**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
+ uint16_t server_port; /**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
+#ifndef DOXYGEN
+ uint32_t client_addr; /**< Adres połączeń bezpośrednich (nieaktualne) */
+ uint16_t client_port; /**< Port połączeń bezpośrednich (nieaktualne) */
+#endif
+ int protocol_version; /**< Wersja protokołu wysyłana do serwera (domyślnie najnowsza obsługiwana) */
+ char *client_version; /**< Wersja klienta wysyłana do serwera (domyślnie najnowsza znana) */
+ int has_audio; /**< Flaga obsługi połączeń głosowych */
+ int last_sysmsg; /**< Numer ostatnio odebranej wiadomości systemowej */
+ uint32_t external_addr; /**< Adres publiczny dla połączeń bezpośrednich (6.x) */
+ uint16_t external_port; /**< Port publiczny dla połączeń bezpośrednich (6.x) */
+#ifndef DOXYGEN
+ int tls; /**< Flaga połączenia szyfrowanego (nieaktualna) */
+#endif
+ int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w kilobajtach */
+#ifndef DOXYGEN
+ int era_omnix; /**< Flaga udawania klienta Era Omnix (nieaktualna) */
+#endif
+ int hash_type; /**< Rodzaj skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyślnie SHA1) */
+ gg_encoding_t encoding; /**< Rodzaj kodowania używanego w sesji (domyślnie CP1250) */
+ gg_resolver_t resolver; /**< Sposób rozwiązywania nazw (patrz \ref build-resolver) */
+ int protocol_features; /**< Opcje protokołu (flagi GG_FEATURE_*). */
+
+#ifndef DOXYGEN
+ char dummy[2 * sizeof(int)]; /**< \internal Miejsce na kilka kolejnych
+ parametrów, żeby wraz z dodawaniem kolejnych
+ parametrów nie zmieniał się rozmiar struktury */
+#endif
+
};
struct gg_session *gg_login(const struct gg_login_params *p);
@@ -367,309 +597,349 @@ int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient,
int gg_ping(struct gg_session *sess);
int gg_userlist_request(struct gg_session *sess, char type, const char *request);
int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
-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);
uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
-struct gg_image_queue {
- uin_t sender; /* nadawca obrazka */
- uint32_t size; /* rozmiar */
- uint32_t crc32; /* suma kontrolna */
- char *filename; /* nazwa pliku */
- char *image; /* bufor z obrazem */
- uint32_t done; /* ile ju wczytano */
-
- struct gg_image_queue *next; /* nastpny na licie */
-};
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type);
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs);
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
-/*
- * enum gg_event_t
+int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type);
+gg_resolver_t gg_http_get_resolver(struct gg_http *gh);
+int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
+int gg_global_set_resolver(gg_resolver_t type);
+gg_resolver_t gg_global_get_resolver(void);
+int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
+/**
+ * Rodzaj zdarzenia.
*
- * rodzaje zdarze.
+ * \ingroup events
*/
enum gg_event_t {
- GG_EVENT_NONE = 0, /* nic si nie wydarzyo */
- GG_EVENT_MSG, /* otrzymano wiadomo */
- GG_EVENT_NOTIFY, /* kto si pojawi */
- GG_EVENT_NOTIFY_DESCR, /* kto si pojawi z opisem */
- GG_EVENT_STATUS, /* kto zmieni stan */
- GG_EVENT_ACK, /* potwierdzenie wysania wiadomoci */
- GG_EVENT_PONG, /* pakiet pong */
- GG_EVENT_CONN_FAILED, /* poczenie si nie udao */
- GG_EVENT_CONN_SUCCESS, /* poczenie si powiodo */
- GG_EVENT_DISCONNECT, /* serwer zrywa poczenie */
-
- GG_EVENT_DCC_NEW, /* nowe poczenie midzy klientami */
- GG_EVENT_DCC_ERROR, /* bd poczenia midzy klientami */
- GG_EVENT_DCC_DONE, /* zakoczono poczenie */
- GG_EVENT_DCC_CLIENT_ACCEPT, /* moment akceptacji klienta */
- GG_EVENT_DCC_CALLBACK, /* klient si poczy na danie */
- GG_EVENT_DCC_NEED_FILE_INFO, /* naley wypeni file_info */
- GG_EVENT_DCC_NEED_FILE_ACK, /* czeka na potwierdzenie pliku */
- GG_EVENT_DCC_NEED_VOICE_ACK, /* czeka na potwierdzenie rozmowy */
- GG_EVENT_DCC_VOICE_DATA, /* ramka danych rozmowy gosowej */
-
- GG_EVENT_PUBDIR50_SEARCH_REPLY, /* odpowiedz wyszukiwania */
- GG_EVENT_PUBDIR50_READ, /* odczytano wasne dane z katalogu */
- GG_EVENT_PUBDIR50_WRITE, /* wpisano wasne dane do katalogu */
-
- GG_EVENT_STATUS60, /* kto zmieni stan w GG 6.0 */
- GG_EVENT_NOTIFY60, /* kto si pojawi w GG 6.0 */
- GG_EVENT_USERLIST, /* odpowied listy kontaktw w GG 6.0 */
- GG_EVENT_IMAGE_REQUEST, /* proba o wysanie obrazka GG 6.0 */
- GG_EVENT_IMAGE_REPLY, /* podesany obrazek GG 6.0 */
- GG_EVENT_DCC_ACK /* potwierdzenie transmisji */
+ GG_EVENT_NONE = 0, /**< Nie wydarzyło się nic wartego uwagi */
+ GG_EVENT_MSG, /**< \brief Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. */
+ GG_EVENT_NOTIFY, /**< \brief Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+ GG_EVENT_NOTIFY_DESCR, /**< \brief Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+ GG_EVENT_STATUS, /**< \brief Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+ GG_EVENT_ACK, /**< Potwierdzenie doręczenia wiadomości */
+ GG_EVENT_PONG, /**< \brief Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. */
+ GG_EVENT_CONN_FAILED, /**< \brief Nie udało się połączyć */
+ GG_EVENT_CONN_SUCCESS, /**< \brief Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. */
+ GG_EVENT_DISCONNECT, /**< \brief Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. */
+
+ GG_EVENT_DCC_NEW, /**< Nowe połączenie bezpośrednie (6.x) */
+ GG_EVENT_DCC_ERROR, /**< Błąd połączenia bezpośredniego (6.x) */
+ GG_EVENT_DCC_DONE, /**< Zakończono połączenie bezpośrednie (6.x) */
+ GG_EVENT_DCC_CLIENT_ACCEPT, /**< Moment akceptacji klienta w połączeniu bezpośrednim (6.x) */
+ GG_EVENT_DCC_CALLBACK, /**< Zwrotne połączenie bezpośrednie (6.x) */
+ GG_EVENT_DCC_NEED_FILE_INFO, /**< Należy wypełnić \c file_info dla połączenia bezpośredniego (6.x) */
+ GG_EVENT_DCC_NEED_FILE_ACK, /**< Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x) */
+ GG_EVENT_DCC_NEED_VOICE_ACK, /**< Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x) */
+ GG_EVENT_DCC_VOICE_DATA, /**< Dane bezpośredniego połączenia głosowego (6.x) */
+
+ GG_EVENT_PUBDIR50_SEARCH_REPLY, /**< Odpowiedź katalogu publicznego */
+ GG_EVENT_PUBDIR50_READ, /**< Odczytano własne dane z katalogu publicznego */
+ GG_EVENT_PUBDIR50_WRITE, /**< Zmieniono własne dane w katalogu publicznym */
+
+ GG_EVENT_STATUS60, /**< Zmiana statusu osoby z listy kontaktów */
+ GG_EVENT_NOTIFY60, /**< Informacja o statusach osób z listy kontaktów */
+ GG_EVENT_USERLIST, /**< Wynik importu lub eksportu listy kontaktów */
+ GG_EVENT_IMAGE_REQUEST, /**< Żądanie przesłania obrazka z wiadommości */
+ GG_EVENT_IMAGE_REPLY, /**< Przysłano obrazek z wiadomości */
+ GG_EVENT_DCC_ACK, /**< Potwierdzenie transmisji w połączeniu bezpośrednim (6.x) */
+
+ GG_EVENT_DCC7_NEW, /**< Nowe połączenie bezpośrednie (7.x) */
+ GG_EVENT_DCC7_ACCEPT, /**< Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor */
+ GG_EVENT_DCC7_REJECT, /**< Odrzucono połączenie bezpośrednie (7.x) */
+ GG_EVENT_DCC7_CONNECTED, /**< Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor */
+ GG_EVENT_DCC7_ERROR, /**< Błąd połączenia bezpośredniego (7.x) */
+ GG_EVENT_DCC7_DONE, /**< Zakończono połączenie bezpośrednie (7.x) */
+ GG_EVENT_DCC7_PENDING, /**< Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor */
+
+ GG_EVENT_XML_EVENT, /**< Otrzymano komunikat systemowy (7.7) */
+ GG_EVENT_DISCONNECT_ACK, /**< \brief Potwierdzenie zakończenia sesji. Informuje o tym, że zmiana stanu na niedostępny z opisem dotarła do serwera i można zakończyć połączenie TCP. */
};
#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
-/*
- * enum gg_failure_t
- *
- * okrela powd nieudanego poczenia.
+/**
+ * Powód nieudanego połączenia.
*/
enum gg_failure_t {
- GG_FAILURE_RESOLVING = 1, /* nie znaleziono serwera */
- GG_FAILURE_CONNECTING, /* nie mona si poczy */
- GG_FAILURE_INVALID, /* serwer zwrci nieprawidowe dane */
- GG_FAILURE_READING, /* zerwano poczenie podczas odczytu */
- GG_FAILURE_WRITING, /* zerwano poczenie podczas zapisu */
- GG_FAILURE_PASSWORD, /* nieprawidowe haso */
- GG_FAILURE_404, /* XXX nieuywane */
- GG_FAILURE_TLS, /* bd negocjacji TLS */
- GG_FAILURE_NEED_EMAIL /* serwer rozczy nas z prob o zmian emaila */
+ GG_FAILURE_RESOLVING = 1, /**< Nie znaleziono serwera */
+ GG_FAILURE_CONNECTING, /**< Błąd połączenia */
+ GG_FAILURE_INVALID, /**< Serwer zwrócił nieprawidłowe dane */
+ GG_FAILURE_READING, /**< Zerwano połączenie podczas odczytu */
+ GG_FAILURE_WRITING, /**< Zerwano połączenie podczas zapisu */
+ GG_FAILURE_PASSWORD, /**< Nieprawidłowe hasło */
+ GG_FAILURE_404, /**< Nieużywane */
+ GG_FAILURE_TLS, /**< Błąd negocjacji szyfrowanego połączenia */
+ GG_FAILURE_NEED_EMAIL, /**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */
+ GG_FAILURE_INTRUDER, /**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */
+ GG_FAILURE_UNAVAILABLE /**< Serwery są wyłączone */
};
-/*
- * enum gg_error_t
+/**
+ * Kod błędu danej operacji.
*
- * okrela rodzaj bdu wywoanego przez dan operacj. nie zawiera
- * przesadnie szczegowych informacji o powodzie bdu, by nie komplikowa
- * obsugi bdw. jeli wymagana jest wiksza dokadno, naley sprawdzi
- * zawarto zmiennej errno.
+ * Nie zawiera przesadnie szczegółowych informacji o powodach błędów, by nie
+ * komplikować ich obsługi. Jeśli wymagana jest większa dokładność, należy
+ * sprawdzić zawartość zmiennej systemowej \c errno.
*/
enum gg_error_t {
- GG_ERROR_RESOLVING = 1, /* bd znajdowania hosta */
- GG_ERROR_CONNECTING, /* bd aczenia si */
- GG_ERROR_READING, /* bd odczytu */
- GG_ERROR_WRITING, /* bd wysyania */
-
- GG_ERROR_DCC_HANDSHAKE, /* bd negocjacji */
- GG_ERROR_DCC_FILE, /* bd odczytu/zapisu pliku */
- GG_ERROR_DCC_EOF, /* plik si skoczy? */
- GG_ERROR_DCC_NET, /* bd wysyania/odbierania */
- GG_ERROR_DCC_REFUSED /* poczenie odrzucone przez usera */
+ GG_ERROR_RESOLVING = 1, /**< Nie znaleziono hosta */
+ GG_ERROR_CONNECTING, /**< Błąd połączenia */
+ GG_ERROR_READING, /**< Błąd odczytu/odbierania */
+ GG_ERROR_WRITING, /**< Błąd zapisu/wysyłania */
+
+ GG_ERROR_DCC_HANDSHAKE, /**< Błąd negocjacji */
+ GG_ERROR_DCC_FILE, /**< Błąd odczytu/zapisu pliku */
+ GG_ERROR_DCC_EOF, /**< Przedwczesny koniec pliku */
+ GG_ERROR_DCC_NET, /**< Błąd wysyłania/odbierania */
+ GG_ERROR_DCC_REFUSED, /**< Połączenie odrzucone */
+
+ GG_ERROR_DCC7_HANDSHAKE, /**< Błąd negocjacji */
+ GG_ERROR_DCC7_FILE, /**< Błąd odczytu/zapisu pliku */
+ GG_ERROR_DCC7_EOF, /**< Przedwczesny koniec pliku */
+ GG_ERROR_DCC7_NET, /**< Błąd wysyłania/odbierania */
+ GG_ERROR_DCC7_REFUSED /**< Połączenie odrzucone */
};
-/*
- * struktury dotyczce wyszukiwania w GG 5.0. NIE NALEY SI DO NICH
- * ODWOYWA BEZPOREDNIO! do dostpu do nich su funkcje gg_pubdir50_*()
+/**
+ * Pole zapytania lub odpowiedzi katalogu publicznego.
*/
struct gg_pubdir50_entry {
- int num;
- char *field;
- char *value;
-};
+ int num; /**< Numer wyniku */
+ char *field; /**< Nazwa pola */
+ char *value; /**< Wartość pola */
+} /* GG_DEPRECATED */;
+/**
+ * Zapytanie lub odpowiedź katalogu publicznego.
+ *
+ * Patrz \c gg_pubdir50_t.
+ */
struct gg_pubdir50_s {
- int count;
- uin_t next;
- int type;
- uint32_t seq;
- struct gg_pubdir50_entry *entries;
- int entries_count;
-};
-
-/*
- * typedef gg_pubdir_50_t
+ int count; /**< Liczba wyników odpowiedzi */
+ uin_t next; /**< Numer początkowy następnego zapytania */
+ int type; /**< Rodzaj zapytania */
+ uint32_t seq; /**< Numer sekwencyjny */
+ struct gg_pubdir50_entry *entries; /**< Pola zapytania lub odpowiedzi */
+ int entries_count; /**< Liczba pól */
+} /* GG_DEPRECATED */;
+
+/**
+ * Zapytanie lub odpowiedź katalogu publicznego.
*
- * typ opisujcy zapytanie lub wynik zapytania katalogu publicznego
- * z protokou GG 5.0. nie naley si odwoywa bezporednio do jego
- * pl -- su do tego funkcje gg_pubdir50_*()
+ * Do pól nie należy się odwoływać bezpośrednio -- wszystkie niezbędne
+ * informacje są dostępne za pomocą funkcji \c gg_pubdir50_*
*/
typedef struct gg_pubdir50_s *gg_pubdir50_t;
-/*
- * struct gg_event
- *
- * struktura opisujca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub
- * z gg_dcc_watch_fd()
+/**
+ * Opis zdarzenia \c GG_EVENT_MSG.
*/
-struct gg_event {
- int type; /* rodzaj zdarzenia -- gg_event_t */
- union { /* @event */
- struct gg_notify_reply *notify; /* informacje o licie kontaktw -- GG_EVENT_NOTIFY */
-
- enum gg_failure_t failure; /* bd poczenia -- GG_EVENT_FAILURE */
-
- struct gg_dcc *dcc_new; /* nowe poczenie bezporednie -- GG_EVENT_DCC_NEW */
-
- int dcc_error; /* bd poczenia bezporedniego -- GG_EVENT_DCC_ERROR */
-
- gg_pubdir50_t pubdir50; /* wynik operacji zwizanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */
-
- struct { /* @msg odebrano wiadomo -- GG_EVENT_MSG */
- uin_t sender; /* numer nadawcy */
- int msgclass; /* klasa wiadomoci */
- time_t time; /* czas nadania */
- unsigned char *message; /* tre wiadomoci */
-
- int recipients_count; /* ilo odbiorcw konferencji */
- uin_t *recipients; /* odbiorcy konferencji */
-
- int formats_length; /* dugo informacji o formatowaniu tekstu */
- void *formats; /* informacje o formatowaniu tekstu */
- } msg;
-
- struct { /* @notify_descr informacje o licie kontaktw z opisami stanu -- GG_EVENT_NOTIFY_DESCR */
- struct gg_notify_reply *notify; /* informacje o licie kontaktw */
- char *descr; /* opis stanu */
- } notify_descr;
-
- struct { /* @status zmiana stanu -- GG_EVENT_STATUS */
- uin_t uin; /* numer */
- uint32_t status; /* nowy stan */
- char *descr; /* opis stanu */
- } status;
-
- struct { /* @status60 zmiana stanu -- GG_EVENT_STATUS60 */
- uin_t uin; /* numer */
- int status; /* nowy stan */
- uint32_t remote_ip; /* adres ip */
- uint16_t remote_port; /* port */
- int version; /* wersja klienta */
- int image_size; /* maksymalny rozmiar grafiki w KiB */
- char *descr; /* opis stanu */
- time_t time; /* czas powrotu */
- } status60;
-
- struct { /* @notify60 informacja o licie kontaktw -- GG_EVENT_NOTIFY60 */
- uin_t uin; /* numer */
- int status; /* stan */
- uint32_t remote_ip; /* adres ip */
- uint16_t remote_port; /* port */
- int version; /* wersja klienta */
- int image_size; /* maksymalny rozmiar grafiki w KiB */
- char *descr; /* opis stanu */
- time_t time; /* czas powrotu */
- } *notify60;
-
- struct { /* @ack potwierdzenie wiadomoci -- GG_EVENT_ACK */
- uin_t recipient; /* numer odbiorcy */
- int status; /* stan dorczenia wiadomoci */
- int seq; /* numer sekwencyjny wiadomoci */
- } ack;
-
- struct { /* @dcc_voice_data otrzymano dane dwikowe -- GG_EVENT_DCC_VOICE_DATA */
- uint8_t *data; /* dane dwikowe */
- int length; /* ilo danych dwikowych */
- } dcc_voice_data;
-
- struct { /* @userlist odpowied listy kontaktw serwera */
- char type; /* rodzaj odpowiedzi */
- char *reply; /* tre odpowiedzi */
- } userlist;
-
- struct { /* @image_request proba o obrazek */
- uin_t sender; /* nadawca proby */
- uint32_t size; /* rozmiar obrazka */
- uint32_t crc32; /* suma kontrolna */
- } image_request;
-
- struct { /* @image_reply odpowied z obrazkiem */
- uin_t sender; /* nadawca odpowiedzi */
- uint32_t size; /* rozmiar obrazka */
- uint32_t crc32; /* suma kontrolna */
- char *filename; /* nazwa pliku */
- char *image; /* bufor z obrazkiem */
- } image_reply;
- } event;
+struct gg_event_msg {
+ uin_t sender; /**< Numer nadawcy */
+ int msgclass; /**< Klasa wiadomości */
+ time_t time; /**< Czas nadania */
+ unsigned char *message; /**< Treść wiadomości */
+
+ int recipients_count; /**< Liczba odbiorców konferencji */
+ uin_t *recipients; /**< Odbiorcy konferencji */
+
+ int formats_length; /**< Długość informacji o formatowaniu tekstu */
+ void *formats; /**< Informacje o formatowaniu tekstu */
+ uint32_t seq; /**< Numer sekwencyjny wiadomości */
+
+ char *xhtml_message; /**< Treść wiadomości w formacie XHTML (może być równe \c NULL, jeśli wiadomość nie zawiera treści XHTML) */
};
-struct gg_event *gg_watch_fd(struct gg_session *sess);
-void gg_event_free(struct gg_event *e);
-#define gg_free_event gg_event_free
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR.
+ */
+struct gg_event_notify_descr {
+ struct gg_notify_reply *notify; /**< Informacje o liście kontaktów */
+ char *descr; /**< Opis status */
+};
-/*
- * funkcje obsugi listy kontaktw.
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS.
*/
-int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
-int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
-int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
-int gg_add_notify(struct gg_session *sess, uin_t uin);
-int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
-int gg_remove_notify(struct gg_session *sess, uin_t uin);
+struct gg_event_status {
+ uin_t uin; /**< Numer Gadu-Gadu */
+ uint32_t status; /**< Nowy status */
+ char *descr; /**< Opis */
+};
-/*
- * funkcje obsugi http.
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS60.
*/
-struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
-int gg_http_watch_fd(struct gg_http *h);
-void gg_http_stop(struct gg_http *h);
-void gg_http_free(struct gg_http *h);
-void gg_http_free_fields(struct gg_http *h);
-#define gg_free_http gg_http_free
+struct gg_event_status60 {
+ uin_t uin; /**< Numer Gadu-Gadu */
+ int status; /**< Nowy status */
+ uint32_t remote_ip; /**< Adres IP dla połączeń bezpośrednich */
+ uint16_t remote_port; /**< Port dla połączeń bezpośrednich */
+ int version; /**< Wersja protokołu */
+ int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+ char *descr; /**< Opis statusu */
+ time_t time; /**< Czas powrotu */
+};
-/*
- * struktury opisujca kryteria wyszukiwania dla gg_search(). nieaktualne,
- * zastpione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI.
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60.
*/
-struct gg_search_request {
- int active;
- unsigned int start;
- char *nickname;
- char *first_name;
- char *last_name;
- char *city;
- int gender;
- int min_birth;
- int max_birth;
- char *email;
- char *phone;
- uin_t uin;
+struct gg_event_notify60 {
+ uin_t uin; /**< Numer Gadu-Gadu */
+ int status; /**< Nowy status */
+ uint32_t remote_ip; /**< Adres IP dla połączeń bezpośrednich */
+ uint16_t remote_port; /**< Port dla połączeń bezpośrednich */
+ int version; /**< Wersja protokołu */
+ int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+ char *descr; /**< Opis statusu */
+ time_t time; /**< Czas powrotu */
};
-struct gg_search {
- int count;
- struct gg_search_result *results;
+/**
+ * Opis zdarzenia \c GG_EVENT_ACK.
+ */
+struct gg_event_ack {
+ uin_t recipient; /**< Numer odbiorcy */
+ int status; /**< Status doręczenia */
+ int seq; /**< Numer sekwencyjny wiadomości */
};
-struct gg_search_result {
- uin_t uin;
- char *first_name;
- char *last_name;
- char *nickname;
- int born;
- int gender;
- char *city;
- int active;
+/**
+ * Opis zdarzenia \c GG_EVENT_USERLIST.
+ */
+struct gg_event_userlist {
+ char type; /**< Rodzaj odpowiedzi */
+ char *reply; /**< Treść odpowiedzi */
};
-#define GG_GENDER_NONE 0
-#define GG_GENDER_FEMALE 1
-#define GG_GENDER_MALE 2
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC_VOICE_DATA.
+ */
+struct gg_event_dcc_voice_data {
+ uint8_t *data; /**< Dane dźwiękowe */
+ int length; /**< Rozmiar danych dźwiękowych */
+};
-/*
- * funkcje wyszukiwania.
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST.
*/
-struct gg_http *gg_search(const struct gg_search_request *r, int async);
-int gg_search_watch_fd(struct gg_http *f);
-void gg_free_search(struct gg_http *f);
-#define gg_search_free gg_free_search
+struct gg_event_image_request {
+ uin_t sender; /**< Nadawca żądania */
+ uint32_t size; /**< Rozmiar obrazka */
+ uint32_t crc32; /**< Suma kontrolna CRC32 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REPLY.
+ */
+struct gg_event_image_reply {
+ uin_t sender; /**< Nadawca obrazka */
+ uint32_t size; /**< Rozmiar obrazka */
+ uint32_t crc32; /**< Suma kontrolna CRC32 */
+ char *filename; /**< Nazwa pliku */
+ char *image; /**< Bufor z obrazkiem */
+};
-const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start);
-const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start);
-const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start);
-const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start);
-void gg_search_request_free(struct gg_search_request *r);
+/**
+ * Opis zdarzenia \c GG_EVENT_XML_EVENT.
+ */
+struct gg_event_xml_event {
+ char *data; /**< Bufor z komunikatem */
+};
-/*
- * funkcje obsugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje
- * zachowuj pewien poziom abstrakcji, eby unikn zmian ABI przy zmianach
- * w protokole.
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
+ */
+struct gg_event_dcc7_connected {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+ // XXX czy coś się przyda?
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_REJECT.
+ */
+struct gg_event_dcc7_reject {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+ int reason; /**< powód odrzucenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT.
+ */
+struct gg_event_dcc7_accept {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+ int type; /**< Sposób połączenia (P2P, przez serwer) */
+ uint32_t remote_ip; /**< Adres zdalnego klienta */
+ uint16_t remote_port; /**< Port zdalnego klienta */
+};
+
+/**
+ * Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(),
+ * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd().
+ *
+ * \ingroup events
+ */
+union gg_event_union {
+ enum gg_failure_t failure; /**< Błąd połączenia (\c GG_EVENT_CONN_FAILED) */
+ struct gg_notify_reply *notify; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY) */
+ struct gg_event_notify_descr notify_descr; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY_DESCR) */
+ struct gg_event_status status; /**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS) */
+ struct gg_event_status60 status60; /**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS60) */
+ struct gg_event_notify60 *notify60; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY60) */
+ struct gg_event_msg msg; /**< Otrzymano wiadomość (\c GG_EVENT_MSG) */
+ struct gg_event_ack ack; /**< Potwierdzenie wiadomości (\c GG_EVENT_ACK) */
+ struct gg_event_image_request image_request; /**< Żądanie wysłania obrazka (\c GG_EVENT_IMAGE_REQUEST) */
+ struct gg_event_image_reply image_reply; /**< Odpowiedź z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */
+ struct gg_event_userlist userlist; /**< Odpowiedź listy kontaktów (\c GG_EVENT_USERLIST) */
+ gg_pubdir50_t pubdir50; /**< Odpowiedź katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */
+ struct gg_event_xml_event xml_event; /**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */
+ struct gg_dcc *dcc_new; /**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC_NEW) */
+ enum gg_error_t dcc_error; /**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC_ERROR) */
+ struct gg_event_dcc_voice_data dcc_voice_data; /**< Dane połączenia głosowego (\c GG_EVENT_DCC_VOICE_DATA) */
+ struct gg_dcc7 *dcc7_new; /**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC7_NEW) */
+ enum gg_error_t dcc7_error; /**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC7_ERROR) */
+ struct gg_event_dcc7_connected dcc7_connected; /**< Informacja o zestawieniu połączenia bezpośredniego (\c GG_EVENT_DCC7_CONNECTED) */
+ struct gg_event_dcc7_reject dcc7_reject; /**< Odrzucono połączenia bezpośredniego (\c GG_EVENT_DCC7_REJECT) */
+ struct gg_event_dcc7_accept dcc7_accept; /**< Zaakceptowano połączenie bezpośrednie (\c GG_EVENT_DCC7_ACCEPT) */
+};
+
+/**
+ * Opis zdarzenia.
+ *
+ * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd()
+ * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu należy zwolnić
+ * za pomocą \c gg_event_free().
*
- * NIE NALEY SI ODWOYWA DO PL gg_pubdir50_t BEZPOREDNIO!
+ * \ingroup events
*/
+struct gg_event {
+ int type; /**< Rodzaj zdarzenia */
+ union gg_event_union event; /**< Informacja o zdarzeniu */
+};
+
+struct gg_event *gg_watch_fd(struct gg_session *sess);
+void gg_event_free(struct gg_event *e);
+
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
+int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
+int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
+int gg_add_notify(struct gg_session *sess, uin_t uin);
+int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
+int gg_remove_notify(struct gg_session *sess, uin_t uin);
+
+struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
+int gg_http_watch_fd(struct gg_http *h);
+void gg_http_stop(struct gg_http *h);
+void gg_http_free(struct gg_http *h);
+
uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req);
gg_pubdir50_t gg_pubdir50_new(int type);
int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value);
@@ -681,6 +951,8 @@ uin_t gg_pubdir50_next(gg_pubdir50_t res);
uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
void gg_pubdir50_free(gg_pubdir50_t res);
+#ifndef DOXYGEN
+
#define GG_PUBDIR50_UIN "FmNumber"
#define GG_PUBDIR50_STATUS "FmStatus"
#define GG_PUBDIR50_FIRSTNAME "firstname"
@@ -699,110 +971,114 @@ void gg_pubdir50_free(gg_pubdir50_t res);
#define GG_PUBDIR50_FAMILYNAME "familyname"
#define GG_PUBDIR50_FAMILYCITY "familycity"
-int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length);
+#else
-/*
- * struct gg_pubdir
+/**
+ * \ingroup pubdir50
+ *
+ * Rodzaj pola zapytania.
+ */
+enum {
+ GG_PUBDIR50_UIN, /**< Numer Gadu-Gadu */
+ GG_PUBDIR50_STATUS, /**< Status (tylko wynik wyszukiwania) */
+ GG_PUBDIR50_FIRSTNAME, /**< Imię */
+ GG_PUBDIR50_LASTNAME, /**< Nazwisko */
+ GG_PUBDIR50_NICKNAME, /**< Pseudonim */
+ GG_PUBDIR50_BIRTHYEAR, /**< Rok urodzenia lub przedział lat oddzielony spacją */
+ GG_PUBDIR50_CITY, /**< Miejscowość */
+ GG_PUBDIR50_GENDER, /**< Płeć */
+ GG_PUBDIR50_ACTIVE, /**< Osoba dostępna (tylko wyszukiwanie) */
+ GG_PUBDIR50_START, /**< Numer początkowy wyszukiwania (tylko wyszukiwanie) */
+ GG_PUBDIR50_FAMILYNAME, /**< Nazwisko rodowe (tylko wysyłanie informacji o sobie) */
+ GG_PUBDIR50_FAMILYCITY, /**< Miejscowość pochodzenia (tylko wysyłanie informacji o sobie) */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolną płeć.
+ */
+enum {
+ GG_PUBDIR50_GENDER_FEMALE, /**< Kobieta */
+ GG_PUBDIR50_GENDER_MALE, /**< Mężczyzna */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_GENDER przy wysyłaniu informacji o sobie.
+ */
+enum {
+ GG_PUBDIR50_GENDER_SET_FEMALE, /**< Kobieta */
+ GG_PUBDIR50_GENDER_SET_MALE, /**< Mężczyzna */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_ACTIVE.
+ */
+enum {
+ GG_PUBDIR50_ACTIVE_TRUE, /**< Wyszukaj tylko osoby dostępne */
+};
+
+#endif /* DOXYGEN */
+
+/**
+ * Wynik operacji na katalogu publicznym.
*
- * operacje na katalogu publicznym.
+ * \ingroup http
*/
struct gg_pubdir {
- int success; /* czy si udao */
- uin_t uin; /* otrzymany numerek. 0 jeli bd */
+ int success; /**< Flaga powodzenia operacji */
+ uin_t uin; /**< Otrzymany numer lub 0 w przypadku błędu */
};
-/* oglne funkcje, nie powinny by uywane */
int gg_pubdir_watch_fd(struct gg_http *f);
void gg_pubdir_free(struct gg_http *f);
-#define gg_free_pubdir gg_pubdir_free
+/**
+ * Token autoryzacji niektórych operacji HTTP.
+ *
+ * \ingroup token
+ */
struct gg_token {
- int width; /* szeroko obrazka */
- int height; /* wysoko obrazka */
- int length; /* ilo znakw w tokenie */
- char *tokenid; /* id tokenu */
+ int width; /**< Szerokość obrazka */
+ int height; /**< Wysokość obrazka */
+ int length; /**< Liczba znaków w tokenie */
+ char *tokenid; /**< Identyfikator tokenu */
};
-/* funkcje dotyczce tokenw */
struct gg_http *gg_token(int async);
int gg_token_watch_fd(struct gg_http *h);
void gg_token_free(struct gg_http *h);
-/* rejestracja nowego numerka */
-struct gg_http *gg_register(const char *email, const char *password, int async);
-struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async);
struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
#define gg_register_watch_fd gg_pubdir_watch_fd
#define gg_register_free gg_pubdir_free
-#define gg_free_register gg_pubdir_free
+#endif
-struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async);
-struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async);
struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
#define gg_unregister_watch_fd gg_pubdir_watch_fd
#define gg_unregister_free gg_pubdir_free
+#endif
-/* przypomnienie hasa e-mailem */
-struct gg_http *gg_remind_passwd(uin_t uin, int async);
-struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async);
struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd
#define gg_remind_passwd_free gg_pubdir_free
-#define gg_free_remind_passwd gg_pubdir_free
+#endif
-/* zmiana hasa */
-struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async);
-struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async);
-struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async);
struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
+#define gg_change_passwd_watch_fd gg_pubdir_watch_fd
#define gg_change_passwd_free gg_pubdir_free
-#define gg_free_change_passwd gg_pubdir_free
-
-/*
- * struct gg_change_info_request
- *
- * opis dania zmiany informacji w katalogu publicznym.
- */
-struct gg_change_info_request {
- char *first_name; /* imi */
- char *last_name; /* nazwisko */
- char *nickname; /* pseudonim */
- char *email; /* email */
- int born; /* rok urodzenia */
- int gender; /* pe */
- char *city; /* miasto */
-};
-
-struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city);
-void gg_change_info_request_free(struct gg_change_info_request *r);
-
-struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async);
-#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
-#define gg_change_pubdir_free gg_pubdir_free
-#define gg_free_change_pubdir gg_pubdir_free
-
-/*
- * funkcje dotyczce listy kontaktw na serwerze.
- */
-struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async);
-int gg_userlist_get_watch_fd(struct gg_http *f);
-void gg_userlist_get_free(struct gg_http *f);
-
-struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async);
-int gg_userlist_put_watch_fd(struct gg_http *f);
-void gg_userlist_put_free(struct gg_http *f);
-
-struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async);
-int gg_userlist_remove_watch_fd(struct gg_http *f);
-void gg_userlist_remove_free(struct gg_http *f);
-
+#endif
-
-/*
- * funkcje dotyczce komunikacji midzy klientami.
- */
-extern int gg_dcc_port; /* port, na ktrym nasuchuje klient */
-extern unsigned long gg_dcc_ip; /* adres, na ktrym nasuchuje klient */
+extern int gg_dcc_port;
+extern unsigned long gg_dcc_ip;
int gg_dcc_request(struct gg_session *sess, uin_t uin);
@@ -814,119 +1090,248 @@ int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename);
int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename);
int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length);
-#define GG_DCC_VOICE_FRAME_LENGTH 195
-#define GG_DCC_VOICE_FRAME_LENGTH_505 326
+#define GG_DCC_VOICE_FRAME_LENGTH 195 /**< Rozmiar pakietu głosowego przed wersją Gadu-Gadu 5.0.5 */
+#define GG_DCC_VOICE_FRAME_LENGTH_505 326 /**< Rozmiar pakietu głosowego od wersji Gadu-Gadu 5.0.5 */
struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
-#define gg_dcc_socket_free gg_free_dcc
+#ifndef DOXYGEN
+#define gg_dcc_socket_free gg_dcc_free
#define gg_dcc_socket_watch_fd gg_dcc_watch_fd
+#endif
struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d);
void gg_dcc_free(struct gg_dcc *c);
-#define gg_free_dcc gg_dcc_free
-/*
- * jeli chcemy sobie podebugowa, wystarczy ustawi `gg_debug_level'.
- * niestety w miar przybywania wpisw `gg_debug(...)' nie chciao mi
- * si ustawia odpowiednich leveli, wic wikszo sza do _MISC.
- */
-extern int gg_debug_level; /* poziom debugowania. mapa bitowa staych GG_DEBUG_* */
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash);
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash);
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);
+void gg_dcc7_free(struct gg_dcc7 *d);
+
+extern int gg_debug_level;
-/*
- * mona poda wskanik do funkcji obsugujcej wywoania gg_debug().
- * nieoficjalne, nieudokumentowane, moe si zmieni. jeli kto jest
- * zainteresowany, niech da zna na ekg-devel.
- */
extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
+extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap);
-/*
- * mona poda plik, do ktrego bd zapisywane teksty z gg_debug().
- */
extern FILE *gg_debug_file;
-#define GG_DEBUG_NET 1
-#define GG_DEBUG_TRAFFIC 2
-#define GG_DEBUG_DUMP 4
-#define GG_DEBUG_FUNCTION 8
-#define GG_DEBUG_MISC 16
+/**
+ * \ingroup debug
+ * @{
+ */
+#define GG_DEBUG_NET 1 /**< Rejestracja zdarzeń związanych z siecią */
+#define GG_DEBUG_TRAFFIC 2 /**< Rejestracja ruchu sieciowego */
+#define GG_DEBUG_DUMP 4 /**< Rejestracja zawartości pakietów */
+#define GG_DEBUG_FUNCTION 8 /**< Rejestracja wywołań funkcji */
+#define GG_DEBUG_MISC 16 /**< Rejestracja różnych informacji */
+/** @} */
#ifdef GG_DEBUG_DISABLE
#define gg_debug(x, y...) do { } while(0)
+#define gg_debug_session(z, x, y...) do { } while(0)
#else
void gg_debug(int level, const char *format, ...);
+void gg_debug_session(struct gg_session *sess, int level, const char *format, ...);
#endif
const char *gg_libgadu_version(void);
-/*
- * konfiguracja http proxy.
- */
-extern int gg_proxy_enabled; /* wcza obsug proxy */
-extern char *gg_proxy_host; /* okrela adres serwera proxy */
-extern int gg_proxy_port; /* okrela port serwera proxy */
-extern char *gg_proxy_username; /* okrela nazw uytkownika przy autoryzacji serwera proxy */
-extern char *gg_proxy_password; /* okrela haso uytkownika przy autoryzacji serwera proxy */
-extern int gg_proxy_http_only; /* wcza obsug proxy wycznie dla usug HTTP */
+extern int gg_proxy_enabled;
+extern char *gg_proxy_host;
+extern int gg_proxy_port;
+extern char *gg_proxy_username;
+extern char *gg_proxy_password;
+extern int gg_proxy_http_only;
+extern unsigned long gg_local_ip;
-/*
- * adres, z ktrego lemy pakiety (np czymy si z serwerem)
- * uywany przy gg_connect()
+#define GG_LOGIN_HASH_GG32 0x01 /**< Algorytm Gadu-Gadu */
+#define GG_LOGIN_HASH_SHA1 0x02 /**< Algorytm SHA1 */
+
+#ifndef DOXYGEN
+
+#define GG_PUBDIR50_WRITE 0x01
+#define GG_PUBDIR50_READ 0x02
+#define GG_PUBDIR50_SEARCH 0x03
+#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
+#define GG_PUBDIR50_SEARCH_REPLY 0x05
+
+#else
+
+/**
+ * \ingroup pubdir50
+ *
+ * Rodzaj zapytania lub odpowiedzi katalogu publicznego.
*/
-extern unsigned long gg_local_ip;
-/*
- * -------------------------------------------------------------------------
- * poniej znajduj si wewntrzne sprawy biblioteki. zwyky klient nie
- * powinien ich w ogle rusza, bo i nie ma po co. wszystko mona zaatwi
- * procedurami wyszego poziomu, ktrych definicje znajduj si na pocztku
- * tego pliku.
- * -------------------------------------------------------------------------
- */
-
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
-#elif defined _WIN32
-int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname);
-#endif
+enum {
+ GG_PUBDIR50_WRITE, /**< Wysłanie do serwera informacji o sobie */
+ GG_PUBDIR50_READ, /**< Pobranie z serwera informacji o sobie */
+ GG_PUBDIR50_SEARCH, /**< Wyszukiwanie w katalogu publicznym */
+ GG_PUBDIR50_SEARCH_REPLY, /**< Wynik wyszukiwania w katalogu publicznym */
+};
-#ifdef _WIN32
-int gg_thread_socket(int thread_id, int socket);
-#endif
+#endif /* DOXYGEN */
+
+/** \cond obsolete */
+
+#define gg_free_event gg_event_free
+#define gg_free_http gg_http_free
+#define gg_free_pubdir gg_pubdir_free
+#define gg_free_register gg_pubdir_free
+#define gg_free_remind_passwd gg_pubdir_free
+#define gg_free_dcc gg_dcc_free
+#define gg_free_change_passwd gg_pubdir_free
+
+struct gg_search_request {
+ int active;
+ unsigned int start;
+ char *nickname;
+ char *first_name;
+ char *last_name;
+ char *city;
+ int gender;
+ int min_birth;
+ int max_birth;
+ char *email;
+ char *phone;
+ uin_t uin;
+} /* GG_DEPRECATED */;
+
+struct gg_search {
+ int count;
+ struct gg_search_result *results;
+} GG_DEPRECATED;
+
+struct gg_search_result {
+ uin_t uin;
+ char *first_name;
+ char *last_name;
+ char *nickname;
+ int born;
+ int gender;
+ char *city;
+ int active;
+} GG_DEPRECATED;
-int gg_resolve(int *fd, int *pid, const char *hostname);
+#define GG_GENDER_NONE 0
+#define GG_GENDER_FEMALE 1
+#define GG_GENDER_MALE 2
-#if defined __GNUC__ && !defined _WIN32
-char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED;
+int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_free_search(struct gg_http *f) GG_DEPRECATED;
+#define gg_search_free gg_free_search
+
+const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED;
+void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED;
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED;
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED;
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED;
+
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_change_info_request {
+ char *first_name;
+ char *last_name;
+ char *nickname;
+ char *email;
+ int born;
+ int gender;
+ char *city;
+} /* GG_DEPRECATED */;
+
+struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city) GG_DEPRECATED;
+void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED;
+#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
+#define gg_change_pubdir_free gg_pubdir_free
+#define gg_free_change_pubdir gg_pubdir_free
+
+struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED;
+int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED;
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED;
+
+/** \endcond */
+
+int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED;
+
+#ifdef __GNUC__
+char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED;
#else
-char *gg_saprintf(const char *format, ...);
+char *gg_saprintf(const char *format, ...) GG_DEPRECATED;
#endif
-char *gg_vsaprintf(const char *format, va_list ap);
+char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
#define gg_alloc_sprintf gg_saprintf
-char *gg_get_line(char **ptr);
-
-int gg_connect(void *addr, int port, int async);
-struct in_addr *gg_gethostbyname(const char *hostname);
-char *gg_read_line(int sock, char *buf, int length);
-void gg_chomp(char *line);
-char *gg_urlencode(const char *str);
-int gg_http_hash(const char *format, ...);
-int gg_read(struct gg_session *sess, char *buf, int length);
-int gg_write(struct gg_session *sess, const char *buf, int length);
-void *gg_recv_packet(struct gg_session *sess);
-int gg_send_packet(struct gg_session *sess, int type, ...);
-unsigned int gg_login_hash(const unsigned char *password, unsigned int seed);
-uint32_t gg_fix32(uint32_t x);
-uint16_t gg_fix16(uint16_t x);
+char *gg_get_line(char **ptr) GG_DEPRECATED;
+
+int gg_connect(void *addr, int port, int async) GG_DEPRECATED;
+struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
+char *gg_read_line(int sock, char *buf, int length) GG_DEPRECATED;
+void gg_chomp(char *line) GG_DEPRECATED;
+char *gg_urlencode(const char *str) GG_DEPRECATED;
+int gg_http_hash(const char *format, ...) GG_DEPRECATED;
+void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED;
+int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED;
+int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED;
+void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED;
+int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED;
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED;
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED;
+uint32_t gg_fix32(uint32_t x) GG_DEPRECATED;
+uint16_t gg_fix16(uint16_t x) GG_DEPRECATED;
#define fix16 gg_fix16
#define fix32 gg_fix32
-char *gg_proxy_auth(void);
-char *gg_base64_encode(const char *buf);
-char *gg_base64_decode(const char *buf);
-int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq);
+char *gg_proxy_auth(void) GG_DEPRECATED;
+char *gg_base64_encode(const char *buf) GG_DEPRECATED;
+char *gg_base64_decode(const char *buf) GG_DEPRECATED;
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED;
+
+/**
+ * Kolejka odbieranych obrazków.
+ */
+struct gg_image_queue {
+ uin_t sender; /**< Nadawca obrazka */
+ uint32_t size; /**< Rozmiar obrazka */
+ uint32_t crc32; /**< Suma kontrolna CRC32 */
+ char *filename; /**< Nazwa pliku */
+ char *image; /**< Bufor z odebranymi danymi */
+ uint32_t done; /**< Rozmiar odebranych danych */
+
+ struct gg_image_queue *next; /**< Kolejny element listy */
+} GG_DEPRECATED;
+
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
#define GG_APPMSG_PORT 80
@@ -941,63 +1346,111 @@ int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int fr
#define GG_HTTPS_PORT 443
#define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)"
-#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158"
-#define GG_DEFAULT_PROTOCOL_VERSION 0x24
+#define GG_DEFAULT_CLIENT_VERSION "8.0.0.7669"
+#define GG_DEFAULT_PROTOCOL_VERSION 0x2e
#define GG_DEFAULT_TIMEOUT 30
#define GG_HAS_AUDIO_MASK 0x40000000
+#define GG_HAS_AUDIO7_MASK 0x20000000
#define GG_ERA_OMNIX_MASK 0x04000000
-#define GG_LIBGADU_VERSION "1.5.20050718"
+#undef GG_LIBGADU_VERSION
+
+#ifndef DOXYGEN
+
+#define GG_FEATURE_MSG77 0x01
+#define GG_FEATURE_STATUS77 0x02
+#define GG_FEATURE_DND_FFC 0x10
+#define GG_FEATURE_IMAGE_DESCR 0x20
+
+/* Poniższe makra zostały zachowane dla zgodności API */
+#define GG_FEATURE_MSG80 0
+#define GG_FEATURE_STATUS80 0
+#define GG_FEATURE_STATUS80BETA 0
+#define GG_FEATURE_ALL (GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR)
+
+#else
+
+/**
+ * \ingroup login
+ *
+ * Flagi opcji protokołu.
+ */
+enum {
+ GG_FEATURE_MSG77, /**< Klient życzy sobie otrzymywać wiadomości zgodnie z protokołem 7.7 */
+ GG_FEATURE_STATUS77, /**< Klient życzy sobie otrzymywać zmiany stanu zgodnie z protokołem 7.7 */
+ GG_FEATURE_DND_FFC, /**< Klient obsługuje statusy "nie przeszkadzać" i "poGGadaj ze mną" */
+ GG_FEATURE_IMAGE_DESCR, /**< Klient obsługuje opisy graficzne oraz flagę \c GG_STATUS80_DESCR_MASK */
+};
+
+
+#endif
#define GG_DEFAULT_DCC_PORT 1550
struct gg_header {
uint32_t type; /* typ pakietu */
- uint32_t length; /* dugo reszty pakietu */
+ uint32_t length; /* długość reszty pakietu */
} GG_PACKED;
#define GG_WELCOME 0x0001
#define GG_NEED_EMAIL 0x0014
struct gg_welcome {
- uint32_t key; /* klucz szyfrowania hasa */
+ uint32_t key; /* klucz szyfrowania hasła */
} GG_PACKED;
#define GG_LOGIN 0x000c
struct gg_login {
- uint32_t uin; /* mj numerek */
- uint32_t hash; /* hash hasa */
- uint32_t status; /* status na dzie dobry */
+ uint32_t uin; /* mój numerek */
+ uint32_t hash; /* hash hasła */
+ uint32_t status; /* status na dzień dobry */
uint32_t version; /* moja wersja klienta */
- uint32_t local_ip; /* mj adres ip */
- uint16_t local_port; /* port, na ktrym sucham */
+ uint32_t local_ip; /* mój adres ip */
+ uint16_t local_port; /* port, na którym słucham */
} GG_PACKED;
#define GG_LOGIN_EXT 0x0013
struct gg_login_ext {
- uint32_t uin; /* mj numerek */
- uint32_t hash; /* hash hasa */
- uint32_t status; /* status na dzie dobry */
+ uint32_t uin; /* mój numerek */
+ uint32_t hash; /* hash hasła */
+ uint32_t status; /* status na dzień dobry */
uint32_t version; /* moja wersja klienta */
- uint32_t local_ip; /* mj adres ip */
- uint16_t local_port; /* port, na ktrym sucham */
- uint32_t external_ip; /* zewntrzny adres ip */
- uint16_t external_port; /* zewntrzny port */
+ uint32_t local_ip; /* mój adres ip */
+ uint16_t local_port; /* port, na którym słucham */
+ uint32_t external_ip; /* zewnętrzny adres ip */
+ uint16_t external_port; /* zewnętrzny port */
} GG_PACKED;
#define GG_LOGIN60 0x0015
struct gg_login60 {
- uint32_t uin; /* mj numerek */
- uint32_t hash; /* hash hasa */
- uint32_t status; /* status na dzie dobry */
+ uint32_t uin; /* mój numerek */
+ uint32_t hash; /* hash hasła */
+ uint32_t status; /* status na dzień dobry */
uint32_t version; /* moja wersja klienta */
uint8_t dunno1; /* 0x00 */
- uint32_t local_ip; /* mj adres ip */
- uint16_t local_port; /* port, na ktrym sucham */
- uint32_t external_ip; /* zewntrzny adres ip */
- uint16_t external_port; /* zewntrzny port */
+ uint32_t local_ip; /* mój adres ip */
+ uint16_t local_port; /* port, na którym słucham */
+ uint32_t external_ip; /* zewnętrzny adres ip */
+ uint16_t external_port; /* zewnętrzny port */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno2; /* 0xbe */
+} GG_PACKED;
+
+#define GG_LOGIN70 0x0019
+
+struct gg_login70 {
+ uint32_t uin; /* mój numerek */
+ uint8_t hash_type; /* rodzaj hashowania hasła */
+ uint8_t hash[64]; /* hash hasła dopełniony zerami */
+ uint32_t status; /* status na dzień dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint8_t dunno1; /* 0x00 */
+ uint32_t local_ip; /* mój adres ip */
+ uint16_t local_port; /* port, na którym słucham */
+ uint32_t external_ip; /* zewnętrzny adres ip (???) */
+ uint16_t external_port; /* zewnętrzny port (???) */
uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
uint8_t dunno2; /* 0xbe */
} GG_PACKED;
@@ -1008,70 +1461,135 @@ struct gg_login60 {
#define GG_PUBDIR50_REQUEST 0x0014
-#define GG_PUBDIR50_WRITE 0x01
-#define GG_PUBDIR50_READ 0x02
-#define GG_PUBDIR50_SEARCH 0x03
-#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
-#define GG_PUBDIR50_SEARCH_REPLY 0x05
-
struct gg_pubdir50_request {
uint8_t type; /* GG_PUBDIR50_* */
- uint32_t seq; /* czas wysania zapytania */
+ uint32_t seq; /* czas wysłania zapytania */
} GG_PACKED;
#define GG_PUBDIR50_REPLY 0x000e
struct gg_pubdir50_reply {
uint8_t type; /* GG_PUBDIR50_* */
- uint32_t seq; /* czas wysania zapytania */
+ uint32_t seq; /* czas wysłania zapytania */
} GG_PACKED;
#define GG_NEW_STATUS 0x0002
-#define GG_STATUS_NOT_AVAIL 0x0001 /* niedostpny */
-#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedostpny z opisem (4.8) */
-#define GG_STATUS_AVAIL 0x0002 /* dostpny */
-#define GG_STATUS_AVAIL_DESCR 0x0004 /* dostpny z opisem (4.9) */
-#define GG_STATUS_BUSY 0x0003 /* zajty */
-#define GG_STATUS_BUSY_DESCR 0x0005 /* zajty z opisem (4.8) */
-#define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (4.6) */
-#define GG_STATUS_INVISIBLE_DESCR 0x0016 /* niewidoczny z opisem (4.9) */
-#define GG_STATUS_BLOCKED 0x0006 /* zablokowany */
+#ifndef DOXYGEN
+
+#define GG_STATUS_NOT_AVAIL 0x0001
+#define GG_STATUS_NOT_AVAIL_DESCR 0x0015
+#define GG_STATUS_FFC 0x0017
+#define GG_STATUS_FFC_DESCR 0x0018
+#define GG_STATUS_AVAIL 0x0002
+#define GG_STATUS_AVAIL_DESCR 0x0004
+#define GG_STATUS_BUSY 0x0003
+#define GG_STATUS_BUSY_DESCR 0x0005
+#define GG_STATUS_DND 0x0021
+#define GG_STATUS_DND_DESCR 0x0022
+#define GG_STATUS_INVISIBLE 0x0014
+#define GG_STATUS_INVISIBLE_DESCR 0x0016
+#define GG_STATUS_BLOCKED 0x0006
+
+#define GG_STATUS_IMAGE_MASK 0x0100
+#define GG_STATUS_DESCR_MASK 0x4000
+#define GG_STATUS_FRIENDS_MASK 0x8000
-#define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (4.6) */
+#else
-#define GG_STATUS_DESCR_MAXSIZE 70
+/**
+ * Rodzaje statusów użytkownika.
+ *
+ * \ingroup status
+ */
+enum {
+ GG_STATUS_NOT_AVAIL, /**< Niedostępny */
+ GG_STATUS_NOT_AVAIL_DESCR, /**< Niedostępny z opisem */
+ GG_STATUS_FFC, /**< PoGGadaj ze mną */
+ GG_STATUS_FFC_DESCR, /**< PoGGadaj ze mną z opisem */
+ GG_STATUS_AVAIL, /**< Dostępny */
+ GG_STATUS_AVAIL_DESCR, /**< Dostępny z opisem */
+ GG_STATUS_BUSY, /**< Zajęty */
+ GG_STATUS_BUSY_DESCR, /**< Zajęty z opisem */
+ GG_STATUS_DND, /**< Nie przeszkadzać */
+ GG_STATUS_DND_DESCR, /**< Nie przeszakdzać z opisem */
+ GG_STATUS_INVISIBLE, /**< Niewidoczny (tylko własny status) */
+ GG_STATUS_INVISIBLE_DESCR, /**< Niewidoczny z opisem (tylko własny status) */
+ GG_STATUS_BLOCKED, /**< Zablokowany (tylko status innych) */
+ GG_STATUS_IMAGE_MASK, /**< Flaga bitowa oznaczająca opis graficzny (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
+ GG_STATUS_DESCR_MASK, /**< Flaga bitowa oznaczająca status z opisem (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
+ GG_STATUS_FRIENDS_MASK, /**< Flaga bitowa dostępności tylko dla znajomych */
+};
-/*
- * makra do atwego i szybkiego sprawdzania stanu.
+#endif /* DOXYGEN */
+
+/**
+ * \ingroup status
+ *
+ * Flaga bitowa dostepnosci informujaca ze mozemy voipowac
*/
+#define GG_STATUS_VOICE_MASK 0x20000 /**< czy ma wlaczone audio (7.7) */
+
+/**
+ * \ingroup status
+ *
+ * Maksymalna długośc opisu.
+ */
+#define GG_STATUS_DESCR_MAXSIZE 255
+#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70
+
+#define GG_STATUS_MASK 0xff
+
/* GG_S_F() tryb tylko dla znajomych */
#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
-/* GG_S() stan bez uwzgldnienia trybu tylko dla znajomych */
-#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
+/* GG_S() stan bez uwzględnienia dodatkowych flag */
+#define GG_S(x) ((x) & GG_STATUS_MASK)
-/* GG_S_A() dostpny */
-#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
-/* GG_S_NA() niedostpny */
-#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
+/* GG_S_FF() chętny do rozmowy */
+#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR)
+
+/* GG_S_AV() dostępny */
+#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_AW() zaraz wracam */
+#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
-/* GG_S_B() zajty */
-#define GG_S_B(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
+/* GG_S_DD() nie przeszkadzać */
+#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_DESCR)
+
+/* GG_S_NA() niedostępny */
+#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
/* GG_S_I() niewidoczny */
#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
-/* GG_S_D() stan opisowy */
-#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
-/* GG_S_BL() blokowany lub blokujcy */
+/* GG_S_A() dostępny lub chętny do rozmowy */
+#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x))
+
+/* GG_S_B() zajęty lub nie przeszkadzać */
+#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x))
+
+
+/* GG_S_D() stan opisowy */
+#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \
+ GG_S(x) == GG_STATUS_FFC_DESCR || \
+ GG_S(x) == GG_STATUS_AVAIL_DESCR || \
+ GG_S(x) == GG_STATUS_BUSY_DESCR || \
+ GG_S(x) == GG_STATUS_DND_DESCR || \
+ GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+
+/* GG_S_BL() blokowany lub blokujący */
#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
+/**
+ * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA)
+ */
struct gg_new_status {
- uint32_t status; /* na jaki zmieni? */
+ uint32_t status; /**< Nowy status */
} GG_PACKED;
#define GG_NOTIFY_FIRST 0x000f
@@ -1081,12 +1599,29 @@ struct gg_new_status {
struct gg_notify {
uint32_t uin; /* numerek danej osoby */
- uint8_t dunno1; /* rodzaj wpisu w licie */
+ uint8_t dunno1; /* rodzaj wpisu w liście */
} GG_PACKED;
-#define GG_USER_OFFLINE 0x01 /* bdziemy niewidoczni dla uytkownika */
-#define GG_USER_NORMAL 0x03 /* zwyky uytkownik */
-#define GG_USER_BLOCKED 0x04 /* zablokowany uytkownik */
+#ifndef DOXYGEN
+
+#define GG_USER_OFFLINE 0x01
+#define GG_USER_NORMAL 0x03
+#define GG_USER_BLOCKED 0x04
+
+#else
+
+/**
+ * \ingroup contacts
+ *
+ * Rodzaj kontaktu.
+ */
+enum {
+ GG_USER_NORMAL, /**< Zwykły kontakt */
+ GG_USER_BLOCKED, /**< Zablokowany */
+ GG_USER_OFFLINE, /**< Niewidoczny dla kontaktu */
+};
+
+#endif /* DOXYGEN */
#define GG_LIST_EMPTY 0x0012
@@ -1096,7 +1631,7 @@ struct gg_notify_reply {
uint32_t uin; /* numerek */
uint32_t status; /* status danej osoby */
uint32_t remote_ip; /* adres ip delikwenta */
- uint16_t remote_port; /* port, na ktrym sucha klient */
+ uint16_t remote_port; /* port, na którym słucha klient */
uint32_t version; /* wersja klienta */
uint16_t dunno2; /* znowu port? */
} GG_PACKED;
@@ -1107,7 +1642,7 @@ struct gg_notify_reply60 {
uint32_t uin; /* numerek plus flagi w MSB */
uint8_t status; /* status danej osoby */
uint32_t remote_ip; /* adres ip delikwenta */
- uint16_t remote_port; /* port, na ktrym sucha klient */
+ uint16_t remote_port; /* port, na którym słucha klient */
uint8_t version; /* wersja klienta */
uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
uint8_t dunno1; /* 0x00 */
@@ -1119,10 +1654,36 @@ struct gg_status60 {
uint32_t uin; /* numerek plus flagi w MSB */
uint8_t status; /* status danej osoby */
uint32_t remote_ip; /* adres ip delikwenta */
- uint16_t remote_port; /* port, na ktrym sucha klient */
+ uint16_t remote_port; /* port, na którym słucha klient */
+ uint8_t version; /* wersja klienta */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno1; /* 0x00 */
+} GG_PACKED;
+
+#define GG_NOTIFY_REPLY77 0x0018
+
+struct gg_notify_reply77 {
+ uint32_t uin; /* numerek plus flagi w MSB */
+ uint8_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym słucha klient */
+ uint8_t version; /* wersja klienta */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno1; /* 0x00 */
+ uint32_t dunno2; /* ? */
+} GG_PACKED;
+
+#define GG_STATUS77 0x0017
+
+struct gg_status77 {
+ uint32_t uin; /* numerek plus flagi w MSB */
+ uint8_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym słucha klient */
uint8_t version; /* wersja klienta */
uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
uint8_t dunno1; /* 0x00 */
+ uint32_t dunno2; /* ? */
} GG_PACKED;
#define GG_ADD_NOTIFY 0x000d
@@ -1142,15 +1703,41 @@ struct gg_status {
#define GG_SEND_MSG 0x000b
+#ifndef DOXYGEN
+
#define GG_CLASS_QUEUED 0x0001
#define GG_CLASS_OFFLINE GG_CLASS_QUEUED
#define GG_CLASS_MSG 0x0004
#define GG_CLASS_CHAT 0x0008
#define GG_CLASS_CTCP 0x0010
#define GG_CLASS_ACK 0x0020
-#define GG_CLASS_EXT GG_CLASS_ACK /* kompatybilno wstecz */
+#define GG_CLASS_EXT GG_CLASS_ACK /**< Dla kompatybilności wstecz */
+
+#else
+
+/**
+ * Klasy wiadomości. Wartości są maskami bitowymi, które w większości
+ * przypadków można łączyć (połączenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT
+ * nie ma sensu).
+ *
+ * \ingroup messages
+ */
+enum {
+ GG_CLASS_MSG, /**< Wiadomość ma pojawić się w osobnym oknie */
+ GG_CLASS_CHAT, /**< Wiadomość ma pojawić się w oknie rozmowy */
+ GG_CLASS_CTCP, /**< Wiadomość przeznaczona dla klienta Gadu-Gadu */
+ GG_CLASS_ACK, /**< Klient nie życzy sobie potwierdzenia */
+ GG_CLASS_QUEUED, /**< Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu) */
+};
+
+#endif /* DOXYGEN */
-#define GG_MSG_MAXSIZE 2000
+/**
+ * Maksymalna długość wiadomości.
+ *
+ * \ingroup messages
+ */
+#define GG_MSG_MAXSIZE 1989
struct gg_send_msg {
uint32_t recipient;
@@ -1163,16 +1750,19 @@ struct gg_msg_richtext {
uint16_t length;
} GG_PACKED;
+/**
+ * Struktura opisująca formatowanie tekstu. W zależności od wartości pola
+ * \c font, zaraz za tą strukturą może wystąpić \c gg_msg_richtext_color
+ * lub \c gg_msg_richtext_image.
+ *
+ * \ingroup messages
+ */
struct gg_msg_richtext_format {
- uint16_t position;
- uint8_t font;
+ uint16_t position; /**< Początkowy znak formatowania (liczony od 0) */
+ uint8_t font; /**< Atrybuty formatowania */
} GG_PACKED;
-struct gg_msg_richtext_image {
- uint16_t unknown1;
- uint32_t size;
- uint32_t crc32;
-} GG_PACKED;
+#ifndef DOXYGEN
#define GG_FONT_BOLD 0x01
#define GG_FONT_ITALIC 0x02
@@ -1180,10 +1770,44 @@ struct gg_msg_richtext_image {
#define GG_FONT_COLOR 0x08
#define GG_FONT_IMAGE 0x80
+#else
+
+/**
+ * Atrybuty formatowania wiadomości.
+ *
+ * \ingroup messages
+ */
+enum {
+ GG_FONT_BOLD,
+ GG_FONT_ITALIC,
+ GG_FONT_UNDERLINE,
+ GG_FONT_COLOR,
+ GG_FONT_IMAGE
+};
+
+#endif /* DOXYGEN */
+
+/**
+ * Struktura opisującą kolor tekstu dla atrybutu \c GG_FONT_COLOR.
+ *
+ * \ingroup messages
+ */
struct gg_msg_richtext_color {
- uint8_t red;
- uint8_t green;
- uint8_t blue;
+ uint8_t red; /**< Składowa czerwona koloru */
+ uint8_t green; /**< Składowa zielona koloru */
+ uint8_t blue; /**< Składowa niebieska koloru */
+} GG_PACKED;
+
+/**
+ * Strukturya opisująca obrazek wstawiony do wiadomości dla atrubutu
+ * \c GG_FONT_IMAGE.
+ *
+ * \ingroup messages
+ */
+struct gg_msg_richtext_image {
+ uint16_t unknown1; /**< Nieznane pole o wartości 0x0109 */
+ uint32_t size; /**< Rozmiar obrazka */
+ uint32_t crc32; /**< Suma kontrolna CRC32 obrazka */
} GG_PACKED;
struct gg_msg_recipients {
@@ -1207,12 +1831,32 @@ struct gg_msg_image_reply {
#define GG_SEND_MSG_ACK 0x0005
+#ifndef DOXYGEN
+
#define GG_ACK_BLOCKED 0x0001
#define GG_ACK_DELIVERED 0x0002
#define GG_ACK_QUEUED 0x0003
#define GG_ACK_MBOXFULL 0x0004
#define GG_ACK_NOT_DELIVERED 0x0006
+#else
+
+/**
+ * Status doręczenia wiadomości.
+ *
+ * \ingroup messages
+ */
+enum
+{
+ GG_ACK_DELIVERED, /**< Wiadomość dostarczono. */
+ GG_ACK_QUEUED, /**< Wiadomość zakolejkowano z powodu niedostępności odbiorcy. */
+ GG_ACK_BLOCKED, /**< Wiadomość zablokowana przez serwer (spam, świąteczne ograniczenia itd.) */
+ GG_ACK_MBOXFULL, /**< Wiadomości nie dostarczono z powodu zapełnionej kolejki wiadomości odbiorcy. */
+ GG_ACK_NOT_DELIVERED /**< Wiadomości nie dostarczono (tylko dla \c GG_CLASS_CTCP). */
+};
+
+#endif /* DOXYGEN */
+
struct gg_send_msg_ack {
uint32_t status;
uint32_t recipient;
@@ -1236,29 +1880,59 @@ struct gg_recv_msg {
#define GG_USERLIST_REQUEST 0x0016
+#define GG_XML_EVENT 0x0027
+
+#ifndef DOXYGEN
+
#define GG_USERLIST_PUT 0x00
#define GG_USERLIST_PUT_MORE 0x01
#define GG_USERLIST_GET 0x02
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj zapytania.
+ */
+enum {
+ GG_USERLIST_PUT, /**< Eksport listy kontaktów. */
+ GG_USERLIST_GET, /**< Import listy kontaktów. */
+};
+
+#endif /* DOXYGEN */
+
struct gg_userlist_request {
uint8_t type;
} GG_PACKED;
#define GG_USERLIST_REPLY 0x0010
+#ifndef DOXYGEN
+
#define GG_USERLIST_PUT_REPLY 0x00
#define GG_USERLIST_PUT_MORE_REPLY 0x02
#define GG_USERLIST_GET_REPLY 0x06
#define GG_USERLIST_GET_MORE_REPLY 0x04
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj odpowiedzi.
+ */
+enum {
+ GG_USERLIST_PUT_REPLY, /**< Wyeksportowano listy kontaktów. */
+ GG_USERLIST_GET_REPLY, /**< Zaimportowano listę kontaktów. */
+};
+
+#endif /* DOXYGEN */
+
struct gg_userlist_reply {
uint8_t type;
} GG_PACKED;
-/*
- * pakiety, stae, struktury dla DCC
- */
-
struct gg_dcc_tiny_packet {
uint8_t type; /* rodzaj pakietu */
} GG_PACKED;
@@ -1274,14 +1948,14 @@ struct gg_dcc_big_packet {
} GG_PACKED;
/*
- * pki co, nie znamy dokadnie protokou. nie wiemy, co czemu odpowiada.
- * nazwy s niepowane i tymczasowe.
+ * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada.
+ * nazwy są niepoważne i tymczasowe.
*/
#define GG_DCC_WANT_FILE 0x0003 /* peer chce plik */
-#define GG_DCC_HAVE_FILE 0x0001 /* wic mu damy */
+#define GG_DCC_HAVE_FILE 0x0001 /* więc mu damy */
#define GG_DCC_HAVE_FILEINFO 0x0003 /* niech ma informacje o pliku */
#define GG_DCC_GIMME_FILE 0x0006 /* peer jest pewny */
-#define GG_DCC_CATCH_FILE 0x0002 /* wysyamy plik */
+#define GG_DCC_CATCH_FILE 0x0002 /* wysyłamy plik */
#define GG_DCC_FILEATTR_READONLY 0x0020
@@ -1290,11 +1964,88 @@ struct gg_dcc_big_packet {
#define GG_DCC_TIMEOUT_FILE_ACK 300 /* 5 minut */
#define GG_DCC_TIMEOUT_VOICE_ACK 300 /* 5 minut */
+#define GG_DCC7_INFO 0x1f
+
+struct gg_dcc7_info {
+ uint32_t uin; /* numer nadawcy */
+ uint32_t type; /* sposób połączenia */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ char info[GG_DCC7_INFO_LEN]; /* informacje o połączeniu "ip port" */
+} GG_PACKED;
+
+#define GG_DCC7_NEW 0x20
+
+struct gg_dcc7_new {
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint32_t uin_from; /* numer nadawcy */
+ uint32_t uin_to; /* numer odbiorcy */
+ uint32_t type; /* rodzaj transmisji */
+ unsigned char filename[GG_DCC7_FILENAME_LEN]; /* nazwa pliku */
+ uint32_t size; /* rozmiar pliku */
+ uint32_t size_hi; /* rozmiar pliku (starsze bajty) */
+ unsigned char hash[GG_DCC7_HASH_LEN]; /* hash SHA1 */
+} GG_PACKED;
+
+#define GG_DCC7_ACCEPT 0x21
+
+struct gg_dcc7_accept {
+ uint32_t uin; /* numer przyjmującego połączenie */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint32_t offset; /* offset przy wznawianiu transmisji */
+ uint32_t dunno1; /* 0x00000000 */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_P2P 0x00000001 /**< Połączenie bezpośrednie */
+#define GG_DCC7_TYPE_SERVER 0x00000002 /**< Połączenie przez serwer */
+
+#define GG_DCC7_REJECT 0x22
+
+struct gg_dcc7_reject {
+ uint32_t uin; /**< Numer odrzucającego połączenie */
+ gg_dcc7_id_t id; /**< Identyfikator połączenia */
+ uint32_t reason; /**< Powód rozłączenia */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_REJECT_BUSY 0x00000001 /**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */
+#define GG_DCC7_REJECT_USER 0x00000002 /**< Użytkownik odrzucił połączenie */
+#define GG_DCC7_REJECT_VERSION 0x00000006 /**< Druga strona ma wersję klienta nieobsługującą połączeń bezpośrednich tego typu */
+
+#define GG_DCC7_ID_REQUEST 0x23
+
+struct gg_dcc7_id_request {
+ uint32_t type; /**< Rodzaj tranmisji */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_VOICE 0x00000001 /**< Transmisja głosu */
+#define GG_DCC7_TYPE_FILE 0x00000004 /**< transmisja pliku */
+
+#define GG_DCC7_ID_REPLY 0x23
+
+struct gg_dcc7_id_reply {
+ uint32_t type; /** Rodzaj transmisji */
+ gg_dcc7_id_t id; /** Przyznany identyfikator */
+} GG_PACKED;
+
+#define GG_DCC7_DUNNO1 0x24
+
+struct gg_dcc7_dunno1 {
+ // XXX
+} GG_PACKED;
+
+#define GG_DCC7_TIMEOUT_CONNECT 10 /* 10 sekund */
+#define GG_DCC7_TIMEOUT_SEND 1800 /* 30 minut */
+#define GG_DCC7_TIMEOUT_GET 1800 /* 30 minut */
+#define GG_DCC7_TIMEOUT_FILE_ACK 300 /* 5 minut */
+#define GG_DCC7_TIMEOUT_VOICE_ACK 300 /* 5 minut */
+
#ifdef __cplusplus
-#ifdef _MSC_VER
+}
+#ifdef _WIN32
#pragma pack(pop)
#endif
-}
#endif
#endif /* __GG_LIBGADU_H */
diff --git a/libpurple/protocols/gg/lib/obsolete.c b/libpurple/protocols/gg/lib/obsolete.c
index 48b5a0a41e..b27473dd78 100644
--- a/libpurple/protocols/gg/lib/obsolete.c
+++ b/libpurple/protocols/gg/lib/obsolete.c
@@ -1,4 +1,4 @@
-/* $Id: obsolete.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */
/*
* (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,16 +14,23 @@
*
* 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.
*/
-/*
- * Plik zawiera deklaracje funkcji, ktre s ju nieaktualne ze wzgldu
- * na zmiany w protokole, ale s wymagane przez aplikacje linkowane ze
- * starszymi wersjami bibliotek.
+/**
+ * \file obsolete.c
+ *
+ * \brief Nieaktualne funkcje
+ *
+ * Plik zawiera definicje funkcji, które są już nieaktualne ze względu
+ * na zmiany w protokole. Programy konsolidowane ze starszych wersjami
+ * bibliotek powinny nadal mieć możliwość działania, mimo ograniczonej
+ * funkcjonalności.
*/
+/** \cond obsolete */
+
#include <errno.h>
#include "libgadu.h"
@@ -205,3 +212,25 @@ void gg_change_info_request_free(struct gg_change_info_request *r)
{
}
+
+int gg_resolve(int *fd, int *pid, const char *hostname)
+{
+ return -1;
+}
+
+void gg_resolve_pthread_cleanup(void *arg, int kill)
+{
+
+}
+
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
+{
+ return -1;
+}
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+{
+ return -1;
+}
+
+/** \endcond */
diff --git a/libpurple/protocols/gg/lib/protocol.h b/libpurple/protocols/gg/lib/protocol.h
new file mode 100644
index 0000000000..d44c745563
--- /dev/null
+++ b/libpurple/protocols/gg/lib/protocol.h
@@ -0,0 +1,165 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef LIBGADU_PROTOCOL_H
+#define LIBGADU_PROTOCOL_H
+
+#include "libgadu.h"
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#endif
+
+#define GG_LOGIN80BETA 0x0029
+
+#define GG_LOGIN80 0x0031
+
+#undef GG_FEATURE_STATUS80BETA
+#undef GG_FEATURE_MSG80
+#undef GG_FEATURE_STATUS80
+#define GG_FEATURE_STATUS80BETA 0x01
+#define GG_FEATURE_MSG80 0x02
+#define GG_FEATURE_STATUS80 0x05
+
+#define GG8_LANG "pl"
+#define GG8_VERSION "Gadu-Gadu Client Build 8.0.0.8731"
+
+struct gg_login80 {
+ uint32_t uin; /* mój numerek */
+ uint8_t language[2]; /* język: GG8_LANG */
+ uint8_t hash_type; /* rodzaj hashowania hasła */
+ uint8_t hash[64]; /* hash hasła dopełniony zerami */
+ uint32_t status; /* status na dzień dobry */
+ uint32_t flags; /* flagi (przeznaczenie nieznane) */
+ uint32_t features; /* opcje protokołu (GG8_FEATURES) */
+ uint32_t local_ip; /* mój adres ip */
+ uint16_t local_port; /* port, na którym słucham */
+ uint32_t external_ip; /* zewnętrzny adres ip (???) */
+ uint16_t external_port; /* zewnętrzny port (???) */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno2; /* 0x64 */
+} GG_PACKED;
+
+#define GG_LOGIN_HASH_TYPE_INVALID 0x0016
+
+#define GG_LOGIN80_OK 0x0035
+
+#define GG_NEW_STATUS80BETA 0x0028
+
+#define GG_NEW_STATUS80 0x0038
+
+/**
+ * Zmiana stanu (pakiet \c GG_NEW_STATUS80)
+ */
+struct gg_new_status80 {
+ uint32_t status; /**< Nowy status */
+ uint32_t flags; /**< flagi (nieznane przeznaczenie) */
+ uint32_t description_size; /**< rozmiar opisu */
+} GG_PACKED;
+
+#define GG_STATUS80BETA 0x002a
+#define GG_NOTIFY_REPLY80BETA 0x002b
+
+#define GG_STATUS80 0x0036
+#define GG_NOTIFY_REPLY80 0x0037
+
+struct gg_notify_reply80 {
+ uint32_t uin; /* numerek plus flagi w najstarszym bajcie */
+ uint32_t status; /* status danej osoby */
+ uint32_t flags; /* flagi (przeznaczenie nieznane) */
+ uint32_t remote_ip; /* adres IP bezpośrednich połączeń */
+ uint16_t remote_port; /* port bezpośrednich połączeń */
+ uint8_t image_size; /* maksymalny rozmiar obrazków w KB */
+ uint8_t unknown2; /* 0x00 */
+ uint32_t unknown3; /* 0x00000000 */
+ uint32_t descr_len; /* rozmiar opisu */
+} GG_PACKED;
+
+#define GG_SEND_MSG80 0x002d
+
+struct gg_send_msg80 {
+ uint32_t recipient;
+ uint32_t seq;
+ uint32_t msgclass;
+ uint32_t offset_plain;
+ uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_RECV_MSG80 0x002e
+
+struct gg_recv_msg80 {
+ uint32_t sender;
+ uint32_t seq;
+ uint32_t time;
+ uint32_t msgclass;
+ uint32_t offset_plain;
+ uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_DISCONNECT_ACK 0x000d
+
+#define GG_DCC7_VOICE_RETRIES 0x11 /* 17 powtorzen */
+
+#define GG_DCC7_RESERVED1 0xdeadc0de
+#define GG_DCC7_RESERVED2 0xdeadbeaf
+
+struct gg_dcc7_voice_auth {
+ uint8_t type; /* 0x00 -> wysylanie ID
+ 0x01 -> potwierdzenie ID
+ */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint32_t reserved1; /* GG_DCC7_RESERVED1 */
+ uint32_t reserved2; /* GG_DCC7_RESERVED2 */
+} GG_PACKED;
+
+struct gg_dcc7_voice_nodata { /* wyciszony mikrofon, ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac polaczenie) */
+ uint8_t type; /* 0x02 */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint32_t reserved1; /* GG_DCC7_RESERVED1 */
+ uint32_t reserved2; /* GG_DCC7_RESERVED2 */
+} GG_PACKED;
+
+struct gg_dcc7_voice_data {
+ uint8_t type; /* 0x03 */
+ uint32_t did; /* XXX: co ile zwieksza sie u nas id pakietu [uzywac 0x28] */
+ uint32_t len; /* rozmiar strukturki - 1 (sizeof(type)) */
+ uint32_t packet_id; /* numerek pakietu */
+ uint32_t datalen; /* rozmiar danych */
+ /* char data[]; */ /* ramki: albo gsm, albo speex, albo melp, albo inne. */
+} GG_PACKED;
+
+struct gg_dcc7_voice_init {
+ uint8_t type; /* 0x04 */
+ uint32_t id; /* nr kroku [0x1 - 0x5] */
+ uint32_t protocol; /* XXX: wersja protokolu (0x29, 0x2a, 0x2b) */
+ uint32_t len; /* rozmiar sizeof(protocol)+sizeof(len)+sizeof(data) = 0x08 + sizeof(data) */
+ /* char data[]; */ /* reszta danych */
+} GG_PACKED;
+
+struct gg_dcc7_voice_init_confirm {
+ uint8_t type; /* 0x05 */
+ uint32_t id; /* id tego co potwierdzamy [0x1 - 0x5] */
+} GG_PACKED;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+
+#endif /* LIBGADU_PROTOCOL_H */
diff --git a/libpurple/protocols/gg/lib/pubdir.c b/libpurple/protocols/gg/lib/pubdir.c
index fe11d251fc..04f3f0a9a5 100644
--- a/libpurple/protocols/gg/lib/pubdir.c
+++ b/libpurple/protocols/gg/lib/pubdir.c
@@ -1,8 +1,9 @@
-/* $Id: pubdir.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: pubdir.c 502 2008-01-10 23:25:17Z wojtekka $ */
/*
- * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
* Dawid Jarosz <dawjar@poczta.onet.pl>
+ * 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
@@ -15,11 +16,15 @@
*
* 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 pubdir.c
+ *
+ * \brief Obsługa katalogu publicznego
+ */
#include <ctype.h>
#include <errno.h>
@@ -29,20 +34,22 @@
#include <string.h>
#include <unistd.h>
-/*
- * gg_register3()
+#include "libgadu.h"
+
+/**
+ * Rejestruje nowego użytkownika.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
*
- * rozpoczyna rejestracj uytkownika protokoem GG 6.0. wymaga wczeniejszego
- * pobrania tokenu za pomoc funkcji gg_token().
+ * \param email Adres e-mail
+ * \param password Hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
*
- * - email - adres e-mail klienta
- * - password - haso klienta
- * - tokenid - identyfikator tokenu
- * - tokenval - warto tokenu
- * - async - poczenie asynchroniczne
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
*
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_register_free(), albo NULL jeli wystpi bd.
+ * \ingroup register
*/
struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async)
{
@@ -121,19 +128,59 @@ struct gg_http *gg_register3(const char *email, const char *password, const char
return h;
}
-/*
- * gg_unregister3()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
*
- * usuwa konto uytkownika z serwera protokoem GG 6.0
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
*
- * - uin - numerek GG
- * - password - haso klienta
- * - tokenid - identyfikator tokenu
- * - tokenval - warto tokenu
- * - async - poczenie asynchroniczne
+ * \param h Struktura połączenia
*
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_unregister_free(), albo NULL jeli wystpi bd.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup register
+ */
+int gg_register_watch_fd(struct gg_httpd *h)
+{
+ return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup register
+ */
+void gg_register_free(struct gg_http *h)
+{
+ return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Usuwa użytkownika.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param password Hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup unregister
*/
struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async)
{
@@ -145,7 +192,7 @@ struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *toke
errno = EFAULT;
return NULL;
}
-
+
__pwd = gg_saprintf("%ld", random());
__fmpwd = gg_urlencode(password);
__tokenid = gg_urlencode(tokenid);
@@ -210,22 +257,61 @@ struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *toke
return h;
}
-/*
- * gg_change_passwd4()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup unregister
+ */
+int gg_unregister_watch_fd(struct gg_httpd *h)
+{
+ return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup unregister
+ */
+void gg_unregister_free(struct gg_http *h)
+{
+ return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia hasło użytkownika.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
*
- * wysya danie zmiany hasa zgodnie z protokoem GG 6.0. wymaga
- * wczeniejszego pobrania tokenu za pomoc funkcji gg_token().
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail
+ * \param passwd Obecne hasło
+ * \param newpasswd Nowe hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
*
- * - uin - numer
- * - email - adres e-mail
- * - passwd - stare haso
- * - newpasswd - nowe haso
- * - tokenid - identyfikator tokenu
- * - tokenval - warto tokenu
- * - async - poczenie asynchroniczne
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
*
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_change_passwd_free(), albo NULL jeli wystpi bd.
+ * \ingroup passwd
*/
struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async)
{
@@ -309,19 +395,59 @@ struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *pass
return h;
}
-/*
- * gg_remind_passwd3()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup passwd
+ */
+int gg_change_passwd_watch_fd(struct gg_httpd *h)
+{
+ return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
*
- * wysya danie przypomnienia hasa e-mailem.
+ * \ingroup passwd
+ */
+void gg_change_passwd_free(struct gg_http *h)
+{
+ return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Wysyła hasło użytkownika na e-mail.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
*
- * - uin - numer
- * - email - adres e-mail taki, jak ten zapisany na serwerze
- * - async - poczenie asynchroniczne
- * - tokenid - identyfikator tokenu
- * - tokenval - warto tokenu
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail (podany przy rejestracji)
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
*
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_remind_passwd_free(), albo NULL jeli wystpi bd.
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup remind
*/
struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async)
{
@@ -396,17 +522,55 @@ struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *toke
return h;
}
-/*
- * gg_pubdir_watch_fd()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
*
- * przy asynchronicznych operacjach na katalogu publicznym naley wywoywa
- * t funkcj przy zmianach na obserwowanym deskryptorze.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * - h - struktura opisujca poczenie
+ * \ingroup remind
+ */
+int gg_remind_watch_fd(struct gg_httpd *h)
+{
+ return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup remind
+ */
+void gg_remind_free(struct gg_http *h)
+{
+ return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
*
- * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie
- * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*/
int gg_pubdir_watch_fd(struct gg_http *h)
{
@@ -447,7 +611,11 @@ int gg_pubdir_watch_fd(struct gg_http *h)
gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body);
- if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
+ if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) {
+ p->success = 1;
+ p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0);
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin);
+ } else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
p->success = 1;
if (tmp[7] == ':')
p->uin = strtol(tmp + 8, NULL, 0);
@@ -458,12 +626,10 @@ int gg_pubdir_watch_fd(struct gg_http *h)
return 0;
}
-/*
- * gg_pubdir_free()
- *
- * zwalnia pami po efektach operacji na katalogu publicznym.
+/**
+ * Zwalnia zasoby po operacji na katalogu publicznym.
*
- * - h - zwalniana struktura
+ * \param h Struktura połączenia
*/
void gg_pubdir_free(struct gg_http *h)
{
@@ -474,14 +640,17 @@ void gg_pubdir_free(struct gg_http *h)
gg_http_free(h);
}
-/*
- * gg_token()
+/**
+ * Pobiera token do autoryzacji operacji na katalogu publicznym.
*
- * pobiera z serwera token do autoryzacji zakadania konta, usuwania
- * konta i zmiany hasa.
+ * Token jest niezbędny do tworzenia nowego i usuwania użytkownika,
+ * zmiany hasła itd.
*
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_token_free(), albo NULL jeli wystpi bd.
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup token
*/
struct gg_http *gg_token(int async)
{
@@ -511,17 +680,18 @@ struct gg_http *gg_token(int async)
return h;
}
-/*
- * gg_token_watch_fd()
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
*
- * przy asynchronicznych operacjach zwizanych z tokenem naley wywoywa
- * t funkcj przy zmianach na obserwowanym deskryptorze.
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
*
- * - h - struktura opisujca poczenie
+ * \param h Struktura połączenia
*
- * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie
- * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup token
*/
int gg_token_watch_fd(struct gg_http *h)
{
@@ -547,8 +717,8 @@ int gg_token_watch_fd(struct gg_http *h)
if (h->state != GG_STATE_PARSING)
return 0;
- /* jeli h->data jest puste, to cigalimy tokenid i url do niego,
- * ale jeli co tam jest, to znaczy, e mamy drugi etap polegajcy
+ /* jeśli h->data jest puste, to ściągaliśmy tokenid i url do niego,
+ * ale jeśli coś tam jest, to znaczy, że mamy drugi etap polegający
* na pobieraniu tokenu. */
if (!h->data) {
int width, height, length;
@@ -573,8 +743,8 @@ int gg_token_watch_fd(struct gg_http *h)
return -1;
}
- /* dostalimy tokenid i wszystkie niezbdne informacje,
- * wic pobierzmy obrazek z tokenem */
+ /* dostaliśmy tokenid i wszystkie niezbędne informacje,
+ * więc pobierzmy obrazek z tokenem */
if (strncmp(url, "http://", 7)) {
path = gg_saprintf("%s?tokenid=%s", url, tokenid);
@@ -623,6 +793,8 @@ int gg_token_watch_fd(struct gg_http *h)
free(path);
free(url);
+ gg_http_free_fields(h);
+
memcpy(h, h2, sizeof(struct gg_http));
free(h2);
@@ -652,12 +824,12 @@ int gg_token_watch_fd(struct gg_http *h)
return 0;
}
-/*
- * gg_token_free()
+/**
+ * Zwalnia zasoby po operacji pobierania tokenu.
*
- * zwalnia pami po efektach pobierania tokenu.
+ * \param h Struktura połączenia
*
- * - h - zwalniana struktura
+ * \ingroup token
*/
void gg_token_free(struct gg_http *h)
{
diff --git a/libpurple/protocols/gg/lib/pubdir50.c b/libpurple/protocols/gg/lib/pubdir50.c
index 3e73133388..2430c8540b 100644
--- a/libpurple/protocols/gg/lib/pubdir50.c
+++ b/libpurple/protocols/gg/lib/pubdir50.c
@@ -1,4 +1,4 @@
-/* $Id: pubdir50.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: pubdir50.c 854 2009-10-12 21:06:28Z wojtekka $ */
/*
* (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,23 +14,32 @@
*
* 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 pubdir50.c
+ *
+ * \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x
+ */
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
-/*
- * gg_pubdir50_new()
+#include "libgadu.h"
+#include "libgadu-internal.h"
+
+/**
+ * Tworzy nowe zapytanie katalogu publicznego.
*
- * tworzy now zmienn typu gg_pubdir50_t.
+ * \param type Rodzaj zapytania
*
- * zaalokowana zmienna lub NULL w przypadku braku pamici.
+ * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku błędu.
+ *
+ * \ingroup pubdir50
*/
gg_pubdir50_t gg_pubdir50_new(int type)
{
@@ -50,17 +59,16 @@ gg_pubdir50_t gg_pubdir50_new(int type)
return res;
}
-/*
- * gg_pubdir50_add_n() // funkcja wewntrzna
- *
- * funkcja dodaje lub zastpuje istniejce pole do zapytania lub odpowiedzi.
+/**
+ * \internal Dodaje lub zastępuje pole zapytania lub odpowiedzi katalogu
+ * publicznego.
*
- * - req - wskanik opisu zapytania,
- * - num - numer wyniku (0 dla zapytania),
- * - field - nazwa pola,
- * - value - warto pola,
+ * \param req Zapytanie lub odpowiedź
+ * \param num Numer wyniku odpowiedzi (0 dla zapytania)
+ * \param field Nazwa pola
+ * \param value Wartość pola
*
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*/
static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
{
@@ -110,31 +118,31 @@ static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, cons
return 0;
}
-/*
- * gg_pubdir50_add()
+/**
+ * Dodaje pole zapytania.
*
- * funkcja dodaje pole do zapytania.
+ * \param req Zapytanie
+ * \param field Nazwa pola
+ * \param value Wartość pola
*
- * - req - wskanik opisu zapytania,
- * - field - nazwa pola,
- * - value - warto pola,
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0/-1
+ * \ingroup pubdir50
*/
int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
{
return gg_pubdir50_add_n(req, 0, field, value);
}
-/*
- * gg_pubdir50_seq_set()
+/**
+ * Ustawia numer sekwencyjny zapytania.
*
- * ustawia numer sekwencyjny zapytania.
+ * \param req Zapytanie
+ * \param seq Numer sekwencyjny
*
- * - req - zapytanie,
- * - seq - nowy numer sekwencyjny.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*
- * 0/-1.
+ * \ingroup pubdir50
*/
int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
{
@@ -151,12 +159,12 @@ int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
return 0;
}
-/*
- * gg_pubdir50_free()
+/**
+ * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego.
*
- * zwalnia pami po zapytaniu lub rezultacie szukania uytkownika.
+ * \param s Zapytanie lub odpowiedź
*
- * - s - zwalniana zmienna,
+ * \ingroup pubdir50
*/
void gg_pubdir50_free(gg_pubdir50_t s)
{
@@ -174,15 +182,15 @@ void gg_pubdir50_free(gg_pubdir50_t s)
free(s);
}
-/*
- * gg_pubdir50()
+/**
+ * Wysyła zapytanie katalogu publicznego do serwera.
*
- * wysya zapytanie katalogu publicznego do serwera.
+ * \param sess Struktura sesji
+ * \param req Zapytanie
*
- * - sess - sesja,
- * - req - zapytanie.
+ * \return Numer sekwencyjny zapytania lub 0 w przypadku błędu
*
- * numer sekwencyjny wyszukiwania lub 0 w przypadku bdu.
+ * \ingroup pubdir50
*/
uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
{
@@ -191,16 +199,16 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
char *buf, *p;
struct gg_pubdir50_request *r;
- gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
if (!sess || !req) {
- gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
errno = EFAULT;
return 0;
}
if (sess->state != GG_STATE_CONNECTED) {
- gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
errno = ENOTCONN;
return 0;
}
@@ -210,30 +218,81 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
if (req->entries[i].num)
continue;
- size += strlen(req->entries[i].field) + 1;
- size += strlen(req->entries[i].value) + 1;
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ size += strlen(req->entries[i].field) + 1;
+ size += strlen(req->entries[i].value) + 1;
+ } else {
+ char *tmp;
+
+ tmp = gg_utf8_to_cp(req->entries[i].field);
+
+ if (tmp == NULL)
+ return -1;
+
+ size += strlen(tmp) + 1;
+
+ free(tmp);
+
+ tmp = gg_utf8_to_cp(req->entries[i].value);
+
+ if (tmp == NULL)
+ return -1;
+
+ size += strlen(tmp) + 1;
+
+ free(tmp);
+ }
}
if (!(buf = malloc(size))) {
- gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
return 0;
}
+ if (!req->seq)
+ req->seq = time(NULL);
+
+ res = req->seq;
+
r = (struct gg_pubdir50_request*) buf;
- res = time(NULL);
r->type = req->type;
- r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL));
- req->seq = gg_fix32(r->seq);
+ r->seq = gg_fix32(req->seq);
for (i = 0, p = buf + 5; i < req->entries_count; i++) {
if (req->entries[i].num)
continue;
- strcpy(p, req->entries[i].field);
- p += strlen(p) + 1;
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ strcpy(p, req->entries[i].field);
+ p += strlen(p) + 1;
+
+ strcpy(p, req->entries[i].value);
+ p += strlen(p) + 1;
+ } else {
+ char *tmp;
+
+ tmp = gg_utf8_to_cp(req->entries[i].field);
+
+ if (tmp == NULL) {
+ free(buf);
+ return -1;
+ }
+
+ strcpy(p, tmp);
+ p += strlen(tmp) + 1;
+ free(tmp);
+
+ tmp = gg_utf8_to_cp(req->entries[i].value);
+
+ if (tmp == NULL) {
+ free(buf);
+ return -1;
+ }
- strcpy(p, req->entries[i].value);
- p += strlen(p) + 1;
+ strcpy(p, tmp);
+ p += strlen(tmp) + 1;
+ free(tmp);
+ }
}
if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
@@ -245,26 +304,26 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
}
/*
- * gg_pubdir50_handle_reply() // funkcja wewntrzna
- *
- * analizuje przychodzcy pakiet odpowiedzi i zapisuje wynik w struct gg_event.
+ * \internal Analizuje przychodzący pakiet odpowiedzi i zapisuje wynik
+ * w strukturze \c gg_event.
*
- * - e - opis zdarzenia
- * - packet - zawarto pakietu odpowiedzi
- * - length - dugo pakietu odpowiedzi
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param packet Pakiet odpowiedzi
+ * \param length Długość pakietu odpowiedzi
*
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
*/
-int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length)
{
const char *end = packet + length, *p;
struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet;
gg_pubdir50_t res;
int num = 0;
- gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length);
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length);
- if (!e || !packet) {
+ if (!sess || !e || !packet) {
gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
errno = EFAULT;
return -1;
@@ -299,11 +358,11 @@ int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
break;
}
- /* brak wynikw? */
+ /* brak wyników? */
if (length == 5)
return 0;
- /* pomi pocztek odpowiedzi */
+ /* pomiń początek odpowiedzi */
p = packet + 5;
while (p < end) {
@@ -311,7 +370,7 @@ int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
field = p;
- /* sprawd, czy nie mamy podziau na kolejne pole */
+ /* sprawdź, czy nie mamy podziału na kolejne pole */
if (!*field) {
num++;
field++;
@@ -320,22 +379,22 @@ int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
value = NULL;
for (p = field; p < end; p++) {
- /* jeli mamy koniec tekstu... */
+ /* jeśli mamy koniec tekstu... */
if (!*p) {
- /* ...i jeszcze nie mielimy wartoci pola to
- * wiemy, e po tym zerze jest warto... */
+ /* ...i jeszcze nie mieliśmy wartości pola to
+ * wiemy, że po tym zerze jest wartość... */
if (!value)
value = p + 1;
else
/* ...w przeciwym wypadku koniec
- * wartoci i moemy wychodzi
- * grzecznie z ptli */
+ * wartości i możemy wychodzić
+ * grzecznie z pętli */
break;
}
}
- /* sprawdmy, czy pole nie wychodzi poza pakiet, eby nie
- * mie segfaultw, jeli serwer przestanie zakacza pakietw
+ /* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie
+ * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
* przez \0 */
if (p == end) {
@@ -345,14 +404,30 @@ int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
p++;
- /* jeli dostalimy namier na nastpne wyniki, to znaczy e
- * mamy koniec wynikw i nie jest to kolejna osoba. */
+ /* jeśli dostaliśmy namier na następne wyniki, to znaczy że
+ * mamy koniec wyników i nie jest to kolejna osoba. */
if (!strcasecmp(field, "nextstart")) {
res->next = atoi(value);
num--;
} else {
- if (gg_pubdir50_add_n(res, num, field, value) == -1)
- goto failure;
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ if (gg_pubdir50_add_n(res, num, field, value) == -1)
+ goto failure;
+ } else {
+ char *tmp;
+
+ tmp = gg_cp_to_utf8(value);
+
+ if (tmp == NULL)
+ goto failure;
+
+ if (gg_pubdir50_add_n(res, num, field, tmp) == -1) {
+ free(tmp);
+ goto failure;
+ }
+
+ free(tmp);
+ }
}
}
@@ -365,16 +440,16 @@ failure:
return -1;
}
-/*
- * gg_pubdir50_get()
+/**
+ * Pobiera pole z odpowiedzi katalogu publicznego.
*
- * pobiera informacj z rezultatu wyszukiwania.
+ * \param res Odpowiedź
+ * \param num Numer wyniku odpowiedzi
+ * \param field Nazwa pola (wielkość liter nie ma znaczenia)
*
- * - res - rezultat wyszukiwania,
- * - num - numer odpowiedzi,
- * - field - nazwa pola (wielko liter nie ma znaczenia).
+ * \return Wartość pola lub \c NULL jeśli nie znaleziono
*
- * warto pola lub NULL, jeli nie znaleziono.
+ * \ingroup pubdir50
*/
const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
{
@@ -399,57 +474,61 @@ const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
return value;
}
-/*
- * gg_pubdir50_count()
+/**
+ * Zwraca liczbę wyników odpowiedzi.
*
- * zwraca ilo wynikw danego zapytania.
+ * \param res Odpowiedź
*
- * - res - odpowied
+ * \return Liczba wyników lub -1 w przypadku błędu
*
- * ilo lub -1 w przypadku bdu.
+ * \ingroup pubdir50
*/
int gg_pubdir50_count(gg_pubdir50_t res)
{
return (!res) ? -1 : res->count;
}
-/*
- * gg_pubdir50_type()
+/**
+ * Zwraca rodzaj zapytania lub odpowiedzi.
*
- * zwraca rodzaj zapytania lub odpowiedzi.
+ * \param res Zapytanie lub odpowiedź
*
- * - res - zapytanie lub odpowied
+ * \return Rodzaj lub -1 w przypadku błędu
*
- * ilo lub -1 w przypadku bdu.
+ * \ingroup pubdir50
*/
int gg_pubdir50_type(gg_pubdir50_t res)
{
return (!res) ? -1 : res->type;
}
-/*
- * gg_pubdir50_next()
+/**
+ * Zwraca numer, od którego należy rozpocząc kolejne wyszukiwanie.
*
- * zwraca numer, od ktrego naley rozpocz kolejne wyszukiwanie, jeli
- * zaley nam na kolejnych wynikach.
+ * Dłuższe odpowiedzi katalogu publicznego są wysyłane przez serwer
+ * w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeśli numer kolejnego
+ * wyszukiwania jest większy od zera, dalsze wyniki można otrzymać przez
+ * wywołanie kolejnego zapytania z określonym numerem początkowym.
*
- * - res - odpowied
+ * \param res Odpowiedź
*
- * numer lub -1 w przypadku bdu.
+ * \return Numer lub -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
*/
uin_t gg_pubdir50_next(gg_pubdir50_t res)
{
return (!res) ? (unsigned) -1 : res->next;
}
-/*
- * gg_pubdir50_seq()
+/**
+ * Zwraca numer sekwencyjny zapytania lub odpowiedzi.
*
- * zwraca numer sekwencyjny zapytania lub odpowiedzi.
+ * \param res Zapytanie lub odpowiedź
*
- * - res - zapytanie lub odpowied
+ * \return Numer sekwencyjny lub -1 w przypadku błędu
*
- * numer lub -1 w przypadku bdu.
+ * \ingroup pubdir50
*/
uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
{
diff --git a/libpurple/protocols/gg/lib/resolver.c b/libpurple/protocols/gg/lib/resolver.c
new file mode 100644
index 0000000000..59e0968a0f
--- /dev/null
+++ b/libpurple/protocols/gg/lib/resolver.c
@@ -0,0 +1,753 @@
+/* $Id$ */
+
+/*
+ * (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
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file resolver.c
+ *
+ * \brief Funkcje rozwiązywania nazw
+ */
+
+#include <sys/wait.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "libgadu.h"
+#include "resolver.h"
+#include "compat.h"
+
+/** Sposób rozwiązywania nazw serwerów */
+static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT;
+
+/** Funkcja rozpoczynająca rozwiązywanie nazwy */
+static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname);
+
+/** Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
+static void (*gg_global_resolver_cleanup)(void **private_data, int force);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+#include <pthread.h>
+
+/**
+ * \internal Funkcja pomocnicza zwalniająca zasoby po rozwiązywaniu nazwy
+ * w wątku.
+ *
+ * \param data Wskaźnik na wskaźnik bufora zaalokowanego w wątku
+ */
+static void gg_gethostbyname_cleaner(void *data)
+{
+ char **buf_ptr = (char**) data;
+
+ if (buf_ptr != NULL) {
+ free(*buf_ptr);
+ *buf_ptr = NULL;
+ }
+}
+
+#endif /* GG_CONFIG_HAVE_PTHREAD */
+
+/**
+ * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
+ *
+ * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
+ * nie, to zwykłej \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ * \param addr Wskaźnik na rezultat rozwiązywania nazwy
+ * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthread)
+{
+#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+ char *buf = NULL;
+ char *new_buf = NULL;
+ struct hostent he;
+ struct hostent *he_ptr = NULL;
+ size_t buf_len = 1024;
+ int result = -1;
+ int h_errnop;
+ int ret = 0;
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ int old_state;
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ pthread_cleanup_push(gg_gethostbyname_cleaner, &buf);
+
+ if (pthread)
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+ buf = malloc(buf_len);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ if (pthread)
+ pthread_setcancelstate(old_state, NULL);
+#endif
+
+ if (buf != NULL) {
+#ifndef sun
+ while ((ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop)) == ERANGE) {
+#else
+ while (((he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop)) == NULL) && (errno == ERANGE)) {
+#endif
+ buf_len *= 2;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ if (pthread)
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+ new_buf = realloc(buf, buf_len);
+
+ if (new_buf != NULL)
+ buf = new_buf;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ if (pthread)
+ pthread_setcancelstate(old_state, NULL);
+#endif
+
+ if (new_buf == NULL) {
+ ret = ENOMEM;
+ break;
+ }
+ }
+
+ if (ret == 0 && he_ptr != NULL) {
+ memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr));
+ result = 0;
+ }
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ if (pthread)
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+ free(buf);
+ buf = NULL;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ if (pthread)
+ pthread_setcancelstate(old_state, NULL);
+#endif
+ }
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ pthread_cleanup_pop(1);
+#endif
+
+ return result;
+#else
+ struct hostent *he;
+
+ he = gethostbyname(hostname);
+
+ if (he == NULL)
+ return -1;
+
+ memcpy(addr, he->h_addr, sizeof(struct in_addr));
+
+ return 0;
+#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
+}
+
+/**
+ * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
+ *
+ * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
+ * nie, to zwykłej \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ *
+ * \return Zaalokowana struktura \c in_addr lub NULL w przypadku błędu.
+ */
+struct in_addr *gg_gethostbyname(const char *hostname)
+{
+ struct in_addr *addr;
+
+ if (!(addr = malloc(sizeof(struct in_addr))))
+ return NULL;
+
+ if (gg_gethostbyname_real(hostname, addr, 0)) {
+ free(addr);
+ return NULL;
+ }
+ return addr;
+}
+
+/**
+ * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
+ */
+struct gg_resolver_fork_data {
+ int pid; /*< Identyfikator procesu */
+};
+
+/**
+ * \internal Rozwiązuje nazwę serwera w osobnym procesie.
+ *
+ * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania
+ * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim
+ * przeprowadzane jest rozwiązywanie nazwy. Deskryptor strony do odczytu
+ * zapisuje się w strukturze sieci i czeka na dane w postaci struktury
+ * \c in_addr. Jeśli nie znaleziono nazwy, zwracana jest \c INADDR_NONE.
+ *
+ * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor
+ * potoku
+ * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
+ * do numeru procesu potomnego rozwiązującego nazwę
+ * \param hostname Nazwa serwera do rozwiązania
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname)
+{
+ struct gg_resolver_fork_data *data = NULL;
+ struct in_addr addr;
+ int pipes[2], new_errno;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
+
+ if (fd == NULL || priv_data == NULL || hostname == NULL) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ data = malloc(sizeof(struct gg_resolver_fork_data));
+
+ if (data == NULL) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n");
+ return -1;
+ }
+
+ if (pipe(pipes) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+ free(data);
+ return -1;
+ }
+
+ data->pid = fork();
+
+ if (data->pid == -1) {
+ new_errno = errno;
+ goto cleanup;
+ }
+
+ if (data->pid == 0) {
+ close(pipes[0]);
+
+ if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+ /* W przypadku błędu gg_gethostbyname_real() zwróci -1
+ * i nie zmieni &addr. Tam jest już INADDR_NONE,
+ * więc nie musimy robić nic więcej. */
+ gg_gethostbyname_real(hostname, &addr, 0);
+ }
+
+ if (write(pipes[1], &addr, sizeof(addr)) != sizeof(addr))
+ exit(1);
+
+ exit(0);
+ }
+
+ close(pipes[1]);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data);
+
+ *fd = pipes[0];
+ *priv_data = data;
+
+ return 0;
+
+cleanup:
+ free(data);
+ close(pipes[0]);
+ close(pipes[1]);
+
+ errno = new_errno;
+
+ return -1;
+}
+
+/**
+ * \internal Usuwanie zasobów po procesie rozwiązywaniu nazwy.
+ *
+ * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
+ * zasobów sesji podczas rozwiązywania nazwy.
+ *
+ * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
+ * danych
+ * \param force Flaga usuwania zasobów przed zakończeniem działania
+ */
+void gg_resolver_fork_cleanup(void **priv_data, int force)
+{
+ struct gg_resolver_fork_data *data;
+
+ if (priv_data == NULL || *priv_data == NULL)
+ return;
+
+ data = (struct gg_resolver_fork_data*) *priv_data;
+ *priv_data = NULL;
+
+ if (force)
+ kill(data->pid, SIGKILL);
+
+ waitpid(data->pid, NULL, WNOHANG);
+
+ free(data);
+}
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+/**
+ * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
+ */
+struct gg_resolver_pthread_data {
+ pthread_t thread; /*< Identyfikator wątku */
+ char *hostname; /*< Nazwa serwera */
+ int rfd; /*< Deskryptor do odczytu */
+ int wfd; /*< Deskryptor do zapisu */
+};
+
+/**
+ * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy.
+ *
+ * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
+ * zasobów sesji podczas rozwiązywania nazwy.
+ *
+ * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
+ * danych
+ * \param force Flaga usuwania zasobów przed zakończeniem działania
+ */
+static void gg_resolver_pthread_cleanup(void **priv_data, int force)
+{
+ struct gg_resolver_pthread_data *data;
+
+ if (priv_data == NULL || *priv_data == NULL)
+ return;
+
+ data = (struct gg_resolver_pthread_data *) *priv_data;
+ *priv_data = NULL;
+
+ if (force) {
+ pthread_cancel(data->thread);
+ pthread_join(data->thread, NULL);
+ }
+
+ free(data->hostname);
+ data->hostname = NULL;
+
+ if (data->wfd != -1) {
+ close(data->wfd);
+ data->wfd = -1;
+ }
+
+ free(data);
+}
+
+/**
+ * \internal Wątek rozwiązujący nazwę.
+ *
+ * \param arg Wskaźnik na strukturę \c gg_resolver_pthread_data
+ */
+static void *gg_resolver_pthread_thread(void *arg)
+{
+ struct gg_resolver_pthread_data *data = arg;
+ struct in_addr addr;
+
+ pthread_detach(pthread_self());
+
+ if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) {
+ /* W przypadku błędu gg_gethostbyname_real() zwróci -1
+ * i nie zmieni &addr. Tam jest już INADDR_NONE,
+ * więc nie musimy robić nic więcej. */
+ gg_gethostbyname_real(data->hostname, &addr, 1);
+ }
+
+ if (write(data->wfd, &addr, sizeof(addr)) == sizeof(addr))
+ pthread_exit(NULL);
+ else
+ pthread_exit((void*) -1);
+
+ return NULL; /* żeby kompilator nie marudził */
+}
+
+/**
+ * \internal Rozwiązuje nazwę serwera w osobnym wątku.
+ *
+ * Funkcja działa analogicznie do \c gg_resolver_fork_start(), z tą różnicą,
+ * że działa na wątkach, nie procesach. Jest dostępna wyłącznie gdy podczas
+ * kompilacji włączono odpowiednią opcję.
+ *
+ * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor
+ * potoku
+ * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
+ * do prywatnych danych wątku rozwiązującego nazwę
+ * \param hostname Nazwa serwera do rozwiązania
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname)
+{
+ struct gg_resolver_pthread_data *data = NULL;
+ int pipes[2], new_errno;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
+
+ if (fd == NULL || priv_data == NULL || hostname == NULL) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ data = malloc(sizeof(struct gg_resolver_pthread_data));
+
+ if (data == NULL) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n");
+ return -1;
+ }
+
+ if (pipe(pipes) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+ free(data);
+ return -1;
+ }
+
+ data->hostname = strdup(hostname);
+
+ if (data->hostname == NULL) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n");
+ new_errno = errno;
+ goto cleanup;
+ }
+
+ data->rfd = pipes[0];
+ data->wfd = pipes[1];
+
+ if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, data)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n");
+ new_errno = errno;
+ goto cleanup;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data);
+
+ *fd = pipes[0];
+ *priv_data = data;
+
+ return 0;
+
+cleanup:
+ if (data) {
+ free(data->hostname);
+ free(data);
+ }
+
+ close(pipes[0]);
+ close(pipes[1]);
+
+ errno = new_errno;
+
+ return -1;
+}
+
+#endif /* GG_CONFIG_HAVE_PTHREAD */
+
+/**
+ * Ustawia sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)
+{
+ if (gs == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (type == GG_RESOLVER_DEFAULT) {
+ if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
+ gs->resolver_type = gg_global_resolver_type;
+ gs->resolver_start = gg_global_resolver_start;
+ gs->resolver_cleanup = gg_global_resolver_cleanup;
+ return 0;
+ }
+
+#if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT)
+ type = GG_RESOLVER_FORK;
+#else
+ type = GG_RESOLVER_PTHREAD;
+#endif
+ }
+
+ switch (type) {
+ case GG_RESOLVER_FORK:
+ gs->resolver_type = type;
+ gs->resolver_start = gg_resolver_fork_start;
+ gs->resolver_cleanup = gg_resolver_fork_cleanup;
+ return 0;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ case GG_RESOLVER_PTHREAD:
+ gs->resolver_type = type;
+ gs->resolver_start = gg_resolver_pthread_start;
+ gs->resolver_cleanup = gg_resolver_pthread_cleanup;
+ return 0;
+#endif
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs)
+{
+ if (gs == NULL) {
+ errno = EINVAL;
+ return GG_RESOLVER_INVALID;
+ }
+
+ return gs->resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+ if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ gs->resolver_type = GG_RESOLVER_CUSTOM;
+ gs->resolver_start = resolver_start;
+ gs->resolver_cleanup = resolver_cleanup;
+
+ return 0;
+}
+
+/**
+ * Ustawia sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura połączenia
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type)
+{
+ if (gh == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (type == GG_RESOLVER_DEFAULT) {
+ if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
+ gh->resolver_type = gg_global_resolver_type;
+ gh->resolver_start = gg_global_resolver_start;
+ gh->resolver_cleanup = gg_global_resolver_cleanup;
+ return 0;
+ }
+
+#if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT)
+ type = GG_RESOLVER_FORK;
+#else
+ type = GG_RESOLVER_PTHREAD;
+#endif
+ }
+
+ switch (type) {
+ case GG_RESOLVER_FORK:
+ gh->resolver_type = type;
+ gh->resolver_start = gg_resolver_fork_start;
+ gh->resolver_cleanup = gg_resolver_fork_cleanup;
+ return 0;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ case GG_RESOLVER_PTHREAD:
+ gh->resolver_type = type;
+ gh->resolver_start = gg_resolver_pthread_start;
+ gh->resolver_cleanup = gg_resolver_pthread_cleanup;
+ return 0;
+#endif
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura połączenia
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_http_get_resolver(struct gg_http *gh)
+{
+ if (gh == NULL) {
+ errno = EINVAL;
+ return GG_RESOLVER_INVALID;
+ }
+
+ return gh->resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura sesji
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+ if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ gh->resolver_type = GG_RESOLVER_CUSTOM;
+ gh->resolver_start = resolver_start;
+ gh->resolver_cleanup = resolver_cleanup;
+
+ return 0;
+}
+
+/**
+ * Ustawia sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_global_set_resolver(gg_resolver_t type)
+{
+ switch (type) {
+ case GG_RESOLVER_DEFAULT:
+ gg_global_resolver_type = type;
+ gg_global_resolver_start = NULL;
+ gg_global_resolver_cleanup = NULL;
+ return 0;
+
+ case GG_RESOLVER_FORK:
+ gg_global_resolver_type = type;
+ gg_global_resolver_start = gg_resolver_fork_start;
+ gg_global_resolver_cleanup = gg_resolver_fork_cleanup;
+ return 0;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ case GG_RESOLVER_PTHREAD:
+ gg_global_resolver_type = type;
+ gg_global_resolver_start = gg_resolver_pthread_start;
+ gg_global_resolver_cleanup = gg_resolver_pthread_cleanup;
+ return 0;
+#endif
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_global_get_resolver(void)
+{
+ return gg_global_resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco:
+ * - \c "int *fd" &mdash; wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku
+ * - \c "void **priv_data" &mdash; wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy
+ * - \c "const char *name" &mdash; nazwa serwera do rozwiązania
+ *
+ * Parametry funkcji zwalniającej zasoby wyglądają następująco:
+ * - \c "void **priv_data" &mdash; wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu
+ * - \c "int force" &mdash; flaga mówiąca o tym, że zasoby są zwalniane przed zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji.
+ *
+ * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub
+ * inny deskryptor pozwalający na co najmniej jednostronną komunikację i
+ * przekazać go w parametrze \c fd. Po zakończeniu rozwiązywania nazwy,
+ * powinien wysłać otrzymany adres IP w postaci sieciowej (big-endian) do
+ * deskryptora. Jeśli rozwiązywanie nazwy się nie powiedzie, należy wysłać
+ * \c INADDR_NONE. Następnie zostanie wywołana funkcja zwalniająca zasoby
+ * z parametrem \c force równym \c 0. Gdyby sesja została zakończona przed
+ * rozwiązaniem nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja
+ * zwalniająca zasoby zostanie wywołana z parametrem \c force równym \c 1.
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+ if (resolver_start == NULL || resolver_cleanup == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ gg_global_resolver_type = GG_RESOLVER_CUSTOM;
+ gg_global_resolver_start = resolver_start;
+ gg_global_resolver_cleanup = resolver_cleanup;
+
+ return 0;
+}
+
diff --git a/libpurple/protocols/gg/lib/resolver.h b/libpurple/protocols/gg/lib/resolver.h
new file mode 100644
index 0000000000..69e3b6ac99
--- /dev/null
+++ b/libpurple/protocols/gg/lib/resolver.h
@@ -0,0 +1,28 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef LIBGADU_RESOLVER_H
+#define LIBGADU_RESOLVER_H
+
+#include <arpa/inet.h>
+
+int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread);
+
+#endif /* LIBGADU_RESOLVER_H */
diff --git a/libpurple/protocols/gg/lib/sha1.c b/libpurple/protocols/gg/lib/sha1.c
new file mode 100644
index 0000000000..673b9b3e40
--- /dev/null
+++ b/libpurple/protocols/gg/lib/sha1.c
@@ -0,0 +1,303 @@
+/* $Id: sha1.c 632 2008-07-30 18:40:06Z darkjames $ */
+
+/*
+ * (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * Public domain SHA-1 implementation by Steve Reid <steve@edmweb.com>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file sha1.c
+ *
+ * \brief Funkcje wyznaczania skrĂłtu SHA1
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+
+/** \cond ignore */
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+
+#include <openssl/sha.h>
+
+#else
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Modified by Wojtek Kaniewski <wojtekka@toxygen.net> for compatibility
+with libgadu and OpenSSL API.
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <string.h>
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} SHA_CTX;
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]);
+static void SHA1_Init(SHA_CTX* context);
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len);
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef GG_CONFIG_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+uint32_t a, b, c, d, e;
+typedef union {
+ unsigned char c[64];
+ uint32_t l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+static unsigned char workspace[64];
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+
+/* SHA1_Init - Initialize new context */
+
+static void SHA1_Init(SHA_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len)
+{
+unsigned int i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1_Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1_Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context)
+{
+uint32_t i, j;
+unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ SHA1_Update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1_Update(context, (unsigned char *)"\0", 1);
+ }
+ SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF /* make SHA1_Transform overwrite it's own static vars */
+ SHA1_Transform(context->state, context->buffer);
+#endif
+}
+
+#endif /* GG_CONFIG_HAVE_OPENSSL */
+
+/** \endcond */
+
+/** \cond internal */
+
+/**
+ * \internal Liczy skrót SHA1 z ziarna i hasła.
+ *
+ * \param password Hasło
+ * \param seed Ziarno
+ * \param result Bufor na wynik funkcji skrĂłtu (20 bajtĂłw)
+ */
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
+{
+ SHA_CTX ctx;
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, (const unsigned char*) password, strlen(password));
+ seed = gg_fix32(seed);
+ SHA1_Update(&ctx, (uint8_t*) &seed, 4);
+
+ SHA1_Final(result, &ctx);
+}
+
+/**
+ * \internal Liczy skrĂłt SHA1 z pliku.
+ *
+ * \param fd Deskryptor pliku
+ * \param result WskaĹşnik na skrĂłt
+ *
+ * \return 0 lub -1
+ */
+int gg_file_hash_sha1(int fd, uint8_t *result)
+{
+ unsigned char buf[4096];
+ SHA_CTX ctx;
+ off_t pos, len;
+ int res;
+
+ if ((pos = lseek(fd, 0, SEEK_CUR)) == (off_t) -1)
+ return -1;
+
+ if ((len = lseek(fd, 0, SEEK_END)) == (off_t) -1)
+ return -1;
+
+ if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+ return -1;
+
+ SHA1_Init(&ctx);
+
+ if (len <= 10485760) {
+ while ((res = read(fd, buf, sizeof(buf))) > 0)
+ SHA1_Update(&ctx, buf, res);
+ } else {
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ int j;
+
+ if (lseek(fd, (len - 1048576) / 9 * i, SEEK_SET) == (off_t) - 1)
+ return -1;
+
+ for (j = 0; j < 1048576 / sizeof(buf); j++) {
+ if ((res = read(fd, buf, sizeof(buf))) != sizeof(buf)) {
+ res = -1;
+ break;
+ }
+
+ SHA1_Update(&ctx, buf, res);
+ }
+
+ if (res == -1)
+ break;
+ }
+ }
+
+ if (res == -1)
+ return -1;
+
+ SHA1_Final(result, &ctx);
+
+ if (lseek(fd, pos, SEEK_SET) == (off_t) -1)
+ return -1;
+
+ return 0;
+}
+
+/** \endcond */