summaryrefslogtreecommitdiff
path: root/protocols
diff options
context:
space:
mode:
authorGoraf <22941576+Goraf@users.noreply.github.com>2018-01-10 13:27:54 +0100
committerGoraf <22941576+Goraf@users.noreply.github.com>2018-01-19 01:39:33 +0100
commit97bb7e79c15952e7f9bf47caef4e3c09dd63c8df (patch)
treeb501cec701c8f98f738b3157f9195c090981d072 /protocols
parent7f1d5f6915ecfbb804319d33e4712455def63182 (diff)
Gadu-Gadu: complete update to libgadu-1.10.1-post
Diffstat (limited to 'protocols')
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj16
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters24
-rw-r--r--protocols/Gadu-Gadu/src/core.cpp60
-rw-r--r--protocols/Gadu-Gadu/src/gg.cpp1
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/common.cpp414
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/dcc7.cpp233
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/debug.cpp301
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/debug.h27
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/encoding.cpp277
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/encoding.h27
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/events.cpp3930
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/handlers.cpp1810
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/http.cpp12
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/internal.h76
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/libgadu.cpp215
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/libgadu.h4435
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/message.cpp664
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/message.h56
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/obsolete.cpp2
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/protocol.h533
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp15
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/resolver.cpp229
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/resolver.h3
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/session.h70
-rw-r--r--protocols/Gadu-Gadu/src/libgadu/sha1.cpp15
25 files changed, 7417 insertions, 6028 deletions
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj b/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj
index 330e678774..b290e4b0a5 100644
--- a/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj
+++ b/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj
@@ -233,15 +233,27 @@
<ClCompile Include="src\libgadu\dcc7.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\libgadu\debug.cpp">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\libgadu\encoding.cpp">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\libgadu\events.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\libgadu\handlers.cpp">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\libgadu\http.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\libgadu\libgadu.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\libgadu\message.cpp">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\libgadu\pthread.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -264,11 +276,15 @@
<ItemGroup>
<ClInclude Include="src\gg_proto.h" />
<ClInclude Include="src\libgadu\compat.h" />
+ <ClInclude Include="src\libgadu\debug.h" />
+ <ClInclude Include="src\libgadu\encoding.h" />
<ClInclude Include="src\libgadu\internal.h" />
<ClInclude Include="src\libgadu\libgadu.h" />
+ <ClInclude Include="src\libgadu\message.h" />
<ClInclude Include="src\libgadu\protocol.h" />
<ClInclude Include="src\libgadu\pthread.h" />
<ClInclude Include="src\libgadu\resolver.h" />
+ <ClInclude Include="src\libgadu\session.h" />
<ClInclude Include="src\libgadu\win32.h" />
<ClInclude Include="src\dynstuff.h" />
<ClInclude Include="src\gg.h" />
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters b/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters
index e73472cc97..ff862fd048 100644
--- a/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters
+++ b/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters
@@ -117,6 +117,18 @@
<ClCompile Include="src\stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\libgadu\debug.cpp">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="src\libgadu\encoding.cpp">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="src\libgadu\handlers.cpp">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="src\libgadu\message.cpp">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\libgadu\compat.h">
@@ -137,6 +149,9 @@
<ClInclude Include="src\libgadu\resolver.h">
<Filter>Source Files\libgadu</Filter>
</ClInclude>
+ <ClInclude Include="src\libgadu\session.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
<ClInclude Include="src\libgadu\win32.h">
<Filter>Source Files\libgadu</Filter>
</ClInclude>
@@ -155,6 +170,15 @@
<ClInclude Include="src\gg_proto.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\libgadu\debug.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="src\libgadu\encoding.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="src\libgadu\message.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="res\resource.rc">
diff --git a/protocols/Gadu-Gadu/src/core.cpp b/protocols/Gadu-Gadu/src/core.cpp
index c0492fe247..e89ab034ea 100644
--- a/protocols/Gadu-Gadu/src/core.cpp
+++ b/protocols/Gadu-Gadu/src/core.cpp
@@ -1123,36 +1123,36 @@ retry:
}
break;
- case GG_EVENT_XML_ACTION:
- if (getByte(GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) {
- wchar_t *xmlAction = mir_a2u(e->event.xml_action.data);
- wchar_t *tag = mir_a2u("events");
- HXML hXml = xmlParseString(xmlAction, nullptr, tag);
-
- if (hXml != nullptr) {
- mir_free(tag);
- tag = mir_a2u("event/type");
- HXML node = xmlGetChildByPath(hXml, tag, 0);
- char *type = node != nullptr ? mir_u2a(xmlGetText(node)) : nullptr;
-
- mir_free(tag);
- tag = mir_a2u("event/sender");
- node = xmlGetChildByPath(hXml, tag, 0);
- char *sender = node != nullptr ? mir_u2a(xmlGetText(node)) : nullptr;
- debugLogA("mainthread() (%x): XML Action type: %s.", this, type != nullptr ? type : "unknown");
- // Avatar change notify
- if (type != nullptr && !mir_strcmp(type, "28")) {
- debugLogA("mainthread() (%x): Client %s changed his avatar.", this, sender);
- requestAvatarInfo(getcontact(atoi(sender), 0, 0, nullptr), 0);
- }
- mir_free(type);
- mir_free(sender);
- xmlDestroyNode(hXml);
- }
- mir_free(tag);
- mir_free(xmlAction);
- }
- break;
+ //case GG_EVENT_XML_ACTION:
+ // if (getByte(GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) {
+ // wchar_t *xmlAction = mir_a2u(e->event.xml_action.data);
+ // wchar_t *tag = mir_a2u("events");
+ // HXML hXml = xmlParseString(xmlAction, nullptr, tag);
+
+ // if (hXml != nullptr) {
+ // mir_free(tag);
+ // tag = mir_a2u("event/type");
+ // HXML node = xmlGetChildByPath(hXml, tag, 0);
+ // char *type = node != nullptr ? mir_u2a(xmlGetText(node)) : nullptr;
+
+ // mir_free(tag);
+ // tag = mir_a2u("event/sender");
+ // node = xmlGetChildByPath(hXml, tag, 0);
+ // char *sender = node != nullptr ? mir_u2a(xmlGetText(node)) : nullptr;
+ // debugLogA("mainthread() (%x): XML Action type: %s.", this, type != nullptr ? type : "unknown");
+ // // Avatar change notify
+ // if (type != nullptr && !mir_strcmp(type, "28")) {
+ // debugLogA("mainthread() (%x): Client %s changed his avatar.", this, sender);
+ // requestAvatarInfo(getcontact(atoi(sender), 0, 0, nullptr), 0);
+ // }
+ // mir_free(type);
+ // mir_free(sender);
+ // xmlDestroyNode(hXml);
+ // }
+ // mir_free(tag);
+ // mir_free(xmlAction);
+ // }
+ // break;
case GG_EVENT_TYPING_NOTIFICATION:
{
diff --git a/protocols/Gadu-Gadu/src/gg.cpp b/protocols/Gadu-Gadu/src/gg.cpp
index b07ebcb25c..52dfd08679 100644
--- a/protocols/Gadu-Gadu/src/gg.cpp
+++ b/protocols/Gadu-Gadu/src/gg.cpp
@@ -402,7 +402,6 @@ static const ggdebug_eventype2string[] =
{GG_EVENT_DCC7_PENDING, "GG_EVENT_DCC7_PENDING"},
{GG_EVENT_XML_EVENT, "GG_EVENT_XML_EVENT"},
{GG_EVENT_DISCONNECT_ACK, "GG_EVENT_DISCONNECT_ACK"},
- {GG_EVENT_XML_ACTION, "GG_EVENT_XML_ACTION"},
{GG_EVENT_TYPING_NOTIFICATION, "GG_EVENT_TYPING_NOTIFICATION"},
{GG_EVENT_USER_DATA, "GG_EVENT_USER_DATA"},
{GG_EVENT_MULTILOGON_MSG, "GG_EVENT_MULTILOGON_MSG"},
diff --git a/protocols/Gadu-Gadu/src/libgadu/common.cpp b/protocols/Gadu-Gadu/src/libgadu/common.cpp
index b115aa0df4..07e6a2646b 100644
--- a/protocols/Gadu-Gadu/src/libgadu/common.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/common.cpp
@@ -1,9 +1,11 @@
-/* coding: UTF-8 */
/* $Id: common.c 13762 2011-08-09 12:35:16Z dezred $ */
/*
- * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
* Robert J. Woźny <speedy@ziew.org>
+ * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliński <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License Version
@@ -20,29 +22,6 @@
* USA.
*/
-/*
- * 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.
- */
-
/**
* \file common.c
*
@@ -75,111 +54,9 @@
#endif /* _WIN32 */
#include "libgadu.h"
-#include "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
-
-/**
- * \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)
- */
-static 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.
- *
- * \param level Poziom wiadomości
- * \param format Format wiadomości (zgodny z \c printf)
- *
- * \ingroup debug
- */
-void gg_debug(int level, const char *format, ...)
-{
- va_list ap;
- int old_errno = errno;
- va_start(ap, format);
- gg_debug_common(NULL, level, format, ap);
- va_end(ap);
- errno = old_errno;
-}
-
-/**
- * \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;
-}
-
-/**
- * \internal Przekazuje informację odpluskwiania związane z zawartością pamięci.
- *
- * \param sess Struktura sesji
- * \param buf Adres w pamięci
- * \param buf_length Ilość danych do wyświetlenia
- * \param format Format wiadomości (zgodny z \c printf)
- *
- * \ingroup debug
- */
-void gg_debug_dump_session(struct gg_session *sess, const void *buf, unsigned int buf_length, const char *format, ...)
-{
- va_list ap;
-
- if ((gg_debug_level & GG_DEBUG_DUMP)) {
- unsigned int i;
-
- va_start(ap, format);
- gg_debug_common(sess, GG_DEBUG_DUMP, format, ap);
- for (i = 0; i < buf_length; ++i)
- gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", ((unsigned char*) buf)[i]);
- gg_debug_session(sess, GG_DEBUG_DUMP, "\n");
- va_end(ap);
- }
-}
-
-#endif
-
-/**
* \internal Odpowiednik funkcji \c vsprintf alokujący miejsce na wynik.
*
* Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
@@ -481,7 +358,8 @@ void gg_chomp(char *line)
*/
char *gg_urlencode(const char *str)
{
- char *q, *buf, hex[] = "0123456789abcdef";
+ char *q, *buf;
+ const char hex[] = "0123456789abcdef";
const char *p;
unsigned int size = 0;
@@ -694,7 +572,7 @@ char *gg_base64_decode(const char *buf)
* \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 *gg_proxy_auth(void)
{
char *tmp, *enc, *out;
size_t tmp_size;
@@ -729,33 +607,73 @@ char *gg_proxy_auth()
/**
* \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;
-
-/**
- * \internal Tworzy tablicę pomocniczą do wyznaczania sumy kontrolnej.
- */
-static void gg_crc32_make_table(void)
+static const uint32_t gg_crc32_table[256] =
{
- uint32_t h = 1;
- unsigned int i, j;
-
- memset(gg_crc32_table, 0, sizeof(gg_crc32_table));
-
- for (i = 128; i; i >>= 1) {
- h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
-
- for (j = 0; j < 256; j += 2 * i)
- gg_crc32_table[i + j] = gg_crc32_table[j] ^ h;
- }
-
- gg_crc32_initialized = 1;
-}
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
/**
* Wyznacza sumę kontrolną CRC32.
@@ -769,10 +687,7 @@ static void gg_crc32_make_table(void)
*/
uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
{
- if (!gg_crc32_initialized)
- gg_crc32_make_table();
-
- if (!buf || len < 0)
+ if (buf == NULL || len < 0)
return crc;
crc ^= 0xffffffffL;
@@ -783,187 +698,6 @@ 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 = (char*)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] = (char)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 = (int)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 = (char*)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] = (char)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:
* c-indentation-style: k&r
diff --git a/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp b/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp
index 8131698be5..465a3cae4a 100644
--- a/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp
@@ -1,5 +1,4 @@
-/* coding: UTF-8 */
-/* $Id: dcc7.c,v 1.2 2007-07-20 23:00:49 wojtekka Exp $ */
+/* $Id$ */
/*
* (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -65,6 +64,13 @@
#include "protocol.h"
#include "resolver.h"
#include "internal.h"
+#include "debug.h"
+
+#define gg_debug_dcc(dcc, level, fmt, ...) \
+ gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt, __VA_ARGS__)
+
+#define gg_debug_dump_dcc(dcc, level, buf, len) \
+ gg_debug_dump(((dcc) != NULL) ? (dcc)->sess : NULL, level, buf, len)
/**
* \internal Dodaje połączenie bezpośrednie do sesji.
@@ -148,7 +154,7 @@ static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_
for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
if (empty) {
- if (tmp->peer_uin == uin && tmp->state != GG_STATE_WAITING_FOR_ACCEPT)
+ if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
return tmp;
} else {
if (!memcmp(&tmp->cid, &id, sizeof(id)))
@@ -168,16 +174,16 @@ static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_
*/
static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc)
{
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc);
if (dcc == NULL || dcc->sess == NULL) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n");
errno = EINVAL;
return -1;
}
if (dcc->sess->resolver_start(&dcc->fd, &dcc->resolver, GG_RELAY_HOST) == -1) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
return -1;
}
@@ -197,16 +203,16 @@ static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc)
*/
static int gg_dcc7_connect(struct gg_dcc7 *dcc)
{
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc);
if (dcc == NULL) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
+ gg_debug_dcc(dcc, 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((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
return -1;
}
@@ -229,60 +235,56 @@ static int gg_dcc7_connect(struct gg_dcc7 *dcc)
static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port)
{
struct sockaddr_in sin;
+ int sin_len = sizeof(sin);
+ int errsv;
SOCKET fd;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
if (!dcc) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons(port);
- if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin)))
- break;
+ if (bind(fd, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to port %d\n", port);
+ goto fail;
+ }
- if (port++ == 65535) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n");
- gg_sock_close(fd);
- errno = ENOENT;
- return -1;
- }
+ if (port == 0 && getsockname(fd, (struct sockaddr*) &sin, &sin_len) == -1) {
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to port %d\n", port);
+ goto fail;
}
if (listen(fd, 1)) {
- int errsv = errno;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
- gg_sock_close(fd);
- errno = errsv;
- return -1;
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
+ goto fail;
}
dcc->fd = fd;
- dcc->local_port = port;
+ dcc->local_port = ntohs(sin.sin_port);
dcc->state = GG_STATE_LISTENING;
dcc->check = GG_CHECK_READ;
dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
return 0;
+
+fail:
+ errsv = errno;
+ gg_sock_close(fd);
+ errno = errsv;
+ return -1;
}
/**
@@ -299,7 +301,7 @@ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
uint16_t local_port;
uint32_t count;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
if (dcc == NULL)
return -1;
@@ -321,18 +323,14 @@ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
else
dcc->local_addr = dcc->sess->external_addr;
- gg_debug_session((dcc)->sess, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port);
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port);
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)), external_port);
- // TODO: implement hash count
- // we MUST fill hash to recive from server request for server connection
- //snprintf((char*) pkt.hash, sizeof(pkt.hash), "0");
- count = dcc->local_addr + external_port * rand();
- mir_snprintf(pkt.hash, sizeof(pkt.hash), "%d", count);
+ snprintf((char*) pkt.hash, sizeof(pkt.hash), "%u", dcc->local_addr + external_port * rand());
return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
}
@@ -346,14 +344,14 @@ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
*/
static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
{
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
if (dcc->reverse) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
return -1;
}
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
gg_sock_close(dcc->fd);
dcc->fd = -1;
dcc->reverse = 1;
@@ -584,10 +582,10 @@ int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
{
struct gg_dcc7_accept pkt;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
if (!dcc || !dcc->sess) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
errno = EFAULT;
return -1;
}
@@ -619,10 +617,10 @@ int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
{
struct gg_dcc7_reject pkt;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
if (!dcc || !dcc->sess) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
errno = EFAULT;
return -1;
}
@@ -674,9 +672,9 @@ int gg_dcc7_abort(struct gg_dcc7 *dcc)
*
* \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)
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
{
- struct gg_dcc7_id_reply *p = (gg_dcc7_id_reply*)payload;
+ const struct gg_dcc7_id_reply *p = (gg_dcc7_id_reply*)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);
@@ -724,9 +722,9 @@ int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload
*
* \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)
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
{
- struct gg_dcc7_accept *p = (struct gg_dcc7_accept*)payload;
+ const struct gg_dcc7_accept *p = (struct gg_dcc7_accept*)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);
@@ -766,11 +764,11 @@ int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *pay
*
* \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)
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
{
- struct gg_dcc7_info *p = (gg_dcc7_info*)payload;
+ const struct gg_dcc7_info *p = (gg_dcc7_info*)payload;
struct gg_dcc7 *dcc;
- char *tmp;
+ const char *tmp;
gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
gg_debug_session(sess, GG_DEBUG_FUNCTION, "// gg_dcc7_handle_info() received address: %s, hash: %s\n", p->info, p->hash);
@@ -805,7 +803,7 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *paylo
}
if (dcc->state == GG_STATE_WAITING_FOR_INFO) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() wainting for info so send one\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() waiting for info so send one\n");
gg_dcc7_listen_and_send_info(dcc);
return 0;
}
@@ -842,7 +840,7 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *paylo
#endif
if (gg_dcc7_get_relay_addr(dcc) == -1) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n");
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -853,7 +851,7 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *paylo
gg_send_packet(dcc->sess, GG_DCC7_INFO, payload, len, NULL);
- break;
+ return 0;
default:
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
@@ -916,9 +914,9 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *paylo
*
* \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)
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
{
- struct gg_dcc7_reject *p = (struct gg_dcc7_reject*)payload;
+ const struct gg_dcc7_reject *p = (struct gg_dcc7_reject*)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);
@@ -955,9 +953,9 @@ int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *pay
*
* \return 0 jeśli się powiodło, -1 w przypadku błędu
*/
-int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
{
- struct gg_dcc7_aborted *p = (gg_dcc7_aborted*)payload;
+ const struct gg_dcc7_aborted *p = (gg_dcc7_aborted*)payload;
struct gg_dcc7 *dcc;
gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_abort(%p, %p, %p, %d)\n", sess, e, payload, len);
@@ -996,9 +994,9 @@ int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, void *payl
*
* \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)
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
{
- struct gg_dcc7_new *p = (gg_dcc7_new*)payload;
+ const struct gg_dcc7_new *p = (gg_dcc7_new*)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);
@@ -1083,10 +1081,10 @@ int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payloa
*/
static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
{
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
if (!dcc) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
errno = EINVAL;
return -1;
}
@@ -1130,16 +1128,16 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
{
struct gg_event *e;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
errno = EINVAL;
return NULL;
}
if (!(e = (gg_event*)malloc(sizeof(struct gg_event)))) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
return NULL;
}
@@ -1154,21 +1152,21 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
int one = 1;
int sin_len = sizeof(sin);
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
return e;
}
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
gg_sock_close(fd);
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
@@ -1198,7 +1196,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
int res = 0, error = 0;
int error_size = sizeof(error);
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
dcc->soft_timeout = 0;
@@ -1206,7 +1204,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
error = ETIMEDOUT;
if (error || (res = gg_getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
if (dcc->relay) {
for (dcc->relay_index++; dcc->relay_index < dcc->relay_count; dcc->relay_index++) {
@@ -1218,7 +1216,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
}
if (dcc->relay_index >= dcc->relay_count) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1238,7 +1236,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
}
}
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
+ 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;
@@ -1252,14 +1250,14 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
{
int res;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
if (!dcc->relay) {
struct gg_dcc7_welcome_p2p welcome, welcome_ok;
welcome_ok.id = dcc->cid;
if ((res = gg_sock_read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+ 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.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1267,7 +1265,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
}
if (memcmp(&welcome, &welcome_ok, sizeof(welcome))) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1279,7 +1277,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
welcome_ok.id = dcc->cid;
if ((res = gg_sock_read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+ 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.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1287,7 +1285,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
}
if (memcmp(&welcome, &welcome_ok, sizeof(welcome)) != 0) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1311,7 +1309,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
{
int res;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
if (!dcc->relay) {
struct gg_dcc7_welcome_p2p welcome;
@@ -1319,7 +1317,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
welcome.id = dcc->cid;
if ((res = gg_sock_write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno));
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1332,7 +1330,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
welcome.id = dcc->cid;
if ((res = gg_sock_write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1357,17 +1355,17 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
char buf[1024];
int chunk, res;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
e->type = GG_EVENT_DCC7_DONE;
e->event.dcc7_done.dcc7 = dcc;
return e;
}
if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
+ 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.dcc7_error = GG_ERROR_DCC7_FILE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1378,7 +1376,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
chunk = sizeof(buf);
if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
+ 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.dcc7_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1386,7 +1384,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
}
if ((res = gg_sock_write(dcc->fd, buf, res)) == -1) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
+ 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.dcc7_error = GG_ERROR_DCC7_NET;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1396,7 +1394,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
dcc->offset += res;
if (dcc->offset >= dcc->size) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
e->type = GG_EVENT_DCC7_DONE;
e->event.dcc7_done.dcc7 = dcc;
return e;
@@ -1414,17 +1412,17 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
char buf[1024];
int res, wres;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
e->type = GG_EVENT_DCC7_DONE;
e->event.dcc7_done.dcc7 = dcc;
return e;
}
if ((res = gg_sock_read(dcc->fd, buf, sizeof(buf))) < 1) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));
+ 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.dcc7_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1434,7 +1432,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
// XXX zapisywać do skutku?
if ((wres = write(dcc->file_fd, buf, res)) < res) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno));
+ 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.dcc7_error = GG_ERROR_DCC7_FILE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1444,7 +1442,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
dcc->offset += res;
if (dcc->offset >= dcc->size) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
e->type = GG_EVENT_DCC7_DONE;
e->event.dcc7_done.dcc7 = dcc;
return e;
@@ -1461,12 +1459,12 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
{
struct in_addr addr;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n");
if (gg_sock_read(dcc->fd, &addr, sizeof(addr)) < sizeof(addr) || addr.s_addr == INADDR_NONE) {
int errno_save = errno;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n");
gg_sock_close(dcc->fd);
dcc->fd = -1;
dcc->sess->resolver_cleanup(&dcc->resolver, 0);
@@ -1477,10 +1475,10 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
return e;
}
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), GG_RELAY_PORT);
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), GG_RELAY_PORT);
if ((dcc->fd = gg_connect(&addr, GG_RELAY_PORT, 1)) == -1) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1500,10 +1498,10 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
int res_size = sizeof(res);
struct gg_dcc7_relay_req pkt;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n");
if (gg_getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) != 0 || res != 0) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1517,10 +1515,11 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
pkt.type = gg_fix16(GG_DCC7_RELAY_TYPE_SERVER);
pkt.dunno1 = gg_fix16(GG_DCC7_RELAY_DUNNO1);
- gg_debug_dump_session((dcc) ? (dcc)->sess : NULL, &pkt, sizeof(pkt), "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic));
+ gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic));
+ gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, (const char*)&pkt, sizeof(pkt));
if ((res = gg_sock_write(dcc->fd, &pkt, sizeof(pkt))) != sizeof(pkt)) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n");
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1542,12 +1541,12 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
int res;
int i;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n");
if ((res = gg_sock_read(dcc->fd, buf, sizeof(buf))) < sizeof(*pkt)) {
if (res == 0)
errno = ECONNRESET;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+ 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.dcc7_error = GG_ERROR_DCC7_RELAY;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1557,7 +1556,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
pkt = (struct gg_dcc7_relay_reply*) buf;
if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY || gg_fix32(pkt->rcount) < 1 || gg_fix32(pkt->rcount > 256) || gg_fix32(pkt->len) < sizeof(*pkt) + gg_fix32(pkt->rcount) * sizeof(srv)) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n");
errno = EINVAL;
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
@@ -1565,7 +1564,8 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
return e;
}
- gg_debug_dump_session((dcc) ? (dcc)->sess : NULL, buf, res, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic));
+ gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic));
+ gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, buf, res);
free(dcc->relay_list);
@@ -1574,7 +1574,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
dcc->relay_list = (gg_dcc7_relay*)malloc(dcc->relay_count * sizeof(gg_dcc7_relay_t));
if (dcc->relay_list == NULL) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory");
dcc->relay_count = 0;
free(e);
return NULL;
@@ -1589,7 +1589,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
dcc->relay_list[i].family = srv.family;
addr.s_addr = srv.addr;
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// %s %d %d\n", inet_ntoa(addr), gg_fix16(srv.port), srv.family);
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// %s %d %d\n", inet_ntoa(addr), gg_fix16(srv.port), srv.family);
}
dcc->relay = 1;
@@ -1603,7 +1603,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
}
if (dcc->relay_index >= dcc->relay_count) {
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1615,7 +1615,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
default:
{
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
+ gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
e->type = GG_EVENT_DCC7_ERROR;
e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
e->event.dcc7_error_ex.dcc7 = dcc;
@@ -1636,7 +1636,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
*/
void gg_dcc7_free(struct gg_dcc7 *dcc)
{
- gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
+ gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
if (!dcc)
return;
@@ -1654,4 +1654,3 @@ void gg_dcc7_free(struct gg_dcc7 *dcc)
free(dcc);
}
-
diff --git a/protocols/Gadu-Gadu/src/libgadu/debug.cpp b/protocols/Gadu-Gadu/src/libgadu/debug.cpp
new file mode 100644
index 0000000000..5b7c0c6577
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/libgadu/debug.cpp
@@ -0,0 +1,301 @@
+/*
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. Woźny <speedy@ziew.org>
+ * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliński <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file debug.c
+ *
+ * \brief Funkcje odpluskwiania
+ */
+#include <sys/types.h>
+#ifdef _WIN32
+#include "win32.h"
+#endif /* _WIN32 */
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libgadu.h"
+#include "debug.h"
+
+/**
+ * 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;
+
+/**
+ * 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
+
+/**
+ * \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 != NULL)
+ (*gg_debug_handler_session)(sess, level, format, ap);
+ else if (gg_debug_handler != NULL)
+ (*gg_debug_handler)(level, format, ap);
+ else if ((gg_debug_level & level) != 0)
+ vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap);
+}
+
+
+/**
+ * \internal Przekazuje informację odpluskawiania.
+ *
+ * \param level Poziom wiadomości
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \ingroup debug
+ */
+void gg_debug(int level, const char *format, ...)
+{
+ va_list ap;
+ int old_errno = errno;
+
+ va_start(ap, format);
+ gg_debug_common(NULL, level, format, ap);
+ va_end(ap);
+ errno = old_errno;
+}
+
+/**
+ * \internal Przekazuje informację odpluskwiania związaną z sesją.
+ *
+ * \param gs 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 *gs, int level, const char *format, ...)
+{
+ va_list ap;
+ int old_errno = errno;
+
+ va_start(ap, format);
+ gg_debug_common(gs, level, format, ap);
+ va_end(ap);
+ errno = old_errno;
+}
+
+/**
+ * \internal Przekazuje zrzut bufora do odpluskwiania.
+ *
+ * \param gs Struktura sesji
+ * \param level Poziom wiadomości
+ * \param buf Bufor danych
+ * \param len Długość bufora danych
+ *
+ * \ingroup debug
+ */
+void gg_debug_dump(struct gg_session *gs, int level, const char *buf, size_t len)
+{
+ char line[80];
+ int i, j;
+
+ for (i = 0; i < len; i += 16) {
+ int ofs;
+
+ sprintf(line, "%.4x: ", i);
+ ofs = 6;
+
+ for (j = 0; j < 16; j++) {
+ if (i + j < len)
+ sprintf(line + ofs, " %02x", (unsigned char) buf[i + j]);
+ else
+ sprintf(line + ofs, " ");
+
+ ofs += 3;
+ }
+
+ sprintf(line + ofs, " ");
+ ofs += 2;
+
+ for (j = 0; j < 16; j++) {
+ unsigned char ch;
+
+ if (i + j < len) {
+ ch = buf[i + j];
+
+ if (ch < 32 || ch > 126)
+ ch = '.';
+ } else {
+ ch = ' ';
+ }
+
+ line[ofs++] = ch;
+ }
+
+ line[ofs++] = '\n';
+ line[ofs++] = 0;
+
+ gg_debug_session(gs, level, "%s", line);
+ }
+}
+
+/**
+ * \internal Zwraca ciąg z nazwą podanego stanu sesji.
+ *
+ * \param state Stan sesji.
+ *
+ * \return Ciąg z nazwą stanu
+ *
+ * \ingroup debug
+ */
+const char *gg_debug_state(enum gg_state_t state)
+{
+ switch (state) {
+#define GG_DEBUG_STATE(x) case x: return #x;
+ GG_DEBUG_STATE(GG_STATE_IDLE)
+ GG_DEBUG_STATE(GG_STATE_RESOLVING)
+ GG_DEBUG_STATE(GG_STATE_CONNECTING)
+ GG_DEBUG_STATE(GG_STATE_READING_DATA)
+ GG_DEBUG_STATE(GG_STATE_ERROR)
+ GG_DEBUG_STATE(GG_STATE_CONNECTING_HUB)
+ GG_DEBUG_STATE(GG_STATE_CONNECTING_GG)
+ GG_DEBUG_STATE(GG_STATE_READING_KEY)
+ GG_DEBUG_STATE(GG_STATE_READING_REPLY)
+ GG_DEBUG_STATE(GG_STATE_CONNECTED)
+ GG_DEBUG_STATE(GG_STATE_SENDING_QUERY)
+ GG_DEBUG_STATE(GG_STATE_READING_HEADER)
+ GG_DEBUG_STATE(GG_STATE_PARSING)
+ GG_DEBUG_STATE(GG_STATE_DONE)
+ GG_DEBUG_STATE(GG_STATE_LISTENING)
+ GG_DEBUG_STATE(GG_STATE_READING_UIN_1)
+ GG_DEBUG_STATE(GG_STATE_READING_UIN_2)
+ GG_DEBUG_STATE(GG_STATE_SENDING_ACK)
+ GG_DEBUG_STATE(GG_STATE_READING_ACK)
+ GG_DEBUG_STATE(GG_STATE_READING_REQUEST)
+ GG_DEBUG_STATE(GG_STATE_SENDING_REQUEST)
+ GG_DEBUG_STATE(GG_STATE_SENDING_FILE_INFO)
+ GG_DEBUG_STATE(GG_STATE_READING_PRE_FILE_INFO)
+ GG_DEBUG_STATE(GG_STATE_READING_FILE_INFO)
+ GG_DEBUG_STATE(GG_STATE_SENDING_FILE_ACK)
+ GG_DEBUG_STATE(GG_STATE_READING_FILE_ACK)
+ GG_DEBUG_STATE(GG_STATE_SENDING_FILE_HEADER)
+ GG_DEBUG_STATE(GG_STATE_READING_FILE_HEADER)
+ GG_DEBUG_STATE(GG_STATE_GETTING_FILE)
+ GG_DEBUG_STATE(GG_STATE_SENDING_FILE)
+ GG_DEBUG_STATE(GG_STATE_READING_VOICE_ACK)
+ GG_DEBUG_STATE(GG_STATE_READING_VOICE_HEADER)
+ GG_DEBUG_STATE(GG_STATE_READING_VOICE_SIZE)
+ GG_DEBUG_STATE(GG_STATE_READING_VOICE_DATA)
+ GG_DEBUG_STATE(GG_STATE_SENDING_VOICE_ACK)
+ GG_DEBUG_STATE(GG_STATE_SENDING_VOICE_REQUEST)
+ GG_DEBUG_STATE(GG_STATE_READING_TYPE)
+ GG_DEBUG_STATE(GG_STATE_TLS_NEGOTIATION)
+ GG_DEBUG_STATE(GG_STATE_REQUESTING_ID)
+ GG_DEBUG_STATE(GG_STATE_WAITING_FOR_ACCEPT)
+ GG_DEBUG_STATE(GG_STATE_WAITING_FOR_INFO)
+ GG_DEBUG_STATE(GG_STATE_READING_ID)
+ GG_DEBUG_STATE(GG_STATE_SENDING_ID)
+ GG_DEBUG_STATE(GG_STATE_RESOLVING_GG)
+ GG_DEBUG_STATE(GG_STATE_RESOLVING_RELAY)
+ GG_DEBUG_STATE(GG_STATE_CONNECTING_RELAY)
+ GG_DEBUG_STATE(GG_STATE_READING_RELAY)
+ GG_DEBUG_STATE(GG_STATE_DISCONNECTING)
+
+ // Celowo nie ma default, żeby kompilator wyłapał brakujące stany
+
+ }
+
+ return NULL;
+}
+
+#else
+
+#undef gg_debug_common
+void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
+{
+}
+
+#undef gg_debug
+void gg_debug(int level, const char *format, ...)
+{
+}
+
+#undef gg_debug_session
+void gg_debug_session(struct gg_session *gs, int level, const char *format, ...)
+{
+}
+
+#undef gg_debug_dump
+void gg_debug_dump(struct gg_session *gs, int level, const char *buf, int len)
+{
+}
+
+#endif
diff --git a/protocols/Gadu-Gadu/src/libgadu/debug.h b/protocols/Gadu-Gadu/src/libgadu/debug.h
new file mode 100644
index 0000000000..8c51853b3e
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/libgadu/debug.h
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 2009 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_DEBUG_H
+#define LIBGADU_DEBUG_H
+
+#include "libgadu.h"
+
+const char *gg_debug_state(enum gg_state_t state);
+void gg_debug_dump(struct gg_session *sess, int level, const char *buf, size_t len);
+
+#endif /* LIBGADU_DEBUG_H */
diff --git a/protocols/Gadu-Gadu/src/libgadu/encoding.cpp b/protocols/Gadu-Gadu/src/libgadu/encoding.cpp
new file mode 100644
index 0000000000..fa6af7eb6d
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/libgadu/encoding.cpp
@@ -0,0 +1,277 @@
+/*
+ * (C) Copyright 2008-2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ * 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.
+ */
+
+#ifdef _WIN32
+#include "win32.h"
+#endif /* _WIN32 */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "libgadu.h"
+
+/**
+ * \file encoding.c
+ *
+ * \brief Funkcje konwersji kodowania tekstu
+ */
+
+/**
+ * \internal Tablica konwersji CP1250 na Unikod.
+ */
+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 src Tekst źródłowy w CP1250.
+ * \param src_length Długość ciągu źródłowego (nigdy ujemna).
+ * \param dst_length Długość ciągu docelowego (jeśli -1, nieograniczona).
+ *
+ * \return Zaalokowany bufor z tekstem w UTF-8.
+ */
+static char *gg_encoding_convert_cp1250_utf8(const char *src, int src_length, int dst_length)
+{
+ int i, j, len;
+ char *result = NULL;
+
+ for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) {
+ uint16_t uc;
+
+ if ((unsigned char) src[i] < 0x80)
+ uc = (unsigned char) src[i];
+ else
+ uc = table_cp1250[(unsigned char) src[i] - 128];
+
+ if (uc < 0x80)
+ len += 1;
+ else if (uc < 0x800)
+ len += 2;
+ else
+ len += 3;
+ }
+
+ if ((dst_length != -1) && (len > dst_length))
+ len = dst_length;
+
+ result = (char *)malloc(len + 1);
+
+ if (result == NULL)
+ return NULL;
+
+ for (i = 0, j = 0; (src[i] != 0) && (i < src_length) && (j < len); i++) {
+ uint16_t uc;
+
+ if ((unsigned char) src[i] < 0x80)
+ uc = (unsigned char) src[i];
+ else
+ uc = table_cp1250[(unsigned char) src[i] - 128];
+
+ if (uc < 0x80)
+ result[j++] = uc;
+ else if (uc < 0x800) {
+ if (j + 1 > len)
+ break;
+ result[j++] = 0xc0 | ((uc >> 6) & 0x1f);
+ result[j++] = 0x80 | (uc & 0x3f);
+ } else {
+ if (j + 2 > len)
+ break;
+ result[j++] = 0xe0 | ((uc >> 12) & 0x1f);
+ result[j++] = 0x80 | ((uc >> 6) & 0x3f);
+ result[j++] = 0x80 | (uc & 0x3f);
+ }
+ }
+
+ result[j] = 0;
+
+ return result;
+}
+
+/**
+ * \internal Zamienia tekst kodowany UTF-8 na CP1250.
+ *
+ * \param src Tekst źródłowy w UTF-8.
+ * \param src_length Długość ciągu źródłowego (nigdy ujemna).
+ * \param dst_length Długość ciągu docelowego (jeśli -1, nieograniczona).
+ *
+ * \return Zaalokowany bufor z tekstem w CP1250.
+ */
+static char *gg_encoding_convert_utf8_cp1250(const char *src, int src_length, int dst_length)
+{
+ char *result;
+ int i, j, len, uc_left = 0;
+ uint32_t uc = 0, uc_min = 0;
+
+ for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) {
+ if ((src[i] & 0xc0) == 0xc0) {
+ len++;
+ } else if ((src[i] & 0x80) == 0x00) {
+ len++;
+ }
+ }
+
+ if ((dst_length != -1) && (len > dst_length))
+ len = dst_length;
+
+ result = (char *)malloc(len + 1);
+
+ if (result == NULL)
+ return NULL;
+
+ for (i = 0, j = 0; (src[i] != 0) && (i < src_length) && (j < len); i++) {
+ if ((unsigned char) src[i] >= 0xf5) {
+ if (uc_left != 0)
+ result[j++] = '?';
+ /* Restricted sequences */
+ result[j++] = '?';
+ uc_left = 0;
+ } else if ((src[i] & 0xf8) == 0xf0) {
+ if (uc_left != 0)
+ result[j++] = '?';
+ uc = src[i] & 0x07;
+ uc_left = 3;
+ uc_min = 0x10000;
+ } else if ((src[i] & 0xf0) == 0xe0) {
+ if (uc_left != 0)
+ result[j++] = '?';
+ uc = src[i] & 0x0f;
+ uc_left = 2;
+ uc_min = 0x800;
+ } else if ((src[i] & 0xe0) == 0xc0) {
+ if (uc_left != 0)
+ result[j++] = '?';
+ uc = src[i] & 0x1f;
+ uc_left = 1;
+ uc_min = 0x80;
+ } else if ((src[i] & 0xc0) == 0x80) {
+ if (uc_left > 0) {
+ uc <<= 6;
+ uc |= src[i] & 0x3f;
+ uc_left--;
+
+ if (uc_left == 0) {
+ int valid = 0;
+ int k;
+
+ if (uc >= uc_min) {
+ for (k = 0; k < 128; k++) {
+ if (uc == table_cp1250[k]) {
+ result[j++] = k + 128;
+ valid = 1;
+ break;
+ }
+ }
+ }
+
+ if (!valid && uc != 0xfeff) /* Byte Order Mark */
+ result[j++] = '?';
+ }
+ }
+ } else {
+ if (uc_left != 0) {
+ result[j++] = '?';
+ uc_left = 0;
+ }
+ result[j++] = src[i];
+ }
+ }
+
+ if ((uc_left != 0) && (src[i] == 0))
+ result[j++] = '?';
+
+ result[j] = 0;
+
+ return result;
+}
+
+/**
+ * \internal Zamienia kodowanie tekstu.
+ *
+ * \param src Tekst źródłowy.
+ * \param src_encoding Kodowanie tekstu źródłowego.
+ * \param dst_encoding Kodowanie tekstu docelowego.
+ * \param src_length Długość ciągu źródłowego w bajtach (nigdy ujemna).
+ * \param dst_length Długość ciągu docelowego w bajtach (jeśli -1, nieograniczona).
+ *
+ * \return Zaalokowany bufor z tekstem w kodowaniu docelowym.
+ */
+char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding, gg_encoding_t dst_encoding, int src_length, int dst_length)
+{
+ char *result;
+
+ if (src == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ // specjalny przypadek obsługiwany ekspresowo
+ if ((dst_encoding == src_encoding) && (dst_length == -1) && (src_length == -1))
+ return strdup(src);
+
+ if (src_length == -1)
+ src_length = strlen(src);
+
+ if (dst_encoding == src_encoding) {
+ int len;
+
+ if (dst_length == -1)
+ len = src_length;
+ else
+ len = (src_length < dst_length) ? src_length : dst_length;
+
+ result = (char *)malloc(len + 1);
+
+ if (result == NULL)
+ return NULL;
+
+ strncpy(result, src, len);
+ result[len] = 0;
+
+ return result;
+ }
+
+ if (dst_encoding == GG_ENCODING_CP1250 && src_encoding == GG_ENCODING_UTF8)
+ return gg_encoding_convert_utf8_cp1250(src, src_length, dst_length);
+
+ if (dst_encoding == GG_ENCODING_UTF8 && src_encoding == GG_ENCODING_CP1250)
+ return gg_encoding_convert_cp1250_utf8(src, src_length, dst_length);
+
+ errno = EINVAL;
+ return NULL;
+}
diff --git a/protocols/Gadu-Gadu/src/libgadu/encoding.h b/protocols/Gadu-Gadu/src/libgadu/encoding.h
new file mode 100644
index 0000000000..e386f48f18
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/libgadu/encoding.h
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 2008-2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ * 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_ENCODING_H
+#define LIBGADU_ENCODING_H
+
+#include "libgadu.h"
+
+char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding, gg_encoding_t dst_encoding, int src_length, int dst_length);
+
+#endif /* LIBGADU_SESSION_H */
diff --git a/protocols/Gadu-Gadu/src/libgadu/events.cpp b/protocols/Gadu-Gadu/src/libgadu/events.cpp
index 0714b2b2a2..9cfd036730 100644
--- a/protocols/Gadu-Gadu/src/libgadu/events.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/events.cpp
@@ -1,2862 +1,1070 @@
-/* coding: UTF-8 */
-/* $Id: events.c 13583 2011-04-12 12:51:18Z dezred $ */
-
-/*
- * (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
- * 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 events.c
- *
- * \brief Obsługa zdarzeń
- */
-
-#ifndef _WIN64
-#define _USE_32BIT_TIME_T
-#endif
-
-#include <sys/types.h>
-#ifdef _WIN32
-#include "win32.h"
-#else
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#endif /* _WIN32 */
-
-#include "compat.h"
-#include "libgadu.h"
-#include "protocol.h"
-#include "internal.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-#ifndef _WIN32
-#include <unistd.h>
-#include <ctype.h>
-#endif /* _WIN32 */
-#ifndef GG_CONFIG_MIRANDA
-#ifdef GG_CONFIG_HAVE_OPENSSL
-# include <openssl/err.h>
-# include <openssl/x509.h>
-#endif
-#endif
-
+/* $Id$ */
+
+/*
+ * (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
+ * 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 events.c
+ *
+ * \brief Obsługa zdarzeń
+ */
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#include <sys/types.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* _WIN32 */
+
+#include "compat.h"
+#include "libgadu.h"
+#include "protocol.h"
+#include "internal.h"
+#include "encoding.h"
+#include "debug.h"
+#include "session.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#ifndef _WIN32
+#include <unistd.h>
+#include <ctype.h>
+#endif /* _WIN32 */
+#ifndef GG_CONFIG_MIRANDA
+#ifdef GG_CONFIG_HAVE_GNUTLS
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+#endif
+#ifdef GG_CONFIG_HAVE_OPENSSL
+# include <openssl/err.h>
+# include <openssl/x509.h>
+#endif
+#endif
+
extern SSL_API sslApi;
-
-/**
- * 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.
- *
- * \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:
- case GG_EVENT_MULTILOGON_MSG:
- free(e->event.msg.message);
- free(e->event.msg.formats);
- free(e->event.msg.recipients);
- free(e->event.msg.xhtml_message);
- 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;
- }
-
- case GG_EVENT_STATUS60:
- free(e->event.status60.descr);
- break;
-
- case GG_EVENT_STATUS:
- free(e->event.status.descr);
- break;
-
- case GG_EVENT_NOTIFY_DESCR:
- free(e->event.notify_descr.notify);
- free(e->event.notify_descr.descr);
- break;
-
- case GG_EVENT_DCC_VOICE_DATA:
- free(e->event.dcc_voice_data.data);
- break;
-
- case GG_EVENT_PUBDIR50_SEARCH_REPLY:
- case GG_EVENT_PUBDIR50_READ:
- case GG_EVENT_PUBDIR50_WRITE:
- gg_pubdir50_free(e->event.pubdir50);
- break;
-
- 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;
-
- case GG_EVENT_XML_ACTION:
- free(e->event.xml_action.data);
- break;
-
- case GG_EVENT_USER_DATA:
- {
- unsigned i, j;
-
- for (i = 0; i < e->event.user_data.user_count; i++) {
- for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
- free(e->event.user_data.users[i].attrs[j].key);
- free(e->event.user_data.users[i].attrs[j].value);
- }
-
- free(e->event.user_data.users[i].attrs);
- }
-
- free(e->event.user_data.users);
-
- break;
- }
-
- case GG_EVENT_MULTILOGON_INFO:
- {
- int i;
-
- for (i = 0; i < e->event.multilogon_info.count; i++)
- free(e->event.multilogon_info.sessions[i].name);
-
- free(e->event.multilogon_info.sessions);
-
- break;
- }
- }
-
- free(e);
-}
-
-/** \cond internal */
-
-/**
- * \internal Usuwa obrazek z kolejki do wysłania.
- *
- * \param s Struktura sesji
- * \param q Struktura obrazka
- * \param freeq Flaga zwolnienia elementu kolejki
- *
- * \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)
-{
- if (!s || !q) {
- errno = EFAULT;
- return -1;
- }
-
- if (s->images == q)
- s->images = q->next;
- else {
- struct gg_image_queue *qq;
-
- for (qq = s->images; qq; qq = qq->next) {
- if (qq->next == q) {
- qq->next = q->next;
- break;
- }
- }
- }
-
- if (freeq) {
- free(q->image);
- free(q->filename);
- free(q);
- }
-
- return 0;
-}
-
-/**
- * \internal Analizuje przychodzący pakiet z obrazkiem.
- *
- * \param e Struktura zdarzenia
- * \param p Bufor z danymi
- * \param len Długość bufora
- * \param sess Struktura sesji
- * \param sender Numer nadawcy
- * \param size Rozmiar pliku (z nagłówka)
- * \param crc32 Suma kontrolna (z nagłówka)
- */
-static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender, uint32_t size, uint32_t crc32)
-{
- struct gg_image_queue *q, *qq;
-
- if (!p || !sess || !e) {
- errno = EFAULT;
- return;
- }
-
- /* znajdź dany obrazek w kolejce danej sesji */
-
- for (qq = sess->images, q = NULL; qq; qq = qq->next) {
- if (sender == qq->sender && size == qq->size && crc32 == qq->crc32) {
- q = qq;
- break;
- }
- }
-
- if (!q) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, size, crc32);
- return;
- }
-
- if (p[0] == 0x05) {
- q->done = 0;
-
- len -= sizeof(struct gg_msg_image_reply);
- p += sizeof(struct gg_msg_image_reply);
-
- 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_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
- return;
- }
-
- len -= (unsigned int)strlen(p) + 1;
- p += strlen(p) + 1;
- } else {
- len -= sizeof(struct gg_msg_image_reply);
- p += sizeof(struct gg_msg_image_reply);
- }
-
- if (q->done + len > q->size)
- len = q->size - q->done;
-
- memcpy(q->image + q->done, p, len);
- q->done += len;
-
- /* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
-
- if (q->done >= q->size) {
- e->type = GG_EVENT_IMAGE_REPLY;
- e->event.image_reply.sender = sender;
- e->event.image_reply.size = q->size;
- e->event.image_reply.crc32 = q->crc32;
- e->event.image_reply.filename = q->filename;
- e->event.image_reply.image = q->image;
-
- gg_image_queue_remove(sess, q, 0);
-
- free(q);
- }
-}
-
-/**
- * \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.
- *
- * \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_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end)
-{
- while (p < packet_end) {
- switch (*p) {
- case 0x01: /* konferencja */
- {
- struct gg_msg_recipients *m = (gg_msg_recipients*)p;
- uint32_t i, count;
-
- if (p + sizeof(*m) > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1)\n");
- goto malformed;
- }
-
- memcpy(&count, &m->count, sizeof(count));
- count = gg_fix32(count);
- p += sizeof(*m);
-
- if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1.5)\n");
- goto malformed;
- }
-
- if (e->event.msg.recipients != NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.recipients already exist\n");
- goto malformed;
- }
-
- e->event.msg.recipients = (uin_t*)malloc(count * sizeof(uin_t));
-
- if (e->event.msg.recipients == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for recipients data\n");
- goto fail;
- }
-
- memcpy(e->event.msg.recipients, p, count * sizeof(uin_t));
- p += count * sizeof(uin_t);
-
- for (i = 0; i < count; i++)
- e->event.msg.recipients[i] = gg_fix32(e->event.msg.recipients[i]);
-
- e->event.msg.recipients_count = count;
-
- break;
- }
-
- case 0x02: /* richtext */
- {
- uint16_t len;
- char *buf;
-
- if (p + 3 > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (2)\n");
- goto malformed;
- }
-
- memcpy(&len, p + 1, sizeof(uint16_t));
- len = gg_fix16(len);
-
- if (e->event.msg.formats != NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.formats already exist\n");
- goto malformed;
- }
-
- buf = (char*)malloc(len);
-
- if (buf == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for richtext data\n");
- goto fail;
- }
-
- p += 3;
-
- if (p + len > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (3)\n");
- free(buf);
- goto malformed;
- }
-
- memcpy(buf, p, len);
-
- e->event.msg.formats = buf;
- e->event.msg.formats_length = len;
-
- p += len;
-
- break;
- }
-
- case 0x04: /* image_request */
- {
- struct gg_msg_image_request *i = (gg_msg_image_request*)p;
-
- if (p + sizeof(*i) > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (3.5)\n");
- goto malformed;
- }
-
- if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (1)\n");
- goto malformed;
- }
-
- memcpy(&e->event.image_request.size, &i->size, sizeof(i->size));
- memcpy(&e->event.image_request.crc32, &i->crc32, sizeof(i->crc32));
-
- e->event.image_request.sender = sender;
- e->event.image_request.size = gg_fix32(e->event.image_request.size);
- e->event.image_request.crc32 = gg_fix32(e->event.image_request.crc32);
-
- e->type = GG_EVENT_IMAGE_REQUEST;
-
- goto handled;
- }
-
- case 0x05: /* image_reply */
- case 0x06:
- {
- struct gg_msg_image_reply *rep = (gg_msg_image_reply*)p;
- uint32_t size;
- uint32_t crc32;
-
- if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (2)\n");
- goto malformed;
- }
-
- if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (4)\n");
- goto malformed;
- }
-
- memcpy(&size, &rep->size, sizeof(size));
- memcpy(&crc32, &rep->crc32, sizeof(crc32));
- size = gg_fix32(size);
- crc32 = gg_fix32(crc32);
-
- if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
- /* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
-
- e->type = GG_EVENT_IMAGE_REPLY;
- e->event.image_reply.sender = sender;
- e->event.image_reply.size = 0;
- e->event.image_reply.crc32 = crc32;
- e->event.image_reply.filename = NULL;
- e->event.image_reply.image = NULL;
- goto handled;
-
- }
-
- gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender, size, crc32);
-
- goto handled;
- }
-
- default:
- {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() 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;
- int ctcp = 0;
-
- 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");
- ctcp = 1;
- 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.seq = gg_fix32(r->seq);
- if (ctcp)
- e->event.msg.message = (char*) strdup("\x02");
- else
- e->event.msg.message = (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
- * \param event Typ zdarzenia
- *
- * \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, int event)
-{
- 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 = event;
- 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 (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;
- }
- }
-
- if (sess->encoding == GG_ENCODING_CP1250) {
- e->event.msg.message = (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 = (char*)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 = (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;
- }
-
- 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;
-}
-
-/**
- * \internal Wysyła potwierdzenie odebrania wiadomości.
- *
- * \param sess Struktura sesji
- *
- * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
- */
-static int gg_handle_recv_msg_ack(struct gg_header *h, struct gg_session *sess)
-{
- char *packet = (char*) h + sizeof(struct gg_header);
- struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet;
- struct gg_recv_msg_ack pkt;
-
- gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg_ack(%p);\n", sess);
-
- if ((sess->protocol_features & GG_FEATURE_MSG_ACK) == 0)
- return 0;
-
- pkt.seq = gg_fix32(r->seq);
-
- return gg_send_packet(sess, GG_RECV_MSG_ACK, &pkt, sizeof(pkt), NULL);
-}
-
-/**
- * \internal Analizuje przychodzący pakiet z danymi kontaktów.
- *
- * \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
- */
-static int gg_handle_user_data(struct gg_session *sess, struct gg_event *e, void *packet, size_t len)
-{
- struct gg_user_data d;
- char *p = (char*) packet;
- char *packet_end = (char*) packet + len;
- struct gg_event_user_data_user *users;
- unsigned i, j;
- int res = 0;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "** gg_handle_user_data(%p, %p, %p, %d);\n", sess, e, packet, len);
-
- e->event.user_data.user_count = 0;
- e->event.user_data.users = NULL;
-
- if (p + sizeof(d) > packet_end)
- goto malformed;
-
- memcpy(&d, p, sizeof(d));
- p += sizeof(d);
-
- d.type = gg_fix32(d.type);
- d.user_count = gg_fix32(d.user_count);
-
- if (d.user_count > 0xffff) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (1)\n");
- goto malformed;
- }
-
- if (d.user_count > 0) {
- users = (gg_event_user_data_user*)calloc(d.user_count, sizeof(struct gg_event_user_data_user));
-
- if (users == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d*%d)\n", d.user_count, sizeof(struct gg_event_user_data_user));
- goto fail;
- }
- } else {
- users = NULL;
- }
-
- e->type = GG_EVENT_USER_DATA;
- e->event.user_data.type = d.type;
- e->event.user_data.user_count = d.user_count;
- e->event.user_data.users = users;
-
- gg_debug_session(sess, GG_DEBUG_DUMP, "type=%d, count=%d\n", d.type, d.user_count);
-
- for (i = 0; i < d.user_count; i++) {
- struct gg_user_data_user u;
- struct gg_event_user_data_attr *attrs;
-
- if (p + sizeof(u) > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (2)\n");
- goto malformed;
- }
-
- memcpy(&u, p, sizeof(u));
- p += sizeof(u);
-
- u.uin = gg_fix32(u.uin);
- u.attr_count = gg_fix32(u.attr_count);
-
- if (u.attr_count > 0xffff) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (2)\n");
- goto malformed;
- }
-
- if (u.attr_count > 0) {
- attrs = (gg_event_user_data_attr*)calloc(u.attr_count, sizeof(struct gg_event_user_data_attr));
-
- if (attrs == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d*%d)\n", u.attr_count, sizeof(struct gg_event_user_data_attr));
- goto fail;
- }
- } else {
- attrs = NULL;
- }
-
- users[i].uin = u.uin;
- users[i].attr_count = u.attr_count;
- users[i].attrs = attrs;
-
- gg_debug_session(sess, GG_DEBUG_DUMP, " uin=%d, count=%d\n", u.uin, u.attr_count);
-
- for (j = 0; j < u.attr_count; j++) {
- uint32_t key_size;
- uint32_t attr_type;
- uint32_t value_size;
- char *key;
- char *value;
-
- if (p + sizeof(key_size) > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (3)\n");
- goto malformed;
- }
-
- memcpy(&key_size, p, sizeof(key_size));
- p += sizeof(key_size);
-
- key_size = gg_fix32(key_size);
-
- if (key_size > 0xffff || p + key_size > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (3)\n");
- goto malformed;
- }
-
- key = (char*)malloc(key_size + 1);
-
- if (key == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d)\n", key_size + 1);
- goto fail;
- }
-
- memcpy(key, p, key_size);
- p += key_size;
-
- key[key_size] = 0;
-
- attrs[j].key = key;
-
- if (p + sizeof(attr_type) + sizeof(value_size) > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (4)\n");
- goto malformed;
- }
-
- memcpy(&attr_type, p, sizeof(attr_type));
- p += sizeof(attr_type);
- memcpy(&value_size, p, sizeof(value_size));
- p += sizeof(value_size);
-
- attrs[j].type = gg_fix32(attr_type);
- value_size = gg_fix32(value_size);
-
- if (value_size > 0xffff || p + value_size > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (5)\n");
- goto malformed;
- }
-
- value = (char*)malloc(value_size + 1);
-
- if (value == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d)\n", value_size + 1);
- goto fail;
- }
-
- memcpy(value, p, value_size);
- p += value_size;
-
- value[value_size] = 0;
-
- attrs[j].value = value;
-
- gg_debug_session(sess, GG_DEBUG_DUMP, " key=\"%s\", type=%d, value=\"%s\"\n", key, attr_type, value);
- }
- }
-
- return 0;
-
-fail:
- res = -1;
-
-malformed:
- e->type = GG_EVENT_NONE;
-
- for (i = 0; i < e->event.user_data.user_count; i++) {
- for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
- free(e->event.user_data.users[i].attrs[j].key);
- free(e->event.user_data.users[i].attrs[j].value);
- }
-
- free(e->event.user_data.users[i].attrs);
- }
-
- free(e->event.user_data.users);
-
- return res;
-}
-
-/**
- * \internal Analizuje przychodzący pakiet z listą sesji multilogowania.
- *
- * \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
- */
-static int gg_handle_multilogon_info(struct gg_session *sess, struct gg_event *e, void *packet, size_t len)
-{
- char *packet_end = (char*) packet + len;
- struct gg_multilogon_info *info = (struct gg_multilogon_info*) packet;
- char *p = (char*) packet + sizeof(*info);
- struct gg_multilogon_session *sessions = NULL;
- size_t count;
- size_t i;
- int res = 0;
-
- gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_multilogon_info(%p, %p, %p, %d);\n", sess, e, packet, len);
-
- count = gg_fix32(info->count);
-
- if (count > 0xffff) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (1)\n");
- goto malformed;
- }
-
- sessions = (gg_multilogon_session*)calloc(count, sizeof(struct gg_multilogon_session));
-
- if (sessions == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d*%d)\n", count, sizeof(struct gg_multilogon_session));
- return -1;
- }
-
- e->type = GG_EVENT_MULTILOGON_INFO;
- e->event.multilogon_info.count = (int)count;
- e->event.multilogon_info.sessions = sessions;
-
- for (i = 0; i < count; i++) {
- struct gg_multilogon_info_item item;
- size_t name_size;
-
- if (p + sizeof(item) > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (2)\n");
- goto malformed;
- }
-
- memcpy(&item, p, sizeof(item));
-
- sessions[i].id = item.conn_id;
- sessions[i].remote_addr = item.addr;
- sessions[i].status_flags = gg_fix32(item.flags);
- sessions[i].protocol_features = gg_fix32(item.features);
- sessions[i].logon_time = gg_fix32(item.logon_time);
-
- p += sizeof(item);
-
- name_size = gg_fix32(item.name_size);
-
- if (name_size > 0xffff || p + name_size > packet_end) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (3)\n");
- goto malformed;
- }
-
- sessions[i].name = (char*)malloc(name_size + 1);
-
- if (sessions[i].name == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d)\n", name_size);
- goto fail;
- }
-
- memcpy(sessions[i].name, p, name_size);
- sessions[i].name[name_size] = 0;
-
- p += name_size;
- }
-
- return 0;
-
-fail:
- res = -1;
-
-malformed:
- e->type = GG_EVENT_NONE;
-
- for (i = 0; i < (size_t)e->event.multilogon_info.count; i++)
- free(e->event.multilogon_info.sessions[i].name);
-
- free(e->event.multilogon_info.sessions);
-
- return res;
-}
-
-/**
- * \internal Odbiera pakiet od serwera.
- *
- * Analizuje pakiet i wypełnia strukturę zdarzenia.
- *
- * \param sess Struktura sesji
- * \param e Struktura zdarzenia
- *
- * \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_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
-
- if (!sess) {
- errno = EFAULT;
- return -1;
- }
-
- if (!(h = (gg_header*)gg_recv_packet(sess))) {
- 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) != -1)
- gg_handle_recv_msg_ack(h, sess);
- else
- goto fail;
- }
-
- break;
- }
-
- case GG_RECV_MSG80:
- {
- if (h->length >= sizeof(struct gg_recv_msg80)) {
- if (gg_handle_recv_msg80(h, e, sess, GG_EVENT_MSG) != -1)
- gg_handle_recv_msg_ack(h, sess);
- else
- goto fail;
- }
-
- break;
- }
-
- case GG_RECV_OWN_MSG:
- {
- if (h->length >= sizeof(struct gg_recv_msg80)) {
- if (gg_handle_recv_msg80(h, e, sess, GG_EVENT_MULTILOGON_MSG) == -1)
- goto fail;
- }
-
- break;
- }
-
- case GG_NOTIFY_REPLY:
- {
- struct gg_notify_reply *n = (gg_notify_reply*)p;
- unsigned int count, i;
- char *tmp;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
-
- if (h->length < sizeof(*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 = (gg_notify_reply*)malloc(sizeof(*n) * 2))) {
- 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_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 = (char*)malloc(count + 1))) {
- 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 = (gg_notify_reply*)malloc(h->length + 2 * sizeof(*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_port = gg_fix16(e->event.notify[i].remote_port);
- e->event.notify[i].version = gg_fix32(e->event.notify[i].version);
- }
- }
-
- break;
- }
-
- case GG_STATUS:
- {
- struct gg_status *s = (gg_status*)p;
-
- 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;
- memcpy(&e->event.status, p, sizeof(*s));
- e->event.status.uin = gg_fix32(e->event.status.uin);
- e->event.status.status = gg_fix32(e->event.status.status);
- if (h->length > sizeof(*s)) {
- int len = h->length - sizeof(*s);
- char *buf = (char*)malloc(len + 1);
- if (buf) {
- memcpy(buf, p + sizeof(*s), len);
- buf[len] = 0;
- }
- e->event.status.descr = buf;
- } else
- e->event.status.descr = NULL;
- }
-
- break;
- }
-
- case GG_NOTIFY_REPLY77:
- case GG_NOTIFY_REPLY80BETA:
- {
- struct gg_notify_reply77 *n = (gg_notify_reply77*)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 = (gg_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 = (char*)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 = (gg_notify_reply77*)((char*)n + sizeof(struct gg_notify_reply77) + descr_len + 1);
- } else {
- length = 0;
- }
-
- } else {
- length -= sizeof(struct gg_notify_reply77);
- n = (gg_notify_reply77*)((char*)n + sizeof(struct gg_notify_reply77));
- }
-
- if (!(tmp = (char*)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 = (gg_event_notify60*)tmp;
- e->event.notify60[++i].uin = 0;
- }
-
- break;
- }
-
- case GG_STATUS77:
- case GG_STATUS80BETA:
- {
- struct gg_status77 *s = (gg_status77*)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 = (char*)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 = (gg_notify_reply60*)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 = (gg_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_reply60)) {
- 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 & 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_reply60));
-
- if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
- if (!(e->event.notify60[i].descr = (char*)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(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len);
- e->event.notify60[i].descr[descr_len] = 0;
-
- /* XXX czas */
-
- length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
- n = (gg_notify_reply60*)((char*)n + sizeof(struct gg_notify_reply60) + descr_len + 1);
- } else {
- length = 0;
- }
-
- } else {
- length -= sizeof(struct gg_notify_reply60);
- n = (gg_notify_reply60*)((char*)n + sizeof(struct gg_notify_reply60));
- }
-
- if (!(tmp = (char*)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 = (gg_event_notify60*)tmp;
- e->event.notify60[++i].uin = 0;
- }
-
- break;
- }
-
- case GG_STATUS60:
- {
- struct gg_status60 *s = (gg_status60*)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 & 0x08000000)
- e->event.status60.version |= GG_ERA_OMNIX_MASK;
-
- if (h->length > sizeof(*s)) {
- int len = h->length - sizeof(*s);
- char *buf = (char*)malloc(len + 1);
-
- if (buf) {
- memcpy(buf, (char*) p + sizeof(*s), len);
- buf[len] = 0;
- }
-
- 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_STATUS80:
- {
- struct gg_notify_reply80 *s = (gg_notify_reply80*)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 = (char*)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 = (gg_notify_reply80*)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 = (gg_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 = (gg_notify_reply80*)((char*)n + sizeof(struct gg_notify_reply80));
-
- if (descr_len) {
- if (length >= descr_len) {
- /* XXX, GG_S_D(n->status) */
- char *descr;
-
- if (!(descr = (char*)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 = (gg_notify_reply80*)((char*)n + descr_len);
- } else
- length = 0;
- }
-
- if (!(tmp = (char*)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 = (gg_event_notify60*)tmp;
- e->event.notify60[++i].uin = 0;
- }
- break;
- }
-
- case GG_SEND_MSG_ACK:
- {
- struct gg_send_msg_ack *s = (gg_send_msg_ack*)p;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
-
- if (h->length < sizeof(*s))
- break;
-
- e->type = GG_EVENT_ACK;
- e->event.ack.status = gg_fix32(s->status);
- e->event.ack.recipient = gg_fix32(s->recipient);
- e->event.ack.seq = gg_fix32(s->seq);
-
- break;
- }
-
- case GG_PONG:
- {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
-
- e->type = GG_EVENT_PONG;
- sess->last_pong = (int)time(NULL);
-
- break;
- }
-
- case GG_DISCONNECTING:
- {
- 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_XML_ACTION:
- {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML action\n");
- e->type = GG_EVENT_XML_ACTION;
- if (!(e->event.xml_action.data = (char *) malloc(h->length + 1))) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML action data\n");
- goto fail;
- }
- memcpy(e->event.xml_action.data, p, h->length);
- e->event.xml_action.data[h->length] = 0;
- break;
- }
-
- case GG_PUBDIR50_REPLY:
- {
- 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
-
- if (h->length < 1)
- break;
-
- /* 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)
- break;
-
- p[0] = GG_USERLIST_PUT_REPLY;
- }
-
- if (h->length > 1) {
- char *tmp;
- unsigned int len = (sess->userlist_reply) ? (unsigned int)strlen(sess->userlist_reply) : 0;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
-
- if (!(tmp = (char*)realloc(sess->userlist_reply, len + h->length))) {
- 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;
- }
-
- sess->userlist_reply = tmp;
- sess->userlist_reply[len + h->length - 1] = 0;
- memcpy(sess->userlist_reply + len, p + 1, h->length - 1);
- }
-
- if (p[0] == GG_USERLIST_GET_MORE_REPLY)
- break;
-
- e->type = GG_EVENT_USERLIST;
- e->event.userlist.type = p[0];
- e->event.userlist.reply = sess->userlist_reply;
- sess->userlist_reply = NULL;
-
- 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_ABORT:
- {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 abort\n");
-
- if (h->length < sizeof(struct gg_dcc7_aborted))
- break;
-
- if (gg_dcc7_handle_abort(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;
- }
-
- case GG_USER_DATA:
- {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received user data\n");
-
- if (h->length < sizeof(struct gg_user_data))
- break;
-
- if (gg_handle_user_data(sess, e, p, h->length) == -1)
- goto fail;
-
- break;
- }
-
- case GG_TYPING_NOTIFICATION:
- {
- struct gg_typing_notification *n = (struct gg_typing_notification*)p;
- uin_t uin;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received typing notification\n");
-
- if (h->length < sizeof(*n))
- break;
-
- memcpy(&uin, &n->uin, sizeof(uin_t));
-
- e->type = GG_EVENT_TYPING_NOTIFICATION;
- e->event.typing_notification.uin = gg_fix32(uin);
- e->event.typing_notification.length = gg_fix16(n->length);
-
- break;
- }
-
- case GG_MULTILOGON_INFO:
- {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received multilogon info\n");
-
- if (h->length < sizeof(struct gg_multilogon_info))
- break;
-
- if (gg_handle_multilogon_info(sess, e, p, h->length) == -1)
- goto fail;
-
- break;
- }
-
- default:
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
- }
-
- free(h);
- return 0;
-
-fail:
- free(h);
- return -1;
-}
-
-/** \endcond */
-
-/**
- * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
- *
- * 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 sess Struktura sesji
- *
- * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
- *
- * \ingroup events
- */
-struct gg_event *gg_watch_fd(struct gg_session *sess)
-{
- struct gg_event *e;
- int res = 0;
- int port = 0;
- int errno2 = 0;
-
- gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
-
- if (!sess) {
- errno = EFAULT;
- return NULL;
- }
-
- if (!(e = (gg_event*)calloc(1, sizeof(*e)))) {
- 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 = gg_sock_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;
- }
-
- res = 0;
- }
-
- switch (sess->state) {
- case GG_STATE_RESOLVING:
- {
- struct in_addr addr;
- int failed = 0;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
-
- if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
- failed = 1;
- errno2 = errno;
- }
-
- gg_sock_close(sess->fd);
- sess->fd = -1;
-
- sess->resolver_cleanup(&sess->resolver, 0);
-
- if (failed) {
- errno = errno2;
- goto fail_resolving;
- }
-
- /* 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
- * bezpośredniego połączenia, jeśli hub leży)
- * z resolvera. */
- if (sess->proxy_addr && sess->proxy_port)
- port = sess->proxy_port;
- else {
- sess->server_addr = sess->hub_addr = addr.s_addr;
- port = GG_APPMSG_PORT;
- }
-
- 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) {
- /* 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;
- }
-
- /* jeśli podano serwer i łączmy się przez proxy,
- * jest to bezpośrednie połączenie, inaczej jest
- * do huba. */
-
- 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;
-
- break;
- }
-
- case GG_STATE_CONNECTING_HUB:
- {
- char buf[1024], *client, *auth;
- int res = 0;
- int res_size = sizeof(res);
- const char *host;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
-
- /* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
- * przypadkiem jakiś błąd. */
- if (sess->async && (gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
- 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));
-
- goto fail_connecting;
- }
-
- 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
- goto fail_connecting;
- }
-
- if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port)
- host = "http://" GG_APPMSG_HOST;
- else
- host = "";
-
- auth = gg_proxy_auth();
-
-#ifdef GG_CONFIG_MIRANDA
- if (sess->tls) {
- snprintf(buf, sizeof(buf) - 1,
- "GET %s/appsvc/appmsg_ver11.asp?tls=1&fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Host: " GG_APPMSG_HOST "\r\n"
- "%s"
- "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
- } else
-#elif GG_CONFIG_HAVE_OPENSSL
- if (sess->ssl != NULL) {
- snprintf(buf, sizeof(buf) - 1,
- "GET %s/appsvc/appmsg_ver11.asp?tls=1&fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
- "Connection: close\r\n"
- "Host: " GG_APPMSG_HOST "\r\n"
- "%s"
- "\r\n", host, sess->uin, sess->last_sysmsg, client, (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. */
- if (sess->client_version) {
- free(sess->client_version);
- sess->client_version = NULL;
- }
-
- 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 (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
- 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;
- sess->state = GG_STATE_IDLE;
- gg_sock_close(sess->fd);
- sess->fd = -1;
- break;
- }
-
- sess->state = GG_STATE_READING_DATA;
- sess->check = GG_CHECK_READ;
- sess->timeout = GG_DEFAULT_TIMEOUT;
-
- break;
- }
-
- case GG_STATE_READING_DATA:
- {
- char buf[1024], *tmp, *host;
- int port = GG_DEFAULT_PORT;
- struct in_addr addr;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
-
- /* czytamy linię z gniazda i obcinamy \r\n. */
- gg_read_line(sess->fd, buf, sizeof(buf) - 1);
- gg_chomp(buf);
- gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
-
- /* 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ę nagłówka. */
- while (strcmp(buf, "\r\n") && strcmp(buf, ""))
- gg_read_line(sess->fd, buf, sizeof(buf) - 1);
-
- /* czytamy pierwszą linię danych. */
- gg_read_line(sess->fd, buf, sizeof(buf) - 1);
- gg_chomp(buf);
-
- /* 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 = (char*)realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
- break;
- }
-
- sysmsg_buf = foo;
-
- if (!len)
- strcpy(sysmsg_buf, tmp);
- else
- strcat(sysmsg_buf, tmp);
-
- len += (int)strlen(tmp);
- }
-
- e->type = GG_EVENT_MSG;
- e->event.msg.msgclass = atoi(buf);
- e->event.msg.sender = 0;
- e->event.msg.message = (char*) sysmsg_buf;
- }
-
- gg_sock_close(sess->fd);
-
- gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
-
- /* analizujemy otrzymane dane. */
- tmp = buf;
-
- while (*tmp && *tmp != ' ')
- tmp++;
- while (*tmp && *tmp == ' ')
- tmp++;
- while (*tmp && *tmp != ' ')
- tmp++;
- while (*tmp && *tmp == ' ')
- tmp++;
- host = tmp;
- while (*tmp && *tmp != ' ')
- tmp++;
- *tmp = 0;
-
- if ((tmp = strchr(host, ':'))) {
- *tmp = 0;
- 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) {
- /* jeśli mamy proxy, łączymy się z nim. */
- if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
- /* 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;
-
- /* Jeśli podano nazwę, nie adres serwera... */
- if (sess->server_addr == INADDR_NONE) {
- if (sess->resolver_start(&sess->fd, &sess->resolver, host) == -1) {
- gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
- goto fail_resolving;
- }
-
- sess->state = GG_STATE_RESOLVING_GG;
- sess->check = GG_CHECK_READ;
- sess->timeout = GG_DEFAULT_TIMEOUT;
- break;
- }
-
- /* łączymy się z właściwym serwerem. */
- if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
- 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 wyszło? próbujemy portu 443. */
- if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
- /* ostatnia deska ratunku zawiodła?
- * w takim razie zwijamy manatki. */
- 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;
- }
-
- case GG_STATE_RESOLVING_GG:
- {
- struct in_addr addr;
- int failed = 0;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING_GG\n");
-
- if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
- failed = 1;
- errno2 = errno;
- }
-
- gg_sock_close(sess->fd);
- sess->fd = -1;
-
- sess->resolver_cleanup(&sess->resolver, 0);
-
- if (failed) {
- errno = errno2;
- goto fail_resolving;
- }
-
- sess->server_addr = addr.s_addr;
-
- /* łączymy się z właściwym serwerem. */
- if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
- 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 wyszło? próbujemy portu 443. */
- if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
- /* ostatnia deska ratunku zawiodła?
- * w takim razie zwijamy manatki. */
- 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;
- }
-
- case GG_STATE_CONNECTING_GG:
- {
- int res = 0;
- int res_size = sizeof(res);
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
-
- sess->soft_timeout = 0;
-
- /* jeśli wystąpił błąd podczas łączenia się... */
- if (sess->async && (sess->timeout == 0 || gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
- /* 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
- goto fail_connecting;
- }
-
- gg_sock_close(sess->fd);
- sess->fd = -1;
-
-#ifdef ETIMEDOUT
- if (sess->timeout == 0)
- errno = ETIMEDOUT;
-#endif
-
-#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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
- goto fail_connecting;
- }
-#endif
-
- 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;
-
- /* próbujemy na port 443. */
- if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
- 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
-
- if (gg_proxy_http_only)
- sess->proxy_port = 0;
-
- /* 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;
-
- if (sess->server_addr)
- addr.s_addr = sess->server_addr;
- else
- addr.s_addr = sess->hub_addr;
-
- snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
-
- 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 (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
- 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_session(sess, GG_DEBUG_MISC, "// %s", auth);
- if (gg_sock_write(sess->fd, auth, (int)strlen(auth)) < (signed)strlen(auth)) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
- free(auth);
- goto fail_connecting;
- }
-
- free(auth);
- }
-
- if (gg_sock_write(sess->fd, "\r\n", 2) < 2) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
- goto fail_connecting;
- }
- }
-
-#ifdef GG_CONFIG_MIRANDA
- if (sess->tls) {
- sess->state = GG_STATE_TLS_NEGOTIATION;
- sess->check = GG_CHECK_WRITE;
- sess->timeout = GG_DEFAULT_TIMEOUT;
-
- break;
- }
-#elif GG_CONFIG_HAVE_OPENSSL
- if (sess->ssl != NULL) {
- SSL_set_fd(sess->ssl, (int)sess->fd);
-
- sess->state = GG_STATE_TLS_NEGOTIATION;
- sess->check = GG_CHECK_WRITE;
- sess->timeout = GG_DEFAULT_TIMEOUT;
-
- break;
- }
-#endif
-
- sess->state = GG_STATE_READING_KEY;
- sess->check = GG_CHECK_READ;
- sess->timeout = GG_DEFAULT_TIMEOUT;
-
- break;
- }
-
-#ifdef GG_CONFIG_MIRANDA
- case GG_STATE_TLS_NEGOTIATION:
- {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
-
- sess->ssl = sslApi.connect(sess->fd, 0, 0);
-
- if (sess->ssl == NULL) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation failed\n");
-
- e->type = GG_EVENT_CONN_FAILED;
- e->event.failure = GG_FAILURE_TLS;
- sess->state = GG_STATE_IDLE;
- gg_sock_close(sess->fd);
- sess->fd = -1;
- break;
- }
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded\n");
-
- sess->state = GG_STATE_READING_KEY;
- sess->check = GG_CHECK_READ;
- sess->timeout = GG_DEFAULT_TIMEOUT;
-
- break;
- }
-#elif GG_CONFIG_HAVE_OPENSSL
- case GG_STATE_TLS_NEGOTIATION:
- {
- int res;
- X509 *peer;
-
- 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_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;
- sess->state = GG_STATE_IDLE;
- gg_sock_close(sess->fd);
- sess->fd = -1;
- break;
- }
-
- if (err == SSL_ERROR_WANT_READ) {
- 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;
- sess->timeout = GG_DEFAULT_TIMEOUT;
-
- break;
- } else if (err == SSL_ERROR_WANT_WRITE) {
- 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;
- sess->timeout = GG_DEFAULT_TIMEOUT;
-
- break;
- } else {
- char buf[256];
-
- ERR_error_string_n(ERR_get_error(), buf, sizeof(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;
- gg_sock_close(sess->fd);
- sess->fd = -1;
- break;
- }
- }
-
- 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_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n");
- else {
- char buf[256];
-
- X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(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_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
- }
-
- sess->state = GG_STATE_READING_KEY;
- sess->check = GG_CHECK_READ;
- sess->timeout = GG_DEFAULT_TIMEOUT;
-
- break;
- }
-#endif
-
- case GG_STATE_READING_KEY:
- {
- struct gg_header *h;
- struct gg_welcome *w;
- unsigned char *password = (unsigned char*) sess->password;
- int ret;
- uint8_t login_hash[64];
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
-
- memset(login_hash, 0, sizeof(login_hash));
-
- /* 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_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_session(sess, GG_DEBUG_MISC, "// %s\n", buf);
- }
-
- /* XXX niech czeka jeszcze raz w tej samej
- * fazie. głupio, ale działa. */
- sess->proxy_port = 0;
-
- break;
- }
-
- /* czytaj pierwszy pakiet. */
- if (!(h = (gg_header*)gg_recv_packet(sess))) {
- 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;
- errno2 = errno;
- gg_sock_close(sess->fd);
- errno = errno2;
- sess->fd = -1;
- break;
- }
-
- if (h->type != GG_WELCOME) {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
- free(h);
- gg_sock_close(sess->fd);
- sess->fd = -1;
- errno = EINVAL;
- e->type = GG_EVENT_CONN_FAILED;
- e->event.failure = GG_FAILURE_INVALID;
- sess->state = GG_STATE_IDLE;
- break;
- }
-
- w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header));
- w->key = gg_fix32(w->key);
-
- 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];
- gg_login_hash_sha1((char*) password, w->key, login_hash);
- bin2hex(login_hash, 20, tmp);
- 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;
-
- if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
- struct sockaddr_in sin;
- int sin_len = sizeof(sin);
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
-
- if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) {
- 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
- sess->client_addr = 0;
- }
- } else
- sess->client_addr = gg_dcc_ip;
-
- if (sess->protocol_version >= 0x2e) {
- struct gg_login80 l;
- const char *version = (sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION;
- uint32_t tmp_version_len = gg_fix32((uint32_t)strlen(GG8_VERSION) + (uint32_t)strlen(version));
- uint32_t tmp_descr_len = gg_fix32((sess->initial_descr) ? (uint32_t)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(sess->status_flags);
- 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), version, strlen(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((uint16_t)((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((uint16_t)((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_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
- errno2 = errno;
- gg_sock_close(sess->fd);
- errno = errno2;
- sess->fd = -1;
- e->type = GG_EVENT_CONN_FAILED;
- e->event.failure = GG_FAILURE_WRITING;
- sess->state = GG_STATE_IDLE;
- break;
- }
-
- sess->state = GG_STATE_READING_REPLY;
- sess->check = GG_CHECK_READ;
-
- break;
- }
-
- case GG_STATE_READING_REPLY:
- {
- struct gg_header *h;
-
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
-
- if (!(h = (gg_header*)gg_recv_packet(sess))) {
- 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;
- errno2 = errno;
- gg_sock_close(sess->fd);
- errno = errno2;
- sess->fd = -1;
- break;
- }
-
- 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);
- break;
- }
-
- if (h->type == GG_LOGIN_FAILED || h->type == GG_LOGIN80_FAILED) {
- 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_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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
- e->event.failure = GG_FAILURE_INVALID;
- errno = EINVAL;
- }
-
- e->type = GG_EVENT_CONN_FAILED;
- sess->state = GG_STATE_IDLE;
- errno2 = errno;
- gg_sock_close(sess->fd);
- errno = errno2;
- sess->fd = -1;
- free(h);
-
- break;
- }
-
- case GG_STATE_CONNECTED:
- {
- gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
-
- sess->last_event = (int)time(NULL);
-
- if ((res = gg_watch_fd_connected(sess, e)) == -1) {
-
- 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;
- res = 0;
- } else
- res = -1;
- }
-
- sess->check = GG_CHECK_READ;
-
- break;
- }
- }
-
-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;
- gg_sock_close(sess->fd);
- errno = errno2;
- sess->fd = -1;
- }
- e->type = GG_EVENT_CONN_FAILED;
- e->event.failure = GG_FAILURE_CONNECTING;
- sess->state = GG_STATE_IDLE;
- goto done;
-
-fail_resolving:
- e->type = GG_EVENT_CONN_FAILED;
- 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;
-}
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */
+
+/**
+ * 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.
+ *
+ * \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:
+ case GG_EVENT_MULTILOGON_MSG:
+ free(e->event.msg.message);
+ free(e->event.msg.formats);
+ free(e->event.msg.recipients);
+ free(e->event.msg.xhtml_message);
+ 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;
+ }
+
+ case GG_EVENT_STATUS60:
+ free(e->event.status60.descr);
+ break;
+
+ case GG_EVENT_STATUS:
+ free(e->event.status.descr);
+ break;
+
+ case GG_EVENT_NOTIFY_DESCR:
+ free(e->event.notify_descr.notify);
+ free(e->event.notify_descr.descr);
+ break;
+
+ case GG_EVENT_DCC_VOICE_DATA:
+ free(e->event.dcc_voice_data.data);
+ break;
+
+ case GG_EVENT_PUBDIR50_SEARCH_REPLY:
+ case GG_EVENT_PUBDIR50_READ:
+ case GG_EVENT_PUBDIR50_WRITE:
+ gg_pubdir50_free(e->event.pubdir50);
+ break;
+
+ 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;
+
+ case GG_EVENT_USER_DATA:
+ {
+ int i, j;
+
+ for (i = 0; i < e->event.user_data.user_count; i++) {
+ for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
+ free(e->event.user_data.users[i].attrs[j].key);
+ free(e->event.user_data.users[i].attrs[j].value);
+ }
+
+ free(e->event.user_data.users[i].attrs);
+ }
+
+ free(e->event.user_data.users);
+
+ break;
+ }
+
+ case GG_EVENT_MULTILOGON_INFO:
+ {
+ int i;
+
+ for (i = 0; i < e->event.multilogon_info.count; i++)
+ free(e->event.multilogon_info.sessions[i].name);
+
+ free(e->event.multilogon_info.sessions);
+
+ break;
+ }
+ }
+
+ free(e);
+}
+
+/** \cond internal */
+
+/**
+ * \internal Usuwa obrazek z kolejki do wysłania.
+ *
+ * \param s Struktura sesji
+ * \param q Struktura obrazka
+ * \param freeq Flaga zwolnienia elementu kolejki
+ *
+ * \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)
+{
+ if (!s || !q) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (s->images == q)
+ s->images = q->next;
+ else {
+ struct gg_image_queue *qq;
+
+ for (qq = s->images; qq; qq = qq->next) {
+ if (qq->next == q) {
+ qq->next = q->next;
+ break;
+ }
+ }
+ }
+
+ if (freeq) {
+ free(q->image);
+ free(q->filename);
+ free(q);
+ }
+
+ return 0;
+}
+
+/** \endcond */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
+ *
+ * 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 sess Struktura sesji
+ *
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup events
+ */
+struct gg_event *gg_watch_fd(struct gg_session *sess)
+{
+ struct gg_event *e;
+ int res = 0;
+ int port = 0;
+ int errno2 = 0;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
+
+ if (!sess) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ if (!(e = (gg_event*)calloc(1, sizeof(*e)))) {
+ 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 = gg_sock_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;
+ }
+
+ res = 0;
+ }
+
+ switch (sess->state) {
+ case GG_STATE_RESOLVING:
+ {
+ struct in_addr addr;
+ int failed = 0;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
+
+ if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+ failed = 1;
+ errno2 = errno;
+ }
+
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+
+ sess->resolver_cleanup(&sess->resolver, 0);
+
+ if (failed) {
+ errno = errno2;
+ goto fail_resolving;
+ }
+
+ /* 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
+ * bezpośredniego połączenia, jeśli hub leży)
+ * z resolvera. */
+ if (sess->proxy_addr && sess->proxy_port)
+ port = sess->proxy_port;
+ else {
+ sess->server_addr = sess->hub_addr = addr.s_addr;
+ port = GG_APPMSG_PORT;
+ }
+
+ 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) {
+ /* 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;
+ }
+
+ /* jeśli podano serwer i łączmy się przez proxy,
+ * jest to bezpośrednie połączenie, inaczej jest
+ * do huba. */
+
+ 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;
+
+ break;
+ }
+
+ case GG_STATE_CONNECTING_HUB:
+ {
+ char buf[1024], *client, *auth;
+ int res = 0;
+ int res_size = sizeof(res);
+ const char *host;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
+
+ /* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
+ * przypadkiem jakiś błąd. */
+ if (sess->async && (gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ 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));
+
+ goto fail_connecting;
+ }
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
+
+ if (sess->client_version != NULL && isdigit(sess->client_version[0]))
+ client = gg_urlencode(sess->client_version);
+ else
+ client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION);
+
+ if (client == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
+ goto fail_connecting;
+ }
+
+ if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port)
+ host = "http://" GG_APPMSG_HOST;
+ else
+ host = "";
+
+ auth = gg_proxy_auth();
+
+#ifdef GG_CONFIG_MIRANDA
+ if (sess->tls) {
+ snprintf(buf, sizeof(buf) - 1,
+ "GET %s/appsvc/appmsg_ver11.asp?tls=1&fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
+ "Connection: close\r\n"
+ "Host: " GG_APPMSG_HOST "\r\n"
+ "%s"
+ "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+ } else
+#elif defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
+ if (sess->ssl != NULL) {
+ snprintf(buf, sizeof(buf) - 1,
+ "GET %s/appsvc/appmsg_ver11.asp?tls=1&fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
+ "Connection: close\r\n"
+ "Host: " GG_APPMSG_HOST "\r\n"
+ "%s"
+ "\r\n", host, sess->uin, sess->last_sysmsg, client, (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);
+
+ 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 (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
+ 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;
+ sess->state = GG_STATE_IDLE;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ sess->state = GG_STATE_READING_DATA;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+
+ case GG_STATE_READING_DATA:
+ {
+ char buf[1024], *tmp, *host;
+ int port = GG_DEFAULT_PORT;
+ struct in_addr addr;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
+
+ /* czytamy linię z gniazda i obcinamy \r\n. */
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+ gg_chomp(buf);
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
+
+ /* 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ę nagłówka. */
+ while (strcmp(buf, "\r\n") && strcmp(buf, ""))
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+
+ /* czytamy pierwszą linię danych. */
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+ gg_chomp(buf);
+
+ /* 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 = (char*)realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
+ break;
+ }
+
+ sysmsg_buf = foo;
+
+ if (!len)
+ strcpy(sysmsg_buf, tmp);
+ else
+ strcat(sysmsg_buf, tmp);
+
+ len += (int)strlen(tmp);
+ }
+
+ e->type = GG_EVENT_MSG;
+ e->event.msg.msgclass = atoi(buf);
+ e->event.msg.sender = 0;
+ e->event.msg.message = (char*) sysmsg_buf;
+ }
+
+ gg_sock_close(sess->fd);
+
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
+
+ /* analizujemy otrzymane dane. */
+ tmp = buf;
+
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ while (*tmp && *tmp == ' ')
+ tmp++;
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ while (*tmp && *tmp == ' ')
+ tmp++;
+ host = tmp;
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ *tmp = 0;
+
+ if ((tmp = strchr(host, ':'))) {
+ *tmp = 0;
+ 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) {
+ /* jeśli mamy proxy, łączymy się z nim. */
+ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
+ /* 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;
+
+ /* Jeśli podano nazwę, nie adres serwera... */
+ if (sess->server_addr == INADDR_NONE) {
+ if (sess->resolver_start(&sess->fd, &sess->resolver, host) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail_resolving;
+ }
+
+ sess->state = GG_STATE_RESOLVING_GG;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ /* łączymy się z właściwym serwerem. */
+ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
+ 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 wyszło? próbujemy portu 443. */
+ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
+ /* ostatnia deska ratunku zawiodła?
+ * w takim razie zwijamy manatki. */
+ 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;
+ }
+
+ case GG_STATE_RESOLVING_GG:
+ {
+ struct in_addr addr;
+ int failed = 0;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING_GG\n");
+
+ if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+ failed = 1;
+ errno2 = errno;
+ }
+
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+
+ sess->resolver_cleanup(&sess->resolver, 0);
+
+ if (failed) {
+ errno = errno2;
+ goto fail_resolving;
+ }
+
+ sess->server_addr = addr.s_addr;
+
+ /* łączymy się z właściwym serwerem. */
+ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
+ 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 wyszło? próbujemy portu 443. */
+ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
+ /* ostatnia deska ratunku zawiodła?
+ * w takim razie zwijamy manatki. */
+ 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;
+ }
+
+ case GG_STATE_CONNECTING_GG:
+ {
+ int res = 0;
+ int res_size = sizeof(res);
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
+
+ sess->soft_timeout = 0;
+
+ /* jeśli wystąpił błąd podczas łączenia się... */
+ if (sess->async && (sess->timeout == 0 || gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ /* 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+ goto fail_connecting;
+ }
+
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+
+#ifdef ETIMEDOUT
+ if (sess->timeout == 0)
+ errno = ETIMEDOUT;
+#endif
+
+#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+ goto fail_connecting;
+ }
+#endif
+
+ 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;
+
+ /* próbujemy na port 443. */
+ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
+ 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+
+ if (gg_proxy_http_only)
+ sess->proxy_port = 0;
+
+ /* 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;
+
+ if (sess->server_addr)
+ addr.s_addr = sess->server_addr;
+ else
+ addr.s_addr = sess->hub_addr;
+
+ snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
+
+ 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 (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
+ 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_session(sess, GG_DEBUG_MISC, "// %s", auth);
+ if (gg_sock_write(sess->fd, auth, (int)strlen(auth)) < (signed)strlen(auth)) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ free(auth);
+ goto fail_connecting;
+ }
+
+ free(auth);
+ }
+
+ if (gg_sock_write(sess->fd, "\r\n", 2) < 2) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ goto fail_connecting;
+ }
+ }
+
+#ifdef GG_CONFIG_MIRANDA
+ if (sess->tls) {
+ sess->state = GG_STATE_TLS_NEGOTIATION;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#elif defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
+ if (sess->ssl != NULL) {
+#ifdef GG_CONFIG_HAVE_GNUTLS
+ gnutls_transport_set_ptr(GG_SESSION_GNUTLS(sess), (gnutls_transport_ptr_t) sess->fd);
+#endif
+#ifdef GG_CONFIG_HAVE_OPENSSL
+ SSL_set_fd(sess->ssl, (int)sess->fd);
+#endif
+
+ sess->state = GG_STATE_TLS_NEGOTIATION;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#endif
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+
+#ifdef GG_CONFIG_MIRANDA
+ case GG_STATE_TLS_NEGOTIATION:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+
+ sess->ssl = sslApi.connect(sess->fd, 0, 0);
+
+ if (sess->ssl == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation failed\n");
+
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_TLS;
+ sess->state = GG_STATE_IDLE;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded\n");
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#elif GG_CONFIG_HAVE_GNUTLS
+ case GG_STATE_TLS_NEGOTIATION:
+ {
+ int res;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+
+gnutls_handshake_repeat:
+ res = gnutls_handshake(GG_SESSION_GNUTLS(sess));
+
+ if (res == GNUTLS_E_AGAIN) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_AGAIN\n");
+
+ sess->state = GG_STATE_TLS_NEGOTIATION;
+ if (gnutls_record_get_direction(GG_SESSION_GNUTLS(sess)) == 0)
+ sess->check = GG_CHECK_READ;
+ else
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ if (res == GNUTLS_E_INTERRUPTED) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_INTERRUPTED\n");
+ goto gnutls_handshake_repeat;
+ }
+
+ if (res != 0) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake error %d\n", res);
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_TLS;
+ sess->state = GG_STATE_IDLE;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n");
+ gg_debug_session(sess, GG_DEBUG_MISC, "// cipher: VERS-%s:%s:%s:%s:COMP-%s\n",
+ gnutls_protocol_get_name(gnutls_protocol_get_version(GG_SESSION_GNUTLS(sess))),
+ gnutls_cipher_get_name(gnutls_cipher_get(GG_SESSION_GNUTLS(sess))),
+ gnutls_kx_get_name(gnutls_kx_get(GG_SESSION_GNUTLS(sess))),
+ gnutls_mac_get_name(gnutls_mac_get(GG_SESSION_GNUTLS(sess))),
+ gnutls_compression_get_name(gnutls_compression_get(GG_SESSION_GNUTLS(sess))));
+
+ if (gnutls_certificate_type_get(GG_SESSION_GNUTLS(sess)) == GNUTLS_CRT_X509) {
+ unsigned int peer_count;
+ const gnutls_datum_t *peers;
+ gnutls_x509_crt_t cert;
+
+ if (gnutls_x509_crt_init(&cert) >= 0) {
+ peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count);
+
+ if (peers != NULL) {
+ char buf[256];
+ size_t size;
+
+ if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) >= 0) {
+ size = sizeof(buf);
+ gnutls_x509_crt_get_dn(cert, buf, &size);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf);
+ size = sizeof(buf);
+ gnutls_x509_crt_get_issuer_dn(cert, buf, &size);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
+ }
+ }
+ }
+ }
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#elif GG_CONFIG_HAVE_OPENSSL
+ case GG_STATE_TLS_NEGOTIATION:
+ {
+ int res;
+ X509 *peer;
+
+ 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_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;
+ sess->state = GG_STATE_IDLE;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ if (err == SSL_ERROR_WANT_READ) {
+ 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;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ } else if (err == SSL_ERROR_WANT_WRITE) {
+ 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;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ } else {
+ char buf[256];
+
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(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;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+ }
+
+ 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_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n");
+ else {
+ char buf[256];
+
+ X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(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_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
+ }
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#endif
+
+ case GG_STATE_READING_KEY:
+ case GG_STATE_READING_REPLY:
+ case GG_STATE_CONNECTED:
+ case GG_STATE_DISCONNECTING:
+ {
+ struct gg_header *gh;
+
+ if (sess->state == GG_STATE_READING_KEY)
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
+ else if (sess->state == GG_STATE_READING_REPLY)
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
+ else if (sess->state == GG_STATE_CONNECTED)
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
+ else if (sess->state == GG_STATE_DISCONNECTING)
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_DISCONNECTING\n");
+
+ /* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie
+ * się tekstu wrzucanego przez proxy. */
+ if (sess->state == GG_STATE_READING_KEY && 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_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_session(sess, GG_DEBUG_MISC, "// %s\n", buf);
+ }
+
+ /* XXX niech czeka jeszcze raz w tej samej
+ * fazie. głupio, ale działa. */
+ sess->proxy_port = 0;
+
+ break;
+ }
+
+ sess->last_event = (int)time(NULL);
+
+ gh = (gg_header*)gg_recv_packet(sess);
+
+ if (gh == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
+ if (errno == EAGAIN) {
+ e->type = GG_EVENT_NONE;
+ res = 0;
+ } else {
+ res = -1;
+ }
+
+ goto done;
+ }
+
+ if (gg_session_handle_packet(sess, gh->type, (const char *) gh + sizeof(struct gg_header), gh->length, e) == -1) {
+ free(gh);
+ res = -1;
+ goto done;
+ }
+
+ free(gh);
+
+ sess->check = GG_CHECK_READ;
+
+ break;
+ }
+ }
+
+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;
+ gg_sock_close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ }
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_CONNECTING;
+ sess->state = GG_STATE_IDLE;
+ goto done;
+
+fail_resolving:
+ e->type = GG_EVENT_CONN_FAILED;
+ 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;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/src/libgadu/handlers.cpp b/protocols/Gadu-Gadu/src/libgadu/handlers.cpp
new file mode 100644
index 0000000000..da09c40b5f
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/libgadu/handlers.cpp
@@ -0,0 +1,1810 @@
+/*
+ * (C) Copyright 2001-2011 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 handlers.c
+ *
+ * \brief Funkcje obsługi przychodzących pakietów
+ */
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#include <sys/types.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+#endif /* _WIN32 */
+
+#include "compat.h"
+#include "libgadu.h"
+#include "resolver.h"
+#include "session.h"
+#include "protocol.h"
+#include "encoding.h"
+#include "message.h"
+#include "internal.h"
+
+#include <errno.h>
+#ifndef _WIN32
+#include <netdb.h>
+#endif /* _WIN32 */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif /* _WIN32 */
+#include <time.h>
+#ifndef GG_CONFIG_MIRANDA
+#ifdef GG_CONFIG_HAVE_OPENSSL
+# include <openssl/err.h>
+# include <openssl/rand.h>
+#endif
+#endif
+
+/**
+ * \internal Struktura opisująca funkcję obsługi pakietu.
+ */
+typedef struct {
+ /* Typ pakietu */
+ uint32_t type;
+ /* Stan w którym pakiet jest obsługiwany */
+ int state;
+ /* Minimalny rozmiar danych pakietu */
+ int min_length;
+ /* Funkcja obsługująca pakiet. Patrz gg_session_handle_packet(). */
+ int (*handler)(struct gg_session *, uint32_t, const char *, size_t, struct gg_event *);
+} gg_packet_handler_t;
+
+/**
+ * \internal Obsługuje pakiet GG_WELCOME.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_welcome(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_welcome *w;
+ int ret;
+ uint8_t hash_buf[64];
+ uint32_t local_ip;
+
+ if (len < sizeof(struct gg_welcome)) {
+ ge->type = GG_EVENT_CONN_FAILED;
+ ge->event.failure = GG_FAILURE_INVALID;
+ gs->state = GG_STATE_IDLE;
+ close(gs->fd);
+ gs->fd = -1;
+ return 0;
+ }
+
+ w = (struct gg_welcome*) ptr;
+ w->key = gg_fix32(w->key);
+
+ memset(hash_buf, 0, sizeof(hash_buf));
+
+ switch (gs->hash_type) {
+ case GG_LOGIN_HASH_GG32:
+ {
+ uint32_t hash;
+
+ hash = gg_fix32(gg_login_hash((unsigned char*) gs->password, w->key));
+ gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash);
+ memcpy(hash_buf, &hash, sizeof(hash));
+
+ break;
+ }
+
+ case GG_LOGIN_HASH_SHA1:
+ {
+#ifndef GG_DEBUG_DISABLE
+ char tmp[41];
+ int i;
+#endif
+
+ gg_login_hash_sha1(gs->password, w->key, hash_buf);
+
+#ifndef GG_DEBUG_DISABLE
+ for (i = 0; i < 40; i += 2)
+ snprintf(tmp + i, sizeof(tmp) - i, "%02x", hash_buf[i / 2]);
+
+ gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp);
+#endif
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+#if 0
+ if (gs->password != NULL && (gs->flags & (1 << GG_SESSION_FLAG_CLEAR_PASSWORD))) {
+ memset(gs->password, 0, strlen(gs->password));
+ free(gs->password);
+ gs->password = NULL;
+ }
+#endif
+
+ if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
+ struct sockaddr_in sin;
+ int sin_len = sizeof(sin);
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
+
+ if (!getsockname(gs->fd, (struct sockaddr*) &sin, &sin_len)) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
+ local_ip = sin.sin_addr.s_addr;
+ } else {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
+ local_ip = 0;
+ }
+ } else
+ local_ip = gg_dcc_ip;
+
+ gs->client_addr = local_ip;
+
+ if (GG_SESSION_IS_PROTOCOL_8_0(gs)) {
+ struct gg_login80 l80;
+ const char *client_name, *version, *descr;
+ uint32_t client_name_len, version_len, descr_len;
+
+ memset(&l80, 0, sizeof(l80));
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending GG_LOGIN80 packet\n");
+ l80.uin = gg_fix32(gs->uin);
+ memcpy(l80.language, GG8_LANG, sizeof(l80.language));
+ l80.hash_type = gs->hash_type;
+ memcpy(l80.hash, hash_buf, sizeof(l80.hash));
+ l80.status = gg_fix32(gs->initial_status ? gs->initial_status : GG_STATUS_AVAIL);
+ l80.flags = gg_fix32(gs->status_flags);
+ l80.features = gg_fix32(gs->protocol_features);
+ l80.image_size = gs->image_size;
+ l80.dunno2 = 0x64;
+
+ if (gs->client_version != NULL && !isdigit(gs->client_version[0])) {
+ client_name = "";
+ client_name_len = 0;
+ } else {
+ client_name = GG8_VERSION;
+ client_name_len = strlen(GG8_VERSION);
+ }
+
+ version = (gs->client_version != NULL) ? gs->client_version : GG_DEFAULT_CLIENT_VERSION;
+ version_len = gg_fix32(client_name_len + strlen(version));
+
+ descr = (gs->initial_descr != NULL) ? gs->initial_descr : "";
+ descr_len = (gs->initial_descr != NULL) ? gg_fix32(strlen(gs->initial_descr)) : 0;
+
+ ret = gg_send_packet(gs,
+ GG_LOGIN80,
+ &l80, sizeof(l80),
+ &version_len, sizeof(version_len),
+ client_name, client_name_len,
+ version, strlen(version),
+ &descr_len, sizeof(descr_len),
+ descr, strlen(descr),
+ NULL);
+ } else {
+ struct gg_login70 l70;
+
+ memset(&l70, 0, sizeof(l70));
+ l70.uin = gg_fix32(gs->uin);
+ l70.hash_type = gs->hash_type;
+ memcpy(l70.hash, hash_buf, sizeof(l70.hash));
+ l70.status = gg_fix32(gs->initial_status ? gs->initial_status : GG_STATUS_AVAIL);
+ l70.version = gg_fix32(gs->protocol_version | gs->protocol_flags);
+ if (gs->external_addr && gs->external_port > 1023) {
+ l70.local_ip = gs->external_addr;
+ l70.local_port = gg_fix16(gs->external_port);
+ } else {
+ l70.local_ip = local_ip;
+ l70.local_port = gg_fix16(gg_dcc_port);
+ }
+
+ l70.image_size = gs->image_size;
+ l70.dunno2 = 0xbe;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending GG_LOGIN70 packet\n");
+ ret = gg_send_packet(gs, GG_LOGIN70, &l70, sizeof(l70), gs->initial_descr, (gs->initial_descr) ? strlen(gs->initial_descr) : 0, NULL);
+ }
+
+ if (ret == -1) {
+ int errno_copy;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
+ errno_copy = errno;
+ close(gs->fd);
+ errno = errno_copy;
+ gs->fd = -1;
+ ge->type = GG_EVENT_CONN_FAILED;
+ ge->event.failure = GG_FAILURE_WRITING;
+ gs->state = GG_STATE_IDLE;
+ return -1;
+ }
+
+ gs->state = GG_STATE_READING_REPLY;
+ gs->check = GG_CHECK_READ;
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_LOGIN_OK.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_login_ok(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
+ ge->type = GG_EVENT_CONN_SUCCESS;
+ gs->state = GG_STATE_CONNECTED;
+ gs->check = GG_CHECK_READ;
+ gs->timeout = -1;
+ gs->status = (gs->initial_status) ? gs->initial_status : GG_STATUS_AVAIL;
+#if 0
+ free(gs->status_descr);
+ gs->status_descr = gs->initial_descr;
+#else
+ free(gs->initial_descr);
+#endif
+ gs->initial_descr = NULL;
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_LOGIN_FAILED.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_login_failed(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ if (type != GG_DISCONNECTING)
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
+ else
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
+ ge->type = GG_EVENT_CONN_FAILED;
+ ge->event.failure = (type != GG_DISCONNECTING) ? GG_FAILURE_PASSWORD : GG_FAILURE_INTRUDER;
+ gs->state = GG_STATE_IDLE;
+ close(gs->fd);
+ gs->fd = -1;
+ errno = EACCES;
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_SEND_MSG_ACK.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_send_msg_ack(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_send_msg_ack *s = (struct gg_send_msg_ack*) ptr;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
+
+ ge->type = GG_EVENT_ACK;
+ ge->event.ack.status = gg_fix32(s->status);
+ ge->event.ack.recipient = gg_fix32(s->recipient);
+ ge->event.ack.seq = gg_fix32(s->seq);
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_PONG.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_pong(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
+
+ ge->type = GG_EVENT_PONG;
+
+ gs->last_pong = time(NULL);
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_DISCONNECTING.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_disconnecting(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
+
+ ge->type = GG_EVENT_DISCONNECT;
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_DISCONNECT_ACK.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_disconnect_ack(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received logoff acknowledge\n");
+
+ ge->type = GG_EVENT_DISCONNECT_ACK;
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiety GG_XML_EVENT i GG_XML_ACTION.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_xml_event(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n");
+
+ ge->type = GG_EVENT_XML_EVENT;
+ ge->event.xml_event.data = (char*)malloc(len + 1);
+
+ if (ge->event.xml_event.data == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ memcpy(ge->event.xml_event.data, ptr, len);
+ ge->event.xml_event.data[len] = 0;
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_PUBDIR50_REPLY.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_pubdir50_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
+
+ return gg_pubdir50_handle_reply_sess(gs, ge, ptr, len);
+}
+
+/**
+ * \internal Obsługuje pakiet GG_USERLIST_REPLY.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_userlist_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ char reply_type;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
+
+ reply_type = ptr[0];
+
+ /* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
+ * gdy otrzymano wszystkie odpowiedzi */
+ if (reply_type == GG_USERLIST_PUT_REPLY || reply_type == GG_USERLIST_PUT_MORE_REPLY) {
+ if (--gs->userlist_blocks)
+ return 0;
+
+ reply_type = GG_USERLIST_PUT_REPLY;
+ }
+
+ if (len > 1) {
+ unsigned int reply_len = (gs->userlist_reply != NULL) ? strlen(gs->userlist_reply) : 0;
+ char *tmp;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", gs->userlist_reply, len);
+
+ tmp = (char*)realloc(gs->userlist_reply, reply_len + len);
+
+ if (tmp == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ gs->userlist_reply = tmp;
+ memcpy(gs->userlist_reply + reply_len, ptr + 1, len - 1);
+ gs->userlist_reply[reply_len + len - 1] = 0;
+ }
+
+ if (reply_type == GG_USERLIST_GET_MORE_REPLY)
+ return 0;
+
+ ge->type = GG_EVENT_USERLIST;
+ ge->event.userlist.type = reply_type;
+ ge->event.userlist.reply = gs->userlist_reply;
+
+ gs->userlist_reply = NULL;
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_DCC7_ID_REPLY.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_dcc7_id_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
+
+ return gg_dcc7_handle_id(gs, ge, ptr, len);
+}
+
+/**
+ * \internal Obsługuje pakiet GG_DCC7_ACCEPT.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_dcc7_accept(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
+
+ return gg_dcc7_handle_accept(gs, ge, ptr, len);
+}
+
+/**
+ * \internal Obsługuje pakiet GG_DCC7_NEW.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_dcc7_new(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
+
+ return gg_dcc7_handle_new(gs, ge, ptr, len);
+}
+
+/**
+ * \internal Obsługuje pakiet GG_DCC7_REJECT.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_dcc7_reject(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
+
+ return gg_dcc7_handle_reject(gs, ge, ptr, len);
+}
+
+/**
+* \internal Obsługuje pakiet GG_DCC7_INFO.
+*
+* Patrz gg_packet_handler_t
+*/
+static int gg_session_handle_dcc7_info(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
+
+ return gg_dcc7_handle_info(gs, ge, ptr, len);
+}
+
+/**
+ * \internal Analizuje przychodzący pakiet z obrazkiem.
+ *
+ * \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, const char *p, unsigned int len, struct gg_session *sess, uin_t sender)
+{
+ struct gg_msg_image_reply *i = (gg_msg_image_reply*) p;
+ struct gg_image_queue *q, *qq;
+
+ if (!p || !sess || !e) {
+ errno = EFAULT;
+ return;
+ }
+
+ /* 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;
+ break;
+ }
+ }
+
+ if (!q) {
+ 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] == GG_MSG_OPTION_IMAGE_REPLY) {
+ q->done = 0;
+
+ len -= sizeof(struct gg_msg_image_reply);
+ p += sizeof(struct gg_msg_image_reply);
+
+ 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_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() out of memory\n");
+ return;
+ }
+
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ } else {
+ len -= sizeof(struct gg_msg_image_reply);
+ p += sizeof(struct gg_msg_image_reply);
+ }
+
+ if (q->done + len > q->size)
+ len = q->size - q->done;
+
+ memcpy(q->image + q->done, p, len);
+ q->done += len;
+
+ /* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
+
+ if (q->done >= q->size) {
+ e->type = GG_EVENT_IMAGE_REPLY;
+ e->event.image_reply.sender = sender;
+ e->event.image_reply.size = q->size;
+ e->event.image_reply.crc32 = q->crc32;
+ e->event.image_reply.filename = q->filename;
+ e->event.image_reply.image = q->image;
+
+ gg_image_queue_remove(sess, q, 0);
+
+ free(q);
+ }
+}
+
+/**
+ * \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.
+ *
+ * \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_options(struct gg_session *sess, struct gg_event *e, uin_t sender, const char *p, const char *packet_end)
+{
+ while (p < packet_end) {
+ switch (*p) {
+ case GG_MSG_OPTION_CONFERENCE:
+ {
+ struct gg_msg_recipients *m = (gg_msg_recipients*) p;
+ uint32_t i, count;
+
+ p += sizeof(*m);
+
+ if (p > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() 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_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1.5)\n");
+ goto malformed;
+ }
+
+ if (e->event.msg.recipients != NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.recipients already exist\n");
+ goto malformed;
+ }
+
+ e->event.msg.recipients = (uin_t*)malloc(count * sizeof(uin_t));
+
+ if (e->event.msg.recipients == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for recipients data\n");
+ goto fail;
+ }
+
+ memcpy(e->event.msg.recipients, p, count * sizeof(uin_t));
+ p += count * sizeof(uin_t);
+
+ for (i = 0; i < count; i++)
+ e->event.msg.recipients[i] = gg_fix32(e->event.msg.recipients[i]);
+
+ e->event.msg.recipients_count = count;
+
+ break;
+ }
+
+ case GG_MSG_OPTION_ATTRIBUTES:
+ {
+ uint16_t len;
+ char *buf;
+
+ if (p + 3 > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (2)\n");
+ goto malformed;
+ }
+
+ memcpy(&len, p + 1, sizeof(uint16_t));
+ len = gg_fix16(len);
+
+ if (e->event.msg.formats != NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.formats already exist\n");
+ goto malformed;
+ }
+
+ buf = (char*)malloc(len);
+
+ if (buf == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for richtext data\n");
+ goto fail;
+ }
+
+ p += 3;
+
+ if (p + len > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (3)\n");
+ free(buf);
+ goto malformed;
+ }
+
+ memcpy(buf, p, len);
+
+ e->event.msg.formats = buf;
+ e->event.msg.formats_length = len;
+
+ p += len;
+
+ break;
+ }
+
+ case GG_MSG_OPTION_IMAGE_REQUEST:
+ {
+ struct gg_msg_image_request *i = (gg_msg_image_request*) p;
+
+ if (p + sizeof(*i) > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+ goto malformed;
+ }
+
+ if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (1)\n");
+ goto malformed;
+ }
+
+ 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;
+
+ goto handled;
+ }
+
+ case GG_MSG_OPTION_IMAGE_REPLY:
+ case GG_MSG_OPTION_IMAGE_REPLY_MORE:
+ {
+ struct gg_msg_image_reply *rep = (gg_msg_image_reply*) p;
+
+ if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (2)\n");
+ goto malformed;
+ }
+
+ if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
+
+ /* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
+
+ e->type = GG_EVENT_IMAGE_REPLY;
+ 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;
+ goto handled;
+
+ } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
+
+ 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, sender);
+
+ goto handled;
+ }
+
+ default:
+ {
+ 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 Wysyła potwierdzenie odebrania wiadomości.
+ *
+ * \param gs Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
+ */
+static int gg_session_send_msg_ack(struct gg_session *gs)
+{
+ struct gg_recv_msg_ack pkt;
+
+ gg_debug_session(gs, GG_DEBUG_FUNCTION, "** gg_session_send_msg_ack(%p);\n", gs);
+
+ if ((gs->protocol_features & GG_FEATURE_MSG_ACK) == 0)
+ return 0;
+
+ gs->recv_msg_count++;
+ pkt.count = gg_fix32(gs->recv_msg_count);
+
+ return gg_send_packet(gs, GG_RECV_MSG_ACK, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Obsługuje pakiet GG_RECV_MSG.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_recv_msg(struct gg_session *sess, uint32_t type, const char *packet, size_t length, struct gg_event *e)
+{
+ const struct gg_recv_msg *r = (const struct gg_recv_msg*) packet;
+ const char *payload = packet + sizeof(struct gg_recv_msg);
+ const char *payload_end = packet + length;
+ char *tmp;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %d, %p);\n", packet, length, e);
+
+ if ((r->seq == 0) && (r->msgclass == 0)) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
+ goto malformed;
+ }
+
+ // jednobajtowa wiadomość o treści \x02 to żądanie połączenia DCC
+ if (*payload == GG_MSG_CALLBACK && payload == payload_end - 1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
+ length = 1;
+ } else {
+ const char *options;
+
+ options = (const char*)memchr(payload, 0, (size_t) (payload_end - payload));
+
+ if (options == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
+ goto malformed;
+ }
+
+ length = (size_t) (options - payload);
+
+ switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), options + 1, payload_end)) {
+ case -1: // handled
+ gg_session_send_msg_ack(sess);
+ 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.seq = gg_fix32(r->seq);
+
+ tmp = gg_encoding_convert(payload, GG_ENCODING_CP1250, sess->encoding, length, -1);
+ if (tmp == NULL)
+ goto fail;
+ e->event.msg.message = tmp;
+
+ gg_session_send_msg_ack(sess);
+ return 0;
+
+fail:
+ free(e->event.msg.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);
+ gg_session_send_msg_ack(sess);
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_RECV_MSG80.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_recv_msg_80(struct gg_session *sess, uint32_t type, const char *packet, size_t length, struct gg_event *e)
+{
+ const struct gg_recv_msg80 *r = (const 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, %d, %p);\n", packet, length, e);
+
+ if (r->seq == 0 && r->msgclass == 0) {
+ 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 >= 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 > 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. */
+ }
+
+ /* Normalna sytuacja, więc nie podpada pod powyższy warunek. */
+ if (offset_attr == length)
+ offset_attr = 0;
+
+ if (memchr(packet + offset_plain, 0, 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 = (type != GG_RECV_OWN_MSG) ? GG_EVENT_MSG : GG_EVENT_MULTILOGON_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 (offset_attr != 0) {
+ switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + length)) {
+ case -1: // handled
+ gg_session_send_msg_ack(sess);
+ return 0;
+
+ case -2: // failed
+ goto fail;
+
+ case -3: // malformed
+ goto malformed;
+ }
+ }
+
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ e->event.msg.message = strdup(packet + offset_plain);
+ } else {
+ if (offset_plain > sizeof(struct gg_recv_msg80)) {
+ int len;
+
+ len = gg_message_html_to_text(NULL, packet + sizeof(struct gg_recv_msg80));
+ e->event.msg.message = (char*)malloc(len + 1);
+
+ if (e->event.msg.message == NULL)
+ goto fail;
+
+ gg_message_html_to_text((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80));
+ } else {
+ e->event.msg.message = gg_encoding_convert(packet + offset_plain, GG_ENCODING_CP1250, sess->encoding, -1, -1);
+ }
+ }
+
+ if (offset_plain > sizeof(struct gg_recv_msg80))
+ e->event.msg.xhtml_message = gg_encoding_convert(packet + sizeof(struct gg_recv_msg80), GG_ENCODING_UTF8, sess->encoding, -1, -1);
+ else
+ e->event.msg.xhtml_message = NULL;
+
+ gg_session_send_msg_ack(sess);
+ 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);
+ gg_session_send_msg_ack(sess);
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_STATUS.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_status(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_status *s = (gg_status*) ptr;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+ ge->type = GG_EVENT_STATUS;
+ ge->event.status.uin = gg_fix32(s->uin);
+ ge->event.status.status = gg_fix32(s->status);
+ ge->event.status.descr = NULL;
+
+ if (len > sizeof(*s)) {
+ ge->event.status.descr = gg_encoding_convert(ptr + sizeof(*s), GG_ENCODING_CP1250, gs->encoding, len - sizeof(*s), -1);
+
+ if (ge->event.status.descr == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiety GG_STATUS60, GG_STATUS77 i GG_STATUS80BETA.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_status_60_77_80beta(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_status60 *s60 = (gg_status60*) ptr;
+ struct gg_status77 *s77 = (gg_status77*) ptr;
+ size_t struct_len;
+ uint32_t uin;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+ ge->type = GG_EVENT_STATUS60;
+ ge->event.status60.descr = NULL;
+ ge->event.status60.time = 0;
+
+ if (type == GG_STATUS60) {
+ uin = gg_fix32(s60->uin);
+ ge->event.status60.status = s60->status;
+ ge->event.status60.remote_ip = s60->remote_ip;
+ ge->event.status60.remote_port = gg_fix16(s60->remote_port);
+ ge->event.status60.version = s60->version;
+ ge->event.status60.image_size = s60->image_size;
+ struct_len = sizeof(*s60);
+ } else {
+ uin = gg_fix32(s77->uin);
+ ge->event.status60.status = s77->status;
+ ge->event.status60.remote_ip = s77->remote_ip;
+ ge->event.status60.remote_port = gg_fix16(s77->remote_port);
+ ge->event.status60.version = s77->version;
+ ge->event.status60.image_size = s77->image_size;
+ struct_len = sizeof(*s77);
+ }
+
+ ge->event.status60.uin = uin & 0x00ffffff;
+
+ if (uin & 0x40000000)
+ ge->event.status60.version |= GG_HAS_AUDIO_MASK;
+ if (uin & 0x20000000)
+ ge->event.status60.version |= GG_HAS_AUDIO7_MASK;
+ if (uin & 0x08000000)
+ ge->event.status60.version |= GG_ERA_OMNIX_MASK;
+
+ if (len > struct_len) {
+ size_t descr_len;
+
+ descr_len = len - struct_len;
+
+ ge->event.status60.descr = gg_encoding_convert(ptr + struct_len, (type == GG_STATUS80BETA) ? GG_ENCODING_UTF8 : GG_ENCODING_CP1250, gs->encoding, descr_len, -1);
+
+ if (ge->event.status60.descr == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ if (descr_len > 4 && ptr[len - 5] == 0) {
+ uint32_t t;
+ memcpy(&t, ptr + len - 4, sizeof(uint32_t));
+ ge->event.status60.time = gg_fix32(t);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_NOTIFY_REPLY.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_notify_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_notify_reply *n = (gg_notify_reply*) ptr;
+ char *descr;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ 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) {
+ size_t descr_len;
+
+ ge->type = GG_EVENT_NOTIFY_DESCR;
+
+ if (!(ge->event.notify_descr.notify = (gg_notify_reply*) malloc(sizeof(*n) * 2))) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+ ge->event.notify_descr.notify[1].uin = 0;
+ memcpy(ge->event.notify_descr.notify, ptr, sizeof(*n));
+ ge->event.notify_descr.notify[0].uin = gg_fix32(ge->event.notify_descr.notify[0].uin);
+ ge->event.notify_descr.notify[0].status = gg_fix32(ge->event.notify_descr.notify[0].status);
+ ge->event.notify_descr.notify[0].remote_port = gg_fix16(ge->event.notify_descr.notify[0].remote_port);
+ ge->event.notify_descr.notify[0].version = gg_fix32(ge->event.notify_descr.notify[0].version);
+
+ descr_len = len - sizeof(*n);
+
+ descr = gg_encoding_convert(ptr + sizeof(*n), GG_ENCODING_CP1250, gs->encoding, descr_len, -1);
+
+ if (descr == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ ge->event.notify_descr.descr = descr;
+
+ } else {
+ unsigned int i, count;
+
+ ge->type = GG_EVENT_NOTIFY;
+
+ if (!(ge->event.notify = (gg_notify_reply*) malloc(len + 2 * sizeof(*n)))) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ memcpy(ge->event.notify, ptr, len);
+ count = len / sizeof(*n);
+ ge->event.notify[count].uin = 0;
+
+ for (i = 0; i < count; i++) {
+ ge->event.notify[i].uin = gg_fix32(ge->event.notify[i].uin);
+ ge->event.notify[i].status = gg_fix32(ge->event.notify[i].status);
+ ge->event.notify[i].remote_port = gg_fix16(ge->event.notify[i].remote_port);
+ ge->event.notify[i].version = gg_fix32(ge->event.notify[i].version);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_STATUS80.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_status_80(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_notify_reply80 *n = (gg_notify_reply80*) ptr;
+ size_t descr_len;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+ ge->type = GG_EVENT_STATUS60;
+ ge->event.status60.uin = gg_fix32(n->uin);
+ ge->event.status60.status = gg_fix32(n->status);
+ ge->event.status60.remote_ip = n->remote_ip;
+ ge->event.status60.remote_port = gg_fix16(n->remote_port);
+ ge->event.status60.version = 0;
+ ge->event.status60.image_size = n->image_size;
+ ge->event.status60.descr = NULL;
+ ge->event.status60.time = 0;
+
+ descr_len = gg_fix32(n->descr_len);
+
+ if (descr_len != 0 && sizeof(struct gg_notify_reply80) + descr_len <= len) {
+ ge->event.status60.descr = gg_encoding_convert((char*) n + sizeof(struct gg_notify_reply80), GG_ENCODING_UTF8, gs->encoding, descr_len, -1);
+
+ if (ge->event.status60.descr == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ /* XXX czas */
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_NOTIFY_REPLY80.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_notify_reply_80(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_notify_reply80 *n = (gg_notify_reply80*) ptr;
+ unsigned int length = len, i = 0;
+
+ // TODO: najpierw przeanalizować strukturę i określić
+ // liczbę rekordów, żeby obyć się bez realloc()
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ ge->type = GG_EVENT_NOTIFY60;
+ ge->event.notify60 = (gg_event_notify60*)malloc(sizeof(*ge->event.notify60));
+
+ if (!ge->event.notify60) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ ge->event.notify60[0].uin = 0;
+
+ while (length >= sizeof(struct gg_notify_reply80)) {
+ uin_t uin = gg_fix32(n->uin);
+ int descr_len;
+ char *tmp;
+
+ ge->event.notify60[i].uin = uin;
+ ge->event.notify60[i].status = gg_fix32(n->status);
+ ge->event.notify60[i].remote_ip = n->remote_ip;
+ ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
+ ge->event.notify60[i].version = 0;
+ ge->event.notify60[i].image_size = n->image_size;
+ ge->event.notify60[i].descr = NULL;
+ ge->event.notify60[i].time = 0;
+
+ descr_len = gg_fix32(n->descr_len);
+
+ if (descr_len != 0) {
+ if (sizeof(struct gg_notify_reply80) + descr_len <= length) {
+ ge->event.notify60[i].descr = gg_encoding_convert((char*) n + sizeof(struct gg_notify_reply80), GG_ENCODING_UTF8, gs->encoding, descr_len, -1);
+
+ if (ge->event.notify60[i].descr == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ /* XXX czas */
+
+ length -= sizeof(struct gg_notify_reply80) + descr_len;
+ n = (gg_notify_reply80*) ((char*) n + sizeof(struct gg_notify_reply80) + descr_len);
+ } else {
+ length = 0;
+ }
+
+ } else {
+ length -= sizeof(struct gg_notify_reply80);
+ n = (gg_notify_reply80*) ((char*) n + sizeof(struct gg_notify_reply80));
+ }
+
+ if (!(tmp = (char*)realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ free(ge->event.notify60);
+ return -1;
+ }
+
+ ge->event.notify60 = (gg_event_notify60*) tmp;
+ ge->event.notify60[++i].uin = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiety GG_NOTIFY_REPLY77 i GG_NOTIFY_REPLY80BETA.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_notify_reply_77_80beta(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_notify_reply77 *n = (gg_notify_reply77*) ptr;
+ unsigned int length = len, i = 0;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ ge->type = GG_EVENT_NOTIFY60;
+ ge->event.notify60 = (gg_event_notify60*)malloc(sizeof(*ge->event.notify60));
+
+ if (ge->event.notify60 == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ ge->event.notify60[0].uin = 0;
+
+ while (length >= sizeof(struct gg_notify_reply77)) {
+ uin_t uin = gg_fix32(n->uin);
+ char *tmp;
+
+ ge->event.notify60[i].uin = uin & 0x00ffffff;
+ ge->event.notify60[i].status = n->status;
+ ge->event.notify60[i].remote_ip = n->remote_ip;
+ ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
+ ge->event.notify60[i].version = n->version;
+ ge->event.notify60[i].image_size = n->image_size;
+ ge->event.notify60[i].descr = NULL;
+ ge->event.notify60[i].time = 0;
+
+ if (uin & 0x40000000)
+ ge->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
+ if (uin & 0x20000000)
+ ge->event.notify60[i].version |= GG_HAS_AUDIO7_MASK;
+ if (uin & 0x08000000)
+ ge->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) {
+ ge->event.notify60[i].descr = gg_encoding_convert((char*) n + sizeof(struct gg_notify_reply77) + 1, (type == GG_NOTIFY_REPLY80BETA) ? GG_ENCODING_UTF8 : GG_ENCODING_CP1250, gs->encoding, descr_len, -1);
+
+ if (ge->event.notify60[i].descr == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ /* XXX czas */
+
+ length -= sizeof(struct gg_notify_reply77) + descr_len + 1;
+ n = (gg_notify_reply77*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1);
+ } else {
+ length = 0;
+ }
+
+ } else {
+ length -= sizeof(struct gg_notify_reply77);
+ n = (gg_notify_reply77*) ((char*) n + sizeof(struct gg_notify_reply77));
+ }
+
+ if (!(tmp = (char*)realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ free(ge->event.notify60);
+ return -1;
+ }
+
+ ge->event.notify60 = (gg_event_notify60*) tmp;
+ ge->event.notify60[++i].uin = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_NOTIFY_REPLY60.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_notify_reply_60(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_notify_reply60 *n = (gg_notify_reply60*) ptr;
+ unsigned int length = len, i = 0;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ ge->type = GG_EVENT_NOTIFY60;
+ ge->event.notify60 = (gg_event_notify60*)malloc(sizeof(*ge->event.notify60));
+
+ if (ge->event.notify60 == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ ge->event.notify60[0].uin = 0;
+
+ while (length >= sizeof(struct gg_notify_reply60)) {
+ uin_t uin = gg_fix32(n->uin);
+ char *tmp;
+
+ ge->event.notify60[i].uin = uin & 0x00ffffff;
+ ge->event.notify60[i].status = n->status;
+ ge->event.notify60[i].remote_ip = n->remote_ip;
+ ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
+ ge->event.notify60[i].version = n->version;
+ ge->event.notify60[i].image_size = n->image_size;
+ ge->event.notify60[i].descr = NULL;
+ ge->event.notify60[i].time = 0;
+
+ if (uin & 0x40000000)
+ ge->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
+ if (uin & 0x08000000)
+ ge->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
+
+ if (GG_S_D(n->status)) {
+ unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
+
+ if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
+ char *descr;
+
+ descr = gg_encoding_convert((char*) n + sizeof(struct gg_notify_reply60) + 1, GG_ENCODING_CP1250, gs->encoding, descr_len, -1);
+
+ if (descr == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ return -1;
+ }
+
+ ge->event.notify60[i].descr = descr;
+
+ /* XXX czas */
+
+ length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
+ n = (gg_notify_reply60*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+ } else {
+ length = 0;
+ }
+
+ } else {
+ length -= sizeof(struct gg_notify_reply60);
+ n = (gg_notify_reply60*) ((char*) n + sizeof(struct gg_notify_reply60));
+ }
+
+ if (!(tmp = (char*)realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
+ free(ge->event.notify60);
+ return -1;
+ }
+
+ ge->event.notify60 = (gg_event_notify60*) tmp;
+ ge->event.notify60[++i].uin = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_USER_DATA.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_user_data(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_user_data d;
+ char *p = (char*) ptr;
+ char *packet_end = (char*) ptr + len;
+ struct gg_event_user_data_user *users;
+ int i, j;
+ int res = 0;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received user data\n");
+
+ ge->event.user_data.user_count = 0;
+ ge->event.user_data.users = NULL;
+
+ if (ptr + sizeof(d) > packet_end)
+ goto malformed;
+
+ memcpy(&d, p, sizeof(d));
+ p += sizeof(d);
+
+ d.type = gg_fix32(d.type);
+ d.user_count = gg_fix32(d.user_count);
+
+ if (d.user_count > 0xffff) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (1)\n");
+ goto malformed;
+ }
+
+ if (d.user_count > 0) {
+ users = (gg_event_user_data_user*)calloc(d.user_count, sizeof(struct gg_event_user_data_user));
+
+ if (users == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() out of memory (%d*%d)\n", d.user_count, sizeof(struct gg_event_user_data_user));
+ goto fail;
+ }
+ } else {
+ users = NULL;
+ }
+
+ ge->type = GG_EVENT_USER_DATA;
+ ge->event.user_data.type = d.type;
+ ge->event.user_data.user_count = d.user_count;
+ ge->event.user_data.users = users;
+
+ gg_debug_session(gs, GG_DEBUG_DUMP, "type=%d, count=%d\n", d.type, d.user_count);
+
+ for (i = 0; i < d.user_count; i++) {
+ struct gg_user_data_user u;
+ struct gg_event_user_data_attr *attrs;
+
+ if (p + sizeof(u) > packet_end) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (2)\n");
+ goto malformed;
+ }
+
+ memcpy(&u, p, sizeof(u));
+ p += sizeof(u);
+
+ u.uin = gg_fix32(u.uin);
+ u.attr_count = gg_fix32(u.attr_count);
+
+ if (u.attr_count > 0xffff) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (2)\n");
+ goto malformed;
+ }
+
+ if (u.attr_count > 0) {
+ attrs = (gg_event_user_data_attr*)calloc(u.attr_count, sizeof(struct gg_event_user_data_attr));
+
+ if (attrs == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() out of memory (%d*%d)\n", u.attr_count, sizeof(struct gg_event_user_data_attr));
+ goto fail;
+ }
+ } else {
+ attrs = NULL;
+ }
+
+ users[i].uin = u.uin;
+ users[i].attr_count = u.attr_count;
+ users[i].attrs = attrs;
+
+ gg_debug_session(gs, GG_DEBUG_DUMP, " uin=%d, count=%d\n", u.uin, u.attr_count);
+
+ for (j = 0; j < u.attr_count; j++) {
+ uint32_t key_size;
+ uint32_t attr_type;
+ uint32_t value_size;
+ char *key;
+ char *value;
+
+ if (p + sizeof(key_size) > packet_end) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (3)\n");
+ goto malformed;
+ }
+
+ memcpy(&key_size, p, sizeof(key_size));
+ p += sizeof(key_size);
+
+ key_size = gg_fix32(key_size);
+
+ if (key_size > 0xffff || p + key_size > packet_end) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (3)\n");
+ goto malformed;
+ }
+
+ key = (char*)malloc(key_size + 1);
+
+ if (key == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() out of memory (%d)\n", key_size + 1);
+ goto fail;
+ }
+
+ memcpy(key, p, key_size);
+ p += key_size;
+
+ key[key_size] = 0;
+
+ attrs[j].key = key;
+
+ if (p + sizeof(attr_type) + sizeof(value_size) > packet_end) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (4)\n");
+ goto malformed;
+ }
+
+ memcpy(&attr_type, p, sizeof(attr_type));
+ p += sizeof(attr_type);
+ memcpy(&value_size, p, sizeof(value_size));
+ p += sizeof(value_size);
+
+ attrs[j].type = gg_fix32(attr_type);
+ value_size = gg_fix32(value_size);
+
+ if (value_size > 0xffff || p + value_size > packet_end) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (5)\n");
+ goto malformed;
+ }
+
+ value = (char*)malloc(value_size + 1);
+
+ if (value == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() out of memory (%d)\n", value_size + 1);
+ goto fail;
+ }
+
+ memcpy(value, p, value_size);
+ p += value_size;
+
+ value[value_size] = 0;
+
+ attrs[j].value = value;
+
+ gg_debug_session(gs, GG_DEBUG_DUMP, " key=\"%s\", type=%d, value=\"%s\"\n", key, attr_type, value);
+ }
+ }
+
+ return 0;
+
+fail:
+ res = -1;
+
+malformed:
+ ge->type = GG_EVENT_NONE;
+
+ for (i = 0; i < ge->event.user_data.user_count; i++) {
+ for (j = 0; j < ge->event.user_data.users[i].attr_count; j++) {
+ free(ge->event.user_data.users[i].attrs[j].key);
+ free(ge->event.user_data.users[i].attrs[j].value);
+ }
+
+ free(ge->event.user_data.users[i].attrs);
+ }
+
+ free(ge->event.user_data.users);
+
+ return res;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_TYPING_NOTIFICATION.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_typing_notification(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ struct gg_typing_notification *n = (struct gg_typing_notification*) ptr;
+ uin_t uin;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received typing notification\n");
+
+ memcpy(&uin, &n->uin, sizeof(uin_t));
+
+ ge->type = GG_EVENT_TYPING_NOTIFICATION;
+ ge->event.typing_notification.uin = gg_fix32(uin);
+ ge->event.typing_notification.length = gg_fix16(n->length);
+
+ return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet GG_MULTILOGON_INFO.
+ *
+ * Patrz gg_packet_handler_t
+ */
+static int gg_session_handle_multilogon_info(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ char *packet_end = (char*) ptr + len;
+ struct gg_multilogon_info *info = (struct gg_multilogon_info*) ptr;
+ char *p = (char*) ptr + sizeof(*info);
+ struct gg_multilogon_session *sessions = NULL;
+ size_t count;
+ size_t i;
+ int res = 0;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received multilogon info\n");
+
+ count = gg_fix32(info->count);
+
+ if (count > 0xffff) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (1)\n");
+ goto malformed;
+ }
+
+ sessions = (gg_multilogon_session*)calloc(count, sizeof(struct gg_multilogon_session));
+
+ if (sessions == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d*%d)\n", count, sizeof(struct gg_multilogon_session));
+ return -1;
+ }
+
+ ge->type = GG_EVENT_MULTILOGON_INFO;
+ ge->event.multilogon_info.count = count;
+ ge->event.multilogon_info.sessions = sessions;
+
+ for (i = 0; i < count; i++) {
+ struct gg_multilogon_info_item item;
+ size_t name_size;
+
+ if (p + sizeof(item) > packet_end) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (2)\n");
+ goto malformed;
+ }
+
+ memcpy(&item, p, sizeof(item));
+
+ sessions[i].id = item.conn_id;
+ sessions[i].remote_addr = item.addr;
+ sessions[i].status_flags = gg_fix32(item.flags);
+ sessions[i].protocol_features = gg_fix32(item.features);
+ sessions[i].logon_time = gg_fix32(item.logon_time);
+
+ p += sizeof(item);
+
+ name_size = gg_fix32(item.name_size);
+
+ if (name_size > 0xffff || p + name_size > packet_end) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (3)\n");
+ goto malformed;
+ }
+
+ sessions[i].name = (char*)malloc(name_size + 1);
+
+ if (sessions[i].name == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d)\n", name_size);
+ goto fail;
+ }
+
+ memcpy(sessions[i].name, p, name_size);
+ sessions[i].name[name_size] = 0;
+
+ p += name_size;
+ }
+
+ return 0;
+
+fail:
+ res = -1;
+
+malformed:
+ ge->type = GG_EVENT_NONE;
+
+ for (i = 0; i < ge->event.multilogon_info.count; i++)
+ free(ge->event.multilogon_info.sessions[i].name);
+
+ free(ge->event.multilogon_info.sessions);
+
+ return res;
+}
+
+/**
+ * \internal Tablica obsługiwanych pakietów
+ */
+static const gg_packet_handler_t handlers[] =
+{
+ { GG_WELCOME, GG_STATE_READING_KEY, 0, gg_session_handle_welcome },
+ { GG_LOGIN_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
+ { GG_LOGIN80_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
+ { GG_NEED_EMAIL, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
+ { GG_LOGIN_FAILED, GG_STATE_READING_REPLY, 0, gg_session_handle_login_failed },
+ { GG_LOGIN80_FAILED, GG_STATE_READING_REPLY, 0, gg_session_handle_login_failed },
+ { GG_SEND_MSG_ACK, GG_STATE_CONNECTED, sizeof(struct gg_send_msg_ack), gg_session_handle_send_msg_ack },
+ { GG_PONG, GG_STATE_CONNECTED, 0, gg_session_handle_pong },
+ { GG_DISCONNECTING, GG_STATE_CONNECTED, 0, gg_session_handle_disconnecting },
+ { GG_DISCONNECT_ACK, GG_STATE_DISCONNECTING, 0, gg_session_handle_disconnect_ack },
+ { GG_XML_EVENT, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event },
+ { GG_PUBDIR50_REPLY, GG_STATE_CONNECTED, 0, gg_session_handle_pubdir50_reply },
+ { GG_USERLIST_REPLY, GG_STATE_CONNECTED, 0, gg_session_handle_userlist_reply },
+ { GG_DCC7_ID_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_id_reply), gg_session_handle_dcc7_id_reply },
+ { GG_DCC7_ACCEPT, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_accept), gg_session_handle_dcc7_accept },
+ { GG_DCC7_NEW, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_new), gg_session_handle_dcc7_new },
+ { GG_DCC7_REJECT, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_reject), gg_session_handle_dcc7_reject },
+ { GG_DCC7_INFO, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_info), gg_session_handle_dcc7_info },
+ { GG_RECV_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg), gg_session_handle_recv_msg },
+ { GG_RECV_MSG80, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 },
+ { GG_STATUS, GG_STATE_CONNECTED, sizeof(struct gg_status), gg_session_handle_status },
+ { GG_STATUS60, GG_STATE_CONNECTED, sizeof(struct gg_status60), gg_session_handle_status_60_77_80beta },
+ { GG_STATUS77, GG_STATE_CONNECTED, sizeof(struct gg_status77), gg_session_handle_status_60_77_80beta },
+ { GG_STATUS80BETA, GG_STATE_CONNECTED, sizeof(struct gg_status77), gg_session_handle_status_60_77_80beta },
+ { GG_STATUS80, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply80), gg_session_handle_status_80 },
+ { GG_NOTIFY_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply), gg_session_handle_notify_reply },
+ { GG_NOTIFY_REPLY60, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply60), gg_session_handle_notify_reply_60 },
+ { GG_NOTIFY_REPLY77, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply77), gg_session_handle_notify_reply_77_80beta },
+ { GG_NOTIFY_REPLY80BETA, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply77), gg_session_handle_notify_reply_77_80beta },
+ { GG_NOTIFY_REPLY80, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply80), gg_session_handle_notify_reply_80 },
+ { GG_USER_DATA, GG_STATE_CONNECTED, sizeof(struct gg_user_data), gg_session_handle_user_data },
+ { GG_TYPING_NOTIFICATION, GG_STATE_CONNECTED, sizeof(struct gg_typing_notification), gg_session_handle_typing_notification },
+ { GG_MULTILOGON_INFO, GG_STATE_CONNECTED, sizeof(struct gg_multilogon_info), gg_session_handle_multilogon_info },
+ { GG_XML_ACTION, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event },
+ { GG_RECV_OWN_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 },
+};
+
+/**
+ * \internal Analizuje przychodzący pakiet danych.
+ *
+ * \param gs Struktura sesji
+ * \param type Typ pakietu
+ * \param ptr Wskaźnik do bufora pakietu
+ * \param len Długość bufora pakietu
+ * \param[out] ge Struktura zdarzenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_session_handle_packet(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
+{
+ int i;
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet(%d, %p, %d)\n", type, ptr, len);
+
+ gs->last_event = time(NULL);
+
+#if 0
+ if ((gs->flags & (1 << GG_SESSION_FLAG_RAW_PACKET)) != 0) {
+ char *tmp;
+
+ tmp = malloc(len);
+
+ if (tmp == NULL) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet() out of memory (%d bytes)\n", len);
+ return -1;
+ }
+
+ memcpy(tmp, ptr, len);
+
+ ge->type = GG_EVENT_RAW_PACKET;
+ ge->event.raw_packet.type = type;
+ ge->event.raw_packet.length = len;
+ ge->event.raw_packet.data = tmp;
+
+ return 0;
+ }
+#endif
+
+ for (i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) {
+ if (handlers[i].type != 0 && handlers[i].type != type)
+ continue;
+
+ if (handlers[i].state != 0 && handlers[i].state != gs->state) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet() packet 0x%02x unexpected in state %d\n", type, gs->state);
+ continue;
+ }
+
+ if (len < handlers[i].min_length) {
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet() packet 0x%02x too short (%d bytes)\n", type, len);
+ continue;
+ }
+
+ return (*handlers[i].handler)(gs, type, ptr, len, ge);
+ }
+
+ gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet() unhandled packet 0x%02x, len %d, state %d\n", type, len, gs->state);
+
+ return 0;
+}
diff --git a/protocols/Gadu-Gadu/src/libgadu/http.cpp b/protocols/Gadu-Gadu/src/libgadu/http.cpp
index e1cc1b457f..5b8549dc3b 100644
--- a/protocols/Gadu-Gadu/src/libgadu/http.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/http.cpp
@@ -1,4 +1,3 @@
-/* coding: UTF-8 */
/* $Id: http.c 11370 2010-03-13 16:17:54Z dezred $ */
/*
@@ -137,21 +136,26 @@ 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 addr;
+ struct in_addr *addr_list = NULL;
+ int addr_count;
- if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
+ if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 0) == -1 || addr_count == 0) {
gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
gg_http_free(h);
+ free(addr_list);
errno = ENOENT;
return NULL;
}
- if ((h->fd = gg_connect(&addr, port, 0)) == -1) {
+ if ((h->fd = gg_connect(&addr_list[0], port, 0)) == -1) {
gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno));
gg_http_free(h);
+ free(addr_list);
return NULL;
}
+ free(addr_list);
+
h->state = GG_STATE_CONNECTING;
while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) {
diff --git a/protocols/Gadu-Gadu/src/libgadu/internal.h b/protocols/Gadu-Gadu/src/libgadu/internal.h
index 0ffd39fdd5..ba9655288a 100644
--- a/protocols/Gadu-Gadu/src/libgadu/internal.h
+++ b/protocols/Gadu-Gadu/src/libgadu/internal.h
@@ -1,48 +1,44 @@
-/* coding: UTF-8 */
-/* $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"
-
+/* $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"
+
struct gg_dcc7_relay {
uint32_t addr;
uint16_t port;
uint8_t family;
};
-typedef struct gg_dcc7_relay gg_dcc7_relay_t;
-
-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);
-void gg_debug_dump_session(struct gg_session *sess, const void *buf, unsigned int buf_length, const char *format, ...);
-
-int gg_resolve(int *fd, int *pid, const char *hostname);
-int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
-void gg_resolve_pthread_cleanup(void *resolver, int kill);
-
+typedef struct gg_dcc7_relay gg_dcc7_relay_t;
+
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length);
+
+int gg_resolve(int *fd, int *pid, const char *hostname);
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
+void gg_resolve_pthread_cleanup(void *resolver, int kill);
+
#ifdef GG_CONFIG_HAVE_UINT64_T
uint64_t gg_fix64(uint64_t x);
-#endif
-
-#endif /* LIBGADU_INTERNAL_H */
+#endif
+
+#endif /* LIBGADU_INTERNAL_H */
diff --git a/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp b/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp
index 197c6093d3..be9c2499b6 100644
--- a/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp
@@ -1,5 +1,4 @@
-/* coding: UTF-8 */
-/* $Id: libgadu.c 13762 2011-08-09 12:35:16Z dezred $ */
+/* $Id$ */
/*
* (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -50,6 +49,9 @@
#include "protocol.h"
#include "resolver.h"
#include "internal.h"
+#include "encoding.h"
+#include "debug.h"
+#include "session.h"
#include <errno.h>
#ifndef _WIN32
@@ -64,53 +66,19 @@
#ifndef _WIN32
#include <unistd.h>
#endif /* _WIN32 */
-#if !defined(GG_CONFIG_MIRANDA) && defined(GG_CONFIG_HAVE_OPENSSL)
+#if !defined(GG_CONFIG_MIRANDA)
+#ifdef GG_CONFIG_HAVE_GNUTLS
+# include <gnutls/gnutls.h>
+#endif
+#ifdef GG_CONFIG_HAVE_OPENSSL
# include <openssl/err.h>
# include <openssl/rand.h>
#endif
+#endif
extern SSL_API sslApi;
/**
- * 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
@@ -180,7 +148,7 @@ static char rcsid[]
#ifdef __GNUC__
__attribute__ ((unused))
#endif
-= "$Id: libgadu.c 13762 2011-08-09 12:35:16Z dezred $";
+= "$Id$";
#endif
#endif /* DOXYGEN */
@@ -323,13 +291,35 @@ unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
*/
int gg_read(struct gg_session *sess, char *buf, int length)
{
+ int res;
+
#ifdef GG_CONFIG_MIRANDA
if (sess->ssl != NULL)
return sslApi.read(sess->ssl, buf, length, 0);
+#elif GG_CONFIG_HAVE_GNUTLS
+ if (sess->ssl != NULL) {
+ for (;;) {
+ res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length);
+
+ if (res < 0) {
+ if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
+ continue;
+
+ if (res == GNUTLS_E_AGAIN)
+ errno = EAGAIN;
+ else
+ errno = EINVAL;
+
+ return -1;
+ }
+
+ return res;
+ }
+ }
#elif GG_CONFIG_HAVE_OPENSSL
if (sess->ssl != NULL) {
for (;;) {
- int res, err;
+ int err;
res = SSL_read(sess->ssl, buf, length);
@@ -352,7 +342,14 @@ int gg_read(struct gg_session *sess, char *buf, int length)
}
#endif
- return gg_sock_read(sess->fd, buf, length);
+ for (;;) {
+ res = gg_sock_read(sess->fd, buf, length);
+
+ if (res == -1 && errno == EINTR)
+ continue;
+
+ return res;
+ }
}
/**
@@ -373,13 +370,35 @@ int gg_read(struct gg_session *sess, char *buf, int length)
*/
static int gg_write_common(struct gg_session *sess, const char *buf, int length)
{
+ int res;
+
#ifdef GG_CONFIG_MIRANDA
if (sess->ssl != NULL)
return sslApi.write(sess->ssl, buf, length);
+#elif GG_CONFIG_HAVE_GNUTLS
+ if (sess->ssl != NULL) {
+ for (;;) {
+ res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length);
+
+ if (res < 0) {
+ if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
+ continue;
+
+ if (res == GNUTLS_E_AGAIN)
+ errno = EAGAIN;
+ else
+ errno = EINVAL;
+
+ return -1;
+ }
+
+ return res;
+ }
+ }
#elif GG_CONFIG_HAVE_OPENSSL
if (sess->ssl != NULL) {
for (;;) {
- int res, err;
+ int err;
res = SSL_write(sess->ssl, (void *)buf, length);
@@ -402,7 +421,14 @@ static int gg_write_common(struct gg_session *sess, const char *buf, int length)
}
#endif
- return gg_sock_write(sess->fd, buf, length);
+ for (;;) {
+ res = gg_sock_write(sess->fd, buf, length);
+
+ if (res == -1 && errno == EINTR)
+ continue;
+
+ return res;
+ }
}
@@ -480,7 +506,7 @@ int gg_write(struct gg_session *sess, const char *buf, int length)
void *gg_recv_packet(struct gg_session *sess)
{
struct gg_header h;
- char *buf = NULL;
+ char *packet;
int ret = 0;
unsigned int offset, size = 0;
@@ -533,7 +559,6 @@ void *gg_recv_packet(struct gg_session *sess)
}
sess->header_done += ret;
-
}
h.type = gg_fix32(h.type);
@@ -552,26 +577,25 @@ void *gg_recv_packet(struct gg_session *sess)
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 = (char*)malloc(sizeof(h) + h.length + 1))) {
+ if (!(sess->recv_buf = (char*)malloc(sizeof(h) + h.length + 1))) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
return NULL;
}
- memcpy(buf, &h, sizeof(h));
+ memcpy(sess->recv_buf, &h, sizeof(h));
offset = 0;
size = h.length;
}
while (size > 0) {
- ret = gg_read(sess, buf + sizeof(h) + offset, size);
- 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);
+ ret = gg_read(sess, sess->recv_buf + sizeof(h) + offset, size);
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, sess->recv_buf + sizeof(h) + offset, size, ret);
if (!ret) {
gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
errno = ECONNRESET;
- return NULL;
+ goto fail;
}
if (ret > -1 && ret <= (int)size) {
offset += ret;
@@ -581,22 +605,30 @@ void *gg_recv_packet(struct gg_session *sess)
if (errno == EAGAIN) {
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;
return NULL;
}
- free(buf);
- return NULL;
+ goto fail;
}
}
+ packet = sess->recv_buf;
+ sess->recv_buf = NULL;
sess->recv_left = 0;
- gg_debug_dump_session(sess, buf, sizeof(h) + h.length, "// gg_recv_packet(0x%.2x)", h.type);
+ gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(type=0x%.2x, length=%d)\n", h.type, h.length);
+ gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(h) + h.length);
+
+ return packet;
+
+fail:
+ free(sess->recv_buf);
+ sess->recv_buf = NULL;
+ sess->recv_left = 0;
- return buf;
+ return NULL;
}
/**
@@ -662,7 +694,8 @@ int gg_send_packet(struct gg_session *sess, int type, ...)
h->type = gg_fix32(type);
h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
- gg_debug_dump_session(sess, tmp, tmp_length, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
+ gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(type=0x%.2x, length=%d)\n", gg_fix32(h->type), gg_fix32(h->length));
+ gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length);
res = gg_write(sess, tmp, tmp_length);
@@ -833,7 +866,7 @@ struct gg_session *gg_login(const struct gg_login_params *p)
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);
+ sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1);
else
sess->initial_descr = strdup(p->status_descr);
@@ -852,7 +885,25 @@ struct gg_session *gg_login(const struct gg_login_params *p)
sess->tls = p->tls;
#endif
if (p->tls == 1) {
-#ifdef GG_CONFIG_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_GNUTLS
+ gg_session_gnutls_t *tmp;
+
+ tmp = malloc(sizeof(gg_session_gnutls_t));
+
+ if (tmp == NULL) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() out of memory for GnuTLS session\n");
+ goto fail;
+ }
+
+ sess->ssl = tmp;
+
+ gnutls_global_init();
+ gnutls_certificate_allocate_credentials(&tmp->xcred);
+ gnutls_init(&tmp->session, GNUTLS_CLIENT);
+ gnutls_priority_set_direct(tmp->session, "NORMAL:-VERS-TLS", NULL);
+// gnutls_priority_set_direct(tmp->session, "NONE:+VERS-SSL3.0:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL", NULL);
+ gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred);
+#elif defined(GG_CONFIG_HAVE_OPENSSL)
char buf[1024];
OpenSSL_add_ssl_algorithms();
@@ -911,14 +962,22 @@ struct gg_session *gg_login(const struct gg_login_params *p)
if (!sess->server_addr) {
if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
- if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
+ struct in_addr *addr_list = NULL;
+ int addr_count;
+
+ if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 0) == -1 || addr_count == 0) {
gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname);
+ free(addr_list);
#ifdef GG_CONFIG_MIRANDA
errno = EACCES;
*gg_failno = GG_FAILURE_RESOLVING;
#endif
goto fail;
}
+
+ addr = addr_list[0];
+
+ free(addr_list);
}
} else {
addr.s_addr = sess->server_addr;
@@ -1067,6 +1126,9 @@ void gg_logoff(struct gg_session *sess)
#ifdef GG_CONFIG_MIRANDA
if (sess->ssl != NULL)
sslApi.shutdown(sess->ssl);
+#elif GG_CONFIG_HAVE_GNUTLS
+ if (sess->ssl != NULL)
+ gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR);
#elif GG_CONFIG_HAVE_OPENSSL
if (sess->ssl != NULL)
SSL_shutdown(sess->ssl);
@@ -1080,6 +1142,18 @@ void gg_logoff(struct gg_session *sess)
sess->fd = -1;
}
+#ifdef GG_CONFIG_HAVE_GNUTLS
+ if (sess->ssl != NULL) {
+ gg_session_gnutls_t *tmp;
+
+ tmp = (gg_session_gnutls_t*) sess->ssl;
+ gnutls_deinit(tmp->session);
+ gnutls_certificate_free_credentials(tmp->xcred);
+ gnutls_global_deinit();
+ free(sess->ssl);
+ }
+#endif
+
if (sess->send_buf) {
free(sess->send_buf);
sess->send_buf = NULL;
@@ -1108,6 +1182,7 @@ void gg_free_session(struct gg_session *sess)
free(sess->password);
free(sess->initial_descr);
free(sess->client_version);
+ free(sess->recv_buf);
free(sess->header_buf);
#ifdef GG_CONFIG_MIRANDA
@@ -1181,7 +1256,7 @@ static int gg_change_status_common(struct gg_session *sess, int status, const ch
if (sess->protocol_version >= 0x2d) {
if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
- new_descr = gg_cp_to_utf8(descr);
+ new_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1);
if (!new_descr)
return -1;
@@ -1246,6 +1321,12 @@ static int gg_change_status_common(struct gg_session *sess, int status, const ch
}
free(new_descr);
+
+ if (GG_S_NA(status)) {
+ sess->state = GG_STATE_DISCONNECTING;
+ sess->timeout = GG_TIMEOUT_DISCONNECT;
+ }
+
return res;
}
@@ -1652,13 +1733,13 @@ int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int r
}
if (sess->encoding == GG_ENCODING_UTF8) {
- if (!(cp_msg = gg_utf8_to_cp((const char *) message)))
+ if (!(cp_msg = gg_encoding_convert((const char *)message, GG_ENCODING_UTF8, GG_ENCODING_CP1250, -1, -1)))
return -1;
utf_msg = (char*) message;
} else {
if (sess->protocol_version >= 0x2d) {
- if (!(utf_msg = gg_cp_to_utf8((const char *) message)))
+ if (!(utf_msg = gg_encoding_convert((const char *)message, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1)))
return -1;
}
diff --git a/protocols/Gadu-Gadu/src/libgadu/libgadu.h b/protocols/Gadu-Gadu/src/libgadu/libgadu.h
index 33224836c3..44e8318b8d 100644
--- a/protocols/Gadu-Gadu/src/libgadu/libgadu.h
+++ b/protocols/Gadu-Gadu/src/libgadu/libgadu.h
@@ -1,1023 +1,1017 @@
-/* coding: UTF-8 */
-/* $Id: libgadu.h 13762 2011-08-09 12:35:16Z dezred $ */
-
-/*
- * (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
- * 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 libgadu.h
- *
- * \brief Główny plik nagłówkowy biblioteki
- */
-
-#ifndef __GG_LIBGADU_H
-#define __GG_LIBGADU_H
-
-/* Defined if libgadu should be compatible with Miranda. */
-#define GG_CONFIG_MIRANDA
-
-#ifdef GG_CONFIG_MIRANDA
-#include <m_ssl.h>
-#include <m_netlib.h>
-#endif
-
-#if defined(__cplusplus) || defined(_WIN32)
-#pragma pack(push, 1)
-#endif
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-/** \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. */
-#define GG_CONFIG_HAVE_PTHREAD
-
-/* Defined if pthread resolver is the default one. */
-#define 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. */
-/* Visual C++ 6.0 has no long long */
-#if !defined(_MSC_VER) || (_MSC_VER >= 1300)
-#define GG_CONFIG_HAVE_LONG_LONG
-#endif
-
-/* Defined if libgadu was compiled and linked with OpenSSL 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
-
-/* MSC have no va_copy */
-#ifndef _MSC_VER
-#define GG_CONFIG_HAVE_VA_COPY
-#define GG_CONFIG_HAVE___VA_COPY
-#endif
-
+/* $Id: libgadu.h 13762 2011-08-09 12:35:16Z dezred $ */
+
+/*
+ * (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
+ * 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 libgadu.h
+ *
+ * \brief Główny plik nagłówkowy biblioteki
+ */
+
+#ifndef __GG_LIBGADU_H
+#define __GG_LIBGADU_H
+
+/* Defined if libgadu should be compatible with Miranda. */
+#define GG_CONFIG_MIRANDA
+
+#ifdef GG_CONFIG_MIRANDA
+#include <m_ssl.h>
+#include <m_netlib.h>
+#endif
+
+#if defined(__cplusplus) || defined(_WIN32)
+#pragma pack(push, 1)
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+/** \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. */
+#define GG_CONFIG_HAVE_PTHREAD
+
+/* Defined if pthread resolver is the default one. */
+#define 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. */
+#define GG_CONFIG_HAVE_LONG_LONG
+
+/* Defined if libgadu was compiled and linked with GnuTLS support. */
+#undef GG_CONFIG_HAVE_GNUTLS
+
+/* Defined if libgadu was compiled and linked with OpenSSL 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
+
+/* MSC have no va_copy */
+#ifndef _MSC_VER
+#define GG_CONFIG_HAVE_VA_COPY
+#define GG_CONFIG_HAVE___VA_COPY
+#endif
+
#if defined(GG_CONFIG_HAVE_OPENSSL) && !defined(GG_CONFIG_MIRANDA)
-#include <openssl/ssl.h>
-#endif
-
-#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;
+#include <openssl/ssl.h>
+#endif
+
+#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;
#ifdef GG_CONFIG_HAVE_LONG_LONG
typedef unsigned long long uint64_t;
#define GG_CONFIG_HAVE_UINT64_T
-#endif
-
-#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
-
-#ifdef _WIN32
-# define kill(pid,sig)
-# ifdef _MSC_VER
-# define vsnprintf _vsnprintf
-# define stat _stat
-# ifndef strdup
-# define strdup _strdup
-# endif
-# define strncasecmp _strnicmp
-# define vsnprintf _vsnprintf
-# define snprintf _snprintf
-# define strcasecmp _stricmp
-# define GG_CONFIG_HAVE_STRTOULL
-# define strtoull _strtoui64
-# endif
-# define gg_sock_write(sock,buf,len) send(sock,(const char*)(buf),len,0)
-# define gg_sock_read(sock,buf,len) recv(sock,(char*)(buf),len,0)
-# define gg_sock_close(sock) closesocket(sock)
-# define gg_getsockopt(sock,level,name,val,len) getsockopt(sock,level,name,(char *)val,len)
-#else
- typedef int SOCKET;
-# define gg_sock_write write
-# define gg_sock_read read
-# define gg_sock_close close
-# define gg_getsockopt getsockopt
-#endif
-
-/** \endcond */
-
-/**
- * Numer Gadu-Gadu.
- */
-typedef uint32_t uin_t;
-
-/**
- * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x.
- */
-typedef struct {
- uint8_t id[8];
-} gg_dcc7_id_t;
-
+#endif
+
+#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
+
+#ifdef _WIN32
+# define kill(pid,sig)
+# ifdef _MSC_VER
+# define vsnprintf _vsnprintf
+# define stat _stat
+# ifndef strdup
+# define strdup _strdup
+# endif
+# define strncasecmp _strnicmp
+# define vsnprintf _vsnprintf
+# define snprintf _snprintf
+# define strcasecmp _stricmp
+# define GG_CONFIG_HAVE_STRTOULL
+# define strtoull _strtoui64
+# endif
+# define gg_sock_write(sock,buf,len) send(sock,(const char*)(buf),len,0)
+# define gg_sock_read(sock,buf,len) recv(sock,(char*)(buf),len,0)
+# define gg_sock_close(sock) closesocket(sock)
+# define gg_getsockopt(sock,level,name,val,len) getsockopt(sock,level,name,(char *)val,len)
+#else
+ typedef int SOCKET;
+# define gg_sock_write write
+# define gg_sock_read read
+# define gg_sock_close close
+# define gg_getsockopt getsockopt
+#endif
+
+/** \endcond */
+
+/**
+ * Numer Gadu-Gadu.
+ */
+typedef uint32_t uin_t;
+
+/**
+ * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x.
+ */
+typedef struct {
+ uint8_t id[8];
+} gg_dcc7_id_t;
+
/**
* Identyfikator sesji multilogowania.
*/
typedef struct {
uint8_t id[8];
} gg_multilogon_id_t;
-
-/**
- * Makro deklarujące pola wspólne dla struktur sesji.
- */
-#define gg_common_head(x) \
- SOCKET 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_dcc7;
-
+
+/**
+ * Makro deklarujące pola wspólne dla struktur sesji.
+ */
+#define gg_common_head(x) \
+ SOCKET 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_dcc7;
+
struct gg_dcc7_relay;
-
-/**
- * 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().
- *
- * \ingroup login
- */
-struct gg_session {
- gg_common_head(struct gg_session)
-
- 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 wywołaniu \c callback */
-
- 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 rozwiązaniu nazwy */
- uint32_t server_addr; /**< Adres serwera otrzymany od huba */
-
- 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; /**< 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; /**< Własny numer Gadu-Gadu */
- char *password; /**< Hasło (zwalniane po użyciu) */
-
- int initial_status; /**< Początkowy status */
- int status; /**< Aktualny status */
-
- 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 protokołu (bez flag) */
- char *client_version; /**< Wersja klienta */
- int last_sysmsg; /**< Numer ostatniej wiadomości systemowej */
-
- char *initial_descr; /**< Początkowy opis statusu */
-
- void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
-
- char *header_buf; /**< Bufor na początek nagłówka pakietu */
- unsigned int header_done; /**< Liczba wczytanych bajtów nagłówka pakietu */
-
-#ifdef GG_CONFIG_MIRANDA
- HSSL ssl;
- int tls; /**< Flaga połączenia szyfrowanego */
-#elif GG_CONFIG_HAVE_OPENSSL
- SSL *ssl; /**< Struktura TLS */
- SSL_CTX *ssl_ctx; /**< Kontekst sesji TLS */
-#else
- void *ssl; /**< Struktura TLS */
- void *ssl_ctx; /**< Kontekst sesji TLS */
-#endif
-
- 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 */
-
- struct gg_image_queue *images; /**< Lista wczytywanych obrazków */
-
- int hash_type; /**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
-
- 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)(SOCKET *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 */
- int status_flags; /**< Flagi statusu */
-};
-
-/**
- * Połączenie HTTP.
- *
- * 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; /**< 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 */
-
- void *data; /**< Dane prywatne usługi HTTP */
-
- char *user_data; /**< Dane prywatne użytkownika (nie są zwalniane) */
-
- void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę */
-
- unsigned int body_done; /**< Liczba odebranych bajtów strony */
-
- gg_resolver_t resolver_type; /**< Sposób rozwiązywania nazw serwerów */
- int (*resolver_start)(SOCKET *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
-
-/** \endcond */
-
-#define GG_MAX_PATH 276 /**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
-
-/**
- * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
- *
- * 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 */
-} /** \cond ignore */ GG_PACKED /** \endcond */;
-
-/**
- * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x.
- *
- * 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; /**< 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; /**< 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 */
-
-#ifdef GG_CONFIG_MIRANDA
- void *contact;
- char *folder;
- uint32_t tick;
-#endif
-};
-
-#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 */
+
+/**
+ * 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().
+ *
+ * \ingroup login
+ */
+struct gg_session {
+ gg_common_head(struct gg_session)
+
+ 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 wywołaniu \c callback */
+
+ 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 rozwiązaniu nazwy */
+ uint32_t server_addr; /**< Adres serwera otrzymany od huba */
+
+ 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; /**< 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; /**< Własny numer Gadu-Gadu */
+ char *password; /**< Hasło (zwalniane po użyciu) */
+
+ int initial_status; /**< Początkowy status */
+ int status; /**< Aktualny status */
+
+ 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 protokołu (bez flag) */
+ char *client_version; /**< Wersja klienta */
+ int last_sysmsg; /**< Numer ostatniej wiadomości systemowej */
+
+ char *initial_descr; /**< Początkowy opis statusu */
+
+ void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
+
+ char *header_buf; /**< Bufor na początek nagłówka pakietu */
+ unsigned int header_done; /**< Liczba wczytanych bajtów nagłówka pakietu */
+
+#ifdef GG_CONFIG_MIRANDA
+ HSSL ssl;
+ int tls; /**< Flaga połączenia szyfrowanego */
+#elif GG_CONFIG_HAVE_OPENSSL
+ SSL *ssl; /**< Struktura TLS */
+ SSL_CTX *ssl_ctx; /**< Kontekst sesji TLS */
+#else
+ void *ssl; /**< Struktura TLS */
+ void *ssl_ctx; /**< Kontekst sesji TLS */
+#endif
+
+ 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 */
+
+ struct gg_image_queue *images; /**< Lista wczytywanych obrazków */
+
+ int hash_type; /**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
+
+ 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)(SOCKET *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 */
+ int status_flags; /**< Flagi statusu */
+ int recv_msg_count; /**< Liczba odebranych wiadomości */
+};
+
+/**
+ * Połączenie HTTP.
+ *
+ * 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; /**< 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 */
+
+ void *data; /**< Dane prywatne usługi HTTP */
+
+ char *user_data; /**< Dane prywatne użytkownika (nie są zwalniane) */
+
+ void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę */
+
+ unsigned int body_done; /**< Liczba odebranych bajtów strony */
+
+ gg_resolver_t resolver_type; /**< Sposób rozwiązywania nazw serwerów */
+ int (*resolver_start)(SOCKET *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
+
+/** \endcond */
+
+#define GG_MAX_PATH 276 /**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
+
+/**
+ * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
+ *
+ * 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 */
+} /** \cond ignore */ GG_PACKED /** \endcond */;
+
+/**
+ * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x.
+ *
+ * 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; /**< 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; /**< 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 */
+
+#ifdef GG_CONFIG_MIRANDA
+ void *contact;
+ char *folder;
+ uint32_t tick;
+#endif
+};
+
+#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 32 /**< Maksymalny rozmiar informacji o połączeniach bezpośrednich */
#define GG_DCC7_INFO_HASH_LEN 32 /**< Maksymalny rozmiar skrótu ip informacji o połączeniach bezpośrednich */
-
-/**
- * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x.
- *
- * \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 */
-
+
+/**
+ * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x.
+ *
+ * \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 */
+
void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
int relay; /**< Flaga mówiąca, że laczymy sie przez serwer */
int relay_index; /**< Numer serwera pośredniczącego, do którego się łączymy */
int relay_count; /**< Rozmiar listy serwerów pośredniczących */
struct gg_dcc7_relay *relay_list; /**< Lista serwerów pośredniczących */
-
-#ifdef GG_CONFIG_MIRANDA
- void *contact;
- char *folder;
- uint32_t tick;
-#endif
-};
-
-/**
- * Rodzaj sesji.
- */
-enum gg_session_t {
- 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 */
-};
-
-/**
- * Aktualny stan sesji.
- */
-enum gg_state_t {
- /* 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 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, /* 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, /* wysyła potwierdzenie pliku */
- GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */
- 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, /* 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, /* 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, /**< 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 identyfikator połączenia bezpośredniego */
- GG_STATE_RESOLVING_GG, /**< Oczekiwanie na rozwiązanie nazwy serwera Gadu-Gadu */
-
+
+#ifdef GG_CONFIG_MIRANDA
+ void *contact;
+ char *folder;
+ uint32_t tick;
+#endif
+};
+
+/**
+ * Rodzaj sesji.
+ */
+enum gg_session_t {
+ 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 */
+};
+
+/**
+ * Aktualny stan sesji.
+ */
+enum gg_state_t {
+ /* 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 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, /* 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, /* wysyła potwierdzenie pliku */
+ GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */
+ 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, /* 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, /* 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, /**< 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 identyfikator połączenia bezpośredniego */
+ GG_STATE_RESOLVING_GG, /**< Oczekiwanie na rozwiązanie nazwy serwera Gadu-Gadu */
+
GG_STATE_RESOLVING_RELAY, /**< Oczekiwanie na rozwiązanie nazwy serwera pośredniczącego */
GG_STATE_CONNECTING_RELAY, /**< Oczekiwanie na połączenie z serwerem pośredniczącym */
- GG_STATE_READING_RELAY /**< Odbieranie danych */
-};
-
-/**
- * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać
- * z deskryptora. Maska bitowa.
- *
- * \ingroup events
- */
-enum gg_check_t {
- GG_CHECK_NONE = 0, /**< Nie sprawdzaj niczego */
- GG_CHECK_WRITE = 1, /**< Sprawdź możliwość zapisu */
- GG_CHECK_READ = 2 /**< Sprawdź możliwość odczytu */
-};
-
-/**
- * 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.
- *
- * \ingroup login
- */
-struct gg_login_params {
- 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_*). */
- int status_flags; /**< Flagi statusu (flagi GG_STATUS_FLAG_*, patrz \ref status). */
-
-#ifndef DOXYGEN
- char dummy[1 * sizeof(int)]; /**< \internal Miejsce na kilka kolejnych
- parametrów, żeby wraz z dodawaniem kolejnych
- parametrów nie zmieniał się rozmiar struktury */
-#endif
-
-};
-
-#ifdef GG_CONFIG_MIRANDA
-struct gg_session *gg_login(const struct gg_login_params *p, SOCKET *gg_sock, int *gg_failno);
-#else
-struct gg_session *gg_login(const struct gg_login_params *p);
-#endif
-void gg_free_session(struct gg_session *sess);
-void gg_logoff(struct gg_session *sess);
-int gg_change_status(struct gg_session *sess, int status);
-int gg_change_status_descr(struct gg_session *sess, int status, const char *descr);
-int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time);
-int gg_change_status_flags(struct gg_session *sess, int flags);
-int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message);
-int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen);
-int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message);
-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);
-int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len);
-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 char *image, int size);
-int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length);
-
-uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
-
-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)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
-
-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)(SOCKET*, 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)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
-
+ GG_STATE_READING_RELAY, /**< Odbieranie danych */
+
+ GG_STATE_DISCONNECTING, /**< Oczekiwanie na potwierdzenie rozłączenia */
+};
+
+/**
+ * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać
+ * z deskryptora. Maska bitowa.
+ *
+ * \ingroup events
+ */
+enum gg_check_t {
+ GG_CHECK_NONE = 0, /**< Nie sprawdzaj niczego */
+ GG_CHECK_WRITE = 1, /**< Sprawdź możliwość zapisu */
+ GG_CHECK_READ = 2 /**< Sprawdź możliwość odczytu */
+};
+
+/**
+ * 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.
+ *
+ * \ingroup login
+ */
+struct gg_login_params {
+ 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_*). */
+ int status_flags; /**< Flagi statusu (flagi GG_STATUS_FLAG_*, patrz \ref status). */
+
+#ifndef DOXYGEN
+ char dummy[1 * sizeof(int)]; /**< \internal Miejsce na kilka kolejnych
+ parametrów, żeby wraz z dodawaniem kolejnych
+ parametrów nie zmieniał się rozmiar struktury */
+#endif
+
+};
+
+#ifdef GG_CONFIG_MIRANDA
+struct gg_session *gg_login(const struct gg_login_params *p, SOCKET *gg_sock, int *gg_failno);
+#else
+struct gg_session *gg_login(const struct gg_login_params *p);
+#endif
+void gg_free_session(struct gg_session *sess);
+void gg_logoff(struct gg_session *sess);
+int gg_change_status(struct gg_session *sess, int status);
+int gg_change_status_descr(struct gg_session *sess, int status, const char *descr);
+int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time);
+int gg_change_status_flags(struct gg_session *sess, int flags);
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message);
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen);
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message);
+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);
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len);
+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 char *image, int size);
+int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length);
+
+uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
+
+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)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
+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)(SOCKET*, 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)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id);
-
-/**
- * Rodzaj zdarzenia.
- *
- * \ingroup events
- */
-enum gg_event_t {
- 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. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
- 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. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
- GG_EVENT_USERLIST, /**< Wynik importu lub eksportu listy kontaktów */
- GG_EVENT_IMAGE_REQUEST, /**< Żądanie przesłania obrazka z wiadomoś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. */
- GG_EVENT_XML_ACTION,
- GG_EVENT_TYPING_NOTIFICATION, /**< Powiadomienie o pisaniu */
- GG_EVENT_USER_DATA, /**< Informacja o kontaktach */
+
+/**
+ * Rodzaj zdarzenia.
+ *
+ * \ingroup events
+ */
+enum gg_event_t {
+ 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. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
+ 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. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
+ GG_EVENT_USERLIST, /**< Wynik importu lub eksportu listy kontaktów */
+ GG_EVENT_IMAGE_REQUEST, /**< Żądanie przesłania obrazka z wiadomoś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. */
+ GG_EVENT_TYPING_NOTIFICATION, /**< Powiadomienie o pisaniu */
+ GG_EVENT_USER_DATA, /**< Informacja o kontaktach */
GG_EVENT_MULTILOGON_MSG, /**< Wiadomość wysłana z innej sesji multilogowania */
GG_EVENT_MULTILOGON_INFO /**< Informacja o innych sesjach multilogowania */
-};
-
-#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
-
-/**
- * Powód nieudanego połączenia.
- */
-enum gg_failure_t {
- 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 */
-};
-
-/**
- * Kod błędu danej operacji.
- *
- * 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, /**< 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 */
+};
+
+#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
+
+/**
+ * Powód nieudanego połączenia.
+ */
+enum gg_failure_t {
+ 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 */
+};
+
+/**
+ * Kod błędu danej operacji.
+ *
+ * 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, /**< 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 */
GG_ERROR_DCC7_RELAY /**< Problem z serwerem pośredniczącym */
-};
-
-/**
- * Pole zapytania lub odpowiedzi katalogu publicznego.
- */
-struct gg_pubdir50_entry {
- 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; /**< 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.
- *
- * 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;
-
-/**
+};
+
+/**
+ * Pole zapytania lub odpowiedzi katalogu publicznego.
+ */
+struct gg_pubdir50_entry {
+ 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; /**< 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.
+ *
+ * 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;
+
+/**
* Opis zdarzeń \c GG_EVENT_MSG i \c GG_EVENT_MULTILOGON_MSG.
- */
-struct gg_event_msg {
- uin_t sender; /**< Numer nadawcy/odbiorcy */
- int msgclass; /**< Klasa wiadomości */
- time_t time; /**< Czas nadania */
- 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) */
-};
-
-/**
- * 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 */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_STATUS.
- */
-struct gg_event_status {
- uin_t uin; /**< Numer Gadu-Gadu */
- uint32_t status; /**< Nowy status */
- char *descr; /**< Opis */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_STATUS60.
- */
-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 */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60.
- */
-struct gg_event_notify60 {
- uin_t uin; /**< Numer Gadu-Gadu. W ostatnim elemencie jest równy 0, a pozostałe pola są niezainicjowane. */
- 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 */
-};
-
-/**
- * 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 */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_USERLIST.
- */
-struct gg_event_userlist {
- char type; /**< Rodzaj odpowiedzi */
- char *reply; /**< Treść odpowiedzi */
-};
-
-/**
- * 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 */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST.
- */
-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 */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_XML_EVENT.
- */
-struct gg_event_xml_event {
- char *data; /**< Bufor z komunikatem */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_XML_ACTION.
- */
-struct gg_event_xml_action {
- char *data; /**< Bufor z komunikatem */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
- */
-struct gg_event_dcc7_connected {
- struct gg_dcc7 *dcc7; /**< Struktura połączenia */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_DCC7_PENDING.
- */
-struct gg_event_dcc7_pending {
- struct gg_dcc7 *dcc7; /**< Struktura połączenia */
-};
-
-/**
- * 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 */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_DCC7_DONE.
- */
-struct gg_event_dcc7_done {
- struct gg_dcc7 *dcc7; /**< Struktura połączenia */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_DCC7_ERROR.
- *
- * \note Odwrotna kolejność pól ma na celu zachowanie ABI.
- */
-struct gg_event_dcc7_error {
- enum gg_error_t error; /**< Kod błędu */
- struct gg_dcc7 *dcc7; /**< Struktura połączenia */
-};
-
-/**
- * Opis zdarzenia \c GG_EVENT_TYPING_NOTIFICATION.
- */
-struct gg_event_typing_notification {
- uin_t uin; /**< Numer rozmówcy */
- int length; /**< Długość tekstu */
-};
-
+ */
+struct gg_event_msg {
+ uin_t sender; /**< Numer nadawcy/odbiorcy */
+ int msgclass; /**< Klasa wiadomości */
+ time_t time; /**< Czas nadania */
+ 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) */
+};
+
+/**
+ * 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 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS.
+ */
+struct gg_event_status {
+ uin_t uin; /**< Numer Gadu-Gadu */
+ uint32_t status; /**< Nowy status */
+ char *descr; /**< Opis */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS60.
+ */
+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 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60.
+ */
+struct gg_event_notify60 {
+ uin_t uin; /**< Numer Gadu-Gadu. W ostatnim elemencie jest równy 0, a pozostałe pola są niezainicjowane. */
+ 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 */
+};
+
+/**
+ * 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 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_USERLIST.
+ */
+struct gg_event_userlist {
+ char type; /**< Rodzaj odpowiedzi */
+ char *reply; /**< Treść odpowiedzi */
+};
+
+/**
+ * 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 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST.
+ */
+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 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_XML_EVENT.
+ */
+struct gg_event_xml_event {
+ char *data; /**< Bufor z komunikatem */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
+ */
+struct gg_event_dcc7_connected {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_PENDING.
+ */
+struct gg_event_dcc7_pending {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+};
+
+/**
+ * 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 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_DONE.
+ */
+struct gg_event_dcc7_done {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_ERROR.
+ *
+ * \note Odwrotna kolejność pól ma na celu zachowanie ABI.
+ */
+struct gg_event_dcc7_error {
+ enum gg_error_t error; /**< Kod błędu */
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_TYPING_NOTIFICATION.
+ */
+struct gg_event_typing_notification {
+ uin_t uin; /**< Numer rozmówcy */
+ int length; /**< Długość tekstu */
+};
+
/**
* Atrybut użytkownika.
*/
@@ -1044,7 +1038,7 @@ struct gg_event_user_data {
size_t user_count; /**< Liczba kontaktów */
struct gg_event_user_data_user *users; /**< Lista kontaktów */
};
-
+
/**
* Struktura opisująca sesję multilogowania.
*/
@@ -1064,1241 +1058,1240 @@ struct gg_event_multilogon_info {
int count; /**< Liczba sesji */
struct gg_multilogon_session *sessions; /** Lista sesji */
};
-
-/**
- * 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_event_xml_action xml_action; /**< Zdarzenie XML (\c GG_EVENT_XML_ACTION) */
- 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_error dcc7_error_ex; /**< Błąd połączenia bezpośredniego ze wskaźnikiem na strukturę połączenia (\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_pending dcc7_pending; /**< Trwa próba połączenia bezpośredniego (\c GG_EVENT_DCC7_PENDING) */
- 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) */
- struct gg_event_dcc7_done dcc7_done; /**< Zakończono połączenie bezpośrednie (\c GG_EVENT_DCC7_DONE) */
- struct gg_event_typing_notification typing_notification; /**< Powiadomienie o pisaniu (\c GG_EVENT_TYPING_NOTIFICATION) */
+
+/**
+ * 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_error dcc7_error_ex; /**< Błąd połączenia bezpośredniego ze wskaźnikiem na strukturę połączenia (\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_pending dcc7_pending; /**< Trwa próba połączenia bezpośredniego (\c GG_EVENT_DCC7_PENDING) */
+ 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) */
+ struct gg_event_dcc7_done dcc7_done; /**< Zakończono połączenie bezpośrednie (\c GG_EVENT_DCC7_DONE) */
+ struct gg_event_typing_notification typing_notification; /**< Powiadomienie o pisaniu (\c GG_EVENT_TYPING_NOTIFICATION) */
struct gg_event_user_data user_data; /**< Informacje o kontaktach */
struct gg_event_msg multilogon_msg; /**< Inna sesja wysłała wiadomość (\c GG_EVENT_MULTILOGON_MSG) */
struct gg_event_multilogon_info multilogon_info; /**< Informacja o innych sesjach multilogowania (\c GG_EVENT_MULTILOGON_INFO) */
-};
-
-/**
- * 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().
- *
- * \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);
-int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq);
-const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field);
-int gg_pubdir50_type(gg_pubdir50_t res);
-int gg_pubdir50_count(gg_pubdir50_t res);
-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"
-#define GG_PUBDIR50_LASTNAME "lastname"
-#define GG_PUBDIR50_NICKNAME "nickname"
-#define GG_PUBDIR50_BIRTHYEAR "birthyear"
-#define GG_PUBDIR50_CITY "city"
-#define GG_PUBDIR50_GENDER "gender"
-#define GG_PUBDIR50_GENDER_FEMALE "1"
-#define GG_PUBDIR50_GENDER_MALE "2"
-#define GG_PUBDIR50_GENDER_SET_FEMALE "2"
-#define GG_PUBDIR50_GENDER_SET_MALE "1"
-#define GG_PUBDIR50_ACTIVE "ActiveOnly"
-#define GG_PUBDIR50_ACTIVE_TRUE "1"
-#define GG_PUBDIR50_START "fmstart"
-#define GG_PUBDIR50_FAMILYNAME "familyname"
-#define GG_PUBDIR50_FAMILYCITY "familycity"
-
-#else
-
-/**
- * \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.
- *
- * \ingroup http
- */
-struct gg_pubdir {
- int success; /**< Flaga powodzenia operacji */
- uin_t uin; /**< Otrzymany numer lub 0 w przypadku błędu */
-};
-
-int gg_pubdir_watch_fd(struct gg_http *f);
-void gg_pubdir_free(struct gg_http *f);
-
-/**
- * Token autoryzacji niektórych operacji HTTP.
- *
- * \ingroup token
- */
-struct gg_token {
- int width; /**< Szerokość obrazka */
- int height; /**< Wysokość obrazka */
- int length; /**< Liczba znaków w tokenie */
- char *tokenid; /**< Identyfikator tokenu */
-};
-
-struct gg_http *gg_token(int async);
-int gg_token_watch_fd(struct gg_http *h);
-void gg_token_free(struct gg_http *h);
-
-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
-#endif
-
-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
-
-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
-#endif
-
-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
-#endif
-
-extern int gg_dcc_port;
-extern unsigned long gg_dcc_ip;
-
-int gg_dcc_request(struct gg_session *sess, uin_t uin);
-
-struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
-struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
-struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
-void gg_dcc_set_type(struct gg_dcc *d, int type);
-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 /**< 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);
-#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);
-
-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);
-int gg_dcc7_abort(struct gg_dcc7 *dcc);
-void gg_dcc7_free(struct gg_dcc7 *d);
-
-extern int gg_debug_level;
-
-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);
-
-extern FILE *gg_debug_file;
-
-/**
- * \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);
-
-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;
-
-#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.
- */
-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 */
-};
-
-#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;
-
-#define GG_GENDER_NONE 0
-#define GG_GENDER_FEMALE 1
-#define GG_GENDER_MALE 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, ...) GG_DEPRECATED;
-#endif
-
-char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
-
-#define gg_alloc_sprintf gg_saprintf
-
-char *gg_get_line(char **ptr) GG_DEPRECATED;
-
-SOCKET gg_connect(void *addr, int port, int async) GG_DEPRECATED;
-#ifdef GG_CONFIG_MIRANDA
+};
+
+/**
+ * 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().
+ *
+ * \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);
+int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq);
+const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field);
+int gg_pubdir50_type(gg_pubdir50_t res);
+int gg_pubdir50_count(gg_pubdir50_t res);
+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"
+#define GG_PUBDIR50_LASTNAME "lastname"
+#define GG_PUBDIR50_NICKNAME "nickname"
+#define GG_PUBDIR50_BIRTHYEAR "birthyear"
+#define GG_PUBDIR50_CITY "city"
+#define GG_PUBDIR50_GENDER "gender"
+#define GG_PUBDIR50_GENDER_FEMALE "1"
+#define GG_PUBDIR50_GENDER_MALE "2"
+#define GG_PUBDIR50_GENDER_SET_FEMALE "2"
+#define GG_PUBDIR50_GENDER_SET_MALE "1"
+#define GG_PUBDIR50_ACTIVE "ActiveOnly"
+#define GG_PUBDIR50_ACTIVE_TRUE "1"
+#define GG_PUBDIR50_START "fmstart"
+#define GG_PUBDIR50_FAMILYNAME "familyname"
+#define GG_PUBDIR50_FAMILYCITY "familycity"
+
+#else
+
+/**
+ * \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.
+ *
+ * \ingroup http
+ */
+struct gg_pubdir {
+ int success; /**< Flaga powodzenia operacji */
+ uin_t uin; /**< Otrzymany numer lub 0 w przypadku błędu */
+};
+
+int gg_pubdir_watch_fd(struct gg_http *f);
+void gg_pubdir_free(struct gg_http *f);
+
+/**
+ * Token autoryzacji niektórych operacji HTTP.
+ *
+ * \ingroup token
+ */
+struct gg_token {
+ int width; /**< Szerokość obrazka */
+ int height; /**< Wysokość obrazka */
+ int length; /**< Liczba znaków w tokenie */
+ char *tokenid; /**< Identyfikator tokenu */
+};
+
+struct gg_http *gg_token(int async);
+int gg_token_watch_fd(struct gg_http *h);
+void gg_token_free(struct gg_http *h);
+
+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
+#endif
+
+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
+
+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
+#endif
+
+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
+#endif
+
+extern int gg_dcc_port;
+extern unsigned long gg_dcc_ip;
+
+int gg_dcc_request(struct gg_session *sess, uin_t uin);
+
+struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+void gg_dcc_set_type(struct gg_dcc *d, int type);
+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 /**< 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);
+#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);
+
+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);
+int gg_dcc7_abort(struct gg_dcc7 *dcc);
+void gg_dcc7_free(struct gg_dcc7 *d);
+
+extern int gg_debug_level;
+
+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);
+
+extern FILE *gg_debug_file;
+
+/**
+ * \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);
+
+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;
+
+#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.
+ */
+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 */
+};
+
+#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;
+
+#define GG_GENDER_NONE 0
+#define GG_GENDER_FEMALE 1
+#define GG_GENDER_MALE 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, ...) GG_DEPRECATED;
+#endif
+
+char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
+
+#define gg_alloc_sprintf gg_saprintf
+
+char *gg_get_line(char **ptr) GG_DEPRECATED;
+
+SOCKET gg_connect(void *addr, int port, int async) GG_DEPRECATED;
+#ifdef GG_CONFIG_MIRANDA
SOCKET gg_connect_internal(void *addr, int port, int async, SOCKET *gg_sock);
-#endif
-struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
-char *gg_read_line(SOCKET 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);
-uint16_t gg_fix16(uint16_t x);
-#define fix16 gg_fix16
-#define fix32 gg_fix32
-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;
-int gg_dcc7_handle_abort(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
-#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
-#define GG_PUBDIR_PORT 80
-#define GG_REGISTER_HOST "register.gadu-gadu.pl"
-#define GG_REGISTER_PORT 80
-#define GG_REMIND_HOST "retr.gadu-gadu.pl"
-#define GG_REMIND_PORT 80
+#endif
+struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
+char *gg_read_line(SOCKET 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);
+uint16_t gg_fix16(uint16_t x);
+#define fix16 gg_fix16
+#define fix32 gg_fix32
+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, const void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
+
+#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
+#define GG_APPMSG_PORT 80
+#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
+#define GG_PUBDIR_PORT 80
+#define GG_REGISTER_HOST "register.gadu-gadu.pl"
+#define GG_REGISTER_PORT 80
+#define GG_REMIND_HOST "retr.gadu-gadu.pl"
+#define GG_REMIND_PORT 80
#define GG_RELAY_HOST "relay.gadu-gadu.pl"
#define GG_RELAY_PORT 80
-
-#define GG_DEFAULT_PORT 8074
-#define GG_HTTPS_PORT 443
-#define GG_HTTP_USERAGENT NETLIB_USER_AGENT
-
-#define GG_DEFAULT_CLIENT_VERSION "10.1.0.11070"
-#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.10.0"
-
-#ifndef DOXYGEN
-
-#define GG_FEATURE_MSG77 0x0001
-#define GG_FEATURE_STATUS77 0x0002
-#define GG_FEATURE_UNKNOWN_4 0x0004
-#define GG_FEATURE_UNKNOWN_8 0x0008
-#define GG_FEATURE_DND_FFC 0x0010
-#define GG_FEATURE_IMAGE_DESCR 0x0020
-#define GG_FEATURE_UNKNOWN_40 0x0040
-#define GG_FEATURE_UNKNOWN_80 0x0080
-#define GG_FEATURE_UNKNOWN_100 0x0100
-#define GG_FEATURE_USER_DATA 0x0200
-#define GG_FEATURE_MSG_ACK 0x0400
-#define GG_FEATURE_UNKNOWN_800 0x0800
-#define GG_FEATURE_UNKNOWN_1000 0x1000
-#define GG_FEATURE_TYPING_NOTIFICATION 0x2000
+
+#define GG_DEFAULT_PORT 8074
+#define GG_HTTPS_PORT 443
+#define GG_HTTP_USERAGENT NETLIB_USER_AGENT
+
+#define GG_DEFAULT_CLIENT_VERSION "10.1.0.11070"
+#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.10.0"
+
+#ifndef DOXYGEN
+
+#define GG_FEATURE_MSG77 0x0001
+#define GG_FEATURE_STATUS77 0x0002
+#define GG_FEATURE_UNKNOWN_4 0x0004
+#define GG_FEATURE_UNKNOWN_8 0x0008
+#define GG_FEATURE_DND_FFC 0x0010
+#define GG_FEATURE_IMAGE_DESCR 0x0020
+#define GG_FEATURE_UNKNOWN_40 0x0040
+#define GG_FEATURE_UNKNOWN_80 0x0080
+#define GG_FEATURE_UNKNOWN_100 0x0100
+#define GG_FEATURE_USER_DATA 0x0200
+#define GG_FEATURE_MSG_ACK 0x0400
+#define GG_FEATURE_UNKNOWN_800 0x0800
+#define GG_FEATURE_UNKNOWN_1000 0x1000
+#define GG_FEATURE_TYPING_NOTIFICATION 0x2000
#define GG_FEATURE_MULTILOGON 0x4000
-
-/* 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_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION)
-
-#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; /* długość reszty pakietu */
-} GG_PACKED;
-
-#define GG_WELCOME 0x0001
-#define GG_NEED_EMAIL 0x0014
-
-struct gg_welcome {
- uint32_t key; /* klucz szyfrowania hasła */
-} GG_PACKED;
-
-#define GG_LOGIN 0x000c
-
-struct gg_login {
- 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; /* 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; /* 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; /* 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; /* 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; /* 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;
-
-#define GG_LOGIN_OK 0x0003
-
-#define GG_LOGIN_FAILED 0x0009
-
-#define GG_PUBDIR50_REQUEST 0x0014
-
-struct gg_pubdir50_request {
- uint8_t type; /* GG_PUBDIR50_* */
- 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 wysłania zapytania */
-} GG_PACKED;
-
-#define GG_NEW_STATUS 0x0002
-
-#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_FLAG_UNKNOWN 0x00000001
-#define GG_STATUS_FLAG_VIDEO 0x00000002
-#define GG_STATUS_FLAG_MOBILE 0x00100000
-#define GG_STATUS_FLAG_SPAM 0x00800000
-
-#else
-
-/**
- * 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 */
-};
-
-/**
- * Rodzaje statusów użytkownika. Mapa bitowa.
- *
- * \ingroup status
- */
-enum {
- GG_STATUS_FLAG_UNKNOWN, /**< Przeznaczenie nieznane, ale występuje zawsze */
- GG_STATUS_FLAG_VIDEO, /**< Klient obsługuje wideorozmowy */
- GG_STATUS_FLAG_MOBILE, /**< Klient mobilny (ikona telefonu komórkowego) */
- GG_STATUS_FLAG_SPAM, /**< Klient chce otrzymywać linki od nieznajomych */
-};
-
-#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 uwzględnienia dodatkowych flag */
-#define GG_S(x) ((x) & GG_STATUS_MASK)
-
-
-/* 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_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_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; /**< Nowy status */
-} GG_PACKED;
-
-#define GG_NOTIFY_FIRST 0x000f
-#define GG_NOTIFY_LAST 0x0010
-
-#define GG_NOTIFY 0x0010
-
-struct gg_notify {
- uint32_t uin; /* numerek danej osoby */
- uint8_t dunno1; /* rodzaj wpisu w liście */
-} GG_PACKED;
-
-#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
-
-#define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */
-
-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 którym słucha klient */
- uint32_t version; /* wersja klienta */
- uint16_t dunno2; /* znowu port? */
-} GG_PACKED;
-
-#define GG_NOTIFY_REPLY60 0x0011
-
-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 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_STATUS60 0x000f
-
-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 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
-#define GG_REMOVE_NOTIFY 0x000e
-
-struct gg_add_remove {
- uint32_t uin; /* numerek */
- uint8_t dunno1; /* bitmapa */
-} GG_PACKED;
-
-#define GG_STATUS 0x0002
-
-struct gg_status {
- uint32_t uin; /* numerek */
- uint32_t status; /* nowy stan */
-} GG_PACKED;
-
-#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 /**< 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 */
-
-/**
- * Maksymalna długość wiadomości.
- *
- * \ingroup messages
- */
-#define GG_MSG_MAXSIZE 1989
-
-struct gg_send_msg {
- uint32_t recipient;
- uint32_t seq;
- uint32_t msgclass;
-} GG_PACKED;
-
-struct gg_msg_richtext {
- uint8_t flag;
- 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; /**< Początkowy znak formatowania (liczony od 0) */
- uint8_t font; /**< Atrybuty formatowania */
-} GG_PACKED;
-
-#ifndef DOXYGEN
-
-#define GG_FONT_BOLD 0x01
-#define GG_FONT_ITALIC 0x02
-#define GG_FONT_UNDERLINE 0x04
-#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; /**< 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 {
- uint8_t flag;
- uint32_t count;
-} GG_PACKED;
-
-struct gg_msg_image_request {
- uint8_t flag;
- uint32_t size;
- uint32_t crc32;
-} GG_PACKED;
-
-struct gg_msg_image_reply {
- uint8_t flag;
- uint32_t size;
- uint32_t crc32;
- /* char filename[]; */
- /* char image[]; */
-} GG_PACKED;
-
-#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;
- uint32_t seq;
-} GG_PACKED;
-
-#define GG_RECV_MSG 0x000a
-
-struct gg_recv_msg {
- uint32_t sender;
- uint32_t seq;
- uint32_t time;
- uint32_t msgclass;
-} GG_PACKED;
-
-#define GG_PING 0x0008
-
-#define GG_PONG 0x0007
-
-#define GG_DISCONNECTING 0x000b
-
-#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;
-
-struct gg_dcc_tiny_packet {
- uint8_t type; /* rodzaj pakietu */
-} GG_PACKED;
-
-struct gg_dcc_small_packet {
- uint32_t type; /* rodzaj pakietu */
-} GG_PACKED;
-
-struct gg_dcc_big_packet {
- uint32_t type; /* rodzaj pakietu */
- uint32_t dunno1; /* niewiadoma */
- uint32_t dunno2; /* niewiadoma */
-} GG_PACKED;
-
-/*
- * 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 /* 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 /* wysyłamy plik */
-
-#define GG_DCC_FILEATTR_READONLY 0x0020
-
-#define GG_DCC_TIMEOUT_SEND 1800 /* 30 minut */
-#define GG_DCC_TIMEOUT_GET 1800 /* 30 minut */
-#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" */
+
+/* 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_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION)
+
+#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; /* długość reszty pakietu */
+} GG_PACKED;
+
+#define GG_WELCOME 0x0001
+#define GG_NEED_EMAIL 0x0014
+
+struct gg_welcome {
+ uint32_t key; /* klucz szyfrowania hasła */
+} GG_PACKED;
+
+#define GG_LOGIN 0x000c
+
+struct gg_login {
+ 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; /* 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; /* 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; /* 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; /* 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; /* 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;
+
+#define GG_LOGIN_OK 0x0003
+
+#define GG_LOGIN_FAILED 0x0009
+
+#define GG_PUBDIR50_REQUEST 0x0014
+
+struct gg_pubdir50_request {
+ uint8_t type; /* GG_PUBDIR50_* */
+ 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 wysłania zapytania */
+} GG_PACKED;
+
+#define GG_NEW_STATUS 0x0002
+
+#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_FLAG_UNKNOWN 0x00000001
+#define GG_STATUS_FLAG_VIDEO 0x00000002
+#define GG_STATUS_FLAG_MOBILE 0x00100000
+#define GG_STATUS_FLAG_SPAM 0x00800000
+
+#else
+
+/**
+ * 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 */
+};
+
+/**
+ * Rodzaje statusów użytkownika. Mapa bitowa.
+ *
+ * \ingroup status
+ */
+enum {
+ GG_STATUS_FLAG_UNKNOWN, /**< Przeznaczenie nieznane, ale występuje zawsze */
+ GG_STATUS_FLAG_VIDEO, /**< Klient obsługuje wideorozmowy */
+ GG_STATUS_FLAG_MOBILE, /**< Klient mobilny (ikona telefonu komórkowego) */
+ GG_STATUS_FLAG_SPAM, /**< Klient chce otrzymywać linki od nieznajomych */
+};
+
+#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 uwzględnienia dodatkowych flag */
+#define GG_S(x) ((x) & GG_STATUS_MASK)
+
+
+/* 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_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_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; /**< Nowy status */
+} GG_PACKED;
+
+#define GG_NOTIFY_FIRST 0x000f
+#define GG_NOTIFY_LAST 0x0010
+
+#define GG_NOTIFY 0x0010
+
+struct gg_notify {
+ uint32_t uin; /* numerek danej osoby */
+ uint8_t dunno1; /* rodzaj wpisu w liście */
+} GG_PACKED;
+
+#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
+
+#define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */
+
+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 którym słucha klient */
+ uint32_t version; /* wersja klienta */
+ uint16_t dunno2; /* znowu port? */
+} GG_PACKED;
+
+#define GG_NOTIFY_REPLY60 0x0011
+
+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 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_STATUS60 0x000f
+
+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 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
+#define GG_REMOVE_NOTIFY 0x000e
+
+struct gg_add_remove {
+ uint32_t uin; /* numerek */
+ uint8_t dunno1; /* bitmapa */
+} GG_PACKED;
+
+#define GG_STATUS 0x0002
+
+struct gg_status {
+ uint32_t uin; /* numerek */
+ uint32_t status; /* nowy stan */
+} GG_PACKED;
+
+#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 /**< 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 */
+
+/**
+ * Maksymalna długość wiadomości.
+ *
+ * \ingroup messages
+ */
+#define GG_MSG_MAXSIZE 1989
+
+struct gg_send_msg {
+ uint32_t recipient;
+ uint32_t seq;
+ uint32_t msgclass;
+} GG_PACKED;
+
+struct gg_msg_richtext {
+ uint8_t flag;
+ 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; /**< Początkowy znak formatowania (liczony od 0) */
+ uint8_t font; /**< Atrybuty formatowania */
+} GG_PACKED;
+
+#ifndef DOXYGEN
+
+#define GG_FONT_BOLD 0x01
+#define GG_FONT_ITALIC 0x02
+#define GG_FONT_UNDERLINE 0x04
+#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; /**< 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 {
+ uint8_t flag;
+ uint32_t count;
+} GG_PACKED;
+
+struct gg_msg_image_request {
+ uint8_t flag;
+ uint32_t size;
+ uint32_t crc32;
+} GG_PACKED;
+
+struct gg_msg_image_reply {
+ uint8_t flag;
+ uint32_t size;
+ uint32_t crc32;
+ /* char filename[]; */
+ /* char image[]; */
+} GG_PACKED;
+
+#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;
+ uint32_t seq;
+} GG_PACKED;
+
+#define GG_RECV_MSG 0x000a
+
+struct gg_recv_msg {
+ uint32_t sender;
+ uint32_t seq;
+ uint32_t time;
+ uint32_t msgclass;
+} GG_PACKED;
+
+#define GG_PING 0x0008
+
+#define GG_PONG 0x0007
+
+#define GG_DISCONNECTING 0x000b
+
+#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;
+
+struct gg_dcc_tiny_packet {
+ uint8_t type; /* rodzaj pakietu */
+} GG_PACKED;
+
+struct gg_dcc_small_packet {
+ uint32_t type; /* rodzaj pakietu */
+} GG_PACKED;
+
+struct gg_dcc_big_packet {
+ uint32_t type; /* rodzaj pakietu */
+ uint32_t dunno1; /* niewiadoma */
+ uint32_t dunno2; /* niewiadoma */
+} GG_PACKED;
+
+/*
+ * 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 /* 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 /* wysyłamy plik */
+
+#define GG_DCC_FILEATTR_READONLY 0x0020
+
+#define GG_DCC_TIMEOUT_SEND 1800 /* 30 minut */
+#define GG_DCC_TIMEOUT_GET 1800 /* 30 minut */
+#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" */
char hash[GG_DCC7_INFO_HASH_LEN];/* skrót "ip" */
-} 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_HIDDEN 0x00000003 /* użytkownik ojest ukryty i nie możesz mu wysłać pliku */
-#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_ABORT 0x0025
-
-struct gg_dcc7_abort {
+} 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 */
-} GG_PACKED;
-
-struct gg_dcc7_aborted {
+ 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 */
-} 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 */
-
-#if defined(__cplusplus) || defined(_WIN32)
-#ifdef _WIN32
-#pragma pack(pop)
-#endif
-#endif
-
-#endif /* __GG_LIBGADU_H */
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */
+ 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_HIDDEN 0x00000003 /* użytkownik ojest ukryty i nie możesz mu wysłać pliku */
+#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_ABORT 0x0025
+//
+//struct gg_dcc7_abort {
+// gg_dcc7_id_t id; /* identyfikator połączenia */
+// uint32_t uin_from; /* numer nadawcy */
+// uint32_t uin_to; /* numer odbiorcy */
+//} GG_PACKED;
+//
+//struct gg_dcc7_aborted {
+// gg_dcc7_id_t id; /* identyfikator połączenia */
+//} 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 */
+
+#if defined(__cplusplus) || defined(_WIN32)
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+#endif
+
+#endif /* __GG_LIBGADU_H */
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/src/libgadu/message.cpp b/protocols/Gadu-Gadu/src/libgadu/message.cpp
new file mode 100644
index 0000000000..d0872c32e9
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/libgadu/message.cpp
@@ -0,0 +1,664 @@
+/*
+ * (C) Copyright 2001-2010 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.
+ */
+
+/**
+ * \file message.c
+ *
+ * \brief Obsługa wiadomości
+ *
+ * Plik zawiera funkcje dotyczące obsługi "klasy" gg_message_t, które
+ * w przyszłości zostaną dołączone do API. Obecnie używane są funkcje
+ * konwersji między tekstem z atrybutami i HTML.
+ */
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#ifdef _WIN32
+#include "win32.h"
+#endif /* _WIN32 */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "message.h"
+
+#if 0
+
+gg_message_t *gg_message_new(void)
+{
+ gg_message_t *gm;
+
+ gm = malloc(sizeof(gg_message_t));
+
+ if (gm == NULL)
+ return NULL;
+
+ memset(gm, 0, sizeof(gg_message_t));
+
+ gm->msgclass = GG_CLASS_CHAT;
+ gm->seq = (uint32_t) -1;
+
+ return gm;
+}
+
+int gg_message_init(gg_message_t *gm, int msgclass, int seq, uin_t *recipients, size_t recipient_count, char *text, char *html, char *attributes, size_t attributes_length, int auto_convert)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ memset(gm, 0, sizeof(gg_message_t));
+ gm->recipients = recipients;
+ gm->recipient_count = recipient_count;
+ gm->text = text;
+ gm->html = html;
+ gm->attributes = attributes;
+ gm->attributes_length = attributes_length;
+ gm->msgclass = msgclass;
+ gm->seq = seq;
+ gm->auto_convert = auto_convert;
+
+ return 0;
+}
+
+void gg_message_free(gg_message_t *gm)
+{
+ if (gm == NULL) {
+ errno = EINVAL;
+ return;
+ }
+
+ free(gm->text);
+ free(gm->text_converted);
+ free(gm->html);
+ free(gm->html_converted);
+ free(gm->recipients);
+ free(gm->attributes);
+
+ free(gm);
+}
+
+int gg_message_set_auto_convert(gg_message_t *gm, int auto_convert)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ gm->auto_convert = !!auto_convert;
+
+ if (!gm->auto_convert) {
+ free(gm->text_converted);
+ free(gm->html_converted);
+ gm->text_converted = NULL;
+ gm->html_converted = NULL;
+ }
+
+ return 0;
+}
+
+int gg_message_get_auto_convert(gg_message_t *gm)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ return gm->auto_convert;
+}
+
+int gg_message_set_recipients(gg_message_t *gm, const uin_t *recipients, size_t recipient_count)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ if (recipient_count >= INT_MAX / sizeof(uin_t)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((recipients == NULL) || (recipient_count == 0)) {
+ free(gm->recipients);
+ gm->recipients = NULL;
+ gm->recipient_count = 0;
+ } else {
+ uin_t *tmp;
+
+ tmp = realloc(gm->recipients, recipient_count * sizeof(uin_t));
+
+ if (tmp == NULL)
+ return -1;
+
+ memcpy(tmp, recipients, recipient_count * sizeof(uin_t));
+
+ gm->recipients = tmp;
+ gm->recipient_count = recipient_count;
+ }
+
+ return 0;
+}
+
+int gg_message_set_recipient(gg_message_t *gm, uin_t recipient)
+{
+ return gg_message_set_recipients(gm, &recipient, 1);
+}
+
+int gg_message_get_recipients(gg_message_t *gm, const uin_t **recipients, size_t *recipient_count)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ if (recipients != NULL)
+ *recipients = gm->recipients;
+
+ if (recipient_count != NULL)
+ *recipient_count = gm->recipient_count;
+
+ return 0;
+}
+
+uin_t gg_message_get_recipient(gg_message_t *gm)
+{
+ GG_MESSAGE_CHECK(gm, (uin_t) -1);
+
+ if ((gm->recipients == NULL) || (gm->recipient_count < 1)) {
+ // errno = XXX;
+ return (uin_t) -1;
+ }
+
+ return gm->recipients[0];
+}
+
+int gg_message_set_class(gg_message_t *gm, uint32_t msgclass)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ gm->msgclass = msgclass;
+
+ return 0;
+}
+
+uint32_t gg_message_get_class(gg_message_t *gm)
+{
+ GG_MESSAGE_CHECK(gm, (uint32_t) -1);
+
+ return gm->msgclass;
+}
+
+int gg_message_set_seq(gg_message_t *gm, uint32_t seq)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ gm->seq = seq;
+
+ return 0;
+}
+
+uint32_t gg_message_get_seq(gg_message_t *gm)
+{
+ GG_MESSAGE_CHECK(gm, (uint32_t) -1);
+
+ return gm->seq;
+}
+
+int gg_message_set_text(gg_message_t *gm, const char *text)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ if (text == NULL) {
+ free(gm->text);
+ gm->text = NULL;
+ } else {
+ char *tmp;
+
+ tmp = strdup(text);
+
+ if (tmp == NULL)
+ return -1;
+
+ free(gm->text);
+ gm->text = tmp;
+ }
+
+ free(gm->html_converted);
+ gm->html_converted = NULL;
+
+ return 0;
+}
+
+const char *gg_message_get_text(gg_message_t *gm)
+{
+ GG_MESSAGE_CHECK(gm, NULL);
+
+ if (gm->text_converted != NULL)
+ return gm->text_converted;
+
+ if (gm->text == NULL && gm->html != NULL && gm->auto_convert) {
+ size_t len;
+
+ free(gm->text_converted);
+
+ len = gg_message_html_to_text(NULL, gm->html);
+
+ gm->text_converted = malloc(len + 1);
+
+ if (gm->text_converted == NULL)
+ return NULL;
+
+ gg_message_html_to_text(gm->text_converted, gm->html);
+
+ return gm->text_converted;
+ }
+
+ return gm->text;
+}
+
+int gg_message_set_html(gg_message_t *gm, const char *html)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ if (html == NULL) {
+ free(gm->html);
+ gm->html = NULL;
+ } else {
+ char *tmp;
+
+ tmp = strdup(html);
+
+ if (tmp == NULL)
+ return -1;
+
+ free(gm->html);
+ gm->html = tmp;
+ }
+
+ free(gm->text_converted);
+ gm->text_converted = NULL;
+
+ return 0;
+}
+
+const char *gg_message_get_html(gg_message_t *gm)
+{
+ GG_MESSAGE_CHECK(gm, NULL);
+
+ if (gm->html_converted != NULL)
+ return gm->html_converted;
+
+ if (gm->html == NULL && gm->text != NULL && gm->auto_convert) {
+ size_t len;
+
+ free(gm->html_converted);
+
+ len = gg_message_text_to_html(NULL, gm->text, gm->attributes, gm->attributes_length);
+
+ gm->html_converted = malloc(len + 1);
+
+ if (gm->html_converted == NULL)
+ return NULL;
+
+ gg_message_text_to_html(gm->html_converted, gm->text, gm->attributes, gm->attributes_length);
+
+ return gm->html_converted;
+ }
+
+ return gm->html;
+}
+
+int gg_message_set_attributes(gg_message_t *gm, const char *attributes, size_t length)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ if (length > 0xfffd) {
+ // errno = XXX;
+ return -1;
+ }
+
+ if ((attributes == NULL) || (length == 0)) {
+ free(gm->attributes);
+ gm->attributes = NULL;
+ gm->attributes_length = 0;
+ } else {
+ char *tmp;
+
+ tmp = realloc(gm->attributes, length);
+
+ if (tmp == NULL)
+ return -1;
+
+ gm->attributes = tmp;
+ gm->attributes_length = length;
+ }
+
+ free(gm->html_converted);
+ gm->html_converted = NULL;
+
+ return 0;
+}
+
+int gg_message_get_attributes(gg_message_t *gm, const char **attributes, size_t *attributes_length)
+{
+ GG_MESSAGE_CHECK(gm, -1);
+
+ if (attributes != NULL)
+ *attributes = gm->attributes;
+
+ if (attributes_length != NULL)
+ *attributes_length = gm->attributes_length;
+
+ return 0;
+}
+
+#endif
+
+/**
+ * \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 src Tekst źródłowy w UTF-8
+ * \param format Atrybuty tekstu źródłowego
+ * \param format_len Długość bloku atrybutów tekstu źródłowego
+ *
+ * \note Wynikowy tekst nie jest idealnym kodem HTML, ponieważ ma jak
+ * dokładniej odzwierciedlać to, co wygenerowałby oryginalny klient.
+ *
+ * \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).
+ */
+size_t gg_message_text_to_html(char *dst, const char *src, const char *format, size_t 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 name=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
+ const int img_len = 29;
+ int char_pos = 0;
+ int format_idx = 0;
+ unsigned char old_attr = 0;
+ const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
+ int len, i;
+ const unsigned char *format_ = (const unsigned char*) format;
+
+ len = 0;
+
+ /* Nie mamy atrybutów dla pierwsze znaku, a tekst nie jest pusty, więc
+ * tak czy inaczej trzeba otworzyć <span>. */
+
+ if (src[0] != 0 && (format_idx + 3 > format_len || (format_[format_idx] | (format_[format_idx + 1] << 8)) != 0)) {
+ if (dst != NULL)
+ sprintf(&dst[len], span_fmt, 0, 0, 0);
+
+ len += span_len;
+ }
+
+ /* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek
+ * na końcu tekstu. */
+
+ for (i = 0; ; i++) {
+ /* Analizuj atrybuty tak długo jak dotyczą aktualnego znaku. */
+ for (;;) {
+ unsigned char attr;
+ int attr_pos;
+
+ if (format_idx + 3 > format_len)
+ break;
+
+ attr_pos = format_[format_idx] | (format_[format_idx + 1] << 8);
+
+ if (attr_pos != char_pos)
+ break;
+
+ attr = format_[format_idx + 2];
+
+ /* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */
+
+ if (src[i] == 0)
+ attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR);
+
+ format_idx += 3;
+
+ if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 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);
+
+ if (src[i] != 0)
+ 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 = (unsigned char*) "\x00\x00\x00";
+ }
+
+ if (src[i] != 0) {
+ if (dst != NULL)
+ sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
+ len += span_len;
+ }
+ } else if (char_pos == 0 && src[0] != 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;
+ }
+
+ /* Doklej znak zachowując htmlowe escapowanie. */
+
+ switch (src[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':
+ case 0:
+ break;
+ default:
+ if (dst != NULL)
+ dst[len] = src[i];
+ len++;
+ }
+
+ /* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */
+
+ if ((src[i] & 0xc0) != 0xc0)
+ char_pos++;
+
+ if (src[i] == 0)
+ break;
+ }
+
+ /* Zamknij tagi. */
+
+ 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);
+
+ if (src[0] != 0)
+ gg_append(dst, &len, "</span>", 7);
+
+ if (dst != NULL)
+ dst[len] = 0;
+
+ return len;
+}
+
+/**
+ * \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.
+ *
+ * \note Funkcja służy do zachowania kompatybilności przy przesyłaniu
+ * wiadomości HTML do klientów, które tego formatu nie obsługują. Z tego
+ * powodu funkcja nie zachowuje formatowania, a jedynie usuwa tagi i
+ * zamienia podstawowe encje na ich odpowiedniki ASCII.
+ *
+ * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
+ */
+size_t gg_message_html_to_text(char *dst, const char *html)
+{
+ const char *src, *entity, *tag;
+ int in_tag, in_entity;
+ size_t len;
+
+ 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 if (strncmp(entity, "&nbsp;", 6) == 0) {
+ dst[len++] = 0xc2;
+ dst[len++] = 0xa0;
+ } else
+ dst[len++] = '?';
+ } else {
+ if (strncmp(entity, "&nbsp;", 6) == 0)
+ len += 2;
+ else
+ 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;
+}
diff --git a/protocols/Gadu-Gadu/src/libgadu/message.h b/protocols/Gadu-Gadu/src/libgadu/message.h
new file mode 100644
index 0000000000..8b4d7f67fa
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/libgadu/message.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 2009 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_MESSAGE_H
+#define LIBGADU_MESSAGE_H
+
+#include <sys/types.h>
+//#include <inttypes.h>
+#include "libgadu.h"
+
+#if 0
+
+struct gg_message {
+ uin_t *recipients;
+ size_t recipient_count;
+ char *text;
+ char *html;
+ char *attributes;
+ size_t attributes_length;
+ uint32_t msgclass;
+ uint32_t seq;
+
+ int auto_convert;
+ char *text_converted;
+ char *html_converted;
+};
+
+#define GG_MESSAGE_CHECK(gm, result) \
+ if ((gm) == NULL) { \
+ errno = EINVAL; \
+ return (result); \
+ }
+
+int gg_message_init(gg_message_t *gm, int msgclass, int seq, uin_t *recipients, size_t recipient_count, char *text, char *xhtml, char *attributes, size_t attributes_length, int auto_convert);
+
+#endif
+
+size_t gg_message_html_to_text(char *dst, const char *html);
+size_t gg_message_text_to_html(char *dst, const char *utf_msg, const char *format, size_t format_len);
+
+#endif /* LIBGADU_MESSAGE_H */
diff --git a/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp b/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp
index f8fe4dc5de..0f75fd2dd2 100644
--- a/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp
@@ -1,4 +1,3 @@
-/* coding: UTF-8 */
/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */
/*
@@ -35,7 +34,6 @@
#include <errno.h>
#include "libgadu.h"
-#include "internal.h"
struct gg_http *gg_userlist_get(uin_t uin, const char *passwd, int async)
{
diff --git a/protocols/Gadu-Gadu/src/libgadu/protocol.h b/protocols/Gadu-Gadu/src/libgadu/protocol.h
index 5b4895c260..16bde0d424 100644
--- a/protocols/Gadu-Gadu/src/libgadu/protocol.h
+++ b/protocols/Gadu-Gadu/src/libgadu/protocol.h
@@ -1,166 +1,165 @@
-/* coding: UTF-8 */
-/* $Id$ */
-
-/*
- * (C) Copyright 2009-2010 Jakub Zawadzki <darkjames@darkjames.ath.cx>
- * Bartłomiej Zimoń <uzi18@o2.pl>
- * 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_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 "
-
-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
-
-/**
- * Logowanie powiodło się (pakiet \c GG_LOGIN80_OK)
- */
-struct gg_login80_ok {
- uint32_t unknown1; /* 0x00000001 */
-} GG_PACKED;
-
-/**
- * Logowanie nie powiodło się (pakiet \c GG_LOGIN80_FAILED)
- */
-#define GG_LOGIN80_FAILED 0x0043
-
-struct gg_login80_failed {
- uint32_t unknown1; /* 0x00000001 */
-} GG_PACKED;
-
-#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 features; /* opcje protokołu */
- 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 unknown1; /* 0x00 */
- uint32_t flags; /* flagi połączenia */
- 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_RECV_MSG_ACK 0x0046
-
-struct gg_recv_msg_ack {
- uint32_t seq;
-} GG_PACKED;
-
-#define GG_USER_DATA 0x0044
-
-struct gg_user_data {
- uint32_t type;
- uint32_t user_count;
-} GG_PACKED;
-
-struct gg_user_data_user {
- uint32_t uin;
- uint32_t attr_count;
-} GG_PACKED;
-
-#define GG_TYPING_NOTIFICATION 0x0059
-
-struct gg_typing_notification {
- uint16_t length;
- uint32_t uin;
-} GG_PACKED;
-
-#define GG_XML_ACTION 0x002c
-
+/* $Id$ */
+
+/*
+ * (C) Copyright 2009-2010 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ * Bartłomiej Zimoń <uzi18@o2.pl>
+ * 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_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 "
+
+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
+
+/**
+ * Logowanie powiodło się (pakiet \c GG_LOGIN80_OK)
+ */
+struct gg_login80_ok {
+ uint32_t unknown1; /* 0x00000001 */
+} GG_PACKED;
+
+/**
+ * Logowanie nie powiodło się (pakiet \c GG_LOGIN80_FAILED)
+ */
+#define GG_LOGIN80_FAILED 0x0043
+
+struct gg_login80_failed {
+ uint32_t unknown1; /* 0x00000001 */
+} GG_PACKED;
+
+#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 features; /* opcje protokołu */
+ 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 unknown1; /* 0x00 */
+ uint32_t flags; /* flagi połączenia */
+ 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_RECV_MSG_ACK 0x0046
+
+struct gg_recv_msg_ack {
+ uint32_t count;
+} GG_PACKED;
+
+#define GG_USER_DATA 0x0044
+
+struct gg_user_data {
+ uint32_t type;
+ uint32_t user_count;
+} GG_PACKED;
+
+struct gg_user_data_user {
+ uint32_t uin;
+ uint32_t attr_count;
+} GG_PACKED;
+
+#define GG_TYPING_NOTIFICATION 0x0059
+
+struct gg_typing_notification {
+ uint16_t length;
+ uint32_t uin;
+} GG_PACKED;
+
+#define GG_XML_ACTION 0x002c
+
#define GG_RECV_OWN_MSG 0x005a
#define GG_MULTILOGON_INFO 0x005b
@@ -183,95 +182,119 @@ struct gg_multilogon_info_item {
struct gg_multilogon_disconnect {
gg_multilogon_id_t conn_id;
-} GG_PACKED;
-
-#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;
-
-#define GG_DCC7_RELAY_TYPE_SERVER 0x01 /* adres serwera, na który spytać o proxy */
-#define GG_DCC7_RELAY_TYPE_PROXY 0x08 /* adresy proxy, na które sie łączyć */
-
-#define GG_DCC7_RELAY_DUNNO1 0x02
-
-#define GG_DCC7_RELAY_REQUEST 0x0a
-
-struct gg_dcc7_relay_req {
- uint32_t magic; /* 0x0a */
- uint32_t len; /* długość całego pakietu */
- gg_dcc7_id_t id; /* identyfikator połączenia */
- uint16_t type; /* typ zapytania */
- uint16_t dunno1; /* 0x02 */
-} GG_PACKED;
-
-#define GG_DCC7_RELAY_REPLY_RCOUNT 0x02
-
-#define GG_DCC7_RELAY_REPLY 0x0b
-
-struct gg_dcc7_relay_reply {
- uint32_t magic; /* 0x0b */
- uint32_t len; /* długość całego pakietu */
- uint32_t rcount; /* ilość serwerów */
-} GG_PACKED;
-
-struct gg_dcc7_relay_reply_server {
- uint32_t addr; /* adres ip serwera */
- uint16_t port; /* port serwera */
- uint8_t family; /* rodzina adresów (na końcu?!) AF_INET=2 */
-} GG_PACKED;
-
-#define GG_DCC7_WELCOME_SERVER 0xc0debabe
-
-struct gg_dcc7_welcome_server {
- uint32_t magic; /* 0xc0debabe */
- gg_dcc7_id_t id; /* identyfikator połączenia */
-} GG_PACKED;
-
-struct gg_dcc7_welcome_p2p {
- gg_dcc7_id_t id; /* identyfikator połączenia */
-} GG_PACKED;
-
-#ifdef _WIN32
-#pragma pack(pop)
-#endif
-
-#endif /* LIBGADU_PROTOCOL_H */
+} GG_PACKED;
+
+#define GG_MSG_CALLBACK 0x02 /**< Żądanie zwrotnego połączenia bezpośredniego */
+
+#define GG_MSG_OPTION_CONFERENCE 0x01
+#define GG_MSG_OPTION_ATTRIBUTES 0x02
+#define GG_MSG_OPTION_IMAGE_REQUEST 0x04
+#define GG_MSG_OPTION_IMAGE_REPLY 0x05
+#define GG_MSG_OPTION_IMAGE_REPLY_MORE 0x06
+
+#define GG_DCC7_ABORT 0x0025
+
+struct gg_dcc7_abort {
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint32_t uin_from; /* numer nadawcy */
+ uint32_t uin_to; /* numer odbiorcy */
+} GG_PACKED;
+
+#define GG_DCC7_ABORTED 0x0025
+
+struct gg_dcc7_aborted {
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+} GG_PACKED;
+
+#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;
+
+#define GG_DCC7_RELAY_TYPE_SERVER 0x01 /* adres serwera, na który spytać o proxy */
+#define GG_DCC7_RELAY_TYPE_PROXY 0x08 /* adresy proxy, na które sie łączyć */
+
+#define GG_DCC7_RELAY_DUNNO1 0x02
+
+#define GG_DCC7_RELAY_REQUEST 0x0a
+
+struct gg_dcc7_relay_req {
+ uint32_t magic; /* 0x0a */
+ uint32_t len; /* długość całego pakietu */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint16_t type; /* typ zapytania */
+ uint16_t dunno1; /* 0x02 */
+} GG_PACKED;
+
+#define GG_DCC7_RELAY_REPLY_RCOUNT 0x02
+
+#define GG_DCC7_RELAY_REPLY 0x0b
+
+struct gg_dcc7_relay_reply {
+ uint32_t magic; /* 0x0b */
+ uint32_t len; /* długość całego pakietu */
+ uint32_t rcount; /* ilość serwerów */
+} GG_PACKED;
+
+struct gg_dcc7_relay_reply_server {
+ uint32_t addr; /* adres ip serwera */
+ uint16_t port; /* port serwera */
+ uint8_t family; /* rodzina adresów (na końcu?!) AF_INET=2 */
+} GG_PACKED;
+
+#define GG_DCC7_WELCOME_SERVER 0xc0debabe
+
+struct gg_dcc7_welcome_server {
+ uint32_t magic; /* 0xc0debabe */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+} GG_PACKED;
+
+struct gg_dcc7_welcome_p2p {
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+} GG_PACKED;
+
+#define GG_TIMEOUT_DISCONNECT 5 /**< Maksymalny czas oczekiwania na rozłączenie */
+
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+
+#endif /* LIBGADU_PROTOCOL_H */
diff --git a/protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp b/protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp
index 4255192923..96e07c9b53 100644
--- a/protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp
@@ -1,4 +1,3 @@
-/* coding: UTF-8 */
/* $Id: pubdir50.c 11370 2010-03-13 16:17:54Z dezred $ */
/*
@@ -23,6 +22,9 @@
* \file pubdir50.c
*
* \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x
+ *
+ * \todo Zoptymalizować konwersję CP1250<->UTF8. Obecnie robiona jest
+ * testowa konwersja, żeby poznać długość tekstu wynikowego.
*/
#ifndef _WIN64
@@ -41,6 +43,7 @@
#include "libgadu.h"
#include "internal.h"
+#include "encoding.h"
/**
* Tworzy nowe zapytanie katalogu publicznego.
@@ -235,7 +238,7 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
} else {
char *tmp;
- tmp = gg_utf8_to_cp(req->entries[i].field);
+ tmp = gg_encoding_convert(req->entries[i].field, sess->encoding, GG_ENCODING_CP1250, -1, -1);
if (tmp == NULL)
return -1;
@@ -244,7 +247,7 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
free(tmp);
- tmp = gg_utf8_to_cp(req->entries[i].value);
+ tmp = gg_encoding_convert(req->entries[i].value, sess->encoding, GG_ENCODING_CP1250, -1, -1);
if (tmp == NULL)
return -1;
@@ -282,7 +285,7 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
} else {
char *tmp;
- tmp = gg_utf8_to_cp(req->entries[i].field);
+ tmp = gg_encoding_convert(req->entries[i].field, sess->encoding, GG_ENCODING_CP1250, -1, -1);
if (tmp == NULL) {
free(buf);
@@ -293,7 +296,7 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
p += strlen(tmp) + 1;
free(tmp);
- tmp = gg_utf8_to_cp(req->entries[i].value);
+ tmp = gg_encoding_convert(req->entries[i].value, sess->encoding, GG_ENCODING_CP1250, -1, -1);
if (tmp == NULL) {
free(buf);
@@ -427,7 +430,7 @@ int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, c
} else {
char *tmp;
- tmp = gg_cp_to_utf8(value);
+ tmp = gg_encoding_convert(value, GG_ENCODING_CP1250, sess->encoding, -1, -1);
if (tmp == NULL)
goto failure;
diff --git a/protocols/Gadu-Gadu/src/libgadu/resolver.cpp b/protocols/Gadu-Gadu/src/libgadu/resolver.cpp
index 9b3ab79147..065169007f 100644
--- a/protocols/Gadu-Gadu/src/libgadu/resolver.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/resolver.cpp
@@ -1,6 +1,3 @@
-/* coding: UTF-8 */
-/* $Id$ */
-
/*
* (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
* Robert J. Woźny <speedy@ziew.org>
@@ -51,6 +48,7 @@
#include "libgadu.h"
#include "resolver.h"
#include "compat.h"
+#include "session.h"
/** Sposób rozwiązywania nazw serwerów */
static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT;
@@ -88,15 +86,17 @@ static void gg_gethostbyname_cleaner(void *data)
* \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.
+ * nie, to zwykłej \c gethostbyname. Wynikiem jest tablica adresów zakończona
+ * wartością INADDR_NONE, którą należy zwolnić po użyciu.
*
* \param hostname Nazwa serwera
- * \param addr Wskaźnik na rezultat rozwiązywania nazwy
+ * \param result Wskaźnik na wskaźnik z tablicą adresów zakończoną INADDR_NONE
+ * \param count Wskaźnik na zmienną, do ktorej zapisze się liczbę wyników
* \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)
+int gg_gethostbyname_real(const char *hostname, struct in_addr **result, int *count, int pthread)
{
#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R
char *buf = NULL;
@@ -104,13 +104,18 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea
struct hostent he;
struct hostent *he_ptr = NULL;
size_t buf_len = 1024;
- int result = -1;
+ int res = -1;
int h_errnop;
int ret = 0;
#ifdef GG_CONFIG_HAVE_PTHREAD
int old_state;
#endif
+ if (result == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
#ifdef GG_CONFIG_HAVE_PTHREAD
pthread_cleanup_push(gg_gethostbyname_cleaner, &buf);
@@ -154,9 +159,41 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea
}
}
- if (ret == 0 && he_ptr != NULL) {
- memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr));
- result = 0;
+ if (ret == 0 && he_ptr != NULL && he_ptr->h_addr_list[0] != NULL) {
+ int i;
+
+ /* Policz liczbę adresów */
+
+ for (i = 0; he_ptr->h_addr_list[i] != NULL; i++)
+ ;
+
+ /* Zaalokuj */
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ if (pthread)
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+ *result = malloc((i + 1) * sizeof(struct in_addr));
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+ if (pthread)
+ pthread_setcancelstate(old_state, NULL);
+#endif
+
+ if (*result == NULL)
+ return -1;
+
+ /* Kopiuj */
+
+ for (i = 0; he_ptr->h_addr_list[i] != NULL; i++)
+ memcpy(&((*result)[i]), he_ptr->h_addr_list[i], sizeof(struct in_addr));
+
+ (*result)[i].s_addr = INADDR_NONE;
+
+ *count = i;
+
+ res = 0;
}
#ifdef GG_CONFIG_HAVE_PTHREAD
@@ -177,26 +214,91 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea
pthread_cleanup_pop(1);
#endif
- return result;
-#else
+ return res;
+#else /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
struct hostent *he;
+ int i;
+
+ if (result == NULL || count == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
he = gethostbyname(hostname);
- if (he == NULL)
+ if (he == NULL || he->h_addr_list[0] == NULL)
+ return -1;
+
+ /* Policz liczbę adresów */
+
+ for (i = 0; he->h_addr_list[i] != NULL; i++)
+ ;
+
+ /* Zaalokuj */
+
+ *result = (in_addr*)malloc((i + 1) * sizeof(struct in_addr));
+
+ if (*result == NULL)
return -1;
- memcpy(addr, he->h_addr, sizeof(struct in_addr));
+ /* Kopiuj */
+
+ for (i = 0; he->h_addr_list[i] != NULL; i++)
+ memcpy(&((*result)[i]), he->h_addr_list[0], sizeof(struct in_addr));
+
+ (*result)[i].s_addr = INADDR_NONE;
+
+ *count = i;
return 0;
#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
}
/**
+ * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora.
+ *
+ * \param fd Deskryptor
+ * \param hostname Nazwa serwera
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_resolver_run(int fd, const char *hostname)
+{
+ struct in_addr addr_ip[2], *addr_list;
+ int addr_count;
+ int res = 0;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_run(%d, %s)\n", fd, hostname);
+
+ if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) {
+ if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 1) == -1) {
+ addr_list = addr_ip;
+ /* addr_ip[0] już zawiera INADDR_NONE */
+ }
+ } else {
+ addr_list = addr_ip;
+ addr_ip[1].s_addr = INADDR_NONE;
+ addr_count = 1;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_resolver_run() count = %d\n", addr_count);
+
+ if (write(fd, addr_list, (addr_count + 1) * sizeof(struct in_addr)) != (addr_count + 1) * sizeof(struct in_addr))
+ res = -1;
+
+ if (addr_list != addr_ip)
+ free(addr_list);
+
+ return res;
+}
+
+/**
* \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.
+ * nie, to zwykłej \c gethostbyname. Funkcja służy do zachowania zgodności
+ * ABI i służy do pobierania tylko pierwszego adresu -- pozostałe mogą
+ * zostać zignorowane przez aplikację.
*
* \param hostname Nazwa serwera
*
@@ -204,16 +306,13 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea
*/
struct in_addr *gg_gethostbyname(const char *hostname)
{
- struct in_addr *addr;
+ struct in_addr *result;
+ int count;
- if (!(addr = (in_addr*)malloc(sizeof(struct in_addr))))
+ if (gg_gethostbyname_real(hostname, &result, &count, 0) == -1)
return NULL;
- if (gg_gethostbyname_real(hostname, addr, 0)) {
- free(addr);
- return NULL;
- }
- return addr;
+ return result;
}
/**
@@ -243,7 +342,6 @@ struct gg_resolver_fork_data {
static int gg_resolver_fork_start(SOCKET *fd, void **priv_data, const char *hostname)
{
struct gg_resolver_fork_data *data = NULL;
- struct in_addr addr;
int new_errno;
SOCKET pipes[2];
@@ -278,16 +376,9 @@ static int gg_resolver_fork_start(SOCKET *fd, void **priv_data, const char *host
if (data->pid == 0) {
gg_sock_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 (gg_sock_write(pipes[1], &addr, sizeof(addr)) != sizeof(addr))
+ if (gg_resolver_run(pipes[1], hostname) == -1)
exit(1);
-
+ else
exit(0);
}
@@ -320,7 +411,7 @@ cleanup:
* danych
* \param force Flaga usuwania zasobów przed zakończeniem działania
*/
-static void gg_resolver_fork_cleanup(void **priv_data, int force)
+void gg_resolver_fork_cleanup(void **priv_data, int force)
{
struct gg_resolver_fork_data *data;
@@ -394,21 +485,13 @@ static void gg_resolver_pthread_cleanup(void **priv_data, int force)
static void *__stdcall gg_resolver_pthread_thread(void *arg)
{
struct gg_resolver_pthread_data *data = (gg_resolver_pthread_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 (gg_sock_write(data->wfd, &addr, sizeof(addr)) == sizeof(addr))
- pthread_exit(NULL);
- else
+ if (gg_resolver_run(data->wfd, data->hostname) == -1)
pthread_exit((void*) -1);
+ else
+ pthread_exit(NULL);
return NULL; /* żeby kompilator nie marudził */
}
@@ -480,10 +563,10 @@ static int gg_resolver_pthread_start(SOCKET *fd, void **priv_data, const char *h
return 0;
cleanup:
- if (data) {
+ if (data != NULL)
free(data->hostname);
- free(data);
- }
+
+ free(data);
gg_sock_close(pipes[0]);
gg_sock_close(pipes[1]);
@@ -505,10 +588,7 @@ cleanup:
*/
int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)
{
- if (gs == NULL) {
- errno = EINVAL;
- return -1;
- }
+ GG_SESSION_CHECK(gs, -1);
if (type == GG_RESOLVER_DEFAULT) {
if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
@@ -555,10 +635,7 @@ int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)
*/
gg_resolver_t gg_session_get_resolver(struct gg_session *gs)
{
- if (gs == NULL) {
- errno = EINVAL;
- return GG_RESOLVER_INVALID;
- }
+ GG_SESSION_CHECK(gs, (gg_resolver_t) -1);
return gs->resolver_type;
}
@@ -570,11 +647,32 @@ gg_resolver_t gg_session_get_resolver(struct gg_session *gs)
* \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 "SOCKET *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_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int))
{
- if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+ GG_SESSION_CHECK(gs, -1);
+
+ if (resolver_start == NULL || resolver_cleanup == NULL) {
errno = EINVAL;
return -1;
}
@@ -729,24 +827,7 @@ gg_resolver_t gg_global_get_resolver(void)
* \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 "SOCKET *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.
+ * Patrz \ref gg_session_set_custom_resolver.
*
* \return 0 jeśli się powiodło, -1 w przypadku błędu
*/
diff --git a/protocols/Gadu-Gadu/src/libgadu/resolver.h b/protocols/Gadu-Gadu/src/libgadu/resolver.h
index 145c5178a4..481c0e5ea4 100644
--- a/protocols/Gadu-Gadu/src/libgadu/resolver.h
+++ b/protocols/Gadu-Gadu/src/libgadu/resolver.h
@@ -1,4 +1,3 @@
-/* coding: UTF-8 */
/* $Id$ */
/*
@@ -28,6 +27,6 @@
#include <arpa/inet.h>
#endif /* _WIN32 */
-int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread);
+int gg_gethostbyname_real(const char *hostname, struct in_addr **result, int *count, int pthread);
#endif /* LIBGADU_RESOLVER_H */
diff --git a/protocols/Gadu-Gadu/src/libgadu/session.h b/protocols/Gadu-Gadu/src/libgadu/session.h
new file mode 100644
index 0000000000..b77f8666e4
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/libgadu/session.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 2008-2010 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_SESSION_H
+#define LIBGADU_SESSION_H
+
+#ifdef GG_CONFIG_HAVE_GNUTLS
+# include <gnutls/gnutls.h>
+#endif
+
+#define GG_SESSION_CHECK(gs, result) \
+ do { \
+ if ((gs) == NULL) { \
+ errno = EINVAL; \
+ return (result); \
+ } \
+ } while (0)
+
+#define GG_SESSION_CHECK_CONNECTED(gs, result) \
+ do { \
+ GG_SESSION_CHECK(gs, result); \
+ \
+ if (!GG_SESSION_IS_CONNECTED(gs)) { \
+ errno = ENOTCONN; \
+ return (result); \
+ } \
+ } while (0)
+
+#define GG_SESSION_IS_PROTOCOL_7_7(gs) ((gs)->protocol_version >= 0x2a)
+#define GG_SESSION_IS_PROTOCOL_8_0(gs) ((gs)->protocol_version >= 0x2d)
+
+#define GG_SESSION_IS_IDLE(gs) ((gs)->state == GG_STATE_IDLE)
+#define GG_SESSION_IS_CONNECTING(gs) ((gs)->state != GG_STATE_IDLE && (gs)->state != GG_STATE_CONNECTED)
+#define GG_SESSION_IS_CONNECTED(gs) ((gs)->state == GG_STATE_CONNECTED)
+
+#ifdef GG_CONFIG_HAVE_GNUTLS
+
+typedef struct {
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t xcred;
+} gg_session_gnutls_t;
+
+#define GG_SESSION_GNUTLS(gs) ((gg_session_gnutls_t*) (gs)->ssl)->session
+
+#endif /* GG_CONFIG_HAVE_GNUTLS */
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+
+#define GG_SESSION_OPENSSL(gs) ((SSL*) (gs)->ssl)
+
+#endif /* GG_CONFIG_HAVE_OPENSSL */
+
+int gg_session_handle_packet(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge);
+
+#endif /* LIBGADU_SESSION_H */
diff --git a/protocols/Gadu-Gadu/src/libgadu/sha1.cpp b/protocols/Gadu-Gadu/src/libgadu/sha1.cpp
index b01b77a00c..155aa85dfd 100644
--- a/protocols/Gadu-Gadu/src/libgadu/sha1.cpp
+++ b/protocols/Gadu-Gadu/src/libgadu/sha1.cpp
@@ -1,5 +1,4 @@
-/* coding: UTF-8 */
-/* $Id: sha1.c,v 1.4 2007-07-20 23:00:50 wojtekka Exp $ */
+/* $Id$ */
/*
* (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -24,7 +23,7 @@
/**
* \file sha1.c
*
- * \brief Funkcje wyznaczania skrĂłtu SHA1
+ * \brief Funkcje wyznaczania skrótu SHA1
*/
#include <string.h>
@@ -38,11 +37,11 @@
#include "libgadu.h"
/**
- * \internal Liczy skrót SHA1 z ziarna i hasła.
+ * \internal Liczy skrót SHA1 z ziarna i hasła.
*
- * \param password Hasło
+ * \param password Hasło
* \param seed Ziarno
- * \param result Bufor na wynik funkcji skrĂłtu (20 bajtĂłw)
+ * \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)
{
@@ -56,10 +55,10 @@ void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
}
/**
- * \internal Liczy skrĂłt SHA1 z pliku.
+ * \internal Liczy skrót SHA1 z pliku.
*
* \param fd Deskryptor pliku
- * \param result WskaĹşnik na skrĂłt
+ * \param result WskaĹşnik na skrót
*
* \return 0 lub -1
*/