From 97bb7e79c15952e7f9bf47caef4e3c09dd63c8df Mon Sep 17 00:00:00 2001 From: Goraf <22941576+Goraf@users.noreply.github.com> Date: Wed, 10 Jan 2018 13:27:54 +0100 Subject: Gadu-Gadu: complete update to libgadu-1.10.1-post --- protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj | 16 + protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters | 24 + protocols/Gadu-Gadu/src/core.cpp | 60 +- protocols/Gadu-Gadu/src/gg.cpp | 1 - protocols/Gadu-Gadu/src/libgadu/common.cpp | 414 +- protocols/Gadu-Gadu/src/libgadu/dcc7.cpp | 233 +- protocols/Gadu-Gadu/src/libgadu/debug.cpp | 301 ++ protocols/Gadu-Gadu/src/libgadu/debug.h | 27 + protocols/Gadu-Gadu/src/libgadu/encoding.cpp | 277 ++ protocols/Gadu-Gadu/src/libgadu/encoding.h | 27 + protocols/Gadu-Gadu/src/libgadu/events.cpp | 3930 +++++-------------- protocols/Gadu-Gadu/src/libgadu/handlers.cpp | 1810 +++++++++ protocols/Gadu-Gadu/src/libgadu/http.cpp | 12 +- protocols/Gadu-Gadu/src/libgadu/internal.h | 76 +- protocols/Gadu-Gadu/src/libgadu/libgadu.cpp | 215 +- protocols/Gadu-Gadu/src/libgadu/libgadu.h | 4513 +++++++++++----------- protocols/Gadu-Gadu/src/libgadu/message.cpp | 664 ++++ protocols/Gadu-Gadu/src/libgadu/message.h | 56 + protocols/Gadu-Gadu/src/libgadu/obsolete.cpp | 2 - protocols/Gadu-Gadu/src/libgadu/protocol.h | 533 +-- protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp | 15 +- protocols/Gadu-Gadu/src/libgadu/resolver.cpp | 229 +- protocols/Gadu-Gadu/src/libgadu/resolver.h | 3 +- protocols/Gadu-Gadu/src/libgadu/session.h | 70 + protocols/Gadu-Gadu/src/libgadu/sha1.cpp | 15 +- 25 files changed, 7456 insertions(+), 6067 deletions(-) create mode 100644 protocols/Gadu-Gadu/src/libgadu/debug.cpp create mode 100644 protocols/Gadu-Gadu/src/libgadu/debug.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/encoding.cpp create mode 100644 protocols/Gadu-Gadu/src/libgadu/encoding.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/handlers.cpp create mode 100644 protocols/Gadu-Gadu/src/libgadu/message.cpp create mode 100644 protocols/Gadu-Gadu/src/libgadu/message.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/session.h (limited to 'protocols') 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 @@ NotUsing + + NotUsing + + + NotUsing + NotUsing + + NotUsing + NotUsing NotUsing + + NotUsing + NotUsing @@ -264,11 +276,15 @@ + + + + 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 @@ Source Files + + Source Files\libgadu + + + Source Files\libgadu + + + Source Files\libgadu + + + Source Files\libgadu + @@ -137,6 +149,9 @@ Source Files\libgadu + + Source Files\libgadu + Source Files\libgadu @@ -155,6 +170,15 @@ Header Files + + Source Files\libgadu + + + Source Files\libgadu + + + Source Files\libgadu + 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 + * (C) Copyright 2001-2006 Wojtek Kaniewski * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Tomasz Chiliński + * Adam Wysocki * * 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,110 +54,8 @@ #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. * @@ -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 @@ -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 + * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Tomasz Chiliński + * Adam Wysocki + * + * 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 +#ifdef _WIN32 +#include "win32.h" +#endif /* _WIN32 */ +#include +#include +#include +#include + +#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 + * + * 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 + * Wojtek Kaniewski + * + * 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 +#include +#include + +#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 + * Wojtek Kaniewski + * + * 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 - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Adam Wysocki - * - * 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 -#ifdef _WIN32 -#include "win32.h" -#else -#include -#include -#include -#include -#endif /* _WIN32 */ - -#include "compat.h" -#include "libgadu.h" -#include "protocol.h" -#include "internal.h" - -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#include -#endif /* _WIN32 */ -#ifndef GG_CONFIG_MIRANDA -#ifdef GG_CONFIG_HAVE_OPENSSL -# include -# include -#endif -#endif - +/* $Id$ */ + +/* + * (C) Copyright 2001-2006 Wojtek Kaniewski + * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Adam Wysocki + * + * 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 +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#endif /* _WIN32 */ + +#include "compat.h" +#include "libgadu.h" +#include "protocol.h" +#include "internal.h" +#include "encoding.h" +#include "debug.h" +#include "session.h" + +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#endif /* _WIN32 */ +#ifndef GG_CONFIG_MIRANDA +#ifdef GG_CONFIG_HAVE_GNUTLS +# include +# include +#endif +#ifdef GG_CONFIG_HAVE_OPENSSL +# include +# include +#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, "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 + * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Tomasz Chiliński + * Adam Wysocki + * + * 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 +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#ifdef sun +# include +#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 +#ifndef _WIN32 +#include +#endif /* _WIN32 */ +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif /* _WIN32 */ +#include +#ifndef GG_CONFIG_MIRANDA +#ifdef GG_CONFIG_HAVE_OPENSSL +# include +# include +#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 - * - * 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 + * + * 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 @@ -50,6 +49,9 @@ #include "protocol.h" #include "resolver.h" #include "internal.h" +#include "encoding.h" +#include "debug.h" +#include "session.h" #include #ifndef _WIN32 @@ -64,52 +66,18 @@ #ifndef _WIN32 #include #endif /* _WIN32 */ -#if !defined(GG_CONFIG_MIRANDA) && defined(GG_CONFIG_HAVE_OPENSSL) +#if !defined(GG_CONFIG_MIRANDA) +#ifdef GG_CONFIG_HAVE_GNUTLS +# include +#endif +#ifdef GG_CONFIG_HAVE_OPENSSL # include # include #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. * @@ -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,2304 +1,2297 @@ -/* coding: UTF-8 */ -/* $Id: libgadu.h 13762 2011-08-09 12:35:16Z dezred $ */ - -/* - * (C) Copyright 2001-2009 Wojtek Kaniewski - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Tomasz Chiliński - * Piotr Wysocki - * Dawid Jarosz - * Jakub Zawadzki - * - * 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 -#include -#endif - -#if defined(__cplusplus) || defined(_WIN32) -#pragma pack(push, 1) -#endif - -#include -#include -#include - -/** \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 . */ -#undef GG_CONFIG_HAVE_STDINT_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_INTTYPES_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_SYS_INTTYPES_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H - -/* Defined if uintX_t types are defined in . */ -#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 + * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Tomasz Chiliński + * Piotr Wysocki + * Dawid Jarosz + * Jakub Zawadzki + * + * 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 +#include +#endif + +#if defined(__cplusplus) || defined(_WIN32) +#pragma pack(push, 1) +#endif + +#include +#include +#include + +/** \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 . */ +#undef GG_CONFIG_HAVE_STDINT_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_INTTYPES_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_SYS_INTTYPES_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H + +/* Defined if uintX_t types are defined in . */ +#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 -#endif - -#ifdef GG_CONFIG_HAVE_STDINT_H -#include -#else -# ifdef GG_CONFIG_HAVE_INTTYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_TYPES_H -# include -# else - -#ifndef __AC_STDINT_H -#define __AC_STDINT_H - -/* ISO C 9X: 7.18 Integer types */ - -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; +#include +#endif + +#ifdef GG_CONFIG_HAVE_STDINT_H +#include +#else +# ifdef GG_CONFIG_HAVE_INTTYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_TYPES_H +# include +# else + +#ifndef __AC_STDINT_H +#define __AC_STDINT_H + +/* ISO C 9X: 7.18 Integer types */ + +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) */ +}; + /** - * Atrybut użytkownika. + * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR. */ -struct gg_event_user_data_attr { - int type; /**< Typ atrybutu */ - char *key; /**< Klucz */ - char *value; /**< Wartość */ +struct gg_event_notify_descr { + struct gg_notify_reply *notify; /**< Informacje o liście kontaktów */ + char *descr; /**< Opis status */ }; /** - * Struktura opisująca kontakt w zdarzeniu GG_EVENT_USER_DATA. + * Opis zdarzenia \c GG_EVENT_STATUS. */ -struct gg_event_user_data_user { - uin_t uin; /**< Numer kontaktu */ - size_t attr_count; /**< Liczba atrybutów */ - struct gg_event_user_data_attr *attrs; /**< Lista atrybutów */ +struct gg_event_status { + uin_t uin; /**< Numer Gadu-Gadu */ + uint32_t status; /**< Nowy status */ + char *descr; /**< Opis */ }; /** - * Opis zdarzenia \c GG_EVENT_USER_DATA. + * Opis zdarzenia \c GG_EVENT_STATUS60. */ -struct gg_event_user_data { - int type; /**< Rodzaj informacji o kontaktach */ - size_t user_count; /**< Liczba kontaktów */ - struct gg_event_user_data_user *users; /**< Lista kontaktów */ +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 */ }; - + /** - * Struktura opisująca sesję multilogowania. + * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60. */ -struct gg_multilogon_session { - gg_multilogon_id_t id; /**< Identyfikator sesji */ - char *name; /**< Nazwa sesji (podana w \c gg_login_params.client_version) */ - uint32_t remote_addr; /**< Adres sesji */ - int status_flags; /**< Flagi statusu sesji */ - int protocol_features; /**< Opcje protokolu sesji */ - time_t logon_time; /**< Czas zalogowania */ +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_MULTILOGON_INFO. + * Opis zdarzenia \c GG_EVENT_ACK. */ -struct gg_event_multilogon_info { - int count; /**< Liczba sesji */ - struct gg_multilogon_session *sessions; /** Lista sesji */ +struct gg_event_ack { + uin_t recipient; /**< Numer odbiorcy */ + int status; /**< Status doręczenia */ + int seq; /**< Numer sekwencyjny wiadomości */ }; - -/** - * 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) */ - 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 -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 -#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_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" */ - 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_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: - */ + +/** + * 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. + */ +struct gg_event_user_data_attr { + int type; /**< Typ atrybutu */ + char *key; /**< Klucz */ + char *value; /**< Wartość */ +}; + +/** + * Struktura opisująca kontakt w zdarzeniu GG_EVENT_USER_DATA. + */ +struct gg_event_user_data_user { + uin_t uin; /**< Numer kontaktu */ + size_t attr_count; /**< Liczba atrybutów */ + struct gg_event_user_data_attr *attrs; /**< Lista atrybutów */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_USER_DATA. + */ +struct gg_event_user_data { + int type; /**< Rodzaj informacji o kontaktach */ + size_t user_count; /**< Liczba kontaktów */ + struct gg_event_user_data_user *users; /**< Lista kontaktów */ +}; + +/** + * Struktura opisująca sesję multilogowania. + */ +struct gg_multilogon_session { + gg_multilogon_id_t id; /**< Identyfikator sesji */ + char *name; /**< Nazwa sesji (podana w \c gg_login_params.client_version) */ + uint32_t remote_addr; /**< Adres sesji */ + int status_flags; /**< Flagi statusu sesji */ + int protocol_features; /**< Opcje protokolu sesji */ + time_t logon_time; /**< Czas zalogowania */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_MULTILOGON_INFO. + */ +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_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 +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, 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_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" */ + 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_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 + * + * 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 +#include +#include +#include +#include + +#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[] = ""; + const int span_len = 75; + const char img_fmt[] = ""; + 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ć . */ + + 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, "", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "", 4); + + if (src[i] != 0) + gg_append(dst, &len, "", 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, "", 3); + + if ((attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "", 3); + + if ((attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "", 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, "
", 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, "
", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "
", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "
", 4); + + if (src[0] != 0) + gg_append(dst, &len, "
", 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, " + * + * 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 +//#include +#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 #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 - * Bartłomiej Zimoń - * Wojtek Kaniewski - * - * 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 + * Bartłomiej Zimoń + * Wojtek Kaniewski + * + * 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 * Robert J. Woźny @@ -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 #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 + * + * 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 +#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 @@ -24,7 +23,7 @@ /** * \file sha1.c * - * \brief Funkcje wyznaczania skrĂłtu SHA1 + * \brief Funkcje wyznaczania skrótu SHA1 */ #include @@ -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 */ -- cgit v1.2.3