diff options
author | Goraf <22941576+Goraf@users.noreply.github.com> | 2018-01-10 13:27:54 +0100 |
---|---|---|
committer | Goraf <22941576+Goraf@users.noreply.github.com> | 2018-01-19 01:39:33 +0100 |
commit | 97bb7e79c15952e7f9bf47caef4e3c09dd63c8df (patch) | |
tree | b501cec701c8f98f738b3157f9195c090981d072 /protocols | |
parent | 7f1d5f6915ecfbb804319d33e4712455def63182 (diff) |
Gadu-Gadu: complete update to libgadu-1.10.1-post
Diffstat (limited to 'protocols')
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, "<", 4) == 0) - dst[len] = '<'; - else if (strncmp(entity, ">", 4) == 0) - dst[len] = '>'; - else if (strncmp(entity, """, 6) == 0) - dst[len] = '"'; - else if (strncmp(entity, "'", 6) == 0) - dst[len] = '\''; - else if (strncmp(entity, "&", 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, "&", 5); + break; + case '<': + gg_append(dst, &len, "<", 4); + break; + case '>': + gg_append(dst, &len, ">", 4); + break; + case '\'': + gg_append(dst, &len, "'", 6); + break; + case '\"': + gg_append(dst, &len, """, 6); + break; + case '\n': + gg_append(dst, &len, "<br>", 4); + break; + case '\r': + 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, "<", 4) == 0) + dst[len++] = '<'; + else if (strncmp(entity, ">", 4) == 0) + dst[len++] = '>'; + else if (strncmp(entity, """, 6) == 0) + dst[len++] = '"'; + else if (strncmp(entity, "'", 6) == 0) + dst[len++] = '\''; + else if (strncmp(entity, "&", 5) == 0) + dst[len++] = '&'; + else if (strncmp(entity, " ", 6) == 0) { + dst[len++] = 0xc2; + dst[len++] = 0xa0; + } else + dst[len++] = '?'; + } else { + if (strncmp(entity, " ", 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" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku + * - \c "void **priv_data" — wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy + * - \c "const char *name" — nazwa serwera do rozwiązania + * + * Parametry funkcji zwalniającej zasoby wyglądają następująco: + * - \c "void **priv_data" — wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu + * - \c "int force" — 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" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku - * - \c "void **priv_data" — wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy - * - \c "const char *name" — nazwa serwera do rozwiązania - * - * Parametry funkcji zwalniającej zasoby wyglądają następująco: - * - \c "void **priv_data" — wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu - * - \c "int force" — 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
*/
|